Beispiel #1
0
        public async Task PreAuthenticate_NoPreviousAuthenticatedRequests_NoCredentialsSent(string credCacheScheme)
        {
            const int NumRequests = 3;
            await LoopbackServer.CreateClientAndServerAsync(async uri =>
            {
                using (HttpClientHandler handler = CreateHttpClientHandler())
                    using (HttpClient client = CreateHttpClient(handler))
                    {
                        client.DefaultRequestHeaders.ConnectionClose = true; // for simplicity of not needing to know every handler's pooling policy
                        handler.PreAuthenticate = true;
                        switch (credCacheScheme)
                        {
                        case null:
                            handler.Credentials = s_credentials;
                            break;

                        default:
                            var cc = new CredentialCache();
                            cc.Add(uri, credCacheScheme, s_credentials);
                            handler.Credentials = cc;
                            break;
                        }

                        for (int i = 0; i < NumRequests; i++)
                        {
                            Assert.Equal("hello world", await client.GetStringAsync(uri));
                        }
                    }
            },
                                                            async server =>
            {
                for (int i = 0; i < NumRequests; i++)
                {
                    List <string> headers = await server.AcceptConnectionSendResponseAndCloseAsync(content: "hello world");
                    Assert.All(headers, header => Assert.DoesNotContain("Authorization", header));
                }
            });
        }
        public async Task PreAuthenticate_FirstRequestNoHeaderAndAuthenticates_SecondRequestPreauthenticates(string credCacheScheme, string authResponse)
        {
            await LoopbackServer.CreateClientAndServerAsync(async uri =>
            {
                using (HttpClientHandler handler = CreateHttpClientHandler())
                    using (HttpClient client = CreateHttpClient(handler))
                    {
                        client.DefaultRequestHeaders.ConnectionClose = true; // for simplicity of not needing to know every handler's pooling policy
                        handler.PreAuthenticate = true;
                        switch (credCacheScheme)
                        {
                        case null:
                            handler.Credentials = s_credentials;
                            break;

                        default:
                            var cc = new CredentialCache();
                            cc.Add(uri, credCacheScheme, s_credentials);
                            handler.Credentials = cc;
                            break;
                        }

                        Assert.Equal("hello world", await client.GetStringAsync(uri));
                        Assert.Equal("hello world", await client.GetStringAsync(uri));
                    }
            },
                                                            async server =>
            {
                List <string> headers = await server.AcceptConnectionSendResponseAndCloseAsync(HttpStatusCode.Unauthorized, authResponse);
                Assert.All(headers, header => Assert.DoesNotContain("Authorization", header));

                for (int i = 0; i < 2; i++)
                {
                    headers = await server.AcceptConnectionSendResponseAndCloseAsync(content: "hello world");
                    Assert.Contains(headers, header => header.Contains("Authorization"));
                }
            });
        }
