public async Task ServerShutsDownGracefullyWhenMaxRequestBufferSizeExceeded() { // Parameters var data = new byte[_dataLength]; var bytesWrittenTimeout = TimeSpan.FromMilliseconds(100); var bytesWrittenPollingInterval = TimeSpan.FromMilliseconds(bytesWrittenTimeout.TotalMilliseconds / 10); var maxSendSize = 4096; var startReadingRequestBody = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); var clientFinishedSendingRequestBody = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); var lastBytesWritten = DateTime.MaxValue; var memoryPoolFactory = new DiagnosticMemoryPoolFactory(allowLateReturn: true); using (var host = await StartWebHost(16 * 1024, data, false, startReadingRequestBody, clientFinishedSendingRequestBody, memoryPoolFactory.Create)) { var port = host.GetPort(); using (var socket = CreateSocket(port)) using (var stream = new NetworkStream(socket)) { await WritePostRequestHeaders(stream, data.Length); var bytesWritten = 0; Func <Task> sendFunc = async() => { while (bytesWritten < data.Length) { var size = Math.Min(data.Length - bytesWritten, maxSendSize); await stream.WriteAsync(data, bytesWritten, size).ConfigureAwait(false); bytesWritten += size; lastBytesWritten = DateTime.Now; } clientFinishedSendingRequestBody.TrySetResult(null); }; var ignore = sendFunc(); // The minimum is (maxRequestBufferSize - maxSendSize + 1), since if bytesWritten is // (maxRequestBufferSize - maxSendSize) or smaller, the client should be able to // complete another send. var minimumExpectedBytesWritten = (16 * 1024) - maxSendSize + 1; // The maximum is harder to determine, since there can be OS-level buffers in both the client // and server, which allow the client to send more than maxRequestBufferSize before getting // paused. We assume the combined buffers are smaller than the difference between // data.Length and maxRequestBufferSize. var maximumExpectedBytesWritten = data.Length - 1; // Block until the send task has gone a while without writing bytes AND // the bytes written exceeds the minimum expected. This indicates the server buffer // is full. // // If the send task is paused before the expected number of bytes have been // written, keep waiting since the pause may have been caused by something else // like a slow machine. while ((DateTime.Now - lastBytesWritten) < bytesWrittenTimeout || bytesWritten < minimumExpectedBytesWritten) { await Task.Delay(bytesWrittenPollingInterval); } // Verify the number of bytes written before the client was paused. Assert.InRange(bytesWritten, minimumExpectedBytesWritten, maximumExpectedBytesWritten); // Dispose host prior to closing connection to verify the server doesn't throw during shutdown // if a connection no longer has alloc and read callbacks configured. await host.StopAsync(); host.Dispose(); } } // Allow appfunc to unblock startReadingRequestBody.SetResult(null); clientFinishedSendingRequestBody.SetResult(null); await memoryPoolFactory.WhenAllBlocksReturned(TestConstants.DefaultTimeout); }
public async Task LargeUpload(long?maxRequestBufferSize, bool connectionAdapter, bool expectPause) { // Parameters var data = new byte[_dataLength]; var bytesWrittenTimeout = TimeSpan.FromMilliseconds(100); var bytesWrittenPollingInterval = TimeSpan.FromMilliseconds(bytesWrittenTimeout.TotalMilliseconds / 10); var maxSendSize = 4096; var startReadingRequestBody = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); var clientFinishedSendingRequestBody = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); var lastBytesWritten = DateTime.MaxValue; var memoryPoolFactory = new DiagnosticMemoryPoolFactory(allowLateReturn: true); using (var host = await StartWebHost(maxRequestBufferSize, data, connectionAdapter, startReadingRequestBody, clientFinishedSendingRequestBody, memoryPoolFactory.Create)) { var port = host.GetPort(); using (var socket = CreateSocket(port)) using (var stream = new NetworkStream(socket)) { await WritePostRequestHeaders(stream, data.Length); var bytesWritten = 0; Func <Task> sendFunc = async() => { while (bytesWritten < data.Length) { var size = Math.Min(data.Length - bytesWritten, maxSendSize); await stream.WriteAsync(data, bytesWritten, size).ConfigureAwait(false); bytesWritten += size; lastBytesWritten = DateTime.Now; } Assert.Equal(data.Length, bytesWritten); clientFinishedSendingRequestBody.TrySetResult(null); }; var sendTask = sendFunc(); if (expectPause) { // The minimum is (maxRequestBufferSize - maxSendSize + 1), since if bytesWritten is // (maxRequestBufferSize - maxSendSize) or smaller, the client should be able to // complete another send. var minimumExpectedBytesWritten = maxRequestBufferSize.Value - maxSendSize + 1; // The maximum is harder to determine, since there can be OS-level buffers in both the client // and server, which allow the client to send more than maxRequestBufferSize before getting // paused. We assume the combined buffers are smaller than the difference between // data.Length and maxRequestBufferSize. var maximumExpectedBytesWritten = data.Length - 1; // Block until the send task has gone a while without writing bytes AND // the bytes written exceeds the minimum expected. This indicates the server buffer // is full. // // If the send task is paused before the expected number of bytes have been // written, keep waiting since the pause may have been caused by something else // like a slow machine. while ((DateTime.Now - lastBytesWritten) < bytesWrittenTimeout || bytesWritten < minimumExpectedBytesWritten) { await Task.Delay(bytesWrittenPollingInterval); } // Verify the number of bytes written before the client was paused. Assert.InRange(bytesWritten, minimumExpectedBytesWritten, maximumExpectedBytesWritten); // Tell server to start reading request body startReadingRequestBody.TrySetResult(null); // Wait for sendTask to finish sending the remaining bytes await sendTask; } else { // Ensure all bytes can be sent before the server starts reading await sendTask; // Tell server to start reading request body startReadingRequestBody.TrySetResult(null); } await AssertStreamContains(stream, $"bytesRead: {data.Length}"); } await host.StopAsync(); } await memoryPoolFactory.WhenAllBlocksReturned(TestConstants.DefaultTimeout); }