Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.7k views
in Technique[技术] by (71.8m points)

okhttp - How to close response body without reading the response body in okhttp3

Environment

  • MacOS
  • JDK 11
  • okhttp-4.9.0

The environment I'm working on is quite sensitive with the network bandwidth.
Sometimes, I don't need to read all the response body. A part of the response body could be enough to decide a result.

I want to close the response(=response body) without reading the body.

What do I want to do is like below

try (Response response = client.newCall(request).execute()) {
    assertThat(response.code(), is(200))
    // do nothing with the response body
}

But, when the connection is established with HTTP1, the ResponseBody::close invokes Http1ExchangeCodec.FixedLengthSource::close in the end.

Http1ExchangeCodec.FixedLengthSource::close

override fun close() {
    if (closed) return

    if (bytesRemaining != 0L &&
        !discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) {
    connection.noNewExchanges() // Unread bytes remain on the stream.
    responseBodyComplete()
    }

    closed = true
}

Then, the discard method reads all the response body source as below.

Util.kt

fun Source.discard(timeout: Int, timeUnit: TimeUnit): Boolean = try {
    this.skipAll(timeout, timeUnit)
} catch (_: IOException) {
    false
}

@Throws(IOException::class)
fun Source.skipAll(duration: Int, timeUnit: TimeUnit): Boolean {
    val nowNs = System.nanoTime()
    val originalDurationNs = if (timeout().hasDeadline()) {
        timeout().deadlineNanoTime() - nowNs
    } else {
        Long.MAX_VALUE
    }
    timeout().deadlineNanoTime(nowNs + minOf(originalDurationNs, timeUnit.toNanos(duration.toLong())))
    return try {
        val skipBuffer = Buffer()
        while (read(skipBuffer, 8192) != -1L) {
            skipBuffer.clear()
        }
        true // Success! The source has been exhausted.
    } catch (_: InterruptedIOException) {
        false // We ran out of time before exhausting the source.
    } finally {
        if (originalDurationNs == Long.MAX_VALUE) {
            timeout().clearDeadline()
        } else {
            timeout().deadlineNanoTime(nowNs + originalDurationNs)
        }
    }
}

It reads all the body and clears buffer. In my case, it's a waste of the CPU time and the network bandwidth.

Is there any way to just close it literally?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

No, there's no way to immediately close the response body. If you never issue another HTTP request on that connection it is a waste of bandwidth and CPU. But it is necessary for the connection to be pooled.

Consider using a HEAD method or adding a range header if you're not interested in the response body. That way you're also saving the server’s CPU and bandwidth.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...