okhttp: HTTP/2 Write Timeouts Aren’t Immediate
- OkHttp 3.12.0
- Android asynctask that sends a set of little file (about 1.3mb each file) via HTTP2 POST to a cloud service, one by one
- Android device with Android 5.1.1. (API LEVEL 22)
- HttpClient build as following:
val client = OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(5000, TimeUnit.MILLISECONDS)
.writeTimeout(5000, TimeUnit.MILLISECONDS)
.callTimeout(10000, TimeUnit.MILLISECONDS)
.build()
- this is the async task code
fun uploadFile(fileName: String, fileInputStream: InputStream, totalNumber: Int, index: Int) {
val client = OkHttpClient.Builder()
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(5000, TimeUnit.MILLISECONDS)
.writeTimeout(5000, TimeUnit.MILLISECONDS)
.callTimeout(10000, TimeUnit.MILLISECONDS)
.build()
val data = fileInputStream.readBytes()
Timber.i("Start uploading $fileName (${data.size} bytes)")
val digest = md5(data)
val body = RequestBody.create(null, data)
val request = Request.Builder()
.addHeader(HEADER_CHECKSUM, digest)
.url(url)
.post(body)
.build()
try {
val call = client.newCall(request)
call.execute().use { response ->
/* do nothing */
}
} catch (e : Exception) {
Timber.i(e)
}
}
- At my offiche I have a very bad network condition with a lot of TCP retransmission
EXPECTED:
- The maximum expected time for each file (callTimeout) should be 10 seconds ACTUAL:
- these are for example the timing for 10 file:
- 85622ms - UPLOAD ERROR
- 8539ms - UPLOAD ERROR
- 18381ms - UPLOAD ERROR
- 8564ms - UPLOAD ERROR
- 10100ms - UPLOAD ERROR
- 6810ms
- 61457ms - UPLOAD ERROR
- 9088ms
- 8040ms
- 22351ms - UPLOAD ERROR
Seven uploads exceeded the callTimeout and goes in error. I can reproduce this behaviour easily but this happens only here at my office, so an example code could be useless
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 26 (5 by maintainers)
I think the core problem is that our HTTP/2 write timeouts don’t have teeth. On HTTP/1 we close the underlying socket which takes effect immediately. On HTTP/2 we cancel the stream, but if it’s currently blocked on a write it won’t cancel until that write returns.
This is the output with LoggingEventListener enabled