Repository: square/okhttp
File: okhttp/src/test/java/okhttp3/internal/http2/HttpOverHttp2Test.java (Java)

Code before:
    BlockingQueue<String> bodies = new LinkedBlockingQueue<>();
    Callback callback = new Callback() {
      @Override public void onResponse(Call call, Response response) throws IOException {
        bodies.add(response.body().string());
      }

      @Override public void onFailure(Call call, IOException e) {
        System.out.println(e);
      }
    };
    client.newCall(new Request.Builder().url(server.url("/")).build()).enqueue(callback);
    client.newCall(new Request.Builder().url(server.url("/")).build()).enqueue(callback);

    assertThat(bodies.poll(3, SECONDS)).isEqualTo("DEF");
    assertThat(bodies.poll(3, SECONDS)).isEqualTo("ABC");
    assertThat(server.getRequestCount()).isEqualTo(2);
  }

  /**
   * We don't know if the connection will support HTTP/2 until after we've connected. When multiple
   * connections are requested concurrently OkHttp will pessimistically connect multiple times, then
   * close any unnecessary connections. This test confirms that behavior works as intended.
   *
   * <p>This test uses proxy tunnels to get a hook while a connection is being established.
   */
  @ParameterizedTest
  @ArgumentsSource(ProtocolParamProvider.class)
  public void concurrentHttp2ConnectionsDeduplicated(
      Protocol protocol, MockWebServer mockWebServer) throws Exception {
    setUp(protocol, mockWebServer);
    assumeTrue(protocol == Protocol.HTTP_2);

    server.useHttps(handshakeCertificates.sslSocketFactory(), true);

    QueueDispatcher queueDispatcher = new QueueDispatcher();
    queueDispatcher.enqueueResponse(new MockResponse()
        .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
        .clearHeaders());
    queueDispatcher.enqueueResponse(new MockResponse()
        .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
        .clearHeaders());
    queueDispatcher.enqueueResponse(new MockResponse()
        .setBody("call2 response"));
    queueDispatcher.enqueueResponse(new MockResponse()
        .setBody("call1 response"));

    // We use a re-entrant dispatcher to initiate one HTTPS connection while the other is in flight.
    server.setDispatcher(new Dispatcher() {
      int requestCount;

      @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException {

Review comment (suggestion):
I don't know the root cause for the flakiness in this case, but I suggest that you look at a few techniques I used in the past to make similar tests deterministic and that may apply here: https://github.com/square/okhttp/pull/4792, https://github.com/square/okhttp/pull/4728, https://github.com/square/okhttp/pull/4772.

Code after:
      .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END)
      .setBody("DEF"));

    CountDownLatch latch = new CountDownLatch(2);
    ArrayList<IOException> errors = new ArrayList<>();

    BlockingQueue<String> bodies = new LinkedBlockingQueue<>();
    Callback callback = new Callback() {
      @Override public void onResponse(Call call, Response response) throws IOException {
        bodies.add(response.body().string());
        latch.countDown();
      }

      @Override public void onFailure(Call call, IOException e) {
        errors.add(e);
        latch.countDown();
      }
    };
    client.newCall(new Request.Builder().url(server.url("/")).build()).enqueue(callback);
    client.newCall(new Request.Builder().url(server.url("/")).build()).enqueue(callback);

    latch.await();

    assertThat(bodies.remove()).isEqualTo("DEF");

    if (errors.isEmpty()) {
      assertThat(bodies.remove()).isEqualTo("ABC");
      assertThat(server.getRequestCount()).isEqualTo(2);
    } else {
      // https://github.com/square/okhttp/issues/4836
      // As documented in SocketPolicy, this is known to be flaky.
      IOException error = errors.get(0);
      if (!(error instanceof StreamResetException)) {
        throw error;
      }
    }
  }

  /**
   * We don't know if the connection will support HTTP/2 until after we've connected. When multiple
   * connections are requested concurrently OkHttp will pessimistically connect multiple times, then
   * close any unnecessary connections. This test confirms that behavior works as intended.
   *
   * <p>This test uses proxy tunnels to get a hook while a connection is being established.
   */
  @ParameterizedTest
  @ArgumentsSource(ProtocolParamProvider.class)
  public void concurrentHttp2ConnectionsDeduplicated(
      Protocol protocol, MockWebServer mockWebServer) throws Exception {
    setUp(protocol, mockWebServer);
    assumeTrue(protocol == Protocol.HTTP_2);