Skip to content

Commit 69e86b9

Browse files
temawiejona86
authored andcommitted
okhttp: Add client transport proxy socket timeout (#9586)
Not having a timeout when reading the response from a proxy server can cause a hang if network connectivity is lost at the same time.
1 parent ce5f789 commit 69e86b9

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

okhttp/src/main/java/io/grpc/okhttp/OkHttpClientTransport.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ protected void handleNotInUse() {
221221
@Nullable
222222
final HttpConnectProxiedSocketAddress proxiedAddr;
223223

224+
@VisibleForTesting
225+
int proxySocketTimeout = 30000;
226+
224227
// The following fields should only be used for test.
225228
Runnable connectingCallback;
226229
SettableFuture<Void> connectedFuture;
@@ -626,8 +629,8 @@ private void sendConnectionPrefaceAndSettings() {
626629

627630
private Socket createHttpProxySocket(InetSocketAddress address, InetSocketAddress proxyAddress,
628631
String proxyUsername, String proxyPassword) throws StatusException {
632+
Socket sock = null;
629633
try {
630-
Socket sock;
631634
// The proxy address may not be resolved
632635
if (proxyAddress.getAddress() != null) {
633636
sock = socketFactory.createSocket(proxyAddress.getAddress(), proxyAddress.getPort());
@@ -636,6 +639,9 @@ private Socket createHttpProxySocket(InetSocketAddress address, InetSocketAddres
636639
socketFactory.createSocket(proxyAddress.getHostName(), proxyAddress.getPort());
637640
}
638641
sock.setTcpNoDelay(true);
642+
// A socket timeout is needed because lost network connectivity while reading from the proxy,
643+
// can cause reading from the socket to hang.
644+
sock.setSoTimeout(proxySocketTimeout);
639645

640646
Source source = Okio.source(sock);
641647
BufferedSink sink = Okio.buffer(Okio.sink(sock));
@@ -682,8 +688,13 @@ private Socket createHttpProxySocket(InetSocketAddress address, InetSocketAddres
682688
statusLine.code, statusLine.message, body.readUtf8());
683689
throw Status.UNAVAILABLE.withDescription(message).asException();
684690
}
691+
// As the socket will be used for RPCs from here on, we want the socket timeout back to zero.
692+
sock.setSoTimeout(0);
685693
return sock;
686694
} catch (IOException e) {
695+
if (sock != null) {
696+
GrpcUtil.closeQuietly(sock);
697+
}
687698
throw Status.UNAVAILABLE.withDescription("Failed trying to connect with proxy").withCause(e)
688699
.asException();
689700
}

okhttp/src/test/java/io/grpc/okhttp/OkHttpClientTransportTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,6 +1877,37 @@ public void proxy_immediateServerClose() throws Exception {
18771877
verify(transportListener, timeout(TIME_OUT_MS)).transportTerminated();
18781878
}
18791879

1880+
@Test
1881+
public void proxy_serverHangs() throws Exception {
1882+
ServerSocket serverSocket = new ServerSocket(0);
1883+
InetSocketAddress targetAddress = InetSocketAddress.createUnresolved("theservice", 80);
1884+
clientTransport = new OkHttpClientTransport(
1885+
channelBuilder.buildTransportFactory(),
1886+
targetAddress,
1887+
"authority",
1888+
"userAgent",
1889+
EAG_ATTRS,
1890+
HttpConnectProxiedSocketAddress.newBuilder()
1891+
.setTargetAddress(targetAddress)
1892+
.setProxyAddress(new InetSocketAddress("localhost", serverSocket.getLocalPort()))
1893+
.build(),
1894+
tooManyPingsRunnable);
1895+
clientTransport.proxySocketTimeout = 10;
1896+
clientTransport.start(transportListener);
1897+
1898+
Socket sock = serverSocket.accept();
1899+
serverSocket.close();
1900+
1901+
BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream(), UTF_8));
1902+
assertEquals("CONNECT theservice:80 HTTP/1.1", reader.readLine());
1903+
assertEquals("Host: theservice:80", reader.readLine());
1904+
while (!"".equals(reader.readLine())) {}
1905+
1906+
verify(transportListener, timeout(200)).transportShutdown(any(Status.class));
1907+
verify(transportListener, timeout(TIME_OUT_MS)).transportTerminated();
1908+
sock.close();
1909+
}
1910+
18801911
@Test
18811912
public void goAway_notUtf8() throws Exception {
18821913
initTransport();

0 commit comments

Comments
 (0)