//******************************************************************************
//
// File: HttpServer.java
// Package: edu.rit.http
// Unit: Class edu.rit.http.HttpServer
//
// This Java source file is copyright (C) 2006 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************
package edu.rit.http;
import edu.rit.util.Logger;
import edu.rit.util.PrintStreamLogger;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
// For unit test main program
// import java.io.PrintWriter;
// import java.util.Map;
/**
* Class HttpServer provides a lightweight HTTP/1.0 server. The HTTP server is
* designed to be embedded inside another application.
*
* When constructed, the HTTP server starts a thread listening for connections
* to a given host and port. When a web browser sets up a connection, the HTTP
* server calls the process() method to process the request. This is an
* abstract method that must be overridden in a subclass. The process()
* method's arguments are an {@linkplain HttpRequest} object from which the
* method reads the HTTP request message and an {@linkplain HttpResponse} object
* to which the method writes the HTTP response message.
*
* The HTTP server assumes that the process() method will not perform
* any lengthy processing and will not block the calling thread. If the HTTP
* request requires lengthy processing or requires blocking, the
* process() method should spawn a separate thread to process the
* request. A thread pool may prove useful; see package java.util.concurrent.
*
* When a client opens a socket connection to the HTTP server, the server places
* a two-second timeout on reading the socket. If the client does not send an
* HTTP request message before the timeout, the {@linkplain HttpRequest}'s
* isValid() method returns false.
*
* @author Alan Kaminsky
* @version 29-Jul-2010
*/
public abstract class HttpServer
{
// Hidden data members.
private ServerSocket myServerSocket;
private AcceptorThread myAcceptorThread;
private Logger myLogger;
// Hidden helper classes.
private class AcceptorThread
extends Thread
{
public void run()
{
try
{
for (;;)
{
Socket socket = myServerSocket.accept();
socket.setSoTimeout (2000);
HttpRequest request = new HttpRequest (socket);
HttpResponse response = new HttpResponse (socket);
try
{
process (request, response);
}
catch (Throwable exc)
{
// Any exception while processing a request: Ignore.
myLogger.log
("Exception while processing HTTP request",
exc);
}
finally
{
try
{
if (! socket.isClosed()) socket.close();
}
catch (Throwable exc)
{
// Any exception while closing socket: Ignore.
myLogger.log
("Exception while closing HTTP socket",
exc);
}
}
socket = null;
request = null;
response = null;
}
}
catch (Throwable exc)
{
// Any exception while accepting a connection: Terminate thread.
if (! myServerSocket.isClosed())
{
myLogger.log
("Exception while accepting HTTP connection",
exc);
}
}
finally
{
myLogger.log ("HTTP server terminating");
}
}
}
// Exported constructors.
/**
* Construct a new HTTP server. The HTTP server will print error messages
* on the standard error.
*
* @param address Host and port to which the HTTP server will listen for
* connections.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public HttpServer
(InetSocketAddress address)
throws IOException
{
this (address, null);
}
/**
* Construct a new HTTP server. The HTTP server will print error messages
* using the given logger.
*
* @param address Host and port to which the HTTP server will listen for
* connections.
* @param logger Error message logger. If null, the HTTP server will
* print error messages on the standard error.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public HttpServer
(InetSocketAddress address,
Logger logger)
throws IOException
{
myLogger = logger == null ? new PrintStreamLogger() : logger;
myServerSocket = new ServerSocket();
myServerSocket.bind (address);
myAcceptorThread = new AcceptorThread();
myAcceptorThread.setDaemon (true);
myAcceptorThread.start();
}
// Exported operations.
/**
* Obtain the host and port to which this HTTP server is listening for
* connections.
*
* @return Host and port.
*/
public InetSocketAddress getAddress()
{
return (InetSocketAddress) myServerSocket.getLocalSocketAddress();
}
/**
* Close this HTTP server.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void close()
throws IOException
{
myServerSocket.close();
}
// Hidden operations.
/**
* Process the given HTTP request. The process() method must be
* overridden in a subclass to read the HTTP request from
* theRequest and write the HTTP response to theResponse.
*
* @param theRequest HTTP request.
* @param theResponse HTTP response.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
protected abstract void process
(HttpRequest theRequest,
HttpResponse theResponse)
throws IOException;
// Unit test main program.
// /**
// * Unit test main program. The program listens for connections to
// * localhost:8080. The program reads each HTTP request from a web browser
// * and merely echoes the request data back to the browser.
// *
// * Usage: java edu.rit.http.HttpServer
// */
// public static void main
// (String[] args)
// throws Exception
// {
// HttpServer server =
// new HttpServer (new InetSocketAddress ("localhost", 8080))
// {
// protected void process
// (HttpRequest request,
// HttpResponse response)
// throws IOException
// {
// if (request.isValid())
// {
// PrintWriter out = response.getPrintWriter();
// out.println ("");
// out.println ("
");
// out.println ("");
// out.println ("");
// out.println ("");
// out.println ("- ");
// out.print ("Method = \"");
// out.print (request.getMethod());
// out.println ("\"");
// out.println ("
- ");
// out.print ("URI = \"");
// out.print (request.getUri());
// out.println ("\"");
// out.println ("
- ");
// out.print ("Version = \"");
// out.print (request.getHttpVersion());
// out.println ("\"");
// for (Map.Entry
entry :
// request.getHeaders())
// {
// out.println ("- ");
// out.print ("Header name = \"");
// out.print (entry.getKey());
// out.print ("\", value = \"");
// out.print (entry.getValue());
// out.println ("\"");
// }
// out.println ("
");
// out.println ("");
// out.println ("");
// }
// else
// {
// response.setStatusCode
// (HttpResponse.Status.STATUS_400_BAD_REQUEST);
// PrintWriter out = response.getPrintWriter();
// out.println ("");
// out.println ("");
// out.println ("");
// out.println ("");
// out.println ("");
// out.println ("400 Bad Request");
// out.println ("
");
// out.println ("You idiot.");
// out.println ("");
// out.println ("");
// }
// response.close();
// }
// };
//
// Thread.currentThread().join();
// }
}
Ads help maintain this website.