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