Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement more ServicePoint properties #97537

Merged
merged 41 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
97d4730
Implement more ServicePoint properties
liveans Jan 26, 2024
b9fdca4
Implement more properties
liveans Jan 31, 2024
69055bb
Make implementation correct
liveans Feb 1, 2024
3591f1a
Change UseNagleAlgorithm default to false
liveans Feb 1, 2024
d861319
Update src/libraries/System.Net.Requests/src/System/Net/HttpWebReques…
liveans Feb 5, 2024
340d7e4
Merge branch 'main' into httpwebrequest-implement-more-property
liveans Feb 5, 2024
043647c
Review feedback
liveans Feb 9, 2024
f843727
Start implement DnsRoundRobin
liveans Feb 9, 2024
f296718
Merge branch 'main' into httpwebrequest-implement-more-property
liveans Feb 9, 2024
c61e5ed
remove passing parameter over ctor for static prop
liveans Feb 9, 2024
30bd75c
Change DefaultMaximumErrorResponseLength default value to -1
liveans Feb 9, 2024
cf3003d
Update ServicePoint parameter passing, implement Certificate on Servi…
liveans Feb 11, 2024
33abd52
Change servicePoint to nullable parameter again and fix bind test
liveans Feb 12, 2024
3033c4a
Change static variable test to RemoteExecutor
liveans Feb 12, 2024
2ee83de
Fix bind throw test for linux and add async to remote executor
liveans Feb 12, 2024
3be4532
Update src/libraries/System.Net.Requests/src/System/Net/HttpWebReques…
liveans Feb 15, 2024
f1ea117
Review feedback
liveans Feb 15, 2024
9260105
Merge branch 'httpwebrequest-implement-more-property' of github.com:l…
liveans Feb 15, 2024
8d28f97
Skip throw bind on Linux
liveans Feb 16, 2024
970bbb1
Add disable reuseaddr
liveans Feb 18, 2024
d9ef66d
Revert "Add disable reuseaddr"
liveans Feb 18, 2024
67655be
some changes on tests
liveans Feb 18, 2024
86f156c
Refactor GetResponseStream method to use TruncatedReadStream
liveans Feb 20, 2024
ca6b379
Fix tests
liveans Feb 20, 2024
89040f6
Convert static property test to RemoteExecutor
liveans Feb 20, 2024
66f7abd
Delete unused NameResolution project from Requests csproj
liveans Feb 20, 2024
50a0f2f
Revert "Delete unused NameResolution project from Requests csproj"
liveans Feb 20, 2024
f9cb55f
Fix socket shutdown
liveans Feb 20, 2024
8c3f0c4
simple changes on test
liveans Feb 20, 2024
a5c136a
Change sync call inside RemoteExecutor
liveans Feb 22, 2024
1d9708d
Update src/libraries/System.Net.Requests/src/System/Net/HttpWebRespon…
liveans Feb 27, 2024
ab78cd1
Merge branch 'main' into httpwebrequest-implement-more-property
liveans Feb 27, 2024
06f8d95
Apply suggestions from code review
liveans Feb 28, 2024
05af2e0
Review feedback
liveans Feb 28, 2024
47d7f27
Change exception handling on remote executor test
liveans Feb 28, 2024
7aa734f
Change connection logic
liveans Feb 28, 2024
657a334
Revert "Change exception handling on remote executor test"
liveans Feb 28, 2024
06f1324
Add forgotten disposal
liveans Feb 28, 2024
520ba04
Revert "Change connection logic"
liveans Feb 28, 2024
a9368bf
Review feedback
liveans Feb 28, 2024
f8cdea3
Apply suggestions from code review
liveans Mar 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
<Reference Include="System.Diagnostics.Tracing" />
<Reference Include="System.Memory" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.NameResolution" />
<Reference Include="System.Net.Primitives" />
<Reference Include="System.Net.Security" />
<Reference Include="System.Net.Sockets" />
Expand Down
86 changes: 69 additions & 17 deletions src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Cache;
using System.Net.Http;
using System.Net.Http.Headers;
Expand Down Expand Up @@ -420,11 +421,7 @@ public string? Referer
/// <devdoc>
/// <para>Sets the media type header</para>
/// </devdoc>
public string? MediaType
{
get;
set;
}
public string? MediaType { get; set; }

