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