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 }