@ParameterizedTest
    @EnumSource(value = SessionProtocol.class, names = {"H1C", "H2C"})
    void confirmResponseStartAndEndInTheSameThread(SessionProtocol protocol)
            throws InterruptedException {
        final AtomicBoolean failed = new AtomicBoolean();
        final RetryRule strategy = (ctx, cause) ->
                UnmodifiableFuture.completedFuture(RetryDecision.retry(Backoff.withoutDelay()));

        final WebClientBuilder builder = WebClient.builder(server.uri(protocol));
        // In order to use a different thread to subscribe to the response.
        builder.decorator(RetryingClient.builder(strategy)
                                        .maxTotalAttempts(2)
                                        .newDecorator());
        builder.decorator((delegate, ctx, req) -> {
            final AtomicReference<Thread> responseStartedThread = new AtomicReference<>();
            ctx.log().whenAvailable(RequestLogProperty.RESPONSE_START_TIME).thenAccept(log -> {
                responseStartedThread.set(Thread.currentThread());
            });
            ctx.log().whenComplete().thenAccept(log -> {
                final Thread thread = responseStartedThread.get();
                if (thread != null && thread != Thread.currentThread()) {
                    logger.error("{} Response ended in another thread: {} != {}",
                                 ctx, thread, Thread.currentThread(), new RuntimeException());
                    failed.set(true);
                }
            });
            return delegate.execute(ctx, req);
        });

        // Execute it as much as we can in order to confirm that there's no problem.
        final WebClient client = builder.build();
        final int n = 1000;
        final CountDownLatch latch = new CountDownLatch(n);
        for (int i = 0; i < n; i++) {
            client.execute(HttpRequest.of(HttpMethod.GET, "/")).aggregate()
                  .handle((unused1, unused2) -> {
                      latch.countDown();
                      return null;
                  });
        }

        latch.await(System.getenv("CI") != null ? 60 : 10, TimeUnit.SECONDS);
        assertThat(failed.get()).isFalse();
    }
This test would be passed because the {@code cancelAction} method of the
{@link HttpResponseWrapper} is invoked in the event loop of the {@link Channel}.
core/src/test/java/com/linecorp/armeria/client/HttpResponseDecoderTest.java