1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  package io.netty5.handler.codec.http2;
16  
17  import io.netty5.buffer.api.Buffer;
18  import io.netty5.util.Send;
19  import io.netty5.channel.ChannelHandler;
20  import io.netty5.channel.ChannelHandlerContext;
21  import io.netty5.handler.codec.base64.Base64;
22  import io.netty5.handler.codec.http.FullHttpResponse;
23  import io.netty5.handler.codec.http.HttpClientUpgradeHandler;
24  import io.netty5.handler.codec.http.HttpRequest;
25  import io.netty5.util.collection.CharObjectMap;
26  import io.netty5.util.internal.UnstableApi;
27  
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.List;
31  
32  import static io.netty5.handler.codec.base64.Base64Dialect.URL_SAFE;
33  import static io.netty5.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME;
34  import static io.netty5.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_SETTINGS_HEADER;
35  import static io.netty5.handler.codec.http2.Http2CodecUtil.SETTING_ENTRY_LENGTH;
36  import static io.netty5.util.CharsetUtil.UTF_8;
37  import static java.util.Objects.requireNonNull;
38  
39  
40  
41  
42  @UnstableApi
43  public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.UpgradeCodec {
44  
45      private static final List<CharSequence> UPGRADE_HEADERS = Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER);
46  
47      private final String handlerName;
48      private final Http2ConnectionHandler connectionHandler;
49      private final ChannelHandler upgradeToHandler;
50      private final ChannelHandler http2MultiplexHandler;
51  
52      public Http2ClientUpgradeCodec(Http2FrameCodec frameCodec, ChannelHandler upgradeToHandler) {
53          this(null, frameCodec, upgradeToHandler);
54      }
55  
56      public Http2ClientUpgradeCodec(String handlerName, Http2FrameCodec frameCodec, ChannelHandler upgradeToHandler) {
57          this(handlerName, frameCodec, upgradeToHandler, null);
58      }
59  
60      
61  
62  
63  
64  
65  
66      public Http2ClientUpgradeCodec(Http2ConnectionHandler connectionHandler) {
67          this((String) null, connectionHandler);
68      }
69  
70      
71  
72  
73  
74  
75  
76  
77      public Http2ClientUpgradeCodec(Http2ConnectionHandler connectionHandler,
78          Http2MultiplexHandler http2MultiplexHandler) {
79          this(null, connectionHandler, http2MultiplexHandler);
80      }
81  
82      
83  
84  
85  
86  
87  
88  
89      public Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler) {
90          this(handlerName, connectionHandler, connectionHandler, null);
91      }
92  
93      
94  
95  
96  
97  
98  
99  
100     public Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler,
101         Http2MultiplexHandler http2MultiplexHandler) {
102         this(handlerName, connectionHandler, connectionHandler, http2MultiplexHandler);
103     }
104 
105     private Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler, ChannelHandler
106         upgradeToHandler, Http2MultiplexHandler http2MultiplexHandler) {
107         this.handlerName = handlerName;
108         this.connectionHandler = requireNonNull(connectionHandler, "connectionHandler");
109         this.upgradeToHandler = requireNonNull(upgradeToHandler, "upgradeToHandler");
110         this.http2MultiplexHandler = http2MultiplexHandler;
111     }
112 
113     @Override
114     public CharSequence protocol() {
115         return HTTP_UPGRADE_PROTOCOL_NAME;
116     }
117 
118     @Override
119     public Collection<CharSequence> setUpgradeHeaders(ChannelHandlerContext ctx,
120         HttpRequest upgradeRequest) {
121         CharSequence settingsValue = getSettingsHeaderValue(ctx);
122         upgradeRequest.headers().set(HTTP_UPGRADE_SETTINGS_HEADER, settingsValue);
123         return UPGRADE_HEADERS;
124     }
125 
126     @Override
127     public void upgradeTo(ChannelHandlerContext ctx, Send<FullHttpResponse> upgradeResponse)
128         throws Exception {
129         upgradeResponse.close();
130         try {
131             
132             ctx.pipeline().addAfter(ctx.name(), handlerName, upgradeToHandler);
133 
134             
135             
136             if (http2MultiplexHandler != null) {
137                 final String name = ctx.pipeline().context(connectionHandler).name();
138                 ctx.pipeline().addAfter(name, null, http2MultiplexHandler);
139             }
140 
141             
142             connectionHandler.onHttpClientUpgrade();
143         } catch (Http2Exception e) {
144             ctx.fireChannelExceptionCaught(e);
145             ctx.close();
146         }
147     }
148 
149     
150 
151 
152 
153     private CharSequence getSettingsHeaderValue(ChannelHandlerContext ctx) {
154         
155         Http2Settings settings = connectionHandler.decoder().localSettings();
156         int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
157         
158         try (Buffer buf = ctx.bufferAllocator().allocate(payloadLength)) {
159             for (CharObjectMap.PrimitiveEntry<Long> entry : settings.entries()) {
160                 buf.writeChar(entry.key());
161                 buf.writeInt(entry.value().intValue());
162             }
163 
164             
165            try (Buffer encodedBuf = Base64.encode(buf, URL_SAFE)) {
166                return encodedBuf.toString(UTF_8);
167            }
168         }
169     }
170 }