1
18
19 package io.undertow.server.protocol.http;
20
21 import java.io.UnsupportedEncodingException;
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.Field;
24 import java.nio.ByteBuffer;
25 import java.nio.charset.StandardCharsets;
26 import java.util.HashMap;
27 import java.util.Map;
28
29 import io.undertow.UndertowMessages;
30 import io.undertow.UndertowOptions;
31 import io.undertow.annotationprocessor.HttpParserConfig;
32 import io.undertow.server.HttpServerExchange;
33 import io.undertow.util.Headers;
34 import io.undertow.util.HttpString;
35 import io.undertow.util.Methods;
36 import io.undertow.util.Protocols;
37 import io.undertow.util.URLUtils;
38 import io.undertow.util.BadRequestException;
39 import org.xnio.OptionMap;
40
41 import static io.undertow.util.Headers.ACCEPT_CHARSET_STRING;
42 import static io.undertow.util.Headers.ACCEPT_ENCODING_STRING;
43 import static io.undertow.util.Headers.ACCEPT_LANGUAGE_STRING;
44 import static io.undertow.util.Headers.ACCEPT_RANGES_STRING;
45 import static io.undertow.util.Headers.ACCEPT_STRING;
46 import static io.undertow.util.Headers.AUTHORIZATION_STRING;
47 import static io.undertow.util.Headers.CACHE_CONTROL_STRING;
48 import static io.undertow.util.Headers.CONNECTION_STRING;
49 import static io.undertow.util.Headers.CONTENT_LENGTH_STRING;
50 import static io.undertow.util.Headers.CONTENT_TYPE_STRING;
51 import static io.undertow.util.Headers.COOKIE_STRING;
52 import static io.undertow.util.Headers.EXPECT_STRING;
53 import static io.undertow.util.Headers.FROM_STRING;
54 import static io.undertow.util.Headers.HOST_STRING;
55 import static io.undertow.util.Headers.IF_MATCH_STRING;
56 import static io.undertow.util.Headers.IF_MODIFIED_SINCE_STRING;
57 import static io.undertow.util.Headers.IF_NONE_MATCH_STRING;
58 import static io.undertow.util.Headers.IF_RANGE_STRING;
59 import static io.undertow.util.Headers.IF_UNMODIFIED_SINCE_STRING;
60 import static io.undertow.util.Headers.MAX_FORWARDS_STRING;
61 import static io.undertow.util.Headers.ORIGIN_STRING;
62 import static io.undertow.util.Headers.PRAGMA_STRING;
63 import static io.undertow.util.Headers.PROXY_AUTHORIZATION_STRING;
64 import static io.undertow.util.Headers.RANGE_STRING;
65 import static io.undertow.util.Headers.REFERER_STRING;
66 import static io.undertow.util.Headers.REFRESH_STRING;
67 import static io.undertow.util.Headers.SEC_WEB_SOCKET_KEY_STRING;
68 import static io.undertow.util.Headers.SEC_WEB_SOCKET_VERSION_STRING;
69 import static io.undertow.util.Headers.SERVER_STRING;
70 import static io.undertow.util.Headers.SSL_CIPHER_STRING;
71 import static io.undertow.util.Headers.SSL_CIPHER_USEKEYSIZE_STRING;
72 import static io.undertow.util.Headers.SSL_CLIENT_CERT_STRING;
73 import static io.undertow.util.Headers.SSL_SESSION_ID_STRING;
74 import static io.undertow.util.Headers.STRICT_TRANSPORT_SECURITY_STRING;
75 import static io.undertow.util.Headers.TRAILER_STRING;
76 import static io.undertow.util.Headers.TRANSFER_ENCODING_STRING;
77 import static io.undertow.util.Headers.UPGRADE_STRING;
78 import static io.undertow.util.Headers.USER_AGENT_STRING;
79 import static io.undertow.util.Headers.VIA_STRING;
80 import static io.undertow.util.Headers.WARNING_STRING;
81 import static io.undertow.util.Methods.CONNECT_STRING;
82 import static io.undertow.util.Methods.DELETE_STRING;
83 import static io.undertow.util.Methods.GET_STRING;
84 import static io.undertow.util.Methods.HEAD_STRING;
85 import static io.undertow.util.Methods.OPTIONS_STRING;
86 import static io.undertow.util.Methods.POST_STRING;
87 import static io.undertow.util.Methods.PUT_STRING;
88 import static io.undertow.util.Methods.TRACE_STRING;
89 import static io.undertow.util.Protocols.HTTP_0_9_STRING;
90 import static io.undertow.util.Protocols.HTTP_1_0_STRING;
91 import static io.undertow.util.Protocols.HTTP_1_1_STRING;
92 import static io.undertow.util.Protocols.HTTP_2_0_STRING;
93
94
104 @HttpParserConfig(methods = {
105 OPTIONS_STRING,
106 GET_STRING,
107 HEAD_STRING,
108 POST_STRING,
109 PUT_STRING,
110 DELETE_STRING,
111 TRACE_STRING,
112 CONNECT_STRING},
113 protocols = {
114 HTTP_0_9_STRING, HTTP_1_0_STRING, HTTP_1_1_STRING, HTTP_2_0_STRING
115 },
116 headers = {
117 ACCEPT_STRING,
118 ACCEPT_CHARSET_STRING,
119 ACCEPT_ENCODING_STRING,
120 ACCEPT_LANGUAGE_STRING,
121 ACCEPT_RANGES_STRING,
122 AUTHORIZATION_STRING,
123 CACHE_CONTROL_STRING,
124 COOKIE_STRING,
125 CONNECTION_STRING,
126 CONTENT_LENGTH_STRING,
127 CONTENT_TYPE_STRING,
128 EXPECT_STRING,
129 FROM_STRING,
130 HOST_STRING,
131 IF_MATCH_STRING,
132 IF_MODIFIED_SINCE_STRING,
133 IF_NONE_MATCH_STRING,
134 IF_RANGE_STRING,
135 IF_UNMODIFIED_SINCE_STRING,
136 MAX_FORWARDS_STRING,
137 ORIGIN_STRING,
138 PRAGMA_STRING,
139 PROXY_AUTHORIZATION_STRING,
140 RANGE_STRING,
141 REFERER_STRING,
142 REFRESH_STRING,
143 SEC_WEB_SOCKET_KEY_STRING,
144 SEC_WEB_SOCKET_VERSION_STRING,
145 SERVER_STRING,
146 SSL_CLIENT_CERT_STRING,
147 SSL_CIPHER_STRING,
148 SSL_SESSION_ID_STRING,
149 SSL_CIPHER_USEKEYSIZE_STRING,
150 STRICT_TRANSPORT_SECURITY_STRING,
151 TRAILER_STRING,
152 TRANSFER_ENCODING_STRING,
153 UPGRADE_STRING,
154 USER_AGENT_STRING,
155 VIA_STRING,
156 WARNING_STRING
157 })
158 public abstract class HttpRequestParser {
159
160 private static final byte[] HTTP;
161 public static final int HTTP_LENGTH;
162
163 private final int maxParameters;
164 private final int maxHeaders;
165 private final boolean allowEncodedSlash;
166 private final boolean decode;
167 private final String charset;
168 private final int maxCachedHeaderSize;
169 private final boolean allowUnescapedCharactersInUrl;
170
171 private static final boolean[] ALLOWED_TARGET_CHARACTER = new boolean[256];
172
173 static {
174 try {
175 HTTP = "HTTP/1.".getBytes("ASCII");
176 HTTP_LENGTH = HTTP.length;
177 } catch (UnsupportedEncodingException e) {
178 throw new RuntimeException(e);
179 }
180 for(int i = 0; i < 256; ++i) {
181 if(i < 32 || i > 126) {
182 ALLOWED_TARGET_CHARACTER[i] = false;
183 } else {
184 switch ((char)i) {
185 case '\"':
186 case '#':
187 case '<':
188 case '>':
189 case '\\':
190 case '^':
191 case '`':
192 case '{':
193 case '|':
194 case '}':
195 ALLOWED_TARGET_CHARACTER[i] = false;
196 break;
197 default:
198 ALLOWED_TARGET_CHARACTER[i] = true;
199 }
200 }
201 }
202 }
203
204 public HttpRequestParser(OptionMap options) {
205 maxParameters = options.get(UndertowOptions.MAX_PARAMETERS, UndertowOptions.DEFAULT_MAX_PARAMETERS);
206 maxHeaders = options.get(UndertowOptions.MAX_HEADERS, UndertowOptions.DEFAULT_MAX_HEADERS);
207 allowEncodedSlash = options.get(UndertowOptions.ALLOW_ENCODED_SLASH, false);
208 decode = options.get(UndertowOptions.DECODE_URL, true);
209 charset = options.get(UndertowOptions.URL_CHARSET, StandardCharsets.UTF_8.name());
210 maxCachedHeaderSize = options.get(UndertowOptions.MAX_CACHED_HEADER_SIZE, UndertowOptions.DEFAULT_MAX_CACHED_HEADER_SIZE);
211 this.allowUnescapedCharactersInUrl = options.get(UndertowOptions.ALLOW_UNESCAPED_CHARACTERS_IN_URL, false);
212 }
213
214 public static final HttpRequestParser instance(final OptionMap options) {
215 try {
216 final Class<?> cls = Class.forName(HttpRequestParser.class.getName() + "$$generated", false, HttpRequestParser.class.getClassLoader());
217
218 Constructor<?> ctor = cls.getConstructor(OptionMap.class);
219 return (HttpRequestParser) ctor.newInstance(options);
220 } catch (Exception e) {
221 throw new RuntimeException(e);
222 }
223 }
224
225
226 public void handle(ByteBuffer buffer, final ParseState currentState, final HttpServerExchange builder) throws BadRequestException {
227 if (currentState.state == ParseState.VERB) {
228
229
230
231
232 final int position = buffer.position();
233 if (buffer.remaining() > 3
234 && buffer.get(position) == 'G'
235 && buffer.get(position + 1) == 'E'
236 && buffer.get(position + 2) == 'T'
237 && buffer.get(position + 3) == ' ') {
238 buffer.position(position + 4);
239 builder.setRequestMethod(Methods.GET);
240 currentState.state = ParseState.PATH;
241 } else {
242 try {
243 handleHttpVerb(buffer, currentState, builder);
244 } catch (IllegalArgumentException e) {
245 throw new BadRequestException(e);
246 }
247 }
248 handlePath(buffer, currentState, builder);
249 boolean failed = false;
250 if (buffer.remaining() > HTTP_LENGTH + 3) {
251 int pos = buffer.position();
252 for (int i = 0; i < HTTP_LENGTH; ++i) {
253 if (HTTP[i] != buffer.get(pos + i)) {
254 failed = true;
255 break;
256 }
257 }
258 if (!failed) {
259 final byte b = buffer.get(pos + HTTP_LENGTH);
260 final byte b2 = buffer.get(pos + HTTP_LENGTH + 1);
261 final byte b3 = buffer.get(pos + HTTP_LENGTH + 2);
262 if (b2 == '\r' && b3 == '\n') {
263 if (b == '1') {
264 builder.setProtocol(Protocols.HTTP_1_1);
265 buffer.position(pos + HTTP_LENGTH + 3);
266 currentState.state = ParseState.HEADER;
267 } else if (b == '0') {
268 builder.setProtocol(Protocols.HTTP_1_0);
269 buffer.position(pos + HTTP_LENGTH + 3);
270 currentState.state = ParseState.HEADER;
271 } else {
272 failed = true;
273 }
274 } else {
275 failed = true;
276 }
277 }
278 } else {
279 failed = true;
280 }
281 if (failed) {
282 handleHttpVersion(buffer, currentState, builder);
283 handleAfterVersion(buffer, currentState);
284 }
285
286 while (currentState.state != ParseState.PARSE_COMPLETE && buffer.hasRemaining()) {
287 handleHeader(buffer, currentState, builder);
288 if (currentState.state == ParseState.HEADER_VALUE) {
289 handleHeaderValue(buffer, currentState, builder);
290 }
291 }
292 return;
293 }
294 handleStateful(buffer, currentState, builder);
295 }
296
297 private void handleStateful(ByteBuffer buffer, ParseState currentState, HttpServerExchange builder) throws BadRequestException {
298 if (currentState.state == ParseState.PATH) {
299 handlePath(buffer, currentState, builder);
300 if (!buffer.hasRemaining()) {
301 return;
302 }
303 }
304
305 if (currentState.state == ParseState.QUERY_PARAMETERS) {
306 handleQueryParameters(buffer, currentState, builder);
307 if (!buffer.hasRemaining()) {
308 return;
309 }
310 }
311
312 if (currentState.state == ParseState.PATH_PARAMETERS) {
313 handlePathParameters(buffer, currentState, builder);
314 if (!buffer.hasRemaining()) {
315 return;
316 }
317
318 if (currentState.state == ParseState.PATH) {
319 handlePath(buffer, currentState, builder);
320 if (!buffer.hasRemaining()) {
321 return;
322 }
323 }
324 }
325
326 if (currentState.state == ParseState.VERSION) {
327 handleHttpVersion(buffer, currentState, builder);
328 if (!buffer.hasRemaining()) {
329 return;
330 }
331 }
332 if (currentState.state == ParseState.AFTER_VERSION) {
333 handleAfterVersion(buffer, currentState);
334 if (!buffer.hasRemaining()) {
335 return;
336 }
337 }
338 while (currentState.state != ParseState.PARSE_COMPLETE) {
339 if (currentState.state == ParseState.HEADER) {
340 handleHeader(buffer, currentState, builder);
341 if (!buffer.hasRemaining()) {
342 return;
343 }
344 }
345 if (currentState.state == ParseState.HEADER_VALUE) {
346 handleHeaderValue(buffer, currentState, builder);
347 if (!buffer.hasRemaining()) {
348 return;
349 }
350 }
351 }
352 }
353
354
355 abstract void handleHttpVerb(ByteBuffer buffer, final ParseState currentState, final HttpServerExchange builder) throws BadRequestException;
356
357 abstract void handleHttpVersion(ByteBuffer buffer, final ParseState currentState, final HttpServerExchange builder) throws BadRequestException;
358
359 abstract void handleHeader(ByteBuffer buffer, final ParseState currentState, final HttpServerExchange builder) throws BadRequestException;
360
361
364 private static final int START = 0;
365 private static final int FIRST_COLON = 1;
366 private static final int FIRST_SLASH = 2;
367 private static final int SECOND_SLASH = 3;
368 private static final int IN_PATH = 4;
369 private static final int HOST_DONE = 5;
370
371
379 @SuppressWarnings("unused")
380 final void handlePath(ByteBuffer buffer, ParseState state, HttpServerExchange exchange) throws BadRequestException {
381 StringBuilder stringBuilder = state.stringBuilder;
382 int parseState = state.parseState;
383 int canonicalPathStart = state.pos;
384 boolean urlDecodeRequired = state.urlDecodeRequired;
385
386 while (buffer.hasRemaining()) {
387 char next = (char) (buffer.get() & 0xFF);
388 if(!allowUnescapedCharactersInUrl && !ALLOWED_TARGET_CHARACTER[next]) {
389 throw new BadRequestException(UndertowMessages.MESSAGES.invalidCharacterInRequestTarget(next));
390 }
391 if (next == ' ' || next == '\t') {
392 if (stringBuilder.length() != 0) {
393 final String path = stringBuilder.toString();
394 parsePathComplete(state, exchange, canonicalPathStart, parseState, urlDecodeRequired, path);
395 exchange.setQueryString("");
396 state.state = ParseState.VERSION;
397 return;
398 }
399 } else if (next == '\r' || next == '\n') {
400 throw UndertowMessages.MESSAGES.failedToParsePath();
401 } else if (next == '?' && (parseState == START || parseState == HOST_DONE || parseState == IN_PATH)) {
402 beginQueryParameters(buffer, state, exchange, stringBuilder, parseState, canonicalPathStart, urlDecodeRequired);
403 return;
404 } else if (next == ';') {
405 state.parseState = parseState;
406 state.urlDecodeRequired = urlDecodeRequired;
407 state.pos = canonicalPathStart;
408
409 state.canonicalPath.append(stringBuilder.substring(canonicalPathStart));
410
411 handlePathParameters(buffer, state, exchange);
412
413
414
415 if(state.state == ParseState.PATH) {
416 canonicalPathStart = stringBuilder.length();
417 stringBuilder.append('/');
418 } else {
419
420 return;
421 }
422 } else {
423
424 if (decode && (next == '%' || next > 127)) {
425 urlDecodeRequired = true;
426 } else if (next == ':' && parseState == START) {
427 parseState = FIRST_COLON;
428 } else if (next == '/' && parseState == FIRST_COLON) {
429 parseState = FIRST_SLASH;
430 } else if (next == '/' && parseState == FIRST_SLASH) {
431 parseState = SECOND_SLASH;
432 } else if (next == '/' && parseState == SECOND_SLASH) {
433 parseState = HOST_DONE;
434 canonicalPathStart = stringBuilder.length();
435 } else if (parseState == FIRST_COLON || parseState == FIRST_SLASH) {
436 parseState = IN_PATH;
437 } else if (next == '/' && parseState != HOST_DONE) {
438 parseState = IN_PATH;
439 }
440 stringBuilder.append(next);
441 }
442
443 }
444 state.parseState = parseState;
445 state.pos = canonicalPathStart;
446 state.urlDecodeRequired = urlDecodeRequired;
447 }
448
449 private void parsePathComplete(ParseState state, HttpServerExchange exchange, int canonicalPathStart, int parseState, boolean urlDecodeRequired, String path) {
450 if (parseState == SECOND_SLASH) {
451 exchange.setRequestPath("/");
452 exchange.setRelativePath("/");
453 exchange.setRequestURI(path, true);
454 } else if (parseState < HOST_DONE && state.canonicalPath.length() == 0) {
455 String decodedPath = decode(path, urlDecodeRequired, state, allowEncodedSlash, false);
456 exchange.setRequestPath(decodedPath);
457 exchange.setRelativePath(decodedPath);
458 exchange.setRequestURI(path, false);
459 } else {
460 handleFullUrl(state, exchange, canonicalPathStart, urlDecodeRequired, path, parseState);
461 }
462 state.stringBuilder.setLength(0);
463 state.canonicalPath.setLength(0);
464 state.parseState = 0;
465 state.pos = 0;
466 state.urlDecodeRequired = false;
467 }
468
469 private void beginQueryParameters(ByteBuffer buffer, ParseState state, HttpServerExchange exchange, StringBuilder stringBuilder, int parseState, int canonicalPathStart, boolean urlDecodeRequired) throws BadRequestException {
470 final String path = stringBuilder.toString();
471 parsePathComplete(state, exchange, canonicalPathStart, parseState, urlDecodeRequired, path);
472 state.state = ParseState.QUERY_PARAMETERS;
473 handleQueryParameters(buffer, state, exchange);
474 }
475
476 private void handleFullUrl(ParseState state, HttpServerExchange exchange, int canonicalPathStart, boolean urlDecodeRequired, String path, int parseState) {
477 state.canonicalPath.append(path.substring(canonicalPathStart));
478 String thePath = decode(state.canonicalPath.toString(), urlDecodeRequired, state, allowEncodedSlash, false);
479 exchange.setRequestPath(thePath);
480 exchange.setRelativePath(thePath);
481 exchange.setRequestURI(path, parseState == HOST_DONE);
482 }
483
484
485
493 @SuppressWarnings("unused")
494 final void handleQueryParameters(ByteBuffer buffer, ParseState state, HttpServerExchange exchange) throws BadRequestException {
495 StringBuilder stringBuilder = state.stringBuilder;
496 int queryParamPos = state.pos;
497 int mapCount = state.mapCount;
498 boolean urlDecodeRequired = state.urlDecodeRequired;
499 String nextQueryParam = state.nextQueryParam;
500
501
502
503
504
505
506
507
508 while (buffer.hasRemaining()) {
509 char next = (char) (buffer.get() & 0xFF);
510 if(!allowUnescapedCharactersInUrl && !ALLOWED_TARGET_CHARACTER[next]) {
511 throw new BadRequestException(UndertowMessages.MESSAGES.invalidCharacterInRequestTarget(next));
512 }
513 if (next == ' ' || next == '\t') {
514 final String queryString = stringBuilder.toString();
515 exchange.setQueryString(queryString);
516 if (nextQueryParam == null) {
517 if (queryParamPos != stringBuilder.length()) {
518 exchange.addQueryParam(decode(stringBuilder.substring(queryParamPos), urlDecodeRequired, state, true, true), "");
519 }
520 } else {
521 exchange.addQueryParam(nextQueryParam, decode(stringBuilder.substring(queryParamPos), urlDecodeRequired, state, true, true));
522 }
523 state.state = ParseState.VERSION;
524 state.stringBuilder.setLength(0);
525 state.pos = 0;
526 state.nextQueryParam = null;
527 state.urlDecodeRequired = false;
528 state.mapCount = 0;
529 return;
530 } else if (next == '\r' || next == '\n') {
531 throw UndertowMessages.MESSAGES.failedToParsePath();
532 } else {
533 if (decode && (next == '+' || next == '%' || next > 127)) {
534 urlDecodeRequired = true;
535 } else if (next == '=' && nextQueryParam == null) {
536 nextQueryParam = decode(stringBuilder.substring(queryParamPos), urlDecodeRequired, state, true, true);
537 urlDecodeRequired = false;
538 queryParamPos = stringBuilder.length() + 1;
539 } else if (next == '&' && nextQueryParam == null) {
540 if (++mapCount >= maxParameters) {
541 throw UndertowMessages.MESSAGES.tooManyQueryParameters(maxParameters);
542 }
543 if (queryParamPos != stringBuilder.length()) {
544 exchange.addQueryParam(decode(stringBuilder.substring(queryParamPos), urlDecodeRequired, state, true, true), "");
545 }
546 urlDecodeRequired = false;
547 queryParamPos = stringBuilder.length() + 1;
548 } else if (next == '&') {
549 if (++mapCount >= maxParameters) {
550 throw UndertowMessages.MESSAGES.tooManyQueryParameters(maxParameters);
551 }
552 exchange.addQueryParam(nextQueryParam, decode(stringBuilder.substring(queryParamPos), urlDecodeRequired, state, true, true));
553 urlDecodeRequired = false;
554 queryParamPos = stringBuilder.length() + 1;
555 nextQueryParam = null;
556 }
557 stringBuilder.append(next);
558
559 }
560
561 }
562 state.pos = queryParamPos;
563 state.nextQueryParam = nextQueryParam;
564 state.urlDecodeRequired = urlDecodeRequired;
565 state.mapCount = mapCount;
566 }
567
568 private String decode(final String value, boolean urlDecodeRequired, ParseState state, final boolean allowEncodedSlash, final boolean formEncoded) {
569 if (urlDecodeRequired) {
570 return URLUtils.decode(value, charset, allowEncodedSlash, formEncoded, state.decodeBuffer);
571 } else {
572 return value;
573 }
574 }
575
576
579 final void handlePathParameters(ByteBuffer buffer, ParseState state, HttpServerExchange exchange) throws BadRequestException {
580
581 state.state = ParseState.PATH_PARAMETERS;
582 boolean urlDecodeRequired = state.urlDecodeRequired;
583 String param = state.nextQueryParam;
584 final StringBuilder stringBuilder = state.stringBuilder;
585 stringBuilder.append(";");
586 int pos = stringBuilder.length();
587
588
589
590
591
592
593
594
595 while (buffer.hasRemaining()) {
596 char next = (char) (buffer.get() & 0xFF);
597 if(!allowUnescapedCharactersInUrl && !ALLOWED_TARGET_CHARACTER[next]) {
598 throw new BadRequestException(UndertowMessages.MESSAGES.invalidCharacterInRequestTarget(next));
599 }
600
601 if (next == ' ' || next == '\t' || next == '?') {
602 handleParsedParam(param, stringBuilder.substring(pos), exchange, urlDecodeRequired, state);
603 final String path = stringBuilder.toString();
604
605 parsePathComplete(state, exchange, path.length(), state.parseState, urlDecodeRequired, path);
606 state.state = ParseState.VERSION;
607 state.nextQueryParam = null;
608 if (next == '?') {
609 state.state = ParseState.QUERY_PARAMETERS;
610 handleQueryParameters(buffer, state, exchange);
611 } else
612 exchange.setQueryString("");
613 return;
614 } else if (next == '\r' || next == '\n') {
615 throw UndertowMessages.MESSAGES.failedToParsePath();
616 } else if (next == '/') {
617 handleParsedParam(param, stringBuilder.substring(pos), exchange, urlDecodeRequired, state);
618 state.pos = stringBuilder.length();
619 state.state = ParseState.PATH;
620 state.nextQueryParam = null;
621 return;
622 } else {
623 if (decode && (next == '+' || next == '%' || next > 127)) {
624 urlDecodeRequired = true;
625 }
626 if (next == '=' && param == null) {
627 param = decode(stringBuilder.substring(pos), urlDecodeRequired, state, true, true);
628 urlDecodeRequired = false;
629 pos = stringBuilder.length() + 1;
630 } else if (next == ';') {
631 handleParsedParam(param, stringBuilder.substring(pos), exchange, urlDecodeRequired, state);
632 param = null;
633 pos = stringBuilder.length() + 1;
634 } else if (next == ',') {
635 if(param == null) {
636 throw UndertowMessages.MESSAGES.failedToParsePath();
637 } else {
638 handleParsedParam(param, stringBuilder.substring(pos), exchange, urlDecodeRequired, state);
639 pos = stringBuilder.length() + 1;
640 }
641 }
642 stringBuilder.append(next);
643 }
644 }
645
646 state.urlDecodeRequired = urlDecodeRequired;
647 state.pos = pos;
648 state.urlDecodeRequired = urlDecodeRequired;
649 state.nextQueryParam = param;
650 }
651
652 private void handleParsedParam(String previouslyParsedParam, String parsedParam, HttpServerExchange exchange, boolean urlDecodeRequired, ParseState state) throws BadRequestException {
653 if (previouslyParsedParam == null) {
654 exchange.addPathParam(decode(parsedParam, urlDecodeRequired, state, true, true), "");
655 } else {
656 exchange.addPathParam(previouslyParsedParam, decode(parsedParam, urlDecodeRequired, state, true, true));
657 }
658 }
659
660
663 private static final int NORMAL = 0;
664 private static final int WHITESPACE = 1;
665 private static final int BEGIN_LINE_END = 2;
666 private static final int LINE_END = 3;
667 private static final int AWAIT_DATA_END = 4;
668
669
677 @SuppressWarnings("unused")
678 final void handleHeaderValue(ByteBuffer buffer, ParseState state, HttpServerExchange builder) throws BadRequestException {
679 HttpString headerName = state.nextHeader;
680 StringBuilder stringBuilder = state.stringBuilder;
681 CacheMap<HttpString, String> headerValuesCache = state.headerValuesCache;
682 if (headerName != null && stringBuilder.length() == 0 && headerValuesCache != null) {
683 String existing = headerValuesCache.get(headerName);
684 if (existing != null) {
685 if (handleCachedHeader(existing, buffer, state, builder)) {
686 return;
687 }
688 }
689 }
690
691 handleHeaderValueCacheMiss(buffer, state, builder, headerName, headerValuesCache, stringBuilder);
692 }
693
694 private void handleHeaderValueCacheMiss(ByteBuffer buffer, ParseState state, HttpServerExchange builder, HttpString headerName, CacheMap<HttpString, String> headerValuesCache, StringBuilder stringBuilder) throws BadRequestException {
695
696 int parseState = state.parseState;
697 while (buffer.hasRemaining() && parseState == NORMAL) {
698 final byte next = buffer.get();
699 if (next == '\r') {
700 parseState = BEGIN_LINE_END;
701 } else if (next == '\n') {
702 parseState = LINE_END;
703 } else if (next == ' ' || next == '\t') {
704 parseState = WHITESPACE;
705 } else {
706 stringBuilder.append((char) (next & 0xFF));
707 }
708 }
709
710 while (buffer.hasRemaining()) {
711 final byte next = buffer.get();
712 switch (parseState) {
713 case NORMAL: {
714 if (next == '\r') {
715 parseState = BEGIN_LINE_END;
716 } else if (next == '\n') {
717 parseState = LINE_END;
718 } else if (next == ' ' || next == '\t') {
719 parseState = WHITESPACE;
720 } else {
721 stringBuilder.append((char) (next & 0xFF));
722 }
723 break;
724 }
725 case WHITESPACE: {
726 if (next == '\r') {
727 parseState = BEGIN_LINE_END;
728 } else if (next == '\n') {
729 parseState = LINE_END;
730 } else if (next == ' ' || next == '\t') {
731 } else {
732 if (stringBuilder.length() > 0) {
733 stringBuilder.append(' ');
734 }
735 stringBuilder.append((char) (next & 0xFF));
736 parseState = NORMAL;
737 }
738 break;
739 }
740 case LINE_END:
741 case BEGIN_LINE_END: {
742 if (next == '\n' && parseState == BEGIN_LINE_END) {
743 parseState = LINE_END;
744 } else if (next == '\t' ||
745 next == ' ') {
746
747 parseState = WHITESPACE;
748 } else {
749
750 String headerValue = stringBuilder.toString();
751
752
753 if (++state.mapCount > maxHeaders) {
754 throw new BadRequestException(UndertowMessages.MESSAGES.tooManyHeaders(maxHeaders));
755 }
756
757 builder.getRequestHeaders().add(headerName, headerValue);
758 if(headerValuesCache != null && headerName.length() + headerValue.length() < maxCachedHeaderSize) {
759 headerValuesCache.put(headerName, headerValue);
760 }
761
762 state.nextHeader = null;
763
764 state.leftOver = next;
765 state.stringBuilder.setLength(0);
766 if (next == '\r') {
767 parseState = AWAIT_DATA_END;
768 } else if (next == '\n') {
769 state.state = ParseState.PARSE_COMPLETE;
770 return;
771 } else {
772 state.state = ParseState.HEADER;
773 state.parseState = 0;
774 return;
775 }
776 }
777 break;
778 }
779 case AWAIT_DATA_END: {
780 state.state = ParseState.PARSE_COMPLETE;
781 return;
782 }
783 }
784 }
785
786 state.parseState = parseState;
787 }
788
789 protected boolean handleCachedHeader(String existing, ByteBuffer buffer, ParseState state, HttpServerExchange builder) throws BadRequestException {
790 int pos = buffer.position();
791 while (pos < buffer.limit() && buffer.get(pos) == ' ') {
792 pos++;
793 }
794 if (existing.length() + 3 + pos > buffer.limit()) {
795 return false;
796 }
797 int i = 0;
798 while (i < existing.length()) {
799 byte b = buffer.get(pos + i);
800 if (b != existing.charAt(i)) {
801 return false;
802 }
803 ++i;
804 }
805 if (buffer.get(pos + i++) != '\r') {
806 return false;
807 }
808 if (buffer.get(pos + i++) != '\n') {
809 return false;
810 }
811 int next = buffer.get(pos + i);
812 if (next == '\t' || next == ' ') {
813
814 return false;
815 }
816 buffer.position(pos + i);
817 if (++state.mapCount > maxHeaders) {
818 throw new BadRequestException(UndertowMessages.MESSAGES.tooManyHeaders(maxHeaders));
819 }
820
821 builder.getRequestHeaders().add(state.nextHeader, existing);
822
823 state.nextHeader = null;
824
825 state.state = ParseState.HEADER;
826 state.parseState = 0;
827 return true;
828 }
829
830 protected void handleAfterVersion(ByteBuffer buffer, ParseState state) throws BadRequestException {
831 boolean newLine = state.leftOver == '\n';
832 while (buffer.hasRemaining()) {
833 final byte next = buffer.get();
834 if (newLine) {
835 if (next == '\n') {
836 state.state = ParseState.PARSE_COMPLETE;
837 return;
838 } else {
839 state.state = ParseState.HEADER;
840 state.leftOver = next;
841 return;
842 }
843 } else {
844 if (next == '\n') {
845 newLine = true;
846 } else if (next != '\r' && next != ' ' && next != '\t') {
847 state.state = ParseState.HEADER;
848 state.leftOver = next;
849 return;
850 } else {
851 throw UndertowMessages.MESSAGES.badRequest();
852 }
853 }
854 }
855 if (newLine) {
856 state.leftOver = '\n';
857 }
858 }
859
860
867 @SuppressWarnings("unused")
868 protected static Map<String, HttpString> httpStrings() {
869 final Map<String, HttpString> results = new HashMap<>();
870 final Class[] classs = {Headers.class, Methods.class, Protocols.class};
871
872 for (Class<?> c : classs) {
873 for (Field field : c.getDeclaredFields()) {
874 if (field.getType().equals(HttpString.class)) {
875 HttpString result = null;
876 try {
877 result = (HttpString) field.get(null);
878 results.put(result.toString(), result);
879 } catch (IllegalAccessException e) {
880 throw new RuntimeException(e);
881 }
882 }
883 }
884 }
885 return results;
886
887 }
888
889 }
890