diff --git a/src/RestSharp/Response/RestResponse.cs b/src/RestSharp/Response/RestResponse.cs index d092271e6..a567f52fb 100644 --- a/src/RestSharp/Response/RestResponse.cs +++ b/src/RestSharp/Response/RestResponse.cs @@ -73,6 +73,9 @@ async Task GetDefaultResponse() { Server = httpResponse.Headers.Server.ToString(), StatusCode = httpResponse.StatusCode, StatusDescription = httpResponse.ReasonPhrase, +#if NET + TrailingHeaders = httpResponse.TrailingHeaders.GetHeaderParameters(), +#endif Version = httpResponse.RequestMessage?.Version }; } @@ -94,4 +97,4 @@ public RestResponse() : this(new()) { } } } -public delegate ResponseStatus CalculateResponseStatus(HttpResponseMessage httpResponse); \ No newline at end of file +public delegate ResponseStatus CalculateResponseStatus(HttpResponseMessage httpResponse); diff --git a/src/RestSharp/Response/RestResponseBase.cs b/src/RestSharp/Response/RestResponseBase.cs index 57b1f5c4a..8236c9163 100644 --- a/src/RestSharp/Response/RestResponseBase.cs +++ b/src/RestSharp/Response/RestResponseBase.cs @@ -111,6 +111,11 @@ protected RestResponseBase(RestRequest request) { /// public IReadOnlyCollection? ContentHeaders { get; set; } + /// + /// Trailer headers returned by server after the response content + /// + public IReadOnlyCollection TrailingHeaders { get; set; } = Array.Empty(); + /// /// Status of the request. Will return Error for transport errors. /// HTTP errors will still return ResponseStatus.Completed, check StatusCode instead diff --git a/test/RestSharp.Tests/ResponseTrailerTests.cs b/test/RestSharp.Tests/ResponseTrailerTests.cs new file mode 100644 index 000000000..457e65fb7 --- /dev/null +++ b/test/RestSharp.Tests/ResponseTrailerTests.cs @@ -0,0 +1,37 @@ +using System.Net; + +namespace RestSharp.Tests; + +public class ResponseTrailerTests { +#if NET + [Fact] + public async Task Should_map_trailing_headers_separately_from_regular_headers() { + using var client = new RestClient( + new RestClientOptions("https://dummy.org") { ConfigureMessageHandler = _ => new TrailerResponseHandler() } + ); + + var response = await client.ExecuteAsync(new RestRequest()); + + response.TrailingHeaders.Should().BeEquivalentTo( + new[] { + new HeaderParameter("Digest", "sha-256=abc"), + new HeaderParameter("Digest", "sha-256=def"), + new HeaderParameter("X-Processing-Status", "complete") + } + ); + response.Headers.Should().ContainSingle(x => x.Name == "X-Regular-Header" && x.Value == "regular"); + response.Headers.Should().NotContain(x => x.Name == "Digest" || x.Name == "X-Processing-Status"); + response.ContentHeaders.Should().NotContain(x => x.Name == "Digest" || x.Name == "X-Processing-Status"); + } + + class TrailerResponseHandler : HttpMessageHandler { + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("{}") }; + response.Headers.TryAddWithoutValidation("X-Regular-Header", "regular"); + response.TrailingHeaders.TryAddWithoutValidation("Digest", ["sha-256=abc", "sha-256=def"]); + response.TrailingHeaders.TryAddWithoutValidation("X-Processing-Status", "complete"); + return Task.FromResult(response); + } + } +#endif +}