Skip to content

Commit cb405dd

Browse files
committed
Added overlayed Netty changes for the version update
1 parent b8e2479 commit cb405dd

File tree

3 files changed

+72
-166
lines changed

3 files changed

+72
-166
lines changed

dev/io.openliberty.io.netty/src/io/netty/handler/codec/http/HttpDecoderConfig.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public final class HttpDecoderConfig implements Cloneable {
3434
private int maxInitialLineLength = HttpObjectDecoder.DEFAULT_MAX_INITIAL_LINE_LENGTH;
3535
private int maxHeaderSize = HttpObjectDecoder.DEFAULT_MAX_HEADER_SIZE;
3636
private int initialBufferSize = HttpObjectDecoder.DEFAULT_INITIAL_BUFFER_SIZE;
37+
private boolean strictLineParsing = HttpObjectDecoder.DEFAULT_STRICT_LINE_PARSING;
3738

3839
// Liberty Http Options
3940
private int limitFieldSize = 32768;
@@ -268,6 +269,34 @@ public HttpDecoderConfig setTrailersFactory(HttpHeadersFactory trailersFactory)
268269
return this;
269270
}
270271

272+
public boolean isStrictLineParsing() {
273+
return strictLineParsing;
274+
}
275+
/**
276+
* The RFC 9112 specification for the HTTP protocol says that the initial start-line, and the following header
277+
* field-lines, must be separated by a Carriage Return (CR) and Line Feed (LF) octet pair, but also offers that
278+
* implementations "MAY" accept just a Line Feed octet as a separator.
279+
* <p>
280+
* Parsing leniencies can increase compatibility with a wider range of implementations, but can also cause
281+
* security vulnerabilities, when multiple systems disagree on the meaning of leniently parsed messages.
282+
* <p>
283+
* When <em>strict line parsing</em> is enabled ({@code true}), then Netty will enforce that start- and header
284+
* field-lines MUST be separated by a CR LF octet pair, and will produce messagas with failed
285+
* {@link io.netty.handler.codec.DecoderResult}s.
286+
* <p>
287+
* When <em>strict line parsing</em> is disabled ({@code false}), then Netty will accept lone LF octets as line
288+
* seperators for the start- and header field-lines.
289+
* <p>
290+
* See <a href="https://datatracker.ietf.org/doc/html/rfc9112#name-message-format">RFC 9112 Section 2.1</a>.
291+
* @param strictLineParsing Whether <em>strict line parsing</em> should be enabled ({@code true}),
292+
* or not ({@code false}).
293+
* @return This decoder config.
294+
*/
295+
public HttpDecoderConfig setStrictLineParsing(boolean strictLineParsing) {
296+
this.strictLineParsing = strictLineParsing;
297+
return this;
298+
}
299+
271300
@Override
272301
public HttpDecoderConfig clone() {
273302
try {

dev/io.openliberty.io.netty/src/io/netty/handler/codec/http/HttpObjectDecoder.java

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.netty.util.AsciiString;
2727
import io.netty.util.ByteProcessor;
2828
import io.netty.util.internal.StringUtil;
29+
import io.netty.util.internal.SystemPropertyUtil;
2930

3031
import java.util.List;
3132
import java.util.concurrent.atomic.AtomicBoolean;
@@ -151,6 +152,23 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
151152
public static final boolean DEFAULT_VALIDATE_HEADERS = true;
152153
public static final int DEFAULT_INITIAL_BUFFER_SIZE = 128;
153154
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+
154172
private final int maxChunkSize;
155173
private final boolean chunkedSupported;
156174
private final boolean allowPartialChunks;
@@ -163,6 +181,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
163181
protected final HttpHeadersFactory trailersFactory;
164182
private final boolean allowDuplicateContentLengths;
165183
private final ByteBuf parserScratchBuffer;
184+
private final Runnable defaultStrictCRLFCheck;
166185
private final HeaderParser headerParser;
167186
private final LineParser lineParser;
168187

@@ -171,7 +190,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
171190
private long contentLength = Long.MIN_VALUE;
172191
private boolean chunked;
173192
private boolean isSwitchingToNonHttp1Protocol;
174-
193+
175194
// Liberty specific configurations
176195
private int limitFieldSize;
177196
private int limitNumHeaders;
@@ -320,6 +339,7 @@ protected HttpObjectDecoder(HttpDecoderConfig config) {
320339
checkNotNull(config, "config");
321340

322341
parserScratchBuffer = Unpooled.buffer(config.getInitialBufferSize());
342+
defaultStrictCRLFCheck = config.isStrictLineParsing() ? THROW_INVALID_LINE_SEPARATOR : null;
323343
shouldDoLibertyCheck = config.isLibertyHttpHeaderOptionsSet();
324344
lineParser = new LineParser(parserScratchBuffer, config.getMaxInitialLineLength());
325345
headerParser = new HeaderParser(parserScratchBuffer, shouldDoLibertyCheck ? -1 : config.getMaxHeaderSize());
@@ -347,12 +367,12 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> ou
347367
if (resetRequested.get()) {
348368
resetNow();
349369
}
350-
370+
351371
switch (currentState) {
352372
case SKIP_CONTROL_CHARS:
353373
// Fall-through
354374
case READ_INITIAL: try {
355-
ByteBuf line = lineParser.parse(buffer);
375+
ByteBuf line = lineParser.parse(buffer, defaultStrictCRLFCheck);
356376
if (line == null) {
357377
return;
358378
}
@@ -461,7 +481,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> ou
461481
* read chunk, read and ignore the CRLF and repeat until 0
462482
*/
463483
case READ_CHUNK_SIZE: try {
464-
ByteBuf line = lineParser.parse(buffer);
484+
ByteBuf line = lineParser.parse(buffer, THROW_INVALID_CHUNK_EXTENSION);
465485
if (line == null) {
466486
return;
467487
}
@@ -499,16 +519,16 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> ou
499519
// fall-through
500520
}
501521
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);
507527
currentState = State.READ_CHUNK_SIZE;
508-
break;
528+
} else {
529+
out.add(invalidChunk(buffer, new InvalidChunkTerminationException()));
509530
}
510531
}
511-
buffer.readerIndex(rIdx);
512532
return;
513533
}
514534
case READ_CHUNK_FOOTER: try {
@@ -731,7 +751,7 @@ private State readHeaders(ByteBuf buffer) {
731751

732752
final HeaderParser headerParser = this.headerParser;
733753

734-
ByteBuf line = headerParser.parse(buffer);
754+
ByteBuf line = headerParser.parse(buffer, defaultStrictCRLFCheck);
735755
if (line == null) {
736756
return null;
737757
}
@@ -759,7 +779,7 @@ private State readHeaders(ByteBuf buffer) {
759779
splitHeader(lineContent, startLine, lineLength);
760780
}
761781

762-
line = headerParser.parse(buffer);
782+
line = headerParser.parse(buffer, defaultStrictCRLFCheck);
763783
if (line == null) {
764784
return null;
765785
}
@@ -855,7 +875,7 @@ protected void handleTransferEncodingChunkedWithContentLength(HttpMessage messag
855875

856876
private LastHttpContent readTrailingHeaders(ByteBuf buffer) {
857877
final HeaderParser headerParser = this.headerParser;
858-
ByteBuf line = headerParser.parse(buffer);
878+
ByteBuf line = headerParser.parse(buffer, defaultStrictCRLFCheck);
859879
if (line == null) {
860880
return null;
861881
}
@@ -898,7 +918,7 @@ private LastHttpContent readTrailingHeaders(ByteBuf buffer) {
898918
name = null;
899919
value = null;
900920
}
901-
line = headerParser.parse(buffer);
921+
line = headerParser.parse(buffer, defaultStrictCRLFCheck);
902922
if (line == null) {
903923
return null;
904924
}
@@ -1167,7 +1187,7 @@ private static class HeaderParser {
11671187
this.maxLength = maxLength;
11681188
}
11691189

1170-
public ByteBuf parse(ByteBuf buffer) {
1190+
public ByteBuf parse(ByteBuf buffer, Runnable strictCRLFCheck) {
11711191
final int readableBytes = buffer.readableBytes();
11721192
final int readerIndex = buffer.readerIndex();
11731193
long maxAllowedBody = 0;
@@ -1199,6 +1219,9 @@ public ByteBuf parse(ByteBuf buffer) {
11991219
// Drop CR if we had a CRLF pair
12001220
endOfSeqIncluded = indexOfLf - 1;
12011221
} else {
1222+
if (strictCRLFCheck != null) {
1223+
strictCRLFCheck.run();
1224+
}
12021225
endOfSeqIncluded = indexOfLf;
12031226
}
12041227
final int newSize = endOfSeqIncluded - readerIndex;
@@ -1234,18 +1257,18 @@ private final class LineParser extends HeaderParser {
12341257
}
12351258

12361259
@Override
1237-
public ByteBuf parse(ByteBuf buffer) {
1260+
public ByteBuf parse(ByteBuf buffer, Runnable strictCRLFCheck) {
12381261
// Suppress a warning because HeaderParser.reset() is supposed to be called
12391262
reset();
12401263
final int readableBytes = buffer.readableBytes();
12411264
if (readableBytes == 0) {
12421265
return null;
12431266
}
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())) {
12461269
return null;
12471270
}
1248-
return super.parse(buffer);
1271+
return super.parse(buffer, strictCRLFCheck);
12491272
}
12501273

12511274
private boolean skipControlChars(ByteBuf buffer, int readableBytes, int readerIndex) {

dev/io.openliberty.io.netty/src/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerBuilder.java

Lines changed: 0 additions & 146 deletions
This file was deleted.

0 commit comments

Comments
 (0)