Skip to content

Commit b897fb8

Browse files
authored
Merge pull request #5537 from square/jwilson.1006.cancel_close
If RST STREAM and END OF STREAM race, send RST STREAM (4.2.x branch)
2 parents 97d25e8 + 10f9227 commit b897fb8

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ class Http2Stream internal constructor(
534534
@Throws(IOException::class)
535535
private fun emitFrame(outFinishedOnLastFrame: Boolean) {
536536
val toWrite: Long
537+
val outFinished: Boolean
537538
synchronized(this@Http2Stream) {
538539
writeTimeout.enter()
539540
try {
@@ -550,11 +551,11 @@ class Http2Stream internal constructor(
550551
checkOutNotClosed() // Kick out if the stream was reset or closed while waiting.
551552
toWrite = minOf(writeBytesMaximum - writeBytesTotal, sendBuffer.size)
552553
writeBytesTotal += toWrite
554+
outFinished = outFinishedOnLastFrame && toWrite == sendBuffer.size && errorCode == null
553555
}
554556

555557
writeTimeout.enter()
556558
try {
557-
val outFinished = outFinishedOnLastFrame && toWrite == sendBuffer.size
558559
connection.writeData(id, outFinished, sendBuffer, toWrite)
559560
} finally {
560561
writeTimeout.exitAndThrowIfTimedOut()
@@ -578,8 +579,11 @@ class Http2Stream internal constructor(
578579
@Throws(IOException::class)
579580
override fun close() {
580581
assert(!Thread.holdsLock(this@Http2Stream))
582+
583+
val outFinished: Boolean
581584
synchronized(this@Http2Stream) {
582585
if (closed) return
586+
outFinished = errorCode == null
583587
}
584588
if (!sink.finished) {
585589
// We have 0 or more frames of data, and 0 or more frames of trailers. We need to send at
@@ -592,7 +596,7 @@ class Http2Stream internal constructor(
592596
while (sendBuffer.size > 0L) {
593597
emitFrame(false)
594598
}
595-
connection.writeHeaders(id, true, trailers!!.toHeaderList())
599+
connection.writeHeaders(id, outFinished, trailers!!.toHeaderList())
596600
}
597601

598602
hasData -> {
@@ -601,7 +605,7 @@ class Http2Stream internal constructor(
601605
}
602606
}
603607

604-
else -> {
608+
outFinished -> {
605609
connection.writeData(id, true, null, 0L)
606610
}
607611
}

okhttp/src/test/java/okhttp3/internal/http2/HttpOverHttp2Test.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
import java.util.concurrent.LinkedBlockingQueue;
3131
import java.util.concurrent.SynchronousQueue;
3232
import java.util.concurrent.TimeUnit;
33+
import java.util.concurrent.atomic.AtomicReference;
3334
import java.util.logging.Level;
3435
import java.util.logging.Logger;
36+
import javax.annotation.Nullable;
3537
import okhttp3.Cache;
3638
import okhttp3.Call;
3739
import okhttp3.Callback;
@@ -1620,4 +1622,30 @@ public void shutdownAfterLateCoalescing() throws Exception {
16201622

16211623
latch.await();
16221624
}
1625+
1626+
@Test public void cancelWhileWritingRequestBodySendsCancelToServer() throws Exception {
1627+
server.enqueue(new MockResponse());
1628+
1629+
AtomicReference<Call> callReference = new AtomicReference<>();
1630+
Call call = client.newCall(new Request.Builder()
1631+
.url(server.url("/"))
1632+
.post(new RequestBody() {
1633+
@Override public @Nullable MediaType contentType() {
1634+
return MediaType.get("text/plain; charset=utf-8");
1635+
}
1636+
1637+
@Override public void writeTo(BufferedSink sink) {
1638+
callReference.get().cancel();
1639+
}
1640+
})
1641+
.build());
1642+
callReference.set(call);
1643+
1644+
try {
1645+
call.execute();
1646+
fail();
1647+
} catch (IOException expected) {
1648+
assertThat(call.isCanceled()).isTrue();
1649+
}
1650+
}
16231651
}

0 commit comments

Comments
 (0)