public async Task CanHandleLargeBlocks() { var input = new TestInput(); var body = MessageBody.For("HTTP/1.0", new Dictionary<string, StringValues>(), input.FrameContext); var stream = new FrameRequestStream(body); // Input needs to be greater than 4032 bytes to allocate a block not backed by a slab. var largeInput = new string('a', 8192); input.Add(largeInput, true); // Add a smaller block to the end so that SocketInput attempts to return the large // block to the memory pool. input.Add("Hello", true); var readBuffer = new byte[8192]; var count1 = await stream.ReadAsync(readBuffer, 0, 8192); Assert.Equal(8192, count1); AssertASCII(largeInput, new ArraySegment<byte>(readBuffer, 0, 8192)); var count2 = await stream.ReadAsync(readBuffer, 0, 8192); Assert.Equal(5, count2); AssertASCII("Hello", new ArraySegment<byte>(readBuffer, 0, 5)); var count3 = await stream.ReadAsync(readBuffer, 0, 8192); Assert.Equal(0, count3); }
private void Execute() { MessageBody = MessageBody.For( HttpVersion, RequestHeaders, this); _keepAlive = MessageBody.RequestKeepAlive; RequestBody = new FrameRequestStream(MessageBody); ResponseBody = new FrameResponseStream(this); DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody); SocketInput.Free(); Task.Run(ExecuteAsync); }
public async Task Http10ConnectionCloseAsync() { var input = new TestInput(); var body = MessageBody.For("HTTP/1.0", new Dictionary<string, StringValues>(), input.FrameContext); var stream = new FrameRequestStream(body); input.Add("Hello", true); var buffer1 = new byte[1024]; var count1 = await stream.ReadAsync(buffer1, 0, 1024); AssertASCII("Hello", new ArraySegment<byte>(buffer1, 0, 5)); var buffer2 = new byte[1024]; var count2 = await stream.ReadAsync(buffer2, 0, 1024); Assert.Equal(0, count2); }
public void Http10ConnectionClose() { var input = new TestInput(); var body = MessageBody.For("HTTP/1.0", new FrameRequestHeaders(), input.FrameContext); var stream = new FrameRequestStream().StartAcceptingReads(body); input.Add("Hello", true); var buffer1 = new byte[1024]; var count1 = stream.Read(buffer1, 0, 1024); AssertASCII("Hello", new ArraySegment<byte>(buffer1, 0, 5)); var buffer2 = new byte[1024]; var count2 = stream.Read(buffer2, 0, 1024); Assert.Equal(0, count2); }
public Frame(ConnectionContext context, IPEndPoint remoteEndPoint, IPEndPoint localEndPoint, Action <IFeatureCollection> prepareRequest) : base(context) { _remoteEndPoint = remoteEndPoint; _localEndPoint = localEndPoint; _prepareRequest = prepareRequest; _pathBase = context.ServerAddress.PathBase; if (ReuseStreams) { _requestBody = new FrameRequestStream(); _responseBody = new FrameResponseStream(this); _duplexStream = new FrameDuplexStream(_requestBody, _responseBody); } FrameControl = this; Reset(); }
/// <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 async Task RequestProcessingAsync() { try { var terminated = false; while (!terminated && !_requestProcessingStopping) { while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) { await SocketInput; } } while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) { await SocketInput; } } if (!terminated && !_requestProcessingStopping) { MessageBody = MessageBody.For(HttpVersion, _requestHeaders, this); _keepAlive = MessageBody.RequestKeepAlive; RequestBody = new FrameRequestStream(MessageBody); ResponseBody = new FrameResponseStream(this); DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody); Exception error = null; try { await Application.Invoke(this).ConfigureAwait(false); // Trigger FireOnStarting if ProduceStart hasn't been called yet. // We call it here, so it can go through our normal error handling // and respond with a 500 if an OnStarting callback throws. if (!_responseStarted) { FireOnStarting(); } } catch (Exception ex) { error = ex; } finally { FireOnCompleted(); ProduceEnd(error); } terminated = !_keepAlive; } Reset(); } } catch (Exception ex) { Log.LogWarning("Connection processing ended abnormally", ex); } finally { try { // Inform client no more data will ever arrive ConnectionControl.End(ProduceEndType.SocketShutdownSend); // Wait for client to either disconnect or send unexpected data await SocketInput; // Dispose socket ConnectionControl.End(ProduceEndType.SocketDisconnect); } catch (Exception ex) { Log.LogWarning("Connection shutdown abnormally", ex); } } }
private void Execute() { MessageBody = MessageBody.For( HttpVersion, RequestHeaders, this); _keepAlive = MessageBody.RequestKeepAlive; RequestBody = new FrameRequestStream(MessageBody); ResponseBody = new FrameResponseStream(this); DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody); SocketInput.Free(); Task.Run(ExecuteAsync); }
/// <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 async Task RequestProcessingAsync() { try { var terminated = false; while (!terminated && !_requestProcessingStopping) { while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) { await SocketInput; } } while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) { await SocketInput; } } if (!terminated && !_requestProcessingStopping) { MessageBody = MessageBody.For(HttpVersion, _requestHeaders, this); _keepAlive = MessageBody.RequestKeepAlive; RequestBody = new FrameRequestStream(MessageBody); ResponseBody = new FrameResponseStream(this); DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody); Exception error = null; try { await Application.Invoke(this).ConfigureAwait(false); // Trigger FireOnStarting if ProduceStart hasn't been called yet. // We call it here, so it can go through our normal error handling // and respond with a 500 if an OnStarting callback throws. if (!_responseStarted) { FireOnStarting(); } } catch (Exception ex) { error = ex; } finally { FireOnCompleted(); ProduceEnd(error); } terminated = !_keepAlive; } Reset(); } } catch (Exception ex) { Log.LogWarning("Connection processing ended abnormally", ex); } finally { try { // Inform client no more data will ever arrive ConnectionControl.End(ProduceEndType.SocketShutdownSend); // Wait for client to either disconnect or send unexpected data await SocketInput; // Dispose socket ConnectionControl.End(ProduceEndType.SocketDisconnect); } catch (Exception ex) { Log.LogWarning("Connection shutdown abnormally", ex); } } }
/// <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() { try { while (!_requestProcessingStopping) { while (!_requestProcessingStopping && !TakeStartLine(SocketInput)) { if (SocketInput.RemoteIntakeFin) { return; } await SocketInput; } while (!_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders)) { if (SocketInput.RemoteIntakeFin) { return; } await SocketInput; } if (!_requestProcessingStopping) { var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this); _keepAlive = messageBody.RequestKeepAlive; // _duplexStream may be null if flag switched while running if (!ReuseStreams || _duplexStream == null) { _requestBody = new FrameRequestStream(); _responseBody = new FrameResponseStream(this); _duplexStream = new FrameDuplexStream(_requestBody, _responseBody); } RequestBody = _requestBody.StartAcceptingReads(messageBody); ResponseBody = _responseBody.StartAcceptingWrites(); DuplexStream = _duplexStream; _abortedCts = null; _manuallySetRequestAbortToken = null; var context = _application.CreateContext(this); try { await _application.ProcessRequestAsync(context).ConfigureAwait(false); } catch (Exception ex) { ReportApplicationError(ex); } 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 (!_responseStarted && _applicationException == null && _onStarting != null) { await FireOnStarting(); } _requestBody.PauseAcceptingReads(); _responseBody.PauseAcceptingWrites(); if (_onCompleted != null) { await FireOnCompleted(); } _application.DisposeContext(context, _applicationException); // If _requestAbort is set, the connection has already been closed. if (!_requestAborted) { _responseBody.ResumeAcceptingWrites(); await ProduceEnd(); if (_keepAlive) { _requestBody.ResumeAcceptingReads(); // Finish reading the request body in case the app did not. await messageBody.Consume(); } } _requestBody.StopAcceptingReads(); _responseBody.StopAcceptingWrites(); } if (!_keepAlive) { return; } } Reset(); } } catch (Exception ex) { Log.LogWarning("Connection processing ended abnormally", ex); } finally { try { _abortedCts = null; // If _requestAborted is set, the connection has already been closed. if (!_requestAborted) { // Inform client no more data will ever arrive ConnectionControl.End(ProduceEndType.SocketShutdownSend); // Wait for client to either disconnect or send unexpected data await SocketInput; // Dispose socket ConnectionControl.End(ProduceEndType.SocketDisconnect); } } catch (Exception ex) { Log.LogWarning("Connection shutdown abnormally", ex); } } }
public Frame(ConnectionContext context, IPEndPoint remoteEndPoint, IPEndPoint localEndPoint, Action<IFeatureCollection> prepareRequest) : base(context) { _remoteEndPoint = remoteEndPoint; _localEndPoint = localEndPoint; _prepareRequest = prepareRequest; _pathBase = context.ServerAddress.PathBase; if (ReuseStreams) { _requestBody = new FrameRequestStream(); _responseBody = new FrameResponseStream(this); _duplexStream = new FrameDuplexStream(_requestBody, _responseBody); } FrameControl = this; Reset(); }
/// <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 async Task RequestProcessingAsync() { try { var terminated = false; while (!terminated && !_requestProcessingStopping) { while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) { await SocketInput; } } while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) { await SocketInput; } } if (!terminated && !_requestProcessingStopping) { MessageBody = MessageBody.For(HttpVersion, _requestHeaders, this); _keepAlive = MessageBody.RequestKeepAlive; RequestBody = new FrameRequestStream(MessageBody); ResponseBody = new FrameResponseStream(this); DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody); try { await Application.Invoke(this).ConfigureAwait(false); } catch (Exception ex) { ReportApplicationError(ex); } 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 (!_responseStarted && _applicationException == null) { await FireOnStarting(); } await FireOnCompleted(); await ProduceEnd(); while (await RequestBody.ReadAsync(_nullBuffer, 0, _nullBuffer.Length) != 0) { // Finish reading the request body in case the app did not. } } terminated = !_keepAlive; } Reset(); } } catch (Exception ex) { Log.LogWarning("Connection processing ended abnormally", ex); } finally { try { // Inform client no more data will ever arrive ConnectionControl.End(ProduceEndType.SocketShutdownSend); // Wait for client to either disconnect or send unexpected data await SocketInput; // Dispose socket ConnectionControl.End(ProduceEndType.SocketDisconnect); } catch (Exception ex) { Log.LogWarning("Connection shutdown abnormally", ex); } } }
/// <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 async Task RequestProcessingAsync() { try { var terminated = false; while (!terminated && !_requestProcessingStopping) { while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) { await SocketInput; } } while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) { await SocketInput; } } if (!terminated && !_requestProcessingStopping) { MessageBody = MessageBody.For(HttpVersion, _requestHeaders, this); _keepAlive = MessageBody.RequestKeepAlive; RequestBody = new FrameRequestStream(MessageBody); ResponseBody = new FrameResponseStream(this); DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody); try { await Application.Invoke(this).ConfigureAwait(false); } catch (Exception ex) { ReportApplicationError(ex); } 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 (!_responseStarted && _applicationException == null) { await FireOnStarting(); } await FireOnCompleted(); await ProduceEnd(); while (await RequestBody.ReadAsync(_nullBuffer, 0, _nullBuffer.Length) != 0) { // Finish reading the request body in case the app did not. } } terminated = !_keepAlive; } Reset(); } } catch (Exception ex) { Log.LogWarning("Connection processing ended abnormally", ex); } finally { try { // Inform client no more data will ever arrive ConnectionControl.End(ProduceEndType.SocketShutdownSend); // Wait for client to either disconnect or send unexpected data await SocketInput; // Dispose socket ConnectionControl.End(ProduceEndType.SocketDisconnect); } catch (Exception ex) { Log.LogWarning("Connection shutdown abnormally", ex); } } }