public async Task GetAsync_ResponseVersion0X_ThrowsOr10(int responseMinorVersion) { bool reportAs10 = PlatformDetection.IsFullFramework; await LoopbackServer.CreateServerAsync(async (server, url) => { using (HttpClient client = CreateHttpClient()) { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); request.Version = HttpVersion.Version11; Task <HttpResponseMessage> getResponseTask = client.SendAsync(request); Task <List <string> > serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(server, $"HTTP/0.{responseMinorVersion} 200 OK\r\nDate: {DateTimeOffset.UtcNow:R}\r\nContent-Length: 0\r\n\r\n", new LoopbackServer.Options { ResponseStreamWrapper = GetStream }); if (reportAs10) { await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); using (HttpResponseMessage response = await getResponseTask) { Assert.Equal(1, response.Version.Major); Assert.Equal(0, response.Version.Minor); } } else { await Assert.ThrowsAsync <HttpRequestException>(async() => await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask)); } } }); }
public async Task GetAsync_ResponseHasNormalLineEndings_Success(string lineEnding) { await LoopbackServer.CreateServerAsync(async (server, url) => { using (HttpClient client = CreateHttpClient()) { Task <HttpResponseMessage> getResponseTask = client.GetAsync(url); Task <List <string> > serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(server, $"HTTP/1.1 200 OK{lineEnding}Date: {DateTimeOffset.UtcNow:R}{lineEnding}Server: TestServer{lineEnding}Content-Length: 0{lineEnding}{lineEnding}", new LoopbackServer.Options { ResponseStreamWrapper = GetStream }); await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); using (HttpResponseMessage response = await getResponseTask) { Assert.Equal(200, (int)response.StatusCode); Assert.Equal("OK", response.ReasonPhrase); Assert.Equal("TestServer", response.Headers.Server.ToString()); } } }); }
public async Task HttpClientHandler_MultipleAuthenticateHeaders_PicksSupported(string authenticateHeader, string supportedAuth, string unsupportedAuth) { if (PlatformDetection.IsWindowsNanoServer || (IsCurlHandler && authenticateHeader.Contains("Digest"))) { // TODO: #28065: Fix failing authentication test cases on different httpclienthandlers. return; } var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password }; await LoopbackServer.CreateServerAsync(async (server, url) => { HttpClientHandler handler = CreateHttpClientHandler(); handler.UseDefaultCredentials = false; var credentials = new CredentialCache(); credentials.Add(url, supportedAuth, new NetworkCredential(Username, Password, Domain)); credentials.Add(url, unsupportedAuth, new NetworkCredential(Username, Password, Domain)); Task serverTask = server.AcceptConnectionPerformAuthenticationAndCloseAsync(authenticateHeader); await TestHelper.WhenAllCompletedOrAnyFailed(CreateAndValidateRequest(handler, url, HttpStatusCode.OK, credentials), serverTask); }, options); }
public async Task SetDelegate_ConnectionSucceeds(SslProtocols acceptedProtocol, bool requestOnlyThisProtocol) { using (var handler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator }) using (var client = new HttpClient(handler)) { if (requestOnlyThisProtocol) { handler.SslProtocols = acceptedProtocol; } var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol }; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options), client.GetAsync(url)); }, options); } }
public async Task GetAsync_NoSpecifiedProtocol_DefaultsToTls12() { using (var handler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = LoopbackServer.AllowAllCertificates }) using (var client = new HttpClient(handler)) { var options = new LoopbackServer.Options { UseSsl = true }; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( client.GetAsync(url), LoopbackServer.AcceptSocketAsync(server, async(s, stream, reader, writer) => { Assert.Equal(SslProtocols.Tls12, Assert.IsType <SslStream>(stream).SslProtocol); await LoopbackServer.ReadWriteAcceptedAsync(s, reader, writer); return(null); }, options)); }, options); } }
public async Task ProxyTunnelRequest_GetAsync_Success() { if (IsWinHttpHandler) { return; } const string Content = "Hello world"; using (LoopbackProxyServer proxyServer = LoopbackProxyServer.Create()) { HttpClientHandler handler = CreateHttpClientHandler(); handler.Proxy = new WebProxy(proxyServer.Uri); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; using (HttpClient client = CreateHttpClient(handler)) { var options = new LoopbackServer.Options { UseSsl = true }; await LoopbackServer.CreateServerAsync(async (server, uri) => { Assert.Equal(proxyServer.Uri, handler.Proxy.GetProxy(uri)); Task <HttpResponseMessage> clientTask = client.GetAsync(uri); await server.AcceptConnectionSendResponseAndCloseAsync(content: Content); using (var response = await clientTask) { Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(Content, await response.Content.ReadAsStringAsync()); } }, options); } Assert.Contains("CONNECT", proxyServer.Requests[0].RequestLine); } }
public async Task ProxyTunnelRequest_OriginServerSendsProxyAuthChallenge_NoProxyAuthPerformed() { if (IsWinHttpHandler) { return; } using (LoopbackProxyServer proxyServer = LoopbackProxyServer.Create()) { HttpClientHandler handler = CreateHttpClientHandler(); handler.Proxy = new WebProxy(proxyServer.Uri) { Credentials = ConstructCredentials(new NetworkCredential("username", "password"), proxyServer.Uri, BasicAuth, true) }; handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; using (HttpClient client = CreateHttpClient(handler)) { var options = new LoopbackServer.Options { UseSsl = true }; await LoopbackServer.CreateServerAsync(async (server, uri) => { Assert.Equal(proxyServer.Uri, handler.Proxy.GetProxy(uri)); Task <HttpResponseMessage> clientTask = client.GetAsync(uri); await server.AcceptConnectionSendResponseAndCloseAsync(statusCode: HttpStatusCode.ProxyAuthenticationRequired, additionalHeaders: "Proxy-Authenticate: Basic"); using (var response = await clientTask) { Assert.Equal(HttpStatusCode.ProxyAuthenticationRequired, response.StatusCode); } }, options); } Assert.Contains("CONNECT", proxyServer.Requests[0].RequestLine); } }
public async Task InfiniteSingleHeader_ThrowsException() { await LoopbackServer.CreateServerAsync(async (server, url) => { using (HttpClientHandler handler = CreateHttpClientHandler()) using (HttpClient client = CreateHttpClient(handler)) { Task <HttpResponseMessage> getAsync = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); await server.AcceptConnectionAsync(async connection => { var cts = new CancellationTokenSource(); Task serverTask = Task.Run(async delegate { await connection.ReadRequestHeaderAndSendCustomResponseAsync("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nMyInfiniteHeader: "); try { while (!cts.IsCancellationRequested) { await connection.Writer.WriteAsync(new string('s', 16000)); await Task.Delay(1); } } catch { } }); Exception e = await Assert.ThrowsAsync <HttpRequestException>(() => getAsync); cts.Cancel(); if (!IsWinHttpHandler) { Assert.Contains((handler.MaxResponseHeadersLength * 1024).ToString(), e.ToString()); } await serverTask; }); } }); }
public async Task GetAsync_AddMultipleCookieHeaders_CookiesSent() { if (IsNetfxHandler) { // Netfx handler does not support custom cookie header return; } await LoopbackServer.CreateServerAsync(async (server, url) => { HttpClientHandler handler = CreateHttpClientHandler(); using (HttpClient client = new HttpClient(handler)) { HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, url); requestMessage.Headers.Add("Cookie", "A=1"); requestMessage.Headers.Add("Cookie", "B=2"); requestMessage.Headers.Add("Cookie", "C=3"); Task <HttpResponseMessage> getResponseTask = client.SendAsync(requestMessage); Task <List <string> > serverTask = server.AcceptConnectionSendResponseAndCloseAsync(); List <string> requestLines = await serverTask; Assert.Equal(1, requestLines.Count(s => s.StartsWith("Cookie: "))); // Multiple Cookie header values are treated as any other header values and are // concatenated using ", " as the separator. var cookieValues = requestLines.Single(s => s.StartsWith("Cookie: ")).Substring(8).Split(new string[] { ", " }, StringSplitOptions.None); Assert.Contains("A=1", cookieValues); Assert.Contains("B=2", cookieValues); Assert.Contains("C=3", cookieValues); Assert.Equal(3, cookieValues.Count()); } }); }
private static void CreateServerAndGet(HttpClient client, HttpCompletionOption completionOption, string responseText) { LoopbackServer.CreateServerAsync((server, url) => { Task <HttpResponseMessage> getAsync = client.GetAsync(url, completionOption); LoopbackServer.AcceptSocketAsync(server, (s, stream, reader, writer) => { while (!string.IsNullOrEmpty(reader.ReadLine())) { ; } writer.Write(responseText); writer.Flush(); s.Shutdown(SocketShutdown.Send); return(Task.CompletedTask); }).GetAwaiter().GetResult(); getAsync.GetAwaiter().GetResult().Dispose(); return(Task.CompletedTask); }).GetAwaiter().GetResult(); }
public async Task GetAsync_RequestVersion10_Success() { await LoopbackServer.CreateServerAsync(async (server, url) => { using (HttpClient client = CreateHttpClient()) { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); request.Version = HttpVersion.Version10; Task <HttpResponseMessage> getResponseTask = client.SendAsync(request); Task <List <string> > serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(server, $"HTTP/1.1 200 OK\r\nDate: {DateTimeOffset.UtcNow:R}\r\nContent-Length: 0\r\n\r\n", new LoopbackServer.Options { ResponseStreamWrapper = GetStream }); await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); var requestLines = await serverTask; Assert.Equal($"GET {url.PathAndQuery} HTTP/1.0", requestLines[0]); } }); }
public async Task SetDelegate_ConnectionSucceeds(SslProtocols acceptedProtocol, bool requestOnlyThisProtocol) { // Overriding flag for the same reason we skip tests on Catalina // On OSX 10.13-10.14 we can override this flag to enable the scenario // Issue: #22089 requestOnlyThisProtocol |= PlatformDetection.IsMacOsHighSierraOrHigher && acceptedProtocol == SslProtocols.Tls; using (HttpClientHandler handler = CreateHttpClientHandler()) using (HttpClient client = CreateHttpClient(handler)) { handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; if (requestOnlyThisProtocol) { handler.SslProtocols = acceptedProtocol; } else { // Explicitly setting protocols clears implementation default // restrictions on minimum TLS/SSL version // We currently know that some platforms like Debian 10 OpenSSL // will by default block < TLS 1.2 handler.SslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; } var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol }; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( server.AcceptConnectionSendResponseAndCloseAsync(), client.GetAsync(url)); }, options); } }
private async Task GetAsyncSuccessHelper(string statusLine, int expectedStatusCode, string expectedReason) { await LoopbackServer.CreateServerAsync(async (server, url) => { using (HttpClient client = CreateHttpClient()) { Task <HttpResponseMessage> getResponseTask = client.GetAsync(url); await TestHelper.WhenAllCompletedOrAnyFailed( getResponseTask, LoopbackServer.ReadRequestAndSendResponseAsync(server, $"{statusLine}\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + "\r\n", new LoopbackServer.Options { ResponseStreamWrapper = GetStream })); using (HttpResponseMessage response = await getResponseTask) { Assert.Equal(expectedStatusCode, (int)response.StatusCode); Assert.Equal(expectedReason, response.ReasonPhrase); } } }); }
public async Task GetAsync_AllowedSSLVersionDiffersFromServer_ThrowsException( SslProtocols allowedProtocol, SslProtocols acceptedProtocol, Type exceptedServerException) { if (!BackendSupportsSslConfiguration) { return; } using (HttpClientHandler handler = CreateHttpClientHandler()) using (var client = new HttpClient(handler)) { handler.SslProtocols = allowedProtocol; handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol }; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( Assert.ThrowsAsync(exceptedServerException, () => server.AcceptConnectionSendResponseAndCloseAsync()), Assert.ThrowsAsync <HttpRequestException>(() => client.GetAsync(url))); }, options); } }
public async Task GetAsync_AllowAutoRedirectTrue_RetainsOriginalFragmentIfAppropriate( string origFragment, string redirFragment, string expectedFragment, bool useRelativeRedirect) { if (IsWinHttpHandler) { // According to https://tools.ietf.org/html/rfc7231#section-7.1.2, // "If the Location value provided in a 3xx (Redirection) response does // not have a fragment component, a user agent MUST process the // redirection as if the value inherits the fragment component of the // URI reference used to generate the request target(i.e., the // redirection inherits the original reference's fragment, if any)." // WINHTTP is not doing this, and thus neither is WinHttpHandler. // It also sometimes doesn't include the fragments for redirects // even in other cases. return; } HttpClientHandler handler = CreateHttpClientHandler(); handler.AllowAutoRedirect = true; using (HttpClient client = CreateHttpClient(handler)) { await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => { origUrl = new UriBuilder(origUrl) { Fragment = origFragment }.Uri; Uri redirectUrl = new UriBuilder(origUrl) { Fragment = redirFragment }.Uri; if (useRelativeRedirect) { redirectUrl = new Uri(redirectUrl.GetComponents(UriComponents.PathAndQuery | UriComponents.Fragment, UriFormat.SafeUnescaped), UriKind.Relative); } Uri expectedUrl = new UriBuilder(origUrl) { Fragment = expectedFragment }.Uri; // Make and receive the first request that'll be redirected. Task <HttpResponseMessage> getResponse = client.GetAsync(origUrl); Task firstRequest = origServer.AcceptConnectionSendResponseAndCloseAsync(HttpStatusCode.Found, $"Location: {redirectUrl}\r\n"); Assert.Equal(firstRequest, await Task.WhenAny(firstRequest, getResponse)); // Receive the second request. Task <List <string> > secondRequest = origServer.AcceptConnectionSendResponseAndCloseAsync(); await TestHelper.WhenAllCompletedOrAnyFailed(secondRequest, getResponse); // Make sure the server received the second request for the right Uri. Assert.NotEmpty(secondRequest.Result); string[] statusLineParts = secondRequest.Result[0].Split(' '); Assert.Equal(3, statusLineParts.Length); Assert.Equal(expectedUrl.GetComponents(UriComponents.PathAndQuery, UriFormat.SafeUnescaped), statusLineParts[1]); // Make sure the request message was updated with the correct redirected location. using (HttpResponseMessage response = await getResponse) { Assert.Equal(200, (int)response.StatusCode); Assert.Equal(expectedUrl.ToString(), response.RequestMessage.RequestUri.ToString()); } }); } }
public async Task Manual_CertificateSentMatchesCertificateReceived_Success( int numberOfRequests, bool reuseClient) // validate behavior with and without connection pooling, which impacts client cert usage { if (BackendDoesNotSupportCustomCertificateHandling) // can't use [Conditional*] right now as it's evaluated at the wrong time for the managed handler { _output.WriteLine($"Skipping {nameof(Manual_CertificateSentMatchesCertificateReceived_Success)}()"); return; } var options = new LoopbackServer.Options { UseSsl = true }; Func<X509Certificate2, HttpClient> createClient = (cert) => { var handler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = delegate { return true; } }; handler.ClientCertificates.Add(cert); return new HttpClient(handler); }; Func<HttpClient, Socket, Uri, X509Certificate2, Task> makeAndValidateRequest = async (client, server, url, cert) => { await TestHelper.WhenAllCompletedOrAnyFailed( client.GetStringAsync(url), LoopbackServer.AcceptSocketAsync(server, async (socket, stream, reader, writer) => { SslStream sslStream = Assert.IsType<SslStream>(stream); Assert.Equal(cert, sslStream.RemoteCertificate); await LoopbackServer.ReadWriteAcceptedAsync(socket, reader, writer); return null; }, options)); }; await LoopbackServer.CreateServerAsync(async (server, url) => { if (reuseClient) { using (X509Certificate2 cert = Configuration.Certificates.GetClientCertificate()) { using (HttpClient client = createClient(cert)) { for (int i = 0; i < numberOfRequests; i++) { await makeAndValidateRequest(client, server, url, cert); GC.Collect(); GC.WaitForPendingFinalizers(); } } } } else { for (int i = 0; i < numberOfRequests; i++) { using (X509Certificate2 cert = Configuration.Certificates.GetClientCertificate()) { using (HttpClient client = createClient(cert)) { await makeAndValidateRequest(client, server, url, cert); } } GC.Collect(); GC.WaitForPendingFinalizers(); } } }, options); }
public async Task AllowAutoRedirect_True_PostToGetDoesNotSendTE(int statusCode) { if (IsCurlHandler && statusCode == 300) { // ISSUE #26434: // CurlHandler doesn't change POST to GET for 300 response (see above test). return; } if (IsWinHttpHandler) { // ISSUE #27440: // This test occasionally fails on WinHttpHandler. // Likely this is due to the way the loopback server is sending the response before reading the entire request. // We should change the server behavior here. return; } HttpClientHandler handler = CreateHttpClientHandler(); using (var client = new HttpClient(handler)) { await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => { var request = new HttpRequestMessage(HttpMethod.Post, origUrl); request.Content = new StringContent(ExpectedContent); request.Headers.TransferEncodingChunked = true; Task <HttpResponseMessage> getResponseTask = client.SendAsync(request); await LoopbackServer.CreateServerAsync(async(redirServer, redirUrl) => { // Original URL will redirect to a different URL Task serverTask = origServer.AcceptConnectionAsync(async connection => { // Send Connection: close so the client will close connection after request is sent, // meaning we can just read to the end to get the content await connection.ReadRequestHeaderAndSendResponseAsync((HttpStatusCode)statusCode, $"Location: {redirUrl}\r\nConnection: close\r\n"); connection.Socket.Shutdown(SocketShutdown.Send); await connection.Reader.ReadToEndAsync(); }); await Task.WhenAny(getResponseTask, serverTask); Assert.False(getResponseTask.IsCompleted, $"{getResponseTask.Status}: {getResponseTask.Exception}"); await serverTask; // Redirected URL answers with success List <string> receivedRequest = null; string receivedContent = null; Task serverTask2 = redirServer.AcceptConnectionAsync(async connection => { // Send Connection: close so the client will close connection after request is sent, // meaning we can just read to the end to get the content receivedRequest = await connection.ReadRequestHeaderAndSendResponseAsync(additionalHeaders: "Connection: close\r\n"); connection.Socket.Shutdown(SocketShutdown.Send); receivedContent = await connection.Reader.ReadToEndAsync(); }); await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask2); string[] statusLineParts = receivedRequest[0].Split(' '); Assert.Equal("GET", statusLineParts[0]); Assert.DoesNotContain(receivedRequest, line => line.StartsWith("Transfer-Encoding")); Assert.DoesNotContain(receivedRequest, line => line.StartsWith("Content-Length")); using (HttpResponseMessage response = await getResponseTask) { Assert.Equal(200, (int)response.StatusCode); } }); }); } }
public async Task GetAsyncVersion11_BadResponseVersion_ThrowsOr00(int responseMajorVersion, int responseMinorVersion) { // Full framework reports 1.0 or 1.1, depending on minor version, instead of throwing bool reportAs1X = PlatformDetection.IsFullFramework; // CurlHandler reports these as 0.0, except for 2.0 which is reported as 2.0, instead of throwing. bool reportAs00 = false; bool reportAs20 = false; if (!PlatformDetection.IsWindows && !UseSocketsHttpHandler) { if (responseMajorVersion == 2 && responseMinorVersion == 0) { reportAs20 = true; } else { reportAs00 = true; } } await LoopbackServer.CreateServerAsync(async (server, url) => { using (HttpClient client = CreateHttpClient()) { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); request.Version = HttpVersion.Version11; Task <HttpResponseMessage> getResponseTask = client.SendAsync(request); Task <List <string> > serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(server, $"HTTP/{responseMajorVersion}.{responseMinorVersion} 200 OK\r\nDate: {DateTimeOffset.UtcNow:R}\r\nContent-Length: 0\r\n\r\n", new LoopbackServer.Options { ResponseStreamWrapper = GetStream_ClientDisconnectOk }); if (reportAs00) { await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); using (HttpResponseMessage response = await getResponseTask) { Assert.Equal(0, response.Version.Major); Assert.Equal(0, response.Version.Minor); } } else if (reportAs20) { await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); using (HttpResponseMessage response = await getResponseTask) { Assert.Equal(2, response.Version.Major); Assert.Equal(0, response.Version.Minor); } } else if (reportAs1X) { await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); using (HttpResponseMessage response = await getResponseTask) { Assert.Equal(1, response.Version.Major); Assert.Equal(responseMinorVersion == 0 ? 0 : 1, response.Version.Minor); } } else { await Assert.ThrowsAsync <HttpRequestException>(async() => await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask)); } } }); }
public async Task GetAsync_SetCookieContainerAndMultipleCookieHeaders_BothCookiesSent() { if (IsNetfxHandler) { // Netfx handler does not support custom cookie header return; } if (IsCurlHandler) { // Issue #26983 // CurlHandler ignores container cookies when custom Cookie header is set. return; } await LoopbackServer.CreateServerAsync(async (server, url) => { HttpClientHandler handler = CreateHttpClientHandler(); handler.CookieContainer = CreateSingleCookieContainer(url); using (HttpClient client = new HttpClient(handler)) { HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, url); requestMessage.Headers.Add("Cookie", "A=1"); requestMessage.Headers.Add("Cookie", "B=2"); Task <HttpResponseMessage> getResponseTask = client.SendAsync(requestMessage); Task <List <string> > serverTask = LoopbackServer.ReadRequestAndSendResponseAsync(server); await TestHelper.WhenAllCompletedOrAnyFailed(getResponseTask, serverTask); List <string> requestLines = await serverTask; Assert.Equal(1, requestLines.Count(s => s.StartsWith("Cookie: "))); // Multiple Cookie header values are treated as any other header values and are // concatenated using ", " as the separator. The container cookie is concatenated to // one of these values using the "; " cookie separator. var cookieValues = requestLines.Single(s => s.StartsWith("Cookie: ")).Substring(8).Split(new string[] { ", " }, StringSplitOptions.None); Assert.Equal(2, cookieValues.Count()); // Find container cookie and remove it so we can validate the rest of the cookie header values bool sawContainerCookie = false; for (int i = 0; i < cookieValues.Length; i++) { if (cookieValues[i].Contains(';')) { Assert.False(sawContainerCookie); var cookies = cookieValues[i].Split(new string[] { "; " }, StringSplitOptions.None); Assert.Equal(2, cookies.Count()); Assert.Contains(s_expectedCookieHeaderValue, cookies); sawContainerCookie = true; cookieValues[i] = cookies.Where(c => c != s_expectedCookieHeaderValue).Single(); } } Assert.Contains("A=1", cookieValues); Assert.Contains("B=2", cookieValues); } }); }
public void SendAsync_ExpectedDiagnosticSourceActivityLogging() { RemoteInvoke(() => { bool requestLogged = false; bool responseLogged = false; bool activityStartLogged = false; bool activityStopLogged = false; bool exceptionLogged = false; Activity parentActivity = new Activity("parent"); parentActivity.AddBaggage("correlationId", Guid.NewGuid().ToString()); parentActivity.AddBaggage("moreBaggage", Guid.NewGuid().ToString()); parentActivity.AddTag("tag", "tag"); //add tag to ensure it is not injected into request parentActivity.Start(); var diagnosticListenerObserver = new FakeDiagnosticListenerObserver(kvp => { if (kvp.Key.Equals("System.Net.Http.Request")) { requestLogged = true; } else if (kvp.Key.Equals("System.Net.Http.Response")) { responseLogged = true; } else if (kvp.Key.Equals("System.Net.Http.Exception")) { exceptionLogged = true; } else if (kvp.Key.Equals("System.Net.Http.HttpRequestOut.Start")) { Assert.NotNull(kvp.Value); Assert.NotNull(Activity.Current); Assert.Equal(parentActivity, Activity.Current.Parent); GetPropertyValueFromAnonymousTypeInstance <HttpRequestMessage>(kvp.Value, "Request"); activityStartLogged = true; } else if (kvp.Key.Equals("System.Net.Http.HttpRequestOut.Stop")) { Assert.NotNull(kvp.Value); Assert.NotNull(Activity.Current); Assert.Equal(parentActivity, Activity.Current.Parent); Assert.True(Activity.Current.Duration != TimeSpan.Zero); GetPropertyValueFromAnonymousTypeInstance <HttpRequestMessage>(kvp.Value, "Request"); GetPropertyValueFromAnonymousTypeInstance <HttpResponseMessage>(kvp.Value, "Response"); var requestStatus = GetPropertyValueFromAnonymousTypeInstance <TaskStatus>(kvp.Value, "RequestTaskStatus"); Assert.Equal(TaskStatus.RanToCompletion, requestStatus); activityStopLogged = true; } }); using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver)) { diagnosticListenerObserver.Enable(); using (var client = new HttpClient()) { LoopbackServer.CreateServerAsync(async(server, url) => { Task <List <string> > requestLines = LoopbackServer.AcceptSocketAsync(server, (s, stream, reader, writer) => LoopbackServer.ReadWriteAcceptedAsync(s, reader, writer)); Task response = client.GetAsync(url); await Task.WhenAll(response, requestLines); AssertHeadersAreInjected(requestLines.Result, parentActivity); }).Wait(); } Assert.True(activityStartLogged, "HttpRequestOut.Start was not logged."); Assert.False(requestLogged, "Request was logged when Activity logging was enabled."); // Poll with a timeout since logging response is not synchronized with returning a response. WaitForTrue(() => activityStopLogged, TimeSpan.FromSeconds(1), "HttpRequestOut.Stop was not logged within 1 second timeout."); Assert.False(exceptionLogged, "Exception was logged for successful request"); Assert.False(responseLogged, "Response was logged when Activity logging was enabled."); diagnosticListenerObserver.Disable(); } return(SuccessExitCode); }).Dispose(); }
public async Task ConnectMethod_Success() { await LoopbackServer.CreateServerAsync(async (server, url) => { using (HttpClient client = CreateHttpClient()) { HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("CONNECT"), url) { Version = UseVersion }; request.Headers.Host = "foo.com:345"; // We need to use ResponseHeadersRead here, otherwise we will hang trying to buffer the response body. Task <HttpResponseMessage> responseTask = client.SendAsync(TestAsync, request, HttpCompletionOption.ResponseHeadersRead); await server.AcceptConnectionAsync(async connection => { // Verify that Host header exist and has same value and URI authority. List <string> lines = await connection.ReadRequestHeaderAsync().ConfigureAwait(false); string authority = lines[0].Split()[1]; foreach (string line in lines) { if (line.StartsWith("Host:", StringComparison.InvariantCultureIgnoreCase)) { Assert.Equal("Host: foo.com:345", line); break; } } Task serverTask = connection.SendResponseAsync(HttpStatusCode.OK); await TestHelper.WhenAllCompletedOrAnyFailed(responseTask, serverTask).ConfigureAwait(false); using (Stream clientStream = await(await responseTask).Content.ReadAsStreamAsync()) { Assert.True(clientStream.CanWrite); Assert.True(clientStream.CanRead); Assert.False(clientStream.CanSeek); TextReader clientReader = new StreamReader(clientStream); TextWriter clientWriter = new StreamWriter(clientStream) { AutoFlush = true }; TextWriter serverWriter = connection.Writer; const string helloServer = "hello server"; const string helloClient = "hello client"; const string goodbyeServer = "goodbye server"; const string goodbyeClient = "goodbye client"; clientWriter.WriteLine(helloServer); Assert.Equal(helloServer, connection.ReadLine()); serverWriter.WriteLine(helloClient); Assert.Equal(helloClient, clientReader.ReadLine()); clientWriter.WriteLine(goodbyeServer); Assert.Equal(goodbyeServer, connection.ReadLine()); serverWriter.WriteLine(goodbyeClient); Assert.Equal(goodbyeClient, clientReader.ReadLine()); } }); } }); }
public async Task Manual_CertificateSentMatchesCertificateReceived_Success( int numberOfRequests, bool reuseClient) // validate behavior with and without connection pooling, which impacts client cert usage { if (!BackendSupportsCustomCertificateHandling) // can't use [Conditional*] right now as it's evaluated at the wrong time for SocketsHttpHandler { _output.WriteLine($"Skipping {nameof(Manual_CertificateSentMatchesCertificateReceived_Success)}()"); return; } if (!UseSocketsHttpHandler) { // Issue #9543: fails sporadically on WinHttpHandler/CurlHandler return; } var options = new LoopbackServer.Options { UseSsl = true }; Func <X509Certificate2, HttpClient> createClient = (cert) => { StandardSocketsHttpHandler handler = CreateSocketsHttpHandler(); handler.SslOptions.RemoteCertificateValidationCallback = SecurityHelper.AllowAllCertificates; handler.SslOptions.ClientCertificates = new X509CertificateCollection(); handler.SslOptions.ClientCertificates.Add(cert); Assert.True(handler.SslOptions.ClientCertificates.Contains(cert)); handler.SslOptions.LocalCertificateSelectionCallback = (object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) => cert; return(new HttpClient(handler)); }; Func <HttpClient, LoopbackServer, Uri, X509Certificate2, Task> makeAndValidateRequest = async(client, server, url, cert) => { await TestHelper.WhenAllCompletedOrAnyFailed( client.GetStringAsync(url), server.AcceptConnectionAsync(async connection => { SslStream sslStream = Assert.IsType <SslStream>(connection.Stream); Assert.Equal(cert, sslStream.RemoteCertificate); await connection.ReadRequestHeaderAndSendResponseAsync(); })); }; await LoopbackServer.CreateServerAsync(async (server, url) => { using (X509Certificate2 cert = Configuration.Certificates.GetClientCertificate()) { if (reuseClient) { using (HttpClient client = createClient(cert)) { for (int i = 0; i < numberOfRequests; i++) { await makeAndValidateRequest(client, server, url, cert); GC.Collect(); GC.WaitForPendingFinalizers(); } } } else { for (int i = 0; i < numberOfRequests; i++) { using (HttpClient client = createClient(cert)) { await makeAndValidateRequest(client, server, url, cert); } GC.Collect(); GC.WaitForPendingFinalizers(); } } } }, options); }
public async Task GetAsync_CancelPendingRequests_DoesntCancelReadAsyncOnResponseStream(CancellationMode mode, bool copyToAsync) { if (IsNetfxHandler) { // throws ObjectDisposedException as part of Stream.CopyToAsync/ReadAsync return; } if (IsCurlHandler) { // Issue #27065 // throws OperationCanceledException from Stream.CopyToAsync/ReadAsync return; } using (HttpClient client = CreateHttpClient()) { client.Timeout = Timeout.InfiniteTimeSpan; await LoopbackServer.CreateServerAsync(async (server, url) => { var clientReadSomeBody = new TaskCompletionSource <bool>(); var clientFinished = new TaskCompletionSource <bool>(); var responseContentSegment = new string('s', 3000); int responseSegments = 4; int contentLength = responseContentSegment.Length * responseSegments; Task serverTask = server.AcceptConnectionAsync(async connection => { await connection.ReadRequestHeaderAndSendCustomResponseAsync( $"HTTP/1.1 200 OK\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + $"Content-Length: {contentLength}\r\n" + $"\r\n"); for (int i = 0; i < responseSegments; i++) { await connection.Writer.WriteAsync(responseContentSegment); if (i == 0) { await clientReadSomeBody.Task; } } await clientFinished.Task; }); using (HttpResponseMessage resp = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead)) using (Stream respStream = await resp.Content.ReadAsStreamAsync()) { var result = new MemoryStream(); int b = respStream.ReadByte(); Assert.NotEqual(-1, b); result.WriteByte((byte)b); Cancel(mode, client, null); // should not cancel the operation, as using ResponseHeadersRead clientReadSomeBody.SetResult(true); if (copyToAsync) { await respStream.CopyToAsync(result, 10, new CancellationTokenSource().Token); } else { byte[] buffer = new byte[10]; int bytesRead; while ((bytesRead = await respStream.ReadAsync(buffer, 0, buffer.Length)) > 0) { result.Write(buffer, 0, bytesRead); } } Assert.Equal(contentLength, result.Length); } clientFinished.SetResult(true); await serverTask; }); } }
public async Task GetAsync_ResponseContentRead_CancelUsingTimeoutOrToken_TaskCanceledQuickly( bool useTimeout, bool startResponseBody) { var cts = new CancellationTokenSource(); // ignored if useTimeout==true TimeSpan timeout = useTimeout ? new TimeSpan(0, 0, 1) : Timeout.InfiniteTimeSpan; CancellationToken cancellationToken = useTimeout ? CancellationToken.None : cts.Token; using (var client = new HttpClient() { Timeout = timeout }) { var triggerResponseWrite = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var triggerRequestCancel = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); await LoopbackServer.CreateServerAsync(async (server, url) => { Task serverTask = LoopbackServer.AcceptSocketAsync(server, async(socket, stream, reader, writer) => { while (!string.IsNullOrEmpty(await reader.ReadLineAsync())) { ; } await writer.WriteAsync( "HTTP/1.1 200 OK\r\n" + $"Date: {DateTimeOffset.UtcNow:R}\r\n" + "Content-Length: 16000\r\n" + "\r\n" + (startResponseBody ? "less than 16000 bytes" : "")); await Task.Delay(1000); triggerRequestCancel.SetResult(true); // allow request to cancel await triggerResponseWrite.Task; // pause until we're released return(null); }); var stopwatch = Stopwatch.StartNew(); if (PlatformDetection.IsFullFramework) { // .NET Framework throws WebException instead of OperationCanceledException. await Assert.ThrowsAnyAsync <WebException>(async() => { Task <HttpResponseMessage> getResponse = client.GetAsync(url, HttpCompletionOption.ResponseContentRead, cancellationToken); await triggerRequestCancel.Task; cts.Cancel(); await getResponse; }); } else { await Assert.ThrowsAnyAsync <OperationCanceledException>(async() => { Task <HttpResponseMessage> getResponse = client.GetAsync(url, HttpCompletionOption.ResponseContentRead, cancellationToken); await triggerRequestCancel.Task; cts.Cancel(); await getResponse; }); } stopwatch.Stop(); _output.WriteLine("GetAsync() completed at: {0}", stopwatch.Elapsed.ToString()); triggerResponseWrite.SetResult(true); Assert.True(stopwatch.Elapsed < new TimeSpan(0, 0, 30), $"Elapsed time {stopwatch.Elapsed} should be less than 30 seconds, was {stopwatch.Elapsed.TotalSeconds}"); }); } }
public async Task Manual_CertificateSentMatchesCertificateReceived_Success( int numberOfRequests, bool reuseClient) // validate behavior with and without connection pooling, which impacts client cert usage { var options = new LoopbackServer.Options { UseSsl = true }; using (X509Certificate2 cert = Configuration.Certificates.GetClientCertificate()) { Func <HttpClient> createClient = () => { var handler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = delegate { return(true); } }; handler.ClientCertificates.Add(cert); return(new HttpClient(handler)); }; Func <HttpClient, Socket, Uri, Task> makeAndValidateRequest = async(client, server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( client.GetStringAsync(url), LoopbackServer.AcceptSocketAsync(server, async(socket, stream, reader, writer) => { SslStream sslStream = Assert.IsType <SslStream>(stream); Assert.Equal(cert, sslStream.RemoteCertificate); await LoopbackServer.ReadWriteAcceptedAsync(socket, reader, writer); return(null); }, options)); }; await LoopbackServer.CreateServerAsync(async (server, url) => { if (reuseClient) { using (HttpClient client = createClient()) { for (int i = 0; i < numberOfRequests; i++) { await makeAndValidateRequest(client, server, url); GC.Collect(); GC.WaitForPendingFinalizers(); } } } else { for (int i = 0; i < numberOfRequests; i++) { using (HttpClient client = createClient()) { await makeAndValidateRequest(client, server, url); } GC.Collect(); GC.WaitForPendingFinalizers(); } } }, options); } }
public async Task Manual_CertificateSentMatchesCertificateReceived_Success( int numberOfRequests, bool reuseClient) // validate behavior with and without connection pooling, which impacts client cert usage { if (!BackendSupportsCustomCertificateHandling) // can't use [Conditional*] right now as it's evaluated at the wrong time for SocketsHttpHandler { _output.WriteLine($"Skipping {nameof(Manual_CertificateSentMatchesCertificateReceived_Success)}()"); return; } var options = new LoopbackServer.Options { UseSsl = true }; Func <X509Certificate2, HttpClient> createClient = (cert) => { HttpClientHandler handler = CreateHttpClientHandler(); handler.ServerCertificateCustomValidationCallback = delegate { return(true); }; handler.ClientCertificates.Add(cert); Assert.True(handler.ClientCertificates.Contains(cert)); return(new HttpClient(handler)); }; Func <HttpClient, LoopbackServer, Uri, X509Certificate2, Task> makeAndValidateRequest = async(client, server, url, cert) => { await TestHelper.WhenAllCompletedOrAnyFailed( client.GetStringAsync(url), server.AcceptConnectionAsync(async connection => { SslStream sslStream = Assert.IsType <SslStream>(connection.Stream); // We can't do Assert.Equal(cert, sslStream.RemoteCertificate) because // on .NET Framework sslStream.RemoteCertificate is always an X509Certificate // object which is not equal to the X509Certificate2 object we use in the tests. // So, we'll just compare a few properties to make sure it's the right certificate. Assert.Equal(cert.Subject, sslStream.RemoteCertificate.Subject); Assert.Equal(cert.Issuer, sslStream.RemoteCertificate.Issuer); await connection.ReadRequestHeaderAndSendResponseAsync(additionalHeaders: "Connection: close\r\n"); })); }; await LoopbackServer.CreateServerAsync(async (server, url) => { using (X509Certificate2 cert = Configuration.Certificates.GetClientCertificate()) { if (reuseClient) { using (HttpClient client = createClient(cert)) { for (int i = 0; i < numberOfRequests; i++) { await makeAndValidateRequest(client, server, url, cert); GC.Collect(); GC.WaitForPendingFinalizers(); } } } else { for (int i = 0; i < numberOfRequests; i++) { using (HttpClient client = createClient(cert)) { await makeAndValidateRequest(client, server, url, cert); } GC.Collect(); GC.WaitForPendingFinalizers(); } } } }, options); }
public async Task MaxConnectionsPerServer_WaitingConnectionsAreCancelable() { if (IsWinHttpHandler) { // Issue #27064: // Throws WinHttpException ("The server returned an invalid or unrecognized response") // while parsing headers. return; } if (IsNetfxHandler) { // Throws HttpRequestException wrapping a WebException for the canceled request // instead of throwing an OperationCanceledException or a canceled WebException directly. return; } using (HttpClientHandler handler = CreateHttpClientHandler()) using (HttpClient client = new HttpClient(handler)) { handler.MaxConnectionsPerServer = 1; client.Timeout = Timeout.InfiniteTimeSpan; await LoopbackServer.CreateServerAsync(async (server, url) => { var serverAboutToBlock = new TaskCompletionSource <bool>(); var blockServerResponse = new TaskCompletionSource <bool>(); Task serverTask1 = LoopbackServer.AcceptSocketAsync(server, async(socket1, stream1, reader1, writer1) => { while (!string.IsNullOrEmpty(await reader1.ReadLineAsync())) { ; } await writer1.WriteAsync($"HTTP/1.1 200 OK\r\nDate: {DateTimeOffset.UtcNow:R}\r\n"); serverAboutToBlock.SetResult(true); await blockServerResponse.Task; await writer1.WriteAsync("Content-Length: 5\r\n\r\nhello"); return(null); }); Task get1 = client.GetAsync(url); await serverAboutToBlock.Task; var cts = new CancellationTokenSource(); Task get2 = ValidateClientCancellationAsync(() => client.GetAsync(url, cts.Token)); Task get3 = ValidateClientCancellationAsync(() => client.GetAsync(url, cts.Token)); Task get4 = client.GetAsync(url); cts.Cancel(); await get2; await get3; blockServerResponse.SetResult(true); await new[] { get1, serverTask1 }.WhenAllOrAnyFailed(); Task serverTask4 = LoopbackServer.AcceptSocketAsync(server, async(socket2, stream2, reader2, writer2) => { while (!string.IsNullOrEmpty(await reader2.ReadLineAsync())) { ; } await writer2.WriteAsync($"HTTP/1.1 200 OK\r\nDate: {DateTimeOffset.UtcNow:R}\r\nContent-Length: 0\r\n\r\n"); return(null); }); await new[] { get4, serverTask4 }.WhenAllOrAnyFailed(); }); } }