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 }