Skip to content

Commit 87087a4

Browse files
committed
core: handle closed transport factory in InternalSubchannel without panic
Catch IllegalStateException when starting a new transport against a closed transport factory and shut down the subchannel safely instead of letting it escape into the SynchronizationContext and causing a channel panic.
1 parent 7fdcde1 commit 87087a4

2 files changed

Lines changed: 29 additions & 4 deletions

File tree

core/src/main/java/io/grpc/internal/InternalSubchannel.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,20 @@ private void startNewTransport() {
273273
TransportLogger transportLogger = new TransportLogger();
274274
// In case the transport logs in the constructor, use the subchannel logId
275275
transportLogger.logId = getLogId();
276-
ConnectionClientTransport transport =
277-
new CallTracingTransport(
278-
transportFactory
279-
.newClientTransport(address, options, transportLogger), callsTracer);
276+
ConnectionClientTransport rawTransport;
277+
try {
278+
rawTransport =
279+
transportFactory.newClientTransport(address, options, transportLogger);
280+
} catch (IllegalStateException e) {
281+
channelLogger.log(
282+
ChannelLogLevel.WARNING, "Transport factory is closed, shutting down subchannel", e);
283+
shutdown(
284+
Status.UNAVAILABLE
285+
.withDescription("Transport factory is closed")
286+
.withCause(e));
287+
return;
288+
}
289+
ConnectionClientTransport transport = new CallTracingTransport(rawTransport, callsTracer);
280290
transportLogger.logId = transport.getLogId();
281291
channelz.addClientSocket(transport);
282292
pendingTransport = transport;

core/src/test/java/io/grpc/internal/InternalSubchannelTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,6 +1671,21 @@ public void subchannelStateChanges_backendServiceFallsBackToResolutionResultAttr
16711671
);
16721672
}
16731673

1674+
@Test
1675+
public void transportFactoryClosed_shutsDownSubchannelSafely() {
1676+
SocketAddress addr = mock(SocketAddress.class);
1677+
createInternalSubchannel(addr);
1678+
assertEquals(IDLE, internalSubchannel.getState());
1679+
1680+
when(mockTransportFactory.newClientTransport(any(), any(), any()))
1681+
.thenThrow(new IllegalStateException("The transport factory is closed."));
1682+
1683+
assertNull(internalSubchannel.obtainActiveTransport());
1684+
assertExactCallbackInvokes(
1685+
"onStateChange:CONNECTING", "onStateChange:SHUTDOWN", "onTerminated");
1686+
assertEquals(SHUTDOWN, internalSubchannel.getState());
1687+
}
1688+
16741689
private void assertNoCallbackInvoke() {
16751690
while (fakeExecutor.runDueTasks() > 0) {}
16761691
assertEquals(0, callbackInvokes.size());

0 commit comments

Comments
 (0)