use of org.eclipse.jetty.util.Callback in project jetty.project by eclipse.
the class StreamResetTest method testAsyncWriteAfterStreamReceivingReset.
@Test
public void testAsyncWriteAfterStreamReceivingReset() throws Exception {
final CountDownLatch resetLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
start(new HttpServlet() {
@Override
protected void doGet(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
Charset charset = StandardCharsets.UTF_8;
final ByteBuffer data = ByteBuffer.wrap("AFTER RESET".getBytes(charset));
response.setStatus(200);
response.setContentType("text/plain;charset=" + charset.name());
response.setContentLength(data.remaining());
response.flushBuffer();
try {
// Wait for the reset to happen.
Assert.assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
// Wait for the reset to arrive to the server and be processed.
Thread.sleep(1000);
} catch (InterruptedException x) {
throw new InterruptedIOException();
}
// Write some content asynchronously after the stream has been reset.
final AsyncContext context = request.startAsync();
new Thread() {
@Override
public void run() {
try {
// Wait for the request thread to exit
// doGet() so this is really asynchronous.
Thread.sleep(1000);
HttpOutput output = (HttpOutput) response.getOutputStream();
output.sendContent(data, new Callback() {
@Override
public void failed(Throwable x) {
context.complete();
dataLatch.countDown();
}
});
} catch (Throwable x) {
x.printStackTrace();
}
}
}.start();
}
});
Session client = newClient(new Session.Listener.Adapter());
MetaData.Request request = newRequest("GET", new HttpFields());
HeadersFrame frame = new HeadersFrame(request, null, true);
client.newStream(frame, new FuturePromise<>(), new Stream.Listener.Adapter() {
@Override
public void onHeaders(Stream stream, HeadersFrame frame) {
stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
resetLatch.countDown();
}
});
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
use of org.eclipse.jetty.util.Callback in project jetty.project by eclipse.
the class StreamResetTest method testResetAfterAsyncRequestAsyncWriteStalledByFlowControl.
@Test
public void testResetAfterAsyncRequestAsyncWriteStalledByFlowControl() throws Exception {
int windowSize = FlowControlStrategy.DEFAULT_WINDOW_SIZE;
CountDownLatch writeLatch = new CountDownLatch(1);
start(new HttpServlet() {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
ServletOutputStream output = response.getOutputStream();
output.setWriteListener(new WriteListener() {
private boolean written;
@Override
public void onWritePossible() throws IOException {
while (output.isReady()) {
if (written) {
asyncContext.complete();
break;
} else {
output.write(new byte[10 * windowSize]);
written = true;
}
}
}
@Override
public void onError(Throwable t) {
writeLatch.countDown();
}
});
}
});
Deque<Callback> dataQueue = new ArrayDeque<>();
AtomicLong received = new AtomicLong();
CountDownLatch latch = new CountDownLatch(1);
Session client = newClient(new Session.Listener.Adapter());
MetaData.Request request = newRequest("GET", new HttpFields());
HeadersFrame frame = new HeadersFrame(request, null, true);
FuturePromise<Stream> promise = new FuturePromise<>();
client.newStream(frame, promise, new Stream.Listener.Adapter() {
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
dataQueue.offer(callback);
// Do not consume the data yet.
if (received.addAndGet(frame.getData().remaining()) == windowSize)
latch.countDown();
}
});
Stream stream = promise.get(5, TimeUnit.SECONDS);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
// Reset and consume.
stream.reset(new ResetFrame(stream.getId(), ErrorCode.CANCEL_STREAM_ERROR.code), Callback.NOOP);
dataQueue.forEach(Callback::succeeded);
Assert.assertTrue(writeLatch.await(5, TimeUnit.SECONDS));
}
use of org.eclipse.jetty.util.Callback in project jetty.project by eclipse.
the class TrailersTest method testServletRequestTrailers.
@Test
public void testServletRequestTrailers() throws Exception {
CountDownLatch trailerLatch = new CountDownLatch(1);
start(new HttpServlet() {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Request jettyRequest = (Request) request;
// No trailers yet.
Assert.assertNull(jettyRequest.getTrailers());
trailerLatch.countDown();
// Read the content.
ServletInputStream input = jettyRequest.getInputStream();
while (true) {
int read = input.read();
if (read < 0)
break;
}
// Now we have the trailers.
HttpFields trailers = jettyRequest.getTrailers();
Assert.assertNotNull(trailers);
Assert.assertNotNull(trailers.get("X-Trailer"));
}
});
Session session = newClient(new Session.Listener.Adapter());
HttpFields requestFields = new HttpFields();
requestFields.put("X-Request", "true");
MetaData.Request request = newRequest("GET", requestFields);
HeadersFrame requestFrame = new HeadersFrame(request, null, false);
FuturePromise<Stream> streamPromise = new FuturePromise<>();
CountDownLatch latch = new CountDownLatch(1);
session.newStream(requestFrame, streamPromise, new Stream.Listener.Adapter() {
@Override
public void onHeaders(Stream stream, HeadersFrame frame) {
MetaData.Response response = (MetaData.Response) frame.getMetaData();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
if (frame.isEndStream())
latch.countDown();
}
});
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
// Send some data.
Callback.Completable callback = new Callback.Completable();
stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(16), false), callback);
Assert.assertTrue(trailerLatch.await(5, TimeUnit.SECONDS));
// Send the trailers.
callback.thenRun(() -> {
HttpFields trailerFields = new HttpFields();
trailerFields.put("X-Trailer", "true");
MetaData trailers = new MetaData(HttpVersion.HTTP_2, trailerFields);
HeadersFrame trailerFrame = new HeadersFrame(stream.getId(), trailers, null, true);
stream.headers(trailerFrame, Callback.NOOP);
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
use of org.eclipse.jetty.util.Callback in project jetty.project by eclipse.
the class RawHTTP2ProxyTest method testRawHTTP2Proxy.
@Test
public void testRawHTTP2Proxy() throws Exception {
byte[] data1 = new byte[1024];
new Random().nextBytes(data1);
ByteBuffer buffer1 = ByteBuffer.wrap(data1);
Server server1 = startServer("server1", new ServerSessionListener.Adapter() {
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("SERVER1 received {}", frame);
return new Stream.Listener.Adapter() {
@Override
public void onHeaders(Stream stream, HeadersFrame frame) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("SERVER1 received {}", frame);
if (frame.isEndStream()) {
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
HeadersFrame reply = new HeadersFrame(stream.getId(), response, null, false);
if (LOGGER.isDebugEnabled())
LOGGER.debug("SERVER1 sending {}", reply);
stream.headers(reply, new Callback() {
@Override
public void succeeded() {
DataFrame data = new DataFrame(stream.getId(), buffer1.slice(), true);
if (LOGGER.isDebugEnabled())
LOGGER.debug("SERVER1 sending {}", data);
stream.data(data, NOOP);
}
});
}
}
};
}
});
ServerConnector connector1 = (ServerConnector) server1.getAttribute("connector");
Server server2 = startServer("server2", new ServerSessionListener.Adapter() {
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("SERVER2 received {}", frame);
return new Stream.Listener.Adapter() {
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("SERVER2 received {}", frame);
callback.succeeded();
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
Callback.Completable completable1 = new Callback.Completable();
HeadersFrame reply = new HeadersFrame(stream.getId(), response, null, false);
if (LOGGER.isDebugEnabled())
LOGGER.debug("SERVER2 sending {}", reply);
stream.headers(reply, completable1);
completable1.thenCompose(ignored -> {
Callback.Completable completable2 = new Callback.Completable();
DataFrame data = new DataFrame(stream.getId(), buffer1.slice(), false);
if (LOGGER.isDebugEnabled())
LOGGER.debug("SERVER2 sending {}", data);
stream.data(data, completable2);
return completable2;
}).thenRun(() -> {
MetaData trailer = new MetaData(HttpVersion.HTTP_2, new HttpFields());
HeadersFrame end = new HeadersFrame(stream.getId(), trailer, null, true);
if (LOGGER.isDebugEnabled())
LOGGER.debug("SERVER2 sending {}", end);
stream.headers(end, Callback.NOOP);
});
}
};
}
});
ServerConnector connector2 = (ServerConnector) server2.getAttribute("connector");
HTTP2Client proxyClient = startClient("proxyClient");
Server proxyServer = startServer("proxyServer", new ClientToProxySessionListener(proxyClient));
ServerConnector proxyConnector = (ServerConnector) proxyServer.getAttribute("connector");
InetSocketAddress proxyAddress = new InetSocketAddress("localhost", proxyConnector.getLocalPort());
HTTP2Client client = startClient("client");
FuturePromise<Session> clientPromise = new FuturePromise<>();
client.connect(proxyAddress, new Session.Listener.Adapter(), clientPromise);
Session clientSession = clientPromise.get(5, TimeUnit.SECONDS);
// Send a request with trailers for server1.
HttpFields fields1 = new HttpFields();
fields1.put("X-Target", String.valueOf(connector1.getLocalPort()));
MetaData.Request request1 = new MetaData.Request("GET", new HttpURI("http://localhost/server1"), HttpVersion.HTTP_2, fields1);
FuturePromise<Stream> streamPromise1 = new FuturePromise<>();
CountDownLatch latch1 = new CountDownLatch(1);
clientSession.newStream(new HeadersFrame(request1, null, false), streamPromise1, new Stream.Listener.Adapter() {
@Override
public void onHeaders(Stream stream, HeadersFrame frame) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("CLIENT received {}", frame);
}
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("CLIENT received {}", frame);
Assert.assertEquals(buffer1.slice(), frame.getData());
callback.succeeded();
latch1.countDown();
}
});
Stream stream1 = streamPromise1.get(5, TimeUnit.SECONDS);
stream1.headers(new HeadersFrame(stream1.getId(), new MetaData(HttpVersion.HTTP_2, new HttpFields()), null, true), Callback.NOOP);
// Send a request for server2.
HttpFields fields2 = new HttpFields();
fields2.put("X-Target", String.valueOf(connector2.getLocalPort()));
MetaData.Request request2 = new MetaData.Request("GET", new HttpURI("http://localhost/server1"), HttpVersion.HTTP_2, fields2);
FuturePromise<Stream> streamPromise2 = new FuturePromise<>();
CountDownLatch latch2 = new CountDownLatch(1);
clientSession.newStream(new HeadersFrame(request2, null, false), streamPromise2, new Stream.Listener.Adapter() {
@Override
public void onHeaders(Stream stream, HeadersFrame frame) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("CLIENT received {}", frame);
if (frame.isEndStream())
latch2.countDown();
}
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("CLIENT received {}", frame);
callback.succeeded();
}
});
Stream stream2 = streamPromise2.get(5, TimeUnit.SECONDS);
stream2.data(new DataFrame(stream2.getId(), buffer1.slice(), true), Callback.NOOP);
Assert.assertTrue(latch1.await(5, TimeUnit.SECONDS));
Assert.assertTrue(latch2.await(5, TimeUnit.SECONDS));
}
use of org.eclipse.jetty.util.Callback in project jetty.project by eclipse.
the class SessionFailureTest method testWriteFailure.
@Test
public void testWriteFailure() throws Exception {
final CountDownLatch writeLatch = new CountDownLatch(1);
final CountDownLatch serverFailureLatch = new CountDownLatch(1);
start(new ServerSessionListener.Adapter() {
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame) {
// Forcibly close the connection.
((HTTP2Session) stream.getSession()).getEndPoint().close();
// Now try to write something: it should fail.
stream.headers(frame, new Callback() {
@Override
public void failed(Throwable x) {
writeLatch.countDown();
}
});
return null;
}
@Override
public void onFailure(Session session, Throwable failure) {
serverFailureLatch.countDown();
}
});
final CountDownLatch clientFailureLatch = new CountDownLatch(1);
Session session = newClient(new Session.Listener.Adapter() {
@Override
public void onFailure(Session session, Throwable failure) {
clientFailureLatch.countDown();
}
});
HeadersFrame frame = new HeadersFrame(newRequest("GET", new HttpFields()), null, true);
Promise<Stream> promise = new Promise.Adapter<>();
session.newStream(frame, promise, null);
Assert.assertTrue(writeLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(clientFailureLatch.await(5, TimeUnit.SECONDS));
long start = System.nanoTime();
long now = System.nanoTime();
while (((HTTP2Session) session).getEndPoint().isOpen()) {
if (TimeUnit.NANOSECONDS.toSeconds(now - start) > 5)
Assert.fail();
Thread.sleep(10);
now = System.nanoTime();
}
}
Aggregations