changeset 1146:2dda3c92a473

webserver - implement cookies
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 01 Feb 2018 03:08:21 -0700
parents 12ececf30597
children 30d87b7d1d62
files src/luan/webserver/Connection.java src/luan/webserver/Request.java src/luan/webserver/RequestParser.java src/luan/webserver/Response.java src/luan/webserver/Util.java src/luan/webserver/examples/Cookies.java src/luan/webserver/examples/Example.java src/luan/webserver/examples/Headers.java
diffstat 8 files changed, 151 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
diff -r 12ececf30597 -r 2dda3c92a473 src/luan/webserver/Connection.java
--- a/src/luan/webserver/Connection.java	Wed Jan 31 01:43:50 2018 -0700
+++ b/src/luan/webserver/Connection.java	Thu Feb 01 03:08:21 2018 -0700
@@ -63,7 +63,7 @@
 				RequestParser parser = new RequestParser(request);
 				parser.parseHead();
 	
-				String lenStr = request.headers.get("Content-Length");
+				String lenStr = (String)request.headers.get("Content-Length");
 				if( lenStr != null ) {
 					int len = Integer.parseInt(lenStr);
 					byte[] body = new byte[len];
@@ -80,7 +80,7 @@
 //System.out.println(request.body);
 				}
 
-				String contentType = request.headers.get("Content-Type");
+				String contentType = (String)request.headers.get("Content-Type");
 				if( contentType != null ) {
 					if( request.body == null ) {
 						logger.error("body is null");
diff -r 12ececf30597 -r 2dda3c92a473 src/luan/webserver/Request.java
--- a/src/luan/webserver/Request.java	Wed Jan 31 01:43:50 2018 -0700
+++ b/src/luan/webserver/Request.java	Thu Feb 01 03:08:21 2018 -0700
@@ -11,7 +11,8 @@
 	public volatile String rawPath;
 	public volatile String path;
 	public volatile String protocol;  // only HTTP/1.1 is accepted
-	public final Map<String,String> headers = Collections.synchronizedMap(new LinkedHashMap<String,String>());
+	public final Map<String,Object> headers = Collections.synchronizedMap(new LinkedHashMap<String,Object>());
 	public final Map<String,Object> parameters = Collections.synchronizedMap(new LinkedHashMap<String,Object>());
+	public final Map<String,String> cookies = Collections.synchronizedMap(new LinkedHashMap<String,String>());
 	public volatile String body;
 }
diff -r 12ececf30597 -r 2dda3c92a473 src/luan/webserver/RequestParser.java
--- a/src/luan/webserver/RequestParser.java	Wed Jan 31 01:43:50 2018 -0700
+++ b/src/luan/webserver/RequestParser.java	Thu Feb 01 03:08:21 2018 -0700
@@ -1,7 +1,5 @@
 package luan.webserver;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
 import java.util.List;
 import java.util.ArrayList;
 import luan.lib.parser.Parser;
@@ -28,6 +26,7 @@
 		while( !parser.match("\r\n") ) {
 			parserHeaderField();
 		}
+		parseCookies();
 	}
 
 	private void parseRequestLine() throws ParseException {
@@ -64,7 +63,7 @@
 		if( !parser.match('/') )
 			throw new ParseException(parser,"bad path");
 		while( safePathChar() || parser.anyOf("&=") );
-		request.path = decode( parser.textFrom(start) );
+		request.path = Util.urlDecode( parser.textFrom(start) );
 	}
 
 	private void parseQuery() throws ParseException {
@@ -74,27 +73,16 @@
 			if( !queryChar() )
 				return;
 			while( queryChar() );
-			String name = decode( parser.textFrom(start) );
+			String name = Util.urlDecode( parser.textFrom(start) );
 			String value;
 			if( parser.match('=') ) {
 				start = parser.currentIndex();
 				while( queryChar() );
-				value = decode( parser.textFrom(start) );
+				value = Util.urlDecode( parser.textFrom(start) );
 			} else {
 				value = "";
 			}
-			Object current = request.parameters.get(name);
-			if( current == null ) {
-				request.parameters.put(name,value);
-			} else if( current instanceof List ) {
-				List list = (List)current;
-				list.add(value);
-			} else {
-				List list = new ArrayList();
-				list.add(current);
-				list.add(value);
-				request.parameters.put(name,list);
-			}
+			Util.add(request.parameters,name,value);
 		}
 	}
 
