Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP7:Update
netty.18722
netty-CVE-2021-21295.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File netty-CVE-2021-21295.patch of Package netty.18722
--- netty-netty-4.1.13.Final/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java 2017-07-06 13:23:51.000000000 +0200 +++ netty-netty-4.1.13.Final/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java 2021-03-12 11:48:15.242277753 +0100 @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.http; +import static io.netty.util.internal.ObjectUtil.checkPositive; + import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; @@ -105,6 +107,7 @@ private final int maxChunkSize; private final boolean chunkedSupported; protected final boolean validateHeaders; + private final boolean allowDuplicateContentLengths; private final HeaderParser headerParser; private final LineParser lineParser; @@ -165,9 +168,20 @@ this(maxInitialLineLength, maxHeaderSize, maxChunkSize, chunkedSupported, validateHeaders, 128); } + /** + * Creates a new instance with the specified parameters. + */ protected HttpObjectDecoder( int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean chunkedSupported, boolean validateHeaders, int initialBufferSize) { + this(maxInitialLineLength, maxHeaderSize, maxChunkSize, chunkedSupported, validateHeaders, initialBufferSize, + false); + } + + protected HttpObjectDecoder( + int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, + boolean chunkedSupported, boolean validateHeaders, int initialBufferSize, + boolean allowDuplicateContentLengths) { if (maxInitialLineLength <= 0) { throw new IllegalArgumentException( "maxInitialLineLength must be a positive integer: " + @@ -189,6 +203,7 @@ this.maxChunkSize = maxChunkSize; this.chunkedSupported = chunkedSupported; this.validateHeaders = validateHeaders; + this.allowDuplicateContentLengths = allowDuplicateContentLengths; } @Override @@ -602,6 +617,19 @@ value = null; State nextState; + List<String> contentLengthFields = headers.getAll(HttpHeaderNames.CONTENT_LENGTH); + if (!contentLengthFields.isEmpty()) { + HttpVersion version = message.protocolVersion(); + boolean isHttp10OrEarlier = version.majorVersion() < 1 || (version.majorVersion() == 1 + && version.minorVersion() == 0); + // Guard against multiple Content-Length headers as stated in + // https://tools.ietf.org/html/rfc7230#section-3.3.2: + contentLength = HttpUtil.normalizeAndGetContentLength(contentLengthFields, + isHttp10OrEarlier, allowDuplicateContentLengths); + if (contentLength != -1) { + headers.set(HttpHeaderNames.CONTENT_LENGTH, contentLength); + } + } if (isContentAlwaysEmpty(message)) { HttpUtil.setTransferEncodingChunked(message, false); --- netty-netty-4.1.13.Final/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java 2017-07-06 13:23:51.000000000 +0200 +++ netty-netty-4.1.13.Final/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java 2021-03-12 11:48:15.242277753 +0100 @@ -74,6 +74,27 @@ return UNKNOWN; } + /** + * Returns the class of the specified HTTP status code. + * @param code Just the numeric portion of the http status code. + */ + public static HttpStatusClass valueOf(CharSequence code) { + if (code != null && code.length() == 3) { + char c0 = code.charAt(0); + return isDigit(c0) && isDigit(code.charAt(1)) && isDigit(code.charAt(2)) ? valueOf(digit(c0) * 100) + : UNKNOWN; + } + return UNKNOWN; + } + + private static int digit(char c) { + return c - '0'; + } + + private static boolean isDigit(char c) { + return c >= '0' && c <= '9'; + } + private final int min; private final int max; private final AsciiString defaultReasonPhrase; --- netty-netty-4.1.13.Final/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java 2017-07-06 13:23:51.000000000 +0200 +++ netty-netty-4.1.13.Final/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java 2021-03-12 11:48:15.242277753 +0100 @@ -18,6 +18,7 @@ import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; +import java.net.InetSocketAddress; import java.net.URI; import java.util.ArrayList; import java.nio.charset.Charset; @@ -25,6 +26,15 @@ import java.util.Iterator; import java.util.List; +import io.netty.handler.codec.Headers; +import io.netty.util.AsciiString; +import io.netty.util.CharsetUtil; +import io.netty.util.NetUtil; +import io.netty.util.internal.ObjectUtil; +import io.netty.util.internal.UnstableApi; + +import static io.netty.util.internal.StringUtil.COMMA; + /** * Utility methods useful in the HTTP context. */ @@ -40,6 +50,7 @@ static final EmptyHttpHeaders EMPTY_HEADERS = new EmptyHttpHeaders(); private static final AsciiString CHARSET_EQUALS = AsciiString.of(HttpHeaderValues.CHARSET + "="); private static final AsciiString SEMICOLON = AsciiString.of(";"); + private static final String COMMA_STRING = String.valueOf(COMMA); private HttpUtil() { } @@ -519,4 +530,103 @@ return contentTypeValue.length() > 0 ? contentTypeValue : null; } } + + /** + * Formats the host string of an address so it can be used for computing an HTTP component + * such as a URL or a Host header + * + * @param addr the address + * @return the formatted String + */ + public static String formatHostnameForHttp(InetSocketAddress addr) { + String hostString = NetUtil.getHostname(addr); + if (NetUtil.isValidIpV6Address(hostString)) { + if (!addr.isUnresolved()) { + hostString = NetUtil.toAddressString(addr.getAddress()); + } + return '[' + hostString + ']'; + } + return hostString; + } + + /** + * Validates, and optionally extracts the content length from headers. This method is not intended for + * general use, but is here to be shared between HTTP/1 and HTTP/2 parsing. + * + * @param contentLengthFields the content-length header fields. + * @param isHttp10OrEarlier {@code true} if we are handling HTTP/1.0 or earlier + * @param allowDuplicateContentLengths {@code true} if multiple, identical-value content lengths should be allowed. + * @return the normalized content length from the headers or {@code -1} if the fields were empty. + * @throws IllegalArgumentException if the content-length fields are not valid + */ + @UnstableApi + public static long normalizeAndGetContentLength( + List<? extends CharSequence> contentLengthFields, boolean isHttp10OrEarlier, + boolean allowDuplicateContentLengths) { + if (contentLengthFields.isEmpty()) { + return -1; + } + + // Guard against multiple Content-Length headers as stated in + // https://tools.ietf.org/html/rfc7230#section-3.3.2: + // + // If a message is received that has multiple Content-Length header + // fields with field-values consisting of the same decimal value, or a + // single Content-Length header field with a field value containing a + // list of identical decimal values (e.g., "Content-Length: 42, 42"), + // indicating that duplicate Content-Length header fields have been + // generated or combined by an upstream message processor, then the + // recipient MUST either reject the message as invalid or replace the + // duplicated field-values with a single valid Content-Length field + // containing that decimal value prior to determining the message body + // length or forwarding the message. + String firstField = contentLengthFields.get(0).toString(); + boolean multipleContentLengths = + contentLengthFields.size() > 1 || firstField.indexOf(COMMA) >= 0; + + if (multipleContentLengths && !isHttp10OrEarlier) { + if (allowDuplicateContentLengths) { + // Find and enforce that all Content-Length values are the same + String firstValue = null; + for (CharSequence field : contentLengthFields) { + String[] tokens = field.toString().split(COMMA_STRING, -1); + for (String token : tokens) { + String trimmed = token.trim(); + if (firstValue == null) { + firstValue = trimmed; + } else if (!trimmed.equals(firstValue)) { + throw new IllegalArgumentException( + "Multiple Content-Length values found: " + contentLengthFields); + } + } + } + // Replace the duplicated field-values with a single valid Content-Length field + firstField = firstValue; + } else { + // Reject the message as invalid + throw new IllegalArgumentException( + "Multiple Content-Length values found: " + contentLengthFields); + } + } + // Ensure we not allow sign as part of the content-length: + // See https://github.com/squid-cache/squid/security/advisories/GHSA-qf3v-rc95-96j5 + if (!Character.isDigit(firstField.charAt(0))) { + // Reject the message as invalid + throw new IllegalArgumentException( + "Content-Length value is not a number: " + firstField); + } + try { + final long value = Long.parseLong(firstField); + if (value < 0) { + // Reject the message as invalid + throw new IllegalArgumentException( + "Content-Length value must be >=0: " + value); + } + return value; + } catch (NumberFormatException e) { + // Reject the message as invalid + throw new IllegalArgumentException( + "Content-Length value is not a number: " + firstField, e); + } + } } --- netty-netty-4.1.13.Final/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java 2017-07-06 13:23:51.000000000 +0200 +++ netty-netty-4.1.13.Final/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java 2021-03-12 11:48:15.242277753 +0100 @@ -16,13 +16,18 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpStatusClass; +import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http2.Http2Connection.Endpoint; +import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.UnstableApi; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import java.util.List; +import static io.netty.handler.codec.http.HttpStatusClass.INFORMATIONAL; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; @@ -47,6 +52,8 @@ */ @UnstableApi public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder { + private static final boolean VALIDATE_CONTENT_LENGTH = + SystemPropertyUtil.getBoolean("io.netty.http2.validateContentLength", true); private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultHttp2ConnectionDecoder.class); private Http2FrameListener internalFrameListener = new PrefaceFrameListener(); private final Http2Connection connection; @@ -55,6 +62,7 @@ private final Http2FrameReader frameReader; private Http2FrameListener listener; private final Http2PromisedRequestVerifier requestVerifier; + private final Http2Connection.PropertyKey contentLengthKey; public DefaultHttp2ConnectionDecoder(Http2Connection connection, Http2ConnectionEncoder encoder, @@ -67,6 +75,7 @@ Http2FrameReader frameReader, Http2PromisedRequestVerifier requestVerifier) { this.connection = checkNotNull(connection, "connection"); + contentLengthKey = this.connection.newKey(); this.frameReader = checkNotNull(frameReader, "frameReader"); this.encoder = checkNotNull(encoder, "encoder"); this.requestVerifier = checkNotNull(requestVerifier, "requestVerifier"); @@ -169,6 +178,23 @@ listener.onUnknownFrame(ctx, frameType, streamId, flags, payload); } + // See https://tools.ietf.org/html/rfc7540#section-8.1.2.6 + private void verifyContentLength(Http2Stream stream, int data, boolean isEnd) throws Http2Exception { + if (!VALIDATE_CONTENT_LENGTH) { + return; + } + ContentLength contentLength = stream.getProperty(contentLengthKey); + if (contentLength != null) { + try { + contentLength.increaseReceivedBytes(connection.isServer(), stream.id(), data, isEnd); + } finally { + if (isEnd) { + stream.removeProperty(contentLengthKey); + } + } + } + } + /** * Handles all inbound frames from the network. */ @@ -178,7 +204,8 @@ boolean endOfStream) throws Http2Exception { Http2Stream stream = connection.stream(streamId); Http2LocalFlowController flowController = flowController(); - int bytesToReturn = data.readableBytes() + padding; + int readable = data.readableBytes(); + int bytesToReturn = readable + padding; final boolean shouldIgnore; try { @@ -233,6 +259,8 @@ throw error; } + verifyContentLength(stream, readable, endOfStream); + // Call back the application and retrieve the number of bytes that have been // immediately processed. bytesToReturn = listener.onDataRead(ctx, streamId, data, padding, endOfStream); @@ -282,6 +310,14 @@ return; } + boolean isInformational = !connection.isServer() && + HttpStatusClass.valueOf(headers.status()) == INFORMATIONAL; + if ((isInformational || !endOfStream) && stream.isHeadersReceived() || stream.isTrailersReceived()) { + throw streamError(streamId, PROTOCOL_ERROR, + "Stream %d received too many headers EOS: %s state: %s", + streamId, endOfStream, stream.state()); + } + switch (stream.state()) { case RESERVED_REMOTE: stream.open(endOfStream); @@ -305,15 +341,36 @@ stream.state()); } - encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive); - - listener.onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endOfStream); + if (!stream.isHeadersReceived()) { + // extract the content-length header + List<? extends CharSequence> contentLength = headers.getAll(HttpHeaderNames.CONTENT_LENGTH); + if (contentLength != null && !contentLength.isEmpty()) { + try { + long cLength = HttpUtil.normalizeAndGetContentLength(contentLength, false, true); + if (cLength != -1) { + headers.setLong(HttpHeaderNames.CONTENT_LENGTH, cLength); + stream.setProperty(contentLengthKey, new ContentLength(cLength)); + } + } catch (IllegalArgumentException e) { + throw streamError(stream.id(), PROTOCOL_ERROR, + "Multiple content-length headers received", e); + } + } + } + stream.headersReceived(isInformational); + try { + verifyContentLength(stream, 0, endOfStream); + encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive); + listener.onHeadersRead(ctx, streamId, headers, streamDependency, + weight, exclusive, padding, endOfStream); + } finally { // If the headers completes this stream, close it. if (endOfStream) { lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture()); } } + } @Override public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, @@ -662,4 +719,40 @@ onUnknownFrame0(ctx, frameType, streamId, flags, payload); } } + + private static final class ContentLength { + private final long expected; + private long seen; + + ContentLength(long expected) { + this.expected = expected; + } + + void increaseReceivedBytes(boolean server, int streamId, int bytes, boolean isEnd) throws Http2Exception { + seen += bytes; + // Check for overflow + if (seen < 0) { + throw streamError(streamId, PROTOCOL_ERROR, + "Received amount of data did overflow and so not match content-length header %d", expected); + } + // Check if we received more data then what was advertised via the content-length header. + if (seen > expected) { + throw streamError(streamId, PROTOCOL_ERROR, + "Received amount of data %d does not match content-length header %d", seen, expected); + } + + if (isEnd) { + if (seen == 0 && !server) { + // This may be a response to a HEAD request, let's just allow it. + return; + } + + // Check that we really saw what was told via the content-length header. + if (expected > seen) { + throw streamError(streamId, PROTOCOL_ERROR, + "Received amount of data %d does not match content-length header %d", seen, expected); + } + } + } + } } --- netty-netty-4.1.13.Final/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java 2017-07-06 13:23:51.000000000 +0200 +++ netty-netty-4.1.13.Final/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java 2021-03-12 11:48:15.242277753 +0100 @@ -375,7 +375,10 @@ private class DefaultStream implements Http2Stream { private static final byte SENT_STATE_RST = 0x1; private static final byte SENT_STATE_HEADERS = 0x2; - private static final byte SENT_STATE_PUSHPROMISE = 0x4; + private static final byte SENT_STATE_TRAILERS = 0x4; + private static final byte SENT_STATE_PUSHPROMISE = 0x8; + private static final byte RECV_STATE_HEADERS = 0x10; + private static final byte RECV_STATE_TRAILERS = 0x20; private final int id; private final PropertyMap properties = new PropertyMap(); private State state; @@ -419,6 +422,29 @@ } @Override + public boolean isTrailersSent() { + return (sentState & SENT_STATE_TRAILERS) != 0; + } + + @Override + public Http2Stream headersReceived(boolean isInformational) { + if (!isInformational) { + sentState |= isHeadersReceived() ? RECV_STATE_TRAILERS : RECV_STATE_HEADERS; + } + return this; + } + + @Override + public boolean isHeadersReceived() { + return (sentState & RECV_STATE_HEADERS) != 0; + } + + @Override + public boolean isTrailersReceived() { + return (sentState & RECV_STATE_TRAILERS) != 0; + } + + @Override public Http2Stream pushPromiseSent() { sentState |= SENT_STATE_PUSHPROMISE; return this; Only in netty-netty-4.1.13.Final/codec-http2/src/main/java/io/netty/handler/codec/http2: Http2SettingsReceivedConsumer.java --- netty-netty-4.1.13.Final/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Stream.java 2017-07-06 13:23:51.000000000 +0200 +++ netty-netty-4.1.13.Final/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Stream.java 2021-03-12 11:48:15.242277753 +0100 @@ -140,6 +140,29 @@ boolean isHeadersSent(); /** + * Indicates whether or not trailers were sent to the remote endpoint. + */ + boolean isTrailersSent(); + + /** + * Indicates that headers have been received. The first call to this method would be for the initial headers + * (see {@link #isHeadersReceived()}} and the second call would indicate the trailers + * (see {@link #isTrailersReceived()}). + * @param isInformational {@code true} if the headers contain an informational status code (for responses only). + */ + Http2Stream headersReceived(boolean isInformational); + + /** + * Indicates whether or not the initial headers have been received. + */ + boolean isHeadersReceived(); + + /** + * Indicates whether or not the trailers have been received. + */ + boolean isTrailersReceived(); + + /** * Indicates that a push promise was sent to the remote endpoint. */ Http2Stream pushPromiseSent(); --- netty-netty-4.1.13.Final/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java 2017-07-06 13:23:51.000000000 +0200 +++ netty-netty-4.1.13.Final/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java 2021-03-12 11:48:15.242277753 +0100 @@ -21,16 +21,21 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.channel.DefaultChannelPromise; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpResponseStatus; import junit.framework.AssertionFailedError; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import static io.netty.buffer.Unpooled.EMPTY_BUFFER; @@ -126,6 +131,21 @@ when(stream.id()).thenReturn(STREAM_ID); when(stream.state()).thenReturn(OPEN); when(stream.open(anyBoolean())).thenReturn(stream); + + final Map<Object, Object> properties = new IdentityHashMap<Object, Object>(); + when(stream.getProperty(ArgumentMatchers.<Http2Connection.PropertyKey>any())).thenAnswer(new Answer<Object>() { + @Override + public Object answer(InvocationOnMock invocationOnMock) { + return properties.get(invocationOnMock.getArgument(0)); + } + }); + when(stream.setProperty(ArgumentMatchers.<Http2Connection.PropertyKey>any(), any())).then(new Answer<Object>() { + @Override + public Object answer(InvocationOnMock invocationOnMock) { + return properties.put(invocationOnMock.getArgument(0), invocationOnMock.getArgument(1)); + } + }); + when(pushStream.id()).thenReturn(PUSH_STREAM_ID); doAnswer(new Answer<Http2Stream>() { @Override @@ -652,6 +672,115 @@ verify(listener).onGoAwayRead(eq(ctx), eq(1), eq(2L), eq(EMPTY_BUFFER)); } + @Test(expected = Http2Exception.StreamException.class) + public void dataContentLengthMissmatch() throws Exception { + dataContentLengthInvalid(false); + } + + @Test(expected = Http2Exception.StreamException.class) + public void dataContentLengthInvalid() throws Exception { + dataContentLengthInvalid(true); + } + + private void dataContentLengthInvalid(boolean negative) throws Exception { + final ByteBuf data = dummyData(); + int padding = 10; + int processedBytes = data.readableBytes() + padding; + mockFlowControl(processedBytes); + try { + decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers() + .setLong(HttpHeaderNames.CONTENT_LENGTH, negative ? -1L : 1L), padding, false); + decode().onDataRead(ctx, STREAM_ID, data, padding, true); + verify(localFlow).receiveFlowControlledFrame(eq(stream), eq(data), eq(padding), eq(true)); + verify(localFlow).consumeBytes(eq(stream), eq(processedBytes)); + + verify(listener, times(1)).onHeadersRead(eq(ctx), anyInt(), + any(Http2Headers.class), eq(0), eq(DEFAULT_PRIORITY_WEIGHT), eq(false), + eq(padding), eq(false)); + // Verify that the event was absorbed and not propagated to the observer. + verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); + } finally { + data.release(); + } + } + + @Test(expected = Http2Exception.StreamException.class) + public void headersContentLengthPositiveSign() throws Exception { + headersContentLengthSign("+1"); + } + + @Test(expected = Http2Exception.StreamException.class) + public void headersContentLengthNegativeSign() throws Exception { + headersContentLengthSign("-1"); + } + + private void headersContentLengthSign(String length) throws Exception { + int padding = 10; + when(connection.isServer()).thenReturn(true); + decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers() + .set(HttpHeaderNames.CONTENT_LENGTH, length), padding, false); + + // Verify that the event was absorbed and not propagated to the observer. + verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), + any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); + } + + @Test(expected = Http2Exception.StreamException.class) + public void headersContentLengthMissmatch() throws Exception { + headersContentLength(false); + } + + @Test(expected = Http2Exception.StreamException.class) + public void headersContentLengthInvalid() throws Exception { + headersContentLength(true); + } + + private void headersContentLength(boolean negative) throws Exception { + int padding = 10; + when(connection.isServer()).thenReturn(true); + decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers() + .setLong(HttpHeaderNames.CONTENT_LENGTH, negative ? -1L : 1L), padding, true); + + // Verify that the event was absorbed and not propagated to the observer. + verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), + any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); + } + + @Test + public void multipleHeadersContentLengthSame() throws Exception { + multipleHeadersContentLength(true); + } + + @Test(expected = Http2Exception.StreamException.class) + public void multipleHeadersContentLengthDifferent() throws Exception { + multipleHeadersContentLength(false); + } + + private void multipleHeadersContentLength(boolean same) throws Exception { + int padding = 10; + when(connection.isServer()).thenReturn(true); + Http2Headers headers = new DefaultHttp2Headers(); + if (same) { + headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 0); + headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 0); + } else { + headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 0); + headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 1); + } + + decode().onHeadersRead(ctx, STREAM_ID, headers, padding, true); + + if (same) { + verify(listener, times(1)).onHeadersRead(eq(ctx), anyInt(), + any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); + assertEquals(1, headers.getAll(HttpHeaderNames.CONTENT_LENGTH).size()); + } else { + // Verify that the event was absorbed and not propagated to the observer. + verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), + any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); + } + } + private static ByteBuf dummyData() { // The buffer is purposely 8 bytes so it will even work for a ping frame. return wrappedBuffer("abcdefgh".getBytes(UTF_8)); --- netty-netty-4.1.13.Final/common/src/main/java/io/netty/util/NetUtil.java 2017-07-06 13:23:51.000000000 +0200 +++ netty-netty-4.1.13.Final/common/src/main/java/io/netty/util/NetUtil.java 2021-03-12 11:48:15.242277753 +0100 @@ -1069,6 +1069,16 @@ } /** + * Returns {@link InetSocketAddress#getHostString()} if Java >= 7, + * or {@link InetSocketAddress#getHostName()} otherwise. + * @param addr The address + * @return the host string + */ + public static String getHostname(InetSocketAddress addr) { + return PlatformDependent.javaVersion() >= 7 ? addr.getHostString() : addr.getHostName(); + } + + /** * Does a range check on {@code value} if is within {@code start} (inclusive) and {@code end} (exclusive). * @param value The value to checked if is within {@code start} (inclusive) and {@code end} (exclusive) * @param start The start of the range (inclusive)
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor