Mercurial Hosting > luan
view src/luan/webserver/RequestParser.java @ 1194:bd0420fb3dd0
handle ParseException in webserver
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 26 Feb 2018 16:29:07 -0700 |
parents | 49fb4e83484f |
children | 886e14903c1e |
line wrap: on
line source
package luan.webserver; 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 parseUrlencoded() throws ParseException { this.parser = new Parser(Util.toString(request.body)); parseQuery(); require( parser.endOfInput() ); } void parseHead() throws ParseException { this.parser = new Parser(request.rawHead); parseRequestLine(); while( !parser.match("\r\n") ) { parserHeaderField(); } parseCookies(); } 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 = Util.urlDecode( parser.textFrom(start) ); } private void parseQuery() throws ParseException { while(true) { while( parser.match('&') ); int start = parser.currentIndex(); if( !queryChar() ) return; while( queryChar() ); String name = Util.urlDecode( parser.textFrom(start) ); String value; if( parser.match('=') ) { start = parser.currentIndex(); while( queryChar() ); value = Util.urlDecode( parser.textFrom(start) ); } else { value = ""; } Util.add(request.parameters,name,value); } } 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") ); Util.add(request.headers,name,value); } private String parseName() throws ParseException { int start = parser.currentIndex(); require( tokenChar() ); while( tokenChar() ); return parser.textFrom(start).toLowerCase(); } private String parseValue() throws ParseException { int start = parser.currentIndex(); while( !testEndOfValue() ) require( 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 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("; ") ); } } private static final String contentTypeStart = "multipart/form-data; boundary="; void parseMultipart() throws ParseException { String contentType = (String)request.headers.get("content-type"); if( !contentType.startsWith(contentTypeStart) ) throw new RuntimeException(contentType); String boundary = "--"+contentType.substring(contentTypeStart.length()); this.parser = new Parser(Util.toString(request.body)); require( parser.match(boundary) ); boundary = "\r\n" + boundary; while( !parser.match("--\r\n") ) { require( parser.match("\r\n") ); require( parser.match("Content-Disposition: form-data; name=") ); String name = quotedString(); String filename = null; boolean isBinary = false; if( parser.match("; filename=") ) { filename = quotedString(); require( parser.match("\r\n") ); require( parser.match("Content-Type: ") ); if( parser.match("application/octet-stream") ) { isBinary = true; } else if( parser.match("text/plain") ) { isBinary = false; } else throw new ParseException(parser,"bad file content-type"); } require( parser.match("\r\n") ); require( parser.match("\r\n") ); int start = parser.currentIndex(); while( !parser.test(boundary) ) { require( parser.anyChar() ); } String value = parser.textFrom(start); if( filename == null ) { Util.add(request.parameters,name,value); } else { Object content = isBinary ? Util.toBytes(value) : value; Request.MultipartFile mf = new Request.MultipartFile(filename,content); Util.add(request.parameters,name,mf); } require( parser.match(boundary) ); } } private String quotedString() throws ParseException { StringBuilder sb = new StringBuilder(); require( parser.match('"') ); while( !parser.match('"') ) { if( parser.match("\\\"") ) { sb.append('"'); } else { require( parser.anyChar() ); sb.append( parser.lastChar() ); } } return sb.toString(); } }