@Test
  public void testStartAsyncThenClientStreamIdleTimeout() throws Exception {
    CountDownLatch serverLatch = new CountDownLatch(1);
    start(new AsyncOnErrorServlet(serverLatch));
    long idleTimeout = 1000;
    client.setIdleTimeout(10 * idleTimeout);

    Session session = newClient(new Session.Listener.Adapter());
    HttpFields fields = new HttpFields();
    MetaData.Request metaData = newRequest("GET", fields);
    HeadersFrame frame = new HeadersFrame(metaData, null, true);
    FuturePromise<Stream> promise = new FuturePromise<>();
    CountDownLatch clientLatch = new CountDownLatch(1);
    session.newStream(
        frame,
        promise,
        new Stream.Listener.Adapter() {
          @Override
          public boolean onIdleTimeout(Stream stream, Throwable x) {
            clientLatch.countDown();
            return true;
          }
        });
    Stream stream = promise.get(5, TimeUnit.SECONDS);
    stream.setIdleTimeout(idleTimeout);

    // When the client resets, the server receives the
    // corresponding frame and acts by notifying the failure,
    // but the response is not sent back to the client.
    Assert.assertTrue(serverLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
    Assert.assertTrue(clientLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
  }
  @Test
  public void testStartAsyncThenClientSessionIdleTimeout() throws Exception {
    CountDownLatch serverLatch = new CountDownLatch(1);
    start(new AsyncOnErrorServlet(serverLatch));
    long idleTimeout = 1000;
    client.setIdleTimeout(idleTimeout);

    Session session = newClient(new Session.Listener.Adapter());
    HttpFields fields = new HttpFields();
    MetaData.Request metaData = newRequest("GET", fields);
    HeadersFrame frame = new HeadersFrame(metaData, null, true);
    FuturePromise<Stream> promise = new FuturePromise<>();
    CountDownLatch clientLatch = new CountDownLatch(1);
    session.newStream(
        frame,
        promise,
        new Stream.Listener.Adapter() {
          @Override
          public void onHeaders(Stream stream, HeadersFrame frame) {
            MetaData.Response response = (MetaData.Response) frame.getMetaData();
            if (response.getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500 && frame.isEndStream())
              clientLatch.countDown();
          }
        });
    Stream stream = promise.get(5, TimeUnit.SECONDS);
    stream.setIdleTimeout(10 * idleTimeout);

    // When the client closes, the server receives the
    // corresponding frame and acts by notifying the failure,
    // which sends back to the client the error response.
    Assert.assertTrue(serverLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
    Assert.assertTrue(clientLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
  }
  @Test
  public void testSomeContentAvailableAfterServiceReturns() throws Exception {
    final AtomicInteger count = new AtomicInteger();
    start(
        new HttpServlet() {
          @Override
          protected void service(final HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
            final AsyncContext asyncContext = request.startAsync();
            asyncContext.setTimeout(0);
            request
                .getInputStream()
                .setReadListener(
                    new EmptyReadListener() {
                      @Override
                      public void onDataAvailable() throws IOException {
                        count.incrementAndGet();
                        ServletInputStream input = request.getInputStream();
                        while (input.isReady()) {
                          int read = input.read();
                          if (read < 0) break;
                        }
                        if (input.isFinished()) asyncContext.complete();
                      }
                    });
          }
        });

    Session session = newClient(new Session.Listener.Adapter());

    HttpFields fields = new HttpFields();
    MetaData.Request metaData = newRequest("GET", fields);
    HeadersFrame frame = new HeadersFrame(metaData, null, false);
    final CountDownLatch latch = new CountDownLatch(1);
    FuturePromise<Stream> promise = new FuturePromise<>();
    session.newStream(
        frame,
        promise,
        new Stream.Listener.Adapter() {
          @Override
          public void onHeaders(Stream stream, HeadersFrame frame) {
            if (frame.isEndStream()) latch.countDown();
          }
        });
    Stream stream = promise.get(5, TimeUnit.SECONDS);

    // Wait until service() returns.
    Thread.sleep(1000);
    stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), false), Callback.NOOP);

    // Wait until onDataAvailable() returns.
    Thread.sleep(1000);
    stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1), true), Callback.NOOP);

    Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
    // Make sure onDataAvailable() has been called twice
    Assert.assertEquals(2, count.get());
  }
  @Test
  public void testLastContentAvailableBeforeService() throws Exception {
    start(
        new HttpServlet() {
          @Override
          protected void service(final HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
            // Wait for the data to fully arrive.
            sleep(1000);

            final AsyncContext asyncContext = request.startAsync();
            asyncContext.setTimeout(0);
            request
                .getInputStream()
                .setReadListener(
                    new EmptyReadListener() {
                      @Override
                      public void onDataAvailable() throws IOException {
                        ServletInputStream input = request.getInputStream();
                        while (input.isReady()) {
                          int read = input.read();
                          if (read < 0) break;
                        }
                        if (input.isFinished()) asyncContext.complete();
                      }
                    });
          }
        });

    Session session = newClient(new Session.Listener.Adapter());

    HttpFields fields = new HttpFields();
    MetaData.Request metaData = newRequest("GET", fields);
    HeadersFrame frame = new HeadersFrame(metaData, null, false);
    final CountDownLatch latch = new CountDownLatch(1);
    FuturePromise<Stream> promise = new FuturePromise<>();
    session.newStream(
        frame,
        promise,
        new Stream.Listener.Adapter() {
          @Override
          public void onHeaders(Stream stream, HeadersFrame frame) {
            if (frame.isEndStream()) latch.countDown();
          }
        });
    Stream stream = promise.get(5, TimeUnit.SECONDS);
    stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(16), true), Callback.NOOP);

    Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
  }