diff src/luan/webserver/RequestParser.java @ 1144:ae0a048f3bc7

webserver - handle POST params
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 31 Jan 2018 00:29:50 -0700
parents src/luan/webserver/RequestHeadParser.java@3bf5190b3c77
children 12ececf30597
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/webserver/RequestParser.java	Wed Jan 31 00:29:50 2018 -0700
@@ -0,0 +1,195 @@
+package luan.webserver;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.List;
+import java.util.ArrayList;
+import luan.lib.parser.Parser;
+import luan.lib.parser.ParseException;
+
+
+final class RequestParser {
+	private final Request request;
+	private Parser parser;
+
+	RequestParser(Request request) {
+		this.request = request;
+	}
+
+	void parsePost() throws ParseException {
+		this.parser = new Parser(request.body);
+		parseQuery();
+		require( parser.endOfInput() );
+	}
+
+	void parseHead() throws ParseException {
+		this.parser = new Parser(request.rawHead);
+		parseRequestLine();
+		while( !parser.match("\r\n") ) {
+			parserHeaderField();
+		}
+	}
+
+	private void parseRequestLine() throws ParseException {
+		parseMethod();
+		require( parser.match(' ') );
+		parseRawPath();
+		require( parser.match(' ') );
+		parseProtocol();
+		require( parser.match("\r\n") );
+	}
+
+	private void parseMethod() throws ParseException {
+		int start = parser.currentIndex();
+		if( !methodChar() )
+			throw new ParseException(parser,"no method");
+		while( methodChar() );
+		request.method = parser.textFrom(start);
+	}
+
+	private boolean methodChar() {
+		return parser.inCharRange('A','Z');
+	}
+
+	private void parseRawPath() throws ParseException {
+		int start = parser.currentIndex();
+		parsePath();
+		if( parser.match('?') )
+			parseQuery();
+		request.rawPath = parser.textFrom(start);
+	}
+
+	private void parsePath() throws ParseException {
+		int start = parser.currentIndex();
+		if( !parser.match('/') )
+			throw new ParseException(parser,"bad path");
+		while( safePathChar() || parser.anyOf("&=") );
+		request.path = decode( parser.textFrom(start) );
+	}
+
+	private void parseQuery() throws ParseException {
+		while(true) {
+			while( parser.match('&') );
+			int start = parser.currentIndex();
+			if( !queryChar() )
+				return;
+			while( queryChar() );
+			String name = decode( parser.textFrom(start) );
+			String value;
+			if( parser.match('=') ) {
+				start = parser.currentIndex();
+				while( queryChar() );
+				value = decode( 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);
+			}
+		}
+	}
+
+	private boolean queryChar() {
+		return safePathChar() || parser.anyOf("?");
+	}
+
+	// where did I get this?
+	private boolean safePathChar() {
+		return parser.inCharRange('A','Z')
+			|| parser.inCharRange('a','z')
+			|| parser.inCharRange('0','9')
+			|| parser.anyOf("-._~:/[]@!$'()*+,;`.%")
+		;
+	}
+
+	private void parseProtocol() throws ParseException {
+		int start = parser.currentIndex();
+		if( !(
+			parser.match("HTTP/")
+			&& parser.inCharRange('0','9')
+			&& parser.match('.')
+			&& parser.inCharRange('0','9')
+		) )
+			throw new ParseException(parser,"bad protocol");
+		request.protocol = parser.textFrom(start);
+	}
+
+
+	private void parserHeaderField() throws ParseException {
+		String name = parseName();
+		require( parser.match(':') );
+		while( parser.anyOf(" \t") );
+		String value = parseValue();
+		while( parser.anyOf(" \t") );
+		require( parser.match("\r\n") );
+		request.headers.put(name,value);
+	}
+
+	private String parseName() throws ParseException {
+		StringBuilder buf = new StringBuilder();
+		boolean cap = true;
+		require( tokenChar() );
+		do {
+			char c = parser.lastChar();
+			if( c == '-' ) {
+				cap = true;
+			} else if( cap ) {
+				c = Character.toUpperCase(c);
+				cap = false;
+			} else {
+				c = Character.toLowerCase(c);
+			}
+			buf.append(c);
+		} while( tokenChar() );
+		return buf.toString();
+	}
+
+	private String parseValue() {
+		int start = parser.currentIndex();
+		while( !testEndOfValue() )
+			parser.anyChar();
+		return parser.textFrom(start);
+	}
+
+	private boolean testEndOfValue() {
+		parser.begin();
+		while( parser.anyOf(" \t") );
+		boolean b = parser.endOfInput() || parser.anyOf("\r\n");
+		parser.failure();  // rollback
+		return b;
+	}
+
+	private void require(boolean b) throws ParseException {
+		if( !b )
+			throw new ParseException(parser,"failed");
+	}
+
+	boolean tokenChar() {
+		if( parser.endOfInput() )
+			return false;
+		char c = parser.currentChar();
+		if( 32 <= c && c <= 126 && "()<>@,;:\\\"/[]?={} \t\r\n".indexOf(c) == -1 ) {
+			parser.anyChar();
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	private static String decode(String s) {
+		try {
+			return URLDecoder.decode(s,"UTF-8");
+		} catch(UnsupportedEncodingException e) {
+			throw new RuntimeException(e);
+		}
+	}
+}