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 int chunkSize = getChunkSize(line.array(), line.arrayOffset() + line.readerIndex(), line.readableBytes());
481 this.chunkSize = chunkSize;
482 if (chunkSize == 0) {
483 currentState = State.READ_CHUNK_FOOTER;
484 return;
485 }
486 currentState = State.READ_CHUNKED_CONTENT;
487
488 } catch (Exception e) {
489 out.add(invalidChunk(buffer, e));
490 return;
491 }
492 case READ_CHUNKED_CONTENT: {
493 assert chunkSize <= Integer.MAX_VALUE;
494 int toRead = Math.min((int) chunkSize, maxChunkSize);
495 if (!allowPartialChunks && buffer.readableBytes() < toRead) {
496 return;
497 }
498 toRead = Math.min(toRead, buffer.readableBytes());
499 if (toRead == 0) {
500 return;
501 }
502 HttpContent chunk = new DefaultHttpContent(buffer.readRetainedSlice(toRead));
503 chunkSize -= toRead;
504
505 out.add(chunk);
506
507 if (chunkSize != 0) {
508 return;
509 }
510 currentState = State.READ_CHUNK_DELIMITER;
511
512 }
513 case READ_CHUNK_DELIMITER: {
514 if (buffer.readableBytes() >= 2) {
515 int rIdx = buffer.readerIndex();
516 if (buffer.getByte(rIdx) == HttpConstants.CR &&
517 buffer.getByte(rIdx + 1) == HttpConstants.LF) {
518 buffer.skipBytes(2);
519 currentState = State.READ_CHUNK_SIZE;
520 } else {
521 out.add(invalidChunk(buffer, new InvalidChunkTerminationException()));
522 }
523 }
524 return;
525 }
526 case READ_CHUNK_FOOTER: try {
527 LastHttpContent trailer = readTrailingHeaders(buffer);
528 if (trailer == null) {
529 return;
530 }
531 out.add(trailer);
532 resetNow();
533 return;
534 } catch (Exception e) {
535 out.add(invalidChunk(buffer, e));
536 return;
537 }
538 case BAD_MESSAGE: {
539
540 buffer.skipBytes(buffer.readableBytes());
541 break;
542 }
543 case UPGRADED: {
544 int readableBytes = buffer.readableBytes();
545 if (readableBytes > 0) {
546
547
548
549
550 out.add(buffer.readBytes(readableBytes));
551 }
552 break;
553 }
554 default:
555 break;
556 }
557 }
558
559 @Override
560 protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
561 super.decodeLast(ctx, in, out);
562
563 if (resetRequested.get()) {
564
565
566 resetNow();
567 }
568
569
570 switch (currentState) {
571 case READ_VARIABLE_LENGTH_CONTENT:
572 if (!chunked && !in.isReadable()) {
573
574 out.add(LastHttpContent.EMPTY_LAST_CONTENT);
575 resetNow();
576 }
577 return;
578 case READ_HEADER:
579
580
581 out.add(invalidMessage(message, Unpooled.EMPTY_BUFFER,
582 new PrematureChannelClosureException("Connection closed before received headers")));
583 resetNow();
584 return;
585 case READ_CHUNK_DELIMITER:
586 case READ_CHUNK_FOOTER:
587 case READ_CHUNKED_CONTENT:
588 case READ_CHUNK_SIZE:
589 case READ_FIXED_LENGTH_CONTENT:
590
591 boolean prematureClosure;
592 if (isDecodingRequest() || chunked) {
593
594 prematureClosure = true;
595 } else {
596
597
598
599 prematureClosure = contentLength > 0;
600 }
601 if (!prematureClosure) {
602 out.add(LastHttpContent.EMPTY_LAST_CONTENT);
603 }
604 resetNow();
605 return;
606 case SKIP_CONTROL_CHARS:
607 case READ_INITIAL:
608 case BAD_MESSAGE:
609 case UPGRADED:
610
611 break;
612 default:
613 throw new IllegalStateException("Unhandled state " + currentState);
614 }
615 }
616
617 @Override
618 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
619 if (evt instanceof HttpExpectationFailedEvent) {
620 switch (currentState) {
621 case READ_FIXED_LENGTH_CONTENT:
622 case READ_VARIABLE_LENGTH_CONTENT:
623 case READ_CHUNK_SIZE:
624 reset();
625 break;
626 default:
627 break;
628 }
629 }
630 super.userEventTriggered(ctx, evt);
631 }
632
633 private void addCurrentMessage(List<Object> out) {
634 HttpMessage message = this.message;
635 assert message != null;
636 this.message = null;
637 out.add(message);
638 }
639
640 protected boolean isContentAlwaysEmpty(HttpMessage msg) {
641 if (msg instanceof HttpResponse) {
642 HttpResponse res = (HttpResponse) msg;
643 final HttpResponseStatus status = res.status();
644 final int code = status.code();
645 final HttpStatusClass statusClass = status.codeClass();
646
647
648
649
650
651
652 if (statusClass == HttpStatusClass.INFORMATIONAL) {
653
654 return !(code == 101 && !res.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT)
655 && res.headers().contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true));
656 }
657
658 switch (code) {
659 case 204: case 304:
660 return true;
661 default:
662 return false;
663 }
664 }
665 return false;
666 }
667
668
669
670
671
672 protected boolean isSwitchingToNonHttp1Protocol(HttpResponse msg) {
673 if (msg.status().code() != HttpResponseStatus.SWITCHING_PROTOCOLS.code()) {
674 return false;
675 }
676 String newProtocol = msg.headers().get(HttpHeaderNames.UPGRADE);
677 return newProtocol == null ||
678 !newProtocol.contains(HttpVersion.HTTP_1_0.text()) &&
679 !newProtocol.contains(HttpVersion.HTTP_1_1.text());
680 }
681
682
683
684
685
686 public void reset() {
687 resetRequested.lazySet(true);
688 }
689
690 private void resetNow() {
691 message = null;
692 name = null;
693 value = null;
694 contentLength = Long.MIN_VALUE;
695 chunked = false;
696 lineParser.reset();
697 headerParser.reset();
698 trailer = null;
699 if (isSwitchingToNonHttp1Protocol) {
700 isSwitchingToNonHttp1Protocol = false;
701 currentState = State.UPGRADED;
702 return;
703 }
704
705 resetRequested.lazySet(false);
706 currentState = State.SKIP_CONTROL_CHARS;
707 }
708
709 private HttpMessage invalidMessage(HttpMessage current, ByteBuf in, Exception cause) {
710 currentState = State.BAD_MESSAGE;
711 message = null;
712 trailer = null;
713
714
715
716 in.skipBytes(in.readableBytes());
717
718 if (current == null) {
719 current = createInvalidMessage();
720 }
721 current.setDecoderResult(DecoderResult.failure(cause));
722
723 return current;
724 }
725
726 private HttpContent invalidChunk(ByteBuf in, Exception cause) {
727 currentState = State.BAD_MESSAGE;
728 message = null;
729 trailer = null;
730
731
732
733 in.skipBytes(in.readableBytes());
734
735 HttpContent chunk = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER);
736 chunk.setDecoderResult(DecoderResult.failure(cause));
737 return chunk;
738 }
739
740 private State readHeaders(ByteBuf buffer) {
741 final HttpMessage message = this.message;
742 final HttpHeaders headers = message.headers();
743
744 final HeaderParser headerParser = this.headerParser;
745
746 ByteBuf line = headerParser.parse(buffer, defaultStrictCRLFCheck);
747 if (line == null) {
748 return null;
749 }
750 int lineLength = line.readableBytes();
751 while (lineLength > 0) {
752 final byte[] lineContent = line.array();
753 final int startLine = line.arrayOffset() + line.readerIndex();
754 final byte firstChar = lineContent[startLine];
755 if (name != null && (firstChar == ' ' || firstChar == '\t')) {
756
757
758 String trimmedLine = langAsciiString(lineContent, startLine, lineLength).trim();
759 String valueStr = value;
760 value = valueStr + ' ' + trimmedLine;
761 } else {
762 if (name != null) {
763 headers.add(name, value);
764 }
765 splitHeader(lineContent, startLine, lineLength);
766 }
767
768 line = headerParser.parse(buffer, defaultStrictCRLFCheck);
769 if (line == null) {
770 return null;
771 }
772 lineLength = line.readableBytes();
773 }
774
775
776 if (name != null) {
777 headers.add(name, value);
778 }
779
780
781 name = null;
782 value = null;
783
784
785 HttpMessageDecoderResult decoderResult = new HttpMessageDecoderResult(lineParser.size, headerParser.size);
786 message.setDecoderResult(decoderResult);
787
788 List<String> contentLengthFields = headers.getAll(HttpHeaderNames.CONTENT_LENGTH);
789 if (!contentLengthFields.isEmpty()) {
790 HttpVersion version = message.protocolVersion();
791 boolean isHttp10OrEarlier = version.majorVersion() < 1 || (version.majorVersion() == 1
792 && version.minorVersion() == 0);
793
794
795 contentLength = HttpUtil.normalizeAndGetContentLength(contentLengthFields,
796 isHttp10OrEarlier, allowDuplicateContentLengths);
797 if (contentLength != -1) {
798 String lengthValue = contentLengthFields.get(0).trim();
799 if (contentLengthFields.size() > 1 ||
800 !lengthValue.equals(Long.toString(contentLength))) {
801 headers.set(HttpHeaderNames.CONTENT_LENGTH, contentLength);
802 }
803 }
804 } else {
805
806
807 contentLength = HttpUtil.getWebSocketContentLength(message);
808 }
809 if (!isDecodingRequest() && message instanceof HttpResponse) {
810 HttpResponse res = (HttpResponse) message;
811 this.isSwitchingToNonHttp1Protocol = isSwitchingToNonHttp1Protocol(res);
812 }
813 if (isContentAlwaysEmpty(message)) {
814 HttpUtil.setTransferEncodingChunked(message, false);
815 return State.SKIP_CONTROL_CHARS;
816 }
817 if (HttpUtil.isTransferEncodingChunked(message)) {
818 this.chunked = true;
819 if (!contentLengthFields.isEmpty() && message.protocolVersion() == HttpVersion.HTTP_1_1) {
820 handleTransferEncodingChunkedWithContentLength(message);
821 }
822 return State.READ_CHUNK_SIZE;
823 }
824 if (contentLength >= 0) {
825 return State.READ_FIXED_LENGTH_CONTENT;
826 }
827 return State.READ_VARIABLE_LENGTH_CONTENT;
828 }
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851 protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) {
852 message.headers().remove(HttpHeaderNames.CONTENT_LENGTH);
853 contentLength = Long.MIN_VALUE;
854 }
855
856 private LastHttpContent readTrailingHeaders(ByteBuf buffer) {
857 final HeaderParser headerParser = this.headerParser;
858 ByteBuf line = headerParser.parse(buffer, defaultStrictCRLFCheck);
859 if (line == null) {
860 return null;
861 }
862 LastHttpContent trailer = this.trailer;
863 int lineLength = line.readableBytes();
864 if (lineLength == 0 && trailer == null) {
865
866
867 return LastHttpContent.EMPTY_LAST_CONTENT;
868 }
869
870 CharSequence lastHeader = null;
871 if (trailer == null) {
872 trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, trailersFactory);
873 }
874 while (lineLength > 0) {
875 final byte[] lineContent = line.array();
876 final int startLine = line.arrayOffset() + line.readerIndex();
877 final byte firstChar = lineContent[startLine];
878 if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
879 List<String> current = trailer.trailingHeaders().getAll(lastHeader);
880 if (!current.isEmpty()) {
881 int lastPos = current.size() - 1;
882
883
884 String lineTrimmed = langAsciiString(lineContent, startLine, line.readableBytes()).trim();
885 String currentLastPos = current.get(lastPos);
886 current.set(lastPos, currentLastPos + lineTrimmed);
887 }
888 } else {
889 splitHeader(lineContent, startLine, lineLength);
890 AsciiString headerName = name;
891 if (!HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(headerName) &&
892 !HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(headerName) &&
893 !HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(headerName)) {
894 trailer.trailingHeaders().add(headerName, value);
895 }
896 lastHeader = name;
897
898 name = null;
899 value = null;
900 }
901 line = headerParser.parse(buffer, defaultStrictCRLFCheck);
902 if (line == null) {
903 return null;
904 }
905 lineLength = line.readableBytes();
906 }
907
908 this.trailer = null;
909 return trailer;
910 }
911
912 protected abstract boolean isDecodingRequest();
913 protected abstract HttpMessage createMessage(String[] initialLine) throws Exception;
914 protected abstract HttpMessage createInvalidMessage();
915
916
917
918
919 private static int skipWhiteSpaces(byte[] hex, int start, int length) {
920 for (int i = 0; i < length; i++) {
921 if (!isWhitespace(hex[start + i])) {
922 return i;
923 }
924 }
925 return length;
926 }
927
928 private static int getChunkSize(byte[] hex, int start, int length) {
929
930 final int skipped = skipWhiteSpaces(hex, start, length);
931 if (skipped == length) {
932
933 throw new NumberFormatException();
934 }
935 start += skipped;
936 length -= skipped;
937 int result = 0;
938 for (int i = 0; i < length; i++) {
939 final int digit = StringUtil.decodeHexNibble(hex[start + i]);
940 if (digit == -1) {
941
942 final byte b = hex[start + i];
943 if (b == ';' || isControlOrWhitespaceAsciiChar(b)) {
944 if (i == 0) {
945
946 throw new NumberFormatException("Empty chunk size");
947 }
948 return result;
949 }
950
951 throw new NumberFormatException("Invalid character in chunk size");
952 }
953 result *= 16;
954 result += digit;
955 if (result < 0) {
956 throw new NumberFormatException("Chunk size overflow: " + result);
957 }
958 }
959 return result;
960 }
961
962 private String[] splitInitialLine(ByteBuf asciiBuffer) {
963 final byte[] asciiBytes = asciiBuffer.array();
964
965 final int arrayOffset = asciiBuffer.arrayOffset();
966
967 final int startContent = arrayOffset + asciiBuffer.readerIndex();
968
969 final int end = startContent + asciiBuffer.readableBytes();
970
971 byte lastByte = asciiBytes[end - 1];
972 if (isControlOrWhitespaceAsciiChar(lastByte)) {
973 if (isDecodingRequest() || !isOWS(lastByte)) {
974
975
976
977
978
979 throw new IllegalArgumentException(
980 "Illegal character in request line: 0x" + Integer.toHexString(lastByte));
981 }
982 }
983
984 final int aStart = findNonSPLenient(asciiBytes, startContent, end);
985 final int aEnd = findSPLenient(asciiBytes, aStart, end);
986
987 final int bStart = findNonSPLenient(asciiBytes, aEnd, end);
988 final int bEnd = findSPLenient(asciiBytes, bStart, end);
989
990 final int cStart = findNonSPLenient(asciiBytes, bEnd, end);
991 final int cEnd = findEndOfString(asciiBytes, Math.max(cStart - 1, startContent), end);
992
993 return new String[]{
994 splitFirstWordInitialLine(asciiBytes, aStart, aEnd - aStart),
995 splitSecondWordInitialLine(asciiBytes, bStart, bEnd - bStart),
996 cStart < cEnd ? splitThirdWordInitialLine(asciiBytes, cStart, cEnd - cStart) : StringUtil.EMPTY_STRING};
997 }
998
999 protected String splitFirstWordInitialLine(final byte[] asciiContent, int start, int length) {
1000 return langAsciiString(asciiContent, start, length);
1001 }
1002
1003 protected String splitSecondWordInitialLine(final byte[] asciiContent, int start, int length) {
1004 return langAsciiString(asciiContent, start, length);
1005 }
1006
1007 protected String splitThirdWordInitialLine(final byte[] asciiContent, int start, int length) {
1008 return langAsciiString(asciiContent, start, length);
1009 }
1010
1011
1012
1013
1014 private static String langAsciiString(final byte[] asciiContent, int start, int length) {
1015 if (length == 0) {
1016 return StringUtil.EMPTY_STRING;
1017 }
1018
1019 if (start == 0) {
1020 if (length == asciiContent.length) {
1021 return new String(asciiContent, 0, 0, asciiContent.length);
1022 }
1023 return new String(asciiContent, 0, 0, length);
1024 }
1025 return new String(asciiContent, 0, start, length);
1026 }
1027
1028 private void splitHeader(byte[] line, int start, int length) {
1029 final int end = start + length;
1030 int nameEnd;
1031 final int nameStart = start;
1032
1033 final boolean isDecodingRequest = isDecodingRequest();
1034 for (nameEnd = nameStart; nameEnd < end; nameEnd ++) {
1035 byte ch = line[nameEnd];
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045 if (ch == ':' ||
1046
1047
1048
1049
1050 (!isDecodingRequest && isOWS(ch))) {
1051 break;
1052 }
1053 }
1054
1055 if (nameEnd == end) {
1056
1057 throw new IllegalArgumentException("No colon found");
1058 }
1059 int colonEnd;
1060 for (colonEnd = nameEnd; colonEnd < end; colonEnd ++) {
1061 if (line[colonEnd] == ':') {
1062 colonEnd ++;
1063 break;
1064 }
1065 }
1066 name = splitHeaderName(line, nameStart, nameEnd - nameStart);
1067 final int valueStart = findNonWhitespace(line, colonEnd, end);
1068 if (valueStart == end) {
1069 value = StringUtil.EMPTY_STRING;
1070 } else {
1071 final int valueEnd = findEndOfString(line, start, end);
1072
1073 value = langAsciiString(line, valueStart, valueEnd - valueStart);
1074 }
1075 }
1076
1077 protected AsciiString splitHeaderName(byte[] sb, int start, int length) {
1078 return new AsciiString(sb, start, length, true);
1079 }
1080
1081 private static int findNonSPLenient(byte[] sb, int offset, int end) {
1082 for (int result = offset; result < end; ++result) {
1083 byte c = sb[result];
1084
1085 if (isSPLenient(c)) {
1086 continue;
1087 }
1088 if (isWhitespace(c)) {
1089
1090 throw new IllegalArgumentException("Invalid separator");
1091 }
1092 return result;
1093 }
1094 return end;
1095 }
1096
1097 private static int findSPLenient(byte[] sb, int offset, int end) {
1098 for (int result = offset; result < end; ++result) {
1099 if (isSPLenient(sb[result])) {
1100 return result;
1101 }
1102 }
1103 return end;
1104 }
1105
1106 private static final boolean[] SP_LENIENT_BYTES;
1107 private static final boolean[] LATIN_WHITESPACE;
1108
1109 static {
1110
1111 SP_LENIENT_BYTES = new boolean[256];
1112 SP_LENIENT_BYTES[128 + ' '] = true;
1113 SP_LENIENT_BYTES[128 + 0x09] = true;
1114 SP_LENIENT_BYTES[128 + 0x0B] = true;
1115 SP_LENIENT_BYTES[128 + 0x0C] = true;
1116 SP_LENIENT_BYTES[128 + 0x0D] = true;
1117
1118 LATIN_WHITESPACE = new boolean[256];
1119 for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
1120 LATIN_WHITESPACE[128 + b] = Character.isWhitespace(b);
1121 }
1122 }
1123
1124 private static boolean isSPLenient(byte c) {
1125
1126 return SP_LENIENT_BYTES[c + 128];
1127 }
1128
1129 private static boolean isWhitespace(byte b) {
1130 return LATIN_WHITESPACE[b + 128];
1131 }
1132
1133 private static int findNonWhitespace(byte[] sb, int offset, int end) {
1134 for (int result = offset; result < end; ++result) {
1135 byte c = sb[result];
1136 if (!isWhitespace(c)) {
1137 return result;
1138 } else if (!isOWS(c)) {
1139
1140 throw new IllegalArgumentException("Invalid separator, only a single space or horizontal tab allowed," +
1141 " but received a '" + c + "' (0x" + Integer.toHexString(c) + ")");
1142 }
1143 }
1144 return end;
1145 }
1146
1147 private static int findEndOfString(byte[] sb, int start, int end) {
1148 for (int result = end - 1; result > start; --result) {
1149 if (!isOWS(sb[result])) {
1150 return result + 1;
1151 }
1152 }
1153 return 0;
1154 }
1155
1156 private static boolean isOWS(byte ch) {
1157 return ch == ' ' || ch == 0x09;
1158 }
1159
1160 private static class HeaderParser {
1161 protected final ByteBuf seq;
1162 protected final int maxLength;
1163 int size;
1164
1165 HeaderParser(ByteBuf seq, int maxLength) {
1166 this.seq = seq;
1167 this.maxLength = maxLength;
1168 }
1169
1170 public ByteBuf parse(ByteBuf buffer, Runnable strictCRLFCheck) {
1171 final int readableBytes = buffer.readableBytes();
1172 final int readerIndex = buffer.readerIndex();
1173 final int maxBodySize = maxLength - size;
1174 assert maxBodySize >= 0;
1175
1176
1177 final long maxBodySizeWithCRLF = maxBodySize + 2L;
1178 final int toProcess = (int) Math.min(maxBodySizeWithCRLF, readableBytes);
1179 final int toIndexExclusive = readerIndex + toProcess;
1180 assert toIndexExclusive >= readerIndex;
1181 final int indexOfLf = buffer.indexOf(readerIndex, toIndexExclusive, HttpConstants.LF);
1182 if (indexOfLf == -1) {
1183 if (readableBytes > maxBodySize) {
1184
1185
1186
1187
1188 throw newException(maxLength);
1189 }
1190 return null;
1191 }
1192 final int endOfSeqIncluded;
1193 if (indexOfLf > readerIndex && buffer.getByte(indexOfLf - 1) == HttpConstants.CR) {
1194
1195 endOfSeqIncluded = indexOfLf - 1;
1196 } else {
1197 if (strictCRLFCheck != null) {
1198 strictCRLFCheck.run();
1199 }
1200 endOfSeqIncluded = indexOfLf;
1201 }
1202 final int newSize = endOfSeqIncluded - readerIndex;
1203 if (newSize == 0) {
1204 seq.clear();
1205 buffer.readerIndex(indexOfLf + 1);
1206 return seq;
1207 }
1208 int size = this.size + newSize;
1209 if (size > maxLength) {
1210 throw newException(maxLength);
1211 }
1212 this.size = size;
1213 seq.clear();
1214 seq.writeBytes(buffer, readerIndex, newSize);
1215 buffer.readerIndex(indexOfLf + 1);
1216 return seq;
1217 }
1218
1219 public void reset() {
1220 size = 0;
1221 }
1222
1223 protected TooLongFrameException newException(int maxLength) {
1224 return new TooLongHttpHeaderException("HTTP header is larger than " + maxLength + " bytes.");
1225 }
1226 }
1227
1228 private final class LineParser extends HeaderParser {
1229
1230 LineParser(ByteBuf seq, int maxLength) {
1231 super(seq, maxLength);
1232 }
1233
1234 @Override
1235 public ByteBuf parse(ByteBuf buffer, Runnable strictCRLFCheck) {
1236
1237 reset();
1238 final int readableBytes = buffer.readableBytes();
1239 if (readableBytes == 0) {
1240 return null;
1241 }
1242 if (currentState == State.SKIP_CONTROL_CHARS &&
1243 skipControlChars(buffer, readableBytes, buffer.readerIndex())) {
1244 return null;
1245 }
1246 return super.parse(buffer, strictCRLFCheck);
1247 }
1248
1249 private boolean skipControlChars(ByteBuf buffer, int readableBytes, int readerIndex) {
1250 assert currentState == State.SKIP_CONTROL_CHARS;
1251 final int maxToSkip = Math.min(maxLength, readableBytes);
1252 final int firstNonControlIndex = buffer.forEachByte(readerIndex, maxToSkip, SKIP_CONTROL_CHARS_BYTES);
1253 if (firstNonControlIndex == -1) {
1254 buffer.skipBytes(maxToSkip);
1255 if (readableBytes > maxLength) {
1256 throw newException(maxLength);
1257 }
1258 return true;
1259 }
1260
1261 buffer.readerIndex(firstNonControlIndex);
1262 currentState = State.READ_INITIAL;
1263 return false;
1264 }
1265
1266 @Override
1267 protected TooLongFrameException newException(int maxLength) {
1268 return new TooLongHttpLineException("An HTTP line is larger than " + maxLength + " bytes.");
1269 }
1270 }
1271
1272 private static final boolean[] ISO_CONTROL_OR_WHITESPACE;
1273
1274 static {
1275 ISO_CONTROL_OR_WHITESPACE = new boolean[256];
1276 for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
1277 ISO_CONTROL_OR_WHITESPACE[128 + b] = Character.isISOControl(b) || isWhitespace(b);
1278 }
1279 }
1280
1281 private static final ByteProcessor SKIP_CONTROL_CHARS_BYTES = new ByteProcessor() {
1282
1283 @Override
1284 public boolean process(byte value) {
1285 return ISO_CONTROL_OR_WHITESPACE[128 + value];
1286 }
1287 };
1288
1289 private static boolean isControlOrWhitespaceAsciiChar(byte b) {
1290 return ISO_CONTROL_OR_WHITESPACE[128 + b];
1291 }
1292 }