public void ProduceStart() { if (_resultStarted) { return; } _resultStarted = true; FireOnSendingHeaders(); _headersSent = true; var status = ReasonPhrases.ToStatus(StatusCode, ReasonPhrase); var responseHeader = CreateResponseHeader(status, ResponseHeaders); SocketOutput.Write( responseHeader.Item1, (error, x) => { if (error != null) { Trace.WriteLine("ProduceStart " + error.ToString()); } ((IDisposable)x).Dispose(); }, responseHeader.Item2); }
private async Task WriteChunkedAsync(ArraySegment <byte> data, CancellationToken cancellationToken) { await SocketOutput.WriteAsync(BeginChunkBytes(data.Count), immediate : false, cancellationToken : cancellationToken); await SocketOutput.WriteAsync(data, immediate : false, cancellationToken : cancellationToken); await SocketOutput.WriteAsync(_endChunkBytes, immediate : true, cancellationToken : cancellationToken); }
public void Start() { KestrelTrace.Log.ConnectionStart(_connectionId); SocketInput = new SocketInput(Memory); SocketOutput = new SocketOutput(Thread, _socket); _frame = new Frame(this); _socket.ReadStart(_allocCallback, _readCallback, this); }
public Connection(ListenerContext context, UvStreamHandle socket) : base(context) { _socket = socket; ConnectionControl = this; _connectionId = Interlocked.Increment(ref _lastConnectionId); _rawSocketInput = new SocketInput(Memory2, ThreadPool); _rawSocketOutput = new SocketOutput(Thread, _socket, Memory2, this, _connectionId, Log, ThreadPool, WriteReqPool); }
public Connection(ListenerContext context, UvStreamHandle socket) : base(context) { _socket = socket; ConnectionControl = this; _connectionId = Interlocked.Increment(ref _lastConnectionId); _rawSocketInput = new SocketInput(Memory2); _rawSocketOutput = new SocketOutput(Thread, _socket, _connectionId, Log); }
public Connection(ListenerContext context, UvStreamHandle socket) : base(context) { _socket = socket; ConnectionControl = this; _connectionId = Interlocked.Increment(ref _lastConnectionId); SocketInput = new SocketInput(Memory2); SocketOutput = new SocketOutput(Thread, _socket, _connectionId, Log); _frame = new Frame(this); }
internal void Contextualize( SocketOutput socketOutput, UvStreamHandle socket, ArraySegment <byte> buffer, Action <Exception, object> callback, object state) { _self = socketOutput; _socket = socket; _buffer = buffer; _callback = callback; _state = state; }
public void ProduceContinue() { if (_responseStarted) { return; } StringValues expect; if (_httpVersion == HttpVersionType.Http1_1 && RequestHeaders.TryGetValue("Expect", out expect) && (expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase)) { SocketOutput.Write(_continueBytes); } }
private async Task ProduceStart(bool immediate, bool appCompleted) { if (_responseStarted) { return; } _responseStarted = true; var status = ReasonPhrases.ToStatus(StatusCode, ReasonPhrase); var responseHeader = CreateResponseHeader(status, appCompleted); using (responseHeader.Item2) { await SocketOutput.WriteAsync(responseHeader.Item1, immediate : immediate); } }
public void Write(ArraySegment <byte> data) { ProduceStart(immediate: false); if (_autoChunk) { if (data.Count == 0) { return; } WriteChunked(data); } else { SocketOutput.Write(data, immediate: true); } }
public void Write(ArraySegment <byte> data) { ProduceStartAndFireOnStarting(immediate: false).GetAwaiter().GetResult(); if (_autoChunk) { if (data.Count == 0) { return; } WriteChunked(data); } else { SocketOutput.Write(data, immediate: true); } }
public async Task WriteAsyncAwaited(ArraySegment <byte> data, CancellationToken cancellationToken) { await ProduceStartAndFireOnStarting(immediate : false); if (_autoChunk) { if (data.Count == 0) { return; } await WriteChunkedAsync(data, cancellationToken); } else { await SocketOutput.WriteAsync(data, immediate : true, cancellationToken : cancellationToken); } }
public void ProduceStart(bool immediate = true, bool appCompleted = false) { // ProduceStart shouldn't no-op in the future just b/c FireOnStarting throws. if (_responseStarted) { return; } FireOnStarting(); _responseStarted = true; var status = ReasonPhrases.ToStatus(StatusCode, ReasonPhrase); var responseHeader = CreateResponseHeader(status, appCompleted); SocketOutput.Write(responseHeader.Item1, immediate: immediate); responseHeader.Item2.Dispose(); }
public Task WriteAsync(ArraySegment <byte> data, CancellationToken cancellationToken) { ProduceStart(immediate: false); if (_autoChunk) { if (data.Count == 0) { return(TaskUtilities.CompletedTask); } return(WriteChunkedAsync(data, cancellationToken)); } else { return(SocketOutput.WriteAsync(data, immediate: true, cancellationToken: cancellationToken)); } }
public void CanWrite1MB() { // This test was added because when initially implementing write-behind buffering in // SocketOutput, the write callback would never be invoked for writes larger than // _maxBytesPreCompleted even after the write actually completed. // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { triggerCompleted(0); return 0; } }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace, ltp, new Queue<UvWriteReq>()); // I doubt _maxBytesPreCompleted will ever be over a MB. If it is, we should change this test. var bufferSize = 1048576; var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize); var completedWh = new ManualResetEventSlim(); // Act socketOutput.WriteAsync(buffer).ContinueWith( (t) => { Assert.Null(t.Exception); completedWh.Set(); } ); // Assert Assert.True(completedWh.Wait(1000)); } }
public void CanWrite1MB() { // This test was added because when initially implementing write-behind buffering in // SocketOutput, the write callback would never be invoked for writes larger than // _maxBytesPreCompleted even after the write actually completed. // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { triggerCompleted(0); return 0; } }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new KestrelTrace(new TestKestrelTrace())); var trace = new KestrelTrace(new TestKestrelTrace()); var socketOutput = new SocketOutput(kestrelThread, socket, 0, trace); // I doubt _maxBytesPreCompleted will ever be over a MB. If it is, we should change this test. var bufferSize = 1048576; var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize); var completedWh = new ManualResetEventSlim(); Action<Exception, object, bool> onCompleted = (ex, state, calledInline) => { Assert.Null(ex); Assert.Null(state); completedWh.Set(); }; // Act socketOutput.Write(buffer, onCompleted, null); // Assert Assert.True(completedWh.Wait(1000)); } }
void IConnectionControl.End(ProduceEndType endType) { lock (_stateLock) { switch (endType) { case ProduceEndType.SocketShutdownSend: if (_connectionState != ConnectionState.Open) { return; } _connectionState = ConnectionState.Shutdown; Log.ConnectionWriteFin(_connectionId); SocketOutput.End(endType); break; case ProduceEndType.ConnectionKeepAlive: if (_connectionState != ConnectionState.Open) { return; } Log.ConnectionKeepAlive(_connectionId); break; case ProduceEndType.SocketDisconnect: if (_connectionState == ConnectionState.Disconnected) { return; } _connectionState = ConnectionState.Disconnected; Log.ConnectionDisconnect(_connectionId); SocketOutput.End(endType); break; } } }
public void ProduceContinue() { if (_resultStarted) { return; } string[] expect; if (HttpVersion.Equals("HTTP/1.1") && RequestHeaders.TryGetValue("Expect", out expect) && (expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase)) { SocketOutput.Write( new ArraySegment <byte>(_continueBytes, 0, _continueBytes.Length), (error, _) => { if (error != null) { Trace.WriteLine("ProduceContinue " + error.ToString()); } }, null); } }
public void WritesDontCompleteImmediatelyWhenTooManyBytesIncludingNonImmediateAreAlreadyPreCompleted() { // This should match _maxBytesPreCompleted in SocketOutput var maxBytesPreCompleted = 65536; var completeQueue = new Queue<Action<int>>(); // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { completeQueue.Enqueue(triggerCompleted); return 0; } }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace, ltp, new Queue<UvWriteReq>()); var bufferSize = maxBytesPreCompleted; var data = new byte[bufferSize]; var fullBuffer = new ArraySegment<byte>(data, 0, bufferSize); var halfBuffer = new ArraySegment<byte>(data, 0, bufferSize / 2); var completedWh = new ManualResetEventSlim(); Action<Task> onCompleted = (Task t) => { Assert.Null(t.Exception); completedWh.Set(); }; // Act socketOutput.WriteAsync(halfBuffer, false).ContinueWith(onCompleted); // Assert // The first write should pre-complete since it is not immediate. Assert.True(completedWh.Wait(1000)); // Arrange completedWh.Reset(); // Act socketOutput.WriteAsync(halfBuffer).ContinueWith(onCompleted); // Assert // The second write should pre-complete since it is <= _maxBytesPreCompleted. Assert.True(completedWh.Wait(1000)); // Arrange completedWh.Reset(); // Act socketOutput.WriteAsync(halfBuffer, false).ContinueWith(onCompleted); // Assert // The third write should pre-complete since it is not immediate, even though too many. Assert.True(completedWh.Wait(1000)); // Arrange completedWh.Reset(); // Act socketOutput.WriteAsync(halfBuffer).ContinueWith(onCompleted); // Assert // Too many bytes are already pre-completed for the fourth write to pre-complete. Assert.False(completedWh.Wait(1000)); // Act while (completeQueue.Count > 0) { completeQueue.Dequeue()(0); } // Assert // Finishing the first write should allow the second write to pre-complete. Assert.True(completedWh.Wait(1000)); } }
public async Task FlushAsync(CancellationToken cancellationToken) { await ProduceStartAndFireOnStarting(immediate : false); await SocketOutput.WriteAsync(_emptyData, immediate : true, cancellationToken : cancellationToken); }
public void Flush() { ProduceStartAndFireOnStarting(immediate: false).GetAwaiter().GetResult(); SocketOutput.Write(_emptyData, immediate: true); }
public WriteContext(SocketOutput self) { Self = self; Buffers = new Queue <ArraySegment <byte> >(); }
private Task CreateResponseHeader( byte[] statusBytes, bool appCompleted, bool immediate) { var begin = SocketOutput.ProducingStart(); var end = begin; if (_keepAlive) { foreach (var connectionValue in _responseHeaders.HeaderConnection) { if (connectionValue.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1) { _keepAlive = false; } } } if (_keepAlive && !_responseHeaders.HasTransferEncoding && !_responseHeaders.HasContentLength) { if (appCompleted) { // Don't set the Content-Length or Transfer-Encoding headers // automatically for HEAD requests or 101, 204, 205, 304 responses. if (Method != "HEAD" && StatusCanHaveBody(StatusCode)) { // Since the app has completed and we are only now generating // the headers we can safely set the Content-Length to 0. _responseHeaders.SetRawContentLength("0", _bytesContentLengthZero); } } else { if (_httpVersion == HttpVersionType.Http1_1) { _autoChunk = true; _responseHeaders.SetRawTransferEncoding("chunked", _bytesTransferEncodingChunked); } else { _keepAlive = false; } } } if (_keepAlive == false && _responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_1) { _responseHeaders.SetRawConnection("close", _bytesConnectionClose); } else if (_keepAlive && _responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_0) { _responseHeaders.SetRawConnection("keep-alive", _bytesConnectionKeepAlive); } end.CopyFrom(_httpVersion == HttpVersionType.Http1_1 ? _bytesHttpVersion1_1 : _bytesHttpVersion1_0); end.CopyFrom(statusBytes); _responseHeaders.CopyTo(ref end); end.CopyFrom(_bytesEndHeaders, 0, _bytesEndHeaders.Length); SocketOutput.ProducingComplete(end); if (immediate) { return(SocketOutput.WriteAsync(default(ArraySegment <byte>), immediate: true)); } else { return(TaskUtilities.CompletedTask); } }
private void WriteChunkedResponseSuffix() { SocketOutput.Write(_endChunkedResponseBytes, immediate: true); }
public void WritesDontCompleteImmediatelyWhenTooManyBytesAreAlreadyPreCompleted() { // This should match _maxBytesPreCompleted in SocketOutput var maxBytesPreCompleted = 65536; var completeQueue = new Queue<Action<int>>(); // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { completeQueue.Enqueue(triggerCompleted); return 0; } }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new KestrelTrace(new TestKestrelTrace())); var trace = new KestrelTrace(new TestKestrelTrace()); var socketOutput = new SocketOutput(kestrelThread, socket, 0, trace); var bufferSize = maxBytesPreCompleted; var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize); var completedWh = new ManualResetEventSlim(); Action<Exception, object, bool> onCompleted = (ex, state, calledInline) => { Assert.Null(ex); Assert.Null(state); completedWh.Set(); }; // Act socketOutput.Write(buffer, onCompleted, null); // Assert // The first write should pre-complete since it is <= _maxBytesPreCompleted. Assert.True(completedWh.Wait(1000)); // Arrange completedWh.Reset(); // Act socketOutput.Write(buffer, onCompleted, null); // Assert // Too many bytes are already pre-completed for the second write to pre-complete. Assert.False(completedWh.Wait(1000)); // Act completeQueue.Dequeue()(0); // Assert // Finishing the first write should allow the second write to pre-complete. Assert.True(completedWh.Wait(1000)); } }
public void ProducingStartAndProducingCompleteCanBeUsedDirectly() { int nBuffers = 0; var nBufferWh = new ManualResetEventSlim(); var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { nBuffers = buffers; nBufferWh.Set(); triggerCompleted(0); return 0; } }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace, ltp, new Queue<UvWriteReq>()); // block 1 var start = socketOutput.ProducingStart(); start.Block.End = start.Block.Data.Offset + start.Block.Data.Count; // block 2 var block2 = memory.Lease(); block2.End = block2.Data.Offset + block2.Data.Count; start.Block.Next = block2; var end = new MemoryPoolIterator2(block2, block2.End); socketOutput.ProducingComplete(end); // A call to Write is required to ensure a write is scheduled socketOutput.WriteAsync(default(ArraySegment<byte>)); Assert.True(nBufferWh.Wait(1000)); Assert.Equal(2, nBuffers); } }
public WriteContext(SocketOutput self) { Self = self; Buffers = new Queue<ArraySegment<byte>>(); }
public void Post(Action<SocketOutput> callback, SocketOutput state) { lock (_workSync) { _workAdding.Enqueue(new Work { CallbackAdapter = _socketCallbackAdapter, Callback = callback, State = state }); } _post.Send(); }
public void Flush() { ProduceStart(immediate: false); SocketOutput.Write(_emptyData, immediate: true); }
private void WriteChunked(ArraySegment <byte> data) { SocketOutput.Write(BeginChunkBytes(data.Count), immediate: false); SocketOutput.Write(data, immediate: false); SocketOutput.Write(_endChunkBytes, immediate: true); }
public void Write(ArraySegment <byte> data, Action <Exception, object> callback, object state) { ProduceStart(); SocketOutput.Write(data, callback, state); }
public WriteContext(SocketOutput self) { Self = self; }
public void WritesDontGetCompletedTooQuickly() { // This should match _maxBytesPreCompleted in SocketOutput var maxBytesPreCompleted = 65536; var completeQueue = new Queue<Action<int>>(); var onWriteWh = new ManualResetEventSlim(); // Arrange var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { completeQueue.Enqueue(triggerCompleted); onWriteWh.Set(); return 0; } }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace, ltp, new Queue<UvWriteReq>()); var bufferSize = maxBytesPreCompleted; var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize); var completedWh = new ManualResetEventSlim(); Action<Task> onCompleted = (Task t) => { Assert.Null(t.Exception); completedWh.Set(); }; var completedWh2 = new ManualResetEventSlim(); Action<Task> onCompleted2 = (Task t) => { Assert.Null(t.Exception); completedWh2.Set(); }; // Act (Pre-complete the maximum number of bytes in preparation for the rest of the test) socketOutput.WriteAsync(buffer).ContinueWith(onCompleted); // Assert // The first write should pre-complete since it is <= _maxBytesPreCompleted. Assert.True(completedWh.Wait(1000)); Assert.True(onWriteWh.Wait(1000)); // Arrange completedWh.Reset(); onWriteWh.Reset(); // Act socketOutput.WriteAsync(buffer).ContinueWith(onCompleted); socketOutput.WriteAsync(buffer).ContinueWith(onCompleted2); Assert.True(onWriteWh.Wait(1000)); completeQueue.Dequeue()(0); // Assert // Too many bytes are already pre-completed for the third but not the second write to pre-complete. // https://github.com/aspnet/KestrelHttpServer/issues/356 Assert.True(completedWh.Wait(1000)); Assert.False(completedWh2.Wait(1000)); // Act completeQueue.Dequeue()(0); // Assert // Finishing the first write should allow the second write to pre-complete. Assert.True(completedWh2.Wait(1000)); } }
public Task FlushAsync(CancellationToken cancellationToken) { ProduceStart(immediate: false); return(SocketOutput.WriteAsync(_emptyData, immediate: true)); }
internal void Contextualize( SocketOutput socketOutput, UvStreamHandle socket, ArraySegment<byte> buffer, Action<Exception, object> callback, object state) { _self = socketOutput; _socket = socket; _buffer = buffer; _callback = callback; _state = state; }