26
26
import io .netty .util .AsciiString ;
27
27
import io .netty .util .ByteProcessor ;
28
28
import io .netty .util .internal .StringUtil ;
29
+ import io .netty .util .internal .SystemPropertyUtil ;
29
30
30
31
import java .util .List ;
31
32
import java .util .concurrent .atomic .AtomicBoolean ;
@@ -151,6 +152,23 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
151
152
public static final boolean DEFAULT_VALIDATE_HEADERS = true ;
152
153
public static final int DEFAULT_INITIAL_BUFFER_SIZE = 128 ;
153
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
+
154
172
private final int maxChunkSize ;
155
173
private final boolean chunkedSupported ;
156
174
private final boolean allowPartialChunks ;
@@ -163,6 +181,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
163
181
protected final HttpHeadersFactory trailersFactory ;
164
182
private final boolean allowDuplicateContentLengths ;
165
183
private final ByteBuf parserScratchBuffer ;
184
+ private final Runnable defaultStrictCRLFCheck ;
166
185
private final HeaderParser headerParser ;
167
186
private final LineParser lineParser ;
168
187
@@ -171,7 +190,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
171
190
private long contentLength = Long .MIN_VALUE ;
172
191
private boolean chunked ;
173
192
private boolean isSwitchingToNonHttp1Protocol ;
174
-
193
+
175
194
// Liberty specific configurations
176
195
private int limitFieldSize ;
177
196
private int limitNumHeaders ;
@@ -320,6 +339,7 @@ protected HttpObjectDecoder(HttpDecoderConfig config) {
320
339
checkNotNull (config , "config" );
321
340
322
341
parserScratchBuffer = Unpooled .buffer (config .getInitialBufferSize ());
342
+ defaultStrictCRLFCheck = config .isStrictLineParsing () ? THROW_INVALID_LINE_SEPARATOR : null ;
323
343
shouldDoLibertyCheck = config .isLibertyHttpHeaderOptionsSet ();
324
344
lineParser = new LineParser (parserScratchBuffer , config .getMaxInitialLineLength ());
325
345
headerParser = new HeaderParser (parserScratchBuffer , shouldDoLibertyCheck ? -1 : config .getMaxHeaderSize ());
@@ -347,12 +367,12 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> ou
347
367
if (resetRequested .get ()) {
348
368
resetNow ();
349
369
}
350
-
370
+
351
371
switch (currentState ) {
352
372
case SKIP_CONTROL_CHARS :
353
373
// Fall-through
354
374
case READ_INITIAL : try {
355
- ByteBuf line = lineParser .parse (buffer );
375
+ ByteBuf line = lineParser .parse (buffer , defaultStrictCRLFCheck );
356
376
if (line == null ) {
357
377
return ;
358
378
}
@@ -461,7 +481,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> ou
461
481
* read chunk, read and ignore the CRLF and repeat until 0
462
482
*/
463
483
case READ_CHUNK_SIZE : try {
464
- ByteBuf line = lineParser .parse (buffer );
484
+ ByteBuf line = lineParser .parse (buffer , THROW_INVALID_CHUNK_EXTENSION );
465
485
if (line == null ) {
466
486
return ;
467
487
}
@@ -499,16 +519,16 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> ou
499
519
// fall-through
500
520
}
501
521
case READ_CHUNK_DELIMITER : {
502
- final int wIdx = buffer .writerIndex ();
503
- int rIdx = buffer .readerIndex ();
504
- while ( wIdx > rIdx ) {
505
- byte next = buffer .getByte (rIdx ++);
506
- if ( next == HttpConstants . LF ) {
522
+ if ( buffer .readableBytes () >= 2 ) {
523
+ int rIdx = buffer .readerIndex ();
524
+ if ( buffer . getByte ( rIdx ) == HttpConstants . CR &&
525
+ buffer .getByte (rIdx + 1 ) == HttpConstants . LF ) {
526
+ buffer . skipBytes ( 2 );
507
527
currentState = State .READ_CHUNK_SIZE ;
508
- break ;
528
+ } else {
529
+ out .add (invalidChunk (buffer , new InvalidChunkTerminationException ()));
509
530
}
510
531
}
511
- buffer .readerIndex (rIdx );
512
532
return ;
513
533
}
514
534
case READ_CHUNK_FOOTER : try {
@@ -731,7 +751,7 @@ private State readHeaders(ByteBuf buffer) {
731
751
732
752
final HeaderParser headerParser = this .headerParser ;
733
753
734
- ByteBuf line = headerParser .parse (buffer );
754
+ ByteBuf line = headerParser .parse (buffer , defaultStrictCRLFCheck );
735
755
if (line == null ) {
736
756
return null ;
737
757
}
@@ -759,7 +779,7 @@ private State readHeaders(ByteBuf buffer) {
759
779
splitHeader (lineContent , startLine , lineLength );
760
780
}
761
781
762
- line = headerParser .parse (buffer );
782
+ line = headerParser .parse (buffer , defaultStrictCRLFCheck );
763
783
if (line == null ) {
764
784
return null ;
765
785
}
@@ -855,7 +875,7 @@ protected void handleTransferEncodingChunkedWithContentLength(HttpMessage messag
855
875
856
876
private LastHttpContent readTrailingHeaders (ByteBuf buffer ) {
857
877
final HeaderParser headerParser = this .headerParser ;
858
- ByteBuf line = headerParser .parse (buffer );
878
+ ByteBuf line = headerParser .parse (buffer , defaultStrictCRLFCheck );
859
879
if (line == null ) {
860
880
return null ;
861
881
}
@@ -898,7 +918,7 @@ private LastHttpContent readTrailingHeaders(ByteBuf buffer) {
898
918
name = null ;
899
919
value = null ;
900
920
}
901
- line = headerParser .parse (buffer );
921
+ line = headerParser .parse (buffer , defaultStrictCRLFCheck );
902
922
if (line == null ) {
903
923
return null ;
904
924
}
@@ -1167,7 +1187,7 @@ private static class HeaderParser {
1167
1187
this .maxLength = maxLength ;
1168
1188
}
1169
1189
1170
- public ByteBuf parse (ByteBuf buffer ) {
1190
+ public ByteBuf parse (ByteBuf buffer , Runnable strictCRLFCheck ) {
1171
1191
final int readableBytes = buffer .readableBytes ();
1172
1192
final int readerIndex = buffer .readerIndex ();
1173
1193
long maxAllowedBody = 0 ;
@@ -1199,6 +1219,9 @@ public ByteBuf parse(ByteBuf buffer) {
1199
1219
// Drop CR if we had a CRLF pair
1200
1220
endOfSeqIncluded = indexOfLf - 1 ;
1201
1221
} else {
1222
+ if (strictCRLFCheck != null ) {
1223
+ strictCRLFCheck .run ();
1224
+ }
1202
1225
endOfSeqIncluded = indexOfLf ;
1203
1226
}
1204
1227
final int newSize = endOfSeqIncluded - readerIndex ;
@@ -1234,18 +1257,18 @@ private final class LineParser extends HeaderParser {
1234
1257
}
1235
1258
1236
1259
@ Override
1237
- public ByteBuf parse (ByteBuf buffer ) {
1260
+ public ByteBuf parse (ByteBuf buffer , Runnable strictCRLFCheck ) {
1238
1261
// Suppress a warning because HeaderParser.reset() is supposed to be called
1239
1262
reset ();
1240
1263
final int readableBytes = buffer .readableBytes ();
1241
1264
if (readableBytes == 0 ) {
1242
1265
return null ;
1243
1266
}
1244
- final int readerIndex = buffer . readerIndex ();
1245
- if ( currentState == State . SKIP_CONTROL_CHARS && skipControlChars (buffer , readableBytes , readerIndex )) {
1267
+ if ( currentState == State . SKIP_CONTROL_CHARS &&
1268
+ skipControlChars (buffer , readableBytes , buffer . readerIndex () )) {
1246
1269
return null ;
1247
1270
}
1248
- return super .parse (buffer );
1271
+ return super .parse (buffer , strictCRLFCheck );
1249
1272
}
1250
1273
1251
1274
private boolean skipControlChars (ByteBuf buffer , int readableBytes , int readerIndex ) {
0 commit comments