Merge pull request #4768 from eclipse/jetty-9.4.x-4764-http2_content_length
Fixes #4764 - HTTP2 Jetty Server does not send back content-length.
This commit is contained in:
commit
0953a93335
|
@ -119,6 +119,11 @@ public class MetaData implements Iterable<HttpField>
|
|||
return _contentLength;
|
||||
}
|
||||
|
||||
public void setContentLength(long contentLength)
|
||||
{
|
||||
_contentLength = contentLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an iterator over the HTTP fields
|
||||
* @see #getFields()
|
||||
|
|
|
@ -111,7 +111,7 @@ public class InterleavingTest extends AbstractTest
|
|||
|
||||
Stream serverStream1 = serverStreams.get(0);
|
||||
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);
|
||||
|
||||
Random random = new Random();
|
||||
|
@ -120,7 +120,7 @@ public class InterleavingTest extends AbstractTest
|
|||
byte[] content2 = new byte[2 * ((ISession)serverStream2.getSession()).updateSendWindow(0)];
|
||||
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()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -217,7 +217,8 @@ public class HpackEncoder
|
|||
// Remove fields as specified in RFC 7540, 8.1.2.2.
|
||||
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;
|
||||
for (String value : fields.getCSV(HttpHeader.CONNECTION, false))
|
||||
{
|
||||
|
@ -225,6 +226,8 @@ public class HpackEncoder
|
|||
hopHeaders = new HashSet<>();
|
||||
hopHeaders.add(StringUtil.asciiToLowerCase(value));
|
||||
}
|
||||
|
||||
boolean contentLengthEncoded = false;
|
||||
for (HttpField field : fields)
|
||||
{
|
||||
HttpHeader header = field.getHeader();
|
||||
|
@ -239,8 +242,17 @@ public class HpackEncoder
|
|||
String name = field.getLowerCaseName();
|
||||
if (hopHeaders != null && hopHeaders.contains(name))
|
||||
continue;
|
||||
if (header == HttpHeader.CONTENT_LENGTH)
|
||||
contentLengthEncoded = true;
|
||||
encode(buffer, field);
|
||||
}
|
||||
|
||||
if (!contentLengthEncoded)
|
||||
{
|
||||
long contentLength = metadata.getContentLength();
|
||||
if (contentLength >= 0)
|
||||
encode(buffer, new HttpField(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength)));
|
||||
}
|
||||
}
|
||||
|
||||
// Check size
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
|
@ -110,6 +111,21 @@ public class HttpTransportOverHTTP2 implements HttpTransport
|
|||
{
|
||||
if (commit.compareAndSet(false, true))
|
||||
{
|
||||
if (lastContent)
|
||||
{
|
||||
long realContentLength = BufferUtil.length(content);
|
||||
long contentLength = info.getContentLength();
|
||||
if (contentLength < 0)
|
||||
{
|
||||
info.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)
|
||||
{
|
||||
Callback commitCallback = new Callback.Nested(callback)
|
||||
|
|
Loading…
Reference in New Issue