comparison 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
comparison
equal deleted inserted replaced
1143:3bf5190b3c77 1144:ae0a048f3bc7
1 package luan.webserver;
2
3 import java.io.UnsupportedEncodingException;
4 import java.net.URLDecoder;
5 import java.util.List;
6 import java.util.ArrayList;
7 import luan.lib.parser.Parser;
8 import luan.lib.parser.ParseException;
9
10
11 final class RequestParser {
12 private final Request request;
13 private Parser parser;
14
15 RequestParser(Request request) {
16 this.request = request;
17 }
18
19 void parsePost() throws ParseException {
20 this.parser = new Parser(request.body);
21 parseQuery();
22 require( parser.endOfInput() );
23 }
24
25 void parseHead() throws ParseException {
26 this.parser = new Parser(request.rawHead);
27 parseRequestLine();
28 while( !parser.match("\r\n") ) {
29 parserHeaderField();
30 }
31 }
32
33 private void parseRequestLine() throws ParseException {
34 parseMethod();
35 require( parser.match(' ') );
36 parseRawPath();
37 require( parser.match(' ') );
38 parseProtocol();
39 require( parser.match("\r\n") );
40 }
41
42 private void parseMethod() throws ParseException {
43 int start = parser.currentIndex();
44 if( !methodChar() )
45 throw new ParseException(parser,"no method");
46 while( methodChar() );
47 request.method = parser.textFrom(start);
48 }
49
50 private boolean methodChar() {
51 return parser.inCharRange('A','Z');
52 }
53
54 private void parseRawPath() throws ParseException {
55 int start = parser.currentIndex();
56 parsePath();
57 if( parser.match('?') )
58 parseQuery();
59 request.rawPath = parser.textFrom(start);
60 }
61
62 private void parsePath() throws ParseException {
63 int start = parser.currentIndex();
64 if( !parser.match('/') )
65 throw new ParseException(parser,"bad path");
66 while( safePathChar() || parser.anyOf("&=") );
67 request.path = decode( parser.textFrom(start) );
68 }
69
70 private void parseQuery() throws ParseException {
71 while(true) {
72 while( parser.match('&') );
73 int start = parser.currentIndex();
74 if( !queryChar() )
75 return;
76 while( queryChar() );
77 String name = decode( parser.textFrom(start) );
78 String value;
79 if( parser.match('=') ) {
80 start = parser.currentIndex();
81 while( queryChar() );
82 value = decode( parser.textFrom(start) );
83 } else {
84 value = "";
85 }
86 Object current = request.parameters.get(name);
87 if( current == null ) {
88 request.parameters.put(name,value);
89 } else if( current instanceof List ) {
90 List list = (List)current;
91 list.add(value);
92 } else {
93 List list = new ArrayList();
94 list.add(current);
95 list.add(value);
96 request.parameters.put(name,list);
97 }
98 }
99 }
100
101 private boolean queryChar() {
102 return safePathChar() || parser.anyOf("?");
103 }
104
105 // where did I get this?
106 private boolean safePathChar() {
107 return parser.inCharRange('A','Z')
108 || parser.inCharRange('a','z')
109 || parser.inCharRange('0','9')
110 || parser.anyOf("-._~:/[]@!$'()*+,;`.%")
111 ;
112 }
113
114 private void parseProtocol() throws ParseException {
115 int start = parser.currentIndex();
116 if( !(
117 parser.match("HTTP/")
118 && parser.inCharRange('0','9')
119 && parser.match('.')
120 && parser.inCharRange('0','9')
121 ) )
122 throw new ParseException(parser,"bad protocol");
123 request.protocol = parser.textFrom(start);
124 }
125
126
127 private void parserHeaderField() throws ParseException {
128 String name = parseName();
129 require( parser.match(':') );
130 while( parser.anyOf(" \t") );
131 String value = parseValue();
132 while( parser.anyOf(" \t") );
133 require( parser.match("\r\n") );
134 request.headers.put(name,value);
135 }
136
137 private String parseName() throws ParseException {
138 StringBuilder buf = new StringBuilder();
139 boolean cap = true;
140 require( tokenChar() );
141 do {
142 char c = parser.lastChar();
143 if( c == '-' ) {
144 cap = true;
145 } else if( cap ) {
146 c = Character.toUpperCase(c);
147 cap = false;
148 } else {
149 c = Character.toLowerCase(c);
150 }
151 buf.append(c);
152 } while( tokenChar() );
153 return buf.toString();
154 }
155
156 private String parseValue() {
157 int start = parser.currentIndex();
158 while( !testEndOfValue() )
159 parser.anyChar();
160 return parser.textFrom(start);
161 }
162
163 private boolean testEndOfValue() {
164 parser.begin();
165 while( parser.anyOf(" \t") );
166 boolean b = parser.endOfInput() || parser.anyOf("\r\n");
167 parser.failure(); // rollback
168 return b;
169 }
170
171 private void require(boolean b) throws ParseException {
172 if( !b )
173 throw new ParseException(parser,"failed");
174 }
175
176 boolean tokenChar() {
177 if( parser.endOfInput() )
178 return false;
179 char c = parser.currentChar();
180 if( 32 <= c && c <= 126 && "()<>@,;:\\\"/[]?={} \t\r\n".indexOf(c) == -1 ) {
181 parser.anyChar();
182 return true;
183 } else {
184 return false;
185 }
186 }
187
188 private static String decode(String s) {
189 try {
190 return URLDecoder.decode(s,"UTF-8");
191 } catch(UnsupportedEncodingException e) {
192 throw new RuntimeException(e);
193 }
194 }
195 }