/* this belongs to the WebRaveler package */ package webraveler.http; import java.io.*; import java.util.*; import java.net.*; import webraveler.io.FixedLengthInputStream; /** * HTTP request. * * @author The Web Raveler Development Squad * @version 1.0 */ public class HTTPRequest extends HTTPMessage implements Serializable { private boolean DEBUG = false; /* variables */ private String method; private String remoteHost; private URI uri; private String uriString; private InetAddress remoteIPAddress; private String protocolVersion; private byte entityBytes[]; private int remotePort; /* constructors */ /** * */ public HTTPRequest() { super(); } /** * Create an HTTPRequest from the raw InputStream. * * @param in */ public HTTPRequest( InputStream in ) throws Exception { String headerString = getRequestHeaders(in); parseHeaders(parseFirstLine(headerString)); parseURI(uriString()); correctHeaders(); setEntityBytes(in); if ( DEBUG ) { System.out.println("HTTPRequest: Created the following:"); print(0); } }//HTTPRequest(InputStream) /** * This constructor takes an unparsed request String and an entityStream * (which should be null unless the method is POST or PUT), and creates * an HTTPRequest object. * * @param request a string containing the unparsed request * @param entityStream an InputStream containing the remainder of the * request, as a sequence of bytes */ public HTTPRequest(String request, InputStream entityStream) { if ( DEBUG ) { System.out.println("HTTPRequest: parsing incoming request."); System.out.println("HTTPRequest: request headers = \n\n'" + request + "'\n"); } /* Parse the first line */ request = parseFirstLine(request); /* try using our URI parser */ try { parseURI(uriString()); } catch (IOException ioe) {ioe.printStackTrace(); } /* Parse the headers */ parseHeaders(request); correctHeaders(); /* Set the entityBytes for the entity body */ setEntityBytes(entityStream); if ( DEBUG ) { System.out.println("HTTPRequest: Created the following:"); print(0); } }//HTTPRequest(...) /* methods */ private void correctHeaders() { // deleteHeader("if-modified-since"); setHeader("Connection","close"); setHeader("Accept-Encoding","identity"); setHeader("Proxy-Connection","close"); setHeader("Via","HTTP/1.0 WR (WebRaveler2 v1.0)"); //add our via header } /** * Read in the request headers, putting them in a string, so that * we can create the HTTPRequest object we'll be using later. * * @param in an InputStream from the client browser, hopefully containing an http request. * @return a boolean success value */ private String getRequestHeaders(InputStream in) throws IOException { String line = new String(); StringBuffer headerString = new StringBuffer(); while( (line = HTTPMessage.readLine(in)).compareTo("") != 0 ) { headerString.append(line+"\r\n"); /* adding line and the CRLF to the request */ }//while headerString.append("\r\n"); /* the blank line after the request headers */ return headerString.toString(); }//getRequestHeaders /** * Parses the first line and sets the method, uri, and protocol/version * * @param requestString a string containing the request * @return a string containing the request without the parsed first line */ private String parseFirstLine(String requestString) { int sep1,sep2,sep3; setUriString(new String()); sep1 = requestString.indexOf(" "); /* find the first space */ method = requestString.substring(0,sep1).trim(); /* the method is the first token */ //System.out.println("HTTPrequest: method = "+method); sep2 = requestString.indexOf(" ",sep1+1); /* find the second space */ setUriString(requestString.substring(sep1+1,sep2).trim()); /* the URI comes next */ //System.out.println("HTTPRequest: uri = "+uriString); sep3 = requestString.indexOf("\r"); protocolVersion = requestString.substring(sep2+1,sep3); /* next comes the protocol/version */ protocolVersion = "HTTP/1.0"; /* Omit the first line from the request string */ requestString = requestString.substring(sep3+2,requestString.length()); return requestString; } // parseFirstLine /** * Given a string representation of the request's URI, * find the host and the port, the old fashioned way (without * the built-in URI class). * * @param uriString a string containing the request's uri * @throws an IOException when the host and port can't be extracted */ public void parseURI(String uriString) throws IOException { String temp = new String(); /* it should start this way anyway, but just to make sure... */ if (uriString.startsWith("http://")) { /* first, extract the portion between the second and * third slashes */ int thirdSlash = uriString.indexOf("/",7); if (thirdSlash == -1) { /* no more slashes, so the whole rest of the string is * our candidate */ temp = uriString.substring(7,uriString.length()); setRemoteHost(new String(temp)); } else { /* there's a third slash */ temp = uriString.substring(7,thirdSlash); } /* the possibilities at this point: * user:password@foo.com:80 * user@foo.com:80 * foo.com:80 * foo.com */ int atIndex; int colon1Index; int colon2Index; atIndex = temp.indexOf("@"); colon1Index = temp.indexOf(":"); colon2Index = temp.indexOf(":",colon1Index+1); if ( colon1Index < atIndex && atIndex < colon2Index ) { // user:password@foo.com:80 setRemoteHost(temp.substring(atIndex+1,colon2Index)); setRemotePort(Integer.parseInt(temp.substring(colon2Index+1,temp.length()))); } //if else if ( atIndex < colon1Index && colon2Index == -1 ) { // user@foo.com:80 setRemoteHost(temp.substring(atIndex+1,colon1Index)); setRemotePort(Integer.parseInt(temp.substring(colon1Index+1,temp.length()))); } //else if else if (atIndex == -1 && colon1Index > colon2Index) { // foo.com:80 setRemoteHost(temp.substring(0,colon1Index)); setRemotePort(Integer.parseInt(temp.substring(colon1Index+1,temp.length()))); } //else if else if ( ( atIndex == -1) && ( colon1Index == -1) ) { // foo.com setRemoteHost(temp); setRemotePort(80); } //else if /* try a stupid hack for cnn.com */ // remoteHost = remoteHost.replaceAll("i.cnn.net","www.cnn.com"); /* System.out.println("remoteHost = "+remoteHost); System.out.println("remotePort = "+remotePort);*/ } else { throw new IOException ("HTTPRequest: parseURI: Parsing failed. Unknown protocol."); } } public void testParseURI (String uriString) { try{ uri = new URI(uriString); } catch(URISyntaxException use) { System.out.println("HTTPRequest: Problem with URI"); use.printStackTrace(); } /* Try using Java's API methods */ setRemoteHost(uri.getHost()); setRemotePort(uri.getPort()); if (remotePort() == -1) setRemotePort(80); } //testParseURI (String) public String getServerRoot(){ return null; } /** * Return a basic GET request for fetching the resource with the given URI. * * @param uri a String containing the URI to be fetched * @return an HTTPRequest for fetching the resource with the given URI */ public static HTTPRequest generateGetRequest(String uri) { HTTPRequest request = new HTTPRequest(); request.setUriString(uri); try{ request.parseURI(uri); }catch(IOException ioe) { System.out.println ("HTTPRequest: generate request");return null; } request.setRemoteHost(new String()); request.setRemotePort(0); try{ request.parseURI(uri); }catch(IOException ioe) { return null; } request.method = "GET"; request.protocolVersion = "HTTP/1.0"; request.setHeader("Connection","close"); request.setHeader("Accept-Encoding","identity"); request.setHeader("Proxy-Connection","close"); return request; }//generateGetRequest /** * Parses the headers and removes the hop-to-hop headers * * @param headerString a string containing the headers */ public void parseHeaders(String headerString) { int colonIndex; int crlfIndex; String headerName = new String(); String headerField = new String(); while (headerString.length() != 0) { /* get the index of the colon*/ colonIndex = headerString.indexOf(":"); /* get the index of the crlf*/ crlfIndex = headerString.indexOf("\r"); if ( headerString.startsWith(" ") ) { /* it's a continuation line; just update the previous hash entry */ setHeader(headerName,headerField+" "+(headerString.substring(0,crlfIndex).trim())); } else { /* this is a new header */ /* if there's no colon in the request, there are no more headers */ if (colonIndex == -1) break; headerName = headerString.substring(0,colonIndex).trim(); headerField = headerString.substring(colonIndex+1,crlfIndex).trim(); /* get rid of [some of] the one-hop header fields */ if (!isHopToHop(headerName)) /* Add the header to the hash table */ setHeader(headerName,headerField); } /* Omit this header from the response string */ headerString = headerString.substring(crlfIndex+2,headerString.length()); } // while } // parseHeaders /** * Determines whether a header is hop-to-hop * * @param headerNameString a string containing the name of the header * @return a boolean indicating whether the header is hop-to-hop */ public boolean isHopToHop(String headerNameString) { if (headerNameString.toLowerCase().equals("keep-alive") || headerNameString.toLowerCase().equals("proxy-connection") || headerNameString.toLowerCase().equals("upgrade") || headerNameString.toLowerCase().equals("connection")){ return true; } // if return false; } // isHopToHop /** * */ public void setEntityBytes(InputStream entity) { /* if this is a persistent connection (which we ain't supporting yet, thank the * Sweet Lord Jesus) then we read in the Content-Length header and * stop reading our bytes at that integer, so as to enable the server to send * multiple responses over the same connection */ /* get the content-length, if present. otherwise, read in until the end of * the input */ String contentLengthString = getHeader("Content-length"); if (contentLengthString != null) { int contentLength; InputStream in = entity; contentLength = Integer.parseInt(contentLengthString); in = new FixedLengthInputStream(entity,contentLength); ByteArrayOutputStream entityByteStream = new ByteArrayOutputStream(); try{ int input; while( (input = in.read()) != -1 ) { entityByteStream.write(input); /* write the input byte to the buffer */ } entityBytes = entityByteStream.toByteArray(); }catch (IOException ioe) { ioe.printStackTrace(); entityBytes = null; } } else { entityBytes = null; } } // setEntityBytes /** * Set the URI of this requestString to the given string representation. * * @param uriString a string containing the uri */ public void setURI(String uriString) { } /** * Writes the HTTP requestString to the given output stream * * @param out an OutputStream to write the requestString to */ public synchronized void write(OutputStream out) { try { /* Write the first line to the output stream */ String firstLine = method + " " + uriString() + " " + protocolVersion + "\r\n"; out.write(firstLine.getBytes()); /* Write the headers to the output stream */ String headerString = new String(); for (Enumeration e = headers.keys() ; e.hasMoreElements() ;) { String element = (String) e.nextElement(); String field = getHeader(element); //if ( DEBUG ) System.out.println("HTTPrequestString: write: element = '"+element+"', field = '"+field+"'"); headerString += element + ": " + field + "\r\n"; }// for out.write(headerString.getBytes()); out.write("\r\n".getBytes()); /* write the blank line after the headers */ //if (DEBUG ) System.out.println("HTTPrequestString: headers = '"+headerString+"'"); /* Write the entity bytes to the output stream * Here, we assume the Connection header has the value "Close", so that * we can read all the way to the end of the input for this socket connection */ if (entityBytes != null) { out.write(entityBytes); } out.flush(); } catch (IOException ioe) { ioe.printStackTrace(); } } // write /** * Print the contents of the requestString to stdout, with the ProxyServer id integer * * @param count an int containing the id of the calling ProxyServer instance */ public synchronized void print(int count){ try{ System.out.print("HTTPRequestString: requestString#"+count+": \n'"); String firstLine = method + " " + uriString() + " " + protocolVersion + "\r\n"; System.out.write(firstLine.getBytes()); /* Write the headers to the output stream */ String headerString = new String(); for (Enumeration e = headers.keys() ; e.hasMoreElements() ;) { String element = (String) e.nextElement(); String field = getHeader(element); headerString += element + ": " + field + "\r\n"; } // for System.out.write(headerString.getBytes()); System.out.write("\r\n".getBytes()); //System.out.write(entityBytes); System.out.println("'\n"); }catch(IOException ioe) { ioe.printStackTrace(); } } /** * Return a URLConnection for fetching this request from a remote * host. * */ public URLConnection getConnection(){ return null; }//getConnection /** * Test whether or not another HTTPRequest is equal to this one. Equality of * two requests is simply equality of their methods, protocolVersions, and uriStrings, * at least for now * * @param request an HTTPRequest object for testing * @return a boolean, true if the two requests are equal and false otherwise */ public boolean equals(HTTPRequest request) { return (uriString().equalsIgnoreCase(request.uriString()) && method.equals(request.method) && protocolVersion.equals(request.protocolVersion)); } /** * @param uriString The uriString to set. */ public void setUriString(String uriString) { this.uriString = uriString; } /** * @return Returns the uriString. */ public String uriString() { return uriString; } /** * @param remoteHost The remoteHost to set. */ public void setRemoteHost(String remoteHost) { this.remoteHost = remoteHost; } /** * @return Returns the remoteHost. */ public String remoteHost() { return remoteHost; } /** * @param remotePort The remotePort to set. */ public void setRemotePort(int remotePort) { this.remotePort = remotePort; } /** * @return Returns the remotePort. */ public int remotePort() { return remotePort; } } // class HTTPrequestString