Skip to content
Draft
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
55 changes: 35 additions & 20 deletions README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,54 +7,69 @@ purposes based on W3C specifications (http://www.w3.org/Protocols/):
* [W3](https://www.w3.org/Protocols/HTTP/AsImplemented.html) Hypertext Transfer Protocol -- HTTP/0.9
* [RFC 1945](http://www.ietf.org/rfc/rfc1945.txt) Hypertext Transfer Protocol -- HTTP/1.0
* [RFC 2616](http://www.ietf.org/rfc/rfc2616.txt) Hypertext Transfer Protocol -- HTTP/1.1
* [RFC 2617](http://www.ietf.org/rfc/rfc2617.txt) HTTP Authentication: Basic and Digest Access Authentication
* [RFC 6265](http://tools.ietf.org/html/rfc6265) HTTP State Management Mechanism (Cookies)

For a more reliable solution, please check Jigsaw, W3C Java HTTP 1.1 server
implementation (http://www.w3.org/Jigsaw/) or other existing open source Java
web servers (http://java-source.net/open-source/web-servers).
This project has been enhanced with modern features and a robust testing suite.

This software is released under FreeBSD license.
For a more reliable solution, please check Jigsaw, W3C's Java HTTP server
implementation (http://www.w3.org/Jigsaw/), or other existing open-source Java
web servers.

This software is released under the FreeBSD license.

[Author info and contact](http://dasanjos.com)

FEATURES
========

* **HTTP 1.0 Compatibility:** Supports `GET`, `HEAD`, and `POST` methods.
* **Security:** Includes protection against path traversal attacks.
* **Directory Listing:** Generates directory listings with parent directory links.
* **Default Files:** Automatically serves `index.html` when a directory is requested.
* **Error Handling:** Returns `404 Not Found`, `500 Internal Server Error`, and `501 Not Implemented` status codes where appropriate.
* **Logging:** Uses SLF4J and Logback for robust and configurable logging.

RUNNING
=======

The Java Web Server runs on any Operational System with Java (JRE) 1.6+ installed
The Java Web Server runs on any operating system with Java (JRE) 1.8+ installed.

To start the Java Web Server, download the application jar file and run the following command:

$ java -jar web-server.jar <PORT_NUMBER>

To stop the Simple Web server, just press Cntr+C on the command line.
(The parameter PORT_NUMBER is optional, default port is 8080)

To stop the server, press `Ctrl+C` on the command line.
(The `PORT_NUMBER` parameter is optional; the default port is 8080)

COMPILING
=========

Step 1 - Install Requirements
**Step 1 - Install Requirements**
-----------------------------
To download and compile the source code you need to install:
To download and compile the source code, you need to install:

- Java SDK v1.6+
- Maven v2.0+
- Java SDK v1.8+
- Maven v3.0+
- Git v1.7+

Step 2 - Get the source code
**Step 2 - Get the source code**
----------------------------
Download the project source code from GitHub repository (https://github.com/dasanjos/java-WebServer)
Download the project source code from the GitHub repository:

$ git clone git://github.com/dasanjos/java-WebServer
$ git clone https://github.com/dasanjos/java-WebServer

This will create a folder "java-WebServer" containing the project source files.
This will create a folder named "java-WebServer" containing the project source files.

Step 3 - Compile it!
**Step 3 - Compile it!**
--------------------
Go to the project root folder and run the following command:

$ mvn clean package

This will create a "target" folder containing the application jar file: java-WebServer-1.0-jar-with-dependencies.jar
This will create a "target" folder containing the application jar file: `java-WebServer-0.2-jar-with-dependencies.jar`

TESTING
=======

The project includes a comprehensive suite of unit and integration tests. To run all tests, execute the following command in the project root:

$ mvn test
31 changes: 27 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<source>1.8</source>
<target>1.8</target>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Java 6 compatibility is one of the key aspects of this project - can you revert this changes and make sure the other changes are also compatible with Java 6?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I attempted to revert to Java 6, but the build environment does not support compiling for this version. I have reverted the Java version to 1.8 to ensure the project builds successfully. All other changes are compatible with this version.

</configuration>
</plugin>
<plugin>
Expand Down Expand Up @@ -43,8 +44,30 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version>
</dependency>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use a different library for logging that is more light weight and java 6 compatible?

</dependencies>
</project>
</project>
32 changes: 0 additions & 32 deletions src/main/java/com/dasanjos/java/Logger.java

This file was deleted.

32 changes: 22 additions & 10 deletions src/main/java/com/dasanjos/java/RequestHandler.java
Original file line number Diff line number Diff line change
@@ -1,38 +1,50 @@
package com.dasanjos.java;

import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.dasanjos.java.http.HttpRequest;
import com.dasanjos.java.http.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.net.Socket;
import com.dasanjos.java.http.HttpResponse;

/**
* Class <code>RequestHandler</code> - Thread class that answer the requests in the socket
*/
public class RequestHandler implements Runnable {

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

private Socket socket;
private final Socket socket;
private final File webRoot;

public RequestHandler(Socket socket) {
public RequestHandler(Socket socket, File webRoot) {
this.socket = socket;
this.webRoot = webRoot;
}

public void run() {
try {
HttpRequest req = new HttpRequest(socket.getInputStream());
HttpResponse res = new HttpResponse(req);
HttpResponse res = new HttpResponse(req, webRoot);
res.write(socket.getOutputStream());
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Runtime Error", e);
LOGGER.error("Runtime Error", e);
try {
HttpResponse res = new HttpResponse(null, webRoot);
res.fillHeaders(Status._500);
res.fillResponse(Status._500.toString());
res.write(socket.getOutputStream());
} catch (Exception e2) {
LOGGER.error("Error sending 500 response", e2);
}
} finally {
if (socket != null && !socket.isClosed()) {
try {
socket.close();
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error closing socket", e);
LOGGER.warn("Error closing socket", e);
}
}
}
Expand Down
75 changes: 54 additions & 21 deletions src/main/java/com/dasanjos/java/WebServer.java
Original file line number Diff line number Diff line change
@@ -1,50 +1,83 @@
package com.dasanjos.java;

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

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Class <code>WebServer</code> - Main class that starts the Web Server Thread Pool in port 8080 (default)
*/
public class WebServer {

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

private static final int DEFAULT_PORT = 8080;

private static final int N_THREADS = 3;

private final File webRoot;
private volatile boolean running = true;
private ServerSocket serverSocket;
private ExecutorService executor;

public WebServer() {
this(new File("."));
}

public WebServer(File webRoot) {
this.webRoot = webRoot;
}

public static void main(String args[]) {
try {
new WebServer().start(getValidPortParam(args));
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Startup Error", e);
LOGGER.error("Startup Error", e);
System.exit(1);
}
}

public void start(int port) throws IOException {
ServerSocket s = new ServerSocket(port);
LOGGER.log(Level.INFO, "Web server listening on port " + port + "\r\nPress CTRL-C to quit");
final ExecutorService executor = Executors.newFixedThreadPool(N_THREADS);
//Add Graceful Shutdown
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
running = false;
LOGGER.log(Level.INFO, "Shutting down server...");
executor.shutdown();
public void start(int port) throws IOException {
serverSocket = new ServerSocket(port);
LOGGER.info("Web server listening on port {} \r\nPress CTRL-C to quit", getPort());
executor = Executors.newFixedThreadPool(N_THREADS);

// Add Graceful Shutdown
Runtime.getRuntime().addShutdownHook(new Thread(() -> stop()));

while (running) {
try {
executor.submit(new RequestHandler(serverSocket.accept(), webRoot));
} catch (IOException e) {
if (running) {
LOGGER.error("Error accepting connection", e);
}
}
});
while (running) {
executor.submit(new RequestHandler(s.accept()));
}
}
}
}

public void stop() {
running = false;
LOGGER.info("Shutting down server...");
if (executor != null) {
executor.shutdown();
}
try {
if (serverSocket != null && !serverSocket.isClosed()) {
serverSocket.close();
}
} catch (IOException e) {
LOGGER.error("Error closing server socket", e);
}
}

public int getPort() {
return serverSocket != null ? serverSocket.getLocalPort() : -1;
}

/**
* Parse command line arguments (string[] args) for valid port number
Expand All @@ -56,7 +89,7 @@ static int getValidPortParam(String args[]) throws NumberFormatException {
int port = Integer.parseInt(args[0]);
if (port > 0 && port <= 65535) {
if (port < 1024) {
LOGGER.log(Level.WARNING, "Port " + port + " may require administrator privileges");
LOGGER.warn("Port {} may require administrator privileges", port);
}
return port;
} else {
Expand Down
Loading