View Javadoc
1   //******************************************************************************
2   //
3   // File:    HttpServer.java
4   // Package: edu.rit.http
5   // Unit:    Class edu.rit.http.HttpServer
6   //
7   // This Java source file is copyright (C) 2006 by Alan Kaminsky. All rights
8   // reserved. For further information, contact the author, Alan Kaminsky, at
9   // ark@cs.rit.edu.
10  //
11  // This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
12  // software; you can redistribute it and/or modify it under the terms of the GNU
13  // General Public License as published by the Free Software Foundation; either
14  // version 3 of the License, or (at your option) any later version.
15  //
16  // PJ is distributed in the hope that it will be useful, but WITHOUT ANY
17  // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18  // A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19  //
20  // Linking this library statically or dynamically with other modules is making a
21  // combined work based on this library. Thus, the terms and conditions of the GNU
22  // General Public License cover the whole combination.
23  //
24  // As a special exception, the copyright holders of this library give you
25  // permission to link this library with independent modules to produce an
26  // executable, regardless of the license terms of these independent modules, and
27  // to copy and distribute the resulting executable under terms of your choice,
28  // provided that you also meet, for each linked independent module, the terms
29  // and conditions of the license of that module. An independent module is a module
30  // which is not derived from or based on this library. If you modify this library,
31  // you may extend this exception to your version of the library, but you are not
32  // obligated to do so. If you do not wish to do so, delete this exception
33  // statement from your version.
34  //
35  // A copy of the GNU General Public License is provided in the file gpl.txt. You
36  // may also obtain a copy of the GNU General Public License on the World Wide
37  // Web at http://www.gnu.org/licenses/gpl.html.
38  //
39  //******************************************************************************
40  package edu.rit.http;
41  
42  import java.io.IOException;
43  import java.net.InetSocketAddress;
44  import java.net.ServerSocket;
45  import java.net.Socket;
46  
47  import edu.rit.util.Logger;
48  import edu.rit.util.PrintStreamLogger;
49  
50  // For unit test main program
51  // import java.io.PrintWriter;
52  // import java.util.Map;
53  /**
54   * Class HttpServer provides a lightweight HTTP/1.0 server. The HTTP server is
55   * designed to be embedded inside another application.
56   * <P>
57   * When constructed, the HTTP server starts a thread listening for connections
58   * to a given host and port. When a web browser sets up a connection, the HTTP
59   * server calls the <code>process()</code> method to process the request. This is an
60   * abstract method that must be overridden in a subclass. The <code>process()</code>
61   * method's arguments are an {@linkplain HttpRequest} object from which the
62   * method reads the HTTP request message and an {@linkplain HttpResponse} object
63   * to which the method writes the HTTP response message.
64   * <P>
65   * The HTTP server assumes that the <code>process()</code> method will not perform
66   * any lengthy processing and will not block the calling thread. If the HTTP
67   * request requires lengthy processing or requires blocking, the
68   * <code>process()</code> method should spawn a separate thread to process the
69   * request. A thread pool may prove useful; see package java.util.concurrent.
70   * <P>
71   * When a client opens a socket connection to the HTTP server, the server places
72   * a two-second timeout on reading the socket. If the client does not send an
73   * HTTP request message before the timeout, the {@linkplain HttpRequest}'s
74   * <code>isValid()</code> method returns false.
75   *
76   * @author Alan Kaminsky
77   * @version 29-Jul-2010
78   */
79  public abstract class HttpServer {
80  
81  // Hidden data members.
82      private ServerSocket myServerSocket;
83      private AcceptorThread myAcceptorThread;
84      private Logger myLogger;
85  
86  // Hidden helper classes.
87      private class AcceptorThread
88              extends Thread {
89  
90          public void run() {
91              try {
92                  for (;;) {
93                      Socket socket = myServerSocket.accept();
94                      socket.setSoTimeout(2000);
95                      HttpRequest request = new HttpRequest(socket);
96                      HttpResponse response = new HttpResponse(socket);
97                      try {
98                          process(request, response);
99                      } catch (Throwable exc) {
100                         // Any exception while processing a request: Ignore.
101                         myLogger.log("Exception while processing HTTP request",
102                                 exc);
103                     } finally {
104                         try {
105                             if (!socket.isClosed()) {
106                                 socket.close();
107                             }
108                         } catch (Throwable exc) {
109                             // Any exception while closing socket: Ignore.
110                             myLogger.log("Exception while closing HTTP socket",
111                                     exc);
112                         }
113                     }
114                     socket = null;
115                     request = null;
116                     response = null;
117                 }
118             } catch (Throwable exc) {
119                 // Any exception while accepting a connection: Terminate thread.
120                 if (!myServerSocket.isClosed()) {
121                     myLogger.log("Exception while accepting HTTP connection",
122                             exc);
123                 }
124             } finally {
125                 myLogger.log("HTTP server terminating");
126             }
127         }
128     }
129 
130 // Exported constructors.
131     /**
132      * Construct a new HTTP server. The HTTP server will print error messages on
133      * the standard error.
134      *
135      * @param address Host and port to which the HTTP server will listen for
136      * connections.
137      * @exception IOException Thrown if an I/O error occurred.
138      * @throws java.io.IOException if any.
139      */
140     public HttpServer(InetSocketAddress address)
141             throws IOException {
142         this(address, null);
143     }
144 
145     /**
146      * Construct a new HTTP server. The HTTP server will print error messages
147      * using the given logger.
148      *
149      * @param address Host and port to which the HTTP server will listen for
150      * connections.
151      * @param logger Error message logger. If null, the HTTP server will print
152      * error messages on the standard error.
153      * @exception IOException Thrown if an I/O error occurred.
154      * @throws java.io.IOException if any.
155      */
156     public HttpServer(InetSocketAddress address,
157             Logger logger)
158             throws IOException {
159         myLogger = logger == null ? new PrintStreamLogger() : logger;
160         myServerSocket = new ServerSocket();
161         myServerSocket.bind(address);
162         myAcceptorThread = new AcceptorThread();
163         myAcceptorThread.setDaemon(true);
164         myAcceptorThread.start();
165     }
166 
167 // Exported operations.
168     /**
169      * Obtain the host and port to which this HTTP server is listening for
170      * connections.
171      *
172      * @return Host and port.
173      */
174     public InetSocketAddress getAddress() {
175         return (InetSocketAddress) myServerSocket.getLocalSocketAddress();
176     }
177 
178     /**
179      * Close this HTTP server.
180      *
181      * @exception IOException Thrown if an I/O error occurred.
182      * @throws java.io.IOException if any.
183      */
184     public void close()
185             throws IOException {
186         myServerSocket.close();
187     }
188 
189 // Hidden operations.
190     /**
191      * Process the given HTTP request. The <code>process()</code> method must be
192      * overridden in a subclass to read the HTTP request from
193      * <code>theRequest</code> and write the HTTP response to <code>theResponse</code>.
194      *
195      * @param theRequest HTTP request.
196      * @param theResponse HTTP response.
197      * @exception IOException Thrown if an I/O error occurred.
198      * @throws java.io.IOException if any.
199      */
200     protected abstract void process(HttpRequest theRequest,
201             HttpResponse theResponse)
202             throws IOException;
203 
204 // Unit test main program.
205 //	/**
206 //	 * Unit test main program. The program listens for connections to
207 //	 * localhost:8080. The program reads each HTTP request from a web browser
208 //	 * and merely echoes the request data back to the browser.
209 //	 * <P>
210 //	 * Usage: java edu.rit.http.HttpServer
211 //	 */
212 //	public static void main
213 //		(String[] args)
214 //		throws Exception
215 //		{
216 //		HttpServer server =
217 //			new HttpServer (new InetSocketAddress ("localhost", 8080))
218 //				{
219 //				protected void process
220 //					(HttpRequest request,
221 //					 HttpResponse response)
222 //					throws IOException
223 //					{
224 //					if (request.isValid())
225 //						{
226 //						PrintWriter out = response.getPrintWriter();
227 //						out.println ("<HTML>");
228 //						out.println ("<HEAD>");
229 //						out.println ("</HEAD>");
230 //						out.println ("<BODY>");
231 //						out.println ("<UL>");
232 //						out.println ("<LI>");
233 //						out.print   ("Method = <code>\"");
234 //						out.print   (request.getMethod());
235 //						out.println ("\"</code>");
236 //						out.println ("<LI>");
237 //						out.print   ("URI = <code>\"");
238 //						out.print   (request.getUri());
239 //						out.println ("\"</code>");
240 //						out.println ("<LI>");
241 //						out.print   ("Version = <code>\"");
242 //						out.print   (request.getHttpVersion());
243 //						out.println ("\"</code>");
244 //						for (Map.Entry<String,String> entry :
245 //									request.getHeaders())
246 //							{
247 //							out.println ("<LI>");
248 //							out.print   ("Header name = <code>\"");
249 //							out.print   (entry.getKey());
250 //							out.print   ("\"</code>, value = <code>\"");
251 //							out.print   (entry.getValue());
252 //							out.println ("\"</code>");
253 //							}
254 //						out.println ("</UL>");
255 //						out.println ("</BODY>");
256 //						out.println ("</HTML>");
257 //						}
258 //					else
259 //						{
260 //						response.setStatusCode
261 //							(HttpResponse.Status.STATUS_400_BAD_REQUEST);
262 //						PrintWriter out = response.getPrintWriter();
263 //						out.println ("<HTML>");
264 //						out.println ("<HEAD>");
265 //						out.println ("</HEAD>");
266 //						out.println ("<BODY>");
267 //						out.println ("<P>");
268 //						out.println ("400 Bad Request");
269 //						out.println ("<P>");
270 //						out.println ("You idiot.");
271 //						out.println ("</BODY>");
272 //						out.println ("</HTML>");
273 //						}
274 //					response.close();
275 //					}
276 //				};
277 //
278 //		Thread.currentThread().join();
279 //		}
280 }