public async Task ZeroLengthWritesAreIgnored(ServiceContext testContext) { 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); }, testContext)) { using (var connection = new TestConnection(server.Port)) { 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 ZeroContentLengthSetAutomaticallyAfterNoWrites(ServiceContext testContext) { using (var server = new TestServer(EmptyApp, testContext)) { using (var connection = new TestConnection(server.Port)) { 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", "Connection: keep-alive", "Content-Length: 0", "", ""); } } }
public async Task Http10KeepAliveTransferEncoding(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) { using (var connection = new TestConnection(server.Port)) { 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", "Connection: keep-alive", "Content-Length: 11", "", "Hello World"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "Content-Length: 7", "", "Goodbye"); } } }
public async Task EmptyResponseBodyHandledCorrectlyWithZeroLengthWrite(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); }, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.SendEnd( "GET / HTTP/1.1", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Transfer-Encoding: chunked", "", "0", "", ""); } } }
public async Task ZeroContentLengthNotSetAutomaticallyForNonKeepAliveRequests(ServiceContext testContext) { using (var server = new TestServer(EmptyApp, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.SendEnd( "GET / HTTP/1.1", "Connection: close", "", ""); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Connection: close", "", ""); } using (var connection = new TestConnection(server.Port)) { await connection.SendEnd( "GET / HTTP/1.0", "", ""); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", ""); } } }
public async Task ConnectionClosesWhenFinReceived(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) { using (var connection = new TestConnection(server.Port)) { 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 Http10KeepAliveNotUsedIfResponseContentLengthNotSet(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.SendEnd( "GET / HTTP/1.0", "Connection: keep-alive", "", "POST / HTTP/1.0", "Content-Length: 7", "Connection: keep-alive", "", "Goodbye"); await connection.Receive( "HTTP/1.0 200 OK", "Connection: keep-alive", "Content-Length: 0", "\r\n"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Goodbye"); } } }
public async Task ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes(ServiceContext testContext) { using (var server = new TestServer(async httpContext => { var request = httpContext.Request; var response = httpContext.Response; response.Headers.Clear(); using (var reader = new StreamReader(request.Body, Encoding.ASCII)) { var statusString = await reader.ReadLineAsync(); response.StatusCode = int.Parse(statusString); } }, testContext)) { using (var connection = new TestConnection(server.Port)) { 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 RequestBodyIsConsumedAutomaticallyIfAppDoesntConsumeItFully(ServiceContext testContext) { using (var server = new TestServer(async httpContext => { var response = httpContext.Response; var request = httpContext.Request; 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(server.Port)) { 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 Http10(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.SendEnd( "POST / HTTP/1.0", "", "Hello World"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Hello World"); } } }
public async Task WritesAreFlushedPriorToResponseCompletion(ServiceContext testContext) { var flushWh = new ManualResetEventSlim(); using (var server = new TestServer(async httpContext => { var response = httpContext.Response; response.Headers.Clear(); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6); // Don't complete response until client has received the first chunk. flushWh.Wait(); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6); }, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.SendEnd( "GET / HTTP/1.1", "", ""); await connection.Receive( "HTTP/1.1 200 OK", "Transfer-Encoding: chunked", "", "6", "Hello ", ""); flushWh.Set(); await connection.ReceiveEnd( "6", "World!", "0", "", ""); } } }
public async Task CanReadAndWriteWithAsyncConnectionFilter() { var serviceContext = new TestServiceContext(new AsyncConnectionFilter()); using (var server = new TestServer(App, serviceContext)) { using (var connection = new TestConnection(server.Port)) { await connection.SendEnd( "POST / HTTP/1.0", "", "Hello World?"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Hello World!"); } } }
public async Task Http10TransferEncoding(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) { using (var connection = new TestConnection(server.Port)) { await connection.SendEnd( "POST / HTTP/1.0", "Transfer-Encoding: chunked", "", "5", "Hello", "6", " World", "0", ""); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "", "Hello World"); } } }
public async Task ReuseStreamsOff(ServiceContext testContext) { testContext.ServerOptions.MaxPooledStreams = 0; var streamCount = 0; var loopCount = 20; Stream lastStream = null; using (var server = new TestServer( context => { if (context.Request.Body != lastStream) { lastStream = context.Request.Body; streamCount++; } context.Response.Headers.Clear(); return(context.Request.Body.CopyToAsync(context.Response.Body)); }, testContext)) { using (var connection = new TestConnection(server.Port)) { var requestData = Enumerable.Repeat("GET / HTTP/1.1\r\n", loopCount) .Concat(new[] { "GET / HTTP/1.1\r\nConnection: close\r\n\r\nGoodbye" }); var responseData = Enumerable.Repeat("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n", loopCount) .Concat(new[] { "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nGoodbye" }); await connection.SendEnd(requestData.ToArray()); await connection.ReceiveEnd(responseData.ToArray()); } Assert.Equal(loopCount + 1, streamCount); } }
public async Task DisconnectingClient(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) { var socket = TestConnection.CreateConnectedLoopbackSocket(server.Port); await Task.Delay(200); socket.Dispose(); await Task.Delay(200); using (var connection = new TestConnection(server.Port)) { await connection.SendEnd( "GET / HTTP/1.0", "\r\n"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", "\r\n"); } } }
public async Task CanReadAndWriteWithRewritingConnectionFilter() { var filter = new RewritingConnectionFilter(); var serviceContext = new TestServiceContext(filter); var sendString = "POST / HTTP/1.0\r\n\r\nHello World?"; using (var server = new TestServer(App, serviceContext)) { using (var connection = new TestConnection(server.Port)) { // "?" 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 ExtensionsAreIgnored(ServiceContext testContext) { var requestCount = 10; var requestsReceived = 0; using (var server = new TestServer(async httpContext => { var response = httpContext.Response; var request = httpContext.Request; var buffer = new byte[200]; Assert.True(string.IsNullOrEmpty(request.Headers["X-Trailer-Header"])); while (await request.Body.ReadAsync(buffer, 0, buffer.Length) != 0) { ;// read to end } if (requestsReceived < requestCount) { Assert.Equal(new string('a', requestsReceived), request.Headers["X-Trailer-Header"].ToString()); } else { Assert.True(string.IsNullOrEmpty(request.Headers["X-Trailer-Header"])); } requestsReceived++; response.Headers.Clear(); response.Headers["Content-Length"] = new[] { "11" }; await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); }, testContext)) { var response = string.Join("\r\n", new string[] { "HTTP/1.1 200 OK", "Content-Length: 11", "", "Hello World" }); var expectedFullResponse = string.Join("", Enumerable.Repeat(response, requestCount + 1)); IEnumerable <string> sendSequence = new string[] { "POST / HTTP/1.1", "Transfer-Encoding: chunked", "", "C;hello there", "HelloChunked", "0;hello there", "" }; for (var i = 1; i < requestCount; i++) { sendSequence = sendSequence.Concat(new string[] { "POST / HTTP/1.1", "Transfer-Encoding: chunked", "", "C;hello there", $"HelloChunk{i:00}", "0;hello there", string.Concat("X-Trailer-Header: ", new string('a', i)), "" }); } sendSequence = sendSequence.Concat(new string[] { "POST / HTTP/1.1", "Content-Length: 7", "", "Goodbye" }); var fullRequest = sendSequence.ToArray(); using (var connection = new TestConnection(server.Port)) { await connection.SendEnd(fullRequest); await connection.ReceiveEnd(expectedFullResponse); } } }
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 httpContext => { var onStartingException = new Exception(); var response = httpContext.Response; 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(server.Port)) { 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", "Connection: close", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "", ""); 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 ThrowingResultsIn500Response(ServiceContext testContext) { bool onStartingCalled = false; var testLogger = new TestApplicationErrorLogger(); testContext.Log = new KestrelTrace(testLogger); using (var server = new TestServer(httpContext => { var response = httpContext.Response; 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(server.Port)) { 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.Receive("Connection: close", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", "", ""); Assert.False(onStartingCalled); Assert.Equal(2, testLogger.ApplicationErrorsLogged); } } }