Beispiel #3
0
        public async Task ProxiedRequest_DefaultPort_PortStrippedOffInUri(string host)
        {
            string addressUri         = $"http://{host}:80/";
            string expectedAddressUri = $"http://{host}/";
            bool   connectionAccepted = false;

            await LoopbackServer.CreateClientAndServerAsync(async proxyUri =>
            {
                using (HttpClientHandler handler = CreateHttpClientHandler())
                    using (HttpClient client = CreateHttpClient(handler))
                    {
                        handler.Proxy = new WebProxy(proxyUri);
                        try { await client.GetAsync(addressUri); } catch { }
                    }
            }, server => server.AcceptConnectionAsync(async connection =>
            {
                connectionAccepted    = true;
                List <string> headers = await connection.ReadRequestHeaderAndSendResponseAsync();
                Assert.Contains($"GET {expectedAddressUri} HTTP/1.1", headers);
            }));

            Assert.True(connectionAccepted);
        }
        public async Task PreAuthenticate_AuthenticatedUrl_ThenTryDifferentUrl_SendsAuthHeaderOnlyIfPrefixMatches(
            string originalRelativeUri, string secondRelativeUri, bool expectedAuthHeader)
        {
            const string AuthResponse = "WWW-Authenticate: Basic realm=\"hello\"\r\n";

            await LoopbackServer.CreateClientAndServerAsync(async uri =>
            {
                using (HttpClientHandler handler = CreateHttpClientHandler())
                    using (HttpClient client = CreateHttpClient(handler))
                    {
                        client.DefaultRequestHeaders.ConnectionClose = true; // for simplicity of not needing to know every handler's pooling policy
                        handler.PreAuthenticate = true;
                        handler.Credentials     = s_credentials;

                        Assert.Equal("hello world 1", await client.GetStringAsync(new Uri(uri, originalRelativeUri)));
                        Assert.Equal("hello world 2", await client.GetStringAsync(new Uri(uri, secondRelativeUri)));
                    }
            },
                                                            async server =>
            {
                List <string> headers = await server.AcceptConnectionSendResponseAndCloseAsync(HttpStatusCode.Unauthorized, AuthResponse);
                Assert.All(headers, header => Assert.DoesNotContain("Authorization", header));

                headers = await server.AcceptConnectionSendResponseAndCloseAsync(content: "hello world 1");
                Assert.Contains(headers, header => header.Contains("Authorization"));

                headers = await server.AcceptConnectionSendResponseAndCloseAsync(content: "hello world 2");
                if (expectedAuthHeader)
                {
                    Assert.Contains(headers, header => header.Contains("Authorization"));
                }
                else
                {
                    Assert.All(headers, header => Assert.DoesNotContain("Authorization", header));
                }
            });
        }
        public async Task PreAuthenticate_SuccessfulBasicButThenFails_DoesntLoopInfinitely()
        {
            await LoopbackServer.CreateClientAndServerAsync(async uri =>
            {
                using (HttpClientHandler handler = CreateHttpClientHandler())
                    using (HttpClient client = CreateHttpClient(handler))
                    {
                        client.DefaultRequestHeaders.ConnectionClose = true; // for simplicity of not needing to know every handler's pooling policy
                        handler.PreAuthenticate = true;
                        handler.Credentials     = s_credentials;

                        // First two requests: initially without auth header, then with
                        Assert.Equal("hello world", await client.GetStringAsync(uri));

                        // Attempt preauth, and when that fails, give up.
                        using (HttpResponseMessage resp = await client.GetAsync(uri))
                        {
                            Assert.Equal(HttpStatusCode.Unauthorized, resp.StatusCode);
                        }
                    }
            },
                                                            async server =>
            {
                // First request, no auth header, challenge Basic
                List <string> headers = headers = await server.AcceptConnectionSendResponseAndCloseAsync(HttpStatusCode.Unauthorized, "WWW-Authenticate: Basic realm=\"hello\"\r\n");
                Assert.All(headers, header => Assert.DoesNotContain("Authorization", header));

                // Second request, contains Basic auth header
                headers = await server.AcceptConnectionSendResponseAndCloseAsync(content: "hello world");
                Assert.Contains(headers, header => header.Contains("Authorization"));

                // Third request, contains Basic auth header but challenges anyway
                headers = headers = await server.AcceptConnectionSendResponseAndCloseAsync(HttpStatusCode.Unauthorized, "WWW-Authenticate: Basic realm=\"hello\"\r\n");
                Assert.Contains(headers, header => header.Contains("Authorization"));
            });
        }
        public async Task GetAsyncWithRedirect_SetCookieContainer_CorrectCookiesSent()
        {
            const string path1 = "/foo";
            const string path2 = "/bar";

            await LoopbackServer.CreateClientAndServerAsync(async url =>
            {
                Uri url1      = new Uri(url, path1);
                Uri url2      = new Uri(url, path2);
                Uri unusedUrl = new Uri(url, "/unused");

                HttpClientHandler handler = CreateHttpClientHandler();
                handler.CookieContainer   = new CookieContainer();
                handler.CookieContainer.Add(url1, new Cookie("cookie1", "value1"));
                handler.CookieContainer.Add(url2, new Cookie("cookie2", "value2"));
                handler.CookieContainer.Add(unusedUrl, new Cookie("cookie3", "value3"));

                using (HttpClient client = new HttpClient(handler))
                {
                    client.DefaultRequestHeaders.ConnectionClose = true; // to avoid issues with connection pooling
                    await client.GetAsync(url1);
                }
            },
                                                            async server =>
            {
                List <string> request1Lines = await server.AcceptConnectionSendResponseAndCloseAsync(HttpStatusCode.Found, $"Location: {path2}\r\n");

                Assert.Contains($"Cookie: cookie1=value1", request1Lines);
                Assert.Equal(1, request1Lines.Count(s => s.StartsWith("Cookie:")));

                List <string> request2Lines = await server.AcceptConnectionSendResponseAndCloseAsync(content: s_simpleContent);

                Assert.Contains($"Cookie: cookie2=value2", request2Lines);
                Assert.Equal(1, request2Lines.Count(s => s.StartsWith("Cookie:")));
            });
        }
Beispiel #7
0
        public async Task ConnectTimeout_TimesOut_Throws()
        {
            await LoopbackServer.CreateClientAndServerAsync(async (handler, client, server, url) =>
            {
                const int NumBacklogSockets = 16; // must be larger than OS' queue length when using Listen(1).
                var socketBacklog           = new List <Socket>(NumBacklogSockets);
                try
                {
                    handler.ConnectTimeout = TimeSpan.FromMilliseconds(10);

                    // Listen's backlog is only advisory; the OS may actually allow a larger backlog than that.
                    // As such, create a bunch of clients to connect to the endpoint so that our actual request
                    // will timeout while trying to connect.
                    for (int i = 0; i < NumBacklogSockets; i++)
                    {
                        var tmpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        var ignored   = tmpClient.ConnectAsync(new IPEndPoint(IPAddress.Parse(url.Host), url.Port));
                        socketBacklog.Add(tmpClient);
                    }

                    // Make the actual connection.  It should timeout in connect.
                    var sw = Stopwatch.StartNew();
                    await Assert.ThrowsAnyAsync <OperationCanceledException>(() => client.GetAsync(url));
                    sw.Stop();

                    Assert.InRange(sw.ElapsedMilliseconds, 1, 10 * 1000); // allow a very wide range
                }
                finally
                {
                    foreach (Socket c in socketBacklog)
                    {
                        c.Dispose();
                    }
                }
            });
        }
        public async Task PostAsync_CancelDuringRequestContentSend_TaskCanceledQuickly(bool chunkedTransfer, CancellationMode mode)
        {
            if (!UseSocketsHttpHandler)
            {
                // Issue #27063: hangs / doesn't cancel
                return;
            }

            var serverRelease = new TaskCompletionSource<bool>();
            await LoopbackServer.CreateClientAndServerAsync(async uri =>
            {
                try
                {
                    using (HttpClient client = CreateHttpClient())
                    {
                        client.Timeout = Timeout.InfiniteTimeSpan;
                        var cts = new CancellationTokenSource();

                        var waitToSend = new TaskCompletionSource<bool>();
                        var contentSending = new TaskCompletionSource<bool>();
                        var req = new HttpRequestMessage(HttpMethod.Post, uri) { Content = new ByteAtATimeContent(int.MaxValue, waitToSend.Task, contentSending) };
                        req.Headers.TransferEncodingChunked = chunkedTransfer;

                        Task<HttpResponseMessage> resp = client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, cts.Token);
                        waitToSend.SetResult(true);
                        await contentSending.Task;
                        Cancel(mode, client, cts);
                        await ValidateClientCancellationAsync(() => resp);
                    }
                }
                finally
                {
                    serverRelease.SetResult(true);
                }
            }, server => server.AcceptConnectionAsync(connection => serverRelease.Task));
        }
        public async Task GetAsyncWithMaxConnections_DisposeBeforeReadingToEnd_DrainsRequestsAndReusesConnection(int totalSize, int readSize, ContentMode mode)
        {
            if (IsWinHttpHandler)
            {
                // WinHttpHandler seems to only do a limited amount of draining, and this test starts
                // failing if there's any measurable delay introduced in the response such that it dribbles
                // in.  So just skip these tests.
                return;
            }

            if (IsCurlHandler)
            {
                // CurlHandler drain behavior is very inconsistent, so just skip these tests.
                return;
            }

            await LoopbackServer.CreateClientAndServerAsync(
                async url =>
            {
                HttpClientHandler handler = CreateHttpClientHandler();
                SetResponseDrainTimeout(handler, Timeout.InfiniteTimeSpan);

                // Set MaxConnectionsPerServer to 1.  This will ensure we will wait for the previous request to drain (or fail to)
                handler.MaxConnectionsPerServer = 1;

                using (var client = new HttpClient(handler))
                {
                    HttpResponseMessage response1 = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
                    ValidateResponseHeaders(response1, totalSize, mode);

                    // Read part but not all of response
                    Stream responseStream = await response1.Content.ReadAsStreamAsync();
                    await ReadToByteCount(responseStream, readSize);

                    response1.Dispose();

                    // Issue another request.  We'll confirm that it comes on the same connection.
                    HttpResponseMessage response2 = await client.GetAsync(url);
                    ValidateResponseHeaders(response2, totalSize, mode);
                    Assert.Equal(totalSize, (await response2.Content.ReadAsStringAsync()).Length);
                }
            },
                async server =>
            {
                string content  = new string('a', totalSize);
                string response = GetResponseForContentMode(content, mode);
                await server.AcceptConnectionAsync(async connection =>
                {
                    // Process the first request, with some introduced delays in the response to
                    // stress the draining.
                    await connection.ReadRequestHeaderAsync().ConfigureAwait(false);
                    foreach (char c in response)
                    {
                        await connection.Writer.WriteAsync(c);
                    }

                    // Process the second request.
                    await connection.ReadRequestHeaderAndSendCustomResponseAsync(response);
                });
            });
        }
        public async Task DecompressedResponse_MethodSpecified_DecompressedContentReturned(string encodingName, bool all)
        {
            Func <Stream, Stream> compress;
            DecompressionMethods  methods;

            switch (encodingName)
            {
            case "gzip":
                compress = s => new GZipStream(s, CompressionLevel.Optimal, leaveOpen: true);
                methods  = all ? DecompressionMethods.GZip : _all;
                break;

#if !NETFRAMEWORK
            case "br":
                if (IsWinHttpHandler)
                {
                    // Brotli only supported on SocketsHttpHandler.
                    return;
                }

                compress = s => new BrotliStream(s, CompressionLevel.Optimal, leaveOpen: true);
                methods  = all ? DecompressionMethods.Brotli : _all;
                break;

            case "deflate":
                // WinHttpHandler continues to use DeflateStream as it doesn't have a newer build than netstandard2.0
                // and doesn't have access to ZLibStream.
                compress = IsWinHttpHandler ?
                           new Func <Stream, Stream>(s => new DeflateStream(s, CompressionLevel.Optimal, leaveOpen: true)) :
                           new Func <Stream, Stream>(s => new ZLibStream(s, CompressionLevel.Optimal, leaveOpen: true));
                methods = all ? DecompressionMethods.Deflate : _all;
                break;
#endif

            default:
                Assert.Contains(encodingName, new[] { "br", "deflate", "gzip" });
                return;
            }

            var expectedContent = new byte[12345];
            new Random(42).NextBytes(expectedContent);

            await LoopbackServer.CreateClientAndServerAsync(async uri =>
            {
                using (HttpClientHandler handler = CreateHttpClientHandler())
                    using (HttpClient client = CreateHttpClient(handler))
                    {
                        handler.AutomaticDecompression = methods;
                        Assert.Equal <byte>(expectedContent, await client.GetByteArrayAsync(uri));
                    }
            }, async server =>
            {
                await server.AcceptConnectionAsync(async connection =>
                {
                    await connection.ReadRequestHeaderAsync();
                    await connection.WriteStringAsync($"HTTP/1.1 200 OK\r\nContent-Encoding: {encodingName}\r\n\r\n");
                    using (Stream compressedStream = compress(connection.Stream))
                    {
                        await compressedStream.WriteAsync(expectedContent);
                    }
                });
            });
        }
Beispiel #11
0
        public async Task DefaultHandler_ImpersonatedUser_Success(bool useNtlm)
        {
            await LoopbackServer.CreateClientAndServerAsync(
                async uri =>
            {
                HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
                requestMessage.Version            = new Version(1, 1);

                var handler = new HttpClientHandler();
                handler.UseDefaultCredentials = true;

                using (var client = new HttpClient(handler))
                {
                    HttpResponseMessage response = await client.SendAsync(requestMessage);
                    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                    Assert.Equal("foo", await response.Content.ReadAsStringAsync());

                    string initialUser = response.Headers.GetValues(NtAuthTests.UserHeaderName).First();

                    using (WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent())
                    {
                        _output.WriteLine($"Starting test as {currentIdentity.Name}");
                    }

                    // get token and run another request as different user.
                    WindowsIdentity.RunImpersonated(_fixture.TestAccount.AccountTokenHandle, () =>
                    {
                        using (WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent())
                        {
                            _output.WriteLine($"Running test as {currentIdentity.Name}");
                            Assert.Equal(_fixture.TestAccount.AccountName, currentIdentity.Name);
                        }

                        requestMessage         = new HttpRequestMessage(HttpMethod.Get, uri);
                        requestMessage.Version = new Version(1, 1);

                        HttpResponseMessage response = client.SendAsync(requestMessage).GetAwaiter().GetResult();
                        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                        Assert.Equal("foo", response.Content.ReadAsStringAsync().GetAwaiter().GetResult());

                        string newUser = response.Headers.GetValues(NtAuthTests.UserHeaderName).First();
                        Assert.Equal(_fixture.TestAccount.AccountName, newUser);
                    });
                }
            },
                async server =>
            {
                await server.AcceptConnectionAsync(async connection =>
                {
                    Task t = useNtlm ? NtAuthTests.HandleNtlmAuthenticationRequest(connection, closeConnection: false) : NtAuthTests.HandleNegotiateAuthenticationRequest(connection, closeConnection: false);
                    await t;
                    _output.WriteLine("Finished first request");

                    // Second request should use new connection as it runs as different user.
                    // We keep first connection open so HttpClient may be tempted top use it.
                    await server.AcceptConnectionAsync(async connection =>
                    {
                        Task t = useNtlm ? NtAuthTests.HandleNtlmAuthenticationRequest(connection, closeConnection: false) : NtAuthTests.HandleNegotiateAuthenticationRequest(connection, closeConnection: false);
                        await t;
                    }).ConfigureAwait(false);
                }).ConfigureAwait(false);
            });
        }
        public async Task DecompressedResponse_MethodSpecified_DecompressedContentReturned(string compressionName, bool all, bool useCopyTo, int contentLength)
        {
            if (IsWinHttpHandler &&
                (compressionName == "br" || compressionName == "zlib"))
            {
                // brotli and zlib not supported on WinHttpHandler
                return;
            }

            Func <Stream, Stream> compress;
            DecompressionMethods  methods;
            string encodingName = compressionName;

            switch (compressionName)
            {
            case "gzip":
                compress = s => new GZipStream(s, CompressionLevel.Optimal, leaveOpen: true);
                methods  = all ? DecompressionMethods.GZip : _all;
                break;

#if !NETFRAMEWORK
            case "br":
                compress = s => new BrotliStream(s, CompressionLevel.Optimal, leaveOpen: true);
                methods  = all ? DecompressionMethods.Brotli : _all;
                break;

            case "zlib":
                compress     = s => new ZLibStream(s, CompressionLevel.Optimal, leaveOpen: true);
                methods      = all ? DecompressionMethods.Deflate : _all;
                encodingName = "deflate";
                break;
#endif

            case "deflate":
                compress = s => new DeflateStream(s, CompressionLevel.Optimal, leaveOpen: true);
                methods  = all ? DecompressionMethods.Deflate : _all;
                break;

            default:
                throw new Exception($"Unexpected compression: {compressionName}");
            }

            var expectedContent = new byte[contentLength];
            new Random(42).NextBytes(expectedContent);

            await LoopbackServer.CreateClientAndServerAsync(async uri =>
            {
                using (HttpClientHandler handler = CreateHttpClientHandler())
                    using (HttpClient client = CreateHttpClient(handler))
                    {
                        handler.AutomaticDecompression = methods;
                        AssertExtensions.SequenceEqual(expectedContent, await client.GetByteArrayAsync(TestAsync, useCopyTo, uri));
                    }
            }, async server =>
            {
                await server.AcceptConnectionAsync(async connection =>
                {
                    await connection.ReadRequestHeaderAsync();
                    await connection.WriteStringAsync($"HTTP/1.1 200 OK\r\nContent-Encoding: {encodingName}\r\n\r\n");
                    using (Stream compressedStream = compress(connection.Stream))
                    {
                        await compressedStream.WriteAsync(expectedContent);
                    }
                });
            });
        }
