Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.

This commit is contained in:
Simone Bordet 2020-04-14 23:37:26 +02:00
commit da651e995f
5 changed files with 142 additions and 4 deletions

View File

@ -109,11 +109,16 @@ public class MetaData implements Iterable<HttpField>
return _contentLength; return _contentLength;
} }
public void setContentLength(long contentLength)
{
_contentLength = contentLength;
}
@Override @Override
public Iterator<HttpField> iterator() public Iterator<HttpField> iterator()
{ {
HttpFields fields = getFields(); HttpFields fields = getFields();
return fields == null ? Collections.<HttpField>emptyIterator() : fields.iterator(); return fields == null ? Collections.emptyIterator() : fields.iterator();
} }
@Override @Override

View File

@ -111,7 +111,7 @@ public class InterleavingTest extends AbstractTest
Stream serverStream1 = serverStreams.get(0); Stream serverStream1 = serverStreams.get(0);
Stream serverStream2 = serverStreams.get(1); Stream serverStream2 = serverStreams.get(1);
MetaData.Response response1 = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields(), 0); MetaData.Response response1 = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
serverStream1.headers(new HeadersFrame(serverStream1.getId(), response1, null, false), Callback.NOOP); serverStream1.headers(new HeadersFrame(serverStream1.getId(), response1, null, false), Callback.NOOP);
Random random = new Random(); Random random = new Random();
@ -120,7 +120,7 @@ public class InterleavingTest extends AbstractTest
byte[] content2 = new byte[2 * ((ISession)serverStream2.getSession()).updateSendWindow(0)]; byte[] content2 = new byte[2 * ((ISession)serverStream2.getSession()).updateSendWindow(0)];
random.nextBytes(content2); random.nextBytes(content2);
MetaData.Response response2 = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields(), 0); MetaData.Response response2 = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
serverStream2.headers(new HeadersFrame(serverStream2.getId(), response2, null, false), new Callback() serverStream2.headers(new HeadersFrame(serverStream2.getId(), response2, null, false), new Callback()
{ {
@Override @Override

View File

@ -224,7 +224,8 @@ public class HpackEncoder
// Remove fields as specified in RFC 7540, 8.1.2.2. // Remove fields as specified in RFC 7540, 8.1.2.2.
if (fields != null) if (fields != null)
{ {
// For example: Connection: Close, TE, Upgrade, Custom. // Remove the headers specified in the Connection header,
// for example: Connection: Close, TE, Upgrade, Custom.
Set<String> hopHeaders = null; Set<String> hopHeaders = null;
for (String value : fields.getCSV(HttpHeader.CONNECTION, false)) for (String value : fields.getCSV(HttpHeader.CONNECTION, false))
{ {
@ -232,6 +233,8 @@ public class HpackEncoder
hopHeaders = new HashSet<>(); hopHeaders = new HashSet<>();
hopHeaders.add(StringUtil.asciiToLowerCase(value)); hopHeaders.add(StringUtil.asciiToLowerCase(value));
} }
boolean contentLengthEncoded = false;
for (HttpField field : fields) for (HttpField field : fields)
{ {
HttpHeader header = field.getHeader(); HttpHeader header = field.getHeader();
@ -246,8 +249,17 @@ public class HpackEncoder
String name = field.getLowerCaseName(); String name = field.getLowerCaseName();
if (hopHeaders != null && hopHeaders.contains(name)) if (hopHeaders != null && hopHeaders.contains(name))
continue; continue;
if (header == HttpHeader.CONTENT_LENGTH)
contentLengthEncoded = true;
encode(buffer, field); encode(buffer, field);
} }
if (!contentLengthEncoded)
{
long contentLength = metadata.getContentLength();
if (contentLength >= 0)
encode(buffer, new HttpField(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength)));
}
} }
// Check size // Check size

View File

@ -0,0 +1,105 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.http2.client.http;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ContentLengthTest extends AbstractTest
{
@ParameterizedTest
@ValueSource(strings = {"GET", "HEAD", "POST", "PUT"})
public void testZeroContentLengthAddedByServer(String method) throws Exception
{
start(new EmptyServerHandler());
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.method(method)
.send();
HttpFields responseHeaders = response.getHeaders();
long contentLength = responseHeaders.getLongField(HttpHeader.CONTENT_LENGTH.asString());
assertEquals(0, contentLength);
}
@ParameterizedTest
@ValueSource(strings = {"GET", "HEAD", "POST", "PUT"})
public void testContentLengthAddedByServer(String method) throws Exception
{
byte[] data = new byte[512];
start(new EmptyServerHandler()
{
@Override
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
response.getOutputStream().write(data);
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.method(method)
.send();
HttpFields responseHeaders = response.getHeaders();
long contentLength = responseHeaders.getLongField(HttpHeader.CONTENT_LENGTH.asString());
assertEquals(data.length, contentLength);
}
@ParameterizedTest
@ValueSource(strings = {"GET", "HEAD", "POST", "PUT"})
public void testGzippedContentLengthAddedByServer(String method) throws Exception
{
byte[] data = new byte[4096];
GzipHandler gzipHandler = new GzipHandler();
gzipHandler.addIncludedMethods(method);
gzipHandler.setMinGzipSize(data.length / 2);
gzipHandler.setHandler(new EmptyServerHandler()
{
@Override
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
response.setContentLength(data.length);
response.getOutputStream().write(data);
}
});
start(gzipHandler);
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.method(method)
.send();
HttpFields responseHeaders = response.getHeaders();
long contentLength = responseHeaders.getLongField(HttpHeader.CONTENT_LENGTH.asString());
assertTrue(0 < contentLength && contentLength < data.length);
}
}

View File

@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
@ -107,6 +108,21 @@ public class HttpTransportOverHTTP2 implements HttpTransport
{ {
if (commit.compareAndSet(false, true)) if (commit.compareAndSet(false, true))
{ {
if (lastContent)
{
long realContentLength = BufferUtil.length(content);
long contentLength = response.getContentLength();
if (contentLength < 0)
{
response.setContentLength(realContentLength);
}
else if (hasContent && contentLength != realContentLength)
{
callback.failed(new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500, String.format("Incorrect Content-Length %d!=%d", contentLength, realContentLength)));
return;
}
}
if (hasContent) if (hasContent)
{ {
Callback commitCallback = new Callback.Nested(callback) Callback commitCallback = new Callback.Nested(callback)