Implementing a simple HTTP Server in java for handling POST methods: A File Upload HTTP Server

Sunday, November 2, 2008

Note: An updated version of this post can be found in http://www.prasannatech.net/2009/03/java-http-post-file-upload-server.html which will handle any kind of file uploads (text and binary files) of large size.

In the last section on java, we have seen how to implement a simple/lightweight HTTP/Web Server to handle HTTP GET methods. In this section, we will see how to implement a simple HTTP/Web server in java to handle POST methods with an example of a file upload server using the POST method through a client form data.

The benefit of implementing our own HTTP Server to handle GET/POST methods is that we can use them for some simple/lightweight applications which doesn't require an Apache Web server or a servlet container to be deployed to handle HTTP client requests, also these simple HTTP servers can be used as utility classes which can be integrated in a small scale applications.

The below HTTP POST Server does the following functions.

1. Displays the File upload form when the home page is accessed (http://127.0.0.1:5000), displays error for other invalid GET requests.

2. Uploads a text file through the file upload form and submit it, the end result is that the file will be uploaded in the directory where the HTTPPOSTServer class runs, Note: This code will work only for text files.

3. Handles text files upto 2MB.

I have tested the code in IE 7, Firefox and Chrome (BTW I needed to change the code a lot to make it IE compatible). The lesson learned, always test your code in IE first to avoid disappointments as its likely to give you some tough time the other way around (nothing against IE though).

So here's the code.

Listing 1: HTTPPOSTServer.java

  1 /*
2 * HTTPPOSTServer.java
3 * Author: S.Prasanna
4 * @version 1.00
5 */
6
7 import java.io.*;
8 import java.net.*;
9 import java.util.*;
10
11 public class HTTPPOSTServer extends Thread {
12
13 static final String HTML_START =
14 "<html>" +
15 "<title>HTTP POST Server in java</title>" +
16 "<body>";
17
18 static final String HTML_END =
19 "</body>" +
20 "</html>";
21
22 Socket connectedClient = null;
23 BufferedReader inFromClient = null;
24 DataOutputStream outToClient = null;
25
26
27 public HTTPPOSTServer(Socket client) {
28 connectedClient = client;
29 }
30
31 public void run() {
32
33 String currentLine = null, postBoundary = null, contentength = null, filename = null, contentLength = null;
34 PrintWriter fout = null;
35
36 try {
37
38 System.out.println( "The Client "+
39 connectedClient.getInetAddress() + ":" + connectedClient.getPort() + " is connected");
40
41 inFromClient = new BufferedReader(new InputStreamReader (connectedClient.getInputStream()));
42 outToClient = new DataOutputStream(connectedClient.getOutputStream());
43
44 currentLine = inFromClient.readLine();
45 String headerLine = currentLine;
46 StringTokenizer tokenizer = new StringTokenizer(headerLine);
47 String httpMethod = tokenizer.nextToken();
48 String httpQueryString = tokenizer.nextToken();
49
50 System.out.println(currentLine);
51
52 if (httpMethod.equals("GET")) {
53 System.out.println("GET request");
54 if (httpQueryString.equals("/")) {
55 // The default home page
56 String responseString = HTTPPOSTServer.HTML_START +
57 "<form action=\"http://127.0.0.1:5000\" enctype=\"multipart/form-data\"" +
58 "method=\"post\">" +
59 "Enter the name of the File <input name=\"file\" type=\"file\"><br>" +
60 "<input value=\"Upload\" type=\"submit\"></form>" +
61 "Upload only text files." +
62 HTTPPOSTServer.HTML_END;
63 sendResponse(200, responseString , false);
64 } else {
65 sendResponse(404, "<b>The Requested resource not found ...." +
66 "Usage: http://127.0.0.1:5000</b>", false);
67 }
68 }
69 else { //POST request
70 System.out.println("POST request");
71 do {
72 currentLine = inFromClient.readLine();
73
74 if (currentLine.indexOf("Content-Type: multipart/form-data") != -1) {
75 String boundary = currentLine.split("boundary=")[1];
76 // The POST boundary
77
78 while (true) {
79 currentLine = inFromClient.readLine();
80 if (currentLine.indexOf("Content-Length:") != -1) {
81 contentLength = currentLine.split(" ")[1];
82 System.out.println("Content Length = " + contentLength);
83 break;
84 }
85 }
86
87 //Content length should be < 2MB
88 if (Long.valueOf(contentLength) > 2000000L) {
89 sendResponse(200, "File size should be < 2MB", false);
90 }
91
92 while (true) {
93 currentLine = inFromClient.readLine();
94 if (currentLine.indexOf("--" + boundary) != -1) {
95 filename = inFromClient.readLine().split("filename=")[1].replaceAll("\"", "");
96 String [] filelist = filename.split("\\" + System.getProperty("file.separator"));
97 filename = filelist[filelist.length - 1];
98 System.out.println("File to be uploaded = " + filename);
99 break;
100 }
101 }
102
103 String fileContentType = inFromClient.readLine().split(" ")[1];
104 System.out.println("File content type = " + fileContentType);
105
106 inFromClient.readLine(); //assert(inFromClient.readLine().equals("")) : "Expected line in POST request is "" ";
107
108 fout = new PrintWriter(filename);
109 String prevLine = inFromClient.readLine();
110 currentLine = inFromClient.readLine();
111
112 //Here we upload the actual file contents
113 while (true) {
114 if (currentLine.equals("--" + boundary + "--")) {
115 fout.print(prevLine);
116 break;
117 }
118 else {
119 fout.println(prevLine);
120 }
121 prevLine = currentLine;
122 currentLine = inFromClient.readLine();
123 }
124
125 sendResponse(200, "File " + filename + " Uploaded..", false);
126 fout.close();
127 } //if
128 }while (inFromClient.ready()); //End of do-while
129 }//else
130 } catch (Exception e) {
131 e.printStackTrace();
132 }
133 }
134
135 public void sendResponse (int statusCode, String responseString, boolean isFile) throws Exception {
136
137 String statusLine = null;
138 String serverdetails = "Server: Java HTTPServer";
139 String contentLengthLine = null;
140 String fileName = null;
141 String contentTypeLine = "Content-Type: text/html" + "\r\n";
142 FileInputStream fin = null;
143
144 if (statusCode == 200)
145 statusLine = "HTTP/1.1 200 OK" + "\r\n";
146 else
147 statusLine = "HTTP/1.1 404 Not Found" + "\r\n";
148
149 if (isFile) {
150 fileName = responseString;
151 fin = new FileInputStream(fileName);
152 contentLengthLine = "Content-Length: " + Integer.toString(fin.available()) + "\r\n";
153 if (!fileName.endsWith(".htm") && !fileName.endsWith(".html"))
154 contentTypeLine = "Content-Type: \r\n";
155 }
156 else {
157 responseString = HTTPPOSTServer.HTML_START + responseString + HTTPPOSTServer.HTML_END;
158 contentLengthLine = "Content-Length: " + responseString.length() + "\r\n";
159 }
160
161 outToClient.writeBytes(statusLine);
162 outToClient.writeBytes(serverdetails);
163 outToClient.writeBytes(contentTypeLine);
164 outToClient.writeBytes(contentLengthLine);
165 outToClient.writeBytes("Connection: close\r\n");
166 outToClient.writeBytes("\r\n");
167
168 if (isFile) sendFile(fin, outToClient);
169 else outToClient.writeBytes(responseString);
170
171 outToClient.close();
172 }
173
174 public void sendFile (FileInputStream fin, DataOutputStream out) throws Exception {
175 byte[] buffer = new byte[1024] ;
176 int bytesRead;
177
178 while ((bytesRead = fin.read(buffer)) != -1 ) {
179 out.write(buffer, 0, bytesRead);
180 }
181 fin.close();
182 }
183
184 public static void main (String args[]) throws Exception {
185
186 ServerSocket Server = new ServerSocket (5000, 10, InetAddress.getByName("127.0.0.1"));
187 System.out.println ("HTTP Server Waiting for client on port 5000");
188
189 while(true) {
190 Socket connected = Server.accept();
191 (new HTTPPOSTServer(connected)).start();
192 }
193 }
194 }
195

Working:

The working of the code is pretty simple, the HTTP server checks for a GET or POST method, and if its a GET method (line 53) for the home page, it displays the file upload form through which the user can upload the file, in case of any other invalid GET requests other than home page, an error message is displayed (line 65).

Line 70 handles POST request and checks the content type (line 74), POST boundary (line 75), content length (line 81) and the file name (line 98), once these values are parsed, the file is uploaded successfully (lines 108 - 126) provided the file length is 88).

I have commented out some assertions which should be true when parsing the POST request string, but then the code will only work when assertions were enabled (using java -ea HTTPPOSTServer), which in general is not a good practice, therefore if you want to learn more about how HTTP POST requests, add your own assertions while parsing the POST request when you expect something in the request line which should match a particular string.

11 comments:

christianjohnsen said...

great work, cheers!

Prasanna Seshadri said...

Thanks a lot Chris!

test.optima said...

Great work!

I was wondering if the do-while loop in the POST processing can be replaced with an if instead or can it even be removed as the input stream should be in ready state.

Prasanna Seshadri said...

Thanks for your inputs, I added the do-while to check if the input stream is in ready state using inFromClient.ready(), otherwise the server waits in the readLine() method forever after processing the POST query, so I used it as a termination condition.

Anyhow I guess it can be replaced by a better construct and I will work on that when time permits.

Anonymous said...

can this be modified to accept images? :/

Prasanna Seshadri said...

Hi,

The above code only works for text files, since images are binary files a different version is required and a different java I/O stream is required for that and I am planning to post that code in sometime as well.

However, I have developed a file upload servlet using jython servlets which will work for any kind of files, do look at it if you can get something from it here

http://www.prasannatech.net/2008/06/server-side-programming-in-jython.html

Look for the file upload section there.

Anonymous said...

if it's not too complicated i'd be pleased if you could give a hint on the solution to use it with images - i'd like to use it for a little project in schoool (we're not allowed to use servlets or something like that)

Prasanna Seshadri said...

Hi,

I could have easily posted the solution which would also handle binary files by replacing the BufferedReader above with DataInputStream, but the problem is that the DataInputStream's readLine method is deprecated http://java.sun.com/j2se/1.5.0/docs/api/java/io/DataInputStream.html#readLine().

Therefore I need to implement a solution which cannot use readLine, otherwise its quite straightforward.

So if you want a quick solution, go with DataInputStream instead of BufferedReader to handle binary files like images.

Anonymous said...

The code doesn't go on recent chrome and mozilla, why?????

Prasanna Seshadri said...

Note: This code was supposed to be running as a standalone java server.

jasonwupilly said...

this is a really helpful blog post! I've been looking for this everywhere


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.