mirror of https://github.com/grpc/grpc-java.git
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.
This commit is contained in:
parent
6b80efcfa8
commit
dd35ae5206
|
|
@ -221,6 +221,9 @@ class OkHttpClientTransport implements ConnectionClientTransport, TransportExcep
|
||||||
@Nullable
|
@Nullable
|
||||||
final HttpConnectProxiedSocketAddress proxiedAddr;
|
final HttpConnectProxiedSocketAddress proxiedAddr;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
int proxySocketTimeout = 30000;
|
||||||
|
|
||||||
// The following fields should only be used for test.
|
// The following fields should only be used for test.
|
||||||
Runnable connectingCallback;
|
Runnable connectingCallback;
|
||||||
SettableFuture<Void> connectedFuture;
|
SettableFuture<Void> connectedFuture;
|
||||||
|
|
@ -626,8 +629,8 @@ class OkHttpClientTransport implements ConnectionClientTransport, TransportExcep
|
||||||
|
|
||||||
private Socket createHttpProxySocket(InetSocketAddress address, InetSocketAddress proxyAddress,
|
private Socket createHttpProxySocket(InetSocketAddress address, InetSocketAddress proxyAddress,
|
||||||
String proxyUsername, String proxyPassword) throws StatusException {
|
String proxyUsername, String proxyPassword) throws StatusException {
|
||||||
|
Socket sock = null;
|
||||||
try {
|
try {
|
||||||
Socket sock;
|
|
||||||
// The proxy address may not be resolved
|
// The proxy address may not be resolved
|
||||||
if (proxyAddress.getAddress() != null) {
|
if (proxyAddress.getAddress() != null) {
|
||||||
sock = socketFactory.createSocket(proxyAddress.getAddress(), proxyAddress.getPort());
|
sock = socketFactory.createSocket(proxyAddress.getAddress(), proxyAddress.getPort());
|
||||||
|
|
@ -636,6 +639,9 @@ class OkHttpClientTransport implements ConnectionClientTransport, TransportExcep
|
||||||
socketFactory.createSocket(proxyAddress.getHostName(), proxyAddress.getPort());
|
socketFactory.createSocket(proxyAddress.getHostName(), proxyAddress.getPort());
|
||||||
}
|
}
|
||||||
sock.setTcpNoDelay(true);
|
sock.setTcpNoDelay(true);
|
||||||
|
// A socket timeout is needed because lost network connectivity while reading from the proxy,
|
||||||
|
// can cause reading from the socket to hang.
|
||||||
|
sock.setSoTimeout(proxySocketTimeout);
|
||||||
|
|
||||||
Source source = Okio.source(sock);
|
Source source = Okio.source(sock);
|
||||||
BufferedSink sink = Okio.buffer(Okio.sink(sock));
|
BufferedSink sink = Okio.buffer(Okio.sink(sock));
|
||||||
|
|
@ -682,8 +688,13 @@ class OkHttpClientTransport implements ConnectionClientTransport, TransportExcep
|
||||||
statusLine.code, statusLine.message, body.readUtf8());
|
statusLine.code, statusLine.message, body.readUtf8());
|
||||||
throw Status.UNAVAILABLE.withDescription(message).asException();
|
throw Status.UNAVAILABLE.withDescription(message).asException();
|
||||||
}
|
}
|
||||||
|
// As the socket will be used for RPCs from here on, we want the socket timeout back to zero.
|
||||||
|
sock.setSoTimeout(0);
|
||||||
return sock;
|
return sock;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
if (sock != null) {
|
||||||
|
GrpcUtil.closeQuietly(sock);
|
||||||
|
}
|
||||||
throw Status.UNAVAILABLE.withDescription("Failed trying to connect with proxy").withCause(e)
|
throw Status.UNAVAILABLE.withDescription("Failed trying to connect with proxy").withCause(e)
|
||||||
.asException();
|
.asException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1877,6 +1877,37 @@ public class OkHttpClientTransportTest {
|
||||||
verify(transportListener, timeout(TIME_OUT_MS)).transportTerminated();
|
verify(transportListener, timeout(TIME_OUT_MS)).transportTerminated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void proxy_serverHangs() throws Exception {
|
||||||
|
ServerSocket serverSocket = new ServerSocket(0);
|
||||||
|
InetSocketAddress targetAddress = InetSocketAddress.createUnresolved("theservice", 80);
|
||||||
|
clientTransport = new OkHttpClientTransport(
|
||||||
|
channelBuilder.buildTransportFactory(),
|
||||||
|
targetAddress,
|
||||||
|
"authority",
|
||||||
|
"userAgent",
|
||||||
|
EAG_ATTRS,
|
||||||
|
HttpConnectProxiedSocketAddress.newBuilder()
|
||||||
|
.setTargetAddress(targetAddress)
|
||||||
|
.setProxyAddress(new InetSocketAddress("localhost", serverSocket.getLocalPort()))
|
||||||
|
.build(),
|
||||||
|
tooManyPingsRunnable);
|
||||||
|
clientTransport.proxySocketTimeout = 10;
|
||||||
|
clientTransport.start(transportListener);
|
||||||
|
|
||||||
|
Socket sock = serverSocket.accept();
|
||||||
|
serverSocket.close();
|
||||||
|
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream(), UTF_8));
|
||||||
|
assertEquals("CONNECT theservice:80 HTTP/1.1", reader.readLine());
|
||||||
|
assertEquals("Host: theservice:80", reader.readLine());
|
||||||
|
while (!"".equals(reader.readLine())) {}
|
||||||
|
|
||||||
|
verify(transportListener, timeout(200)).transportShutdown(any(Status.class));
|
||||||
|
verify(transportListener, timeout(TIME_OUT_MS)).transportTerminated();
|
||||||
|
sock.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void goAway_notUtf8() throws Exception {
|
public void goAway_notUtf8() throws Exception {
|
||||||
initTransport();
|
initTransport();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue