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 }