Implementing a simple HTTP Server in Java to handle GET methods

Saturday, October 4, 2008

Implementing a simple HTTP Server in java using sockets is an useful application, which I thought would be worth sharing here. When I was implementing my own search engine, I always needed a light weight socket HTTP Server which I can bundle with my package and every time you need a HTTP/Web server, you need not to go for Apache Web Server which would be an overkill for simple applications which serves only static content, say a Google desktop search application where you just need a HTML user interface served through a simple HTTP Server.

In this HTTP Server we will see

1. How to process HTTP requests
2. How to handle HTTP GET methods
3. How to create multiple threads of control to handle each HTTP client

The below HTTP Server prints the client HTTP request in the home page, serves any valid files requested through GET method, otherwise prints a HTTP 404 page not found error message, lets look at the code and a simple explanation.


/*
* myHTTPServer.java
* Author: S.Prasanna
* @version 1.00
*/

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

public class myHTTPServer extends Thread {

static final String HTML_START =
"<html>" +
"<title>HTTP Server in java</title>" +
"<body>";

static final String HTML_END =
"</body>" +
"</html>";

Socket connectedClient = null;
BufferedReader inFromClient = null;
DataOutputStream outToClient = null;


public myHTTPServer(Socket client) {
connectedClient = client;
}

public void run() {

try {

System.out.println( "The Client "+
connectedClient.getInetAddress() + ":" + connectedClient.getPort() + " is connected");

inFromClient = new BufferedReader(new InputStreamReader (connectedClient.getInputStream()));
outToClient = new DataOutputStream(connectedClient.getOutputStream());

String requestString = inFromClient.readLine();
String headerLine = requestString;

StringTokenizer tokenizer = new StringTokenizer(headerLine);
String httpMethod = tokenizer.nextToken();
String httpQueryString = tokenizer.nextToken();

StringBuffer responseBuffer = new StringBuffer();
responseBuffer.append("<b> This is the HTTP Server Home Page.... </b><BR>");
responseBuffer.append("The HTTP Client request is ....<BR>");

System.out.println("The HTTP request string is ....");
while (inFromClient.ready())
{
// Read the HTTP complete HTTP Query
responseBuffer.append(requestString + "<BR>");
System.out.println(requestString);
requestString = inFromClient.readLine();
}

if (httpMethod.equals("GET")) {
if (httpQueryString.equals("/")) {
// The default home page
sendResponse(200, responseBuffer.toString(), false);
} else {
//This is interpreted as a file name
String fileName = httpQueryString.replaceFirst("/", "");
fileName = URLDecoder.decode(fileName);
if (new File(fileName).isFile()){
sendResponse(200, fileName, true);
}
else {
sendResponse(404, "<b>The Requested resource not found ...." +
"Usage: http://127.0.0.1:5000 or http://127.0.0.1:5000/</b>", false);
}
}
}
else sendResponse(404, "<b>The Requested resource not found ...." +
"Usage: http://127.0.0.1:5000 or http://127.0.0.1:5000/</b>", false);
} catch (Exception e) {
e.printStackTrace();
}
}

public void sendResponse (int statusCode, String responseString, boolean isFile) throws Exception {

String statusLine = null;
String serverdetails = "Server: Java HTTPServer";
String contentLengthLine = null;
String fileName = null;
String contentTypeLine = "Content-Type: text/html" + "\r\n";
FileInputStream fin = null;

if (statusCode == 200)
statusLine = "HTTP/1.1 200 OK" + "\r\n";
else
statusLine = "HTTP/1.1 404 Not Found" + "\r\n";

if (isFile) {
fileName = responseString;
fin = new FileInputStream(fileName);
contentLengthLine = "Content-Length: " + Integer.toString(fin.available()) + "\r\n";
if (!fileName.endsWith(".htm") && !fileName.endsWith(".html"))
contentTypeLine = "Content-Type: \r\n";
}
else {
responseString = myHTTPServer.HTML_START + responseString + myHTTPServer.HTML_END;
contentLengthLine = "Content-Length: " + responseString.length() + "\r\n";
}

outToClient.writeBytes(statusLine);
outToClient.writeBytes(serverdetails);
outToClient.writeBytes(contentTypeLine);
outToClient.writeBytes(contentLengthLine);
outToClient.writeBytes("Connection: close\r\n");
outToClient.writeBytes("\r\n");

if (isFile) sendFile(fin, outToClient);
else outToClient.writeBytes(responseString);

outToClient.close();
}

public void sendFile (FileInputStream fin, DataOutputStream out) throws Exception {
byte[] buffer = new byte[1024] ;
int bytesRead;

while ((bytesRead = fin.read(buffer)) != -1 ) {
out.write(buffer, 0, bytesRead);
}
fin.close();
}

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

ServerSocket Server = new ServerSocket (5000, 10, InetAddress.getByName("127.0.0.1"));
System.out.println ("TCPServer Waiting for client on port 5000");

while(true) {
Socket connected = Server.accept();
(new myHTTPServer(connected)).start();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

Explanation:

The code is pretty simple, in main (lines 133 - 143), we accept new connections and invoke a thread on the newly created HTTPServer instance which will process the request.

In Line 53, while (inFromClient.ready()) loops till all HTTP request parameters are recieved, this means reading all lines in the input HTTP request are read, the ready function of BufferedReader tells whether the stream is ready to be read or not, therefore we process the HTTP request from the client till the stream is ready thereby ensuring that we get the complete request available for processing, including all request headers.

In Line 67, we use fileName = URLDecoder.decode(fileName) to decode the request GET method URL, most HTTP coding errors happen because the input HTTP request is not URLDecoded as a result we won't get the correct parameters to process, therefore one needs to URLDecode the input request string.

Other things are straightforward, we print the HTTP GET Request headers in the homepage, else the requested file is served, else a 404 Page not found error is output if the input GET request is not a valid file in the file system.

Output:

1. http://127.0.0.1:5000 should print the home page with the HTTP request parameters
2. http://127.0.0.1:5000/myHTTPServer.java would download the java source file, provided you run the java class file in the same location or try any other file in the filesystem by giving the absolute path, that would be downloaded.
3. http://127.0.0.1:5000/invalidfile would result in HTTP 404 file not found error.

We will see how to handle HTTP POST methods in future with an example of a file upload HTTP server.

13 comments:

RED Patents Online said...

Nice work! Although a much simpler way of doing it is to leverage the Jetty APIs to implement all this functionality for free. An example of this if given here: http://gluga.com/programming/embedding-jetty-web-server/

This will significantly reduce your coding efforts and give you much greater flexibility for extending the functionality of your application in future maintenance.

All depends on your needs though. The above solution is a nice simple stand-alone implementation.

Prasanna Seshadri said...

Hi,

Thanks for your comments and suggestions,I looked at Jetty APIs, looks cool, the example code illustrated there was also very effective.

This code I implemented to have some utility HTTP Class for one one of my project, I have also implemented a stand alone file upload server to handle POST requests (http://www.prasannatech.net/2008/11/http-web-server-java-post-file-upload.html), in future I will look in to the Jetty API for these kind of things.

ginko said...

To get the maximum out of this tutorial it is recommended that you have a basic grasp on how HTTP works. If the little man inside your head says what tha H is HTTP, you should go to the bottom and check some of the pointers. There you will find some nice text to explain the basic concept.

Vaibhav said...

Hello sir!
Im a newbie to Java, how does the client side program contact with this server? I mean let's say i have a form and i need to send that info to the server and get a page back front he server. where do I store the response page?in my system?

Matteo Cappelli aka roghan said...

It's a good example of programming! I understand as an HTTP server creates the response...

bye!

eteles said...

Just wanted to say thanks. Is is blogs like these that just simplify things and spread some light on those dark programming days :)

Kuan Yeah said...

Thank you so much

Cisco Trainer said...

Dear Sir,

Thanks a lot for finding time to educate others...

I am a networking trainer, but curious to know how this work..

The program starts from public static void main() and create an instance of ServerSocket class (I remember ServerSocket class belongs to java.net package). Then it create an infinite loop.

Could you please explain what is Socket class? Is is from java.io package?

After that an instance of myHTTPServer is created and the constructor is called. From there I am not able to follow.

Request you to kindly explain.

How can we implement an HTTP client? Any built-in classes available? This is just to create a program to download files using HTTP.

Thanks a lot

Prashant Srivastava said...

i want to use it with webservice

a cilent is calling some webservice and wenservice need to start this server

I am getting socket write error

PR said...

Very Good !!

Deepak said...

I need to decrypt a video from this..
can u please explain me how to use this code for my purpose..

Anonymous said...

So, why does this give a null pointer exception and what can I do to stop it?

Anonymous said...

The Client /127.0.0.1:52549 is connected
The Client /127.0.0.1:52550 is connected
The HTTP request string is ....
GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
java.lang.NullPointerException
at java.util.StringTokenizer.(StringTokenizer.java:182)
at java.util.StringTokenizer.(StringTokenizer.java:219)
at MITM.myHTTPServer.run(myHTTPServer.java:35)


Copyright © 2008 Prasanna Seshadri, www.prasannatech.net, All Rights Reserved.
No part of the content or this site may be reproduced without prior written permission of the author.