Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow configuring web socket close timeout #8189

Merged
merged 1 commit into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@ class MockWebServer : Closeable {
extensions = WebSocketExtensions.parse(webSocketResponse.headers),
// Compress all messages if compression is enabled.
minimumDeflateSize = 0L,
closeTimeoutMillis = RealWebSocket.CANCEL_AFTER_CLOSE_MILLIS,
)
val name = "MockWebServer WebSocket ${request.path!!}"
webSocket.initReaderAndWriter(name, streams)
Expand Down
4 changes: 4 additions & 0 deletions okhttp/api/okhttp.api
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,7 @@ public class okhttp3/OkHttpClient : okhttp3/Call$Factory, okhttp3/WebSocket$Fact
public final fun callTimeoutMillis ()I
public final fun certificateChainCleaner ()Lokhttp3/internal/tls/CertificateChainCleaner;
public final fun certificatePinner ()Lokhttp3/CertificatePinner;
public final fun closeTimeoutMillis ()I
public final fun connectTimeoutMillis ()I
public final fun connectionPool ()Lokhttp3/ConnectionPool;
public final fun connectionSpecs ()Ljava/util/List;
Expand Down Expand Up @@ -961,6 +962,9 @@ public final class okhttp3/OkHttpClient$Builder {
public final fun callTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;
public final fun callTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;
public final fun certificatePinner (Lokhttp3/CertificatePinner;)Lokhttp3/OkHttpClient$Builder;
public final fun closeTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;
public final fun closeTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;
public final fun closeTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;
public final fun connectTimeout (JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder;
public final fun connectTimeout (Ljava/time/Duration;)Lokhttp3/OkHttpClient$Builder;
public final fun connectTimeout-LRDsOJo (J)Lokhttp3/OkHttpClient$Builder;
Expand Down
50 changes: 50 additions & 0 deletions okhttp/src/main/kotlin/okhttp3/OkHttpClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ open class OkHttpClient internal constructor(
@get:JvmName("pingIntervalMillis")
val pingIntervalMillis: Int = builder.pingInterval

/** Web socket close timeout (in milliseconds). */
@get:JvmName("closeTimeoutMillis")
val closeTimeoutMillis: Int = builder.closeTimeout

/**
* Minimum outbound web socket message size (in bytes) that will be compressed.
* The default is 1024 bytes.
Expand Down Expand Up @@ -327,6 +331,7 @@ open class OkHttpClient internal constructor(
// extensions is always null for clients:
extensions = null,
minimumDeflateSize = minWebSocketMessageToCompress,
closeTimeoutMillis = closeTimeoutMillis.toLong(),
)
webSocket.connect(this)
return webSocket
Expand Down Expand Up @@ -572,6 +577,7 @@ open class OkHttpClient internal constructor(
internal var readTimeout = 10_000
internal var writeTimeout = 10_000
internal var pingInterval = 0
internal var closeTimeout = RealWebSocket.CANCEL_AFTER_CLOSE_MILLIS.toInt()
internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
internal var routeDatabase: RouteDatabase? = null
internal var taskRunner: TaskRunner? = null
Expand Down Expand Up @@ -606,6 +612,7 @@ open class OkHttpClient internal constructor(
this.readTimeout = okHttpClient.readTimeoutMillis
this.writeTimeout = okHttpClient.writeTimeoutMillis
this.pingInterval = okHttpClient.pingIntervalMillis
this.closeTimeout = okHttpClient.closeTimeoutMillis
this.minWebSocketMessageToCompress = okHttpClient.minWebSocketMessageToCompress
this.routeDatabase = okHttpClient.routeDatabase
this.taskRunner = okHttpClient.taskRunner
Expand Down Expand Up @@ -1276,6 +1283,49 @@ open class OkHttpClient internal constructor(
pingInterval = checkDuration("duration", duration)
}

/**
* Sets the close timeout for web socket connections. A value of 0 means no timeout, otherwise
* values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The close timeout is the maximum amount of time after the client calls [close] to wait
* for a graceful shutdown. If the server doesn't respond the web socket will be canceled.
* The default value is 60 seconds.
*/
fun closeTimeout(
timeout: Long,
unit: TimeUnit,
) = apply {
closeTimeout = checkDuration("timeout", timeout, unit)
}

/**
* Sets the close timeout for web socket connections. A value of 0 means no timeout, otherwise
* values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The close timeout is the maximum amount of time after the client calls [close] to wait
* for a graceful shutdown. If the server doesn't respond the web socket will be canceled.
* The default value is 60 seconds.
*/
@SuppressLint("NewApi")
@IgnoreJRERequirement
fun closeTimeout(duration: Duration) =
apply {
closeTimeout(duration.toMillis(), MILLISECONDS)
}

/**
* Sets the close timeout for web socket connections. A value of 0 means no timeout, otherwise
* values must be between 1 and [Integer.MAX_VALUE] when converted to milliseconds.
*
* The close timeout is the maximum amount of time after the client calls [close] to wait
* for a graceful shutdown. If the server doesn't respond the web socket will be canceled.
* The default value is 60 seconds.
*/
fun closeTimeout(duration: KotlinDuration) =
apply {
closeTimeout = checkDuration("duration", duration)
}

/**
* Sets minimum outbound web socket message size (in bytes) that will be compressed.
*
Expand Down
5 changes: 3 additions & 2 deletions okhttp/src/main/kotlin/okhttp3/internal/ws/RealWebSocket.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class RealWebSocket(
private var extensions: WebSocketExtensions?,
/** If compression is negotiated, outbound messages of this size and larger will be compressed. */
private var minimumDeflateSize: Long,
private val closeTimeoutMillis: Long,
) : WebSocket, WebSocketReader.FrameCallback {
private val key: String

Expand Down Expand Up @@ -469,7 +470,7 @@ class RealWebSocket(
code: Int,
reason: String?,
): Boolean {
return close(code, reason, CANCEL_AFTER_CLOSE_MILLIS)
return close(code, reason, closeTimeoutMillis)
}

@Synchronized fun close(
Expand Down Expand Up @@ -712,7 +713,7 @@ class RealWebSocket(
* The maximum amount of time after the client calls [close] to wait for a graceful shutdown. If
* the server doesn't respond the web socket will be canceled.
*/
private const val CANCEL_AFTER_CLOSE_MILLIS = 60L * 1000
const val CANCEL_AFTER_CLOSE_MILLIS = 60L * 1000

/**
* The smallest message that will be compressed. We use 1024 because smaller messages already
Expand Down
1 change: 1 addition & 0 deletions okhttp/src/test/java/okhttp3/OkHttpClientTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class OkHttpClientTest {
assertThat(client.readTimeoutMillis).isEqualTo(10000)
assertThat(client.writeTimeoutMillis).isEqualTo(10000)
assertThat(client.pingIntervalMillis).isEqualTo(0)
assertThat(client.closeTimeoutMillis).isEqualTo(60_000)
}

@Test fun webSocketDefaults() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ class RealWebSocketTest {
responseHeaders,
),
RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE,
RealWebSocket.CANCEL_AFTER_CLOSE_MILLIS,
)
webSocket!!.initReaderAndWriter(name, this)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,7 @@ class WebSocketHttpTest {
client.pingIntervalMillis.toLong(),
null,
0L,
client.closeTimeoutMillis.toLong(),
)
webSocket.connect(client)
return webSocket
Expand Down