[BUG] azure-core-http-netty ignores response timeout when write timeout is disabled

クローズ
#50,795 コメント 3 件 リアクション 1 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@alzimmermsft がすでに取り組んでいます。

2026年10月14日 から。

  • #50821 @alzimmermsft による — オープン

評価

この issue はまだ評価されていません。

説明

Azure.Core Client customer-reported needs-team-attention question

Describe the bug

With azure-core-http-netty 1.16.5, setting writeTimeout(Duration.ZERO) also prevents the configured response timeout from firing.

I reproduced this with a WireMock endpoint that matches the request body and delays its response by one second. With a 100 ms response timeout and write timeout disabled, the request succeeds after about one second. Setting a positive write timeout makes the same request time out after about 100 ms.

Disabling the write timeout should only disable the write timeout, not the response timeout.

Exception or Stack Trace

No SDK exception is thrown in the disabled-write case. The reproducing test fails with this assertion:

org.opentest4j.AssertionFailedError: Expected java.lang.RuntimeException to be thrown, but nothing was thrown.
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:152)
	at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:73)
	at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:35)
	at org.junit.jupiter.api.Assertions.assertThrows(Assertions.java:3128)
	at AzureNettyResponseTimeoutWireMockTest.responseTimeoutShouldNotDependOnWriteTimeout(AzureNettyResponseTimeoutWireMockTest.java:53)

To Reproduce

  1. In a Java test project using azure-core-http-netty:1.16.5 and JUnit Jupiter, add the WireMock test dependency below if it is not already available.
  2. Copy the test class below into src/test/java/AzureNettyResponseTimeoutWireMockTest.java.
  3. Run mvn -Dtest=AzureNettyResponseTimeoutWireMockTest test (from the HTTP Netty module if using the SDK repository).
  4. Observe that the writeTimeoutMillis=0 case fails because no exception is thrown; the writeTimeoutMillis=5000 control case passes with the expected SDK response-timeout exception. No Azure credentials or service are required.

Verified output:

Tests run: 2, Failures: 1, Errors: 0, Skipped: 0

Maven exits with code 1 because the disabled-write case reproduces the bug.

Code Snippet

WireMock test dependency, if needed. The standalone artifact avoids changing the project's Jetty dependencies:

<dependency>
  <groupId>org.wiremock</groupId>
  <artifactId>wiremock-standalone</artifactId>
  <version>3.13.1</version>
  <scope>test</scope>
</dependency>
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpMethod;
import com.azure.core.http.HttpRequest;
import com.azure.core.http.HttpResponse;
import com.azure.core.http.netty.NettyAsyncHttpClientBuilder;
import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.time.Duration;
import java.util.concurrent.TimeoutException;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class AzureNettyResponseTimeoutWireMockTest {
    private WireMockServer wireMock;

    @BeforeEach
    void setUp() {
        wireMock = new WireMockServer(wireMockConfig().bindAddress("127.0.0.1").dynamicPort());
        wireMock.start();
        wireMock.stubFor(post(urlEqualTo("/timeout"))
            .withRequestBody(equalTo("payload"))
            .willReturn(aResponse().withStatus(200).withFixedDelay(1_000)));
    }

    @AfterEach
    void tearDown() {
        if (wireMock != null) wireMock.stop();
    }

    @ParameterizedTest(name = "writeTimeoutMillis={0}")
    @ValueSource(longs = {0, 5_000})
    @Timeout(10)
    void responseTimeoutShouldNotDependOnWriteTimeout(long writeTimeoutMillis) {
        HttpClient client = new NettyAsyncHttpClientBuilder()
            .writeTimeout(Duration.ofMillis(writeTimeoutMillis))
            .responseTimeout(Duration.ofMillis(100))
            .build();
        HttpRequest request = new HttpRequest(HttpMethod.POST, "http://127.0.0.1:" + wireMock.port() + "/timeout")
            .setBody("payload");

        RuntimeException failure = assertThrows(RuntimeException.class, () -> {
            try (HttpResponse ignored = client.send(request).block(Duration.ofSeconds(3))) {
                // Returning normally means the configured response timeout did not fire.
            }
        });
        Throwable root = failure;
        while (root.getCause() != null) root = root.getCause();
        assertInstanceOf(TimeoutException.class, root);
        assertTrue(root.getMessage().contains("Channel response timed out"));
    }
}

Only writeTimeout and responseTimeout are configured on the SDK client; all other client settings retain their defaults. The three-second blocking timeout is just a test safety bound, and the assertion specifically checks the SDK's response-timeout exception.

Expected behavior

Both parameterized cases should pass: with a 100 ms response timeout and a one-second server response delay, the SDK should throw a response-timeout exception whether the write timeout is enabled or disabled.

Screenshots

Not applicable.

Setup (please complete the following information):

  • OS: macOS 26.5
  • IDE: Not applicable; reproduced from the terminal using Maven.
  • Library/Libraries:
    • com.azure:azure-core-http-netty:1.16.5
    • com.azure:azure-core:1.58.1 (SDK transitive dependency)
    • io.projectreactor.netty:reactor-netty-http:1.2.18 (SDK transitive dependency)
    • org.junit.jupiter:junit-jupiter:5.13.4 (test dependency)
    • org.wiremock:wiremock-standalone:3.13.1 (test dependency)
  • Java version: Temurin OpenJDK 26.0.1+8. The reproduction compiles with Java release 17.
  • App Server/Environment: Standalone Maven/JUnit test with a local HTTP server on 127.0.0.1; no application server or Azure service.
  • Frameworks: JUnit Jupiter and WireMock. No Spring Boot or other application framework.

The standalone reproduction uses the SDK's normal transitive dependencies. No NoClassDefFoundError or NoSuchMethodError occurs in this reproduction; a dependency version mismatch is not suspected.

Additional context

The suspected cause is in Azure SDK's own AzureSdkHandler, rather than Netty or Reactor Netty:

sdk/core/azure-core-http-netty/src/main/java/com/azure/core/http/netty/implementation/AzureSdkHandler.java

Source, lines 129-138: the write-completion listener is installed only when writeTimeoutMillis > 0 at line 129. That listener also calls startResponseTracking() at line 134. When write timeout is disabled, the branch at line 138 writes without installing this listener.

Information Checklist

  • Bug Description Added
  • Repro Steps Added
  • Setup information Added
主要言語
Java
スター
2.6k
フォーク
2.2k
平均マージ
2日 1時間
マージ済み PR(30日)
229

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Azure/azure-sdk-for-java のほかの issue

Azure/azure-sdk-for-java の issue をすべて見る

似ている issue

Java の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。