Skip to content

Commit f04809d

Browse files
authored
[4.x] Fix websocket reconnect race condition (#7815) (#7817)
* Fix websocket reconnect race condition (#7815) (cherry picked from commit f62fd47) * Attempt to fix a flaky test * Evict connection pool a second time after tests (#7819) (cherry picked from commit e2344c7) * Simplify
1 parent 07b5d82 commit f04809d

File tree

3 files changed

+77
-9
lines changed

3 files changed

+77
-9
lines changed

okhttp-testing-support/src/main/kotlin/okhttp3/OkHttpClientTestRule.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ class OkHttpClientTestRule : TestRule {
153153
println("After delay: " + connectionPool.connectionCount())
154154
}
155155

156-
assertEquals(0, connectionPool.connectionCount())
156+
connectionPool.evictAll()
157+
assertEquals("Still ${connectionPool.connectionCount()} connections open", 0, connectionPool.connectionCount())
157158
}
158159
}
159160

okhttp/src/main/kotlin/okhttp3/internal/ws/RealWebSocket.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ class RealWebSocket(
170170
checkUpgradeSuccess(response, exchange)
171171
streams = exchange!!.newWebSocketStreams()
172172
} catch (e: IOException) {
173-
exchange?.webSocketUpgradeFailed()
174173
failWebSocket(e, response)
175174
response.closeQuietly()
175+
exchange?.webSocketUpgradeFailed()
176176
return
177177
}
178178

okhttp/src/test/java/okhttp3/internal/ws/WebSocketHttpTest.java

+74-7
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
import java.net.ProtocolException;
2323
import java.net.SocketTimeoutException;
2424
import java.time.Duration;
25+
import java.util.ArrayList;
26+
import java.util.List;
2527
import java.util.Random;
2628
import java.util.concurrent.CountDownLatch;
2729
import java.util.concurrent.TimeUnit;
2830
import java.util.concurrent.atomic.AtomicInteger;
31+
import okhttp3.ConnectionPool;
2932
import okhttp3.OkHttpClient;
3033
import okhttp3.OkHttpClientTestRule;
3134
import okhttp3.Protocol;
@@ -315,11 +318,16 @@ private OkHttpClientTestRule configureClientTestRule() {
315318
webServer.enqueue(new MockResponse()
316319
.setResponseCode(101)
317320
.setHeader("Upgrade", "websocket")
318-
.setHeader("Sec-WebSocket-Accept", "ujmZX4KXZqjwy6vi1aQFH5p4Ygk="));
319-
newWebSocket();
321+
.setHeader("Sec-WebSocket-Accept", "ujmZX4KXZqjwy6vi1aQFH5p4Ygk=")
322+
);
323+
webServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START));
324+
325+
RealWebSocket webSocket = newWebSocket();
320326

321327
clientListener.assertFailure(101, null, ProtocolException.class,
322328
"Expected 'Connection' header value 'Upgrade' but was 'null'");
329+
330+
webSocket.cancel();
323331
}
324332

325333
@Test public void wrongConnectionHeader() throws IOException {
@@ -328,21 +336,29 @@ private OkHttpClientTestRule configureClientTestRule() {
328336
.setHeader("Upgrade", "websocket")
329337
.setHeader("Connection", "Downgrade")
330338
.setHeader("Sec-WebSocket-Accept", "ujmZX4KXZqjwy6vi1aQFH5p4Ygk="));
331-
newWebSocket();
339+
webServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START));
340+
341+
RealWebSocket webSocket = newWebSocket();
332342

333343
clientListener.assertFailure(101, null, ProtocolException.class,
334344
"Expected 'Connection' header value 'Upgrade' but was 'Downgrade'");
345+
346+
webSocket.cancel();
335347
}
336348

337349
@Test public void missingUpgradeHeader() throws IOException {
338350
webServer.enqueue(new MockResponse()
339351
.setResponseCode(101)
340352
.setHeader("Connection", "Upgrade")
341353
.setHeader("Sec-WebSocket-Accept", "ujmZX4KXZqjwy6vi1aQFH5p4Ygk="));
342-
newWebSocket();
354+
webServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START));
355+
356+
RealWebSocket webSocket = newWebSocket();
343357

