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 GetAsync_AllowedSSLVersion_Succeeds(SslProtocols acceptedProtocol, bool requestOnlyThisProtocol)
 {
     using (var handler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = LoopbackServer.AllowAllCertificates })
     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_DisallowTls10_AllowTls11_AllowTls12()
        {
            using (var handler = new HttpClientHandler() { SslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12, ServerCertificateCustomValidationCallback = LoopbackServer.AllowAllCertificates })
            using (var client = new HttpClient(handler))
            {
                if (BackendSupportsSslConfiguration)
                {
                    LoopbackServer.Options options = new LoopbackServer.Options { UseSsl = true };

                    options.SslProtocols = SslProtocols.Tls;
                    await LoopbackServer.CreateServerAsync(async (server, url) =>
                    {
                        await TestHelper.WhenAllCompletedOrAnyFailed(
                            Assert.ThrowsAsync<IOException>(() => LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options)),
                            Assert.ThrowsAsync<HttpRequestException>(() => client.GetAsync(url)));
                    }, options);

                    foreach (var prot in new[] { SslProtocols.Tls11, SslProtocols.Tls12 })
                    {
                        options.SslProtocols = prot;
                        await LoopbackServer.CreateServerAsync(async (server, url) =>
                        {
                            await TestHelper.WhenAllCompletedOrAnyFailed(
                                LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options),
                                client.GetAsync(url));
                        }, options);
                    }
                }
                else
                {
                    await Assert.ThrowsAnyAsync<NotSupportedException>(() => client.GetAsync($"http://{Guid.NewGuid().ToString()}/"));
                }
            }
        }
 public async Task GetAsync_AllowedSSLVersionDiffersFromServer_ThrowsException(
     SslProtocols allowedProtocol, SslProtocols acceptedProtocol, Type exceptedServerException)
 {
     using (var handler = new HttpClientHandler() { SslProtocols = allowedProtocol, ServerCertificateCustomValidationCallback = LoopbackServer.AllowAllCertificates })
     using (var client = new HttpClient(handler))
     {
         var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol };
         await LoopbackServer.CreateServerAsync(async (server, url) =>
         {
             await TestHelper.WhenAllCompletedOrAnyFailed(
                 Assert.ThrowsAsync(exceptedServerException, () => LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options)),
                 Assert.ThrowsAsync<HttpRequestException>(() => 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 GetAsync_IPBasedUri_Success(IPAddress address)
 {
     using (var client = new HttpClient())
     {
         var options = new LoopbackServer.Options { Address = address };
         await LoopbackServer.CreateServerAsync(async (server, url) =>
         {
             await TestHelper.WhenAllCompletedOrAnyFailed(
                 LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options),
                 client.GetAsync(url));
         }, options);
     }
 }
        internal static bool IsDigestAuthTokenValid(string clientResponse, string requestMethod, LoopbackServer.Options options)
        {
            string clientHash = clientResponse.Substring(clientResponse.IndexOf(nameof(AuthenticationProtocols.Digest), StringComparison.OrdinalIgnoreCase) +
                                                         nameof(AuthenticationProtocols.Digest).Length).Trim();

            string[] values = clientHash.Split(',');

            string username = null, uri = null, realm = null, nonce = null, response = null, algorithm = null, cnonce = null, opaque = null, qop = null, nc = null;
            bool   userhash = false;

            for (int i = 0; i < values.Length; i++)
            {
                string trimmedValue = values[i].Trim();
                if (trimmedValue.Contains(nameof(username)))
                {
                    // Username is a quoted string.
                    int startIndex = trimmedValue.IndexOf('"');

                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        username    = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1);
                    }

                    // Username is mandatory.
                    if (string.IsNullOrEmpty(username))
                    {
                        return(false);
                    }
                }
                else if (trimmedValue.Contains(nameof(userhash)) && trimmedValue.Contains("true"))
                {
                    userhash = true;
                }
                else if (trimmedValue.Contains(nameof(uri)))
                {
                    int startIndex = trimmedValue.IndexOf('"');
                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        uri         = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1);
                    }

                    // Request uri is mandatory.
                    if (string.IsNullOrEmpty(uri))
                    {
                        return(false);
                    }
                }
                else if (trimmedValue.Contains(nameof(realm)))
                {
                    // Realm is a quoted string.
                    int startIndex = trimmedValue.IndexOf('"');
                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        realm       = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1);
                    }

                    // Realm is mandatory.
                    if (string.IsNullOrEmpty(realm))
                    {
                        return(false);
                    }
                }
                else if (trimmedValue.Contains(nameof(cnonce)))
                {
                    // CNonce is a quoted string.
                    int startIndex = trimmedValue.IndexOf('"');
                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        cnonce      = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1);
                    }
                }
                else if (trimmedValue.Contains(nameof(nonce)))
                {
                    // Nonce is a quoted string.
                    int startIndex = trimmedValue.IndexOf('"');
                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        nonce       = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1);
                    }

                    // Nonce is mandatory.
                    if (string.IsNullOrEmpty(nonce))
                    {
                        return(false);
                    }
                }
                else if (trimmedValue.Contains(nameof(response)))
                {
                    // response is a quoted string.
                    int startIndex = trimmedValue.IndexOf('"');
                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        response    = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1);
                    }

                    // Response is mandatory.
                    if (string.IsNullOrEmpty(response))
                    {
                        return(false);
                    }
                }
                else if (trimmedValue.Contains(nameof(algorithm)))
                {
                    int startIndex = trimmedValue.IndexOf('=');
                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        algorithm   = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex).Trim();
                    }
                }
                else if (trimmedValue.Contains(nameof(opaque)))
                {
                    // Opaque is a quoted string.
                    int startIndex = trimmedValue.IndexOf('"');
                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        opaque      = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1);
                    }
                }
                else if (trimmedValue.Contains(nameof(qop)))
                {
                    int startIndex = trimmedValue.IndexOf('"');
                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        qop         = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1);
                    }
                    else if ((startIndex = trimmedValue.IndexOf('=')) != -1)
                    {
                        startIndex += 1;
                        qop         = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex).Trim();
                    }
                }
                else if (trimmedValue.Contains(nameof(nc)))
                {
                    int startIndex = trimmedValue.IndexOf('=');
                    if (startIndex != -1)
                    {
                        startIndex += 1;
                        nc          = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex).Trim();
                    }
                }
            }

            // Verify username.
            if (userhash && ComputeHash(options.Username + ":" + realm, algorithm) != username)
            {
                return(false);
            }

            if (!userhash && options.Username != username)
            {
                return(false);
            }

            if (string.IsNullOrEmpty(algorithm))
            {
                algorithm = "sha-256";
            }

            // Calculate response and compare with the client response hash.
            string a1 = options.Username + ":" + realm + ":" + options.Password;

            if (algorithm.Contains("sess"))
            {
                a1 = ComputeHash(a1, algorithm) + ":" + nonce;

                if (cnonce != null)
                {
                    a1 += ":" + cnonce;
                }
            }

            string a2 = requestMethod + ":" + uri;

            if (!string.IsNullOrEmpty(qop) && qop.Equals("auth-int"))
            {
                // Request content is empty.
                a2 = a2 + ":" + ComputeHash(string.Empty, algorithm);
            }

            string serverResponseHash = ComputeHash(a1, algorithm) + ":" + nonce + ":";

            if (nc != null)
            {
                serverResponseHash += nc + ":";
            }

            if (cnonce != null)
            {
                serverResponseHash += cnonce + ":";
            }

            if (qop != null)
            {
                serverResponseHash += qop + ":";
            }

            serverResponseHash += ComputeHash(a2, algorithm);
            serverResponseHash  = ComputeHash(serverResponseHash, algorithm);

            return(response == serverResponseHash);
        }