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();
|
Stream stream = getHttpChannel().getStream();
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
return Content.Chunk.from(new EOFException("Channel has been released"));
|
return Content.Chunk.from(new EOFException("Channel has been released"));
|
||||||
|
|
||||||
Stream.Data data = stream.readData();
|
Stream.Data data = stream.readData();
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Read stream data {} in {}", data, this);
|
LOG.debug("Read stream data {} in {}", data, this);
|
||||||
|
@ -77,14 +78,26 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements HTTP2Channel.
|
||||||
stream.demand();
|
stream.demand();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFrame frame = data.frame();
|
DataFrame frame = data.frame();
|
||||||
boolean last = frame.remaining() == 0 && frame.isEndStream();
|
boolean last = frame.remaining() == 0 && frame.isEndStream();
|
||||||
if (!last)
|
if (!last)
|
||||||
return Content.Chunk.asChunk(frame.getByteBuffer(), last, data);
|
return Content.Chunk.asChunk(frame.getByteBuffer(), false, data);
|
||||||
|
|
||||||
data.release();
|
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);
|
responseSuccess(getHttpExchange(), null);
|
||||||
return Content.Chunk.EOF;
|
return Content.Chunk.EOF;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failAndClose(Throwable failure)
|
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.Response;
|
||||||
import org.eclipse.jetty.client.Result;
|
import org.eclipse.jetty.client.Result;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.HttpURI;
|
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.instanceOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -824,6 +826,54 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest
|
||||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
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
|
@Test
|
||||||
@Tag("external")
|
@Tag("external")
|
||||||
public void testExternalServer() throws Exception
|
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.server.NetworkConnector;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
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.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@Tag("flaky") // TODO investigate H3
|
|
||||||
public class HttpClientContinueTest extends AbstractTest
|
public class HttpClientContinueTest extends AbstractTest
|
||||||
{
|
{
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -271,11 +269,18 @@ public class HttpClientContinueTest extends AbstractTest
|
||||||
{
|
{
|
||||||
assertTrue(result.isFailed());
|
assertTrue(result.isFailed());
|
||||||
assertNotNull(result.getRequestFailure());
|
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();
|
byte[] content = getContent();
|
||||||
assertNotNull(content);
|
assertNotNull(content);
|
||||||
assertTrue(content.length > 0);
|
assertTrue(content.length > 0);
|
||||||
assertEquals(error, result.getResponse().getStatus());
|
}
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -830,7 +835,7 @@ public class HttpClientContinueTest extends AbstractTest
|
||||||
startServer(Transport.HTTP, new HttpServlet()
|
startServer(Transport.HTTP, new HttpServlet()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
{
|
{
|
||||||
assertEquals(0, request.getContentLengthLong());
|
assertEquals(0, request.getContentLengthLong());
|
||||||
assertNotNull(request.getHeader(HttpHeader.EXPECT.asString()));
|
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.server.NetworkConnector;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
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.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@Tag("flaky") // TODO investigate H3
|
|
||||||
public class HttpClientContinueTest extends AbstractTest
|
public class HttpClientContinueTest extends AbstractTest
|
||||||
{
|
{
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -272,11 +270,18 @@ public class HttpClientContinueTest extends AbstractTest
|
||||||
{
|
{
|
||||||
assertTrue(result.isFailed());
|
assertTrue(result.isFailed());
|
||||||
assertNotNull(result.getRequestFailure());
|
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();
|
byte[] content = getContent();
|
||||||
assertNotNull(content);
|
assertNotNull(content);
|
||||||
assertTrue(content.length > 0);
|
assertTrue(content.length > 0);
|
||||||
assertEquals(error, result.getResponse().getStatus());
|
}
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -889,7 +894,7 @@ public class HttpClientContinueTest extends AbstractTest
|
||||||
startServer(Transport.HTTP, new HttpServlet()
|
startServer(Transport.HTTP, new HttpServlet()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
{
|
{
|
||||||
assertEquals(0, request.getContentLengthLong());
|
assertEquals(0, request.getContentLengthLong());
|
||||||
assertNotNull(request.getHeader(HttpHeader.EXPECT.asString()));
|
assertNotNull(request.getHeader(HttpHeader.EXPECT.asString()));
|
||||||
|
|
|
@ -196,11 +196,18 @@ public class HttpClientContinueTest extends AbstractTest
|
||||||
{
|
{
|
||||||
assertTrue(result.isFailed());
|
assertTrue(result.isFailed());
|
||||||
assertNotNull(result.getRequestFailure());
|
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();
|
byte[] content = getContent();
|
||||||
assertNotNull(content);
|
assertNotNull(content);
|
||||||
assertTrue(content.length > 0);
|
assertTrue(content.length > 0);
|
||||||
assertEquals(error, result.getResponse().getStatus());
|
}
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue