Monday, August 9, 2010

Multi-threaded HTTP Server

My earlier post on creating a single threaded HTTP/1.0 Server using Java gave an idea about how to implement an HTTP server. This post is an extension to the same program. I have implemented a Multi-threaded version of the same server. The source code is given below.

Implementation
A new thread is spawned upon receiving a connection request from a client. This socket is used to transfer data between these two end-systems. Since the server implements HTTP/1.0, the connection is terminated after the request is satisfied by the server.

The code is available here.

Multi-threaded server

/*
* Coded By: Rahul Krishnan
*/

import java.net.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;

class MTServer implements Runnable {
// Socket available to all the methods in the class.
private Socket connectionSocket;

// Constructor.
MTServer (Socket sock) {
connectionSocket = sock;
}

// Main starts here.
public static void main(String argv[]) throws Exception {
// Listen to the client connection.
ServerSocket listenSocket = new ServerSocket(4444);

// Spawn threads for all the new connections.
while (true) {
Socket connectionSocket = listenSocket.accept();
Thread T = new Thread(new MTServer(connectionSocket));
T.start();
}
}

// Thread code.
public void run() {
String httpReqMsg = null; // Strores the request from client.
String fileName = null; // Stores the file name to be fetched.
BufferedReader inFromClient = null; // Input Stream - from the client.
DataOutputStream outToClient = null;// Output Stream - to client.
FileInputStream inFile = null; // Input Stream - from file.

try {
inFromClient = new BufferedReader(
new InputStreamReader(connectionSocket.getInputStream()));
outToClient = new DataOutputStream(
connectionSocket.getOutputStream());

// Read the request from client.
httpReqMsg = inFromClient.readLine();
}
catch (IOException exception) {
exception.printStackTrace();
}

StringTokenizer tokenizedLine = new StringTokenizer(httpReqMsg);
// Extract the file name.
if (tokenizedLine.nextToken().equals("GET")) {
fileName = tokenizedLine.nextToken();

// Process the file name.
if (fileName.startsWith("/") == true) {
// Extract the portion after '/' in the file name.
fileName = fileName.substring(1);
}

// Access the file.
File file = new File(fileName);
int numOfBytes = (int) file.length();

byte[] fileInBytes = new byte[numOfBytes];

try {
inFile = new FileInputStream(fileName);
}
catch (FileNotFoundException exception) {
exception.printStackTrace();
}

try {
inFile.read(fileInBytes);
}
catch (IOException exception) {
exception.printStackTrace();
}

try {
// Server response message starts.
outToClient.writeBytes("HTTP/1.0 200 OK\r\n");

// Send the content type.
if (fileName.endsWith(".jpg")) {
outToClient.writeBytes("Content-Type: image/jpeg\r\n");
}
if (fileName.endsWith(".gif")) {
outToClient.writeBytes("Content-Type: image/gif\r\n");
}
// Send the content length.
outToClient.writeBytes("Content-Lenght: " + numOfBytes + "\r\n");

// Important : comply with the HTTP reply format.
outToClient.writeBytes("\r\n");
// Write the file into the output stream.
outToClient.write(fileInBytes, 0, numOfBytes);
connectionSocket.close();
}
catch (IOException exception) {
exception.printStackTrace();
}
}
}
}

Sunday, August 1, 2010

Single-threaded HTTP Server

An HTTP Server is an application layer process which accepts and responds to client requests for web objects (pages, images, videos etc.). To explain the process in simple terms, I have included the source code for a basic single threaded server.

HTTP Server
This server will establish TCP connection with the requesting clients. Once done, it can accept "GET" HTTP messages from these clients. The server processes it (parses the request message to extract the file url). This server can accept only jpg and gif file requests. If the file is found in the server, an HTTP response message is generated and send back to the client. After this, the server closes the connection with the client.

Implementation Details
The server is an HTTP 1.0 implementation for it works on non-persistent connection to send and receive data.

I have attached the source code below. You can also access it from here.


/*
* Coded by: Rahul Krishnan
* Description:
* A basic single threaded HTTP server program. It can accept
* GET messages from the clients requesting for jpg and gif
* files. The server then sends the response as HTTP packets.
*/

import java.net.*;
import java.io.*;
import java.util.*;

class WebServer {
public static void main(String argv[]) throws Exception {
String httpReqMsg; // Strores the request from client.
String fileName; // Stores the file name to be fetched.

ServerSocket listenSocket = new ServerSocket(4444);
Socket connectionSocket = listenSocket.accept();

BufferedReader inFromClient = new BufferedReader(
new InputStreamReader(connectionSocket.getInputStream()));

DataOutputStream outToClient = new DataOutputStream(
connectionSocket.getOutputStream());

// Read the request from client.
httpReqMsg = inFromClient.readLine();

StringTokenizer tokenizedLine = new StringTokenizer(httpReqMsg);

// Extract the file name.
if (tokenizedLine.nextToken().equals("GET")) {
fileName = tokenizedLine.nextToken();

// Process the file name.
if (fileName.startsWith("/") == true) {
// Extract the portion after '/' in the file name.
fileName = fileName.substring(1);
}

// Access the file.
File file = new File(fileName);
int numOfBytes = (int) file.length();

byte[] fileInBytes = new byte[numOfBytes];
FileInputStream inFile = new FileInputStream(fileName);

inFile.read(fileInBytes);

// Server response message starts.
outToClient.writeBytes("HTTP/1.0 200 OK\r\n");

// Send the content type.
if (fileName.endsWith(".jpg")) {
outToClient.writeBytes("Content-Type: image/jpeg\r\n");
}
if (fileName.endsWith(".gif")) {
outToClient.writeBytes("Content-Type: image/gif\r\n");
}
// Send the content length.
outToClient.writeBytes("Content-Lenght: " + numOfBytes + "\r\n");

// Important : comply with the HTTP reply format.
outToClient.writeBytes("\r\n");
// Write the file into the output stream.
outToClient.write(fileInBytes, 0, numOfBytes);
connectionSocket.close();
}
else System.out.println("Bad Request Message");
}
}