public async Task ResponsesAreChunkedAutomatically() { using (var server = new TestServer(async frame => { frame.ResponseHeaders.Clear(); await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6); await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6); })) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Transfer-Encoding: chunked", "", "6", "Hello ", "6", "World!", "0", "", ""); } } }
public async Task ZeroLengthWritesAreIgnored() { using (var server = new TestServer(async httpContext => { var response = httpContext.Response; response.Headers.Clear(); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6); await response.Body.WriteAsync(new byte[0], 0, 0); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6); })) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Transfer-Encoding: chunked", "", "6", "Hello ", "6", "World!", "0", "", ""); } } }
public async Task CanReadAndWriteWithAsyncConnectionFilter() { var serviceContext = new TestServiceContext() { ConnectionFilter = new AsyncConnectionFilter() }; using (var server = new TestServer(App, serviceContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "POST / HTTP/1.0", "", "Hello World?"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Hello World!"); } } }
public async Task EmptyResponseBodyHandledCorrectlyWithZeroLengthWrite() { using (var server = new TestServer(async frame => { frame.ResponseHeaders.Clear(); await frame.ResponseBody.WriteAsync(new byte[0], 0, 0); })) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Transfer-Encoding: chunked", "", "0", "", ""); } } }
public async Task CanReadAndWriteWithRewritingConnectionFilter() { var filter = new RewritingConnectionFilter(); var serviceContext = new TestServiceContext() { ConnectionFilter = filter }; var sendString = "POST / HTTP/1.0\r\n\r\nHello World?"; using (var server = new TestServer(App, serviceContext)) { using (var connection = new TestConnection()) { // "?" changes to "!" await connection.SendEnd(sendString); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Hello World!"); } } Assert.Equal(sendString.Length, filter.BytesRead); }
public async Task ThrowingInOnStartingResultsIn500Response() { using (var server = new TestServer(frame => { frame.OnStarting(_ => { throw new Exception(); }, null); frame.ResponseHeaders.Clear(); frame.ResponseHeaders["Content-Length"] = new[] { "11" }; // If we write to the response stream, we will not get a 500. return Task.FromResult<object>(null); })) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", ""); await connection.Receive( "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.Receive( "Content-Length: 0", "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "Connection: close", "", ""); } } }
public async Task ThrowingAfterPartialWriteKillsConnection() { bool onStartingCalled = false; using (var server = new TestServer(async frame => { frame.OnStarting(_ => { onStartingCalled = true; return Task.FromResult<object>(null); }, null); frame.ResponseHeaders.Clear(); frame.ResponseHeaders["Content-Length"] = new[] { "11" }; await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Hello"), 0, 5); throw new Exception(); })) { using (var connection = new TestConnection()) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello"); Assert.True(onStartingCalled); } } }
public async Task ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes() { using (var server = new TestServer(async frame => { frame.ResponseHeaders.Clear(); using (var reader = new StreamReader(frame.RequestBody, Encoding.ASCII)) { var statusString = await reader.ReadLineAsync(); frame.StatusCode = int.Parse(statusString); } })) { using (var connection = new TestConnection()) { await connection.SendEnd( "POST / HTTP/1.1", "Content-Length: 3", "", "101POST / HTTP/1.1", "Content-Length: 3", "", "204POST / HTTP/1.1", "Content-Length: 3", "", "205POST / HTTP/1.1", "Content-Length: 3", "", "304POST / HTTP/1.1", "Content-Length: 3", "", "200"); await connection.ReceiveEnd( "HTTP/1.1 101 Switching Protocols", "", "HTTP/1.1 204 No Content", "", "HTTP/1.1 205 Reset Content", "", "HTTP/1.1 304 Not Modified", "", "HTTP/1.1 200 OK", "Content-Length: 0", "", ""); } } }
public async Task ZeroContentLengthNotSetAutomaticallyForNonKeepAliveRequests() { using (var server = new TestServer(EmptyApp)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "Connection: close", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Connection: close", "", ""); } using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.0", "", ""); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", ""); } } }
public async Task DisconnectingClient() { using (var server = new TestServer(App)) { var socket = new Socket(SocketType.Stream, ProtocolType.IP); socket.Connect(IPAddress.Loopback, 54321); await Task.Delay(200); socket.Disconnect(false); socket.Dispose(); await Task.Delay(200); using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.0", "\r\n"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "\r\n"); } } }
public async Task Http10KeepAliveTransferEncoding() { using (var server = new TestServer(AppChunked)) { using (var connection = new TestConnection()) { await connection.SendEnd( "POST / HTTP/1.0", "Transfer-Encoding: chunked", "Connection: keep-alive", "", "5", "Hello", "6", " World", "0", "POST / HTTP/1.0", "", "Goodbye"); await connection.Receive( "HTTP/1.0 200 OK", "Content-Length: 11", "Connection: keep-alive", "", "Hello World"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "Content-Length: 7", "", "Goodbye"); } } }
public async Task Http10TransferEncoding() { using (var server = new TestServer(App)) { using (var connection = new TestConnection()) { await connection.Send( "POST / HTTP/1.0", "Transfer-Encoding: chunked", "", "5", "Hello", "6", " World", "0\r\n"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Hello World"); } } }
public async Task Http10(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "POST / HTTP/1.0", "", "Hello World"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Hello World"); } } }
public async Task ConnectionClosedIfExeptionThrownAfterZeroLengthWrite() { 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(); })) { using (var connection = new TestConnection()) { // 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.ReceiveEnd( "HTTP/1.1 200 OK", "Transfer-Encoding: chunked", "", ""); } } }
public async Task Http10KeepAliveContentLength(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "POST / HTTP/1.0", "Connection: keep-alive", "Content-Length: 11", "", "Hello WorldPOST / HTTP/1.0", "", "Goodbye"); await connection.Receive( "HTTP/1.0 200 OK", "Content-Length: 11", "Connection: keep-alive", "", "Hello World"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "Content-Length: 7", "", "Goodbye"); } } }
public async Task Http10ContentLength() { using (var server = new TestServer(App)) { using (var connection = new TestConnection()) { await connection.Send( "POST / HTTP/1.0", "Content-Length: 11", "", "Hello World"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Hello World"); } } }
public async Task ThrowingResultsIn500Response(ServiceContext testContext) { bool onStartingCalled = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(frame => { var response = frame.Get<IHttpResponseFeature>(); response.OnStarting(_ => { onStartingCalled = true; return Task.FromResult<object>(null); }, null); // Anything added to the ResponseHeaders dictionary is ignored response.Headers.Clear(); response.Headers["Content-Length"] = "11"; throw new Exception(); }, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", ""); await connection.Receive( "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.Receive( "Content-Length: 0", "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "Connection: close", "", ""); Assert.False(onStartingCalled); Assert.Equal(2, testLogger.ApplicationErrorsLogged); } } }
public async Task Http10KeepAliveNotUsedIfResponseContentLengthNotSet() { using (var server = new TestServer(App)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.0", "Connection: keep-alive", "", "POST / HTTP/1.0", "Connection: keep-alive", "Content-Length: 7", "", "Goodbye"); await connection.Receive( "HTTP/1.0 200 OK", "Content-Length: 0", "Connection: keep-alive", "\r\n"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Goodbye"); } } }
public async Task ThrowingAfterPartialWriteKillsConnection(ServiceContext testContext) { bool onStartingCalled = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async frame => { var response = frame.Get<IHttpResponseFeature>(); 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()) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello"); Assert.True(onStartingCalled); Assert.Equal(1, testLogger.ApplicationErrorsLogged); } } }
public async Task Expect100ContinueForBody() { using (var server = new TestServer(AppChunked)) { using (var connection = new TestConnection()) { await connection.Send( "POST / HTTP/1.1", "Expect: 100-continue", "Content-Length: 11", "Connection: close", "\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", "Content-Length: 11", "Connection: close", "", "Hello World"); } } }
public async Task ConnectionClosesWhenFinReceived(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "Post / HTTP/1.1", "Content-Length: 7", "", "Goodbye"); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 0", "", "HTTP/1.1 200 OK", "Content-Length: 7", "", "Goodbye"); } } }
public async Task ZeroContentLengthSetAutomaticallyAfterNoWrites() { using (var server = new TestServer(EmptyApp)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.0", "Connection: keep-alive", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 0", "", "HTTP/1.0 200 OK", "Content-Length: 0", "Connection: keep-alive", "", ""); } } }
public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(ServiceContext testContext) { var onStartingCallCount1 = 0; var onStartingCallCount2 = 0; var failedWriteCount = 0; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async frame => { var onStartingException = new Exception(); var response = frame.Get<IHttpResponseFeature>(); response.OnStarting(_ => { onStartingCallCount1++; throw onStartingException; }, null); response.OnStarting(_ => { onStartingCallCount2++; throw onStartingException; }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; var writeException = await Assert.ThrowsAsync<ObjectDisposedException>(async () => await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11)); Assert.Same(onStartingException, writeException.InnerException); failedWriteCount++; }, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", ""); await connection.Receive( "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.Receive( "Content-Length: 0", "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "Connection: close", "", ""); Assert.Equal(2, onStartingCallCount1); // The second OnStarting callback should not be called since the first failed. Assert.Equal(0, onStartingCallCount2); Assert.Equal(2, testLogger.ApplicationErrorsLogged); } } }
public async Task ZeroContentLengthNotSetAutomaticallyForHeadRequests() { using (var server = new TestServer(EmptyApp)) { using (var connection = new TestConnection()) { await connection.SendEnd( "HEAD / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "", ""); } } }
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 frame => { var response = frame.Get<IHttpResponseFeature>(); 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()) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "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 ThrowingResultsIn500Response() { bool onStartingCalled = false; using (var server = new TestServer(frame => { frame.OnStarting(_ => { onStartingCalled = true; return Task.FromResult<object>(null); }, null); // Anything added to the ResponseHeaders dictionary is ignored frame.ResponseHeaders.Clear(); frame.ResponseHeaders["Content-Length"] = new[] { "11" }; throw new Exception(); })) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", ""); await connection.Receive( "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.Receive( "Content-Length: 0", "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "Connection: close", "", ""); Assert.False(onStartingCalled); } } }
public async Task RequestBodyIsConsumedAutomaticallyIfAppDoesntConsumeItFully(ServiceContext testContext) { using (var server = new TestServer(async frame => { var response = frame.Get<IHttpResponseFeature>(); var request = frame.Get<IHttpRequestFeature>(); Assert.Equal("POST", request.Method); 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()) { await connection.SendEnd( "POST / HTTP/1.1", "Content-Length: 5", "", "HelloPOST / HTTP/1.1", "Transfer-Encoding: chunked", "", "C", "HelloChunked", "0", "POST / HTTP/1.1", "Content-Length: 7", "", "Goodbye"); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello WorldHTTP/1.1 200 OK", "Content-Length: 11", "", "Hello WorldHTTP/1.1 200 OK", "Content-Length: 11", "", "Hello World"); } } }
public async Task ConnectionClosesWhenFinReceivedBeforeRequestCompletes() { using (var server = new TestServer(AppChunked)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET /"); await connection.ReceiveEnd(); } using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "Post / HTTP/1.1"); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 0", "", ""); } using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "Post / HTTP/1.1", "Content-Length: 7"); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 0", "", ""); } } }
public async Task Http11() { using (var server = new TestServer(AppChunked)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", "Goodbye"); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Content-Length: 0", "", "HTTP/1.1 200 OK", "Content-Length: 7", "Connection: close", "", "Goodbye"); } } }
public async Task ThrowingInOnStartingResultsInFailedWrites() { using (var server = new TestServer(async frame => { var onStartingException = new Exception(); frame.OnStarting(_ => { throw onStartingException; }, null); frame.ResponseHeaders.Clear(); frame.ResponseHeaders["Content-Length"] = new[] { "11" }; var writeException = await Assert.ThrowsAsync<Exception>(async () => await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11)); Assert.Same(onStartingException, writeException); // The second write should succeed since the OnStarting callback will not be called again await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Exception!!"), 0, 11); })) { using (var connection = new TestConnection()) { await connection.Send( "GET / HTTP/1.1", "", ""); await connection.Receive( "HTTP/1.1 200 OK", "Content-Length: 11", "", "Exception!!"); ; } } }
public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(ServiceContext testContext) { var onStartingCallCount1 = 0; var onStartingCallCount2 = 0; var failedWriteCount = 0; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(async frame => { var onStartingException = new Exception(); var response = frame.Get <IHttpResponseFeature>(); response.OnStarting(_ => { onStartingCallCount1++; throw onStartingException; }, null); response.OnStarting(_ => { onStartingCallCount2++; throw onStartingException; }, null); response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; var writeException = await Assert.ThrowsAsync <ObjectDisposedException>(async() => await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11)); Assert.Same(onStartingException, writeException.InnerException); failedWriteCount++; }, testContext)) { using (var connection = new TestConnection()) { await connection.SendEnd( "GET / HTTP/1.1", "", "GET / HTTP/1.1", "Connection: close", "", ""); await connection.Receive( "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.Receive( "Content-Length: 0", "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "Connection: close", "", ""); Assert.Equal(2, onStartingCallCount1); // The second OnStarting callback should not be called since the first failed. Assert.Equal(0, onStartingCallCount2); Assert.Equal(2, testLogger.ApplicationErrorsLogged); } } }