//
// Multithreaded Java WebServer
// (C) 2001 Anders Gidenstam
// (based on a lab in Computer Networking: ..)
//
import java.io.*;
import java.net.*;
import java.util.*;
import java.net.InetAddress.*;
import java.text.SimpleDateFormat;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public final class WebServer
{
public static void main(String argv[]) throws Exception
{
// Set port number
int port = 7000;
// Establish the listening socket
ServerSocket serverSocket = new ServerSocket(port);
WebServer.log("Port number is: " + serverSocket.getLocalPort());
// Wait for and process HTTP service requests
while (true) {
// Wait for TCP connection
Socket requestSocket = serverSocket.accept();
requestSocket.setSoLinger(true, 5);
// Create an object to handle the request
HttpRequest request = new HttpRequest(requestSocket);
//request.run()
// Create a new thread for the request
Thread thread = new Thread(request);
// Start the thread
thread.start();
}
}
final static void log(String s)
{
System.out.println("LOG: " + s.trim());
}
final static void errLog(String s)
{
System.err.println("ERR: " + s.trim());
}
final static void errLog(Exception e)
{
e.printStackTrace();
}
}
final class HttpRequest implements Runnable
{
// Constants
final static String HTTP_VERSION = "HTTP/1.0";
final static String CRLF = "\r\n";
// Recognized HTTP methods
final static class HttpMethod
{
final static String GET = "GET";
final static String HEAD = "HEAD";
final static String POST = "POST";
}
final static class HttpResponse
{
final static String OK = HTTP_VERSION + " 200 OK" + CRLF;
final static String NOT_MODIFIED = HTTP_VERSION + " 304 Not Modified" + CRLF;
final static String NOT_FOUND = HTTP_VERSION + " 404 Not Found" + CRLF;
final static String BAD_REQUEST = HTTP_VERSION + " 400 Bad Request" + CRLF;
final static String INTERNAL_SERVER_ERROR = HTTP_VERSION + " 500 Internal Server Error" + CRLF;
final static String NOT_IMPLEMENTED = HTTP_VERSION + " 501 Not Implemented" + CRLF;
}
private Socket socket;
private DataOutputStream outs;
private BufferedReader br;
// Constructor
public HttpRequest(Socket socket) throws Exception
{
this.socket = socket;
this.outs = new DataOutputStream(this.socket.getOutputStream());
this.br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
// Implements the run() method of the Runnable interface
public void run()
{
try {
processRequest();
} catch (Exception e) {
WebServer.errLog(e);
try {
DataOutputStream outs = new DataOutputStream(socket.getOutputStream());
outs.writeChars(HttpResponse.INTERNAL_SERVER_ERROR);
outs.writeChars(CRLF);
outs.writeChars("
500 Internal Server Error
");
} catch (Exception e2) {
WebServer.errLog(e2);
}
} finally {
// Close streams and sockets
try {
br.close();
outs.close();
socket.close();
} catch (Exception e3) {
WebServer.errLog(e3);
}
}
}
// Process a HTTP request
private void processRequest() throws Exception
{
// Get the request line of the HTTP request
String requestLine = br.readLine();
// get headers
String h = null;
Map headers = new HashMap();
while (true) {
h = br.readLine();
if (h.length() == 0) break;
String[] a = h.split(":", 2);
if (a != null && a.length == 2 && a[0] != null && a[0].length() > 0 && a[1] != null && a[1].length() > 1) {
headers.put(a[0].toLowerCase().trim(), a[1].trim());
}
}
if (requestLine == null) {
WebServer.errLog("Request is empty");
return;
}
// Display the request line
WebServer.log("[Request] " + requestLine);
// split on ASCII 32
String[] tokens = requestLine.split(" ");
String request = tokens[0];
boolean isAHTTPVersion = false;
if (tokens.length == 3 && tokens[2].length() > 7) {
Pattern pattern = Pattern.compile("HTTP/\\d\\.\\d");
Matcher matcher = pattern.matcher(tokens[2]);
isAHTTPVersion = matcher.find();
}
if(tokens.length != 3 || tokens[0].length() == 0 || tokens[1].length() == 0 || tokens[2].length() == 0 || !isAHTTPVersion) {
WebServer.errLog("Wrong number of arguments in request!");
WebServer.errLog(HttpResponse.BAD_REQUEST);
sendHeader(HttpResponse.BAD_REQUEST);
sendText("400 Bad Request
");
} else if(!tokens[2].equals("HTTP/1.0")) {
WebServer.errLog(HttpResponse.NOT_IMPLEMENTED);
sendHeader(HttpResponse.NOT_IMPLEMENTED);
sendText("501 Not Implemented
");
} else if(tokens[1].charAt(0) != '/') {
WebServer.errLog(HttpResponse.BAD_REQUEST);
sendHeader(HttpResponse.BAD_REQUEST);
sendText("400 Bad Request
");
} else if((request.equals(HttpMethod.GET) || request.equals(HttpMethod.HEAD)) && tokens[2].equals(HTTP_VERSION)) {
FileInputStream filein;
try {
File f = new File("." + tokens[1]);
// check if the file has been modified since
boolean modifiedSince = true;
if (headers.containsKey("if-modified-since")) {
String dateString = headers.get("if-modified-since");
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
Date date = format.parse(dateString);
Date modified = new Date(f.lastModified());
modifiedSince = modified.after(date);
}
if (!modifiedSince) {
WebServer.log(HttpResponse.NOT_MODIFIED);
sendHeader(HttpResponse.NOT_MODIFIED);
sendText("304 Not Modified
");
} else {
filein = new FileInputStream(f);
WebServer.log(HttpResponse.OK);
sendHeader(HttpResponse.OK, f);
if(request.equals(HttpMethod.GET)) {
sendBytes(filein, outs);
}
}
} catch (FileNotFoundException e) {
WebServer.errLog(HttpResponse.NOT_FOUND);
sendHeader(HttpResponse.NOT_FOUND);
sendText("404 Not Found
");
}
} else if(request.equals(HttpMethod.POST)) {
WebServer.errLog(HttpResponse.NOT_IMPLEMENTED);
sendHeader(HttpResponse.NOT_IMPLEMENTED);
sendText("501 Not Implemented
");
} else {
WebServer.errLog(HttpResponse.BAD_REQUEST);
sendHeader(HttpResponse.BAD_REQUEST);
sendText("400 Bad Request
");
}
}
private void sendHeader(final String r, File f)
{
String response = r.toString();
response += "Date: " + new Date().toString() + CRLF;
response += "Server: Labbserver" + CRLF;
response += "Allow: " + HttpMethod.GET + " " + HttpMethod.HEAD + CRLF;
if (f != null) {
response += "Content-Length: " + f.length() + CRLF;
response += "Content-Type: " + contentType(f.getName()) + CRLF;
response += "Last-Modified: " + new Date(f.lastModified()).toString() + CRLF;
} else {
// default content type
response += "Content-Type: text/html" + CRLF;
}
response += CRLF;
try {
outs.writeChars(response);
} catch(Exception e) {
WebServer.errLog(e);
}
}
private void sendHeader(final String r)
{
sendHeader(r, null);
}
private void sendText(String s)
{
try {
outs.writeChars(s);
} catch(Exception e) {
WebServer.errLog(e);
}
}
private static void sendBytes(FileInputStream fins, OutputStream outs) throws Exception
{
// Coopy buffer
byte[] buffer = new byte[1024];
int bytes = 0;
while ((bytes = fins.read(buffer)) != -1) {
outs.write(buffer, 0, bytes);
}
}
private static String contentType(String fileName)
{
if (fileName.toLowerCase().endsWith(".htm") || fileName.toLowerCase().endsWith(".html")) {
return "text/html";
} else if (fileName.toLowerCase().endsWith(".gif")) {
return "image/gif";
} else if (fileName.toLowerCase().endsWith(".png")) {
return "image/png";
} else if (fileName.toLowerCase().endsWith(".jpg") || fileName.toLowerCase().endsWith(".jpeg")) {
return "image/jpeg";
} else {
return "application/octet-stream";
}
}
}