use of org.eclipse.jetty.http2.frames.HeadersFrame in project jetty.project by eclipse.
the class FlowControlStrategyTest method testSessionStalledStallsNewStreams.
@Test
public void testSessionStalledStallsNewStreams() throws Exception {
final int windowSize = 1024;
start(new ServerSessionListener.Adapter() {
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame requestFrame) {
MetaData.Request request = (MetaData.Request) requestFrame.getMetaData();
if ("POST".equalsIgnoreCase(request.getMethod())) {
// Send data to consume most of the session window.
ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE - windowSize);
DataFrame dataFrame = new DataFrame(stream.getId(), data, true);
stream.data(dataFrame, Callback.NOOP);
return null;
} else {
// For every stream, send down half the window size of data.
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
Callback.Completable completable = new Callback.Completable();
stream.headers(responseFrame, completable);
completable.thenRun(() -> {
DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.allocate(windowSize / 2), true);
stream.data(dataFrame, Callback.NOOP);
});
return null;
}
}
});
Session session = newClient(new Session.Listener.Adapter());
// First request is just to consume most of the session window.
final List<Callback> callbacks1 = new ArrayList<>();
final CountDownLatch prepareLatch = new CountDownLatch(1);
MetaData.Request request1 = newRequest("POST", new HttpFields());
session.newStream(new HeadersFrame(request1, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter() {
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
// Do not consume the data to reduce the session window.
callbacks1.add(callback);
if (frame.isEndStream())
prepareLatch.countDown();
}
});
Assert.assertTrue(prepareLatch.await(5, TimeUnit.SECONDS));
// Second request will consume half of the remaining the session window.
MetaData.Request request2 = newRequest("GET", new HttpFields());
session.newStream(new HeadersFrame(request2, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter() {
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
// Do not consume it to stall flow control.
}
});
// Third request will consume the whole session window, which is now stalled.
// A fourth request will not be able to receive data.
MetaData.Request request3 = newRequest("GET", new HttpFields());
session.newStream(new HeadersFrame(request3, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter() {
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
// Do not consume it to stall flow control.
}
});
// Fourth request is now stalled.
final CountDownLatch latch = new CountDownLatch(1);
MetaData.Request request4 = newRequest("GET", new HttpFields());
session.newStream(new HeadersFrame(request4, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter() {
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
callback.succeeded();
if (frame.isEndStream())
latch.countDown();
}
});
// Verify that the data does not arrive because the server session is stalled.
Assert.assertFalse(latch.await(1, TimeUnit.SECONDS));
// This will open up the session window, allowing the fourth stream to send data.
for (Callback callback : callbacks1) callback.succeeded();
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
use of org.eclipse.jetty.http2.frames.HeadersFrame in project jetty.project by eclipse.
the class FlowControlStrategyTest method testClientSendingInitialSmallWindow.
@Test
public void testClientSendingInitialSmallWindow() throws Exception {
start(new ServerSessionListener.Adapter() {
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame) {
MetaData metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, new HttpFields());
HeadersFrame responseFrame = new HeadersFrame(stream.getId(), metaData, null, false);
Callback.Completable completable = new Callback.Completable();
stream.headers(responseFrame, completable);
return new Stream.Listener.Adapter() {
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
// Since we echo back the data
// asynchronously we must copy it.
ByteBuffer data = frame.getData();
ByteBuffer copy = ByteBuffer.allocateDirect(data.remaining());
copy.put(data).flip();
completable.thenRun(() -> stream.data(new DataFrame(stream.getId(), copy, frame.isEndStream()), callback));
}
};
}
});
final int initialWindow = 16;
Session session = newClient(new Session.Listener.Adapter() {
@Override
public Map<Integer, Integer> onPreface(Session session) {
Map<Integer, Integer> settings = new HashMap<>();
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, initialWindow);
return settings;
}
});
byte[] requestData = new byte[initialWindow * 4];
new Random().nextBytes(requestData);
byte[] responseData = new byte[requestData.length];
final ByteBuffer responseContent = ByteBuffer.wrap(responseData);
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
Promise.Completable<Stream> completable = new Promise.Completable<>();
final CountDownLatch latch = new CountDownLatch(1);
session.newStream(requestFrame, completable, new Stream.Listener.Adapter() {
@Override
public void onData(Stream stream, DataFrame frame, Callback callback) {
responseContent.put(frame.getData());
callback.succeeded();
if (frame.isEndStream())
latch.countDown();
}
});
completable.thenAccept(stream -> {
ByteBuffer requestContent = ByteBuffer.wrap(requestData);
DataFrame dataFrame = new DataFrame(stream.getId(), requestContent, true);
stream.data(dataFrame, Callback.NOOP);
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
responseContent.flip();
Assert.assertArrayEquals(requestData, responseData);
}
use of org.eclipse.jetty.http2.frames.HeadersFrame in project jetty.project by eclipse.
the class FlowControlStrategyTest method testClientExceedingStreamWindow.
@Test
public void testClientExceedingStreamWindow() throws Exception {
// On server, we don't consume the data.
start(new ServerSessionListener.Adapter() {
@Override
public Map<Integer, Integer> onPreface(Session session) {
// Enlarge the session window.
((ISession) session).updateRecvWindow(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
return super.onPreface(session);
}
});
final CountDownLatch closeLatch = new CountDownLatch(1);
Session session = newClient(new Session.Listener.Adapter() {
@Override
public void onClose(Session session, GoAwayFrame frame) {
if (frame.getError() == ErrorCode.FLOW_CONTROL_ERROR.code)
closeLatch.countDown();
}
});
// Consume the whole stream window.
MetaData.Request metaData = newRequest("POST", new HttpFields());
HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
FuturePromise<Stream> streamPromise = new FuturePromise<>();
session.newStream(requestFrame, streamPromise, new Stream.Listener.Adapter());
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
final CountDownLatch dataLatch = new CountDownLatch(1);
stream.data(new DataFrame(stream.getId(), data, false), new Callback() {
@Override
public InvocationType getInvocationType() {
return InvocationType.NON_BLOCKING;
}
@Override
public void succeeded() {
dataLatch.countDown();
}
});
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
// Wait for a while before doing the "sneaky" write
// below, see comments in the previous test case.
Thread.sleep(1000);
// Now the client is supposed to not send more frames.
// If it does, the connection must be closed.
HTTP2Session http2Session = (HTTP2Session) session;
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(connector.getByteBufferPool());
ByteBuffer extraData = ByteBuffer.allocate(1024);
http2Session.getGenerator().data(lease, new DataFrame(stream.getId(), extraData, true), extraData.remaining());
List<ByteBuffer> buffers = lease.getByteBuffers();
http2Session.getEndPoint().write(Callback.NOOP, buffers.toArray(new ByteBuffer[buffers.size()]));
// Expect the connection to be closed.
Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
}
use of org.eclipse.jetty.http2.frames.HeadersFrame in project jetty.project by eclipse.
the class HTTP2Test method testCleanGoAwayDoesNotTriggerFailureNotification.
@Test
public void testCleanGoAwayDoesNotTriggerFailureNotification() throws Exception {
start(new ServerSessionListener.Adapter() {
@Override
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame) {
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
HeadersFrame response = new HeadersFrame(stream.getId(), metaData, null, true);
stream.headers(response, Callback.NOOP);
// Close cleanly.
stream.getSession().close(ErrorCode.NO_ERROR.code, null, Callback.NOOP);
return null;
}
});
CountDownLatch closeLatch = new CountDownLatch(1);
CountDownLatch failureLatch = new CountDownLatch(1);
Session session = newClient(new Session.Listener.Adapter() {
@Override
public void onClose(Session session, GoAwayFrame frame) {
closeLatch.countDown();
}
@Override
public void onFailure(Session session, Throwable failure) {
failureLatch.countDown();
}
});
MetaData.Request metaData = newRequest("GET", new HttpFields());
HeadersFrame request = new HeadersFrame(metaData, null, true);
session.newStream(request, new Promise.Adapter<>(), new Stream.Listener.Adapter());
// Make sure onClose() is called.
Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
Assert.assertFalse(failureLatch.await(1, TimeUnit.SECONDS));
}
use of org.eclipse.jetty.http2.frames.HeadersFrame in project jetty.project by eclipse.
the class HTTP2Test method testCustomResponseCode.
@Test
public void testCustomResponseCode() throws Exception {
final int status = 475;
start(new HttpServlet() {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(status);
}
});
Session session = newClient(new Session.Listener.Adapter());
HttpFields fields = new HttpFields();
MetaData.Request metaData = newRequest("GET", fields);
HeadersFrame frame = new HeadersFrame(metaData, null, true);
final CountDownLatch latch = new CountDownLatch(1);
session.newStream(frame, new Promise.Adapter<>(), new Stream.Listener.Adapter() {
@Override
public void onHeaders(Stream stream, HeadersFrame frame) {
MetaData.Response response = (MetaData.Response) frame.getMetaData();
Assert.assertEquals(status, response.getStatus());
if (frame.isEndStream())
latch.countDown();
}
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
Aggregations