Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.
This commit is contained in:
commit
39d122a9fb
|
@ -35,6 +35,15 @@ public interface ContentDecoder
|
|||
*/
|
||||
public abstract ByteBuffer decode(ByteBuffer buffer);
|
||||
|
||||
/**
|
||||
* <p>Releases the ByteBuffer returned by {@link #decode(ByteBuffer)}.</p>
|
||||
*
|
||||
* @param decoded the ByteBuffer returned by {@link #decode(ByteBuffer)}
|
||||
*/
|
||||
public default void release(ByteBuffer decoded)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for {@link ContentDecoder}s; subclasses must implement {@link #newContentDecoder()}.
|
||||
* <p>
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
|
||||
/**
|
||||
|
@ -25,7 +27,7 @@ import org.eclipse.jetty.io.ByteBufferPool;
|
|||
*/
|
||||
public class GZIPContentDecoder extends org.eclipse.jetty.http.GZIPContentDecoder implements ContentDecoder
|
||||
{
|
||||
private static final int DEFAULT_BUFFER_SIZE = 2048;
|
||||
public static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
public GZIPContentDecoder()
|
||||
{
|
||||
|
@ -42,6 +44,13 @@ public class GZIPContentDecoder extends org.eclipse.jetty.http.GZIPContentDecode
|
|||
super(byteBufferPool, bufferSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean decodedChunk(ByteBuffer chunk)
|
||||
{
|
||||
super.decodedChunk(chunk);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized {@link ContentDecoder.Factory} for the "gzip" encoding.
|
||||
*/
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.eclipse.jetty.client;
|
|||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
|
@ -37,7 +36,7 @@ import org.eclipse.jetty.http.HttpHeader;
|
|||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.CountingCallback;
|
||||
import org.eclipse.jetty.util.IteratingNestedCallback;
|
||||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -339,35 +338,7 @@ public abstract class HttpReceiver
|
|||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
List<ByteBuffer> decodeds = new ArrayList<>(2);
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
if (!decoded.hasRemaining())
|
||||
continue;
|
||||
decodeds.add(decoded);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Response content decoded ({}) {}{}{}", decoder, response, System.lineSeparator(), BufferUtil.toDetailString(decoded));
|
||||
}
|
||||
|
||||
if (decodeds.isEmpty())
|
||||
{
|
||||
callback.succeeded();
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = decodeds.size();
|
||||
CountingCallback counter = new CountingCallback(callback, size);
|
||||
for (ByteBuffer decoded : decodeds)
|
||||
notifier.notifyContent(response, decoded, counter, contentListeners);
|
||||
}
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
callback.failed(x);
|
||||
}
|
||||
new Decoder(notifier, response, decoder, buffer, callback).iterate();
|
||||
}
|
||||
|
||||
if (updateResponseState(ResponseState.TRANSIENT, ResponseState.CONTENT))
|
||||
|
@ -615,4 +586,47 @@ public abstract class HttpReceiver
|
|||
*/
|
||||
FAILURE
|
||||
}
|
||||
|
||||
private class Decoder extends IteratingNestedCallback
|
||||
{
|
||||
private final ResponseNotifier notifier;
|
||||
private final HttpResponse response;
|
||||
private final ContentDecoder decoder;
|
||||
private final ByteBuffer buffer;
|
||||
private ByteBuffer decoded;
|
||||
|
||||
public Decoder(ResponseNotifier notifier, HttpResponse response, ContentDecoder decoder, ByteBuffer buffer, Callback callback)
|
||||
{
|
||||
super(callback);
|
||||
this.notifier = notifier;
|
||||
this.response = response;
|
||||
this.decoder = decoder;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Action process() throws Throwable
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
decoded = decoder.decode(buffer);
|
||||
if (decoded.hasRemaining())
|
||||
break;
|
||||
if (!buffer.hasRemaining())
|
||||
return Action.SUCCEEDED;
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Response content decoded ({}) {}{}{}", decoder, response, System.lineSeparator(), BufferUtil.toDetailString(decoded));
|
||||
|
||||
notifier.notifyContent(response, decoded, this, contentListeners);
|
||||
return Action.SCHEDULED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
decoder.release(decoded);
|
||||
super.succeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,288 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.client;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@Deprecated
|
||||
public class GZIPContentDecoderTest
|
||||
{
|
||||
@Test
|
||||
public void testStreamNoBlocks() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
GZIPInputStream input = new GZIPInputStream(new ByteArrayInputStream(bytes), 1);
|
||||
int read = input.read();
|
||||
assertEquals(-1, read);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamBigBlockOneByteAtATime() throws Exception
|
||||
{
|
||||
String data = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data += data;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
baos = new ByteArrayOutputStream();
|
||||
GZIPInputStream input = new GZIPInputStream(new ByteArrayInputStream(bytes), 1);
|
||||
int read;
|
||||
while ((read = input.read()) >= 0)
|
||||
baos.write(read);
|
||||
assertEquals(data, new String(baos.toByteArray(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoBlocks() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder();
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
|
||||
assertEquals(0, decoded.remaining());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallBlock() throws Exception
|
||||
{
|
||||
String data = "0";
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder();
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallBlockWithGZIPChunkedAtBegin() throws Exception
|
||||
{
|
||||
String data = "0";
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
// The header is 10 bytes, chunk at 11 bytes
|
||||
byte[] bytes1 = new byte[11];
|
||||
System.arraycopy(bytes, 0, bytes1, 0, bytes1.length);
|
||||
byte[] bytes2 = new byte[bytes.length - bytes1.length];
|
||||
System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder();
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1));
|
||||
assertEquals(0, decoded.capacity());
|
||||
decoded = decoder.decode(ByteBuffer.wrap(bytes2));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallBlockWithGZIPChunkedAtEnd() throws Exception
|
||||
{
|
||||
String data = "0";
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
// The trailer is 8 bytes, chunk the last 9 bytes
|
||||
byte[] bytes1 = new byte[bytes.length - 9];
|
||||
System.arraycopy(bytes, 0, bytes1, 0, bytes1.length);
|
||||
byte[] bytes2 = new byte[bytes.length - bytes1.length];
|
||||
System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder();
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
assertFalse(decoder.isFinished());
|
||||
decoded = decoder.decode(ByteBuffer.wrap(bytes2));
|
||||
assertEquals(0, decoded.remaining());
|
||||
assertTrue(decoder.isFinished());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallBlockWithGZIPTrailerChunked() throws Exception
|
||||
{
|
||||
String data = "0";
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
// The trailer is 4+4 bytes, chunk the last 3 bytes
|
||||
byte[] bytes1 = new byte[bytes.length - 3];
|
||||
System.arraycopy(bytes, 0, bytes1, 0, bytes1.length);
|
||||
byte[] bytes2 = new byte[bytes.length - bytes1.length];
|
||||
System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder();
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1));
|
||||
assertEquals(0, decoded.capacity());
|
||||
decoded = decoder.decode(ByteBuffer.wrap(bytes2));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoSmallBlocks() throws Exception
|
||||
{
|
||||
String data1 = "0";
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data1.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes1 = baos.toByteArray();
|
||||
|
||||
String data2 = "1";
|
||||
baos = new ByteArrayOutputStream();
|
||||
output = new GZIPOutputStream(baos);
|
||||
output.write(data2.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes2 = baos.toByteArray();
|
||||
|
||||
byte[] bytes = new byte[bytes1.length + bytes2.length];
|
||||
System.arraycopy(bytes1, 0, bytes, 0, bytes1.length);
|
||||
System.arraycopy(bytes2, 0, bytes, bytes1.length, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder();
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
assertEquals(data1, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
assertTrue(decoder.isFinished());
|
||||
assertTrue(buffer.hasRemaining());
|
||||
decoded = decoder.decode(buffer);
|
||||
assertEquals(data2, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
assertTrue(decoder.isFinished());
|
||||
assertFalse(buffer.hasRemaining());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigBlock() throws Exception
|
||||
{
|
||||
String data = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data += data;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
String result = "";
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder();
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
result += StandardCharsets.UTF_8.decode(decoded).toString();
|
||||
}
|
||||
assertEquals(data, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigBlockOneByteAtATime() throws Exception
|
||||
{
|
||||
String data = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data += data;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
String result = "";
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(64);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
if (decoded.hasRemaining())
|
||||
result += StandardCharsets.UTF_8.decode(decoded).toString();
|
||||
}
|
||||
assertEquals(data, result);
|
||||
assertTrue(decoder.isFinished());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigBlockWithExtraBytes() throws Exception
|
||||
{
|
||||
String data1 = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 10; ++i)
|
||||
data1 += data1;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
GZIPOutputStream output = new GZIPOutputStream(baos);
|
||||
output.write(data1.getBytes(StandardCharsets.UTF_8));
|
||||
output.close();
|
||||
byte[] bytes1 = baos.toByteArray();
|
||||
|
||||
String data2 = "HELLO";
|
||||
byte[] bytes2 = data2.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
byte[] bytes = new byte[bytes1.length + bytes2.length];
|
||||
System.arraycopy(bytes1, 0, bytes, 0, bytes1.length);
|
||||
System.arraycopy(bytes2, 0, bytes, bytes1.length, bytes2.length);
|
||||
|
||||
String result = "";
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(64);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
if (decoded.hasRemaining())
|
||||
result += StandardCharsets.UTF_8.decode(decoded).toString();
|
||||
if (decoder.isFinished())
|
||||
break;
|
||||
}
|
||||
assertEquals(data1, result);
|
||||
assertTrue(buffer.hasRemaining());
|
||||
assertEquals(data2, StandardCharsets.UTF_8.decode(buffer).toString());
|
||||
}
|
||||
}
|
|
@ -18,29 +18,36 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
public class HttpClientGZIPTest extends AbstractHttpClientServerTest
|
||||
{
|
||||
@ParameterizedTest
|
||||
|
@ -48,12 +55,11 @@ public class HttpClientGZIPTest extends AbstractHttpClientServerTest
|
|||
public void testGZIPContentEncoding(Scenario scenario) throws Exception
|
||||
{
|
||||
final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
start(scenario, new AbstractHandler()
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
GZIPOutputStream gzipOutput = new GZIPOutputStream(response.getOutputStream());
|
||||
gzipOutput.write(data);
|
||||
|
@ -75,12 +81,11 @@ public class HttpClientGZIPTest extends AbstractHttpClientServerTest
|
|||
public void testGZIPContentOneByteAtATime(Scenario scenario) throws Exception
|
||||
{
|
||||
final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
start(scenario, new AbstractHandler()
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
|
||||
ByteArrayOutputStream gzipData = new ByteArrayOutputStream();
|
||||
|
@ -112,12 +117,11 @@ public class HttpClientGZIPTest extends AbstractHttpClientServerTest
|
|||
public void testGZIPContentSentTwiceInOneWrite(Scenario scenario) throws Exception
|
||||
{
|
||||
final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
start(scenario, new AbstractHandler()
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
|
||||
ByteArrayOutputStream gzipData = new ByteArrayOutputStream();
|
||||
|
@ -164,12 +168,11 @@ public class HttpClientGZIPTest extends AbstractHttpClientServerTest
|
|||
private void testGZIPContentFragmented(Scenario scenario, final int fragment) throws Exception
|
||||
{
|
||||
final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
start(scenario, new AbstractHandler()
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
|
||||
ByteArrayOutputStream gzipData = new ByteArrayOutputStream();
|
||||
|
@ -204,12 +207,11 @@ public class HttpClientGZIPTest extends AbstractHttpClientServerTest
|
|||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testGZIPContentCorrupted(Scenario scenario) throws Exception
|
||||
{
|
||||
start(scenario, new AbstractHandler()
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
// Not gzipped, will cause the client to blow up.
|
||||
response.getOutputStream().print("0123456789");
|
||||
|
@ -228,6 +230,46 @@ public class HttpClientGZIPTest extends AbstractHttpClientServerTest
|
|||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testLargeGZIPContentDoesNotPolluteByteBufferPool(Scenario scenario) throws Exception
|
||||
{
|
||||
String digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
Random random = new Random();
|
||||
byte[] content = new byte[1024 * 1024];
|
||||
for (int i = 0; i < content.length; ++i)
|
||||
content[i] = (byte)digits.charAt(random.nextInt(digits.length()));
|
||||
start(scenario, new EmptyServerHandler()
|
||||
{
|
||||
@Override
|
||||
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.setContentType("text/plain;charset=" + StandardCharsets.US_ASCII.name());
|
||||
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), "gzip");
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(response.getOutputStream());
|
||||
gzip.write(content);
|
||||
gzip.finish();
|
||||
}
|
||||
});
|
||||
|
||||
ByteBufferPool pool = client.getByteBufferPool();
|
||||
assumeTrue(pool instanceof MappedByteBufferPool);
|
||||
MappedByteBufferPool bufferPool = (MappedByteBufferPool)pool;
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scenario.getScheme())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertArrayEquals(content, response.getContent());
|
||||
|
||||
long directMemory = bufferPool.getMemory(true);
|
||||
assertThat(directMemory, lessThan((long)content.length));
|
||||
long heapMemory = bufferPool.getMemory(false);
|
||||
assertThat(heapMemory, lessThan((long)content.length));
|
||||
}
|
||||
|
||||
private static void sleep(long ms) throws IOException
|
||||
{
|
||||
try
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.ZipException;
|
||||
|
@ -28,13 +31,13 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
import org.eclipse.jetty.util.component.Destroyable;
|
||||
|
||||
/**
|
||||
* Decoder for the "gzip" encoding.
|
||||
* <p>
|
||||
* A decoder that inflates gzip compressed data that has been
|
||||
* optimized for async usage with minimal data copies.
|
||||
* <p>Decoder for the "gzip" content encoding.</p>
|
||||
* <p>This decoder inflates gzip compressed data, and has
|
||||
* been optimized for async usage with minimal data copies.</p>
|
||||
*/
|
||||
public class GZIPContentDecoder implements Destroyable
|
||||
{
|
||||
private final List<ByteBuffer> _inflateds = new ArrayList<>();
|
||||
private final Inflater _inflater = new Inflater(true);
|
||||
private final ByteBufferPool _pool;
|
||||
private final int _bufferSize;
|
||||
|
@ -46,12 +49,12 @@ public class GZIPContentDecoder implements Destroyable
|
|||
|
||||
public GZIPContentDecoder()
|
||||
{
|
||||
this(null,2048);
|
||||
this(null, 2048);
|
||||
}
|
||||
|
||||
public GZIPContentDecoder(int bufferSize)
|
||||
{
|
||||
this(null,bufferSize);
|
||||
this(null, bufferSize);
|
||||
}
|
||||
|
||||
public GZIPContentDecoder(ByteBufferPool pool, int bufferSize)
|
||||
|
@ -61,68 +64,95 @@ public class GZIPContentDecoder implements Destroyable
|
|||
reset();
|
||||
}
|
||||
|
||||
/** Inflate compressed data from a buffer.
|
||||
/**
|
||||
* <p>Inflates compressed data from a buffer.</p>
|
||||
* <p>The buffers returned by this method should be released
|
||||
* via {@link #release(ByteBuffer)}.</p>
|
||||
* <p>This method may fully consume the input buffer, but return
|
||||
* only a chunk of the inflated bytes, to allow applications to
|
||||
* consume the inflated chunk before performing further inflation,
|
||||
* applying backpressure. In this case, this method should be
|
||||
* invoked again with the same input buffer (even if
|
||||
* it's already fully consumed) and that will produce another
|
||||
* chunk of inflated bytes. Termination happens when the input
|
||||
* buffer is fully consumed, and the returned buffer is empty.</p>
|
||||
* <p>See {@link #decodedChunk(ByteBuffer)} to perform inflating
|
||||
* in a non-blocking way that allows to apply backpressure.</p>
|
||||
*
|
||||
* @param compressed Buffer containing compressed data.
|
||||
* @return Buffer containing inflated data.
|
||||
* @param compressed the buffer containing compressed data.
|
||||
* @return a buffer containing inflated data.
|
||||
*/
|
||||
public ByteBuffer decode(ByteBuffer compressed)
|
||||
{
|
||||
decodeChunks(compressed);
|
||||
if (BufferUtil.isEmpty(_inflated) || _state==State.CRC || _state==State.ISIZE )
|
||||
return BufferUtil.EMPTY_BUFFER;
|
||||
|
||||
if (_inflateds.isEmpty())
|
||||
{
|
||||
if (BufferUtil.isEmpty(_inflated) || _state == State.CRC || _state == State.ISIZE)
|
||||
return BufferUtil.EMPTY_BUFFER;
|
||||
ByteBuffer result = _inflated;
|
||||
_inflated = null;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inflateds.add(_inflated);
|
||||
_inflated = null;
|
||||
int length = _inflateds.stream().mapToInt(Buffer::remaining).sum();
|
||||
ByteBuffer result = acquire(length);
|
||||
for (ByteBuffer buffer : _inflateds)
|
||||
{
|
||||
BufferUtil.append(result, buffer);
|
||||
release(buffer);
|
||||
}
|
||||
_inflateds.clear();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when a chunk of data is inflated.
|
||||
/**
|
||||
* <p>Called when a chunk of data is inflated.</p>
|
||||
* <p>The default implementation aggregates all the chunks
|
||||
* into a single buffer returned from {@link #decode(ByteBuffer)}.
|
||||
* Derived implementations may choose to consume chunks individually
|
||||
* and return false to prevent further inflation until a subsequent
|
||||
* call to {@link #decode(ByteBuffer)} or {@link #decodeChunks(ByteBuffer)}.
|
||||
* into a single buffer returned from {@link #decode(ByteBuffer)}.</p>
|
||||
* <p>Derived implementations may choose to consume inflated chunks
|
||||
* individually and return {@code true} from this method to prevent
|
||||
* further inflation until a subsequent call to {@link #decode(ByteBuffer)}
|
||||
* or {@link #decodeChunks(ByteBuffer)} is made.
|
||||
*
|
||||
* @param chunk The inflated chunk of data
|
||||
* @return False if inflating should continue, or True if the call
|
||||
* @param chunk the inflated chunk of data
|
||||
* @return false if inflating should continue, or true if the call
|
||||
* to {@link #decodeChunks(ByteBuffer)} or {@link #decode(ByteBuffer)}
|
||||
* should return, allowing back pressure of compressed data.
|
||||
* should return, allowing to consume the inflated chunk and apply
|
||||
* backpressure
|
||||
*/
|
||||
protected boolean decodedChunk(ByteBuffer chunk)
|
||||
{
|
||||
if (_inflated==null)
|
||||
if (_inflated == null)
|
||||
{
|
||||
_inflated=chunk;
|
||||
_inflated = chunk;
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = _inflated.remaining() + chunk.remaining();
|
||||
if (size<=_inflated.capacity())
|
||||
if (BufferUtil.space(_inflated) >= chunk.remaining())
|
||||
{
|
||||
BufferUtil.append(_inflated,chunk);
|
||||
BufferUtil.append(_inflated, chunk);
|
||||
release(chunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteBuffer bigger=acquire(size);
|
||||
int pos=BufferUtil.flipToFill(bigger);
|
||||
BufferUtil.put(_inflated,bigger);
|
||||
BufferUtil.put(chunk,bigger);
|
||||
BufferUtil.flipToFlush(bigger,pos);
|
||||
release(_inflated);
|
||||
release(chunk);
|
||||
_inflated = bigger;
|
||||
_inflateds.add(_inflated);
|
||||
_inflated = chunk;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflate compressed data.
|
||||
* <p>Inflates compressed data.</p>
|
||||
* <p>Inflation continues until the compressed block end is reached, there is no
|
||||
* more compressed data or a call to {@link #decodedChunk(ByteBuffer)} returns true.
|
||||
* @param compressed Buffer of compressed data to inflate
|
||||
* more compressed data or a call to {@link #decodedChunk(ByteBuffer)} returns true.</p>
|
||||
*
|
||||
* @param compressed the buffer of compressed data to inflate
|
||||
*/
|
||||
protected void decodeChunks(ByteBuffer compressed)
|
||||
{
|
||||
|
@ -169,12 +199,12 @@ public class GZIPContentDecoder implements Destroyable
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
if (buffer==null)
|
||||
if (buffer == null)
|
||||
buffer = acquire(_bufferSize);
|
||||
|
||||
try
|
||||
{
|
||||
int length = _inflater.inflate(buffer.array(),buffer.arrayOffset(),buffer.capacity());
|
||||
int length = _inflater.inflate(buffer.array(), buffer.arrayOffset(), buffer.capacity());
|
||||
buffer.limit(length);
|
||||
}
|
||||
catch (DataFormatException x)
|
||||
|
@ -195,7 +225,7 @@ public class GZIPContentDecoder implements Destroyable
|
|||
return;
|
||||
if (compressed.hasArray())
|
||||
{
|
||||
_inflater.setInput(compressed.array(),compressed.arrayOffset()+compressed.position(),compressed.remaining());
|
||||
_inflater.setInput(compressed.array(), compressed.arrayOffset() + compressed.position(), compressed.remaining());
|
||||
compressed.position(compressed.limit());
|
||||
}
|
||||
else
|
||||
|
@ -354,7 +384,7 @@ public class GZIPContentDecoder implements Destroyable
|
|||
|
||||
// TODO ByteBuffer result = output == null ? BufferUtil.EMPTY_BUFFER : ByteBuffer.wrap(output);
|
||||
reset();
|
||||
return ;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -369,7 +399,7 @@ public class GZIPContentDecoder implements Destroyable
|
|||
}
|
||||
finally
|
||||
{
|
||||
if (buffer!=null)
|
||||
if (buffer != null)
|
||||
release(buffer);
|
||||
}
|
||||
}
|
||||
|
@ -400,26 +430,26 @@ public class GZIPContentDecoder implements Destroyable
|
|||
}
|
||||
|
||||
/**
|
||||
* @param capacity capacity capacity of the allocated ByteBuffer
|
||||
* @return An indirect buffer of the configured buffersize either from the pool or freshly allocated.
|
||||
* @param capacity capacity of the ByteBuffer to acquire
|
||||
* @return a heap buffer of the configured capacity either from the pool or freshly allocated.
|
||||
*/
|
||||
public ByteBuffer acquire(int capacity)
|
||||
{
|
||||
return _pool==null?BufferUtil.allocate(capacity):_pool.acquire(capacity,false);
|
||||
return _pool == null ? BufferUtil.allocate(capacity) : _pool.acquire(capacity, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release an allocated buffer.
|
||||
* <p>This method will called {@link ByteBufferPool#release(ByteBuffer)} if a buffer pool has
|
||||
* been configured. This method should be called once for all buffers returned from {@link #decode(ByteBuffer)}
|
||||
* or passed to {@link #decodedChunk(ByteBuffer)}.
|
||||
* @param buffer The buffer to release.
|
||||
* <p>Releases an allocated buffer.</p>
|
||||
* <p>This method calls {@link ByteBufferPool#release(ByteBuffer)} if a buffer pool has
|
||||
* been configured.</p>
|
||||
* <p>This method should be called once for all buffers returned from {@link #decode(ByteBuffer)}
|
||||
* or passed to {@link #decodedChunk(ByteBuffer)}.</p>
|
||||
*
|
||||
* @param buffer the buffer to release.
|
||||
*/
|
||||
public void release(ByteBuffer buffer)
|
||||
{
|
||||
@SuppressWarnings("ReferenceEquality")
|
||||
boolean isTheEmptyBuffer = (buffer==BufferUtil.EMPTY_BUFFER);
|
||||
if (_pool!=null && !isTheEmptyBuffer)
|
||||
if (_pool != null && !BufferUtil.isTheEmptyBuffer(buffer))
|
||||
_pool.release(buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -35,14 +31,17 @@ import org.junit.jupiter.api.AfterEach;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class GZIPContentDecoderTest
|
||||
{
|
||||
ArrayByteBufferPool pool;
|
||||
AtomicInteger buffers = new AtomicInteger(0);
|
||||
private ArrayByteBufferPool pool;
|
||||
private AtomicInteger buffers = new AtomicInteger(0);
|
||||
|
||||
@BeforeEach
|
||||
public void beforeClass() throws Exception
|
||||
public void before()
|
||||
{
|
||||
buffers.set(0);
|
||||
pool = new ArrayByteBufferPool()
|
||||
|
@ -52,7 +51,7 @@ public class GZIPContentDecoderTest
|
|||
public ByteBuffer acquire(int size, boolean direct)
|
||||
{
|
||||
buffers.incrementAndGet();
|
||||
return super.acquire(size,direct);
|
||||
return super.acquire(size, direct);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,19 +65,19 @@ public class GZIPContentDecoderTest
|
|||
}
|
||||
|
||||
@AfterEach
|
||||
public void afterClass() throws Exception
|
||||
public void after()
|
||||
{
|
||||
assertEquals(0,buffers.get());
|
||||
assertEquals(0, buffers.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompresedContentFormat() throws Exception
|
||||
public void testCompressedContentFormat()
|
||||
{
|
||||
assertTrue(CompressedContentFormat.tagEquals("tag","tag"));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"tag\"","\"tag\""));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"tag\"","\"tag--gzip\""));
|
||||
assertFalse(CompressedContentFormat.tagEquals("Zag","Xag--gzip"));
|
||||
assertFalse(CompressedContentFormat.tagEquals("xtag","tag"));
|
||||
assertTrue(CompressedContentFormat.tagEquals("tag", "tag"));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"tag\"", "\"tag\""));
|
||||
assertTrue(CompressedContentFormat.tagEquals("\"tag\"", "\"tag--gzip\""));
|
||||
assertFalse(CompressedContentFormat.tagEquals("Zag", "Xag--gzip"));
|
||||
assertFalse(CompressedContentFormat.tagEquals("xtag", "tag"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -122,7 +121,7 @@ public class GZIPContentDecoderTest
|
|||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
|
||||
assertEquals(0, decoded.remaining());
|
||||
}
|
||||
|
@ -138,7 +137,7 @@ public class GZIPContentDecoderTest
|
|||
output.close();
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
decoder.release(decoded);
|
||||
|
@ -161,7 +160,7 @@ public class GZIPContentDecoderTest
|
|||
byte[] bytes2 = new byte[bytes.length - bytes1.length];
|
||||
System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1));
|
||||
assertEquals(0, decoded.capacity());
|
||||
decoded = decoder.decode(ByteBuffer.wrap(bytes2));
|
||||
|
@ -186,7 +185,7 @@ public class GZIPContentDecoderTest
|
|||
byte[] bytes2 = new byte[bytes.length - bytes1.length];
|
||||
System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1));
|
||||
assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
assertFalse(decoder.isFinished());
|
||||
|
@ -214,7 +213,7 @@ public class GZIPContentDecoderTest
|
|||
byte[] bytes2 = new byte[bytes.length - bytes1.length];
|
||||
System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048);
|
||||
ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1));
|
||||
assertEquals(0, decoded.capacity());
|
||||
decoder.release(decoded);
|
||||
|
@ -244,7 +243,7 @@ public class GZIPContentDecoderTest
|
|||
System.arraycopy(bytes1, 0, bytes, 0, bytes1.length);
|
||||
System.arraycopy(bytes2, 0, bytes, bytes1.length, bytes2.length);
|
||||
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
assertEquals(data1, StandardCharsets.UTF_8.decode(decoded).toString());
|
||||
|
@ -271,7 +270,7 @@ public class GZIPContentDecoderTest
|
|||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
String result = "";
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool,2048);
|
||||
GZIPContentDecoder decoder = new GZIPContentDecoder(pool, 2048);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.io.OutputStream;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -41,12 +42,14 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.eclipse.jetty.client.ContentDecoder;
|
||||
import org.eclipse.jetty.client.GZIPContentDecoder;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -275,7 +278,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onDataAvailable() throws IOException
|
||||
public void onDataAvailable()
|
||||
{
|
||||
iterate();
|
||||
}
|
||||
|
@ -370,9 +373,8 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
|
|||
if (size > 0)
|
||||
{
|
||||
CountingCallback counter = new CountingCallback(callback, size);
|
||||
for (int i = 0; i < size; ++i)
|
||||
for (ByteBuffer buffer : buffers)
|
||||
{
|
||||
ByteBuffer buffer = buffers.get(i);
|
||||
newContentBytes += buffer.remaining();
|
||||
provider.offer(buffer, counter);
|
||||
}
|
||||
|
@ -476,9 +478,8 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
|
|||
if (size > 0)
|
||||
{
|
||||
Callback counter = size == 1 ? callback : new CountingCallback(callback, size);
|
||||
for (int i = 0; i < size; ++i)
|
||||
for (ByteBuffer buffer : buffers)
|
||||
{
|
||||
ByteBuffer buffer = buffers.get(i);
|
||||
newContentBytes += buffer.remaining();
|
||||
proxyWriter.offer(buffer, counter);
|
||||
}
|
||||
|
@ -540,9 +541,8 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
|
|||
if (size > 0)
|
||||
{
|
||||
Callback callback = size == 1 ? complete : new CountingCallback(complete, size);
|
||||
for (int i = 0; i < size; ++i)
|
||||
for (ByteBuffer buffer : buffers)
|
||||
{
|
||||
ByteBuffer buffer = buffers.get(i);
|
||||
newContentBytes += buffer.remaining();
|
||||
proxyWriter.offer(buffer, callback);
|
||||
}
|
||||
|
@ -686,7 +686,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
|
|||
/**
|
||||
* <p>Allows applications to transform upstream and downstream content.</p>
|
||||
* <p>Typical use cases of transformations are URL rewriting of HTML anchors
|
||||
* (where the value of the <code>href</code> attribute of <a> elements
|
||||
* (where the value of the {@code href} attribute of <a> elements
|
||||
* is modified by the proxy), field renaming of JSON documents, etc.</p>
|
||||
* <p>Applications should override {@link #newClientRequestContentTransformer(HttpServletRequest, Request)}
|
||||
* and/or {@link #newServerResponseContentTransformer(HttpServletRequest, HttpServletResponse, Response)}
|
||||
|
@ -762,16 +762,23 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
|
|||
private static final Logger logger = Log.getLogger(GZIPContentTransformer.class);
|
||||
|
||||
private final List<ByteBuffer> buffers = new ArrayList<>(2);
|
||||
private final ContentDecoder decoder = new GZIPContentDecoder();
|
||||
private final ContentTransformer transformer;
|
||||
private final ContentDecoder decoder;
|
||||
private final ByteArrayOutputStream out;
|
||||
private final GZIPOutputStream gzipOut;
|
||||
|
||||
public GZIPContentTransformer(ContentTransformer transformer)
|
||||
{
|
||||
this(null, transformer);
|
||||
}
|
||||
|
||||
public GZIPContentTransformer(HttpClient httpClient, ContentTransformer transformer)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.transformer = transformer;
|
||||
ByteBufferPool byteBufferPool = httpClient == null ? null : httpClient.getByteBufferPool();
|
||||
this.decoder = new GZIPContentDecoder(byteBufferPool, GZIPContentDecoder.DEFAULT_BUFFER_SIZE);
|
||||
this.out = new ByteArrayOutputStream();
|
||||
this.gzipOut = new GZIPOutputStream(out);
|
||||
}
|
||||
|
@ -787,6 +794,7 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
|
|||
if (logger.isDebugEnabled())
|
||||
logger.debug("Ungzipping {} bytes, finished={}", input.remaining(), finished);
|
||||
|
||||
List<ByteBuffer> decodeds = Collections.emptyList();
|
||||
if (!input.hasRemaining())
|
||||
{
|
||||
if (finished)
|
||||
|
@ -794,14 +802,19 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
|
|||
}
|
||||
else
|
||||
{
|
||||
while (input.hasRemaining())
|
||||
decodeds = new ArrayList<>();
|
||||
while (true)
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(input);
|
||||
boolean complete = finished && !input.hasRemaining();
|
||||
decodeds.add(decoded);
|
||||
boolean decodeComplete = !input.hasRemaining() && !decoded.hasRemaining();
|
||||
boolean complete = finished && decodeComplete;
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Ungzipped {} bytes, complete={}", decoded.remaining(), complete);
|
||||
if (decoded.hasRemaining() || complete)
|
||||
transformer.transform(decoded, complete, buffers);
|
||||
if (decodeComplete)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -811,6 +824,8 @@ public class AsyncMiddleManServlet extends AbstractProxyServlet
|
|||
buffers.clear();
|
||||
output.add(result);
|
||||
}
|
||||
|
||||
decodeds.forEach(decoder::release);
|
||||
}
|
||||
|
||||
private ByteBuffer gzip(List<ByteBuffer> buffers, boolean finished) throws IOException
|
||||
|
|
|
@ -18,13 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.proxy;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -62,7 +55,6 @@ import org.eclipse.jetty.client.api.ContentProvider;
|
|||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||
import org.eclipse.jetty.client.util.DeferredContentProvider;
|
||||
import org.eclipse.jetty.client.util.FutureResponseListener;
|
||||
|
@ -87,10 +79,16 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class AsyncMiddleManServletTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(AsyncMiddleManServletTest.class);
|
||||
|
@ -120,7 +118,7 @@ public class AsyncMiddleManServletTest
|
|||
|
||||
private void startProxy(AsyncMiddleManServlet proxyServlet) throws Exception
|
||||
{
|
||||
startProxy(proxyServlet, new HashMap<String, String>());
|
||||
startProxy(proxyServlet, new HashMap<>());
|
||||
}
|
||||
|
||||
private void startProxy(AsyncMiddleManServlet proxyServlet, Map<String, String> initParams) throws Exception
|
||||
|
@ -145,7 +143,7 @@ public class AsyncMiddleManServletTest
|
|||
|
||||
proxy.start();
|
||||
|
||||
stackless=new StacklessLogging(proxyServlet._log);
|
||||
stackless = new StacklessLogging(proxyServlet._log);
|
||||
}
|
||||
|
||||
private void startClient() throws Exception
|
||||
|
@ -219,7 +217,7 @@ public class AsyncMiddleManServletTest
|
|||
@Override
|
||||
protected ContentTransformer newClientRequestContentTransformer(HttpServletRequest clientRequest, Request proxyRequest)
|
||||
{
|
||||
return new GZIPContentTransformer(ContentTransformer.IDENTITY);
|
||||
return new GZIPContentTransformer(getHttpClient(), ContentTransformer.IDENTITY);
|
||||
}
|
||||
});
|
||||
startClient();
|
||||
|
@ -247,7 +245,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), "gzip");
|
||||
response.getOutputStream().write(gzipBytes);
|
||||
|
@ -318,7 +316,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), "gzip");
|
||||
|
||||
|
@ -412,7 +410,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), "gzip");
|
||||
|
||||
|
@ -454,7 +452,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
// decode input stream thru gzip
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
@ -500,13 +498,9 @@ public class AsyncMiddleManServletTest
|
|||
@Override
|
||||
protected ContentTransformer newClientRequestContentTransformer(HttpServletRequest clientRequest, Request proxyRequest)
|
||||
{
|
||||
return new ContentTransformer()
|
||||
{
|
||||
@Override
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output) throws IOException
|
||||
return (input, finished, output) ->
|
||||
{
|
||||
throw new NullPointerException("explicitly_thrown_by_test");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -537,7 +531,7 @@ public class AsyncMiddleManServletTest
|
|||
private int count;
|
||||
|
||||
@Override
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output) throws IOException
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output)
|
||||
{
|
||||
if (++count < 2)
|
||||
output.add(input);
|
||||
|
@ -553,14 +547,10 @@ public class AsyncMiddleManServletTest
|
|||
DeferredContentProvider content = new DeferredContentProvider();
|
||||
client.newRequest("localhost", serverConnector.getLocalPort())
|
||||
.content(content)
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isSucceeded() && result.getResponse().getStatus() == 502)
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
content.offer(ByteBuffer.allocate(512));
|
||||
|
@ -578,7 +568,7 @@ public class AsyncMiddleManServletTest
|
|||
testDownstreamTransformationThrows(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
// To trigger the test failure we need that onContent()
|
||||
// is called twice, so the second time the test throws.
|
||||
|
@ -597,7 +587,7 @@ public class AsyncMiddleManServletTest
|
|||
testDownstreamTransformationThrows(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
// To trigger the test failure we need that onContent()
|
||||
// is called only once, so the the test throws from onSuccess().
|
||||
|
@ -621,7 +611,7 @@ public class AsyncMiddleManServletTest
|
|||
private int count;
|
||||
|
||||
@Override
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output) throws IOException
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output)
|
||||
{
|
||||
if (++count < 2)
|
||||
output.add(input);
|
||||
|
@ -660,7 +650,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
OutputStream output = response.getOutputStream();
|
||||
if (gzipped)
|
||||
|
@ -694,25 +684,17 @@ public class AsyncMiddleManServletTest
|
|||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", serverConnector.getLocalPort())
|
||||
.onResponseContent(new Response.ContentListener()
|
||||
{
|
||||
@Override
|
||||
public void onContent(Response response, ByteBuffer content)
|
||||
.onResponseContent((response, content) ->
|
||||
{
|
||||
// Slow down the reader so that the
|
||||
// write from the proxy gets congested.
|
||||
sleep(1);
|
||||
}
|
||||
})
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
.send(result ->
|
||||
{
|
||||
assertTrue(result.isSucceeded());
|
||||
assertEquals(200, result.getResponse().getStatus());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(latch.await(15, TimeUnit.SECONDS));
|
||||
|
@ -724,7 +706,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
byte[] chunk = new byte[1024];
|
||||
int contentLength = 2 * chunk.length;
|
||||
|
@ -741,14 +723,10 @@ public class AsyncMiddleManServletTest
|
|||
@Override
|
||||
protected ContentTransformer newServerResponseContentTransformer(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Response serverResponse)
|
||||
{
|
||||
return new ContentTransformer()
|
||||
{
|
||||
@Override
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output) throws IOException
|
||||
return (input, finished, output) ->
|
||||
{
|
||||
if (!finished)
|
||||
output.add(input);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -779,15 +757,11 @@ public class AsyncMiddleManServletTest
|
|||
DeferredContentProvider content = new DeferredContentProvider();
|
||||
client.newRequest("localhost", serverConnector.getLocalPort())
|
||||
.content(content)
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
.send(result ->
|
||||
{
|
||||
System.err.println(result);
|
||||
if (result.getResponse().getStatus() == 500)
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
content.offer(ByteBuffer.allocate(512));
|
||||
sleep(1000);
|
||||
|
@ -822,14 +796,10 @@ public class AsyncMiddleManServletTest
|
|||
DeferredContentProvider content = new DeferredContentProvider();
|
||||
client.newRequest("localhost", serverConnector.getLocalPort())
|
||||
.content(content)
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
.send(result ->
|
||||
{
|
||||
if (result.getResponse().getStatus() == 502)
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
content.offer(ByteBuffer.allocate(512));
|
||||
sleep(1000);
|
||||
|
@ -857,7 +827,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
ServletOutputStream output = response.getOutputStream();
|
||||
output.write(new byte[512]);
|
||||
|
@ -898,7 +868,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.getOutputStream().write(json.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
@ -916,7 +886,7 @@ public class AsyncMiddleManServletTest
|
|||
{
|
||||
InputStream input = source.getInputStream();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> obj = (Map<String, Object>)JSON.parse(new InputStreamReader(input, "UTF-8"));
|
||||
Map<String, Object> obj = (Map<String, Object>)JSON.parse(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
// Transform the object.
|
||||
obj.put(key2, obj.remove(key1));
|
||||
try (OutputStream output = sink.getOutputStream())
|
||||
|
@ -961,7 +931,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
// Write the content in two chunks.
|
||||
int chunk = data.length / 2;
|
||||
|
@ -1021,7 +991,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.getOutputStream().write(json.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
@ -1041,7 +1011,7 @@ public class AsyncMiddleManServletTest
|
|||
{
|
||||
InputStream input = source.getInputStream();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> obj = (Map<String, Object>)JSON.parse(new InputStreamReader(input, "UTF-8"));
|
||||
Map<String, Object> obj = (Map<String, Object>)JSON.parse(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
// Transform the object.
|
||||
obj.put(key2, obj.remove(key1));
|
||||
try (OutputStream output = sink.getOutputStream())
|
||||
|
@ -1080,7 +1050,7 @@ public class AsyncMiddleManServletTest
|
|||
}
|
||||
|
||||
// File deletion is delayed on windows, testing for deletion is not going to work
|
||||
if(!OS.WINDOWS.isCurrentOs())
|
||||
if (!OS.WINDOWS.isCurrentOs())
|
||||
{
|
||||
try (DirectoryStream<Path> paths = Files.newDirectoryStream(targetTestsDir, outputPrefix + "*.*"))
|
||||
{
|
||||
|
@ -1097,7 +1067,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
IO.copy(request.getInputStream(), IO.getNullStream());
|
||||
}
|
||||
|
@ -1163,7 +1133,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
|
||||
response.setContentLength(2);
|
||||
|
@ -1237,7 +1207,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.getOutputStream().write(json.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
@ -1260,7 +1230,7 @@ public class AsyncMiddleManServletTest
|
|||
if (readSource)
|
||||
{
|
||||
InputStream input = source.getInputStream();
|
||||
JSON.parse(new InputStreamReader(input, "UTF-8"));
|
||||
JSON.parse(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
}
|
||||
// No transformation.
|
||||
return false;
|
||||
|
@ -1289,7 +1259,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED_401);
|
||||
response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "Basic realm=\"test\"");
|
||||
|
@ -1304,7 +1274,7 @@ public class AsyncMiddleManServletTest
|
|||
return new AfterContentTransformer()
|
||||
{
|
||||
@Override
|
||||
public boolean transform(Source source, Sink sink) throws IOException
|
||||
public boolean transform(Source source, Sink sink)
|
||||
{
|
||||
transformed.set(true);
|
||||
return false;
|
||||
|
@ -1433,7 +1403,7 @@ public class AsyncMiddleManServletTest
|
|||
private ByteBuffer buffer;
|
||||
|
||||
@Override
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output) throws IOException
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output)
|
||||
{
|
||||
// Buffer only the first chunk.
|
||||
if (buffer == null)
|
||||
|
@ -1495,7 +1465,7 @@ public class AsyncMiddleManServletTest
|
|||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||
{
|
||||
if (req.getHeader("Via") != null)
|
||||
resp.addHeader(PROXIED_HEADER, "true");
|
||||
|
@ -1503,9 +1473,11 @@ public class AsyncMiddleManServletTest
|
|||
}
|
||||
});
|
||||
final String proxyTo = "http://localhost:" + serverConnector.getLocalPort();
|
||||
AsyncMiddleManServlet proxyServlet = new AsyncMiddleManServlet.Transparent() {
|
||||
AsyncMiddleManServlet proxyServlet = new AsyncMiddleManServlet.Transparent()
|
||||
{
|
||||
@Override
|
||||
protected ContentTransformer newServerResponseContentTransformer(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Response serverResponse) {
|
||||
protected ContentTransformer newServerResponseContentTransformer(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Response serverResponse)
|
||||
{
|
||||
return ContentTransformer.IDENTITY;
|
||||
}
|
||||
};
|
||||
|
@ -1689,7 +1661,7 @@ public class AsyncMiddleManServletTest
|
|||
private final List<ByteBuffer> buffers = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output) throws IOException
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output)
|
||||
{
|
||||
if (input.hasRemaining())
|
||||
{
|
||||
|
@ -1715,7 +1687,7 @@ public class AsyncMiddleManServletTest
|
|||
private StringBuilder head = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output) throws IOException
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output)
|
||||
{
|
||||
if (input.hasRemaining() && head != null)
|
||||
{
|
||||
|
@ -1723,12 +1695,12 @@ public class AsyncMiddleManServletTest
|
|||
if (lnPos == -1)
|
||||
{
|
||||
// no linefeed found, copy it all
|
||||
copyHeadBytes(input,input.limit());
|
||||
copyHeadBytes(input, input.limit());
|
||||
}
|
||||
else
|
||||
{
|
||||
// found linefeed
|
||||
copyHeadBytes(input,lnPos);
|
||||
copyHeadBytes(input, lnPos);
|
||||
output.addAll(getHeadBytes());
|
||||
// mark head as sent
|
||||
head = null;
|
||||
|
@ -1764,7 +1736,7 @@ public class AsyncMiddleManServletTest
|
|||
|
||||
private List<ByteBuffer> getHeadBytes()
|
||||
{
|
||||
ByteBuffer buf = BufferUtil.toBuffer(head.toString(),StandardCharsets.UTF_8);
|
||||
ByteBuffer buf = BufferUtil.toBuffer(head.toString(), StandardCharsets.UTF_8);
|
||||
return Collections.singletonList(buf);
|
||||
}
|
||||
}
|
||||
|
@ -1772,7 +1744,7 @@ public class AsyncMiddleManServletTest
|
|||
private static class DiscardContentTransformer implements AsyncMiddleManServlet.ContentTransformer
|
||||
{
|
||||
@Override
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output) throws IOException
|
||||
public void transform(ByteBuffer input, boolean finished, List<ByteBuffer> output)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue