m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.RequestHeader &&
m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler";
}));
- Assert.Equal(
+ Assert.StartsWith(
@"Request Headers:
Authorization: *
Cache-Control: no-cache
m.EventId == LoggingHttpMessageHandler.Log.EventIds.RequestHeader &&
m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler";
}));
- Assert.Equal(
+ Assert.StartsWith(
@"Request Headers:
Authorization: *
Cache-Control: no-cache
m.EventId == LoggingHttpMessageHandler.Log.EventIds.ResponseHeader &&
m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler";
}));
- Assert.Equal(
+ Assert.StartsWith(
@"Response Headers:
X-Sensitive: *
Y-Non-Sensitive: innocuous value
m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.ResponseHeader &&
m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler";
}));
- Assert.Equal(
+ Assert.StartsWith(
@"Response Headers:
X-Sensitive: *
Y-Non-Sensitive: innocuous value
m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.RequestHeader &&
m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler";
}));
- Assert.Equal(
+ Assert.StartsWith(
@"Request Headers:
Authorization: *
Cache-Control: no-cache
m.EventId == LoggingHttpMessageHandler.Log.EventIds.RequestHeader &&
m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler";
}));
- Assert.Equal(
+ Assert.StartsWith(
@"Request Headers:
Authorization: *
Cache-Control: no-cache
m.EventId == LoggingHttpMessageHandler.Log.EventIds.ResponseHeader &&
m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler";
}));
- Assert.Equal(
+ Assert.StartsWith(
@"Response Headers:
X-Sensitive: *
Y-Non-Sensitive: innocuous value
m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.ResponseHeader &&
m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler";
}));
- Assert.Equal(
+ Assert.StartsWith(
@"Response Headers:
X-Sensitive: *
Y-Non-Sensitive: innocuous value
{
public HttpResponseMessage() { }
public HttpResponseMessage(System.Net.HttpStatusCode statusCode) { }
- public System.Net.Http.HttpContent? Content { get { throw null; } set { } }
+ [System.Diagnostics.CodeAnalysis.AllowNull]
+ public System.Net.Http.HttpContent Content { get { throw null; } set { } }
public System.Net.Http.Headers.HttpResponseHeaders Headers { get { throw null; } }
public bool IsSuccessStatusCode { get { throw null; } }
public string? ReasonPhrase { get { throw null; } set { } }
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<AssemblyName>System.Net.Http</AssemblyName>
<Compile Include="System\Net\Http\ByteArrayHelpers.cs" />
<Compile Include="System\Net\Http\ClientCertificateOption.cs" />
<Compile Include="System\Net\Http\DelegatingHandler.cs" />
+ <Compile Include="System\Net\Http\EmptyContent.cs" />
+ <Compile Include="System\Net\Http\EmptyReadStream.cs" />
<Compile Include="System\Net\Http\FormUrlEncodedContent.cs" />
<Compile Include="System\Net\Http\Headers\AltSvcHeaderParser.cs" />
<Compile Include="System\Net\Http\Headers\AltSvcHeaderValue.cs" />
<Compile Include="System\Net\Http\Headers\KnownHeader.cs" />
<Compile Include="System\Net\Http\Headers\HttpHeaderType.cs" />
<Compile Include="System\Net\Http\Headers\KnownHeaders.cs" />
+ <Compile Include="System\Net\Http\HttpBaseStream.cs" />
<Compile Include="System\Net\Http\HttpClient.cs" />
<Compile Include="System\Net\Http\HttpClientHandler.cs" />
<Compile Include="System\Net\Http\HttpClientHandler.Core.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\CreditManager.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\CreditWaiter.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\DecompressionHandler.cs" />
- <Compile Include="System\Net\Http\SocketsHttpHandler\EmptyReadStream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\FailedProxyCache.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2Connection.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http2ConnectionException.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\Http3RequestStream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpAuthenticatedConnectionHandler.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpAuthority.cs" />
- <Compile Include="System\Net\Http\SocketsHttpHandler\HttpBaseStream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpConnection.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpConnectionBase.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpConnectionHandler.cs" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Net.Http
+{
+ /// <summary>Provides a zero-length HttpContent implementation.</summary>
+ internal sealed class EmptyContent : HttpContent
+ {
+ protected internal override bool TryComputeLength(out long length)
+ {
+ length = 0;
+ return true;
+ }
+
+ protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) =>
+ Task.CompletedTask;
+
+ protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) =>
+ cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
+ SerializeToStreamAsync(stream, context);
+
+ protected override Task<Stream> CreateContentReadStreamAsync() =>
+ Task.FromResult<Stream>(EmptyReadStream.Instance);
+
+ protected override Task<Stream> CreateContentReadStreamAsync(CancellationToken cancellationToken) =>
+ cancellationToken.IsCancellationRequested ? Task.FromCanceled<Stream>(cancellationToken) :
+ CreateContentReadStreamAsync();
+
+ internal override Stream? TryCreateContentReadStream() => EmptyReadStream.Instance;
+
+ internal override bool AllowDuplex => false;
+ }
+}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Headers;
using System.Text;
internal void SetVersionWithoutValidation(Version value) => _version = value;
- public HttpContent? Content
+ [AllowNull]
+ public HttpContent Content
{
- get { return _content; }
+ get { return _content ??= new EmptyContent(); }
set
{
CheckDisposed();
{
Assert.Same(string.Empty, await client.GetStringAsync(CreateFakeUri()));
Assert.Same(Array.Empty<byte>(), await client.GetByteArrayAsync(CreateFakeUri()));
- Assert.Same(Stream.Null, await client.GetStreamAsync(CreateFakeUri()));
+
+ Stream s = await client.GetStreamAsync(CreateFakeUri());
+ Assert.NotNull(s);
+ Assert.Equal(-1, s.ReadByte());
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
using System.IO;
-using System.Net.Http.Headers;
-using System.Threading;
using System.Threading.Tasks;
using Xunit;
Assert.Equal(HttpStatusCode.OK, rm.StatusCode);
Assert.Equal("OK", rm.ReasonPhrase);
Assert.Equal(new Version(1, 1), rm.Version);
- Assert.Null(rm.Content);
+ Assert.NotNull(rm.Content);
Assert.Null(rm.RequestMessage);
}
}
Assert.Equal(HttpStatusCode.Accepted, rm.StatusCode);
Assert.Equal("Accepted", rm.ReasonPhrase);
Assert.Equal(new Version(1, 1), rm.Version);
- Assert.Null(rm.Content);
+ Assert.NotNull(rm.Content);
Assert.Null(rm.RequestMessage);
}
}
{
using (var rm = new HttpResponseMessage())
{
+ HttpContent c1 = rm.Content;
+ Assert.Same(c1, rm.Content);
+
rm.Content = null;
- Assert.Null(rm.Content);
+
+ HttpContent c2 = rm.Content;
+ Assert.Same(c2, rm.Content);
+
+ Assert.NotSame(c1, c2);
}
}
}
[Fact]
+ public async Task DefaultContent_ReadableNotWritable_Success()
+ {
+ var resp = new HttpResponseMessage();
+
+ HttpContent c = resp.Content;
+ Assert.NotNull(c);
+ Assert.Same(c, resp.Content);
+ Assert.NotSame(resp.Content, new HttpResponseMessage().Content);
+
+ Assert.Equal(0, c.Headers.ContentLength);
+
+ Task<Stream> t = c.ReadAsStreamAsync();
+ Assert.Equal(TaskStatus.RanToCompletion, t.Status);
+
+ Stream s = await t;
+ Assert.NotNull(s);
+
+ Assert.Equal(-1, s.ReadByte());
+ Assert.Equal(0, s.Read(new byte[1], 0, 1));
+ Assert.Equal(0, await s.ReadAsync(new byte[1], 0, 1));
+ Assert.Equal(0, await s.ReadAsync(new Memory<byte>(new byte[1])));
+
+ Assert.Throws<NotSupportedException>(() => s.WriteByte(0));
+ Assert.Throws<NotSupportedException>(() => s.Write(new byte[1], 0, 1));
+ await Assert.ThrowsAsync<NotSupportedException>(() => s.WriteAsync(new byte[1], 0, 1));
+ await Assert.ThrowsAsync<NotSupportedException>(async () => await s.WriteAsync(new ReadOnlyMemory<byte>(new byte[1])));
+ }
+
+ [Fact]
public void ToString_DefaultAndNonDefaultInstance_DumpAllFields()
{
using (var rm = new HttpResponseMessage())
using HttpResponseMessage m = await ctx.SendAsync(req);
ValidateStatusCode(m);
- ValidateServerContent(await m.Content!.ReadAsStringAsync(), expectedLength);
+ ValidateServerContent(await m.Content.ReadAsStringAsync(), expectedLength);
}),
("GET Partial",
ValidateStatusCode(m);
- using (Stream s = await m.Content!.ReadAsStreamAsync())
+ using (Stream s = await m.Content.ReadAsStreamAsync())
{
s.ReadByte(); // read single byte from response and throw the rest away
}
ValidateStatusCode(res);
- await res.Content!.ReadAsStringAsync();
+ await res.Content.ReadAsStringAsync();
bool isValidChecksum = ValidateServerChecksum(res.Headers, expectedChecksum);
string failureDetails = isValidChecksum ? "server checksum matches client checksum" : "server checksum mismatch";
using HttpResponseMessage m = await ctx.SendAsync(req);
ValidateStatusCode(m);
- ValidateContent(expectedResponse, await m.Content!.ReadAsStringAsync(), $"Uri: {uri}");
+ ValidateContent(expectedResponse, await m.Content.ReadAsStringAsync(), $"Uri: {uri}");
}),
("GET Aborted",
ValidateStatusCode(m);
string checksumMessage = ValidateServerChecksum(m.Headers, checksum) ? "server checksum matches client checksum" : "server checksum mismatch";
- ValidateContent(content, await m.Content!.ReadAsStringAsync(), checksumMessage);
+ ValidateContent(content, await m.Content.ReadAsStringAsync(), checksumMessage);
}),
("POST Multipart Data",
ValidateStatusCode(m);
string checksumMessage = ValidateServerChecksum(m.Headers, checksum) ? "server checksum matches client checksum" : "server checksum mismatch";
- ValidateContent(formData.expected, await m.Content!.ReadAsStringAsync(), checksumMessage);
+ ValidateContent(formData.expected, await m.Content.ReadAsStringAsync(), checksumMessage);
}),
("POST Duplex",
using HttpResponseMessage m = await ctx.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
ValidateStatusCode(m);
- string response = await m.Content!.ReadAsStringAsync();
+ string response = await m.Content.ReadAsStringAsync();
string checksumMessage = ValidateServerChecksum(m.TrailingHeaders, checksum, required: false) ? "server checksum matches client checksum" : "server checksum mismatch";
ValidateContent(content, await m.Content.ReadAsStringAsync(), checksumMessage);
using HttpResponseMessage m = await ctx.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
ValidateStatusCode(m);
- string response = await m.Content!.ReadAsStringAsync();
+ string response = await m.Content.ReadAsStringAsync();
// trailing headers not supported for all servers, so do not require checksums
bool isValidChecksum = ValidateServerChecksum(m.TrailingHeaders, checksum, required: false);
ValidateStatusCode(m);
string checksumMessage = ValidateServerChecksum(m.Headers, checksum) ? "server checksum matches client checksum" : "server checksum mismatch";
- ValidateContent(content, await m.Content!.ReadAsStringAsync(), checksumMessage);
+ ValidateContent(content, await m.Content.ReadAsStringAsync(), checksumMessage);
}),
("HEAD",
ValidateStatusCode(m);
- if (m.Content!.Headers.ContentLength != expectedLength)
+ if (m.Content.Headers.ContentLength != expectedLength)
{
throw new Exception($"Expected {expectedLength}, got {m.Content.Headers.ContentLength}");
}
ValidateStatusCode(m);
- string r = await m.Content!.ReadAsStringAsync();
+ string r = await m.Content.ReadAsStringAsync();
if (r != "") throw new Exception($"Got unexpected response: {r}");
}),
ValidateStatusCode(m);
- string r = await m.Content!.ReadAsStringAsync();
+ string r = await m.Content.ReadAsStringAsync();
if (r != "") throw new Exception($"Got unexpected response: {r}");
}),
using HttpResponseMessage m = await ctx.SendAsync(req);
ValidateStatusCode(m);
- ValidateServerContent(await m.Content!.ReadAsStringAsync(), expectedLength);
+ ValidateServerContent(await m.Content.ReadAsStringAsync(), expectedLength);
}),
};
Link="ProductionCode\Common\System\Threading\Tasks\TaskToApm.cs" />
<Compile Include="..\..\src\System\Net\Http\SocketsHttpHandler\AuthenticationHelper.Digest.cs"
Link="ProductionCode\System\Net\Http\SocketsHttpHandler\AuthenticationHelper.Digest.cs" />
+ <Compile Include="..\..\src\System\Net\Http\HttpBaseStream.cs"
+ Link="ProductionCode\System\Net\Http\HttpBaseStream.cs" />
<Compile Include="..\..\src\System\Net\Http\ByteArrayContent.cs"
Link="ProductionCode\System\Net\Http\ByteArrayContent.cs" />
<Compile Include="..\..\src\System\Net\Http\ClientCertificateOption.cs"
Link="ProductionCode\System\IO\DelegatingStream.cs" />
<Compile Include="..\..\src\System\Net\Http\ByteArrayHelpers.cs"
Link="ProductionCode\System\Net\Http\ByteArrayHelpers.cs" />
+ <Compile Include="..\..\src\System\Net\Http\EmptyContent.cs"
+ Link="ProductionCode\System\Net\Http\EmptyContent.cs" />
+ <Compile Include="..\..\src\System\Net\Http\EmptyReadStream.cs"
+ Link="ProductionCode\System\Net\Http\EmptyReadStream.cs" />
<Compile Include="..\..\src\System\Net\Http\FormUrlEncodedContent.cs"
Link="ProductionCode\System\Net\Http\FormUrlEncodedContent.cs" />
<Compile Include="..\..\src\System\Net\Http\Headers\AltSvcHeaderParser.cs"