private static async Task <ArraySegment <byte> > PeekAsyncAwaited(this SocketInput input) { while (true) { await input; var fin = input.CheckFinOrThrow(); var begin = input.ConsumingStart(); var segment = begin.PeekArraySegment(); input.ConsumingComplete(begin, begin); if (segment.Count != 0 || fin) { return(segment); } } }
public static ValueTask <ArraySegment <byte> > PeekAsync(this SocketInput input) { while (input.IsCompleted) { var fin = input.CheckFinOrThrow(); var begin = input.ConsumingStart(); var segment = begin.PeekArraySegment(); input.ConsumingComplete(begin, begin); if (segment.Count != 0 || fin) { return(new ValueTask <ArraySegment <byte> >(segment)); } } return(new ValueTask <ArraySegment <byte> >(input.PeekAsyncAwaited())); }
private static async Task <int> ReadAsyncAwaited(this SocketInput input, byte[] buffer, int offset, int count) { while (true) { await input; var fin = input.CheckFinOrThrow(); var begin = input.ConsumingStart(); int actual; var end = begin.CopyTo(buffer, offset, count, out actual); input.ConsumingComplete(end, end); if (actual != 0 || fin) { return(actual); } } }
public static ValueTask <int> ReadAsync(this SocketInput input, byte[] buffer, int offset, int count) { while (input.IsCompleted) { var fin = input.CheckFinOrThrow(); var begin = input.ConsumingStart(); int actual; var end = begin.CopyTo(buffer, offset, count, out actual); input.ConsumingComplete(end, end); if (actual != 0 || fin) { return(new ValueTask <int>(actual)); } } return(new ValueTask <int>(input.ReadAsyncAwaited(buffer, offset, count))); }
private async Task <ArraySegment <byte> > PeekStateMachineAsync() { while (_mode < Mode.Trailer) { while (_mode == Mode.Prefix) { var fin = _input.CheckFinOrThrow(); ParseChunkedPrefix(); if (_mode != Mode.Prefix) { break; } else if (fin) { _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete); } await _input; } while (_mode == Mode.Extension) { var fin = _input.CheckFinOrThrow(); ParseExtension(); if (_mode != Mode.Extension) { break; } else if (fin) { _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete); } await _input; } while (_mode == Mode.Data) { var fin = _input.CheckFinOrThrow(); var segment = PeekChunkedData(); if (segment.Count != 0) { return(segment); } else if (_mode != Mode.Data) { break; } else if (fin) { _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete); } await _input; } while (_mode == Mode.Suffix) { var fin = _input.CheckFinOrThrow(); ParseChunkedSuffix(); if (_mode != Mode.Suffix) { break; } else if (fin) { _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete); } await _input; } } // Chunks finished, parse trailers while (_mode == Mode.Trailer) { var fin = _input.CheckFinOrThrow(); ParseChunkedTrailer(); if (_mode != Mode.Trailer) { break; } else if (fin) { _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete); } await _input; } if (_mode == Mode.TrailerHeaders) { while (!_context.TakeMessageHeaders(_input, _requestHeaders)) { if (_input.CheckFinOrThrow()) { if (_context.TakeMessageHeaders(_input, _requestHeaders)) { break; } else { _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete); } } await _input; } _mode = Mode.Complete; } return(default(ArraySegment <byte>)); }
/// <summary> /// Primary loop which consumes socket input, parses it for protocol framing, and invokes the /// application delegate for as long as the socket is intended to remain open. /// The resulting Task from this loop is preserved in a field which is used when the server needs /// to drain and close all currently active connections. /// </summary> public override async Task RequestProcessingAsync() { var requestLineStatus = RequestLineStatus.Empty; try { while (!_requestProcessingStopping) { ConnectionControl.SetTimeout(_keepAliveMilliseconds, TimeoutAction.CloseConnection); while (!_requestProcessingStopping) { requestLineStatus = TakeStartLine(SocketInput); if (requestLineStatus == RequestLineStatus.Done) { break; } if (SocketInput.CheckFinOrThrow()) { // We need to attempt to consume start lines and headers even after // SocketInput.RemoteIntakeFin is set to true to ensure we don't close a // connection without giving the application a chance to respond to a request // sent immediately before the a FIN from the client. requestLineStatus = TakeStartLine(SocketInput); if (requestLineStatus == RequestLineStatus.Empty) { return; } if (requestLineStatus != RequestLineStatus.Done) { RejectRequest(RequestRejectionReason.InvalidRequestLine, requestLineStatus.ToString()); } break; } await SocketInput; } InitializeHeaders(); while (!_requestProcessingStopping && !TakeMessageHeaders(SocketInput, FrameRequestHeaders)) { if (SocketInput.CheckFinOrThrow()) { // We need to attempt to consume start lines and headers even after // SocketInput.RemoteIntakeFin is set to true to ensure we don't close a // connection without giving the application a chance to respond to a request // sent immediately before the a FIN from the client. if (!TakeMessageHeaders(SocketInput, FrameRequestHeaders)) { RejectRequest(RequestRejectionReason.MalformedRequestInvalidHeaders); } break; } await SocketInput; } if (!_requestProcessingStopping) { var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this); _keepAlive = messageBody.RequestKeepAlive; _upgrade = messageBody.RequestUpgrade; InitializeStreams(messageBody); var context = _application.CreateContext(this); try { try { await _application.ProcessRequestAsync(context).ConfigureAwait(false); if (Volatile.Read(ref _requestAborted) == 0) { VerifyResponseContentLength(); } } catch (Exception ex) { ReportApplicationError(ex); if (ex is BadHttpRequestException) { throw; } } finally { // Trigger OnStarting if it hasn't been called yet and the app hasn't // already failed. If an OnStarting callback throws we can go through // our normal error handling in ProduceEnd. // https://github.com/aspnet/KestrelHttpServer/issues/43 if (!HasResponseStarted && _applicationException == null && _onStarting != null) { await FireOnStarting(); } PauseStreams(); if (_onCompleted != null) { await FireOnCompleted(); } } // If _requestAbort is set, the connection has already been closed. if (Volatile.Read(ref _requestAborted) == 0) { ResumeStreams(); if (HasResponseStarted) { // If the response has already started, call ProduceEnd() before // consuming the rest of the request body to prevent // delaying clients waiting for the chunk terminator: // // https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663 // // ProduceEnd() must be called before _application.DisposeContext(), to ensure // HttpContext.Response.StatusCode is correctly set when // IHttpContextFactory.Dispose(HttpContext) is called. await ProduceEnd(); } if (_keepAlive) { // Finish reading the request body in case the app did not. await messageBody.Consume(); } if (!HasResponseStarted) { await ProduceEnd(); } } else if (!HasResponseStarted) { // If the request was aborted and no response was sent, there's no // meaningful status code to log. StatusCode = 0; } } catch (BadHttpRequestException ex) { // Handle BadHttpRequestException thrown during app execution or remaining message body consumption. // This has to be caught here so StatusCode is set properly before disposing the HttpContext // (DisposeContext logs StatusCode). SetBadRequestState(ex); } finally { _application.DisposeContext(context, _applicationException); // StopStreams should be called before the end of the "if (!_requestProcessingStopping)" block // to ensure InitializeStreams has been called. StopStreams(); } } if (!_keepAlive) { // End the connection for non keep alive as data incoming may have been thrown off return; } // Don't reset frame state if we're exiting the loop. This avoids losing request rejection // information (for 4xx response), and prevents ObjectDisposedException on HTTPS (ODEs // will be thrown if PrepareRequest is not null and references objects disposed on connection // close - see https://github.com/aspnet/KestrelHttpServer/issues/1103#issuecomment-250237677). if (!_requestProcessingStopping) { Reset(); } } } catch (BadHttpRequestException ex) { // Handle BadHttpRequestException thrown during request line or header parsing. // SetBadRequestState logs the error. SetBadRequestState(ex); } catch (IOException ex) when(ex.InnerException is UvException) { // Don't log ECONNRESET errors made between requests. Browsers like IE will reset connections regularly. if (requestLineStatus != RequestLineStatus.Empty || ((UvException)ex.InnerException).StatusCode != Constants.ECONNRESET) { Log.RequestProcessingError(ConnectionId, ex); } } catch (Exception ex) { Log.LogWarning(0, ex, "Connection processing ended abnormally"); } finally { try { // If _requestAborted is set, the connection has already been closed. if (Volatile.Read(ref _requestAborted) == 0) { await TryProduceInvalidRequestResponse(); ConnectionControl.End(ProduceEndType.SocketShutdown); } } catch (Exception ex) { Log.LogWarning(0, ex, "Connection shutdown abnormally"); } } }