34
34
import io .grpc .internal .GrpcAttributes ;
35
35
import io .grpc .internal .GrpcUtil ;
36
36
import io .netty .channel .ChannelDuplexHandler ;
37
- import io .netty .channel .ChannelFuture ;
38
37
import io .netty .channel .ChannelFutureListener ;
39
38
import io .netty .channel .ChannelHandler ;
40
39
import io .netty .channel .ChannelHandlerContext ;
41
- import io .netty .channel .ChannelInboundHandler ;
42
40
import io .netty .channel .ChannelInboundHandlerAdapter ;
43
- import io .netty .channel .ChannelPromise ;
44
41
import io .netty .handler .codec .http .DefaultHttpRequest ;
45
42
import io .netty .handler .codec .http .HttpClientCodec ;
46
43
import io .netty .handler .codec .http .HttpClientUpgradeHandler ;
50
47
import io .netty .handler .codec .http2 .Http2ClientUpgradeCodec ;
51
48
import io .netty .handler .proxy .HttpProxyHandler ;
52
49
import io .netty .handler .proxy .ProxyConnectionEvent ;
53
- import io .netty .handler .proxy .ProxyHandler ;
54
50
import io .netty .handler .ssl .OpenSsl ;
55
51
import io .netty .handler .ssl .OpenSslEngine ;
56
52
import io .netty .handler .ssl .SslContext ;
59
55
import io .netty .util .AsciiString ;
60
56
import io .netty .util .Attribute ;
61
57
import io .netty .util .AttributeMap ;
62
- import io .netty .util .ReferenceCountUtil ;
63
58
import java .net .SocketAddress ;
64
59
import java .net .URI ;
65
- import java .util .ArrayDeque ;
66
60
import java .util .Arrays ;
67
- import java .util .Queue ;
68
61
import java .util .logging .Level ;
69
62
import java .util .logging .Logger ;
70
63
import javax .annotation .Nullable ;
@@ -195,20 +188,16 @@ private void fireProtocolNegotiationEvent(ChannelHandlerContext ctx, SSLSession
195
188
public static ProtocolNegotiator httpProxy (final SocketAddress proxyAddress ,
196
189
final @ Nullable String proxyUsername , final @ Nullable String proxyPassword ,
197
190
final ProtocolNegotiator negotiator ) {
191
+ checkNotNull (negotiator , "negotiator" );
192
+ checkNotNull (proxyAddress , "proxyAddress" );
198
193
final AsciiString scheme = negotiator .scheme ();
199
- Preconditions .checkNotNull (proxyAddress , "proxyAddress" );
200
- Preconditions .checkNotNull (negotiator , "negotiator" );
201
194
class ProxyNegotiator implements ProtocolNegotiator {
202
195
@ Override
203
196
public ChannelHandler newHandler (GrpcHttp2ConnectionHandler http2Handler ) {
204
- HttpProxyHandler proxyHandler ;
205
- if (proxyUsername == null || proxyPassword == null ) {
206
- proxyHandler = new HttpProxyHandler (proxyAddress );
207
- } else {
208
- proxyHandler = new HttpProxyHandler (proxyAddress , proxyUsername , proxyPassword );
209
- }
210
- return new BufferUntilProxyTunnelledHandler (
211
- proxyHandler , negotiator .newHandler (http2Handler ));
197
+ ChannelHandler protocolNegotiationHandler = negotiator .newHandler (http2Handler );
198
+ ChannelHandler ppnh = new ProxyProtocolNegotiationHandler (
199
+ proxyAddress , proxyUsername , proxyPassword , protocolNegotiationHandler );
200
+ return ppnh ;
212
201
}
213
202
214
203
@ Override
@@ -228,34 +217,45 @@ public void close() {
228
217
}
229
218
230
219
/**
231
- * Buffers all writes until the HTTP CONNECT tunnel is established.
220
+ * A Proxy handler follows {@link ProtocolNegotiationHandler} pattern. Upon successful proxy
221
+ * connection, this handler will install {@code next} handler which should be a handler from
222
+ * other type of {@link ProtocolNegotiator} to continue negotiating protocol using proxy.
232
223
*/
233
- static final class BufferUntilProxyTunnelledHandler extends AbstractBufferingHandler {
224
+ static final class ProxyProtocolNegotiationHandler extends ProtocolNegotiationHandler {
234
225
235
- public BufferUntilProxyTunnelledHandler ( ProxyHandler proxyHandler , ChannelHandler handler ) {
236
- super ( proxyHandler , handler ) ;
237
- }
226
+ private final SocketAddress address ;
227
+ @ Nullable private final String userName ;
228
+ @ Nullable private final String password ;
238
229
239
- @ Override
240
- public void userEventTriggered (ChannelHandlerContext ctx , Object evt ) throws Exception {
241
- if (evt instanceof ProxyConnectionEvent ) {
242
- writeBufferedAndRemove (ctx );
243
- }
244
- super .userEventTriggered (ctx , evt );
230
+ public ProxyProtocolNegotiationHandler (
231
+ SocketAddress address ,
232
+ @ Nullable String userName ,
233
+ @ Nullable String password ,
234
+ ChannelHandler next ) {
235
+ super (next );
236
+ this .address = checkNotNull (address , "address" );
237
+ this .userName = userName ;
238
+ this .password = password ;
245
239
}
246
240
247
241
@ Override
248
- public void channelInactive (ChannelHandlerContext ctx ) throws Exception {
249
- fail (ctx , unavailableException ("Connection broken while trying to CONNECT through proxy" ));
250
- super .channelInactive (ctx );
242
+ protected void protocolNegotiationEventTriggered (ChannelHandlerContext ctx ) {
243
+ HttpProxyHandler nettyProxyHandler ;
244
+ if (userName == null || password == null ) {
245
+ nettyProxyHandler = new HttpProxyHandler (address );
246
+ } else {
247
+ nettyProxyHandler = new HttpProxyHandler (address , userName , password );
248
+ }
249
+ ctx .pipeline ().addBefore (ctx .name (), /* newName= */ null , nettyProxyHandler );
251
250
}
252
251
253
252
@ Override
254
- public void close (ChannelHandlerContext ctx , ChannelPromise future ) throws Exception {
255
- if (ctx .channel ().isActive ()) { // This may be a notification that the socket was closed
256
- fail (ctx , unavailableException ("Channel closed while trying to CONNECT through proxy" ));
253
+ protected void userEventTriggered0 (ChannelHandlerContext ctx , Object evt ) throws Exception {
254
+ if (evt instanceof ProxyConnectionEvent ) {
255
+ fireProtocolNegotiationEvent (ctx );
256
+ } else {
257
+ super .userEventTriggered (ctx , evt );
257
258
}
258
- super .close (ctx , future );
259
259
}
260
260
}
261
261
@@ -527,208 +527,6 @@ static void logSslEngineDetails(Level level, ChannelHandlerContext ctx, String m
527
527
log .log (level , builder .toString (), t );
528
528
}
529
529
530
- /**
531
- * Buffers all writes until either {@link #writeBufferedAndRemove(ChannelHandlerContext)} or
532
- * {@link #fail(ChannelHandlerContext, Throwable)} is called. This handler allows us to
533
- * write to a {@link io.netty.channel.Channel} before we are allowed to write to it officially
534
- * i.e. before it's active or the TLS Handshake is complete.
535
- */
536
- public abstract static class AbstractBufferingHandler extends ChannelDuplexHandler {
537
-
538
- private ChannelHandler [] handlers ;
539
- private Queue <ChannelWrite > bufferedWrites = new ArrayDeque <>();
540
- private boolean writing ;
541
- private boolean flushRequested ;
542
- private Throwable failCause ;
543
-
544
- /**
545
- * @param handlers the ChannelHandlers are added to the pipeline on channelRegistered and
546
- * before this handler.
547
- */
548
- protected AbstractBufferingHandler (ChannelHandler ... handlers ) {
549
- this .handlers = handlers ;
550
- }
551
-
552
- /**
553
- * When this channel is registered, we will add all the ChannelHandlers passed into our
554
- * constructor to the pipeline.
555
- */
556
- @ Override
557
- public void channelRegistered (ChannelHandlerContext ctx ) throws Exception {
558
- /**
559
- * This check is necessary as a channel may be registered with different event loops during it
560
- * lifetime and we only want to configure it once.
561
- */
562
- if (handlers != null && handlers .length > 0 ) {
563
- for (ChannelHandler handler : handlers ) {
564
- ctx .pipeline ().addBefore (ctx .name (), null , handler );
565
- }
566
- ChannelHandler handler0 = handlers [0 ];
567
- ChannelHandlerContext handler0Ctx = ctx .pipeline ().context (handlers [0 ]);
568
- handlers = null ;
569
- if (handler0Ctx != null ) { // The handler may have removed itself immediately
570
- if (handler0 instanceof ChannelInboundHandler ) {
571
- ((ChannelInboundHandler ) handler0 ).channelRegistered (handler0Ctx );
572
- } else {
573
- handler0Ctx .fireChannelRegistered ();
574
- }
575
- }
576
- } else {
577
- super .channelRegistered (ctx );
578
- }
579
- }
580
-
581
- /**
582
- * Do not rely on channel handlers to propagate exceptions to us.
583
- * {@link NettyClientHandler} is an example of a class that does not propagate exceptions.
584
- * Add a listener to the connect future directly and do appropriate error handling.
585
- */
586
- @ Override
587
- public void connect (final ChannelHandlerContext ctx , SocketAddress remoteAddress ,
588
- SocketAddress localAddress , ChannelPromise promise ) throws Exception {
589
- super .connect (ctx , remoteAddress , localAddress , promise );
590
- promise .addListener (new ChannelFutureListener () {
591
- @ Override
592
- public void operationComplete (ChannelFuture future ) throws Exception {
593
- if (!future .isSuccess ()) {
594
- fail (ctx , future .cause ());
595
- }
596
- }
597
- });
598
- }
599
-
600
- /**
601
- * If we encounter an exception, then notify all buffered writes that we failed.
602
- */
603
- @ Override
604
- public void exceptionCaught (ChannelHandlerContext ctx , Throwable cause ) throws Exception {
605
- fail (ctx , cause );
606
- }
607
-
608
- /**
609
- * If this channel becomes inactive, then notify all buffered writes that we failed.
610
- */
611
- @ Override
612
- public void channelInactive (ChannelHandlerContext ctx ) throws Exception {
613
- fail (ctx , unavailableException ("Connection broken while performing protocol negotiation" ));
614
- super .channelInactive (ctx );
615
- }
616
-
617
- /**
618
- * Buffers the write until either {@link #writeBufferedAndRemove(ChannelHandlerContext)} is
619
- * called, or we have somehow failed. If we have already failed in the past, then the write
620
- * will fail immediately.
621
- */
622
- @ Override
623
- @ SuppressWarnings ("FutureReturnValueIgnored" )
624
- public void write (ChannelHandlerContext ctx , Object msg , ChannelPromise promise )
625
- throws Exception {
626
- /**
627
- * This check handles a race condition between Channel.write (in the calling thread) and the
628
- * removal of this handler (in the event loop thread).
629
- * The problem occurs in e.g. this sequence:
630
- * 1) [caller thread] The write method identifies the context for this handler
631
- * 2) [event loop] This handler removes itself from the pipeline
632
- * 3) [caller thread] The write method delegates to the invoker to call the write method in
633
- * the event loop thread. When this happens, we identify that this handler has been
634
- * removed with "bufferedWrites == null".
635
- */
636
- if (failCause != null ) {
637
- promise .setFailure (failCause );
638
- ReferenceCountUtil .release (msg );
639
- } else if (bufferedWrites == null ) {
640
- super .write (ctx , msg , promise );
641
- } else {
642
- bufferedWrites .add (new ChannelWrite (msg , promise ));
643
- }
644
- }
645
-
646
- /**
647
- * Calls to this method will not trigger an immediate flush. The flush will be deferred until
648
- * {@link #writeBufferedAndRemove(ChannelHandlerContext)}.
649
- */
650
- @ Override
651
- public void flush (ChannelHandlerContext ctx ) {
652
- /**
653
- * Swallowing any flushes is not only an optimization but also required
654
- * for the SslHandler to work correctly. If the SslHandler receives multiple
655
- * flushes while the handshake is still ongoing, then the handshake "randomly"
656
- * times out. Not sure at this point why this is happening. Doing a single flush
657
- * seems to work but multiple flushes don't ...
658
- */
659
- if (bufferedWrites == null ) {
660
- ctx .flush ();
661
- } else {
662
- flushRequested = true ;
663
- }
664
- }
665
-
666
- /**
667
- * If we are still performing protocol negotiation, then this will propagate failures to all
668
- * buffered writes.
669
- */
670
- @ Override
671
- public void close (ChannelHandlerContext ctx , ChannelPromise future ) throws Exception {
672
- if (ctx .channel ().isActive ()) { // This may be a notification that the socket was closed
673
- fail (ctx , unavailableException ("Channel closed while performing protocol negotiation" ));
674
- }
675
- super .close (ctx , future );
676
- }
677
-
678
- /**
679
- * Propagate failures to all buffered writes.
680
- */
681
- @ SuppressWarnings ("FutureReturnValueIgnored" )
682
- protected final void fail (ChannelHandlerContext ctx , Throwable cause ) {
683
- if (failCause == null ) {
684
- failCause = cause ;
685
- }
686
- if (bufferedWrites != null ) {
687
- while (!bufferedWrites .isEmpty ()) {
688
- ChannelWrite write = bufferedWrites .poll ();
689
- write .promise .setFailure (cause );
690
- ReferenceCountUtil .release (write .msg );
691
- }
692
- bufferedWrites = null ;
693
- }
694
-
695
- ctx .fireExceptionCaught (cause );
696
- }
697
-
698
- @ SuppressWarnings ("FutureReturnValueIgnored" )
699
- protected final void writeBufferedAndRemove (ChannelHandlerContext ctx ) {
700
- if (!ctx .channel ().isActive () || writing ) {
701
- return ;
702
- }
703
- // Make sure that method can't be reentered, so that the ordering
704
- // in the queue can't be messed up.
705
- writing = true ;
706
- while (!bufferedWrites .isEmpty ()) {
707
- ChannelWrite write = bufferedWrites .poll ();
708
- ctx .write (write .msg , write .promise );
709
- }
710
- assert bufferedWrites .isEmpty ();
711
- bufferedWrites = null ;
712
- if (flushRequested ) {
713
- ctx .flush ();
714
- }
715
- // Removal has to happen last as the above writes will likely trigger
716
- // new writes that have to be added to the end of queue in order to not
717
- // mess up the ordering.
718
- ctx .pipeline ().remove (this );
719
- }
720
-
721
- private static class ChannelWrite {
722
- Object msg ;
723
- ChannelPromise promise ;
724
-
725
- ChannelWrite (Object msg , ChannelPromise promise ) {
726
- this .msg = msg ;
727
- this .promise = promise ;
728
- }
729
- }
730
- }
731
-
732
530
/**
733
531
* Adapts a {@link ProtocolNegotiationEvent} to the {@link GrpcHttp2ConnectionHandler}.
734
532
*/
0 commit comments