public async Task Expect100ContinueForBody(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.Send( "POST / HTTP/1.1", "Expect: 100-continue", "Connection: close", "Content-Length: 11", "\r\n"); await connection.Receive("HTTP/1.1 100 Continue", "\r\n"); await connection.SendEnd("Hello World"); await connection.Receive( "HTTP/1.1 200 OK", "Connection: close", "Content-Length: 11", "", "Hello World"); } } }
public async Task NoErrorsLoggedWhenServerEndsConnectionBeforeClient(ServiceContext testContext) { var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async httpContext => { var response = httpContext.Response; response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); }, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.Send( "GET / HTTP/1.0", "", ""); await connection.ReceiveForcedEnd( "HTTP/1.0 200 OK", "Content-Length: 11", "", "Hello World"); } } Assert.Equal(0, testLogger.TotalErrorsLogged); }
public async Task ConnectionClosedIfExeptionThrownAfterZeroLengthWrite(ServiceContext testContext) { using (var server = new TestServer(async httpContext => { var response = httpContext.Response; response.Headers.Clear(); await response.Body.WriteAsync(new byte[0], 0, 0); throw new Exception(); }, testContext)) { using (var connection = new TestConnection(server.Port)) { // SendEnd is not called, so it isn't the client closing the connection. await connection.Send( "GET / HTTP/1.1", "", ""); // Headers are sent before connection is closed, but chunked body terminator isn't sent await connection.ReceiveForcedEnd( "HTTP/1.1 200 OK", "Transfer-Encoding: chunked", "", ""); } } }
public async Task ConnectionClosedIfExeptionThrownAfterWrite(ServiceContext testContext) { using (var server = new TestServer(async httpContext => { var response = httpContext.Response; response.Headers.Clear(); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World!"), 0, 12); throw new Exception(); }, testContext)) { using (var connection = new TestConnection(server.Port)) { // SendEnd is not called, so it isn't the client closing the connection. // client closing the connection. await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveForcedEnd( "HTTP/1.1 200 OK", "Transfer-Encoding: chunked", "", "c", "Hello World!", ""); } } }
public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection(ServiceContext testContext) { var onCompletedCalled1 = false; var onCompletedCalled2 = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async httpContext => { var response = httpContext.Response; response.OnCompleted(_ => { onCompletedCalled1 = true; throw new Exception(); }, null); response.OnCompleted(_ => { onCompletedCalled2 = true; throw new Exception(); }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); }, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveForcedEnd( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello World"); } // All OnCompleted callbacks should be called even if they throw. Assert.Equal(2, testLogger.ApplicationErrorsLogged); Assert.True(onCompletedCalled1); Assert.True(onCompletedCalled2); } }
public async Task InvalidSizedDataResultsIn400(ServiceContext testContext) { using (var server = new TestServer(async httpContext => { var response = httpContext.Response; var request = httpContext.Request; var buffer = new byte[200]; while (await request.Body.ReadAsync(buffer, 0, buffer.Length) != 0) { ;// read to end } response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); }, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.Send( "POST / HTTP/1.1", "Transfer-Encoding: chunked", "", "C", "HelloChunkedIn"); await connection.Receive( "HTTP/1.1 400 Bad Request", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveForcedEnd( "Content-Length: 0", "Server: Kestrel", "", ""); } } }
public async Task ThrowingAfterPartialWriteKillsConnection(ServiceContext testContext) { bool onStartingCalled = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async httpContext => { var response = httpContext.Response; response.OnStarting(_ => { onStartingCalled = true; return(Task.FromResult <object>(null)); }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello"), 0, 5); throw new Exception(); }, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveForcedEnd( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello"); Assert.True(onStartingCalled); Assert.Equal(1, testLogger.ApplicationErrorsLogged); } } }
public async Task RequestsCanBeAbortedMidRead(ServiceContext testContext) { var readTcs = new TaskCompletionSource <object>(); var registrationTcs = new TaskCompletionSource <int>(); var requestId = 0; using (var server = new TestServer(async httpContext => { requestId++; var response = httpContext.Response; var request = httpContext.Request; var lifetime = httpContext.Features.Get <IHttpRequestLifetimeFeature>(); lifetime.RequestAborted.Register(() => registrationTcs.TrySetResult(requestId)); if (requestId == 1) { response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "5" }; await response.WriteAsync("World"); } else { var readTask = request.Body.CopyToAsync(Stream.Null); lifetime.Abort(); try { await readTask; } catch (Exception ex) { readTcs.SetException(ex); throw; } readTcs.SetException(new Exception("This shouldn't be reached.")); } }, testContext)) { using (var connection = new TestConnection(server.Port)) { // Never send the body so CopyToAsync always fails. await connection.Send( "POST / HTTP/1.1", "Content-Length: 5", "", "HelloPOST / HTTP/1.1", "Content-Length: 5", "", ""); await connection.ReceiveForcedEnd( "HTTP/1.1 200 OK", "Content-Length: 5", "", "World"); } } await Assert.ThrowsAsync <TaskCanceledException>(async() => await readTcs.Task); // The cancellation token for only the last request should be triggered. var abortedRequestId = await registrationTcs.Task; Assert.Equal(2, abortedRequestId); }
public async Task FailedWritesResultInAbortedRequest(ServiceContext testContext) { // This should match _maxBytesPreCompleted in SocketOutput var maxBytesPreCompleted = 65536; // Ensure string is long enough to disable write-behind buffering var largeString = new string('a', maxBytesPreCompleted + 1); var writeTcs = new TaskCompletionSource <object>(); var registrationWh = new ManualResetEventSlim(); var connectionCloseWh = new ManualResetEventSlim(); using (var server = new TestServer(async httpContext => { var response = httpContext.Response; var request = httpContext.Request; var lifetime = httpContext.Features.Get <IHttpRequestLifetimeFeature>(); lifetime.RequestAborted.Register(() => registrationWh.Set()); await request.Body.CopyToAsync(Stream.Null); connectionCloseWh.Wait(); response.Headers.Clear(); try { // Ensure write is long enough to disable write-behind buffering for (int i = 0; i < 100; i++) { await response.WriteAsync(largeString, lifetime.RequestAborted); registrationWh.Wait(1000); } } catch (Exception ex) { writeTcs.SetException(ex); throw; } writeTcs.SetException(new Exception("This shouldn't be reached.")); }, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.Send( "POST / HTTP/1.1", "Content-Length: 5", "", "Hello"); // Don't wait to receive the response. Just close the socket. } connectionCloseWh.Set(); // Write failed await Assert.ThrowsAsync <TaskCanceledException>(async() => await writeTcs.Task); // RequestAborted tripped Assert.True(registrationWh.Wait(1000)); } }