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