@@ -92,10 +92,10 @@ final class OkHttpServerTransport implements ServerTransport,
92
92
private static final ByteString CONTENT_LENGTH = ByteString .encodeUtf8 ("content-length" );
93
93
94
94
private final Config config ;
95
- private final Socket bareSocket ;
96
95
private final Variant variant = new Http2 ();
97
96
private final TransportTracer tracer ;
98
97
private final InternalLogId logId ;
98
+ private Socket socket ;
99
99
private ServerTransportListener listener ;
100
100
private Executor transportExecutor ;
101
101
private ScheduledExecutorService scheduledExecutorService ;
@@ -141,11 +141,11 @@ final class OkHttpServerTransport implements ServerTransport,
141
141
142
142
public OkHttpServerTransport (Config config , Socket bareSocket ) {
143
143
this .config = Preconditions .checkNotNull (config , "config" );
144
- this .bareSocket = Preconditions .checkNotNull (bareSocket , "bareSocket" );
144
+ this .socket = Preconditions .checkNotNull (bareSocket , "bareSocket" );
145
145
146
146
tracer = config .transportTracerFactory .create ();
147
147
tracer .setFlowControlWindowReader (this ::readFlowControlWindow );
148
- logId = InternalLogId .allocate (getClass (), bareSocket .getRemoteSocketAddress ().toString ());
148
+ logId = InternalLogId .allocate (getClass (), socket .getRemoteSocketAddress ().toString ());
149
149
transportExecutor = config .transportExecutorPool .getObject ();
150
150
scheduledExecutorService = config .scheduledExecutorServicePool .getObject ();
151
151
keepAliveEnforcer = new KeepAliveEnforcer (config .permitKeepAliveWithoutCalls ,
@@ -161,10 +161,17 @@ public void start(ServerTransportListener listener) {
161
161
162
162
private void startIo (SerializingExecutor serializingExecutor ) {
163
163
try {
164
- bareSocket .setTcpNoDelay (true );
164
+ // The socket implementation is lazily initialized, but had broken thread-safety
165
+ // for that laziness https://bugs.openjdk.org/browse/JDK-8278326.
166
+ // As a workaround, we lock to synchronize initialization with shutdown().
167
+ synchronized (lock ) {
168
+ socket .setTcpNoDelay (true );
169
+ }
165
170
HandshakerSocketFactory .HandshakeResult result =
166
- config .handshakerSocketFactory .handshake (bareSocket , Attributes .EMPTY );
167
- Socket socket = result .socket ;
171
+ config .handshakerSocketFactory .handshake (socket , Attributes .EMPTY );
172
+ synchronized (lock ) {
173
+ this .socket = result .socket ;
174
+ }
168
175
this .attributes = result .attributes ;
169
176
170
177
int maxQueuedControlFrames = 10000 ;
@@ -249,7 +256,7 @@ public void data(boolean outFinished, int streamId, Buffer source, int byteCount
249
256
log .log (Level .INFO , "Socket failed to handshake" , ex );
250
257
}
251
258
}
252
- GrpcUtil .closeQuietly (bareSocket );
259
+ GrpcUtil .closeQuietly (socket );
253
260
terminated ();
254
261
}
255
262
}
@@ -268,7 +275,7 @@ private void shutdown(@Nullable Long gracefulShutdownPeriod) {
268
275
this .gracefulShutdownPeriod = gracefulShutdownPeriod ;
269
276
if (frameWriter == null ) {
270
277
handshakeShutdown = true ;
271
- GrpcUtil .closeQuietly (bareSocket );
278
+ GrpcUtil .closeQuietly (socket );
272
279
} else {
273
280
// RFC7540 §6.8. Begin double-GOAWAY graceful shutdown. To wait one RTT we use a PING, but
274
281
// we also set a timer to limit the upper bound in case the PING is excessively stalled or
@@ -309,7 +316,7 @@ public void shutdownNow(Status reason) {
309
316
synchronized (lock ) {
310
317
if (frameWriter == null ) {
311
318
handshakeShutdown = true ;
312
- GrpcUtil .closeQuietly (bareSocket );
319
+ GrpcUtil .closeQuietly (socket );
313
320
return ;
314
321
}
315
322
}
@@ -360,7 +367,7 @@ private void abruptShutdown(
360
367
361
368
private void triggerForcefulClose () {
362
369
// Safe to do unconditionally; no need to check if timer cancellation raced
363
- GrpcUtil .closeQuietly (bareSocket );
370
+ GrpcUtil .closeQuietly (socket );
364
371
}
365
372
366
373
private void terminated () {
@@ -396,9 +403,9 @@ public ListenableFuture<InternalChannelz.SocketStats> getStats() {
396
403
synchronized (lock ) {
397
404
return Futures .immediateFuture (new InternalChannelz .SocketStats (
398
405
tracer .getStats (),
399
- bareSocket .getLocalSocketAddress (),
400
- bareSocket .getRemoteSocketAddress (),
401
- Utils .getSocketOptions (bareSocket ),
406
+ socket .getLocalSocketAddress (),
407
+ socket .getRemoteSocketAddress (),
408
+ Utils .getSocketOptions (socket ),
402
409
securityInfo ));
403
410
}
404
411
}
@@ -593,12 +600,12 @@ public void run() {
593
600
} finally {
594
601
// Wait for the abrupt shutdown to be processed by AsyncSink and close the socket
595
602
try {
596
- GrpcUtil .exhaust (bareSocket .getInputStream ());
603
+ GrpcUtil .exhaust (socket .getInputStream ());
597
604
} catch (IOException ex ) {
598
605
// Unable to wait, so just proceed to tear-down. The socket is probably already closed so
599
606
// the GOAWAY can't be sent anyway.
600
607
}
601
- GrpcUtil .closeQuietly (bareSocket );
608
+ GrpcUtil .closeQuietly (socket );
602
609
terminated ();
603
610
Thread .currentThread ().setName (threadName );
604
611
}
@@ -1108,7 +1115,7 @@ public void onPingTimeout() {
1108
1115
synchronized (lock ) {
1109
1116
goAwayStatus = Status .UNAVAILABLE
1110
1117
.withDescription ("Keepalive failed. Considering connection dead" );
1111
- GrpcUtil .closeQuietly (bareSocket );
1118
+ GrpcUtil .closeQuietly (socket );
1112
1119
}
1113
1120
}
1114
1121
}
0 commit comments