/// <summary> /// Created only by HttpServer /// </summary> internal HttpContext(TcpClient client, HttpReader reader, HttpWriter writer, bool isSecure) { mTcpClient = client; mReader = reader; mWriter = writer; IsSecure = isSecure; }
async void TryProcessRequestsAsync(TcpClient client, X509Certificate certificate) { int concurrentRequests = Interlocked.Add(ref mConcurrentRequests, 1); Thread.VolatileWrite(ref mMaxConcurrentRequests, Math.Max(concurrentRequests, Thread.VolatileRead(ref mMaxConcurrentRequests))); using (client) { HttpContext context = null; try { // Setup stream or SSL stream client.NoDelay = true; var tcpStream = (Stream)client.GetStream(); bool isSecure = false; if (certificate != null) { // Wrap stream in an SSL stream, and authenticate var sslStream = new SslStream(tcpStream, false); await sslStream.AuthenticateAsServerAsync(certificate); tcpStream = sslStream; isSecure = true; } // Process requests on this possibly persistent TCP stream var reader = new HttpReader(tcpStream, mCancellationToken, Sync); var writer = new HttpWriter(tcpStream, mCancellationToken, Sync); context = new HttpContext(client, reader, writer, isSecure); await ProcessRequests(context, reader, writer).ConfigureAwait(false); } catch (Exception ex) { try { await ProcessExceptionAsync(context, ex); } catch (Exception doubleFaultEx) { Log.Write("DOUBLE FAULT EXCEPTION", doubleFaultEx); } } } Interlocked.Add(ref mConcurrentRequests, -1); }
/// <summary> /// Only called by HttpContext /// </summary> internal WebSocket(HttpContext context, string protocol) { mState = WebSocketState.Open; mContext = context; var request = context.Request; var requestProtocol = request.Headers["sec-websocket-protocol"]; // TBD: Split "," separated list if (protocol != requestProtocol) { throw new HttpException(400, "Invalid websocket protocol. Client requested '" + requestProtocol + "', server accepted '" + protocol + "'"); } // RFC 6455, 4.2.1 and 4.2.2 string key = request.Headers["sec-websocket-key"]; if (key == "") { throw new HttpException(400, "Websocket key not sent by client"); } string keyHash; using (var sha = SHA1.Create()) keyHash = Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))); var response = mContext.Response; response.StatusCode = 101; response.StatusMessage = "Switching Protocols"; response.Connection = "Upgrade"; response.Headers["upgrade"] = "websocket"; response.Headers["Sec-WebSocket-Accept"] = keyHash; response.Headers["Sec-WebSocket-Protocol"] = protocol; request.ContentLength = long.MaxValue; // Stream never ends mWriter = mContext.GetWriter(long.MaxValue); mReader = mContext.GetReader(); }
/// <summary> /// Process requests as long as there is not an error /// </summary> async Task ProcessRequests(HttpContext context, HttpReader reader, HttpWriter writer) { int persistentConnections = 0; do { persistentConnections++; try { // Read header reader.ReadTimeout = HeaderTimeout; var buffer = await reader.ReadHttpHeaderAsyncInternal(); reader.PositionInternal = 0; reader.LengthInternal = HttpContext.HTTP_HEADER_MAX_SIZE; if (buffer.Count == 0) { return; // Connection closed } // Parse header context.ResetRequestInternal(HttpRequest.Parse(buffer), new HttpResponse()); } catch (Exception ex) { Log.Write("HTTP header exception: " + ex.GetType() + " - " + ex.Message); return; // Close connection } // Handle body reader.ReadTimeout = BodyTimeout; writer.WriteTimeout = BodyTimeout; try { // Process HTTP request var handler = HttpHandler; if (handler == null) { throw new HttpException(503, "HTTP request handler not installed"); } await handler(context).ConfigureAwait(false); } catch (HttpException ex) when(ex.KeepConnectionOpen && !context.Response.HeaderSent) { // Keep the persistent connection open only if it's OK to do so. await ProcessExceptionAsync(context, ex); } await writer.FlushAsync(); // Any of these problems will terminate a persistent connection var response = context.Response; var request = context.Request; var isWebsocket = request.IsWebSocketRequest; if (!response.HeaderSent) { throw new HttpException(500, "Request handler did not send a response for: " + context.Request.Path); } if (!isWebsocket && reader.Position != request.ContentLength && request.ContentLength >= 0) { throw new HttpException(500, "Request handler did not read correct number of bytes: " + request.Path); } if (!isWebsocket && writer.Position != response.ContentLength) { throw new HttpException(500, "Request handler did not write correct number of bytes: " + request.Path); } } while (!context.Request.IsWebSocketRequest && context.Response.Connection == "keep-alive"); }