Beispiel #13
0
        public async Task ProxySetViaEnvironmentVariable_DefaultProxyCredentialsUsed(bool useProxy)
        {
            const string ExpectedUsername = "******";
            const string ExpectedPassword = "******";

            LoopbackServer.Options options = new LoopbackServer.Options {
                IsProxy = true, Username = ExpectedUsername, Password = ExpectedPassword
            };

            await LoopbackServer.CreateClientAndServerAsync(uri => Task.Run(() =>
            {
                var psi = new ProcessStartInfo();
                psi.Environment.Add("http_proxy", $"http://{uri.Host}:{uri.Port}");

                RemoteExecutor.Invoke(async(useProxyString, useVersionString, uriString) =>
                {
                    using (HttpClientHandler handler = CreateHttpClientHandler(useVersionString))
                        using (HttpClient client = CreateHttpClient(handler, useVersionString))
                        {
                            var creds = new NetworkCredential(ExpectedUsername, ExpectedPassword);
                            handler.DefaultProxyCredentials = creds;
                            handler.UseProxy = bool.Parse(useProxyString);

                            HttpResponseMessage response = await client.GetAsync(uriString);
                            // Correctness of user and password is done in server part.
                            Assert.True(response.StatusCode == HttpStatusCode.OK);
                        };
                }, useProxy.ToString(), UseVersion.ToString(),
                                      // If proxy is used , the url does not matter. We set it to be different to avoid confusion.
                                      useProxy ? Configuration.Http.RemoteEchoServer.ToString() : uri.ToString(),
                                      new RemoteInvokeOptions {
                    StartInfo = psi
                }).Dispose();
            }),
                                                            server => server.AcceptConnectionAsync(async connection =>
            {
                const string headerName = "Proxy-Authorization";
                List <string> lines     = await connection.ReadRequestHeaderAsync().ConfigureAwait(false);

                // First request should not have proxy credentials in either case.
                for (int i = 1; i < lines.Count; i++)
                {
                    Assert.False(lines[i].StartsWith(headerName));
                }

                if (useProxy)
                {
                    // Reject request and wait for authenticated one.
                    await connection.SendResponseAsync(HttpStatusCode.ProxyAuthenticationRequired, "Proxy-Authenticate: Basic realm=\"NetCore\"\r\n").ConfigureAwait(false);

                    lines      = await connection.ReadRequestHeaderAsync().ConfigureAwait(false);
                    bool valid = false;
                    for (int i = 1; i < lines.Count; i++)
                    {
                        if (lines[i].StartsWith(headerName))
                        {
                            valid = LoopbackServer.IsBasicAuthTokenValid(lines[i], options);
                        }
                    }

                    Assert.True(valid);
                }

                await connection.SendResponseAsync(HttpStatusCode.OK).ConfigureAwait(false);
            }));
        }