CS 580 Client-Server Programming Fall Semester, 2000 Assignment 1 Solution |
||
---|---|---|
© 2000, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 09-Oct-00 |
Assignment 1 Solution
Some performance Issues
Always make the program work then worry about performance
Profile code to find out were the code is slow
Some Common places that Slow down Java servers
Reading one char per read in a stream
Unicode Strings
System.arraycopy
Primitive to copy arrays
System.arrayCopy(Source, sourceStartIndex, Destination, destinationStartIndex, lengthToCopy )
ByteReader & ByteQueue
ByteQueue
ByteQueue
package WebProxy; public class ByteQueue { byte[] elements; int queueSize = 0; public ByteQueue() { this(128); } public ByteQueue( int size) { elements = new byte[size]; } public byte[] removeAll() { return remove(queueSize); } public int size() { return queueSize; } public void add(byte[] bytes) { add( bytes,0, bytes.length); }
ByteQueue public void add(byte[] bytes, int offset, int length) { if (queueSize + length > elements.length) growelements(); System.arraycopy(bytes, offset, elements,queueSize, length); queueSize = queueSize + length; } public byte[] remove( int size) { int avaliableSize = Math.min( size, queueSize); byte[] response = new byte[avaliableSize]; System.arraycopy(elements, 0, response, 0,avaliableSize); System.arraycopy(elements, avaliableSize, elements, 0, queueSize - avaliableSize); queueSize = queueSize - avaliableSize; return response; } public boolean contains(byte[] pattern) { return -1 != indexOf( pattern); }
ByteQueue
public int indexOf( byte[] pattern ) { for (int k = 0; k < queueSize - pattern.length + 1; k++) { int offset =0; while (offset < pattern.length && pattern[offset] == elements[k + offset]) offset++; if (offset == pattern.length) return k; } return -1; } private void growelements() { byte[] oldelements = elements; elements = new byte[oldelements.length * 2 ]; System.arraycopy(oldelements, 0, elements, 0,queueSize); } }
Tesing ByteQueue
Sample Test
public void testQueueIndex() { ByteQueue queue = new ByteQueue( ); queue.add( "0123456789112342".getBytes() ); assert("a", queue.indexOf( "12".getBytes()) == 1); assert("b", queue.indexOf( "3".getBytes()) == 3); assert("c", queue.indexOf( "6".getBytes()) == 6); assert("d", queue.indexOf( "11".getBytes()) == 10); assert("e", queue.indexOf( "112342".getBytes()) == 10); }
ByteReader
package WebProxy; import java.io.*; public class ByteReader { static int READ_SIZE = 512; static int BUFFER_CAPACITY = 1024 * 4; BufferedInputStream in; ByteQueue buffer = new ByteQueue(BUFFER_CAPACITY); boolean atEOF = false; public ByteReader( InputStream input) { in = new BufferedInputStream( input ); } public byte[] readUpTo( byte[] pattern) throws IOException { int location = readToFindIndexOf( pattern); return remove( location); } public byte[] readThrough( byte[] pattern) throws IOException { int location = readToFindIndexOf( pattern); return remove( location + pattern.length); }
ByteReader public byte[] read( int length) throws IOException { while ( (!isAtEnd() && length > buffer.size())) readBlock(); return remove( length); } public void skip( int length) throws IOException { read( length); } public byte[] readAll() throws IOException { while (!isAtEnd()) readBlock(); return remove( buffer.size()); } public boolean isAtEnd() { return atEOF; } private int readToFindIndexOf( byte[] pattern) throws IOException { int location; while ((location = buffer.indexOf( pattern)) == -1) readBlock(); return location; }
ByteReader private byte[] remove( int size) throws IOException { return buffer.remove( size); } private void readBlock() throws IOException { byte[] readBuffer = new byte[READ_SIZE]; int bytesRead = in.read( readBuffer); if (-1 == bytesRead) atEOF = true; else buffer.add( readBuffer, 0, bytesRead); } }
Sample ByteReader Test
public void testByteInput() throws IOException { String response= "caaaat\r\n" + "do\rg\r\n\r\nacat"; ByteReader in = new ByteReader(new StringBufferInputStream( response)); byte[] answer = in.readUpTo("\r\n".getBytes()); assert( "line 1", (new String( answer)).equals( "caaaat")); in.skip( 2); answer = in.readThrough("\r\n".getBytes()); assert( "line 2", (new String( answer)).equals( "do\rg\r\n")); in.readThrough("\r\n".getBytes()); answer = in.readAll(); assert( "line 3", (new String( answer)).equals( "acat")); assert( "end", ( in.isAtEnd())); } }
HttpRequest & HttpResponse
The http request and response have structure, which is similar
Give each a class to parse/create requests and responses
Use super class to leverage common structure
HttpTransaction
Super class of HttpRequest & HttpResponse
package WebProxy; import java.io.*; import java.util.StringTokenizer; import java.util.HashMap; import java.util.Iterator; public abstract class HttpTransaction { HashMap headers = new HashMap(); byte[] body; abstract void parseFirstLine(ByteReader input ); abstract String firstLineToString(); public void fromStream( InputStream input) throws IOException { ByteReader bufferedInput = new ByteReader( input); parseFirstLine(bufferedInput); if ( hasHeaders() ) parseHeaders(bufferedInput); if (hasBody() ) parseBody(bufferedInput); }
HttpTransaction public byte[] toBytes() { if (!hasBody()) return toString().getBytes(); String headerString = toString(); int length = headerString.length() + body.length; byte[] transaction = new byte[length]; System.arraycopy(headerString.getBytes(), 0, transaction, 0, headerString.length()); System.arraycopy(body, 0, transaction, headerString.length(), body.length); return transaction; } public boolean hasContentLength() { return (headers.containsKey( "Content-length" ) || headers.containsKey( "Content-Length" )); } public int contentLength() { String length = null; if (headers.containsKey( "Content-length" ) ) length = (String)headers.get( "Content-length" ); else length = (String)headers.get( "Content-Length" ); return Integer.parseInt (length); }
HttpTransaction public boolean hasBody() { return hasContentLength(); } public byte[] body() { return body; } void parseBody( ByteReader input) throws IOException { if (hasContentLength()) { int bodyLength = contentLength(); body = input.read( bodyLength); } else body = input.readAll(); } public String toString() { if (hasHeaders()) return firstLineToString() + headerToString(); else return firstLineToString(); }
HttpTransaction String headerToString() { StringBuffer headerString = new StringBuffer(); Iterator keys = headers.keySet().iterator(); while (keys.hasNext()) { Object key = keys.next(); Object value = headers.get( key); headerString.append(key ); headerString.append(": " ); headerString.append(value ); headerString.append("\r\n"); } headerString.append("\r\n"); return headerString.toString(); } public String header( String headerName ) { return (String) headers.get( headerName); } public boolean hasHeaders() { return true; }
HttpTransaction void parseHeaders( ByteReader input ) { try { String requestLine = readLine(input); while (requestLine.length() > 0 ) { int separatorIndex = requestLine.indexOf(':'); String name = requestLine.substring(0, separatorIndex); String value = requestLine.substring(separatorIndex + 1).trim(); headers.put( name, value); requestLine = readLine(input); } } catch (IOException readError ) { } } String readLine( ByteReader input ) throws IOException { byte[] line = input.readUpTo( "\r\n".getBytes()); input.skip(2); return new String( line, 0); } }
HttpResponse
package WebProxy; import java.io.IOException; public class HttpResponse extends HttpTransaction { String statusLine; public String firstLineToString() { return statusLine + "\r\n"; } void parseFirstLine(ByteReader input) { try { statusLine = readLine(input); } catch (IOException readError ) { } } public boolean hasBody() { return true; } }
Sample HttpResponse Test
public void testHTTPResponseNoContentBytes() throws IOException { String response= "HTTP/1.1 200 OK\r\n" + "Date: hi\r\n\r\nacat"; HttpResponse request = new HttpResponse(); request.fromStream( new StringBufferInputStream( response)); assert( "no content body", (new String( request.body())).equals( "acat")); byte[] requestBytes = request.toBytes(); assert( "no content all", (new String(requestBytes)).equals( response)); }
HttpRequest
package WebProxy; import java.io.*; import java.util.StringTokenizer; import java.net.Socket; public class HttpRequest extends HttpTransaction { String requestUri; String host; int port = 80; String requestType; //GET, POST, etc String httpVersion; public String firstLineToString() { StringBuffer request = new StringBuffer(); request.append( requestType); request.append( ' '); request.append( requestUri); if ( isHttp09() ) { request.append("\r\n"); return request.toString(); } request.append( ' '); request.append( httpVersion); request.append("\r\n"); return request.toString(); }
HttpRequest public HttpResponse getResponse() throws IOException { Socket webServer = new Socket(host, port); BufferedOutputStream toServer = new BufferedOutputStream( webServer.getOutputStream() ); byte[] requestBytes = this.toBytes(); toServer.write( requestBytes); toServer.flush(); HttpResponse response = new HttpResponse(); response.fromStream( webServer.getInputStream() ); webServer.close(); return response; } public String getHost( ) { return host; } public int getPort( ) { return port ; } public void setHost( String hostName) { host = hostName; } public void setPort( int portNumber) { port = portNumber; }
HttpRequest public void setUri( String pageUri) { requestUri = reduceURI( pageUri); } public boolean hasHeaders() { return !isHttp09(); } public boolean isGet() { if (requestType == null) return false; return requestType.equals( "GET"); } public String getUri() { return requestUri; } public boolean isHttp09() { return httpVersion.equals("0.9"); } public boolean isHttp10() { return httpVersion.equals("HTTP/1.0"); }
HttpRequest private String reduceURI(String uri) { if (uri.toLowerCase().startsWith( "http://") ) { int pathStart = uri.indexOf( "/", 7); setPortAndHost( uri.substring(7,pathStart)); return uri.substring(pathStart); } return uri; } private void setPortAndHost(String hostPort) { int portIndicator = hostPort.indexOf( ":"); if (-1 == portIndicator ) { host = hostPort; return; } host = hostPort.substring(0, portIndicator); port = Integer.parseInt( hostPort.substring( portIndicator + 1 )); }
HttpRequest private void parseFirstLine(ByteReader input) { try { String requestLine = readLine(input); StringTokenizer parser = new StringTokenizer( requestLine); requestType = parser.nextToken(); requestUri = reduceURI(parser.nextToken()); if (parser.hasMoreTokens() ) httpVersion = parser.nextToken(); else httpVersion = "0.9"; } catch (IOException readError ) { } } }
Sample HttpRequest Test
public void testHTTPBody() throws IOException { String simpleRequest = "GET /index.html HTTP/1.0\r\n" + "Content-length: 4\r\n\r\nacat"; HttpRequest request = new HttpRequest(); request.fromStream( new StringBufferInputStream( simpleRequest)); assert( "GET", request.isGet() ); assert( "uri",request.getUri().equals("/index.html")); byte[] body = request.body(); assert( "body", (new String( body)).equals( "acat")); }
Main Program
Using the Server example in the notes we get our processRequest:
static void processRequest(InputStream in,OutputStream out) throws Exception { HttpRequest request = new HttpRequest(); request.fromStream( in ); System.out.println(request.toString()); System.out.println( "Contacting server"); HttpResponse response = request.getResponse(); System.out.println( "Response"); System.out.println( response.toString()); byte[] responseBytes = response.toBytes(); BufferedOutputStream bufferedOut = new BufferedOutputStream( out ); bufferedOut.write( responseBytes); bufferedOut.flush(); }
Copyright ©, All rights reserved.
2000 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.
Previous    visitors since 09-Oct-00    Next