Skip to content

Commit b147d28

Browse files
authored
Fix for stalled streams (#7801) (#7818)
(cherry picked from commit b11a6a8)
1 parent f04809d commit b147d28

File tree

1 file changed

+15
-15
lines changed

1 file changed

+15
-15
lines changed

okhttp/src/main/kotlin/okhttp3/internal/http2/Http2Stream.kt

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,8 @@ class Http2Stream internal constructor(
364364

365365
val unacknowledgedBytesRead = readBytesTotal - readBytesAcknowledged
366366
if (errorExceptionToDeliver == null &&
367-
unacknowledgedBytesRead >= connection.okHttpSettings.initialWindowSize / 2) {
367+
unacknowledgedBytesRead >= connection.okHttpSettings.initialWindowSize / 2
368+
) {
368369
// Flow control: notify the peer that we're ready for more data! Only send a
369370
// WINDOW_UPDATE if the stream isn't in error.
370371
connection.writeWindowUpdateLater(id, unacknowledgedBytesRead)
@@ -387,8 +388,6 @@ class Http2Stream internal constructor(
387388
}
388389

389390
if (readBytesDelivered != -1L) {
390-
// Update connection.unacknowledgedBytesRead outside the synchronized block.
391-
updateConnectionFlowControl(readBytesDelivered)
392391
return readBytesDelivered
393392
}
394393

@@ -418,41 +417,39 @@ class Http2Stream internal constructor(
418417
internal fun receive(source: BufferedSource, byteCount: Long) {
419418
this@Http2Stream.assertThreadDoesntHoldLock()
420419

421-
var byteCount = byteCount
420+
var remainingByteCount = byteCount
422421

423-
while (byteCount > 0L) {
422+
while (remainingByteCount > 0L) {
424423
val finished: Boolean
425424
val flowControlError: Boolean
426425
synchronized(this@Http2Stream) {
427426
finished = this.finished
428-
flowControlError = byteCount + readBuffer.size > maxByteCount
427+
flowControlError = remainingByteCount + readBuffer.size > maxByteCount
429428
}
430429

431430
// If the peer sends more data than we can handle, discard it and close the connection.
432431
if (flowControlError) {
433-
source.skip(byteCount)
432+
source.skip(remainingByteCount)
434433
closeLater(ErrorCode.FLOW_CONTROL_ERROR)
435434
return
436435
}
437436

438437
// Discard data received after the stream is finished. It's probably a benign race.
439438
if (finished) {
440-
source.skip(byteCount)
439+
source.skip(remainingByteCount)
441440
return
442441
}
443442

444443
// Fill the receive buffer without holding any locks.
445-
val read = source.read(receiveBuffer, byteCount)
444+
val read = source.read(receiveBuffer, remainingByteCount)
446445
if (read == -1L) throw EOFException()
447-
byteCount -= read
446+
remainingByteCount -= read
448447

449448
// Move the received data to the read buffer to the reader can read it. If this source has
450449
// been closed since this read began we must discard the incoming data and tell the
451450
// connection we've done so.
452-
var bytesDiscarded = 0L
453451
synchronized(this@Http2Stream) {
454452
if (closed) {
455-
bytesDiscarded = receiveBuffer.size
456453
receiveBuffer.clear()
457454
} else {
458455
val wasEmpty = readBuffer.size == 0L
@@ -462,10 +459,13 @@ class Http2Stream internal constructor(
462459
}
463460
}
464461
}
465-
if (bytesDiscarded > 0L) {
466-
updateConnectionFlowControl(bytesDiscarded)
467-
}
468462
}
463+
464+
// Update the connection flow control, as this is a shared resource.
465+
// Even if our stream doesn't need more data, others might.
466+
// But delay updating the stream flow control until that stream has been
467+
// consumed
468+
updateConnectionFlowControl(byteCount)
469469
}
470470

471471
override fun timeout(): Timeout = readTimeout

0 commit comments

Comments
 (0)