1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.Unpooled;
20 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.channel.ChannelPipeline;
22 import io.netty.handler.codec.ByteToMessageDecoder;
23 import io.netty.handler.codec.DecoderResult;
24 import io.netty.handler.codec.PrematureChannelClosureException;
25 import io.netty.handler.codec.TooLongFrameException;
26 import io.netty.util.AsciiString;
27 import io.netty.util.ByteProcessor;
28 import io.netty.util.internal.StringUtil;
29 import io.netty.util.internal.SystemPropertyUtil;
30
31 import java.util.List;
32 import java.util.concurrent.atomic.AtomicBoolean;
33
34 import static io.netty.util.internal.ObjectUtil.checkNotNull;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146 public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
147 public static final int DEFAULT_MAX_INITIAL_LINE_LENGTH = 4096;
148 public static final int DEFAULT_MAX_HEADER_SIZE = 8192;
149 public static final boolean DEFAULT_CHUNKED_SUPPORTED = true;
150 public static final boolean DEFAULT_ALLOW_PARTIAL_CHUNKS = true;
151 public static final int DEFAULT_MAX_CHUNK_SIZE = 8192;
152 public static final boolean DEFAULT_VALIDATE_HEADERS = true;
153 public static final int DEFAULT_INITIAL_BUFFER_SIZE = 128;
154 public static final boolean DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS = false;
155 public static final boolean DEFAULT_STRICT_LINE_PARSING =
156 SystemPropertyUtil.getBoolean("io.netty.handler.codec.http.defaultStrictLineParsing", true);
157
158 private static final Runnable THROW_INVALID_CHUNK_EXTENSION = new Runnable() {
159 @Override
160 public void run() {
161 throw new InvalidChunkExtensionException();
162 }
163 };
164
165 private static final Runnable THROW_INVALID_LINE_SEPARATOR = new Runnable() {
166 @Override
167 public void run() {
168 throw new InvalidLineSeparatorException();
169 }
170 };
171
172 private final int maxChunkSize;
173 private final boolean chunkedSupported;
174 private final boolean allowPartialChunks;
175
176
177
178 @Deprecated
179 protected final boolean validateHeaders;
180 protected final HttpHeadersFactory headersFactory;
181 protected final HttpHeadersFactory trailersFactory;
182 private final boolean allowDuplicateContentLengths;
183 private final ByteBuf parserScratchBuffer;
184 private final Runnable defaultStrictCRLFCheck;
185 private final HeaderParser headerParser;
186 private final LineParser lineParser;
187
188 private HttpMessage message;
189 private long chunkSize;
190 private long contentLength = Long.MIN_VALUE;
191 private boolean chunked;
192 private boolean isSwitchingToNonHttp1Protocol;
193
194 private final AtomicBoolean resetRequested = new AtomicBoolean();
195
196
197 private AsciiString name;
198 private String value;
199 private LastHttpContent trailer;
200
201 @Override
202 protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
203 try {
204 parserScratchBuffer.release();
205 } finally {
206 super.handlerRemoved0(ctx);
207 }
208 }
209
210
211
212
213
214 private enum State {
215 SKIP_CONTROL_CHARS,
216 READ_INITIAL,
217 READ_HEADER,
218 READ_VARIABLE_LENGTH_CONTENT,
219 READ_FIXED_LENGTH_CONTENT,
220 READ_CHUNK_SIZE,
221 READ_CHUNKED_CONTENT,
222 READ_CHUNK_DELIMITER,
223 READ_CHUNK_FOOTER,
224 BAD_MESSAGE,
225 UPGRADED
226 }
227
228 private State currentState = State.SKIP_CONTROL_CHARS;
229
230
231
232
233
234
235 protected HttpObjectDecoder() {
236 this(new HttpDecoderConfig());
237 }
238
239
240
241
242
243
244 @Deprecated
245 protected HttpObjectDecoder(
246 int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported) {
247 this(new HttpDecoderConfig()
248 .setMaxInitialLineLength(maxInitialLineLength)
249 .setMaxHeaderSize(maxHeaderSize)
250 .setMaxChunkSize(maxChunkSize)
251 .setChunkedSupported(chunkedSupported));
252 }
253
254
255
256
257
258
259 @Deprecated
260 protected HttpObjectDecoder(
261 int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,
262 boolean chunkedSupported, boolean validateHeaders) {
263 this(new HttpDecoderConfig()
264 .setMaxInitialLineLength(maxInitialLineLength)
265 .setMaxHeaderSize(maxHeaderSize)
266 .setMaxChunkSize(maxChunkSize)
267 .setChunkedSupported(chunkedSupported)
268 .setValidateHeaders(validateHeaders));
269 }
270
271
272
273
274
275
276 @Deprecated
277 protected HttpObjectDecoder(
278 int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,
279 boolean chunkedSupported, boolean validateHeaders, int initialBufferSize) {
280 this(new HttpDecoderConfig()
281 .setMaxInitialLineLength(maxInitialLineLength)
282 .setMaxHeaderSize(maxHeaderSize)
283 .setMaxChunkSize(maxChunkSize)
284 .setChunkedSupported(chunkedSupported)
285 .setValidateHeaders(validateHeaders)
286 .setInitialBufferSize(initialBufferSize));
287 }
288
289
290
291
292
293
294 @Deprecated
295 protected HttpObjectDecoder(
296 int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,
297 boolean chunkedSupported, boolean validateHeaders, int initialBufferSize,
298 boolean allowDuplicateContentLengths) {
299 this(new HttpDecoderConfig()
300 .setMaxInitialLineLength(maxInitialLineLength)
301 .setMaxHeaderSize(maxHeaderSize)
302 .setMaxChunkSize(maxChunkSize)
303 .setChunkedSupported(chunkedSupported)
304 .setValidateHeaders(validateHeaders)
305 .setInitialBufferSize(initialBufferSize)
306 .setAllowDuplicateContentLengths(allowDuplicateContentLengths));
307 }
308
309
310
311
312
313
314 @Deprecated
315 protected HttpObjectDecoder(
316 int maxInitialLineLength, int maxHeaderSize, int maxChunkSize,
317 boolean chunkedSupported, boolean validateHeaders, int initialBufferSize,
318 boolean allowDuplicateContentLengths, boolean allowPartialChunks) {
319 this(new HttpDecoderConfig()
320 .setMaxInitialLineLength(maxInitialLineLength)
321 .setMaxHeaderSize(maxHeaderSize)
322 .setMaxChunkSize(maxChunkSize)
323 .setChunkedSupported(chunkedSupported)
324 .setValidateHeaders(validateHeaders)
325 .setInitialBufferSize(initialBufferSize)
326 .setAllowDuplicateContentLengths(allowDuplicateContentLengths)
327 .setAllowPartialChunks(allowPartialChunks));
328 }
329
330
331
332
333 protected HttpObjectDecoder(HttpDecoderConfig config) {
334 checkNotNull(config, "config");
335
336 parserScratchBuffer = Unpooled.buffer(config.getInitialBufferSize());
337 defaultStrictCRLFCheck = config.isStrictLineParsing() ? THROW_INVALID_LINE_SEPARATOR : null;
338 lineParser = new LineParser(parserScratchBuffer, config.getMaxInitialLineLength());
339 headerParser = new HeaderParser(parserScratchBuffer, config.getMaxHeaderSize());
340 maxChunkSize = config.getMaxChunkSize();
341 chunkedSupported = config.isChunkedSupported();
342 headersFactory = config.getHeadersFactory();
343 trailersFactory = config.getTrailersFactory();
344 validateHeaders = isValidating(headersFactory);
345 allowDuplicateContentLengths = config.isAllowDuplicateContentLengths();
346 allowPartialChunks = config.isAllowPartialChunks();
347 }
348
349 protected boolean isValidating(HttpHeadersFactory headersFactory) {
350 if (headersFactory instanceof DefaultHttpHeadersFactory) {
351 DefaultHttpHeadersFactory builder = (DefaultHttpHeadersFactory) headersFactory;
352 return builder.isValidatingHeaderNames() || builder.isValidatingHeaderValues();
353 }
354 return true;
355 }
356
357 @Override
358 protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
359 if (resetRequested.get()) {
360 resetNow();
361 }
362
363 switch (currentState) {
364 case SKIP_CONTROL_CHARS:
365
366 case READ_INITIAL: try {
367 ByteBuf line = lineParser.parse(buffer, defaultStrictCRLFCheck);
368 if (line == null) {
369 return;
370 }
371 final String[] initialLine = splitInitialLine(line);
372 assert initialLine.length == 3 : "initialLine::length must be 3";
373
374 message = createMessage(initialLine);
375 currentState = State.READ_HEADER;
376
377 } catch (Exception e) {
378 out.add(invalidMessage(message, buffer, e));
379 return;
380 }
381 case READ_HEADER: try {
382 State nextState = readHeaders(buffer);
383 if (nextState == null) {
384 return;
385 }
386 currentState = nextState;
387 switch (nextState) {
388 case SKIP_CONTROL_CHARS:
389
390
391 addCurrentMessage(out);
392 out.add(LastHttpContent.EMPTY_LAST_CONTENT);
393 resetNow();
394 return;
395 case READ_CHUNK_SIZE:
396 if (!chunkedSupported) {
397 throw new IllegalArgumentException("Chunked messages not supported");
398 }
399
400 addCurrentMessage(out);
401 return;
402 default:
403
404
405
406
407
408
409 if (contentLength == 0 || contentLength == -1 && isDecodingRequest()) {
410 addCurrentMessage(out);
411 out.add(LastHttpContent.EMPTY_LAST_CONTENT);
412 resetNow();
413 return;
414 }
415
416 assert nextState == State.READ_FIXED_LENGTH_CONTENT ||
417 nextState == State.READ_VARIABLE_LENGTH_CONTENT;
418
419 addCurrentMessage(out);
420
421 if (nextState == State.READ_FIXED_LENGTH_CONTENT) {
422
423 chunkSize = contentLength;
424 }
425
426
427 return;
428 }
429 } catch (Exception e) {
430 out.add(invalidMessage(message, buffer, e));
431 return;
432 }
433 case READ_VARIABLE_LENGTH_CONTENT: {
434
435 int toRead = Math.min(buffer.readableBytes(), maxChunkSize);
436 if (toRead > 0) {
437 ByteBuf content = buffer.readRetainedSlice(toRead);
438 out.add(new DefaultHttpContent(content));
439 }
440 return;
441 }
442 case READ_FIXED_LENGTH_CONTENT: {
443 int readLimit = buffer.readableBytes();
444
445
446
447
448
449
450
451 if (readLimit == 0) {
452 return;
453 }
454
455 int toRead = Math.min(readLimit, maxChunkSize);
456 if (toRead > chunkSize) {
457 toRead = (int) chunkSize;
458 }
459 ByteBuf content = buffer.readRetainedSlice(toRead);
460 chunkSize -= toRead;
461
462 if (chunkSize == 0) {
463
464 out.add(new DefaultLastHttpContent(content, trailersFactory));
465 resetNow();
466 } else {
467 out.add(new DefaultHttpContent(content));
468 }
469 return;
470 }
471
472
473
474
475 case READ_CHUNK_SIZE: try {
476 ByteBuf line = lineParser.parse(buffer, THROW_INVALID_CHUNK_EXTENSION);
477 if (line == null) {
478 return;
479 }
480 checkChunkExtensions(line);
481 int chunkSize = getChunkSize(line.array(), line.arrayOffset() + line.readerIndex(), line.readableBytes());
482 this.chunkSize = chunkSize;
483 if (chunkSize == 0) {
484 currentState = State.READ_CHUNK_FOOTER;
485 return;
486 }
487 currentState = State.READ_CHUNKED_CONTENT;
488
489 } catch (Exception e) {
490 out.add(invalidChunk(buffer, e));
491 return;
492 }
493 case READ_CHUNKED_CONTENT: {
494 assert chunkSize <= Integer.MAX_VALUE;
495 int toRead = Math.min((int) chunkSize, maxChunkSize);
496 if (!allowPartialChunks && buffer.readableBytes() < toRead) {
497 return;
498 }
499 toRead = Math.min(toRead, buffer.readableBytes());
500 if (toRead == 0) {
501 return;
502 }
503 HttpContent chunk = new DefaultHttpContent(buffer.readRetainedSlice(toRead));
504 chunkSize -= toRead;
505
506 out.add(chunk);
507
508 if (chunkSize != 0) {
509 return;
510 }
511 currentState = State.READ_CHUNK_DELIMITER;
512
513 }
514 case READ_CHUNK_DELIMITER: {
515 if (buffer.readableBytes() >= 2) {
516 int rIdx = buffer.readerIndex();
517 if (buffer.getByte(rIdx) == HttpConstants.CR &&
518 buffer.getByte(rIdx + 1) == HttpConstants.LF) {
519 buffer.skipBytes(2);
520 currentState = State.READ_CHUNK_SIZE;
521 } else {
522 out.add(invalidChunk(buffer, new InvalidChunkTerminationException()));
523 }
524 }
525 return;
526 }
527 case READ_CHUNK_FOOTER: try {
528 LastHttpContent trailer = readTrailingHeaders(buffer);
529 if (trailer == null) {
530 return;
531 }
532 out.add(trailer);
533 resetNow();
534 return;
535 } catch (Exception e) {
536 out.add(invalidChunk(buffer, e));
537 return;
538 }
539 case BAD_MESSAGE: {
540
541 buffer.skipBytes(buffer.readableBytes());
542 break;
543 }
544 case UPGRADED: {
545 int readableBytes = buffer.readableBytes();
546 if (readableBytes > 0) {
547
548
549
550
551 out.add(buffer.readBytes(readableBytes));
552 }
553 break;
554 }
555 default:
556 break;
557 }
558 }
559
560 @Override
561 protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
562 super.decodeLast(ctx, in, out);
563
564 if (resetRequested.get()) {
565
566
567 resetNow();
568 }
569
570
571 switch (currentState) {
572 case READ_VARIABLE_LENGTH_CONTENT:
573 if (!chunked && !in.isReadable()) {
574
575 out.add(LastHttpContent.EMPTY_LAST_CONTENT);
576 resetNow();
577 }
578 return;
579 case READ_HEADER:
580
581
582 out.add(invalidMessage(message, Unpooled.EMPTY_BUFFER,
583 new PrematureChannelClosureException("Connection closed before received headers")));
584 resetNow();
585 return;
586 case READ_CHUNK_DELIMITER:
587 case READ_CHUNK_FOOTER:
588 case READ_CHUNKED_CONTENT:
589 case READ_CHUNK_SIZE:
590 case READ_FIXED_LENGTH_CONTENT:
591
592 boolean prematureClosure;
593 if (isDecodingRequest() || chunked) {
594
595 prematureClosure = true;
596 } else {
597
598
599
600 prematureClosure = contentLength > 0;
601 }
602 if (!prematureClosure) {
603 out.add(LastHttpContent.EMPTY_LAST_CONTENT);
604 }
605 resetNow();
606 return;
607 case SKIP_CONTROL_CHARS:
608 case READ_INITIAL:
609 case BAD_MESSAGE:
610 case UPGRADED:
611
612 break;
613 default:
614 throw new IllegalStateException("Unhandled state " + currentState);
615 }
616 }
617
618 @Override
619 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
620 if (evt instanceof HttpExpectationFailedEvent) {
621 switch (currentState) {
622 case READ_FIXED_LENGTH_CONTENT:
623 case READ_VARIABLE_LENGTH_CONTENT:
624 case READ_CHUNK_SIZE:
625 reset();
626 break;
627 default:
628 break;
629 }
630 }
631 super.userEventTriggered(ctx, evt);
632 }
633
634 private void addCurrentMessage(List<Object> out) {
635 HttpMessage message = this.message;
636 assert message != null;
637 this.message = null;
638 out.add(message);
639 }
640
641 protected boolean isContentAlwaysEmpty(HttpMessage msg) {
642 if (msg instanceof HttpResponse) {
643 HttpResponse res = (HttpResponse) msg;
644 final HttpResponseStatus status = res.status();
645 final int code = status.code();
646 final HttpStatusClass statusClass = status.codeClass();
647
648
649
650
651
652
653 if (statusClass == HttpStatusClass.INFORMATIONAL) {
654
655 return !(code == 101 && !res.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT)
656 && res.headers().contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true));
657 }
658
659 switch (code) {
660 case 204: case 304:
661 return true;
662 default:
663 return false;
664 }
665 }
666 return false;
667 }
668
669
670
671
672
673 protected boolean isSwitchingToNonHttp1Protocol(HttpResponse msg) {
674 if (msg.status().code() != HttpResponseStatus.SWITCHING_PROTOCOLS.code()) {
675 return false;
676 }
677 String newProtocol = msg.headers().get(HttpHeaderNames.UPGRADE);
678 return newProtocol == null ||
679 !newProtocol.contains(HttpVersion.HTTP_1_0.text()) &&
680 !newProtocol.contains(HttpVersion.HTTP_1_1.text());
681 }
682
683
684
685
686
687 public void reset() {
688 resetRequested.lazySet(true);
689 }
690
691 private void resetNow() {
692 message = null;
693 name = null;
694 value = null;
695 contentLength = Long.MIN_VALUE;
696 chunked = false;
697 lineParser.reset();
698 headerParser.reset();
699 trailer = null;
700 if (isSwitchingToNonHttp1Protocol) {
701 isSwitchingToNonHttp1Protocol = false;
702 currentState = State.UPGRADED;
703 return;
704 }
705
706 resetRequested.lazySet(false);
707 currentState = State.SKIP_CONTROL_CHARS;
708 }
709
710 private HttpMessage invalidMessage(HttpMessage current, ByteBuf in, Exception cause) {
711 currentState = State.BAD_MESSAGE;
712 message = null;
713 trailer = null;
714
715
716
717 in.skipBytes(in.readableBytes());
718
719 if (current == null) {
720 current = createInvalidMessage();
721 }
722 current.setDecoderResult(DecoderResult.failure(cause));
723
724 return current;
725 }
726
727 private static void checkChunkExtensions(ByteBuf line) {
728 int extensionsStart = line.bytesBefore((byte) ';');
729 if (extensionsStart == -1) {
730 return;
731 }
732 HttpChunkLineValidatingByteProcessor processor = new HttpChunkLineValidatingByteProcessor();
733 line.forEachByte(processor);
734 processor.finish();
735 }
736
737 private HttpContent invalidChunk(ByteBuf in, Exception cause) {
738 currentState = State.BAD_MESSAGE;
739 message = null;
740 trailer = null;
741
742
743
744 in.skipBytes(in.readableBytes());
745
746 HttpContent chunk = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER);
747 chunk.setDecoderResult(DecoderResult.failure(cause));
748 return chunk;
749 }
750
751 private State readHeaders(ByteBuf buffer) {
752 final HttpMessage message = this.message;
753 final HttpHeaders headers = message.headers();
754
755 final HeaderParser headerParser = this.headerParser;
756
757 ByteBuf line = headerParser.parse(buffer, defaultStrictCRLFCheck);
758 if (line == null) {
759 return null;
760 }
761 int lineLength = line.readableBytes();
762 while (lineLength > 0) {
763 final byte[] lineContent = line.array();
764 final int startLine = line.arrayOffset() + line.readerIndex();
765 final byte firstChar = lineContent[startLine];
766 if (name != null && (firstChar == ' ' || firstChar == '\t')) {
767
768
769 String trimmedLine = langAsciiString(lineContent, startLine, lineLength).trim();
770 String valueStr = value;
771 value = valueStr + ' ' + trimmedLine;
772 } else {
773 if (name != null) {
774 headers.add(name, value);
775 }
776 splitHeader(lineContent, startLine, lineLength);
777 }
778
779 line = headerParser.parse(buffer, defaultStrictCRLFCheck);
780 if (line == null) {
781 return null;
782 }
783 lineLength = line.readableBytes();
784 }
785
786
787 if (name != null) {
788 headers.add(name, value);
789 }
790
791
792 name = null;
793 value = null;
794
795
796 HttpMessageDecoderResult decoderResult = new HttpMessageDecoderResult(lineParser.size, headerParser.size);
797 message.setDecoderResult(decoderResult);
798
799 List<String> contentLengthFields = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
800 if (!contentLengthFields.isEmpty()) {
801 HttpVersion version = message.protocolVersion();
802 boolean isHttp10OrEarlier = version.majorVersion() < 1 || (version.majorVersion() == 1
803 && version.minorVersion() == 0);
804
805
806 contentLength = HttpUtil.normalizeAndGetContentLength(contentLengthFields,
807 isHttp10OrEarlier, allowDuplicateContentLengths);
808 if (contentLength != -1) {
809 String lengthValue = contentLengthFields.get(0).trim();
810 if (contentLengthFields.size() > 1 ||
811 !lengthValue.equals(Long.toString(contentLength))) {
812 headers.set(HttpHeaderNames.CONTENT_LENGTH, contentLength);
813 }
814 }
815 } else {
816
817
818 contentLength = HttpUtil.getWebSocketContentLength(message);
819 }
820 if (!isDecodingRequest() && message instanceof HttpResponse) {
821 HttpResponse res = (HttpResponse) message;
822 this.isSwitchingToNonHttp1Protocol = isSwitchingToNonHttp1Protocol(res);
823 }
824 if (isContentAlwaysEmpty(message)) {
825 HttpUtil.setTransferEncodingChunked(message, false);
826 return State.SKIP_CONTROL_CHARS;
827 }
828 if (HttpUtil.isTransferEncodingChunked(message)) {
829 this.chunked = true;
830 if (!contentLengthFields.isEmpty() && message.protocolVersion() == HttpVersion.HTTP_1_1) {
831 handleTransferEncodingChunkedWithContentLength(message);
832 }
833 return State.READ_CHUNK_SIZE;
834 }
835 if (contentLength >= 0) {
836 return State.READ_FIXED_LENGTH_CONTENT;
837 }
838 return State.READ_VARIABLE_LENGTH_CONTENT;
839 }
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862 protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) {
863 message.headers().remove(HttpHeaderNames.CONTENT_LENGTH);
864 contentLength = Long.MIN_VALUE;
865 }
866
867 private LastHttpContent readTrailingHeaders(ByteBuf buffer) {
868 final HeaderParser headerParser = this.headerParser;
869 ByteBuf line = headerParser.parse(buffer, defaultStrictCRLFCheck);
870 if (line == null) {
871 return null;
872 }
873 LastHttpContent trailer = this.trailer;
874 int lineLength = line.readableBytes();
875 if (lineLength == 0 && trailer == null) {
876
877
878 return LastHttpContent.EMPTY_LAST_CONTENT;
879 }
880
881 if (trailer == null) {
882 trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, trailersFactory);
883 }
884 while (lineLength > 0) {
885 final byte[] lineContent = line.array();
886 final int startLine = line.arrayOffset() + line.readerIndex();
887 final byte firstChar = lineContent[startLine];
888 if (name != null && (firstChar == ' ' || firstChar == '\t')) {
889
890
891 String trimmedLine = langAsciiString(lineContent, startLine, lineLength).trim();
892 String valueStr = value;
893 value = valueStr + ' ' + trimmedLine;
894 } else {
895 if (name != null && isPermittedTrailingHeader(name)) {
896 trailer.trailingHeaders().add(name, value);
897 }
898 splitHeader(lineContent, startLine, lineLength);
899 }
900
901 line = headerParser.parse(buffer, defaultStrictCRLFCheck);
902 if (line == null) {
903 return null;
904 }
905 lineLength = line.readableBytes();
906 }
907
908
909 if (name != null && isPermittedTrailingHeader(name)) {
910 trailer.trailingHeaders().add(name, value);
911 }
912
913
914 name = null;
915 value = null;
916
917 this.trailer = null;
918 return trailer;
919 }
920
921
922
923
924 private static boolean isPermittedTrailingHeader(final AsciiString name) {
925 return !HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(name) &&
926 !HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(name) &&
927 !HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(name);
928 }
929
930 protected abstract boolean isDecodingRequest();
931 protected abstract HttpMessage createMessage(String[] initialLine) throws Exception;
932 protected abstract HttpMessage createInvalidMessage();
933
934
935
936
937 private static int skipWhiteSpaces(byte[] hex, int start, int length) {
938 for (int i = 0; i < length; i++) {
939 if (!isWhitespace(hex[start + i])) {
940 return i;
941 }
942 }
943 return length;
944 }
945
946 private static int getChunkSize(byte[] hex, int start, int length) {
947
948 final int skipped = skipWhiteSpaces(hex, start, length);
949 if (skipped == length) {
950
951 throw new NumberFormatException();
952 }
953 start += skipped;
954 length -= skipped;
955 int result = 0;
956 for (int i = 0; i < length; i++) {
957 final int digit = StringUtil.decodeHexNibble(hex[start + i]);
958 if (digit == -1) {
959
960 final byte b = hex[start + i];
961 if (b == ';' || isControlOrWhitespaceAsciiChar(b)) {
962 if (i == 0) {
963
964 throw new NumberFormatException("Empty chunk size");
965 }
966 return result;
967 }
968
969 throw new NumberFormatException("Invalid character in chunk size");
970 }
971 result *= 16;
972 result += digit;
973 if (result < 0) {
974 throw new NumberFormatException("Chunk size overflow: " + result);
975 }
976 }
977 return result;
978 }
979
980 private String[] splitInitialLine(ByteBuf asciiBuffer) {
981 final byte[] asciiBytes = asciiBuffer.array();
982
983 final int arrayOffset = asciiBuffer.arrayOffset();
984
985 final int startContent = arrayOffset + asciiBuffer.readerIndex();
986
987 final int end = startContent + asciiBuffer.readableBytes();
988
989 byte lastByte = asciiBytes[end - 1];
990 if (isControlOrWhitespaceAsciiChar(lastByte)) {
991 if (isDecodingRequest() || !isOWS(lastByte)) {
992
993
994
995
996
997 throw new IllegalArgumentException(
998 "Illegal character in request line: 0x" + Integer.toHexString(lastByte));
999 }
1000 }
1001
1002 final int aStart = findNonSPLenient(asciiBytes, startContent, end);
1003 final int aEnd = findSPLenient(asciiBytes, aStart, end);
1004
1005 final int bStart = findNonSPLenient(asciiBytes, aEnd, end);
1006 final int bEnd = findSPLenient(asciiBytes, bStart, end);
1007
1008 final int cStart = findNonSPLenient(asciiBytes, bEnd, end);
1009 final int cEnd = findEndOfString(asciiBytes, Math.max(cStart - 1, startContent), end);
1010
1011 return new String[]{
1012 splitFirstWordInitialLine(asciiBytes, aStart, aEnd - aStart),
1013 splitSecondWordInitialLine(asciiBytes, bStart, bEnd - bStart),
1014 cStart < cEnd ? splitThirdWordInitialLine(asciiBytes, cStart, cEnd - cStart) : StringUtil.EMPTY_STRING};
1015 }
1016
1017 protected String splitFirstWordInitialLine(final byte[] asciiContent, int start, int length) {
1018 return langAsciiString(asciiContent, start, length);
1019 }
1020
1021 protected String splitSecondWordInitialLine(final byte[] asciiContent, int start, int length) {
1022 return langAsciiString(asciiContent, start, length);
1023 }
1024
1025 protected String splitThirdWordInitialLine(final byte[] asciiContent, int start, int length) {
1026 return langAsciiString(asciiContent, start, length);
1027 }
1028
1029
1030
1031
1032 private static String langAsciiString(final byte[] asciiContent, int start, int length) {
1033 if (length == 0) {
1034 return StringUtil.EMPTY_STRING;
1035 }
1036
1037 if (start == 0) {
1038 if (length == asciiContent.length) {
1039 return new String(asciiContent, 0, 0, asciiContent.length);
1040 }
1041 return new String(asciiContent, 0, 0, length);
1042 }
1043 return new String(asciiContent, 0, start, length);
1044 }
1045
1046 private void splitHeader(byte[] line, int start, int length) {
1047 final int end = start + length;
1048 int nameEnd;
1049 final int nameStart = start;
1050
1051 final boolean isDecodingRequest = isDecodingRequest();
1052 for (nameEnd = nameStart; nameEnd < end; nameEnd ++) {
1053 byte ch = line[nameEnd];
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063 if (ch == ':' ||
1064
1065
1066
1067
1068 (!isDecodingRequest && isOWS(ch))) {
1069 break;
1070 }
1071 }
1072
1073 if (nameEnd == end) {
1074
1075 throw new IllegalArgumentException("No colon found");
1076 }
1077 int colonEnd;
1078 for (colonEnd = nameEnd; colonEnd < end; colonEnd ++) {
1079 if (line[colonEnd] == ':') {
1080 colonEnd ++;
1081 break;
1082 }
1083 }
1084 name = splitHeaderName(line, nameStart, nameEnd - nameStart);
1085 final int valueStart = findNonWhitespace(line, colonEnd, end);
1086 if (valueStart == end) {
1087 value = StringUtil.EMPTY_STRING;
1088 } else {
1089 final int valueEnd = findEndOfString(line, start, end);
1090
1091 value = langAsciiString(line, valueStart, valueEnd - valueStart);
1092 }
1093 }
1094
1095 protected AsciiString splitHeaderName(byte[] sb, int start, int length) {
1096 return new AsciiString(sb, start, length, true);
1097 }
1098
1099 private static int findNonSPLenient(byte[] sb, int offset, int end) {
1100 for (int result = offset; result < end; ++result) {
1101 byte c = sb[result];
1102
1103 if (isSPLenient(c)) {
1104 continue;
1105 }
1106 if (isWhitespace(c)) {
1107
1108 throw new IllegalArgumentException("Invalid separator");
1109 }
1110 return result;
1111 }
1112 return end;
1113 }
1114
1115 private static int findSPLenient(byte[] sb, int offset, int end) {
1116 for (int result = offset; result < end; ++result) {
1117 if (isSPLenient(sb[result])) {
1118 return result;
1119 }
1120 }
1121 return end;
1122 }
1123
1124 private static final boolean[] SP_LENIENT_BYTES;
1125 private static final boolean[] LATIN_WHITESPACE;
1126
1127 static {
1128
1129 SP_LENIENT_BYTES = new boolean[256];
1130 SP_LENIENT_BYTES[128 + ' '] = true;
1131 SP_LENIENT_BYTES[128 + 0x09] = true;
1132 SP_LENIENT_BYTES[128 + 0x0B] = true;
1133 SP_LENIENT_BYTES[128 + 0x0C] = true;
1134 SP_LENIENT_BYTES[128 + 0x0D] = true;
1135
1136 LATIN_WHITESPACE = new boolean[256];
1137 for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
1138 LATIN_WHITESPACE[128 + b] = Character.isWhitespace(b);
1139 }
1140 }
1141
1142 private static boolean isSPLenient(byte c) {
1143
1144 return SP_LENIENT_BYTES[c + 128];
1145 }
1146
1147 private static boolean isWhitespace(byte b) {
1148 return LATIN_WHITESPACE[b + 128];
1149 }
1150
1151 private static int findNonWhitespace(byte[] sb, int offset, int end) {
1152 for (int result = offset; result < end; ++result) {
1153 byte c = sb[result];
1154 if (!isWhitespace(c)) {
1155 return result;
1156 } else if (!isOWS(c)) {
1157
1158 throw new IllegalArgumentException("Invalid separator, only a single space or horizontal tab allowed," +
1159 " but received a '" + c + "' (0x" + Integer.toHexString(c) + ")");
1160 }
1161 }
1162 return end;
1163 }
1164
1165 private static int findEndOfString(byte[] sb, int start, int end) {
1166 for (int result = end - 1; result > start; --result) {
1167 if (!isOWS(sb[result])) {
1168 return result + 1;
1169 }
1170 }
1171 return 0;
1172 }
1173
1174 private static boolean isOWS(byte ch) {
1175 return ch == ' ' || ch == 0x09;
1176 }
1177
1178 private static class HeaderParser {
1179 protected final ByteBuf seq;
1180 protected final int maxLength;
1181 int size;
1182
1183 HeaderParser(ByteBuf seq, int maxLength) {
1184 this.seq = seq;
1185 this.maxLength = maxLength;
1186 }
1187
1188 public ByteBuf parse(ByteBuf buffer, Runnable strictCRLFCheck) {
1189 final int readableBytes = buffer.readableBytes();
1190 final int readerIndex = buffer.readerIndex();
1191 final int maxBodySize = maxLength - size;
1192 assert maxBodySize >= 0;
1193
1194
1195 final long maxBodySizeWithCRLF = maxBodySize + 2L;
1196 final int toProcess = (int) Math.min(maxBodySizeWithCRLF, readableBytes);
1197 final int toIndexExclusive = readerIndex + toProcess;
1198 assert toIndexExclusive >= readerIndex;
1199 final int indexOfLf = buffer.indexOf(readerIndex, toIndexExclusive, HttpConstants.LF);
1200 if (indexOfLf == -1) {
1201 if (readableBytes > maxBodySize) {
1202
1203
1204
1205
1206 throw newException(maxLength);
1207 }
1208 return null;
1209 }
1210 final int endOfSeqIncluded;
1211 if (indexOfLf > readerIndex && buffer.getByte(indexOfLf - 1) == HttpConstants.CR) {
1212
1213 endOfSeqIncluded = indexOfLf - 1;
1214 } else {
1215 if (strictCRLFCheck != null) {
1216 strictCRLFCheck.run();
1217 }
1218 endOfSeqIncluded = indexOfLf;
1219 }
1220 final int newSize = endOfSeqIncluded - readerIndex;
1221 if (newSize == 0) {
1222 seq.clear();
1223 buffer.readerIndex(indexOfLf + 1);
1224 return seq;
1225 }
1226 int size = this.size + newSize;
1227 if (size > maxLength) {
1228 throw newException(maxLength);
1229 }
1230 this.size = size;
1231 seq.clear();
1232 seq.writeBytes(buffer, readerIndex, newSize);
1233 buffer.readerIndex(indexOfLf + 1);
1234 return seq;
1235 }
1236
1237 public void reset() {
1238 size = 0;
1239 }
1240
1241 protected TooLongFrameException newException(int maxLength) {
1242 return new TooLongHttpHeaderException("HTTP header is larger than " + maxLength + " bytes.");
1243 }
1244 }
1245
1246 private final class LineParser extends HeaderParser {
1247
1248 LineParser(ByteBuf seq, int maxLength) {
1249 super(seq, maxLength);
1250 }
1251
1252 @Override
1253 public ByteBuf parse(ByteBuf buffer, Runnable strictCRLFCheck) {
1254
1255 reset();
1256 final int readableBytes = buffer.readableBytes();
1257 if (readableBytes == 0) {
1258 return null;
1259 }
1260 if (currentState == State.SKIP_CONTROL_CHARS &&
1261 skipControlChars(buffer, readableBytes, buffer.readerIndex())) {
1262 return null;
1263 }
1264 return super.parse(buffer, strictCRLFCheck);
1265 }
1266
1267 private boolean skipControlChars(ByteBuf buffer, int readableBytes, int readerIndex) {
1268 assert currentState == State.SKIP_CONTROL_CHARS;
1269 final int maxToSkip = Math.min(maxLength, readableBytes);
1270 final int firstNonControlIndex = buffer.forEachByte(readerIndex, maxToSkip, SKIP_CONTROL_CHARS_BYTES);
1271 if (firstNonControlIndex == -1) {
1272 buffer.skipBytes(maxToSkip);
1273 if (readableBytes > maxLength) {
1274 throw newException(maxLength);
1275 }
1276 return true;
1277 }
1278
1279 buffer.readerIndex(firstNonControlIndex);
1280 currentState = State.READ_INITIAL;
1281 return false;
1282 }
1283
1284 @Override
1285 protected TooLongFrameException newException(int maxLength) {
1286 return new TooLongHttpLineException("An HTTP line is larger than " + maxLength + " bytes.");
1287 }
1288 }
1289
1290 private static final boolean[] ISO_CONTROL_OR_WHITESPACE;
1291
1292 static {
1293 ISO_CONTROL_OR_WHITESPACE = new boolean[256];
1294 for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
1295 ISO_CONTROL_OR_WHITESPACE[128 + b] = Character.isISOControl(b) || isWhitespace(b);
1296 }
1297 }
1298
1299 private static final ByteProcessor SKIP_CONTROL_CHARS_BYTES = new ByteProcessor() {
1300
1301 @Override
1302 public boolean process(byte value) {
1303 return ISO_CONTROL_OR_WHITESPACE[128 + value];
1304 }
1305 };
1306
1307 private static boolean isControlOrWhitespaceAsciiChar(byte b) {
1308 return ISO_CONTROL_OR_WHITESPACE[128 + b];
1309 }
1310 }