Assignment 

Objectives

The object of this exercise is to write a multi-threaded HTTP server, showing both your understanding of the networking API and also of the programming techniques required to leverage that API effectively.

There are three small exercises which do not count for marks directly, but which are intended to help you prepare for the main element of the assignment.  You do not have to do these exercises, but you are assumed to have a full understanding of their content.

Environment and tooling

These exercises should all be written in (preferably) C or (if you cannot program in C) Java.  They should all run, unmodified, on the school’s Linux teaching systems.  If you develop on another platform, you should confirm that your code builds and runs on the school Linux machines.

Your program should access the socket interface directly.  For C this is straightforward; for Java, you should use java.net.Socket, not one of the more abstract networking classes.

If you work in Java, your program should run from the command line using the java command; it is not sufficient to submit an Eclipse project and expect the marker to figure out how to run it.   You should include a Makefile whose all target uses the javac command to compile your code.

If you work in C, there should be a Makefile whose all target builds all the required programs.

Preparatory Exercises

  1. Write a program which accepts a single incoming connection, using listen()and accept(), and displays all received data on the screen.  You can test this program using telnet or netcat.  Once you have completed this task, swap your code with a colleague and have them test it; they should give you a one or two paragraph report on its function and the code itself.
  2. Extend this program so that it will accept multiple connections, in parallel, and display all received traffic on the screen.  This will involve the use of pthreads, for C, or you can if you wish use fork().   For Java, you will need to useimplements Runnableextends Thread or some other threading mechanism.  Again, have a colleague (ideally a different one) evaluate your code.
  3. Extend this program so that instead of just displaying what is sent to it, it instead parses an HTTP request and request header (you will need to read RFC 2616 for the full details) and displays the broken-out fields on the screen.  You should be able to test this with wgetor curl: the client will show errors when your program closes the connection without returning any results, but your program should produce correct output on the screen.  Again, have a colleague (and, again, a different one) evaluate your code.

If requested I can set Canvas up to handle this peer feedback, but I would prefer you to actually meet and talk to people!

Main Exercise(This is the assignment )

You should write a web server which interacts correctly with a web browser, serving content from either the filesystem or a small database.  It should provide an index page when visited with a null URI, and then correctly handle subsequent requests arises from clicking on the items in the index.

The program should be correctly multi-threaded, and handle multiple clients connecting rapidly in parallel.

Extra marks are available for implementing any of compression, encryption with SSL or the ability to fetch byte-ranges of resources.

You should document your code at a high level (at most three pages) and provide a short diary (again, at most three pages) describing your experiences and the challenges you encountered.  You should also write briefly about what you would do differently were you to repeat the exercise.

Solution 

Server.java

import java.io.*;

import java.net.ServerSocket;

import java.net.Socket;

import java.nio.file.Files;

import java.util.Scanner;

public class Server {

private static final String SERVER_PUBLIC_DIR_name = “www”;

public static void main(String[] args) throws Exception {

int port = 1989; // port of the server

ServerSocket serverSocket = new ServerSocket(port); // start server socket using the specific port

System.out.println(“Server started”);

System.out.println(“URL http://localhost:” + port);

while (true) { // infinite loop to recive clients requests until the server stop

Socket clientSocket = serverSocket.accept(); // accept server connection

new ServerThread(clientSocket).start();

}

}

private static File getFileByURL(String url) {

url = removeQueryString(url);

if (isEmpty(url)) { // if the URL is empty return index.html

return getFile(“/index.html”);

} else {

File file = getFile(url);

if (file == null) { // if the file dosent exist return 404 page

return getFile(“/404.html”);

} else if (file.isDirectory()) {

// if the demanded file is directory return the index.html file inside this directory

return url.endsWith(“/”) ? getFile(url + “index.html”) : getFile(url + “/index.html”);

} else {

return file;

}

}

}

private static String removeQueryString(String url) {

if (url.contains(“?”))

return url.split(“\\?”)[0];

else

return url;

}

private static File getFile(String s) {

File file = new File(SERVER_PUBLIC_DIR_name + s);

if (file.exists())

return file;

else

return null;

}

private static boolean isEmpty(String url) {

return url.isEmpty() || url.equals(“/”);

}

private static class ServerThread extends Thread {

private Socket clientSocket;

public ServerThread(Socket clientSocket) {

this.clientSocket = clientSocket;

}

@Override

public void run() {

try {

BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); // create the  BuffredReader to get clients informations

PrintStream out = new PrintStream(new BufferedOutputStream(

clientSocket.getOutputStream()));

String urlAndMethod = in.readLine(); // read the first line which contain url and the request method and the protocol for example (GET /index.html HTTP2)

String url = “/index.html”; // init the url with index.html the file which is render if the url is empty

String[] urlAndMethodSplited; // split the first line to get the URL

urlAndMethodSplited = urlAndMethod.split(” “);

url = urlAndMethodSplited[1]; // the url

System.out.println(“REQUEST : ” + url);

File file = getFileByURL(url); // get the file which is demanded in the url

FileInputStream fileInputStream = new FileInputStream(file);

// print response headers

out.print(“HTTP/1.0 200 OK\r\n”);

out.print(“Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n”);

out.print(“Server: Apache/0.8.4\r\n”);

out.print(“Content-Type: ” + Files.probeContentType(file.toPath()) + “\r\n”);

out.print(“Content-Length: ” + file.length() + “\r\n”);

out.print(“Expires: Sat, 01 Jan 2000 00:59:59 GMT\r\n”);

out.print(“Last-modified: Fri, 09 Aug 1996 14:21:40 GMT\r\n”);

out.print(“\r\n”);

// write the content of the File in the response

byte[] a = new byte[(int) file.length()];

int n;

while ((n = fileInputStream.read(a)) > 0)

out.write(a, 0, n);

out.close();

out.close();

in.close();

clientSocket.close();

System.out.println(“RESPONSE : 200”);

} catch (IOException ignored) {

}

}

}

}