Mercurial Hosting > luan
changeset 1144:ae0a048f3bc7
webserver - handle POST params
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 31 Jan 2018 00:29:50 -0700 |
parents | 3bf5190b3c77 |
children | 12ececf30597 |
files | src/luan/webserver/Connection.java src/luan/webserver/Request.java src/luan/webserver/RequestHeadParser.java src/luan/webserver/RequestParser.java src/luan/webserver/examples/post.html |
diffstat | 5 files changed, 264 insertions(+), 226 deletions(-) [+] |
line wrap: on
line diff
--- a/src/luan/webserver/Connection.java Tue Jan 30 23:53:28 2018 -0700 +++ b/src/luan/webserver/Connection.java Wed Jan 31 00:29:50 2018 -0700 @@ -26,39 +26,68 @@ private void handle() { try { - InputStream in = socket.getInputStream(); - byte[] a = new byte[8192]; - int endOfHeader; - int size = 0; - int left = a.length; - outer: while(true) { - int n = in.read(a,size,left); - if( n == -1 ) { - if( size == 0 ) { - socket.close(); - return; + Request request = new Request(); + { + InputStream in = socket.getInputStream(); + byte[] a = new byte[8192]; + int endOfHeader; + int size = 0; + int left = a.length; + outer: while(true) { + int n = in.read(a,size,left); + if( n == -1 ) { + if( size == 0 ) { + socket.close(); + return; + } + throw new IOException("unexpected end of input at "+size); } - throw new IOException("unexpected end of input at "+size); - } - size += n; - for( int i=0; i<=size-4; i++ ) { - if( a[i]=='\r' && a[i+1]=='\n' && a[i+2]=='\r' && a[i+3]=='\n' ) { - endOfHeader = i + 4; - break outer; + size += n; + for( int i=0; i<=size-4; i++ ) { + if( a[i]=='\r' && a[i+1]=='\n' && a[i+2]=='\r' && a[i+3]=='\n' ) { + endOfHeader = i + 4; + break outer; + } + } + left -= n; + if( left == 0 ) { + byte[] a2 = new byte[2*a.length]; + System.arraycopy(a,0,a2,0,size); + a = a2; + left = a.length - size; } } - left -= n; - if( left == 0 ) { - byte[] a2 = new byte[2*a.length]; - System.arraycopy(a,0,a2,0,size); - a = a2; - left = a.length - size; + String rawHead = new String(a,0,endOfHeader); +//System.out.println(rawHead); + request.rawHead = rawHead; + RequestParser parser = new RequestParser(request); + parser.parseHead(); + + String lenStr = request.headers.get("Content-Length"); + if( lenStr != null ) { + int len = Integer.parseInt(lenStr); + byte[] body = new byte[len]; + size -= endOfHeader; + System.arraycopy(a,endOfHeader,body,0,size); + while( size < len ) { + int n = in.read(body,size,len-size); + if( n == -1 ) { + throw new IOException("unexpected end of input at "+size); + } + size += n; + } + request.body = new String(body); +//System.out.println(request.body); + } + + if( request.method.equals("POST") ) { + if( request.body == null ) { + logger.error("post body is null"); + } else { + parser.parsePost(); + } } } - String rawRequest = new String(a,0,endOfHeader); -//System.out.println(rawRequest); - Request request = RequestHeadParser.parse(rawRequest); -//System.out.println(request.headers); Response response = server.handler.handle(request); response.headers.put("Connection","close"); @@ -73,7 +102,7 @@ } catch(IOException e) { logger.info("",e); } catch(ParseException e) { - logger.info("",e); + logger.warn("",e); } }
--- a/src/luan/webserver/Request.java Tue Jan 30 23:53:28 2018 -0700 +++ b/src/luan/webserver/Request.java Wed Jan 31 00:29:50 2018 -0700 @@ -13,4 +13,5 @@ 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> parameters = Collections.synchronizedMap(new LinkedHashMap<String,Object>()); + public volatile String body; }
--- a/src/luan/webserver/RequestHeadParser.java Tue Jan 30 23:53:28 2018 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -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 RequestHeadParser { - - static Request parse(String text) throws ParseException { - RequestHeadParser rhp = new RequestHeadParser(text); - rhp.parse(); - return rhp.request; - } - - private final Request request = new Request(); - private final Parser parser; - - private RequestHeadParser(String text) { - this.parser = new Parser(text); - request.rawHead = text; - } - - private void parse() throws ParseException { - 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); - } - } -}
--- /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); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/webserver/examples/post.html Wed Jan 31 00:29:50 2018 -0700 @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> + <body> + <form action=/params method=post> + <p>a <input name=a></p> + <p>b <input name=b></p> + <p><input type=submit></p> + </form> + </body> +</html>