public async Task AltSvc_ResponseFrame_UpgradeFrom20_Success() { using Http2LoopbackServer firstServer = Http2LoopbackServer.CreateServer(); using Http3LoopbackServer secondServer = new Http3LoopbackServer(); using HttpClient client = CreateHttpClient(); Task <HttpResponseMessage> firstResponseTask = client.GetAsync(firstServer.Address); Task serverTask = Task.Run(async() => { using Http2LoopbackConnection connection = await firstServer.EstablishConnectionAsync(); int streamId = await connection.ReadRequestHeaderAsync(); await connection.SendDefaultResponseHeadersAsync(streamId); await connection.WriteFrameAsync(new AltSvcFrame("", $"h3=\"{secondServer.Address.IdnHost}:{secondServer.Address.Port}\"", streamId)); await connection.SendResponseDataAsync(streamId, Array.Empty <byte>(), true); }); await new[] { firstResponseTask, serverTask }.WhenAllOrAnyFailed(30_000); HttpResponseMessage firstResponse = firstResponseTask.Result; Assert.True(firstResponse.IsSuccessStatusCode); await AltSvc_Upgrade_Success(firstServer, secondServer, client); }
public async Task Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved() { using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = new HttpClient(CreateHttpClientHandler(useSocketsHttpHandler: true, useHttp2LoopbackServer: true))) { Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address); await server.EstablishConnectionAsync(); int streamId = await server.ReadRequestHeaderAsync(); // Response header. await server.SendDefaultResponseHeadersAsync(streamId); // No data. // Response trailing headers await server.SendResponseHeadersAsync(streamId, isTrailingHeader : true, headers : s_trailingHeaders); HttpResponseMessage response = await sendTask; Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal <byte>(Array.Empty <byte>(), await response.Content.ReadAsByteArrayAsync()); Assert.Contains("amazingtrailer", response.TrailingHeaders.GetValues("MyCoolTrailerHeader")); Assert.Contains("World", response.TrailingHeaders.GetValues("Hello")); } }
public async Task AltSvc_ResponseFrame_UpgradeFrom20_Success() { using Http2LoopbackServer firstServer = Http2LoopbackServer.CreateServer(); using Http3LoopbackServer secondServer = new Http3LoopbackServer(); using HttpClient client = CreateHttpClient(CreateHttpClientHandler(HttpVersion.Version30)); Task <HttpResponseMessage> firstResponseTask = client.GetAsync(firstServer.Address); using (Http2LoopbackConnection connection = await firstServer.EstablishConnectionAsync()) { int streamId = await connection.ReadRequestHeaderAsync(); await connection.SendDefaultResponseHeadersAsync(streamId); await connection.WriteFrameAsync(new AltSvcFrame("", $"h3={secondServer.Address.IdnHost}:{secondServer.Address.Port}", streamId)); await connection.SendResponseDataAsync(streamId, Array.Empty <byte>(), true); } HttpResponseMessage firstResponse = await firstResponseTask; Assert.True(firstResponse.IsSuccessStatusCode); await AltSvc_Upgrade_Success(firstServer, secondServer, client); }
public async Task AltSvc_ConnectionFrame_UpgradeFrom20_Success() { // [ActiveIssue("https://github.com/dotnet/runtime/issues/54050")] if (UseQuicImplementationProvider == QuicImplementationProviders.Mock) { return; } using Http2LoopbackServer firstServer = Http2LoopbackServer.CreateServer(); using Http3LoopbackServer secondServer = CreateHttp3LoopbackServer(); using HttpClient client = CreateHttpClient(HttpVersion.Version20); Task <HttpResponseMessage> firstResponseTask = client.GetAsync(firstServer.Address); Task serverTask = Task.Run(async() => { using Http2LoopbackConnection connection = await firstServer.EstablishConnectionAsync(); int streamId = await connection.ReadRequestHeaderAsync(); await connection.WriteFrameAsync(new AltSvcFrame($"https://{firstServer.Address.IdnHost}:{firstServer.Address.Port}", $"h3=\"{secondServer.Address.IdnHost}:{secondServer.Address.Port}\"", streamId: 0)); await connection.SendDefaultResponseAsync(streamId); }); await new[] { firstResponseTask, serverTask }.WhenAllOrAnyFailed(30_000); HttpResponseMessage firstResponse = firstResponseTask.Result; Assert.True(firstResponse.IsSuccessStatusCode); await AltSvc_Upgrade_Success(firstServer, secondServer, client); }
public async Task Http2GetAsync_TrailerHeaders_TrailingHeaderNoBody() { using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = CreateHttpClient()) { Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address); Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); int streamId = await connection.ReadRequestHeaderAsync(); // Response header. await connection.SendDefaultResponseHeadersAsync(streamId); await connection.SendResponseHeadersAsync(streamId, endStream : true, isTrailingHeader : true, headers : TrailingHeaders); HttpResponseMessage response = await sendTask; Assert.Equal(HttpStatusCode.OK, response.StatusCode); var trailingHeaders = GetTrailingHeaders(response); Assert.Equal(TrailingHeaders.Count, trailingHeaders.Count()); Assert.Contains("amazingtrailer", trailingHeaders.GetValues("MyCoolTrailerHeader")); Assert.Contains("World", trailingHeaders.GetValues("Hello")); } }
public async Task Http2GetAsync_NoTrailingHeaders_EmptyCollection() { using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = CreateHttpClient()) { Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address); Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); int streamId = await connection.ReadRequestHeaderAsync(); // Response header. await connection.SendDefaultResponseHeadersAsync(streamId); // Response data. await connection.WriteFrameAsync(MakeDataFrame(streamId, DataBytes, endStream : true)); // Server doesn't send trailing header frame. HttpResponseMessage response = await sendTask; Assert.Equal(HttpStatusCode.OK, response.StatusCode); var trailingHeaders = GetTrailingHeaders(response); Assert.NotNull(trailingHeaders); Assert.Equal(0, trailingHeaders.Count()); } }
public async Task Http2GetAsyncResponseHeadersReadOption_TrailingHeaders_Available() { using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = CreateHttpClient()) { Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address, HttpCompletionOption.ResponseHeadersRead); Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); int streamId = await connection.ReadRequestHeaderAsync(); // Response header. await connection.SendDefaultResponseHeadersAsync(streamId); // Response data, missing Trailers. await connection.WriteFrameAsync(MakeDataFrame(streamId, DataBytes)); HttpResponseMessage response = await sendTask; Assert.Equal(HttpStatusCode.OK, response.StatusCode); // Pending read on the response content. var trailingHeaders = GetTrailingHeaders(response); Assert.True(trailingHeaders == null || trailingHeaders.Count() == 0); Stream stream = await response.Content.ReadAsStreamAsync(TestAsync); Byte[] data = new Byte[100]; await stream.ReadAsync(data, 0, data.Length); // Intermediate test - haven't reached stream EOF yet. trailingHeaders = GetTrailingHeaders(response); Assert.True(trailingHeaders == null || trailingHeaders.Count() == 0); // Finish data stream and write out trailing headers. await connection.WriteFrameAsync(MakeDataFrame(streamId, DataBytes)); await connection.SendResponseHeadersAsync(streamId, endStream : true, isTrailingHeader : true, headers : TrailingHeaders); // Read data until EOF is reached while (stream.Read(data, 0, data.Length) != 0) { ; } trailingHeaders = GetTrailingHeaders(response); Assert.Equal(TrailingHeaders.Count, trailingHeaders.Count()); Assert.Contains("amazingtrailer", trailingHeaders.GetValues("MyCoolTrailerHeader")); Assert.Contains("World", trailingHeaders.GetValues("Hello")); // Read when already zero. Trailers shouldn't be changed. stream.Read(data, 0, data.Length); trailingHeaders = GetTrailingHeaders(response); Assert.Equal(TrailingHeaders.Count, trailingHeaders.Count()); } }
public async Task ReadAndWriteAfterServerHasSentEndStream_Success() { TaskCompletionSource <Stream> requestStreamTcs = new TaskCompletionSource <Stream>(TaskCreationOptions.RunContinuationsAsynchronously); TaskCompletionSource <object> completeStreamTcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = CreateHttpClient()) { HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, server.Address); message.Version = new Version(2, 0); message.Content = new StreamingContent(async s => { await s.WriteAsync(new byte[50]); requestStreamTcs.SetResult(s); await completeStreamTcs.Task; }); Task serverActions = RunServer(); HttpResponseMessage response = await client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead); Assert.Equal(HttpStatusCode.OK, response.StatusCode); await serverActions; Stream requestStream = await requestStreamTcs.Task; Stream responseStream = await response.Content.ReadAsStreamAsync(); // Successfully because endstream hasn't been read yet. await requestStream.WriteAsync(new byte[50]); byte[] buffer = new byte[50]; int readCount = await responseStream.ReadAsync(buffer, 0, buffer.Length); Assert.Equal(DataBytes.Length, readCount); readCount = await responseStream.ReadAsync(buffer, 0, buffer.Length); Assert.Equal(0, readCount); async Task RunServer() { Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); int streamId = await connection.ReadRequestHeaderAsync(expectEndOfStream : false); await connection.SendDefaultResponseHeadersAsync(streamId, endStream : false); await connection.WriteFrameAsync(MakeDataFrame(streamId, DataBytes, endStream : true)); }; } }
public async Task AfterReadResponseServerError_ClientRead() { TaskCompletionSource <Stream> requestStreamTcs = new TaskCompletionSource <Stream>(TaskCreationOptions.RunContinuationsAsynchronously); TaskCompletionSource <object> completeStreamTcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = CreateHttpClient()) { HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, server.Address); message.Version = new Version(2, 0); message.Content = new StreamingContent(async s => { requestStreamTcs.SetResult(s); await completeStreamTcs.Task; }); Task <HttpResponseMessage> sendTask = client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead); Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); Stream requestStream = await requestStreamTcs.Task; await requestStream.WriteAsync(new byte[50]); int streamId = await connection.ReadRequestHeaderAsync(expectEndOfStream : false); Frame frame = await connection.ReadDataFrameAsync(); // Response header. await connection.SendDefaultResponseHeadersAsync(streamId); // Response data. await connection.WriteFrameAsync(MakeDataFrame(streamId, DataBytes, endStream : false)); HttpResponseMessage response = await sendTask; Assert.Equal(HttpStatusCode.OK, response.StatusCode); Stream responseStream = await response.Content.ReadAsStreamAsync(); // Read response data. byte[] buffer = new byte[1024]; int readCount = await responseStream.ReadAsync(buffer, 0, buffer.Length); Assert.Equal(DataBytes.Length, readCount); // Server sends RST_STREAM. await connection.WriteFrameAsync(new RstStreamFrame(FrameFlags.EndStream, 0, streamId)); await Assert.ThrowsAsync <IOException>(() => responseStream.ReadAsync(buffer, 0, buffer.Length)); } }
private static async Task <int> EstablishConnectionAndProcessOneRequestAsync(HttpClient client, Http2LoopbackServer server) { // Establish connection and send initial request/response to ensure connection is available for subsequent use Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address); await server.EstablishConnectionAsync(); int streamId = await server.ReadRequestHeaderAsync(); await server.SendDefaultResponseAsync(streamId); HttpResponseMessage response = await sendTask; Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(0, (await response.Content.ReadAsByteArrayAsync()).Length); return(streamId); }
public async Task Http2GetAsync_MissingTrailer_TrailingHeadersAccepted(bool responseHasContentLength) { using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = CreateHttpClient()) { Task <HttpResponseMessage> sendTask = client.GetAsync(server.Address); Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); int streamId = await connection.ReadRequestHeaderAsync(); // Response header. if (responseHasContentLength) { await connection.SendResponseHeadersAsync(streamId, endStream : false, headers : new[] { new HttpHeaderData("Content-Length", DataBytes.Length.ToString()) }); } else { await connection.SendDefaultResponseHeadersAsync(streamId); } // Response data, missing Trailers. await connection.WriteFrameAsync(MakeDataFrame(streamId, DataBytes)); // Additional trailing header frame. await connection.SendResponseHeadersAsync(streamId, isTrailingHeader : true, headers : TrailingHeaders, endStream : true); HttpResponseMessage response = await sendTask; Assert.Equal(HttpStatusCode.OK, response.StatusCode); var trailingHeaders = GetTrailingHeaders(response); Assert.Equal(TrailingHeaders.Count, trailingHeaders.Count()); Assert.Contains("amazingtrailer", trailingHeaders.GetValues("MyCoolTrailerHeader")); Assert.Contains("World", trailingHeaders.GetValues("Hello")); } }
private async Task EstablishConnectionAsync(Http2LoopbackServer server) { _connection = await server.EstablishConnectionAsync(); _incomingFramesTask = ProcessIncomingFramesAsync(_incomingFramesCts.Token); }
public async Task WriteRequestAfterReadResponse() { TaskCompletionSource <object> tcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = CreateHttpClient()) { HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, server.Address); message.Version = new Version(2, 0); message.Content = new StreamingContent(async s => { await s.WriteAsync(new byte[50]); await tcs.Task; await s.WriteAsync(new byte[50]); }, length: null); Task <HttpResponseMessage> sendTask = client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead); Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); int streamId = await connection.ReadRequestHeaderAsync(expectEndOfStream : false); Frame frame = await connection.ReadDataFrameAsync(); // Response header. await connection.SendDefaultResponseHeadersAsync(streamId); // Response data. await connection.WriteFrameAsync(MakeDataFrame(streamId, DataBytes, endStream : false)); HttpResponseMessage response = await sendTask; Assert.Equal(HttpStatusCode.OK, response.StatusCode); using Stream responseStream = await response.Content.ReadAsStreamAsync(); // Read response data. byte[] buffer = new byte[1024]; int readCount = await responseStream.ReadAsync(buffer, 0, buffer.Length); Assert.Equal(DataBytes.Length, readCount); // Finish sending request data. tcs.SetResult(null); frame = await connection.ReadDataFrameAsync(); // Response data. await connection.WriteFrameAsync(MakeDataFrame(streamId, DataBytes, endStream : true)); // Finish reading response data. readCount = await responseStream.ReadAsync(buffer, 0, buffer.Length); Assert.Equal(DataBytes.Length, readCount); readCount = await responseStream.ReadAsync(buffer, 0, buffer.Length); Assert.Equal(0, readCount); } }
public async Task BackwardsCompatibility_DowngradeToHttp11() { TaskCompletionSource <object> completeStreamTcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) using (HttpClient client = CreateHttpClient()) { HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, server.Address); message.Version = new Version(2, 0); message.Content = new StreamingContent(async s => { await completeStreamTcs.Task; }); Task <HttpResponseMessage> sendTask = client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead); // If WinHTTP doesn't support streaming a request without a length then it will fallback // to HTTP/1.1. This is pretty weird behavior but we keep it for backwards compatibility. Exception ex = await Assert.ThrowsAsync <Exception>(async() => await server.EstablishConnectionAsync()); Assert.Equal("HTTP/1.1 request sent to HTTP/2 connection.", ex.Message); completeStreamTcs.SetResult(null); } }