Mercurial Hosting > luan
diff src/luan/webserver/RequestHeadParser.java @ 1137:c123ee15f99b
add webserver
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 29 Jan 2018 18:49:59 -0700 |
parents | |
children | 3bf5190b3c77 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/webserver/RequestHeadParser.java Mon Jan 29 18:49:59 2018 -0700 @@ -0,0 +1,138 @@ +package luan.webserver; + +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(' ') ); + parsePath(); + 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 parsePath() throws ParseException { + int start = parser.currentIndex(); + if( !parser.match('/') ) + throw new ParseException(parser,"bad path"); + while( + parser.inCharRange('A','Z') + || parser.inCharRange('a','z') + || parser.inCharRange('0','9') + || parser.anyOf("-._~:/?#[]@!$&'()*+,;=`.") + ); + request.path = parser.textFrom(start); + } + + 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; + } + } +}