diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttpHandler.cs index bf3cc3132baf53..f77f1420beee3e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttpHandler.cs @@ -60,7 +60,7 @@ public async Task SendRequestAsync(HttpRequestMessage reque // we will leave scope of this method // we need to pass the ownership of the request and this wrapper to the response (via response content stream) // unless we know that we are not streaming anymore - incomingStream = new WasiInputStream(this, incomingResponse.Consume());// passing self ownership, passing body ownership + incomingStream = new WasiInputStream(this, incomingResponse.Consume(), response);// passing self ownership, passing body ownership response.Content = new StreamContent(incomingStream); // passing incomingStream ownership to SendAsync() caller WasiHttpInterop.ConvertResponseHeaders(incomingResponse, response); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttpInterop.cs b/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttpInterop.cs index c99f3281492b0f..facc4382139738 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttpInterop.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttpInterop.cs @@ -178,6 +178,16 @@ public static void ConvertResponseHeaders(IncomingResponse incomingResponse, Htt } } + public static HttpResponseHeaders ConvertTrailingResponseHeaders(Fields headers) + { + var result = new HttpResponseHeaders(); + foreach ((var key, var value) in headers.Entries()) + { + result.Add(key, Encoding.UTF8.GetString(value)); + } + return result; + } + private static bool IsContentHeader(string headerName) { return HeaderDescriptor.TryGet(headerName, out HeaderDescriptor descriptor) && (descriptor.HeaderType & HttpHeaderType.Content) != 0; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiInputStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiInputStream.cs index 5738da0b8a8634..abb5758c74ed41 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiInputStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiInputStream.cs @@ -18,6 +18,7 @@ internal sealed class WasiInputStream : Stream private WasiRequestWrapper wrapper; // owned by this instance private IncomingBody body; // owned by this instance private InputStream stream; // owned by this instance + private HttpResponseMessage response; private int offset; private byte[]? buffer; @@ -28,11 +29,16 @@ internal sealed class WasiInputStream : Stream public override bool CanWrite => false; public override bool CanSeek => false; - public WasiInputStream(WasiRequestWrapper wrapper, IncomingBody body) + public WasiInputStream( + WasiRequestWrapper wrapper, + IncomingBody body, + HttpResponseMessage response + ) { this.wrapper = wrapper; this.body = body; this.stream = body.Stream(); + this.response = response; } ~WasiInputStream() @@ -111,6 +117,7 @@ CancellationToken cancellationToken if (((StreamError)e.Value).Tag == StreamError.CLOSED) { otherSideClosed = true; + await ReadTrailingHeaders(cancellationToken).ConfigureAwait(false); return 0; } else @@ -158,6 +165,46 @@ public override async ValueTask ReadAsync( return result; } + private async Task ReadTrailingHeaders(CancellationToken cancellationToken) + { + isClosed = true; + stream.Dispose(); + using var futureTrailers = IncomingBody.Finish(body); + while (true) + { + var trailers = futureTrailers.Get(); + if (trailers is null) + { + cancellationToken.ThrowIfCancellationRequested(); + await WasiHttpInterop + .RegisterWasiPollable(futureTrailers.Subscribe(), cancellationToken) + .ConfigureAwait(false); + } + else + { + var inner = ((Result, None>)trailers!).AsOk; + if (inner.IsOk) + { + using var headers = inner.AsOk; + if (headers is not null) + { + response.StoreReceivedTrailingHeaders( + WasiHttpInterop.ConvertTrailingResponseHeaders(headers) + ); + } + + break; + } + else + { + throw new HttpRequestException( + WasiHttpInterop.ErrorCodeToString(inner.AsErr) + ); + } + } + } + } + #region PlatformNotSupported public override void Flush()