Skip to content

Commit a724806

Browse files
committed
Acknowledge and apply inbound settings atomically
Closes: #5422 Unfortunately testing this is awkward because it's racy. I did run a stress test that used to reproduce the problem, and now it doesn't, so I am satisfied.
1 parent 9ce9d1a commit a724806

File tree

2 files changed

+36
-38
lines changed

2 files changed

+36
-38
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,7 @@ public final class Http2ConnectionTest {
946946
// fake a settings frame with clear flag set.
947947
Settings settings2 = new Settings();
948948
settings2.set(MAX_CONCURRENT_STREAMS, 60000);
949-
connection.readerRunnable.settings(true, settings2);
949+
connection.readerRunnable.applyAndAckSettings(true, settings2);
950950

951951
synchronized (connection) {
952952
assertThat(connection.peerSettings.getHeaderTableSize()).isEqualTo(-1);

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

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ public final class Http2Connection implements Closeable {
129129
// TODO: MWS will need to guard on this setting before attempting to push.
130130
final Settings peerSettings = new Settings();
131131

132-
boolean receivedInitialPeerSettings = false;
133132
final Socket socket;
134133
final Http2Writer writer;
135134

@@ -690,53 +689,52 @@ class ReaderRunnable extends NamedRunnable implements Http2Reader.Handler {
690689
}
691690
}
692691

693-
@Override public void settings(boolean clearPrevious, Settings newSettings) {
692+
@Override public void settings(boolean clearPrevious, Settings settings) {
693+
try {
694+
writerExecutor.execute(new NamedRunnable("OkHttp %s ACK Settings", connectionName) {
695+
@Override public void execute() {
696+
applyAndAckSettings(clearPrevious, settings);
697+
}
698+
});
699+
} catch (RejectedExecutionException ignored) {
700+
// This connection has been closed.
701+
}
702+
}
703+
704+
void applyAndAckSettings(boolean clearPrevious, Settings settings) {
694705
long delta = 0;
695706
Http2Stream[] streamsToNotify = null;
696-
synchronized (Http2Connection.this) {
697-
int priorWriteWindowSize = peerSettings.getInitialWindowSize();
698-
if (clearPrevious) peerSettings.clear();
699-
peerSettings.merge(newSettings);
700-
applyAndAckSettings(newSettings);
701-
int peerInitialWindowSize = peerSettings.getInitialWindowSize();
702-
if (peerInitialWindowSize != -1 && peerInitialWindowSize != priorWriteWindowSize) {
703-
delta = peerInitialWindowSize - priorWriteWindowSize;
704-
if (!receivedInitialPeerSettings) {
705-
receivedInitialPeerSettings = true;
706-
}
707-
if (!streams.isEmpty()) {
708-
streamsToNotify = streams.values().toArray(new Http2Stream[streams.size()]);
707+
synchronized (writer) {
708+
synchronized (Http2Connection.this) {
709+
int priorWriteWindowSize = peerSettings.getInitialWindowSize();
710+
if (clearPrevious) peerSettings.clear();
711+
peerSettings.merge(settings);
712+
int peerInitialWindowSize = peerSettings.getInitialWindowSize();
713+
if (peerInitialWindowSize != -1 && peerInitialWindowSize != priorWriteWindowSize) {
714+
delta = peerInitialWindowSize - priorWriteWindowSize;
715+
streamsToNotify = !streams.isEmpty()
716+
? streams.values().toArray(new Http2Stream[streams.size()])
717+
: null;
709718
}
710719
}
711-
listenerExecutor.execute(new NamedRunnable("OkHttp %s settings", connectionName) {
712-
@Override public void execute() {
713-
listener.onSettings(Http2Connection.this);
714-
}
715-
});
720+
try {
721+
writer.applyAndAckSettings(peerSettings);
722+
} catch (IOException e) {
723+
failConnection(e);
724+
}
716725
}
717-
if (streamsToNotify != null && delta != 0) {
726+
if (streamsToNotify != null) {
718727
for (Http2Stream stream : streamsToNotify) {
719728
synchronized (stream) {
720729
stream.addBytesToWriteWindow(delta);
721730
}
722731
}
723732
}
724-
}
725-
726-
private void applyAndAckSettings(final Settings peerSettings) {
727-
try {
728-
writerExecutor.execute(new NamedRunnable("OkHttp %s ACK Settings", connectionName) {
729-
@Override public void execute() {
730-
try {
731-
writer.applyAndAckSettings(peerSettings);
732-
} catch (IOException e) {
733-
failConnection(e);
734-
}
735-
}
736-
});
737-
} catch (RejectedExecutionException ignored) {
738-
// This connection has been closed.
739-
}
733+
listenerExecutor.execute(new NamedRunnable("OkHttp %s settings", connectionName) {
734+
@Override public void execute() {
735+
listener.onSettings(Http2Connection.this);
736+
}
737+
});
740738
}
741739

742740
@Override public void ackSettings() {

0 commit comments

Comments
 (0)