344358
clientListener.assertFailure(101, null, ProtocolException.class,
345359
"Expected 'Upgrade' header value 'websocket' but was 'null'");
360+
361+
webSocket.cancel();
346362
}
347363

348364
@Test public void wrongUpgradeHeader() throws IOException {
@@ -351,21 +367,29 @@ private OkHttpClientTestRule configureClientTestRule() {
351367
.setHeader("Connection", "Upgrade")
352368
.setHeader("Upgrade", "Pepsi")
353369
.setHeader("Sec-WebSocket-Accept", "ujmZX4KXZqjwy6vi1aQFH5p4Ygk="));
354-
newWebSocket();
370+
webServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START));
371+
372+
RealWebSocket webSocket = newWebSocket();
355373

356374
clientListener.assertFailure(101, null, ProtocolException.class,
357375
"Expected 'Upgrade' header value 'websocket' but was 'Pepsi'");
376+
377+
webSocket.cancel();
358378
}
359379

360380
@Test public void missingMagicHeader() throws IOException {
361381
webServer.enqueue(new MockResponse()
362382
.setResponseCode(101)
363383
.setHeader("Connection", "Upgrade")
364384
.setHeader("Upgrade", "websocket"));
365-
newWebSocket();
385+
webServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START));
386+
387+
RealWebSocket webSocket = newWebSocket();
366388

367389
clientListener.assertFailure(101, null, ProtocolException.class,
368390
"Expected 'Sec-WebSocket-Accept' header value 'ujmZX4KXZqjwy6vi1aQFH5p4Ygk=' but was 'null'");
391+
392+
webSocket.cancel();
369393
}
370394

371395
@Test public void wrongMagicHeader() throws IOException {
@@ -374,10 +398,14 @@ private OkHttpClientTestRule configureClientTestRule() {
374398
.setHeader("Connection", "Upgrade")
375399
.setHeader("Upgrade", "websocket")
376400
.setHeader("Sec-WebSocket-Accept", "magic"));
377-
newWebSocket();
401+
webServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START));
402+
403+
RealWebSocket webSocket = newWebSocket();
378404

379405
clientListener.assertFailure(101, null, ProtocolException.class,
380406
"Expected 'Sec-WebSocket-Accept' header value 'ujmZX4KXZqjwy6vi1aQFH5p4Ygk=' but was 'magic'");
407+
408+
webSocket.cancel();
381409
}
382410

383411
@Test public void clientIncludesForbiddenHeader() throws IOException {
@@ -800,6 +828,45 @@ private OkHttpClientTestRule configureClientTestRule() {
800828
webSocket.close(1000, null);
801829
}
802830

831+
/** https://github.com/square/okhttp/issues/7768 */
832+
@Test public void reconnectingToNonWebSocket() throws InterruptedException {
833+
for (int i = 0; i < 30; i++) {
834+
webServer.enqueue(new MockResponse()
835+
.setBodyDelay(100, TimeUnit.MILLISECONDS)
836+
.setBody("Wrong endpoint")
837+
.setResponseCode(401));
838+
}
839+
840+
Request request = new Request.Builder()
841+
.url(webServer.url("/"))
842+
.build();
843+
844+
CountDownLatch attempts = new CountDownLatch(20);
845+
846+
List<WebSocket> webSockets = new ArrayList<>();
847+
848+
WebSocketListener reconnectOnFailure = new WebSocketListener() {
849+
@Override
850+
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
851+
if (attempts.getCount() > 0) {
852+
clientListener.setNextEventDelegate(this);
853+
webSockets.add(client.newWebSocket(request, clientListener));
854+
attempts.countDown();
855+
}
856+
}
857+
};
858+
859+
clientListener.setNextEventDelegate(reconnectOnFailure);
860+
861+
webSockets.add(client.newWebSocket(request, clientListener));
862+
863+
attempts.await();
864+
865+
for (WebSocket webSocket: webSockets) {
866+
webSocket.cancel();
867+
}
868+
}
869+
803870
@Test public void compressedMessages() throws Exception {
804871
successfulExtensions("permessage-deflate");
805872
}

0 commit comments

Comments
 (0)