Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea/
httpserver/target
2 changes: 0 additions & 2 deletions README.md

This file was deleted.

11 changes: 0 additions & 11 deletions Request.txt

This file was deleted.

Binary file removed WebRoot/favicon.ico
Binary file not shown.
17 changes: 0 additions & 17 deletions WebRoot/index.html

This file was deleted.

Binary file removed WebRoot/logo.png
Binary file not shown.
17 changes: 17 additions & 0 deletions httpserver/WebRoot/Index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<html lang="">
<head>
<title>Simple Java HTTP Server</title>
<style>
body {
text-align: center;
}
</style>
</head>
<br>
<h1>Welcome to my rendered page!</h1>
<br>
<h2>This page was delivered using my custom-built HTTP Server made in Java.</h2>
<br>
<br>
<img src="randomlogo.png" alt="broken"/>
</html>
Binary file added httpserver/WebRoot/favicon.ico
Binary file not shown.
Binary file added httpserver/WebRoot/randomlogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions httpserver/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.johan.httpserver</groupId>
<artifactId>httpserver</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.3</version>
<configuration>
<mainClass>com.johan.httpserver.httpserver</mainClass>
</configuration>
</plugin>
</plugins>
</build>

<!--Dependencies-->
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.21.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.21.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.13</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>24.0.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.johan.http;

public class BadHttpVersionException extends Exception {
public BadHttpVersionException() {
super("Unsupported or malformed HTTP version");
}

public BadHttpVersionException(String message) {
super(message);
}
}
17 changes: 17 additions & 0 deletions httpserver/src/main/java/com/johan/http/HttpMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.johan.http;

public enum HttpMethod {
GET, HEAD;
public static final int MAX_LENGTH;

//this checks all the valid METHODS declared in the server and sets max_length as the biggest header
static{
int tempMaxLength = -1;
for (HttpMethod method : HttpMethod.values()){
if(method.name().length()>tempMaxLength){
tempMaxLength = method.name().length();
}
}
MAX_LENGTH = tempMaxLength;
}
}
135 changes: 135 additions & 0 deletions httpserver/src/main/java/com/johan/http/HttpParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.johan.http;

import org.slf4j.LoggerFactory;
import org.slf4j.Logger;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

public class HttpParser {

private final static Logger LOGGER = LoggerFactory.getLogger(HttpParser.class);

private static final int SP = 0x20; //32
private static final int CR = 0x0D; //13
private static final int LF = 0x0A; //10

public static HttpRequest parseHttpReq(InputStream iptStream) throws HttpParsingException {
InputStreamReader isr = new InputStreamReader(iptStream, StandardCharsets.US_ASCII);

HttpRequest request = new HttpRequest();
try {
parseRequestLine(isr, request);
parseHeader(isr, request);
}catch(IOException e){
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_400_BAD_REQ);
}
parseBody(isr, request);

return request;
}
private static void parseRequestLine(InputStreamReader isr, HttpRequest req) throws IOException, HttpParsingException {
boolean methodParsed = false;
boolean reqTargetParsed = false;
StringBuilder processDataBuffer = new StringBuilder();
int _byte;
while ((_byte=isr.read()) >=0){
if(_byte==CR){
_byte=isr.read();
if(_byte==LF){
LOGGER.debug("Request Line VERSION to process : {}", processDataBuffer.toString());
if (!methodParsed || !reqTargetParsed) {
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_400_BAD_REQ);
}
try {
req.setHttpVersion(processDataBuffer.toString());
}catch (BadHttpVersionException e) {
LOGGER.error("Bad HTTP Version received : {}", e.getMessage());
throw new HttpParsingException(HttpStatusCode.SERVER_ERROR_505_HTTP_VERSION_NOT_SUPPORTED);
}
return;
}else{
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_400_BAD_REQ);
}
}
if(_byte==SP){
if (!methodParsed) {
LOGGER.debug("Request Line METHOD to process : {}", processDataBuffer.toString());
try {
req.setMethod(processDataBuffer.toString());
methodParsed = true;
}catch (HttpParsingException e){
LOGGER.error("Invalid HTTP Method received : {}", e.getMessage());
throw new HttpParsingException(HttpStatusCode.SERVER_ERROR_501_NOT_IMPLEMENTED);
}
}else if (!reqTargetParsed) {
LOGGER.debug("Request Line REQ TARGET to process : {}", processDataBuffer.toString());
req.setRequestTarget(processDataBuffer.toString());
reqTargetParsed = true;
}
else{
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_400_BAD_REQ);
}
processDataBuffer.delete(0, processDataBuffer.length());
}else{
processDataBuffer.append((char)_byte);
if(!methodParsed){
if(processDataBuffer.length()>HttpMethod.MAX_LENGTH){
LOGGER.error("Terminating connection, Bad Method received : {}", processDataBuffer.toString());
throw new HttpParsingException(HttpStatusCode.SERVER_ERROR_501_NOT_IMPLEMENTED);
}
}
}
}
}

