Mercurial Hosting > luan
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); + } + } +}