Skip to content

Connect Timeout - Thread.Interrupt() doesn't cancel call.execute() when we're stuck at the connection state #6611

@om3g4zell

Description

@om3g4zell

Describe the bug

When the server is unreachable and isn't able to answer to the connect request. Using
Thread.interrupt() doesn't cancel the call and only unblock after the connectTimeout
property has passed.

Expected behaviour

I Exptect Thread.interrupt() to have the same behaviour as call.cancel() -> Cancel the call even when stuck at the connecting state.

Client information

OS : Unix
OKHttp Version : 4.9.0
JDK : AdoptOpenJDK 11

Step to reproduce

Make a call that would result in a connectTimeout and try to interrupt it with Thread.interrupt()

Here is a java test (using assertJ)

    @Test
    void interupt_on_connect( ) throws InterruptedException {

        var connectTimeout = Duration.ofMillis( 500 );
        var client = new OkHttpClient.Builder( )
                .connectTimeout( connectTimeout )
                .build( );

        var call = client.newCall( new Request.Builder( )
                .url( new HttpUrl.Builder( )
                        .scheme( "http" )
                        // Host that don't listen packets nor reject them
                        .host( "10.255.255.1" )
                        .build( ) )
                .get( )
                .build( ) );

        var thread = new Thread( ( ) -> {
            try (var resp = call.execute( )) {
                fail( "Shouldn't succeed" );
            } catch (IOException e) {
                // NO-OP
            }
        } );
        thread.start( );
        // Test will fail using Thread.interrupt
        thread.interrupt( );

        // Test will succeed using call.cancel()
        //call.cancel();
        thread.join( connectTimeout.toMillis( ) );
        assertThat(thread.isAlive( ) ).isFalse();
    }

The test is passing using call.cancel() and failing using Thread.interrupt()

Here is a unit test that can be placed in CancelTest.kt

  @ParameterizedTest
  @ArgumentsSource(CancelModelParamProvider::class)
  fun cancelConnecting(mode: Pair<CancelMode, ConnectionType>) {
    val connectTimeout = Duration.ofMillis(500);

    setUp(mode)
    val client = client.newBuilder()
      .connectTimeout(connectTimeout)
      .build()
    val call = client.newCall(
      Request.Builder()
        .url(HttpUrl.Builder()
          .scheme(connectionType.name)
          // // Host that don't listen packets nor reject them
          .host("10.255.255.1")
          .build())
        .get()
        .build()
    )
    cancelLater(call, 100)
    try {
      call.execute()
      fail("")
    } catch (expected: IOException) {
      expected.printStackTrace()
      assertEquals(cancelMode == INTERRUPT, Thread.interrupted())

      // If connect timeout exception happens it means that the call hasn't been canceled
      assertThat(expected).isNotInstanceOf(SocketTimeoutException::class.java)
    }
  }

Test using the H2 ConnectionType will fail as it's not a valid shcheme.

Tests using call.cancel() will succeed.
Tests using Thread.interrupt() will fail after the connectTimeout delay.
The Thrown exception is

java.net.SocketTimeoutException: connect timed out

instead of

java.net.SocketException: Socket closed

with the call.cancel() method

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug in existing code

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions