Skip to content

Commit 8f86110

Browse files
committed
Confirm concurrent HTTP/2 requests with empty flow-control window.
1 parent 44c2696 commit 8f86110

File tree

2 files changed

+55
-19
lines changed

2 files changed

+55
-19
lines changed

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

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -311,15 +311,7 @@ private static OkHttpClient buildHttp2Client() {
311311
.build());
312312
Response response1 = call1.execute();
313313

314-
// Wait until the server has completely filled the stream and connection flow-control windows.
315-
int expectedFrameCount = Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE / 16384;
316-
int dataFrameCount = 0;
317-
while (dataFrameCount < expectedFrameCount) {
318-
String log = http2Handler.take();
319-
if (log.equals("FINE: << 0x00000003 16384 DATA ")) {
320-
dataFrameCount++;
321-
}
322-
}
314+
waitForDataFrames(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE);
323315

324316
// Cancel the call and discard what we've buffered for the response body. This should free up
325317
// the connection flow-control window so new requests can proceed.
@@ -334,6 +326,18 @@ private static OkHttpClient buildHttp2Client() {
334326
assertEquals("abc", response2.body().string());
335327
}
336328

329+
/** Wait for the client to receive {@code dataLength} DATA frames. */
330+
private void waitForDataFrames(int dataLength) throws Exception {
331+
int expectedFrameCount = dataLength / 16384;
332+
int dataFrameCount = 0;
333+
while (dataFrameCount < expectedFrameCount) {
334+
String log = http2Handler.take();
335+
if (log.equals("FINE: << 0x00000003 16384 DATA ")) {
336+
dataFrameCount++;
337+
}
338+
}
339+
}
340+
337341
@Test public void connectionWindowUpdateOnClose() throws Exception {
338342
server.enqueue(new MockResponse()
339343
.setBody(new Buffer().write(new byte[Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE + 1])));
@@ -345,15 +349,7 @@ private static OkHttpClient buildHttp2Client() {
345349
.build());
346350
Response response1 = call1.execute();
347351

348-
// Wait until the server has completely filled the stream and connection flow-control windows.
349-
int expectedFrameCount = Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE / 16384;
350-
int dataFrameCount = 0;
351-
while (dataFrameCount < expectedFrameCount) {
352-
String log = http2Handler.take();
353-
if (log.equals("FINE: << 0x00000003 16384 DATA ")) {
354-
dataFrameCount++;
355-
}
356-
}
352+
waitForDataFrames(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE);
357353

358354
// Cancel the call and close the response body. This should discard the buffered data and update
359355
// the connnection flow-control window.
@@ -367,6 +363,38 @@ private static OkHttpClient buildHttp2Client() {
367363
assertEquals("abc", response2.body().string());
368364
}
369365

366+
@Test public void concurrentRequestWithEmptyFlowControlWindow() throws Exception {
367+
server.enqueue(new MockResponse()
368+
.setBody(new Buffer().write(new byte[Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE])));
369+
server.enqueue(new MockResponse()
370+
.setBody("abc"));
371+
372+
Call call1 = client.newCall(new Request.Builder()
373+
.url(server.url("/"))
374+
.build());
375+
Response response1 = call1.execute();
376+
377+
waitForDataFrames(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE);
378+
379+
assertEquals(Http2Connection.OKHTTP_CLIENT_WINDOW_SIZE, response1.body().contentLength());
380+
int read = response1.body().source().read(new byte[8192]);
381+
assertEquals(8192, read);
382+
383+
// Make a second call that should transmit the response headers. The response body won't be
384+
// transmitted until the flow-control window is updated from the first request.
385+
Call call2 = client.newCall(new Request.Builder()
386+
.url(server.url("/"))
387+
.build());
388+
Response response2 = call2.execute();
389+
assertEquals(200, response2.code());
390+
391+
// Close the response body. This should discard the buffered data and update the connection
392+
// flow-control window.
393+
response1.close();
394+
395+
assertEquals("abc", response2.body().string());
396+
}
397+
370398
/** https://github.com/square/okhttp/issues/373 */
371399
@Test @Ignore public void synchronousRequest() throws Exception {
372400
server.enqueue(new MockResponse().setBody("A"));

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,17 @@ public void sendResponseHeaders(List<Header> responseHeaders, boolean out) throw
180180
outFinished = true;
181181
}
182182
}
183+
184+
// Only DATA frames are subject to flow-control. Transmit the HEADER frame if the connection
185+
// flow-control window is fully depleted.
186+
boolean connectionWindowEmpty;
187+
synchronized (connection) {
188+
connectionWindowEmpty = connection.bytesLeftInWriteWindow == 0L;
189+
}
190+
183191
connection.writeSynReply(id, outFinished, responseHeaders);
184192

185-
if (outFinished) {
193+
if (outFinished || connectionWindowEmpty) {
186194
connection.flush();
187195
}
188196
}

0 commit comments

Comments
 (0)