1 module hunt.http.server.Http1ServerConnection;
2 
3 import hunt.http.server.HttpServerConnection;
4 import hunt.http.server.Http1ServerRequestHandler;
5 import hunt.http.server.Http1ServerTunnelConnection;
6 import hunt.http.server.Http2ServerConnection;
7 import hunt.http.server.ServerSessionListener;
8 import hunt.http.server.WebSocketHandler;
9 
10 import hunt.http.codec.http.decode.HttpParser;
11 import hunt.http.codec.http.decode.SettingsBodyParser;
12 import hunt.http.codec.http.encode.HttpGenerator;
13 import hunt.http.codec.http.encode.PredefinedHttp1Response;
14 
15 import hunt.http.codec.websocket.model.AcceptHash;
16 import hunt.http.codec.websocket.model.ExtensionConfig;
17 import hunt.http.codec.websocket.stream.WebSocketConnectionImpl;
18 import hunt.http.codec.websocket.stream.IOState;
19 
20 import hunt.http.codec.http.frame.HeadersFrame;
21 import hunt.http.codec.http.frame.PrefaceFrame;
22 import hunt.http.codec.http.frame.SettingsFrame;
23 
24 import hunt.http.codec.http.model;
25 import hunt.http.codec.http.stream;
26 
27 // import hunt.http.util.Completable;
28 
29 import hunt.http.HttpConnection;
30 import hunt.http.HttpField;
31 import hunt.http.HttpFields;
32 import hunt.http.HttpHeader;
33 import hunt.http.HttpMethod;
34 import hunt.http.HttpOptions;
35 import hunt.http.HttpOutputStream;
36 import hunt.http.HttpRequest;
37 import hunt.http.HttpResponse;
38 import hunt.http.HttpStatus;
39 import hunt.http.HttpVersion;
40 
41 import hunt.http.WebSocketCommon;
42 import hunt.http.WebSocketConnection;
43 import hunt.http.WebSocketFrame;
44 
45 import hunt.net.secure.SecureSession;
46 import hunt.net.Connection;
47 
48 import hunt.io.BufferUtils;
49 import hunt.io.ByteBuffer;
50 import hunt.collection.List;
51 
52 import hunt.stream;
53 import hunt.Assert;
54 import hunt.Exceptions;
55 import hunt.concurrency.Promise;
56 import hunt.concurrency.FuturePromise;
57 import hunt.text.Common;
58 import hunt.util.ConverterUtils;
59 
60 import hunt.logging;
61 
62 import std.algorithm;
63 import std.array;
64 import std.base64;
65 
66 
67 /**
68 */
69 class Http1ServerConnection : AbstractHttp1Connection, HttpServerConnection {
70 
71     private WebSocketHandler webSocketHandler;
72     private ServerSessionListener serverSessionListener;
73     private Http1ServerRequestHandler serverRequestHandler;
74     private shared bool upgradeHttp2Complete = false;
75     private shared bool upgradeWebSocketComplete = false;
76     private Promise!HttpTunnelConnection tunnelConnectionPromise;
77 
78     this(HttpOptions config, Connection tcpSession, Http1ServerRequestHandler requestHandler,
79             ServerSessionListener serverSessionListener, 
80             WebSocketHandler webSocketHandler) {
81         
82         version (HUNT_DEBUG) 
83             trace("Initializing Http1ServerConnection ...");
84         super(config, tcpSession, requestHandler, null);
85         requestHandler.connection = this;
86         this.serverSessionListener = serverSessionListener;
87         this.serverRequestHandler = requestHandler;
88         this.webSocketHandler = webSocketHandler;
89     }
90 
91     override protected HttpParser initHttpParser(HttpOptions config,
92             HttpRequestParsingHandler requestHandler, HttpResponseParsingHandler responseHandler) {
93         return new HttpParser(requestHandler, config.getMaxRequestHeadLength());
94     }
95 
96     override HttpConnectionType getConnectionType() {
97         return super.getConnectionType();
98     }
99 
100     // override bool isSecured() {
101     //     return super.isSecured();
102     // }
103 
104     HttpParser getParser() {
105         return parser;
106     }
107 
108     HttpOptions getHttpOptions() {
109         return config;
110     }
111 
112     HttpRequest getRequest() {
113         return serverRequestHandler.request;
114     }
115 
116     HttpResponse getResponse() {
117         return serverRequestHandler.response;
118     }
119 
120     void response100Continue() {
121         serverRequestHandler.outputStream.response100Continue();
122     }
123 
124     private void responseH2c() {
125         serverRequestHandler.outputStream.responseH2c();
126     }
127 
128     override void upgradeHttpTunnel(Promise!HttpTunnelConnection tunnelConnectionPromise) {
129         this.tunnelConnectionPromise = tunnelConnectionPromise;
130     }
131 
132     override FuturePromise!HttpTunnelConnection upgradeHttpTunnel() {
133         auto c = new FuturePromise!HttpTunnelConnection();
134         tunnelConnectionPromise = c;
135         return c;
136     }
137 
138     Http1ServerTunnelConnection createHttpTunnel() {
139         if (tunnelConnectionPromise !is null) {
140             Http1ServerTunnelConnection tunnelConnection = new Http1ServerTunnelConnection(
141                     _tcpSession, _httpVersion);
142             _tcpSession.setAttribute(HttpConnection.NAME, tunnelConnection);
143             tunnelConnectionPromise.succeeded(tunnelConnection);
144             return tunnelConnection;
145         } else {
146             return null;
147         }
148     }
149 
150     bool directUpgradeHttp2(HttpRequest request) {
151         version (HUNT_DEBUG) info("Upgrading to Http2");
152 
153         if (HttpMethod.PRI.isSame(request.getMethod())) {
154             Http2ServerConnection http2ServerConnection = new Http2ServerConnection(config,
155                     _tcpSession, serverSessionListener);
156             _tcpSession.setAttribute(HttpConnection.NAME, http2ServerConnection);
157             http2ServerConnection.getParser().directUpgrade();
158             upgradeHttp2Complete = true;
159             return true;
160         } else {
161             return false;
162         }
163     }
164 
165     bool upgradeProtocol(HttpRequest request, HttpResponse response,
166             HttpOutputStream output, HttpConnection connection) {
167         version (HUNT_HTTP_DEBUG) info("try upgrading protocol ...");                
168         if(request is null)
169             return false;
170         switch (ProtocolHelper.from(request)) {
171         case Protocol.H2: {
172                 if (upgradeHttp2Complete) {
173                     throw new IllegalStateException("The connection has been upgraded HTTP2");
174                 }
175 
176                 HttpField settingsField = request.getFields().getField(HttpHeader.HTTP2_SETTINGS);
177                 assert(settingsField !is null, "The http2 setting field must be not null.");
178 
179                 // byte[] settings = Base64Utils.decodeFromUrlSafeString(settingsField.getValue());
180                 byte[] settings = cast(byte[]) Base64.decode(settingsField.getValue());
181                 version (HUNT_HTTP_DEBUG) {
182                     tracef("the server received settings %s", ConverterUtils.toHexString(settings));
183                 }
184 
185                 SettingsFrame settingsFrame = SettingsBodyParser.parseBody(
186                         BufferUtils.toBuffer(settings));
187                 if (settingsFrame is null) {
188                     throw new BadMessageException("settings frame parsing error");
189                 } else {
190                     responseH2c();
191 
192                     Http2ServerConnection http2ServerConnection = new Http2ServerConnection(config,
193                             _tcpSession,  serverSessionListener);
194                     _tcpSession.setAttribute(HttpConnection.NAME, http2ServerConnection);
195                     upgradeHttp2Complete = true;
196                     http2ServerConnection.getParser().standardUpgrade();
197 
198                     serverSessionListener.onAccept(http2ServerConnection.getHttp2Session());
199                     SessionSPI sessionSPI = http2ServerConnection.getSessionSPI();
200 
201                     sessionSPI.onFrame(new PrefaceFrame());
202                     sessionSPI.onFrame(settingsFrame);
203                     sessionSPI.onFrame(new HeadersFrame(1, request, null, true));
204                 }
205                 return true;
206             }
207 
208         case Protocol.WEB_SOCKET: {
209                 if (upgradeWebSocketComplete) {
210                     throw new IllegalStateException("The connection has been upgraded WebSocket");
211                 }
212 
213                 assert(HttpMethod.GET.isSame(request.getMethod()),
214                         "The method of the request MUST be GET in the websocket handshake.");
215                 assert(request.getHttpVersion() == HttpVersion.HTTP_1_1,
216                         "The http version MUST be HTTP/1.1");
217 
218                 bool accept = webSocketHandler.acceptUpgrade(request, response, output, connection);
219                 if (!accept) {
220                     return false;
221                 }
222 
223                 string key = request.getFields().get("Sec-WebSocket-Key");
224                 assert(!key.empty(), "Missing request header 'Sec-WebSocket-Key'");
225 
226                 // dfmt off
227                 WebSocketConnectionImpl webSocketConnection = new WebSocketConnectionImpl(
228                          _tcpSession,
229                         null, webSocketHandler.getWebSocketPolicy(),
230                         request, response, config);
231 
232                 webSocketConnection.setNextIncomingFrames(new class IncomingFrames {
233 
234                     void incomingError(Exception t) {
235                         version(HUNT_DEBUG) warning(t.msg);
236                         version(HUNT_HTTP_DEBUG_MORE) warning(t);
237                         webSocketHandler.onError(t, webSocketConnection);
238                     }
239 
240                     void incomingFrame(WebSocketFrame frame) {
241                         version(HUNT_HTTP_DEBUG_MORE) trace(BufferUtils.toDetailString(frame.getPayload()));
242                         webSocketHandler.onFrame(frame, webSocketConnection);
243                     }
244                 });
245                 
246                 IOState ioState = webSocketConnection.getIOState();
247                 ioState.addListener((WebSocketConnectionState state) {
248                     version(HUNT_HTTP_DEBUG) info("State: ", state);
249                     if(state == WebSocketConnectionState.CLOSED) {
250                         webSocketHandler.onClosed(webSocketConnection);
251                     }
252                 });
253 
254                 ioState.onConnected();
255                 ioState.onOpened();
256 
257 // dfmt on
258                 ExtensionConfig[] negotiatedExtensions = webSocketConnection.getExtensionNegotiator()
259                     .negotiate(request);
260 
261                 response.setStatus(HttpStatus.SWITCHING_PROTOCOLS_101);
262                 response.getFields().put(HttpHeader.UPGRADE, "WebSocket");
263                 response.getFields().add(HttpHeader.CONNECTION.asString(), "Upgrade");
264                 response.getFields().add(HttpHeader.SEC_WEBSOCKET_ACCEPT.asString(),
265                         AcceptHash.hashKey(key));
266 
267                 if (!negotiatedExtensions.empty) {
268                     auto r = negotiatedExtensions.filter!(
269                             e => e.getName() == ("permessage-deflate"));
270                     if (!r.empty) {
271                         r.front.getParameters().clear();
272                     }
273                     response.getFields().add(HttpHeader.SEC_WEBSOCKET_EXTENSIONS.asString(),
274                             ExtensionConfig.toHeaderValue(negotiatedExtensions));
275                 }
276 
277                 IOUtils.close(output);
278                 // output.close();
279                 _tcpSession.setAttribute(HttpConnection.NAME, webSocketConnection);
280                 upgradeWebSocketComplete = true;
281                 webSocketHandler.onOpen(webSocketConnection);
282                 return true;
283             }
284         default:
285             return false;
286         }
287     }
288 
289     bool getUpgradeHttp2Complete() {
290         return upgradeHttp2Complete;
291     }
292 
293     bool getUpgradeWebSocketComplete() {
294         return upgradeWebSocketComplete;
295     }
296 
297     Promise!HttpTunnelConnection getTunnelConnectionPromise() {
298         return tunnelConnectionPromise;
299     }
300 }
301 
302 /**
303 */
304 class Http1ServerResponseOutputStream : AbstractHttp1OutputStream {
305 
306     private Http1ServerConnection connection;
307     private HttpGenerator httpGenerator;
308 
309     this(HttpResponse response, Http1ServerConnection connection) {
310         super(response, false);
311         this.connection = connection;
312         httpGenerator = new HttpGenerator(true, false);
313     }
314 
315     Http1ServerConnection getHttp1ServerConnection() {
316         return connection;
317     }
318 
319     void responseH2c() {
320         getSession().encode(BufferUtils.toBuffer(PredefinedHttp1Response.H2C_BYTES));
321     }
322 
323     void response100Continue() {
324         getSession().encode(BufferUtils.toBuffer(PredefinedHttp1Response.CONTINUE_100_BYTES));
325     }
326 
327     override protected void generateHttpMessageSuccessfully() {
328         version (HUNT_HTTP_DEBUG) {
329             tracef("server session %s generates the HTTP message completely",
330                     connection.getId());
331         }
332 
333         HttpResponse response = connection.getResponse();
334         HttpRequest request = connection.getRequest();
335 
336         string requestConnectionValue = "close";
337         HttpVersion ver = HttpVersion.HTTP_1_1;
338         if(request !is null) {
339             requestConnectionValue = request.getFields().get(HttpHeader.CONNECTION);
340             ver = request.getHttpVersion();
341         }
342 
343         assert(response !is null, "The response can't be null");
344 
345         HttpFields responseFields = response.getFields();
346 
347         string responseConnectionValue = "";
348         if(responseFields !is null) {
349             responseFields.get(HttpHeader.CONNECTION);
350         } else {
351             warning("The HttpFields is null");
352         }
353 
354         if (ver == HttpVersion.HTTP_1_1) { // the persistent connection is default in HTTP 1.1
355             if ("close".equalsIgnoreCase(requestConnectionValue)
356                     || "close".equalsIgnoreCase(responseConnectionValue)) {
357                 connection.close();
358             } else {
359                 version (HUNT_HTTP_DEBUG) {
360                     infof("the server %s connection %d is persistent",
361                             response.getHttpVersion(), connection.getId());
362                 }
363             }
364         } else if (ver == HttpVersion.HTTP_1_0) {
365             if ("keep-alive".equalsIgnoreCase(requestConnectionValue)
366                     && "keep-alive".equalsIgnoreCase(responseConnectionValue)) {
367                 tracef("the server %s connection %s is persistent",
368                         response.getHttpVersion(), connection.getId());
369             } else {
370                 connection.close();
371             }
372         } else {
373             throw new IllegalStateException(
374                     "server response does not support the http version " ~ connection.getHttpVersion()
375                     .toString());
376         }
377     }
378 
379     override protected void generateHttpMessageExceptionally(HttpGenerator.Result actualResult,
380             HttpGenerator.State actualState, HttpGenerator.Result expectedResult,
381             HttpGenerator.State expectedState) {
382         errorf("http1 generator error, actual: [%s, %s], expected: [%s, %s]",
383                 actualResult, actualState, expectedResult, expectedState);
384         throw new IllegalStateException("server generates http message exception.");
385     }
386 
387     override protected ByteBuffer getHeaderByteBuffer() {
388         return BufferUtils.allocate(connection.getHttpOptions().getMaxResponseHeadLength());
389     }
390 
391     override protected ByteBuffer getTrailerByteBuffer() {
392         return BufferUtils.allocate(connection.getHttpOptions()
393                 .getMaxResponseTrailerLength());
394     }
395 
396     override protected Connection getSession() {
397         return connection.getTcpConnection();
398     }
399 
400     override protected HttpGenerator getHttpGenerator() {
401         return httpGenerator;
402     }
403 
404 }