Merge pull request #10071 from eclipse/jetty-12.0.x-sizeLimitHandler
add SizeLimitHandler to Jetty-12
This commit is contained in:
commit
993a111805
|
@ -410,7 +410,11 @@ public class ServerFCGIConnection extends AbstractConnection implements Connecti
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Request {} failure on {}: {}", request, stream, failure);
|
||||
if (stream != null)
|
||||
stream.getHttpChannel().onFailure(new BadMessageException(null, failure));
|
||||
{
|
||||
Runnable runnable = stream.getHttpChannel().onFailure(new BadMessageException(null, failure));
|
||||
if (runnable != null)
|
||||
getExecutor().execute(runnable);
|
||||
}
|
||||
stream = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -213,7 +213,10 @@ public class HttpTester
|
|||
|
||||
public static Response parseResponse(String response)
|
||||
{
|
||||
return parseResponse(response, false);
|
||||
Response r = new Response();
|
||||
HttpParser parser = new HttpParser(r);
|
||||
parser.parseNext(BufferUtil.toBuffer(response));
|
||||
return r;
|
||||
}
|
||||
|
||||
private static Response parseResponse(String response, boolean head)
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.eclipse.jetty.http.HttpException;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
/**
|
||||
* A handler that can limit the size of message bodies in requests and responses.
|
||||
*
|
||||
* <p>The optional request and response limits are imposed by checking the {@code Content-Length}
|
||||
* header or observing the actual bytes seen by the handler. Handler order is important, in as much
|
||||
* as if this handler is before a the {@link org.eclipse.jetty.server.handler.gzip.GzipHandler},
|
||||
* then it will limit compressed sized, if it as after the {@link
|
||||
* org.eclipse.jetty.server.handler.gzip.GzipHandler} then the limit is applied to uncompressed
|
||||
* bytes. If a size limit is exceeded then {@link BadMessageException} is thrown with a {@link
|
||||
* org.eclipse.jetty.http.HttpStatus#PAYLOAD_TOO_LARGE_413} status.
|
||||
*/
|
||||
public class SizeLimitHandler extends Handler.Wrapper
|
||||
{
|
||||
private final long _requestLimit;
|
||||
private final long _responseLimit;
|
||||
private long _read = 0;
|
||||
private long _written = 0;
|
||||
|
||||
/**
|
||||
* @param requestLimit The request body size limit in bytes or -1 for no limit
|
||||
* @param responseLimit The response body size limit in bytes or -1 for no limit
|
||||
*/
|
||||
public SizeLimitHandler(long requestLimit, long responseLimit)
|
||||
{
|
||||
_requestLimit = requestLimit;
|
||||
_responseLimit = responseLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
HttpField contentLengthField = request.getHeaders().getField(HttpHeader.CONTENT_LENGTH);
|
||||
if (contentLengthField != null)
|
||||
{
|
||||
long contentLength = contentLengthField.getLongValue();
|
||||
if (contentLength > _requestLimit)
|
||||
throw new BadMessageException(413, "Request body is too large: " + contentLength + ">" + _requestLimit);
|
||||
}
|
||||
|
||||
HttpFields.Mutable.Wrapper httpFields = new HttpFields.Mutable.Wrapper(response.getHeaders())
|
||||
{
|
||||
@Override
|
||||
public HttpField onAddField(HttpField field)
|
||||
{
|
||||
if (field.getHeader().is(HttpHeader.CONTENT_LENGTH.asString()))
|
||||
{
|
||||
long contentLength = field.getLongValue();
|
||||
if (contentLength > _responseLimit)
|
||||
throw new HttpException.RuntimeException(500, "Response body is too large: " + contentLength + ">" + _responseLimit);
|
||||
}
|
||||
return super.onAddField(field);
|
||||
}
|
||||
};
|
||||
|
||||
response = new Response.Wrapper(request, response)
|
||||
{
|
||||
@Override
|
||||
public HttpFields.Mutable getHeaders()
|
||||
{
|
||||
return httpFields;
|
||||
}
|
||||
};
|
||||
|
||||
request.addHttpStreamWrapper(httpStream -> new HttpStream.Wrapper(httpStream)
|
||||
{
|
||||
@Override
|
||||
public Content.Chunk read()
|
||||
{
|
||||
Content.Chunk chunk = super.read();
|
||||
if (chunk == null)
|
||||
return null;
|
||||
if (chunk.getFailure() != null)
|
||||
return chunk;
|
||||
|
||||
// Check request content limit.
|
||||
ByteBuffer content = chunk.getByteBuffer();
|
||||
if (content != null && content.remaining() > 0)
|
||||
{
|
||||
_read += content.remaining();
|
||||
if (_requestLimit >= 0 && _read > _requestLimit)
|
||||
{
|
||||
BadMessageException e = new BadMessageException(413, "Request body is too large: " + _read + ">" + _requestLimit);
|
||||
request.fail(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(MetaData.Request request, MetaData.Response response, boolean last, ByteBuffer content, Callback callback)
|
||||
{
|
||||
// Check response content limit.
|
||||
if (content != null && content.remaining() > 0)
|
||||
{
|
||||
if (_responseLimit >= 0 && (_written + content.remaining()) > _responseLimit)
|
||||
{
|
||||
callback.failed(new HttpException.RuntimeException(500, "Response body is too large: " +
|
||||
_written + content.remaining() + ">" + _responseLimit));
|
||||
return;
|
||||
}
|
||||
_written += content.remaining();
|
||||
}
|
||||
|
||||
super.send(request, response, last, content, callback);
|
||||
}
|
||||
});
|
||||
|
||||
return super.handle(request, response, callback);
|
||||
}
|
||||
}
|
|
@ -980,7 +980,9 @@ public class HttpChannelState implements HttpChannel, Components
|
|||
@Override
|
||||
public void fail(Throwable failure)
|
||||
{
|
||||
_httpChannelState.onFailure(failure);
|
||||
Runnable runnable = _httpChannelState.onFailure(failure);
|
||||
if (runnable != null)
|
||||
getContext().execute(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.server.handler;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.eclipse.jetty.http.HttpException;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.SizeLimitHandler;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class SizeLimitHandlerTest
|
||||
{
|
||||
private Server _server;
|
||||
private LocalConnector _local;
|
||||
private ContextHandler _contextHandler;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
_server = new Server();
|
||||
HttpConfiguration config = new HttpConfiguration();
|
||||
config.setOutputBufferSize(1024);
|
||||
_local = new LocalConnector(_server, new HttpConnectionFactory(config));
|
||||
_server.setConnectors(new Connector[]{_local});
|
||||
SizeLimitHandler sizeLimitHandler = new SizeLimitHandler(8192, 8192);
|
||||
_contextHandler = new ContextHandler("/ctx");
|
||||
_server.setHandler(sizeLimitHandler);
|
||||
sizeLimitHandler.setHandler(_contextHandler);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallGET() throws Exception
|
||||
{
|
||||
_contextHandler.setHandler(new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public boolean handle(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
response.write(true, BufferUtil.toBuffer("Hello World"), callback);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_server.start();
|
||||
HttpTester.Response response = HttpTester.parseResponse(
|
||||
_local.getResponse("GET /ctx/hello HTTP/1.0\r\n\r\n"));
|
||||
assertThat(response.getStatus(), equalTo(200));
|
||||
assertThat(response.getContent(), containsString("Hello World"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLargeGETContentLengthKnown() throws Exception
|
||||
{
|
||||
_contextHandler.setHandler(new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public boolean handle(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
response.getHeaders().put(HttpHeader.CONTENT_LENGTH, 8 * 1024 + 1);
|
||||
fail();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_server.start();
|
||||
HttpTester.Response response = HttpTester.parseResponse(
|
||||
_local.getResponse("GET /ctx/hello HTTP/1.0\r\n\r\n"));
|
||||
assertThat(response.getStatus(), equalTo(500));
|
||||
assertThat(response.getContent(), containsString("8193>8192"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLargeGETSingleByteBuffer() throws Exception
|
||||
{
|
||||
_contextHandler.setHandler(new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public boolean handle(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
|
||||
response.write(true, ByteBuffer.wrap(new byte[8 * 1024 + 1]), callback);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_server.start();
|
||||
HttpTester.Response response = HttpTester.parseResponse(
|
||||
_local.getResponse("GET /ctx/hello HTTP/1.0\r\n\r\n"));
|
||||
assertThat(response.getStatus(), equalTo(500));
|
||||
assertThat(response.getContent(), containsString("8193>8192"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLargeGETManyWrites() throws Exception
|
||||
{
|
||||
AtomicBoolean error = new AtomicBoolean();
|
||||
_contextHandler.setHandler(new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public boolean handle(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
byte[] data = new byte[1024];
|
||||
Arrays.fill(data, (byte)'X');
|
||||
data[1023] = (byte)'\n';
|
||||
String text = new String(data, 0, 1024, Charset.defaultCharset());
|
||||
OutputStream outputStream = Content.Sink.asOutputStream(response);
|
||||
PrintWriter out = new PrintWriter(outputStream);
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i <= 10; i++)
|
||||
{
|
||||
out.println(i);
|
||||
out.write(text);
|
||||
out.flush();
|
||||
}
|
||||
out.close();
|
||||
callback.succeeded();
|
||||
}
|
||||
catch (HttpException.RuntimeException e)
|
||||
{
|
||||
error.set(true);
|
||||
throw e;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_server.start();
|
||||
HttpTester.Response response = HttpTester.parseResponse(
|
||||
_local.getResponse("GET /ctx/hello HTTP/1.0\r\n\r\n"));
|
||||
assertThat(response.getStatus(), equalTo(200));
|
||||
assertTrue(error.get());
|
||||
|
||||
assertThat(response.getContent(), not(containsString("8")));
|
||||
assertThat(response.getContent(), not(containsString("9")));
|
||||
assertThat(response.getContent(), not(containsString("10")));
|
||||
assertFalse(response.isComplete());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallPOST() throws Exception
|
||||
{
|
||||
_contextHandler.setHandler(new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public boolean handle(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
String content = IO.toString(Content.Source.asInputStream(request));
|
||||
PrintWriter out = new PrintWriter(Content.Sink.asOutputStream(response));
|
||||
out.println("OK " + content.length());
|
||||
out.close();
|
||||
callback.succeeded();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_server.start();
|
||||
HttpTester.Response response = HttpTester.parseResponse(
|
||||
_local.getResponse("POST /ctx/hello HTTP/1.0\r\n" +
|
||||
"Content-Length: 8\r\n" +
|
||||
"\r\n" +
|
||||
"123456\r\n"));
|
||||
assertThat(response.getStatus(), equalTo(200));
|
||||
assertThat(response.getContent(), containsString("OK 8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLargePOSTKnownSize() throws Exception
|
||||
{
|
||||
_contextHandler.setHandler(new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public boolean handle(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
String content = IO.toString(Content.Source.asInputStream(request));
|
||||
PrintWriter out = new PrintWriter(Content.Sink.asOutputStream(response));
|
||||
out.println("OK " + content.length());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_server.start();
|
||||
HttpTester.Response response = HttpTester.parseResponse(
|
||||
_local.getResponse("POST /ctx/hello HTTP/1.0\r\n" +
|
||||
"Content-Length: 32768\r\n" +
|
||||
"\r\n" +
|
||||
"123456..."));
|
||||
assertThat(response.getStatus(), equalTo(413));
|
||||
assertThat(response.getContent(), containsString("32768>8192"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLargePOSTUnknownSize() throws Exception
|
||||
{
|
||||
_contextHandler.setHandler(new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public boolean handle(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
String content = IO.toString(Content.Source.asInputStream(request));
|
||||
PrintWriter out = new PrintWriter(Content.Sink.asOutputStream(response));
|
||||
out.println("OK " + content.length());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_server.start();
|
||||
|
||||
try (LocalConnector.LocalEndPoint endp = _local.executeRequest(
|
||||
"POST /ctx/hello HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Transfer-Encoding: chunked\r\n" +
|
||||
"\r\n"))
|
||||
{
|
||||
byte[] data = new byte[1024];
|
||||
Arrays.fill(data, (byte)'X');
|
||||
data[1023] = (byte)'\n';
|
||||
String text = new String(data, 0, 1024, Charset.defaultCharset());
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
endp.addInput("400\r\n" + text + "\r\n");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(endp.getResponse());
|
||||
|
||||
assertThat(response.getStatus(), equalTo(413));
|
||||
assertThat(response.getContent(), containsString(">8192"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.ee10.servlet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.eclipse.jetty.client.BytesRequestContent;
|
||||
import org.eclipse.jetty.client.ContentResponse;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.Request;
|
||||
import org.eclipse.jetty.client.StringRequestContent;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SizeLimitHandler;
|
||||
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
|
||||
public class SizeLimitHandlerServletTest
|
||||
{
|
||||
private static final int SIZE_LIMIT = 1024;
|
||||
private Server _server;
|
||||
private ServerConnector _connector;
|
||||
private HttpClient _client;
|
||||
|
||||
@BeforeEach
|
||||
public void before() throws Exception
|
||||
{
|
||||
_server = new Server();
|
||||
_connector = new ServerConnector(_server);
|
||||
_server.addConnector(_connector);
|
||||
|
||||
ServletContextHandler contextHandler = new ServletContextHandler();
|
||||
GzipHandler gzipHandler = new GzipHandler();
|
||||
gzipHandler.setInflateBufferSize(1024);
|
||||
SizeLimitHandler sizeLimitHandler = new SizeLimitHandler(SIZE_LIMIT, SIZE_LIMIT);
|
||||
sizeLimitHandler.setHandler(gzipHandler);
|
||||
contextHandler.insertHandler(sizeLimitHandler);
|
||||
|
||||
contextHandler.addServlet(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException
|
||||
{
|
||||
String requestContent = IO.toString(req.getInputStream());
|
||||
resp.getWriter().print(requestContent);
|
||||
}
|
||||
}, "/");
|
||||
|
||||
_server.setHandler(contextHandler);
|
||||
_server.start();
|
||||
|
||||
_client = new HttpClient();
|
||||
_client.start();
|
||||
_client.getContentDecoderFactories().clear();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() throws Exception
|
||||
{
|
||||
_client.stop();
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGzippedEcho() throws Exception
|
||||
{
|
||||
String content = "x".repeat(SIZE_LIMIT * 2);
|
||||
|
||||
URI uri = URI.create("http://localhost:" + _connector.getLocalPort());
|
||||
ContentResponse response = _client.POST(uri)
|
||||
.headers(httpFields ->
|
||||
{
|
||||
httpFields.add(HttpHeader.CONTENT_ENCODING, "gzip");
|
||||
httpFields.add(HttpHeader.ACCEPT_ENCODING, "gzip");
|
||||
})
|
||||
.body(gzipContent(content)).send();
|
||||
|
||||
assertThat(response.getHeaders().get(HttpHeader.CONTENT_ENCODING), equalTo("gzip"));
|
||||
assertThat(response.getHeaders().getLongField(HttpHeader.CONTENT_LENGTH), lessThan((long)SIZE_LIMIT));
|
||||
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(response.getContent());
|
||||
GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
|
||||
String responseContent = IO.toString(gzipInputStream);
|
||||
assertThat(responseContent, equalTo(content));
|
||||
assertThat(responseContent.length(), greaterThan(SIZE_LIMIT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalEcho() throws Exception
|
||||
{
|
||||
String content = "x".repeat(SIZE_LIMIT * 2);
|
||||
|
||||
URI uri = URI.create("http://localhost:" + _connector.getLocalPort());
|
||||
ContentResponse response = _client.POST(uri)
|
||||
.body(new StringRequestContent(content)).send();
|
||||
|
||||
assertThat(response.getStatus(), equalTo(HttpStatus.PAYLOAD_TOO_LARGE_413));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGzipEchoNoAcceptEncoding() throws Exception
|
||||
{
|
||||
String content = "x".repeat(SIZE_LIMIT * 2);
|
||||
URI uri = URI.create("http://localhost:" + _connector.getLocalPort());
|
||||
|
||||
AtomicInteger contentReceived = new AtomicInteger();
|
||||
CompletableFuture<Throwable> failure = new CompletableFuture<>();
|
||||
_client.POST(uri)
|
||||
.headers(httpFields -> httpFields.add(HttpHeader.CONTENT_ENCODING, "gzip"))
|
||||
.body(gzipContent(content))
|
||||
.onResponseContentAsync((response, chunk, demander) ->
|
||||
{
|
||||
contentReceived.addAndGet(chunk.getByteBuffer().remaining());
|
||||
chunk.release();
|
||||
demander.run();
|
||||
}).send(result -> failure.complete(result.getFailure()));
|
||||
|
||||
Throwable exception = failure.get(5, TimeUnit.SECONDS);
|
||||
assertThat(exception, instanceOf(EOFException.class));
|
||||
assertThat(contentReceived.get(), lessThan(SIZE_LIMIT));
|
||||
}
|
||||
|
||||
public static Request.Content gzipContent(String content) throws Exception
|
||||
{
|
||||
byte[] bytes = content.getBytes();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
|
||||
gzipOutputStream.write(bytes);
|
||||
gzipOutputStream.close();
|
||||
return new BytesRequestContent(outputStream.toByteArray());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue