Skip to content

Commit 979e6ac

Browse files
authored
Add Content to all HttpResponseMessages (#387)
- fix #386 - reenable `net6.0` tests disabled in #384 for this issue - also, check `Stream.CanSeek` less when calculating `HttpMessageContent.ContentLength` value - use inner `HttpContent.Headers.ContentLength` if available - this changes behaviour slightly for both requests and responses - observable as `HttpMessageContent.Headers.ContentLength!=null` for empty messages - for responses, avoids `ContentLength==null` versus `ContentLength==0` inconsistencies (depending on platform) - `ContentLength==null` may still occur in some corner cases (which existed before) - e.g. when inner `HttpContent` doesn't know its length and `ReadAsStreamAsync()` hasn't completed - main change means `HttpResponseMessage`s we expose to user code are consistent across platforms - note: user code won't see `EmptyContent` in `HttpResponseMessage`s we create
1 parent 0f4c624 commit 979e6ac

File tree

10 files changed

+29
-20
lines changed

10 files changed

+29
-20
lines changed

Diff for: src/System.Net.Http.Formatting/HttpContentMessageExtensions.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ private static HttpContent CreateHeaderFields(HttpHeaders source, HttpHeaders de
451451
Contract.Assert(destination != null, "destination headers cannot be null");
452452
Contract.Assert(contentStream != null, "contentStream must be non null");
453453
HttpContentHeaders contentHeaders = null;
454-
HttpContent content = null;
454+
HttpContent content;
455455

456456
// Set the header fields
457457
foreach (KeyValuePair<string, IEnumerable<string>> header in source)
@@ -481,6 +481,10 @@ private static HttpContent CreateHeaderFields(HttpHeaders source, HttpHeaders de
481481
content = new StreamContent(contentStream);
482482
contentHeaders.CopyTo(content.Headers);
483483
}
484+
else
485+
{
486+
content = new StreamContent(Stream.Null);
487+
}
484488

485489
return content;
486490
}

Diff for: src/System.Net.Http.Formatting/HttpMessageContent.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ protected override async Task SerializeToStreamAsync(Stream stream, TransportCon
205205
protected override bool TryComputeLength(out long length)
206206
{
207207
// We have four states we could be in:
208+
// 0. We have content and it knows its ContentLength.
208209
// 1. We have content, but the task is still running or finished without success
209210
// 2. We have content, the task has finished successfully, and the stream came back as a null or non-seekable
210211
// 3. We have content, the task has finished successfully, and the stream is seekable, so we know its length
@@ -214,11 +215,13 @@ protected override bool TryComputeLength(out long length)
214215
// For #3, we return true & the size of our headers + the content length
215216
// For #4, we return true & the size of our headers
216217

217-
bool hasContent = _streamTask.Value != null;
218218
length = 0;
219219

220-
// Cases #1, #2, #3
221-
if (hasContent)
220+
if (Content?.Headers.ContentLength is not null)
221+
{
222+
length = (long)Content.Headers.ContentLength; // Case #0
223+
}
224+
else if (_streamTask.Value is not null)
222225
{
223226
Stream readStream;
224227
if (!_streamTask.Value.TryGetResult(out readStream) // Case #1

Diff for: src/System.Net.Http.Formatting/HttpRequestMessageExtensions.cs

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.ComponentModel;
55
using System.Diagnostics.CodeAnalysis;
6+
using System.IO;
67
using System.Web.Http;
78

89
namespace System.Net.Http
@@ -29,6 +30,7 @@ public static HttpResponseMessage CreateResponse(this HttpRequestMessage request
2930

3031
return new HttpResponseMessage
3132
{
33+
Content = new StreamContent(Stream.Null),
3234
StatusCode = statusCode,
3335
RequestMessage = request
3436
};
@@ -49,6 +51,7 @@ public static HttpResponseMessage CreateResponse(this HttpRequestMessage request
4951

5052
return new HttpResponseMessage
5153
{
54+
Content = new StreamContent(Stream.Null),
5255
RequestMessage = request
5356
};
5457
}

Diff for: test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ public override async Task WriteToStreamAsync_WhenObjectIsNull_WritesDataButDoes
588588
// Arrange
589589
JsonMediaTypeFormatter formatter = CreateFormatter();
590590
Stream stream = new MemoryStream();
591-
HttpContent content = new StringContent(String.Empty);
591+
HttpContent content = new StreamContent(Stream.Null);
592592

593593
// Act
594594
await formatter.WriteToStreamAsync(typeof(SampleType), null, stream, content, null);

Diff for: test/System.Net.Http.Formatting.Test/Handlers/ProgressMessageHandlerTest.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,8 @@ public async Task SendAsync_DoesNotInsertSendProgressWithoutEntityOrHandlerPrese
5656
}
5757

5858
[Theory]
59-
#if !NET6_0_OR_GREATER // https://github.com/aspnet/AspNetWebStack/issues/386
6059
[InlineData(false, false)]
6160
[InlineData(false, true)]
62-
#endif
6361
[InlineData(true, false)]
6462
[InlineData(true, true)]
6563
public async Task SendAsync_InsertsReceiveProgressWhenResponseEntityPresent(bool insertResponseEntity, bool addReceiveProgressHandler)
@@ -86,7 +84,8 @@ public async Task SendAsync_InsertsReceiveProgressWhenResponseEntityPresent(bool
8684
}
8785
else
8886
{
89-
Assert.Null(response.Content);
87+
Assert.NotNull(response.Content);
88+
Assert.Equal(0L, response.Content.Headers.ContentLength);
9089
}
9190
}
9291
}

Diff for: test/System.Net.Http.Formatting.Test/Handlers/ProgressStreamTest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ internal static ProgressStream CreateProgressStream(
274274
Stream iStream = innerStream ?? new Mock<Stream>().Object;
275275
ProgressMessageHandler pHandler = progressMessageHandler ?? new ProgressMessageHandler();
276276
HttpRequestMessage req = request ?? new HttpRequestMessage();
277-
HttpResponseMessage rsp = response ?? new HttpResponseMessage();
277+
HttpResponseMessage rsp = response ?? new HttpResponseMessage() { Content = new StreamContent(Stream.Null) };
278278
return new ProgressStream(iStream, pHandler, req, rsp);
279279
}
280280

Diff for: test/System.Net.Http.Formatting.Test/HttpMessageContentTests.cs

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System.IO;
45
using System.Net.Http.Headers;
56
using System.Threading.Tasks;
67
using Microsoft.TestCommon;
@@ -39,10 +40,8 @@ private static HttpResponseMessage CreateResponse(bool containsEntity)
3940
httpResponse.ReasonPhrase = ParserData.HttpReasonPhrase;
4041
httpResponse.Version = new Version("1.2");
4142
AddMessageHeaders(httpResponse.Headers);
42-
if (containsEntity)
43-
{
44-
httpResponse.Content = new StringContent(ParserData.HttpMessageEntity);
45-
}
43+
httpResponse.Content =
44+
containsEntity ? new StringContent(ParserData.HttpMessageEntity) : new StreamContent(Stream.Null);
4645

4746
return httpResponse;
4847
}
@@ -76,6 +75,7 @@ private static async Task ValidateRequest(HttpContent content, bool containsEnti
7675
private static async Task ValidateResponse(HttpContent content, bool containsEntity)
7776
{
7877
Assert.Equal(ParserData.HttpResponseMediaType, content.Headers.ContentType);
78+
7979
long? length = content.Headers.ContentLength;
8080
Assert.NotNull(length);
8181

@@ -164,7 +164,6 @@ public async Task SerializeRequestMultipleTimes()
164164
}
165165
}
166166

167-
#if !NET6_0_OR_GREATER // https://github.com/aspnet/AspNetWebStack/issues/386
168167
[Fact]
169168
public async Task SerializeResponse()
170169
{
@@ -186,7 +185,6 @@ public async Task SerializeResponseMultipleTimes()
186185
await ValidateResponse(instance, false);
187186
}
188187
}
189-
#endif
190188

191189
[Fact]
192190
public async Task SerializeRequestWithEntity()
@@ -243,7 +241,6 @@ public async Task SerializeRequestAsync()
243241
}
244242
}
245243

246-
#if !NET6_0_OR_GREATER // https://github.com/aspnet/AspNetWebStack/issues/386
247244
[Fact]
248245
public async Task SerializeResponseAsync()
249246
{
@@ -254,7 +251,6 @@ public async Task SerializeResponseAsync()
254251
await ValidateResponse(instance, false);
255252
}
256253
}
257-
#endif
258254

259255
[Fact]
260256
public async Task SerializeRequestWithPortAndQueryAsync()

Diff for: test/System.Net.Http.Formatting.Test/ParserData.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,15 @@ public static TheoryDataSet<string> InvalidStatusCodes
198198
((int)HttpStatus).ToString() +
199199
" " +
200200
HttpReasonPhrase +
201-
"\r\nN1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\n\r\n";
201+
"\r\nN1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\nContent-Length: 0\r\n\r\n";
202202

203203
public static readonly string HttpRequestWithEntity =
204204
HttpMethod +
205205
" /some/path HTTP/1.2\r\nHost: " +
206206
HttpHostName +
207207
"\r\nN1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\nContent-Type: " +
208208
TextContentType +
209+
"\r\nContent-Length: 100" +
209210
"\r\n\r\n" +
210211
HttpMessageEntity;
211212

@@ -216,6 +217,7 @@ public static TheoryDataSet<string> InvalidStatusCodes
216217
HttpReasonPhrase +
217218
"\r\nN1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\nContent-Type: " +
218219
TextContentType +
220+
"\r\nContent-Length: 100" +
219221
"\r\n\r\n" +
220222
HttpMessageEntity;
221223
}

Diff for: test/System.Web.Http.Test/Controllers/VoidResultConverterTest.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public void Convert_ReturnsResponseMessageWithRequestAssignedAndNoContentToRefle
3131
var result = _converter.Convert(_context, null);
3232

3333
Assert.Equal(HttpStatusCode.NoContent, result.StatusCode);
34-
Assert.Null(result.Content);
34+
Assert.NotNull(result.Content);
35+
Assert.Equal(0L, result.Content.Headers.ContentLength);
3536
Assert.Same(_request, result.RequestMessage);
3637
}
3738
}

Diff for: test/System.Web.Http.WebHost.Test/WebHostExceptionHandlerTests.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ public async Task HandleAsync_IfCatchBlockIsWebHostBufferedContent_WithCreateExc
220220
{
221221
Assert.NotNull(response);
222222
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
223-
Assert.Null(response.Content);
223+
Assert.NotNull(response.Content);
224+
Assert.Equal(0L, response.Content.Headers.ContentLength);
224225
Assert.Same(expectedRequest, response.RequestMessage);
225226
}
226227
}

0 commit comments

Comments
 (0)