private bool _preAuthenticate;
private DecompressionMethods _automaticDecompression = HttpHandlerDefaults.DefaultAutomaticDecompression;
+ private static readonly object s_syncRoot = new object();
+ private static volatile HttpClient s_cachedHttpClient;
+ private static HttpClientParameters s_cachedHttpClientParameters;
+
//these should be safe.
[Flags]
private enum Booleans : uint
IsWebSocketRequest = 0x00000800,
Default = AllowAutoRedirect | AllowWriteStreamBuffering | ExpectContinue
}
+
+ private class HttpClientParameters
+ {
+ public readonly DecompressionMethods AutomaticDecompression;
+ public readonly bool AllowAutoRedirect;
+ public readonly int MaximumAutomaticRedirections;
+ public readonly int MaximumResponseHeadersLength;
+ public readonly bool PreAuthenticate;
+ public readonly TimeSpan Timeout;
+ public readonly SecurityProtocolType SslProtocols;
+ public readonly bool CheckCertificateRevocationList;
+ public readonly ICredentials Credentials;
+ public readonly IWebProxy Proxy;
+ public readonly RemoteCertificateValidationCallback ServerCertificateValidationCallback;
+ public readonly X509CertificateCollection ClientCertificates;
+ public readonly CookieContainer CookieContainer;
+
+ public HttpClientParameters(HttpWebRequest webRequest)
+ {
+ AutomaticDecompression = webRequest.AutomaticDecompression;
+ AllowAutoRedirect = webRequest.AllowAutoRedirect;
+ MaximumAutomaticRedirections = webRequest.MaximumAutomaticRedirections;
+ MaximumResponseHeadersLength = webRequest.MaximumResponseHeadersLength;
+ PreAuthenticate = webRequest.PreAuthenticate;
+ Timeout = webRequest.Timeout == Threading.Timeout.Infinite
+ ? Threading.Timeout.InfiniteTimeSpan
+ : TimeSpan.FromMilliseconds(webRequest.Timeout);
+ SslProtocols = ServicePointManager.SecurityProtocol;
+ CheckCertificateRevocationList = ServicePointManager.CheckCertificateRevocationList;
+ Credentials = webRequest._credentials;
+ Proxy = webRequest._proxy;
+ ServerCertificateValidationCallback = webRequest.ServerCertificateValidationCallback ?? ServicePointManager.ServerCertificateValidationCallback;
+ ClientCertificates = webRequest._clientCertificates;
+ CookieContainer = webRequest._cookieContainer;
+ }
+
+ public bool Matches(HttpClientParameters requestParameters)
+ {
+ return AutomaticDecompression == requestParameters.AutomaticDecompression
+ && AllowAutoRedirect == requestParameters.AllowAutoRedirect
+ && MaximumAutomaticRedirections == requestParameters.MaximumAutomaticRedirections
+ && MaximumResponseHeadersLength == requestParameters.MaximumResponseHeadersLength
+ && PreAuthenticate == requestParameters.PreAuthenticate
+ && Timeout == requestParameters.Timeout
+ && SslProtocols == requestParameters.SslProtocols
+ && CheckCertificateRevocationList == requestParameters.CheckCertificateRevocationList
+ && ReferenceEquals(Credentials, requestParameters.Credentials)
+ && ReferenceEquals(Proxy, requestParameters.Proxy)
+ && ReferenceEquals(ServerCertificateValidationCallback, requestParameters.ServerCertificateValidationCallback)
+ && ReferenceEquals(ClientCertificates, requestParameters.ClientCertificates)
+ && ReferenceEquals(CookieContainer, requestParameters.CookieContainer);
+ }
+
+ public bool AreParametersAcceptableForCaching()
+ {
+ return Credentials == null
+ && ReferenceEquals(Proxy, DefaultWebProxy)
+ && ServerCertificateValidationCallback == null
+ && ClientCertificates == null
+ && CookieContainer == null;
+ }
+ }
+
private const string ContinueHeader = "100-continue";
private const string ChunkedHeader = "chunked";
throw new InvalidOperationException(SR.net_reqsubmitted);
}
- var handler = new HttpClientHandler();
var request = new HttpRequestMessage(new HttpMethod(_originVerb), _requestUri);
- using (var client = new HttpClient(handler))
+ bool disposeRequired = false;
+ HttpClient client = null;
+ try
{
+ client = GetCachedOrCreateHttpClient(out disposeRequired);
if (_requestStream != null)
{
ArraySegment<byte> bytes = _requestStream.GetBuffer();
request.Content = new ByteArrayContent(bytes.Array, bytes.Offset, bytes.Count);
}
- handler.AutomaticDecompression = AutomaticDecompression;
- handler.Credentials = _credentials;
- handler.AllowAutoRedirect = AllowAutoRedirect;
- handler.MaxAutomaticRedirections = MaximumAutomaticRedirections;
- handler.MaxResponseHeadersLength = MaximumResponseHeadersLength;
- handler.PreAuthenticate = PreAuthenticate;
- client.Timeout = Timeout == Threading.Timeout.Infinite ?
- Threading.Timeout.InfiniteTimeSpan :
- TimeSpan.FromMilliseconds(Timeout);
-
- if (_cookieContainer != null)
- {
- handler.CookieContainer = _cookieContainer;
- Debug.Assert(handler.UseCookies); // Default of handler.UseCookies is true.
- }
- else
- {
- handler.UseCookies = false;
- }
-
- Debug.Assert(handler.UseProxy); // Default of handler.UseProxy is true.
- Debug.Assert(handler.Proxy == null); // Default of handler.Proxy is null.
-
- // HttpClientHandler default is to use a proxy which is the system proxy.
- // This is indicated by the properties 'UseProxy == true' and 'Proxy == null'.
- //
- // However, HttpWebRequest doesn't have a separate 'UseProxy' property. Instead,
- // the default of the 'Proxy' property is a non-null IWebProxy object which is the
- // system default proxy object. If the 'Proxy' property were actually null, then
- // that means don't use any proxy.
- //
- // So, we need to map the desired HttpWebRequest proxy settings to equivalent
- // HttpClientHandler settings.
- if (_proxy == null)
- {
- handler.UseProxy = false;
- }
- else if (!object.ReferenceEquals(_proxy, WebRequest.GetSystemWebProxy()))
- {
- handler.Proxy = _proxy;
- }
- else
- {
- // Since this HttpWebRequest is using the default system proxy, we need to
- // pass any proxy credentials that the developer might have set via the
- // WebRequest.DefaultWebProxy.Credentials property.
- handler.DefaultProxyCredentials = _proxy.Credentials;
- }
-
- handler.ClientCertificates.AddRange(ClientCertificates);
-
- // Set relevant properties from ServicePointManager
- handler.SslProtocols = (SslProtocols)ServicePointManager.SecurityProtocol;
- handler.CheckCertificateRevocationList = ServicePointManager.CheckCertificateRevocationList;
- RemoteCertificateValidationCallback rcvc = ServerCertificateValidationCallback != null ?
- ServerCertificateValidationCallback :
- ServicePointManager.ServerCertificateValidationCallback;
- if (rcvc != null)
- {
- RemoteCertificateValidationCallback localRcvc = rcvc;
- handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => localRcvc(this, cert, chain, errors);
- }
-
if (_hostUri != null)
{
request.Headers.Host = Host;
return response;
}
+ finally
+ {
+ if (disposeRequired)
+ {
+ client?.Dispose();
+ }
+ }
}
public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state)
string s = Address.Scheme + "://" + hostName + Address.PathAndQuery;
return Uri.TryCreate(s, UriKind.Absolute, out hostUri);
}
+
+ private HttpClient GetCachedOrCreateHttpClient(out bool disposeRequired)
+ {
+ var parameters = new HttpClientParameters(this);
+ if (parameters.AreParametersAcceptableForCaching())
+ {
+ disposeRequired = false;
+ if (s_cachedHttpClient == null)
+ {
+ lock (s_syncRoot)
+ {
+ if (s_cachedHttpClient == null)
+ {
+ s_cachedHttpClientParameters = parameters;
+ s_cachedHttpClient = CreateHttpClient(parameters, null);
+ return s_cachedHttpClient;
+ }
+ }
+ }
+
+ if (s_cachedHttpClientParameters.Matches(parameters))
+ {
+ return s_cachedHttpClient;
+ }
+ }
+
+ disposeRequired = true;
+ return CreateHttpClient(parameters, this);
+ }
+
+ private static HttpClient CreateHttpClient(HttpClientParameters parameters, HttpWebRequest request)
+ {
+ HttpClient client = null;
+ try
+ {
+ var handler = new HttpClientHandler();
+ client = new HttpClient(handler);
+ handler.AutomaticDecompression = parameters.AutomaticDecompression;
+ handler.Credentials = parameters.Credentials;
+ handler.AllowAutoRedirect = parameters.AllowAutoRedirect;
+ handler.MaxAutomaticRedirections = parameters.MaximumAutomaticRedirections;
+ handler.MaxResponseHeadersLength = parameters.MaximumResponseHeadersLength;
+ handler.PreAuthenticate = parameters.PreAuthenticate;
+ client.Timeout = parameters.Timeout;
+
+ if (parameters.CookieContainer != null)
+ {
+ handler.CookieContainer = parameters.CookieContainer;
+ Debug.Assert(handler.UseCookies); // Default of handler.UseCookies is true.
+ }
+ else
+ {
+ handler.UseCookies = false;
+ }
+
+ Debug.Assert(handler.UseProxy); // Default of handler.UseProxy is true.
+ Debug.Assert(handler.Proxy == null); // Default of handler.Proxy is null.
+
+ // HttpClientHandler default is to use a proxy which is the system proxy.
+ // This is indicated by the properties 'UseProxy == true' and 'Proxy == null'.
+ //
+ // However, HttpWebRequest doesn't have a separate 'UseProxy' property. Instead,
+ // the default of the 'Proxy' property is a non-null IWebProxy object which is the
+ // system default proxy object. If the 'Proxy' property were actually null, then
+ // that means don't use any proxy.
+ //
+ // So, we need to map the desired HttpWebRequest proxy settings to equivalent
+ // HttpClientHandler settings.
+ if (parameters.Proxy == null)
+ {
+ handler.UseProxy = false;
+ }
+ else if (!object.ReferenceEquals(parameters.Proxy, WebRequest.GetSystemWebProxy()))
+ {
+ handler.Proxy = parameters.Proxy;
+ }
+ else
+ {
+ // Since this HttpWebRequest is using the default system proxy, we need to
+ // pass any proxy credentials that the developer might have set via the
+ // WebRequest.DefaultWebProxy.Credentials property.
+ handler.DefaultProxyCredentials = parameters.Proxy.Credentials;
+ }
+
+ if (parameters.ClientCertificates != null)
+ {
+ handler.ClientCertificates.AddRange(parameters.ClientCertificates);
+ }
+
+ // Set relevant properties from ServicePointManager
+ handler.SslProtocols = (SslProtocols)parameters.SslProtocols;
+ handler.CheckCertificateRevocationList = parameters.CheckCertificateRevocationList;
+ RemoteCertificateValidationCallback rcvc = parameters.ServerCertificateValidationCallback;
+ if (rcvc != null)
+ {
+ RemoteCertificateValidationCallback localRcvc = rcvc;
+ HttpWebRequest localRequest = request;
+ handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => localRcvc(localRequest, cert, chain, errors);
+ }
+
+ return client;
+ }
+ catch
+ {
+ client?.Dispose();
+ throw;
+ }
+ }
}
}
using System.Linq;
using System.Net.Cache;
using System.Net.Http;
+using System.Net.Security;
using System.Net.Sockets;
using System.Net.Test.Common;
using System.Runtime.Serialization.Formatters.Binary;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
using System.Text;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DotNet.RemoteExecutor;
public partial class HttpWebRequestTest
{
+ public class HttpWebRequestParameters
+ {
+ public DecompressionMethods AutomaticDecompression { get; set; }
+ public bool AllowAutoRedirect { get; set; }
+ public int MaximumAutomaticRedirections { get; set; }
+ public int MaximumResponseHeadersLength { get; set; }
+ public bool PreAuthenticate { get; set; }
+ public int Timeout { get; set; }
+ public SecurityProtocolType SslProtocols { get; set; }
+ public bool CheckCertificateRevocationList { get; set; }
+ public bool NewCredentials { get; set; }
+ public bool NewProxy { get; set; }
+ public bool NewServerCertificateValidationCallback { get; set; }
+ public bool NewClientCertificates { get; set; }
+ public bool NewCookieContainer { get; set; }
+
+ public void Configure(HttpWebRequest webRequest)
+ {
+ webRequest.AutomaticDecompression = AutomaticDecompression;
+ webRequest.AllowAutoRedirect = AllowAutoRedirect;
+ webRequest.MaximumAutomaticRedirections = MaximumAutomaticRedirections;
+ webRequest.MaximumResponseHeadersLength = MaximumResponseHeadersLength;
+ webRequest.PreAuthenticate = PreAuthenticate;
+ webRequest.Timeout = Timeout;
+ ServicePointManager.SecurityProtocol = SslProtocols;
+ ServicePointManager.CheckCertificateRevocationList = CheckCertificateRevocationList;
+ if (NewCredentials)
+ webRequest.Credentials = CredentialCache.DefaultCredentials;
+ if (NewProxy)
+ webRequest.Proxy = new WebProxy();
+ if (NewServerCertificateValidationCallback)
+ ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
+ if (NewClientCertificates)
+ webRequest.ClientCertificates = new X509CertificateCollection();
+ if (NewCookieContainer)
+ webRequest.CookieContainer = new CookieContainer();
+ }
+ }
+
private const string RequestBody = "This is data to POST.";
private readonly byte[] _requestBodyBytes = Encoding.UTF8.GetBytes(RequestBody);
private readonly NetworkCredential _explicitCredential = new NetworkCredential("user", "password", "domain");
public static readonly object[][] EchoServers = Configuration.Http.EchoServers;
+ public static IEnumerable<object[]> CachableWebRequestParameters()
+ {
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 10000}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.Deflate,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 10000}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 3, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 10000}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 110, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 10000}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = false, SslProtocols = SecurityProtocolType.Tls12, Timeout = 10000}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls11, Timeout = 10000}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 10250}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 100000}, true};
+ }
+
+ public static IEnumerable<object[]> MixedWebRequestParameters()
+ {
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 100000}, true};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 10000,
+ NewServerCertificateValidationCallback = true }, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 100000,
+ NewCredentials = true}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 100000,
+ NewProxy = true}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 100000,
+ NewClientCertificates = true}, false};
+ yield return new object[] {new HttpWebRequestParameters { AllowAutoRedirect = true, AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2, MaximumResponseHeadersLength = 100, PreAuthenticate = true, SslProtocols = SecurityProtocolType.Tls12, Timeout = 100000,
+ NewCookieContainer = true}, false};
+ }
+
public static IEnumerable<object[]> Dates_ReadValue_Data()
{
var zero_formats = new[]
WebRequest.DefaultWebProxy.Credentials = new NetworkCredential(user, pw);
HttpWebRequest request = HttpWebRequest.CreateHttp(Configuration.Http.RemoteEchoServer);
- using (var response = (HttpWebResponse) await request.GetResponseAsync())
+ using (var response = (HttpWebResponse)await request.GetResponseAsync())
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
Assert.Equal(MediaType, request.MediaType);
}
+ [Theory, MemberData(nameof(MixedWebRequestParameters))]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)]
+ public void GetResponseAsync_ParametersAreNotCachable_CreateNewClient(HttpWebRequestParameters requestParameters, bool connectionReusedParameter)
+ {
+ RemoteExecutor.Invoke(async (serializedParameters, connectionReusedString) =>
+ {
+ var parameters = JsonSerializer.Deserialize<HttpWebRequestParameters>(serializedParameters);
+
+ using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+ {
+ listener.Bind(new IPEndPoint(IPAddress.Loopback, 0));
+ listener.Listen(1);
+ var ep = (IPEndPoint)listener.LocalEndPoint;
+ var uri = new Uri($"http://{ep.Address}:{ep.Port}/");
+
+ HttpWebRequest request0 = WebRequest.CreateHttp(uri);
+ HttpWebRequest request1 = WebRequest.CreateHttp(uri);
+ parameters.Configure(request0);
+ parameters.Configure(request1);
+ request0.Method = HttpMethod.Get.Method;
+ request1.Method = HttpMethod.Get.Method;
+
+ string responseContent = "Test response.";
+
+ Task<WebResponse> firstResponseTask = request0.GetResponseAsync();
+ using (Socket server = await listener.AcceptAsync())
+ using (var serverStream = new NetworkStream(server, ownsSocket: false))
+ using (var serverReader = new StreamReader(serverStream))
+ {
+ await ReplyToClient(responseContent, server, serverReader);
+ await VerifyResponse(responseContent, firstResponseTask);
+
+ Task<Socket> secondAccept = listener.AcceptAsync();
+
+ Task<WebResponse> secondResponseTask = request1.GetResponseAsync();
+ await ReplyToClient(responseContent, server, serverReader);
+ if (bool.Parse(connectionReusedString))
+ {
+ Assert.False(secondAccept.IsCompleted);
+ await VerifyResponse(responseContent, secondResponseTask);
+ }
+ else
+ {
+ await VerifyNewConnection(responseContent, secondAccept, secondResponseTask);
+ }
+ }
+ }
+ return RemoteExecutor.SuccessExitCode;
+ }, JsonSerializer.Serialize<HttpWebRequestParameters>(requestParameters), connectionReusedParameter.ToString()).Dispose();
+ }
+
+ [Fact]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)]
+ public void GetResponseAsync_ParametersAreCachableButDifferent_CreateNewClient()
+ {
+ RemoteExecutor.Invoke(async () =>
+ {
+ using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+ {
+ listener.Bind(new IPEndPoint(IPAddress.Loopback, 0));
+ listener.Listen(1);
+ var ep = (IPEndPoint)listener.LocalEndPoint;
+ var uri = new Uri($"http://{ep.Address}:{ep.Port}/");
+
+ var referenceParameters = new HttpWebRequestParameters
+ {
+ AllowAutoRedirect = true,
+ AutomaticDecompression = DecompressionMethods.GZip,
+ MaximumAutomaticRedirections = 2,
+ MaximumResponseHeadersLength = 100,
+ PreAuthenticate = true,
+ SslProtocols = SecurityProtocolType.Tls12,
+ Timeout = 100000
+ };
+ HttpWebRequest firstRequest = WebRequest.CreateHttp(uri);
+ referenceParameters.Configure(firstRequest);
+ firstRequest.Method = HttpMethod.Get.Method;
+
+ string responseContent = "Test response.";
+
+ Task<WebResponse> firstResponseTask = firstRequest.GetResponseAsync();
+ using (Socket server = await listener.AcceptAsync())
+ using (var serverStream = new NetworkStream(server, ownsSocket: false))
+ using (var serverReader = new StreamReader(serverStream))
+ {
+ await ReplyToClient(responseContent, server, serverReader);
+ await VerifyResponse(responseContent, firstResponseTask);
+
+ foreach (object[] caseRow in CachableWebRequestParameters())
+ {
+ var currentParameters = (HttpWebRequestParameters)caseRow[0];
+ bool connectionReused = (bool)caseRow[1];
+ Task<Socket> secondAccept = listener.AcceptAsync();
+
+ HttpWebRequest currentRequest = WebRequest.CreateHttp(uri);
+ currentParameters.Configure(currentRequest);
+
+ Task<WebResponse> currentResponseTask = currentRequest.GetResponseAsync();
+ if (connectionReused)
+ {
+ await ReplyToClient(responseContent, server, serverReader);
+ Assert.False(secondAccept.IsCompleted);
+ await VerifyResponse(responseContent, currentResponseTask);
+ }
+ else
+ {
+ await VerifyNewConnection(responseContent, secondAccept, currentResponseTask);
+ }
+ }
+ }
+ }
+ return RemoteExecutor.SuccessExitCode;
+ }).Dispose();
+ }
+
[Fact]
public async Task HttpWebRequest_EndGetRequestStreamContext_ExpectedValue()
{
}
}
+ private static async Task VerifyNewConnection(string responseContent, Task<Socket> secondAccept, Task<WebResponse> currentResponseTask)
+ {
+ Socket secondServer = await secondAccept;
+ Assert.True(secondAccept.IsCompleted);
+ using (var secondStream = new NetworkStream(secondServer, ownsSocket: false))
+ using (var secondReader = new StreamReader(secondStream))
+ {
+ await ReplyToClient(responseContent, secondServer, secondReader);
+ await VerifyResponse(responseContent, currentResponseTask);
+ }
+ }
+
+ private static async Task ReplyToClient(string responseContent, Socket server, StreamReader serverReader)
+ {
+ string responseBody =
+ "HTTP/1.1 200 OK\r\n" +
+ $"Date: {DateTimeOffset.UtcNow:R}\r\n" +
+ $"Content-Length: {responseContent.Length}\r\n" +
+ "\r\n" + responseContent;
+ while (!string.IsNullOrWhiteSpace(await serverReader.ReadLineAsync())) ;
+ await server.SendAsync(new ArraySegment<byte>(Encoding.ASCII.GetBytes(responseBody)), SocketFlags.None);
+ }
+
+ private static async Task VerifyResponse(string expectedResponse, Task<WebResponse> responseTask)
+ {
+ WebResponse firstRequest = await responseTask;
+ using (Stream firstResponseStream = firstRequest.GetResponseStream())
+ using (var reader = new StreamReader(firstResponseStream))
+ {
+ string response = reader.ReadToEnd();
+ Assert.Equal(expectedResponse, response);
+ }
+ }
+
[Fact]
public void HttpWebRequest_Serialize_Fails()
{