diff --git a/bff/samples/Hosts.Tests/BffTests.cs b/bff/samples/Hosts.Tests/BffTests.cs index 0dba94ea6..5242e71ea 100644 --- a/bff/samples/Hosts.Tests/BffTests.cs +++ b/bff/samples/Hosts.Tests/BffTests.cs @@ -52,5 +52,7 @@ public async Task Can_logout() await _bffClient.InvokeApi("/local/self-contained", expectedResponse: HttpStatusCode.Unauthorized); } + } + } diff --git a/bff/samples/Hosts.Tests/TestInfra/AppHostFixture.cs b/bff/samples/Hosts.Tests/TestInfra/AppHostFixture.cs index 59b9ee6b8..506813537 100644 --- a/bff/samples/Hosts.Tests/TestInfra/AppHostFixture.cs +++ b/bff/samples/Hosts.Tests/TestInfra/AppHostFixture.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Net.Sockets; using Aspire.Hosting; using Microsoft.Extensions.Logging; @@ -53,7 +54,8 @@ public async Task InitializeAsync() { return new SocketsHttpHandler() { - CookieContainer = new CookieContainer() + UseCookies = false, + AllowAutoRedirect = false }; })); @@ -134,6 +136,9 @@ public async Task DisposeAsync() public HttpClient CreateHttpClient(string clientName) { + HttpMessageHandler inner; + Uri? baseAddress = null; + if (UsingAlreadyRunningInstance) { var url = clientName switch @@ -141,40 +146,46 @@ public HttpClient CreateHttpClient(string clientName) "bff" => "https://localhost:5002", _ => throw new InvalidOperationException("client not configured") }; + baseAddress = new Uri(url); - var socketHandler = new SocketsHttpHandler() + inner = new SocketsHttpHandler() { + UseCookies = false, AllowAutoRedirect = false, }; + } + else + { +#if DEBUG_NCRUNCH + throw new InvalidOperationException("This should not be reached in NCrunch"); +#else + if (App == null) throw new NotSupportedException("App should not be null"); + var client = App.CreateHttpClient(clientName); + baseAddress = client.BaseAddress; + inner = new CloningHttpMessageHandler(client); + } - var loggingHandler = - new OutboundRequestLoggingHandler( - CreateLogger() - , _ => true) - { - InnerHandler = socketHandler - }; - var cookieHandler = new CookieHandler(loggingHandler, new CookieContainer()); - - var redirectHandler = new AutoFollowRedirectHandler(CreateLogger()) + var loggingHandler = + new OutboundRequestLoggingHandler( + CreateLogger() + , _ => true) { - InnerHandler = cookieHandler + InnerHandler = inner }; + var cookieHandler = new CookieHandler(loggingHandler, new CookieContainer()); + var redirectHandler = new AutoFollowRedirectHandler(CreateLogger()) + { + InnerHandler = cookieHandler + }; - return new HttpClient(redirectHandler) - { - BaseAddress = new Uri(url) - }; - } -#if DEBUG_NCRUNCH - throw new InvalidOperationException("This should not be reached in NCrunch"); -#else - if (App == null) throw new NotSupportedException("App should not be null"); - var client = App.CreateHttpClient(clientName); - return client; + return new HttpClient(redirectHandler) + { + BaseAddress = baseAddress + }; + #endif } diff --git a/bff/samples/Hosts.Tests/TestInfra/AutoFollowRedirectHandler.cs b/bff/samples/Hosts.Tests/TestInfra/AutoFollowRedirectHandler.cs index a667483ca..f9793f393 100644 --- a/bff/samples/Hosts.Tests/TestInfra/AutoFollowRedirectHandler.cs +++ b/bff/samples/Hosts.Tests/TestInfra/AutoFollowRedirectHandler.cs @@ -30,4 +30,92 @@ protected override async Task SendAsync(HttpRequestMessage throw new InvalidOperationException("Keeps redirecting forever"); } +} + +public class CloningHttpMessageHandler : HttpMessageHandler +{ + private readonly HttpClient _innerHttpClient; + + public CloningHttpMessageHandler(HttpClient innerHttpClient) + { + _innerHttpClient = innerHttpClient ?? throw new ArgumentNullException(nameof(innerHttpClient)); + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + // Clone the incoming request + var clonedRequest = await CloneHttpRequestMessageAsync(request); + + // Send the cloned request using the inner HttpClient + var response = await _innerHttpClient.SendAsync(clonedRequest, cancellationToken); + + // Clone the response and return it + return await CloneHttpResponseMessageAsync(response); + } + + private async Task CloneHttpRequestMessageAsync(HttpRequestMessage original) + { + var cloned = new HttpRequestMessage(original.Method, original.RequestUri) + { + Version = original.Version + }; + + // Copy the content if present + if (original.Content != null) + { + //var memoryStream = new MemoryStream(); + //await original.Content.CopyToAsync(memoryStream); + //memoryStream.Position = 0; + //cloned.Content = new StreamContent(memoryStream); + cloned.Content = new StreamContent(await original.Content.ReadAsStreamAsync()); + + // Copy headers from the original content to the cloned content + foreach (var header in original.Content.Headers) + { + cloned.Content.Headers.Add(header.Key, header.Value); + } + } + + // Copy headers + foreach (var header in original.Headers) + { + cloned.Headers.Add(header.Key, header.Value); + } + + return cloned; + } + + private async Task CloneHttpResponseMessageAsync(HttpResponseMessage original) + { + var cloned = new HttpResponseMessage(original.StatusCode) + { + Version = original.Version, + ReasonPhrase = original.ReasonPhrase, + RequestMessage = original.RequestMessage, + }; + + // Copy the content if present + if (original.Content != null) + { + //var memoryStream = new MemoryStream(); + //await original.Content.CopyToAsync(memoryStream); + //memoryStream.Position = 0; + //cloned.Content = new StreamContent(memoryStream); + cloned.Content = new StreamContent(await original.Content.ReadAsStreamAsync()); + + // Copy headers from the original content to the cloned content + foreach (var header in original.Content.Headers) + { + cloned.Content.Headers.Add(header.Key, header.Value); + } + } + + // Copy headers + foreach (var header in original.Headers) + { + cloned.Headers.Add(header.Key, header.Value); + } + + return cloned; + } } \ No newline at end of file