/// <devdoc>
/// <para>
Expand Down Expand Up @@ -681,11 +678,7 @@ public static int DefaultMaximumResponseHeadersLength
}
}

// NOP
public static int DefaultMaximumErrorResponseLength
{
get; set;
}
public static int DefaultMaximumErrorResponseLength { get; set; } = -1;
liveans marked this conversation as resolved.
Show resolved Hide resolved

private static RequestCachePolicy? _defaultCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
private static bool _isDefaultCachePolicySet;
Expand Down Expand Up @@ -806,10 +799,12 @@ public Version ProtocolVersion
if (value.Equals(HttpVersion.Version11))
{
IsVersionHttp10 = false;
ServicePoint.ProtocolVersion = HttpVersion.Version11;
MihaZupan marked this conversation as resolved.
Show resolved Hide resolved
}
else if (value.Equals(HttpVersion.Version10))
{
IsVersionHttp10 = true;
ServicePoint.ProtocolVersion = HttpVersion.Version10;
}
else
{
Expand Down Expand Up @@ -1621,6 +1616,13 @@ private static HttpClient CreateHttpClient(HttpClientParameters parameters, Http
handler.UseCookies = false;
}

if (parameters.ServicePoint is { } servicePoint)
{
handler.MaxConnectionsPerServer = servicePoint.ConnectionLimit;
handler.PooledConnectionIdleTimeout = TimeSpan.FromMilliseconds(servicePoint.MaxIdleTime);
liveans marked this conversation as resolved.
Show resolved Hide resolved
handler.PooledConnectionLifetime = TimeSpan.FromMilliseconds(servicePoint.ConnectionLeaseTimeout);
}

Debug.Assert(handler.UseProxy); // Default of handler.UseProxy is true.
Debug.Assert(handler.Proxy == null); // Default of handler.Proxy is null.

Expand All @@ -1638,7 +1640,7 @@ private static HttpClient CreateHttpClient(HttpClientParameters parameters, Http
{
handler.UseProxy = false;
}
else if (!object.ReferenceEquals(parameters.Proxy, WebRequest.GetSystemWebProxy()))
else if (!ReferenceEquals(parameters.Proxy, GetSystemWebProxy()))
{
handler.Proxy = parameters.Proxy;
}
Expand All @@ -1659,10 +1661,19 @@ private static HttpClient CreateHttpClient(HttpClientParameters parameters, Http
handler.SslOptions.EnabledSslProtocols = (SslProtocols)parameters.SslProtocols;
handler.SslOptions.CertificateRevocationCheckMode = parameters.CheckCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck;
RemoteCertificateValidationCallback? rcvc = parameters.ServerCertificateValidationCallback;
if (rcvc != null)
handler.SslOptions.RemoteCertificateValidationCallback = (message, cert, chain, errors) =>
{
handler.SslOptions.RemoteCertificateValidationCallback = (message, cert, chain, errors) => rcvc(request!, cert, chain, errors);
}
if (parameters.ServicePoint is { } servicePoint)
{
servicePoint.Certificate = cert;
}
if (rcvc is not null)
liveans marked this conversation as resolved.
Show resolved Hide resolved
{
return rcvc(request!, cert, chain, errors);
}

return errors == SslPolicyErrors.None;
};

// Set up a ConnectCallback so that we can control Socket-specific settings, like ReadWriteTimeout => socket.Send/ReceiveTimeout.
handler.ConnectCallback = async (context, cancellationToken) =>
Expand All @@ -1671,6 +1682,10 @@ private static HttpClient CreateHttpClient(HttpClientParameters parameters, Http

try
{
//Start dns resolve
IPAddress[] addresses = parameters.Async ?
await Dns.GetHostAddressesAsync(context.DnsEndPoint.Host, cancellationToken).ConfigureAwait(false) :
Dns.GetHostAddresses(context.DnsEndPoint.Host);
liveans marked this conversation as resolved.
Show resolved Hide resolved
if (parameters.ServicePoint is { } servicePoint)
{
if (servicePoint.ReceiveBufferSize != -1)
Expand All @@ -1684,19 +1699,56 @@ private static HttpClient CreateHttpClient(HttpClientParameters parameters, Http
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, keepAlive.Time);
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, keepAlive.Interval);
}

BindHelper(servicePoint, addresses, socket, context.DnsEndPoint.Port);
static void BindHelper(ServicePoint servicePoint, IPAddress[] addresses, Socket socket, int port)
{
if (servicePoint.BindIPEndPointDelegate is null)
{
return;
}

const int MaxRetries = 100;
foreach (IPAddress address in addresses)
{
int retryCount = 0;
for (; retryCount < MaxRetries; retryCount++)
{
IPEndPoint? endPoint = servicePoint.BindIPEndPointDelegate(servicePoint, new IPEndPoint(address, port), retryCount);
if (endPoint is null) // Get other address to try
{
break;
}
try
liveans marked this conversation as resolved.
Show resolved Hide resolved
{
socket.Bind(endPoint);
return; // Bind successful, exit loops.
liveans marked this conversation as resolved.
Show resolved Hide resolved
}
catch
{
continue;
}
}

if (retryCount >= MaxRetries)
{
throw new OverflowException(); //TODO (aaksoy): Add SR for this.
liveans marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

socket.NoDelay = true;
socket.NoDelay = !(parameters.ServicePoint?.UseNagleAlgorithm) ?? true;

if (parameters.Async)
{
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false);
await socket.ConnectAsync(addresses, context.DnsEndPoint.Port, cancellationToken).ConfigureAwait(false);
}
else
{
using (cancellationToken.UnsafeRegister(s => ((Socket)s!).Dispose(), socket))
{
socket.Connect(context.DnsEndPoint);
socket.Connect(addresses, context.DnsEndPoint.Port);
}

// Throw in case cancellation caused the socket to be disposed after the Connect completed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Net.Http;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace System.Net
{
Expand Down Expand Up @@ -337,7 +339,14 @@ public override Stream GetResponseStream()
CheckDisposed();
if (_httpResponseMessage.Content != null)
{
return _httpResponseMessage.Content.ReadAsStream();
Stream contentStream = _httpResponseMessage.Content.ReadAsStream();
int maxErrorResponseLength = HttpWebRequest.DefaultMaximumErrorResponseLength;
if (maxErrorResponseLength < 0 || StatusCode < HttpStatusCode.BadRequest)
{
return contentStream;
}

return new TruncatedReadStream(contentStream, maxErrorResponseLength);
}

return Stream.Null;
Expand Down Expand Up @@ -371,5 +380,53 @@ private void CheckDisposed()
}

private static string GetHeaderValueAsString(IEnumerable<string> values) => string.Join(", ", values);

internal sealed class TruncatedReadStream(Stream innerStream, int maxSize) : Stream
{
public override bool CanRead => true;

public override bool CanSeek => false;

public override bool CanWrite => false;

public override long Length => throw new NotSupportedException();

public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }

public override void Flush() => innerStream.Flush();
public override Task FlushAsync(CancellationToken cancellationToken) => base.FlushAsync(cancellationToken);
liveans marked this conversation as resolved.
Show resolved Hide resolved
public override int Read(byte[] buffer, int offset, int count)
{
return Read(new Span<byte>(buffer, offset, count));
}
public override int Read(Span<byte> buffer)
{
int readBytes = innerStream.Read(buffer.Slice(0, Math.Min(buffer.Length, maxSize)));
maxSize -= readBytes;
return readBytes;
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
}
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
int readBytes = await innerStream.ReadAsync(buffer.Slice(0, Math.Min(buffer.Length, maxSize)), cancellationToken)
.ConfigureAwait(false);
maxSize -= readBytes;
return readBytes;
}
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
public override ValueTask DisposeAsync() => base.DisposeAsync();
protected override void Dispose(bool disposing)
{
if (disposing)
{
innerStream.Dispose();
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public static int MaxServicePointIdleTime
}
}

public static bool UseNagleAlgorithm { get; set; } = true;
public static bool UseNagleAlgorithm { get; set; }

public static bool Expect100Continue { get; set; } = true;

Expand Down
Loading
Loading