Merged branch 'jetty-12.0.x' into 'jetty-12.1.x'.
Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
commit
4b40aa7116
|
@ -68,6 +68,7 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
|||
Stream stream = getHttpChannel().getStream();
|
||||
if (stream == null)
|
||||
return Content.Chunk.from(new EOFException("Channel has been released"));
|
||||
|
||||
Stream.Data data = stream.readData();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Read stream data {} in {}", data, this);
|
||||
|
@ -77,14 +78,26 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
|||
stream.demand();
|
||||
return null;
|
||||
}
|
||||
|
||||
DataFrame frame = data.frame();
|
||||
boolean last = frame.remaining() == 0 && frame.isEndStream();
|
||||
if (!last)
|
||||
return Content.Chunk.asChunk(frame.getByteBuffer(), last, data);
|
||||
return Content.Chunk.asChunk(frame.getByteBuffer(), false, data);
|
||||
|
||||
data.release();
|
||||
|
||||
if (stream.isReset())
|
||||
{
|
||||
Throwable failure = new EOFException("Stream has been reset");
|
||||
responseFailure(failure, Promise.noop());
|
||||
return Content.Chunk.from(failure);
|
||||
}
|
||||
else
|
||||
{
|
||||
responseSuccess(getHttpExchange(), null);
|
||||
return Content.Chunk.EOF;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failAndClose(Throwable failure)
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.eclipse.jetty.client.Origin;
|
|||
import org.eclipse.jetty.client.Response;
|
||||
import org.eclipse.jetty.client.Result;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
|
@ -92,6 +93,7 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
|||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -824,6 +826,54 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest
|
|||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnreadRequestContentDrainsResponseContent() throws Exception
|
||||
{
|
||||
start(new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public boolean handle(Request request, org.eclipse.jetty.server.Response response, Callback callback)
|
||||
{
|
||||
// Do not read the request content,
|
||||
// the server will reset the stream,
|
||||
// then send a response with content.
|
||||
ByteBuffer content = ByteBuffer.allocate(1024);
|
||||
response.getHeaders().put(HttpHeader.CONTENT_LENGTH, content.remaining());
|
||||
response.write(true, content, callback);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
AtomicReference<Content.Source> contentSourceRef = new AtomicReference<>();
|
||||
AtomicReference<Content.Chunk> chunkRef = new AtomicReference<>();
|
||||
CountDownLatch responseFailureLatch = new CountDownLatch(1);
|
||||
AtomicReference<Result> resultRef = new AtomicReference<>();
|
||||
httpClient.newRequest("localhost", connector.getLocalPort())
|
||||
.method(HttpMethod.POST)
|
||||
.body(new AsyncRequestContent(ByteBuffer.allocate(1024)))
|
||||
.onResponseContentSource((response, contentSource) -> contentSourceRef.set(contentSource))
|
||||
// The request is failed before the response, verify that
|
||||
// reading at the request failure event yields a failure chunk.
|
||||
.onRequestFailure((request, failure) -> chunkRef.set(contentSourceRef.get().read()))
|
||||
.onResponseFailure((response, failure) -> responseFailureLatch.countDown())
|
||||
.send(resultRef::set);
|
||||
|
||||
// Wait for the RST_STREAM to arrive and drain the response content.
|
||||
assertTrue(responseFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Verify that the chunk read at the request failure event is a failure chunk.
|
||||
Content.Chunk chunk = chunkRef.get();
|
||||
assertTrue(Content.Chunk.isFailure(chunk, true));
|
||||
// Reading more also yields a failure chunk.
|
||||
chunk = contentSourceRef.get().read();
|
||||
assertTrue(Content.Chunk.isFailure(chunk, true));
|
||||
|
||||
Result result = await().atMost(5, TimeUnit.SECONDS).until(resultRef::get, notNullValue());
|
||||
assertEquals(HttpStatus.OK_200, result.getResponse().getStatus());
|
||||
assertNotNull(result.getRequestFailure());
|
||||
assertNotNull(result.getResponseFailure());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("external")
|
||||
public void testExternalServer() throws Exception
|
||||
|
|
|
@ -55,7 +55,6 @@ import org.eclipse.jetty.http.HttpTester;
|
|||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
@ -68,7 +67,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Tag("flaky") // TODO investigate H3
|
||||
public class HttpClientContinueTest extends AbstractTest
|
||||
{
|
||||
@ParameterizedTest
|
||||
|
@ -271,11 +269,18 @@ public class HttpClientContinueTest extends AbstractTest
|
|||
{
|
||||
assertTrue(result.isFailed());
|
||||
assertNotNull(result.getRequestFailure());
|
||||
assertNull(result.getResponseFailure());
|
||||
assertEquals(error, result.getResponse().getStatus());
|
||||
Throwable responseFailure = result.getResponseFailure();
|
||||
// For HTTP/2 the response may fail because the
|
||||
// server may not fully read the request content,
|
||||
// and sends a reset that may drop the response
|
||||
// content and cause the response failure.
|
||||
if (responseFailure == null)
|
||||
{
|
||||
byte[] content = getContent();
|
||||
assertNotNull(content);
|
||||
assertTrue(content.length > 0);
|
||||
assertEquals(error, result.getResponse().getStatus());
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -830,7 +835,7 @@ public class HttpClientContinueTest extends AbstractTest
|
|||
startServer(Transport.HTTP, new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
assertEquals(0, request.getContentLengthLong());
|
||||
assertNotNull(request.getHeader(HttpHeader.EXPECT.asString()));
|
||||
|
|
|
@ -55,7 +55,6 @@ import org.eclipse.jetty.http.HttpTester;
|
|||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
@ -68,7 +67,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Tag("flaky") // TODO investigate H3
|
||||
public class HttpClientContinueTest extends AbstractTest
|
||||
{
|
||||
@ParameterizedTest
|
||||
|
@ -272,11 +270,18 @@ public class HttpClientContinueTest extends AbstractTest
|
|||
{
|
||||
assertTrue(result.isFailed());
|
||||
assertNotNull(result.getRequestFailure());
|
||||
assertNull(result.getResponseFailure());
|
||||
assertEquals(error, result.getResponse().getStatus());
|
||||
Throwable responseFailure = result.getResponseFailure();
|
||||
// For HTTP/2 the response may fail because the
|
||||
// server may not fully read the request content,
|
||||
// and sends a reset that may drop the response
|
||||
// content and cause the response failure.
|
||||
if (responseFailure == null)
|
||||
{
|
||||
byte[] content = getContent();
|
||||
assertNotNull(content);
|
||||
assertTrue(content.length > 0);
|
||||
assertEquals(error, result.getResponse().getStatus());
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -889,7 +894,7 @@ public class HttpClientContinueTest extends AbstractTest
|
|||
startServer(Transport.HTTP, new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
assertEquals(0, request.getContentLengthLong());
|
||||
assertNotNull(request.getHeader(HttpHeader.EXPECT.asString()));
|
||||
|
|
|
@ -196,11 +196,18 @@ public class HttpClientContinueTest extends AbstractTest
|
|||
{
|
||||
assertTrue(result.isFailed());
|
||||
assertNotNull(result.getRequestFailure());
|
||||
assertNull(result.getResponseFailure());
|
||||
assertEquals(error, result.getResponse().getStatus());
|
||||
Throwable responseFailure = result.getResponseFailure();
|
||||
// For HTTP/2 the response may fail because the
|
||||
// server may not fully read the request content,
|
||||
// and sends a reset that may drop the response
|
||||
// content and cause the response failure.
|
||||
if (responseFailure == null)
|
||||
{
|
||||
byte[] content = getContent();
|
||||
assertNotNull(content);
|
||||
assertTrue(content.length > 0);
|
||||
assertEquals(error, result.getResponse().getStatus());
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue