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 }