@@ -131,7 +119,7 @@
 		String value = parseValue();
 		while( parser.anyOf(" \t") );
 		require( parser.match("\r\n") );
-		request.headers.put(name,value);
+		Util.add(request.headers,name,value);
 	}
 
 	private String parseName() throws ParseException {
@@ -185,11 +173,26 @@
 		}
 	}
 
-	private static String decode(String s) {
-		try {
-			return URLDecoder.decode(s,"UTF-8");
-		} catch(UnsupportedEncodingException e) {
-			throw new RuntimeException(e);
+
+	private void parseCookies() throws ParseException {
+		String text = (String)request.headers.get("Cookie");
+		if( text == null )
+			return;
+		this.parser = new Parser(text);
+		while(true) {
+			int start = parser.currentIndex();
+			while( parser.noneOf("=;") );
+			String name = Util.urlDecode( parser.textFrom(start) );
+			if( parser.match('=') ) {
+				start = parser.currentIndex();
+				while( parser.noneOf(";") );
+				String value = Util.urlDecode( parser.textFrom(start) );
+				request.cookies.put(name,value);
+			}
+			if( parser.endOfInput() )
+				return;
+			require( parser.match("; ") );
 		}
 	}
+
 }
diff -r 12ececf30597 -r 2dda3c92a473 src/luan/webserver/Response.java
--- a/src/luan/webserver/Response.java	Wed Jan 31 01:43:50 2018 -0700
+++ b/src/luan/webserver/Response.java	Thu Feb 01 03:08:21 2018 -0700
@@ -4,12 +4,13 @@
 import java.util.Map;
 import java.util.LinkedHashMap;
 import java.util.Collections;
+import java.util.List;
 
 
 public class Response {
 	public final String protocol = "HTTP/1.1";
 	public volatile Status status = Status.OK;
-	public final Map<String,String> headers = Collections.synchronizedMap(new LinkedHashMap<String,String>());
+	public final Map<String,Object> headers = Collections.synchronizedMap(new LinkedHashMap<String,Object>());
 	{
 		headers.put("Server","ThreeBody");
 	}
@@ -26,6 +27,25 @@
 	}
 
 
+	public void addHeader(String name,String value) {
+		Util.add(headers,name,value);
+	}
+
+	public void setCookie(String name,String value,Map<String,String> attributes) {
+		StringBuilder buf = new StringBuilder();
+		buf.append( Util.urlEncode(name) );
+		buf.append( '=' );
+		buf.append( Util.urlEncode(value) );
+		for( Map.Entry<String,String> entry : attributes.entrySet() ) {
+			buf.append( "; " );
+			buf.append( entry.getKey() );
+			buf.append( '=' );
+			buf.append( entry.getValue() );
+		}
+		addHeader( "Set-Cookie", buf.toString() );
+	}
+
+
 	public String toHeaderString() {
 		StringBuilder sb = new StringBuilder();
 		sb.append( protocol )
@@ -33,10 +53,16 @@
 			.append( ' ' ).append( status.reason )
 			.append( "\r\n" )
 		;
-		for( Map.Entry<String,String> entry : headers.entrySet() ) {
+		for( Map.Entry<String,Object> entry : headers.entrySet() ) {
 			String name = entry.getKey();
-			String value = entry.getValue();
-			sb.append( name ).append( ": " ).append( value ).append( "\r\n" );
+			Object value = entry.getValue();
+			if( value instanceof List ) {
+				for( Object v : (List)value ) {
+					sb.append( name ).append( ": " ).append( v ).append( "\r\n" );
+				}
+			} else {
+				sb.append( name ).append( ": " ).append( value ).append( "\r\n" );
+			}
 		}
 		sb.append( "\r\n" );
 		return sb.toString();
diff -r 12ececf30597 -r 2dda3c92a473 src/luan/webserver/Util.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/webserver/Util.java	Thu Feb 01 03:08:21 2018 -0700
@@ -0,0 +1,45 @@
+package luan.webserver;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+
+
+final class Util {
+
+	static String urlDecode(String s) {
+		try {
+			return URLDecoder.decode(s,"UTF-8");
+		} catch(UnsupportedEncodingException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	static String urlEncode(String s) {
+		try {
+			return URLEncoder.encode(s,"UTF-8");
+		} catch(UnsupportedEncodingException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	static void add(Map<String,Object> map,String name,String value) {
+		Object current = map.get(name);
+		if( current == null ) {
+			map.put(name,value);
+		} else if( current instanceof List ) {
+			List list = (List)current;
+			list.add(value);
+		} else {
+			List list = new ArrayList();
+			list.add(current);
+			list.add(value);
+			map.put(name,list);
+		}
+	}
+
+	private Util() {}  // never
+}
diff -r 12ececf30597 -r 2dda3c92a473 src/luan/webserver/examples/Cookies.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/webserver/examples/Cookies.java	Thu Feb 01 03:08:21 2018 -0700
@@ -0,0 +1,42 @@
+package luan.webserver.examples;
+
+import java.io.Writer;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+import luan.webserver.Handler;
+import luan.webserver.Request;
+import luan.webserver.Response;
+import luan.webserver.ResponseOutputStream;
+
+
+public final class Cookies implements Handler {
+
+	public Response handle(Request request) {
+		Response response = new Response();
+		String name = (String)request.parameters.get("name");
+		if( name != null ) {
+			Map<String,String> attributes = new HashMap<String,String>();
+			String value = (String)request.parameters.get("value");
+			if( value != null ) {
+				response.setCookie(name,value,attributes);
+			} else {
+				attributes.put("Max-Age","0");
+				response.setCookie(name,"delete",attributes);
+			}
+		}
+		response.headers.put( "Content-Type", "text/plain; charset=UTF-8" );
+		try {
+			Writer writer = new OutputStreamWriter( new ResponseOutputStream(response) );
+			for( Map.Entry<String,String> entry : request.cookies.entrySet() ) {
+				writer.write(entry.getKey()+" = "+entry.getValue()+"\n");
+			}
+			writer.close();
+		} catch(IOException e) {
+			throw new RuntimeException(e);
+		}
+		return response;
+	}
+
+}
diff -r 12ececf30597 -r 2dda3c92a473 src/luan/webserver/examples/Example.java
--- a/src/luan/webserver/examples/Example.java	Wed Jan 31 01:43:50 2018 -0700
+++ b/src/luan/webserver/examples/Example.java	Thu Feb 01 03:08:21 2018 -0700
@@ -18,6 +18,7 @@
 import luan.webserver.handlers.LogHandler;
 import luan.webserver.handlers.FileHandler;
 import luan.webserver.handlers.ListHandler;
+import luan.webserver.handlers.ContentTypeHandler;
 
 
 public class Example implements Handler {
@@ -45,8 +46,10 @@
 		map.put( "/hello", new Example() );
 		map.put( "/headers", new Headers() );
 		map.put( "/params", new Params() );
+		map.put( "/cookies", new Cookies() );
 		Handler handler = new MapHandler(map);
 		handler = new ListHandler( handler, new FileHandler() );
+		handler = new ContentTypeHandler(handler);
 		handler = new SafeHandler(handler);
 		handler = new LogHandler(handler);
 		new Server(8080,handler).start();
diff -r 12ececf30597 -r 2dda3c92a473 src/luan/webserver/examples/Headers.java
--- a/src/luan/webserver/examples/Headers.java	Wed Jan 31 01:43:50 2018 -0700
+++ b/src/luan/webserver/examples/Headers.java	Thu Feb 01 03:08:21 2018 -0700
@@ -17,7 +17,7 @@
 		response.headers.put( "Content-Type", "text/plain; charset=UTF-8" );
 		try {
 			Writer writer = new OutputStreamWriter( new ResponseOutputStream(response) );
-			for( Map.Entry<String,String> entry : request.headers.entrySet() ) {
+			for( Map.Entry<String,Object> entry : request.headers.entrySet() ) {
 				writer.write(entry.getKey()+": "+entry.getValue()+"\n");
 			}
 			writer.close();