package context.arch.comm.protocol; import java.net.Socket; import java.io.BufferedReader; import java.io.StringReader; import java.io.InputStreamReader; import java.util.StringTokenizer; import java.util.Date; import java.io.IOException; import context.arch.comm.CommunicationsServer; import context.arch.comm.CommunicationsObject; import context.arch.comm.DataObject; /** * This class subclasses TCPServerSocket, listening for and handling HTTP requests. * It implements the CommunicationsServer interface * * @see context.arch.comm.protocol.TCPServerSocket * @see context.arch.comm.CommunicationsServer */ public class HTTPServerSocket extends TCPServerSocket implements CommunicationsServer { /** * Debug flag. Set to true to see debug messages. */ public static boolean DEBUG = false; /** * Default port for HTTP communications is 80 */ public static final int DEFAULT_PORT = 80; /** * The protocol being used is HTTP */ public static final String PROTOCOL = "HTTP"; /** * HTTP GET request type */ public static final String GET = "GET"; /** * HTTP POST request type */ public static final String POST = "POST"; private CommunicationsObject commObject; /** * Basic constructor for HTTPServerSocket that calls TCPServerSocket * * @param object Handle of the generic instantiating communications object * @see #DEFAULT_PORT * @see context.arch.comm.protocol.TCPServerSocket */ public HTTPServerSocket(CommunicationsObject object) { super(DEFAULT_PORT); commObject = object; } /** * Constructor for HTTPServerSocket that calls TCPServerSocket with the * given port * * @param object Handle of the generic instantiating communications object * @param port Port to use to receive communications on * @see context.arch.comm.protocol.TCPServerSocket */ public HTTPServerSocket(CommunicationsObject object, Integer port) { super(port.intValue()); commObject = object; } /** * Stub method that handles incoming HTTP requests. It calls the equivalent * method in the CommunicationsObject and then closes the socket. * * @param dataSocket Socket to receive HTTP data from * @see context.arch.comm.CommunicationsObject#handleIncomingRequest(java.net.Socket) */ public void handleIncomingRequest(Socket dataSocket) { commObject.handleIncomingRequest(dataSocket); try { dataSocket.close(); } catch (IOException ioe) { System.out.println("Couldn't close socket: "+ioe); } } /** * Method that takes a reply message and adds the necessary HTTP protocol * * @param data Reply to a received request * @return the reply with the added HTTP protocol * @exception context.arch.comm.protocol.ProtocolException if the protocol * can not be added */ public String addReplyProtocol(String data) throws ProtocolException { StringBuffer sb = new StringBuffer(); sb.append("HTTP/1.0 200 OK\r\n"); Date now = new Date(); sb.append("Date: "+now+"\r\n"); sb.append("Server: context/1.0\r\n"); sb.append("Content-type: text/xml\r\n"); sb.append("Content-length: "+data.length()+"\r\n\r\n"); // AKD added sb.append(data); //commObject.println("\nHTTPServerSocket addReplyProtocol:\n"+sb.toString()); return (sb.toString()); } /** * Method that strips the HTTP protocol from a request message. This only * deals with GET and POST headers. If any other header is received, it will * throw a ProtocolException * * @param data Socket the request is coming from * @return the request with the HTTP protocol stripped away * @exception context.arch.comm.protocol.ProtocolException if the protocol * can not be stripped away */ public RequestData stripRequestProtocol(Socket data) throws ProtocolException { String method; // String version = ""; BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new InputStreamReader(data.getInputStream()), 2048); String get = bufferedReader.readLine(); StringTokenizer tokenizer = new StringTokenizer(get); int bytesRead = 0; int bytesReadThisTime = 0; char [] tempdata; method = tokenizer.nextToken(); if (method.equals("GET")) { String file= tokenizer.nextToken(); if (tokenizer.hasMoreTokens()) { // version = tokenizer.nextToken(); } while ((get = bufferedReader.readLine()) != null) { if (get.trim().equals("")) { break; } } //commObject.println("HTTPServerSocket stripRequestProtocol:GET\n"+file); return new RequestData(GET,file,null); } else if (method.equals(POST)) { String file= tokenizer.nextToken(); if (tokenizer.hasMoreTokens()) { // version = tokenizer.nextToken(); } // DS, 10/27/1998: I found out that some clients don't capitalize Content-Length correctly. // Thus, I've modified the following line from: // while (!(get.startsWith("Content-Length:"))) { to: String marker = "content-length:"; while (!(get.toLowerCase().startsWith(marker))) { get = bufferedReader.readLine(); } int length = 0; try { length = new Integer(get.substring(marker.length()).trim()).intValue(); } catch (NumberFormatException nfe) { System.out.println("HTTPServerSocket <stripReplyProtocol> RequestServerSocket run error: "+nfe); throw new ProtocolException(); } // if (DEBUG) { // commObject.println ("HTTPServerSocket <stripReplyProtocol> Content-Length is: " + length); // } while (!(get.trim().equals(""))) { get = bufferedReader.readLine(); } char[] postdata = new char[length]; tempdata = new char [length]; while (bytesRead < length) { if (bufferedReader.ready()){ int ix = bytesRead; // index to current end of tempdata bytesReadThisTime = bufferedReader.read(postdata, 0, length); // DS: check we've read what we should bytesRead += bytesReadThisTime; if (DEBUG) { System.out.println("HTTPServerSocket <stripReplyProtocol> read " + bytesReadThisTime + " more bytes, "+bytesRead); } for (int i = 0; i < bytesReadThisTime; i++) { tempdata[ix + i] = postdata[i]; } postdata = new char[length]; // Not sure why this is commented out // Daniel, 9/30/1998 // if (bytesRead < length) { // System.out.println ("HTTPServerSocket stripRequestProtocol: could read only " + bytesRead + " bytes instead of " + length); // throw new ProtocolException (); // } } } String readerData = new String(tempdata); StringReader reader = new StringReader(readerData); //commObject.println("\nHTTPServerSocket stripRequestProtocol -POST- file:\n"+ file + "\ndata:\n"+readerData); return new RequestData(RequestData.DECODE,file, reader); } else { System.out.println("HTTPServerSocket stripRequestProtocol: invalid protocol use"); throw new ProtocolException(); } } catch (IOException ioe) { System.out.println("HTTPServerSocket stripRequestProtocol IOException: "+ioe); throw new ProtocolException(); } } /** * This method generates an error message if a request can't * be handled properly, to the point where a contextual error message * can still be sent as the reply. NOT IMPLEMENTED YET, CURRENTLY RETURNS * EMPTY DATAOBJECT - AKD * @return error message in the form of a DataObject * @see #getFatalMessage() */ public DataObject getErrorMessage() { return new DataObject(); } /** * This method generates an fatal message if a request can't * be handled properly, to the point where no contextual error message * can be sent as the reply. NOT IMPLEMENTED YET, CURRENTLY RETURNS * EMPTY STRING - AKD * * @return fatal error message * @see #getErrorMessage() */ public String getFatalMessage() { return new String(""); } /** * This method stops the server from receiving more data * * @see context.arch.comm.protocol.TCPServerSocket#stopServer() */ public void quit() { super.stopServer(); } /** * Method to get the communications protocol being used * * @return communications protocol being used * @see #PROTOCOL */ public String getProtocol() { return PROTOCOL; } }