private static void parseHeader(InputStreamReader isr, HttpRequest req) throws HttpParsingException, IOException {
StringBuilder processDataBuffer = new StringBuilder();
boolean headerParsed = false;
int _byte;
while ((_byte=isr.read()) >=0){
if(_byte==CR){
_byte=isr.read();
if(_byte==LF) {
String line = processDataBuffer.toString().trim();
if (line.isEmpty()) {
if(!headerParsed){
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_400_BAD_REQ);
}
return;
}
processingHeaderField(processDataBuffer, req);
headerParsed = true;
processDataBuffer.delete(0, processDataBuffer.length());
} else{
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_400_BAD_REQ);
}
} else{
processDataBuffer.append((char)_byte);
}
}
}

private static void processingHeaderField(StringBuilder processDataBuffer, HttpRequest req) throws HttpParsingException {
String rawHeaderField = processDataBuffer.toString();
if (rawHeaderField.length() > 8192) {
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_414_URI_TOO_LONG);
}
int colonIndex = rawHeaderField.indexOf(':');
if(colonIndex == -1){
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_400_BAD_REQ);
}
String fieldName = rawHeaderField.substring(0, colonIndex).trim();
String fieldValue = rawHeaderField.substring(colonIndex+1).trim();
if(fieldName.isEmpty()){
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_400_BAD_REQ);
}
req.addHeader(fieldName, fieldValue);
}

private static void parseBody(InputStreamReader isr, HttpRequest req) {

}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.coderfromscratch.http;
package com.johan.http;

public class HttpParsingException extends Exception {

Expand Down
65 changes: 65 additions & 0 deletions httpserver/src/main/java/com/johan/http/HttpRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.johan.http;

import java.util.HashMap;
import java.util.Set;

public class HttpRequest {

private HttpMethod method;
private String requestTarget;
private String originalHttpVersion;
private HttpVersion getBestCompatibleVersion;
private HashMap<String, String> headers = new HashMap<>();

HttpRequest() {
}

public HttpMethod getMethod() {
return method;
}

public String getRequestTarget() {
return requestTarget;
}

public HttpVersion getBestCompatibleVersion() { return getBestCompatibleVersion; }

public String getOriginalHttpVersion() { return originalHttpVersion; }

public Set<String> getHeaderNames() {
return headers.keySet();
}

public String getHeader(String headerName) {
return headers.get(headerName.toLowerCase());
}

void setMethod(String methodName) throws HttpParsingException {
for (HttpMethod method: HttpMethod.values()) {
if(methodName.equals(method.name())){
this.method = method;
return;
}
}
throw new HttpParsingException(HttpStatusCode.SERVER_ERROR_501_NOT_IMPLEMENTED);
}

public void setRequestTarget(String requestTarget) throws HttpParsingException {
if (requestTarget == null || requestTarget.isEmpty()){
throw new HttpParsingException(HttpStatusCode.CLIENT_ERROR_400_BAD_REQ);
}
this.requestTarget = requestTarget;
}

public void setHttpVersion(String originalHttpVersion) throws BadHttpVersionException, HttpParsingException {
this.originalHttpVersion = originalHttpVersion;
this.getBestCompatibleVersion = HttpVersion.getBestCompatibleVersion(originalHttpVersion);
if(this.getBestCompatibleVersion == null){
throw new BadHttpVersionException();
}
}

void addHeader(String headerName, String HeaderField){
headers.put(headerName.toLowerCase(), HeaderField);
}
}
21 changes: 21 additions & 0 deletions httpserver/src/main/java/com/johan/http/HttpStatusCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.johan.http;

public enum HttpStatusCode {
/* ---Client Errors --- */
CLIENT_ERROR_400_BAD_REQ(400, "Bad Request"),
CLIENT_ERROR_401_METHOD_NOT_ALLOWED(401, "Method not allowed"),
CLIENT_ERROR_404_NOT_FOUND(404, "Request Target not found"),
CLIENT_ERROR_414_URI_TOO_LONG(414, "URI too long"),
/* ---Server Errors --- */
SERVER_ERROR_500_INTERNAL_SERVER_ERROR(500, "Internal server error"),
SERVER_ERROR_501_NOT_IMPLEMENTED(501, "Method Not implemented"),
SERVER_ERROR_505_HTTP_VERSION_NOT_SUPPORTED(505, "Http Version Not Supported");

public final int STATUS_CODE;
public final String MESSAGE;

HttpStatusCode(int STATUS_CODE, String MESSAGE) {
this.STATUS_CODE=STATUS_CODE;
this.MESSAGE = MESSAGE;
}
}
Loading