Add tests to all handlers.
authorLakshmi Priya Sekar <lasekar@microsoft.com>
Tue, 13 Feb 2018 23:38:23 +0000 (15:38 -0800)
committerLakshmi Priya Sekar <lasekar@microsoft.com>
Wed, 14 Feb 2018 23:52:54 +0000 (15:52 -0800)
Commit migrated from https://github.com/dotnet/corefx/commit/aa2482155262063229ea3f0e5d868890d8490415

src/libraries/Common/tests/System/Net/Http/LoopbackServer.cs
src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Authentication.cs [new file with mode: 0644]
src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs
src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj

index 05f9f62..c04dbd5 100644 (file)
@@ -183,7 +183,7 @@ namespace System.Net.Test.Common
                     if (string.IsNullOrEmpty(username))
                         return false;
                 }
-                if (trimmedValue.Contains(nameof(userhash)) && trimmedValue.Contains("true"))
+                else if (trimmedValue.Contains(nameof(userhash)) && trimmedValue.Contains("true"))
                 {
                     userhash = true;
                 }
@@ -260,9 +260,6 @@ namespace System.Net.Test.Common
                         startIndex += 1;
                         algorithm = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex).Trim();
                     }
-
-                    if (string.IsNullOrEmpty(algorithm))
-                        algorithm = "sha-256";
                 }
                 else if (trimmedValue.Contains(nameof(opaque)))
                 {
@@ -276,10 +273,15 @@ namespace System.Net.Test.Common
                 }
                 else if (trimmedValue.Contains(nameof(qop)))
                 {
-                    int startIndex = trimmedValue.IndexOf('=');
+                    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();
                     }
                 }
@@ -305,26 +307,39 @@ namespace System.Net.Test.Common
                 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 + ":" + cnonce ?? string.Empty;
+                a1 = ComputeHash(a1, algorithm) + ":" + nonce;
+
+                if (cnonce != null)
+                    a1 += ":" + cnonce;
             }
 
             string a2 = requestMethod + ":" + uri;
-            if (qop.Equals("auth-int"))
+            if (!string.IsNullOrEmpty(qop) && qop.Equals("auth-int"))
             {
                 string content = requestContent ?? string.Empty;
                 a2 = a2 + ":" + ComputeHash(content, algorithm);
             }
 
-            string serverResponseHash = ComputeHash(ComputeHash(a1, algorithm) + ":" +
-                                        nonce + ":" +
-                                        nc + ":" +
-                                        cnonce + ":" +
-                                        qop + ":" +
-                                        ComputeHash(a2, algorithm), 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;
         }
@@ -372,7 +387,8 @@ namespace System.Net.Test.Common
                 Stream stream = new NetworkStream(s, ownsSocket: false);
                 if (_options.UseSsl)
                 {
-                    var sslStream = new SslStream(stream, false, delegate { return true; });
+                    var sslStream = new SslStream(stream, false, delegate
+                    { return true; });
                     using (var cert = Configuration.Certificates.GetServerCertificate())
                     {
                         await sslStream.AuthenticateAsServerAsync(
@@ -545,7 +561,8 @@ namespace System.Net.Test.Common
             {
                 var lines = new List<string>();
                 string line;
-                while (!string.IsNullOrEmpty(line = await _reader.ReadLineAsync().ConfigureAwait(false)))
+                while (!string.IsNullOrEmpty(line = reader.ReadLine()));
+                    ;
                 {
                     lines.Add(line);
                 }
diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Authentication.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Authentication.cs
new file mode 100644 (file)
index 0000000..531f636
--- /dev/null
@@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Net.Test.Common;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xunit;
+
+namespace System.Net.Http.Functional.Tests
+{
+    public class HttpClientHandler_Authentication_Test : HttpClientTestBase
+    {
+        private const string Username = "testusername";
+        private const string Password = "testpassword";
+        private const string Domain = "testdomain";
+
+        private NetworkCredential _credentials = new NetworkCredential(Username, Password, Domain);
+
+        private Func<HttpClientHandler, Uri, HttpStatusCode, NetworkCredential, Task> _createAndValidateRequest = async (handler, url, expectedStatusCode, credentials) =>
+        {
+            handler.Credentials = credentials;
+
+            using (HttpClient client = new HttpClient(handler))
+            using (HttpResponseMessage response = await client.GetAsync(url))
+            {
+                Assert.Equal(expectedStatusCode, response.StatusCode);
+            }
+        };
+
+        [Theory]
+        [MemberData(nameof(Authentication_TestData))]
+        public async Task HttpClientHandler_Authentication_Succeeds(string authenticateHeader, bool result)
+        {
+            var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password };
+            await LoopbackServer.CreateServerAsync(async (server, url) =>
+            {
+                string serverResponse = $"HTTP/1.1 401 UnAuthorized\r\nDate: {DateTimeOffset.UtcNow:R}\r\nWWW-Authenticate: {authenticateHeader}\r\nContent-Length: 0\r\n\r\n";
+                HttpClientHandler handler = CreateHttpClientHandler();
+                Task serverTask = result ?
+                    LoopbackServer.ReadRequestAndAuthenticateAsync(server, serverResponse, options) :
+                    LoopbackServer.ReadRequestAndSendResponseAsync(server, serverResponse, options);
+                await TestHelper.WhenAllCompletedOrAnyFailed(_createAndValidateRequest(handler, url, result ? HttpStatusCode.OK : HttpStatusCode.Unauthorized, _credentials), serverTask);
+            }, options);
+        }
+
+        [Theory]
+        [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\"")]
+        [InlineData("WWW-Authenticate: Basic realm=\"hello1\"\r\nWWW-Authenticate: Basic realm=\"hello2\"")]
+        [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Basic realm=\"hello\"")]
+        [InlineData("WWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\"\r\nWWW-Authenticate: Basic realm=\"hello\"")]
+        public async void HttpClientHandler_MultipleAuthenticateHeaders_Succeeds(string authenticateHeader)
+        {
+            var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password };
+            await LoopbackServer.CreateServerAsync(async (server, url) =>
+            {
+                string serverResponse = $"HTTP/1.1 401 UnAuthorized\r\nDate: {DateTimeOffset.UtcNow:R}\r\n{authenticateHeader}\r\nContent-Length: 0\r\n\r\n";
+                HttpClientHandler handler = CreateHttpClientHandler();
+                Task serverTask = LoopbackServer.ReadRequestAndAuthenticateAsync(server, serverResponse, options);
+                await TestHelper.WhenAllCompletedOrAnyFailed(_createAndValidateRequest(handler, url, HttpStatusCode.OK, _credentials), serverTask);
+            }, options);
+        }
+
+        [Theory]
+        [InlineData("HTTP/1.1 401 UnAuthorized\r\nWWW-Authenticate: Basic realm=\"hello\"\r\nContent-Length: 0\r\n\r\n")]
+        [InlineData("HTTP/1.1 401 UnAuthorized\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"testnonce\"\r\nContent-Length: 0\r\n\r\n")]
+        public async void HttpClientHandler_IncorrectCredentials_Fails(string serverResponse)
+        {
+            var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password };
+            await LoopbackServer.CreateServerAsync(async (server, url) =>
+            {
+                HttpClientHandler handler = CreateHttpClientHandler();
+                Task serverTask = LoopbackServer.ReadRequestAndAuthenticateAsync(server, serverResponse, options);
+                await TestHelper.WhenAllCompletedOrAnyFailed(_createAndValidateRequest(handler, url, HttpStatusCode.Unauthorized, new NetworkCredential("wronguser", "wrongpassword")), serverTask);
+            }, options);
+        }
+
+        public static IEnumerable<object[]> Authentication_TestData()
+        {
+            yield return new object[] { "Basic realm=\"testrealm\"", true };
+            yield return new object[] { "Basic ", true };
+            yield return new object[] { "Basic realm=withoutquotes", true };
+            yield return new object[] { "Basic something, Digest something", false };
+
+            // Add digest tests fail on CurlHandler.
+            if (PlatformDetection.IsWindows)
+            {
+                yield return new object[] { "Digest realm=\"testrealm\" nonce=\"testnonce\"", false };
+                yield return new object[] { $"Digest realm=\"testrealm\", nonce=\"{Convert.ToBase64String(Encoding.UTF8.GetBytes($"{DateTimeOffset.UtcNow}:XMh;z+$5|`i6Hx}}\", qop=auth-int"))}\"", true };
+                yield return new object[] { "Digest realm=\"api@example.org\", qop=\"auth\", algorithm=MD5-sess, nonce=\"5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK\", " +
+                "opaque=\"HRPCssKJSGjCrkzDg8OhwpzCiGPChXYjwrI2QmXDnsOS\", charset=UTF-8, userhash=true", true };
+                yield return new object[] { "Digest realm=\"testrealm1\", nonce=\"testnonce1\" Digest realm=\"testrealm2\", nonce=\"testnonce2\"", false };
+                yield return new object[] { $"Basic realm=\"testrealm\", " +
+                $"Digest realm=\"testrealm\", nonce=\"{Convert.ToBase64String(Encoding.UTF8.GetBytes($"{DateTimeOffset.UtcNow}:XMh;z+$5|`i6Hx}}"))}\", algorithm=MD5", true };
+                yield return new object[] { $"Digest realm=\"testrealm\", nonce=\"testnonce\", algorithm=MD5 " +
+                $"Basic realm=\"testrealm\"", false };
+            }
+        }
+    }
+}
index 3dfef3e..8ae2d43 100644 (file)
@@ -135,95 +135,25 @@ namespace System.Net.Http.Functional.Tests
         protected override bool UseSocketsHttpHandler => true;
     }
 
-    public sealed class SocketsHttpHandler_Authentication_Test : HttpClientTestBase
+    public sealed class SocketsHttpHandler_HttpClientHandler_Authentication_Test : HttpClientHandler_Authentication_Test
     {
         protected override bool UseSocketsHttpHandler => true;
 
-        private const string Username = "testusername";
-        private const string Password = "testpassword";
-        private const string Domain = "testdomain";
-
-        private NetworkCredential _credentials = new NetworkCredential(Username, Password, Domain);
-
-        private Func<HttpClientHandler, Uri, HttpStatusCode, NetworkCredential, Task> _createAndValidateRequest = async (handler, url, expectedStatusCode, credentials) =>
-        {
-            handler.Credentials = credentials;
-
-            using (HttpClient client = new HttpClient(handler))
-            using (HttpResponseMessage response = await client.GetAsync(url))
-            {
-                Assert.Equal(expectedStatusCode, response.StatusCode);
-            }
-        };
-
-        [Theory]
-        [MemberData(nameof(Authentication_TestData))]
-        public async void HttpClientHandler_Authentication_Succeeds(string authenticateHeader, bool result)
-        {
-            var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password };
-            await LoopbackServer.CreateServerAsync(async (server, url) =>
-            {
-                string serverResponse = $"HTTP/1.1 401 UnAuthorized\r\nDate: {DateTimeOffset.UtcNow:R}\r\nWWW-Authenticate: {authenticateHeader}\r\nContent-Length: 0\r\n\r\n";
-                HttpClientHandler handler = CreateHttpClientHandler();
-                Task serverTask = result ?
-                    LoopbackServer.ReadRequestAndAuthenticateAsync(server, serverResponse, options) :
-                    LoopbackServer.ReadRequestAndSendResponseAsync(server, serverResponse, options);
-                await TestHelper.WhenAllCompletedOrAnyFailed(_createAndValidateRequest(handler, url, result ? HttpStatusCode.OK : HttpStatusCode.Unauthorized, _credentials), serverTask);
-            }, options);
-        }
-
         [Theory]
-        [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\"")]
-        [InlineData("WWW-Authenticate: Basic realm=\"hello1\"\r\nWWW-Authenticate: Basic realm=\"hello2\"")]
-        [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Basic realm=\"hello\"")]
-        [InlineData("WWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\"\r\nWWW-Authenticate: Basic realm=\"hello\"")]
-        public async void HttpClientHandler_MultipleAuthenticateHeaders_Succeeds(string authenticateHeader)
+        [MemberData(nameof(Authentication_SocketsHttpHandler_TestData))]
+        public async void SocketsHttpHandler_Authentication_Succeeds(string authenticateHeader, bool result)
         {
-            var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password };
-            await LoopbackServer.CreateServerAsync(async (server, url) =>
-            {
-                string serverResponse = $"HTTP/1.1 401 UnAuthorized\r\nDate: {DateTimeOffset.UtcNow:R}\r\n{authenticateHeader}\r\nContent-Length: 0\r\n\r\n";
-                HttpClientHandler handler = CreateHttpClientHandler();
-                Task serverTask = LoopbackServer.ReadRequestAndAuthenticateAsync(server, serverResponse, options);
-                await TestHelper.WhenAllCompletedOrAnyFailed(_createAndValidateRequest(handler, url, HttpStatusCode.OK, _credentials), serverTask);
-            }, options);
+            await HttpClientHandler_Authentication_Succeeds(authenticateHeader, result);
         }
 
-        [Theory]
-        [InlineData("HTTP/1.1 401 UnAuthorized\r\nWWW-Authenticate: Basic realm=\"hello\"\r\nContent-Length: 0\r\n\r\n")]
-        [InlineData("HTTP/1.1 401 UnAuthorized\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"testnonce\"\r\nContent-Length: 0\r\n\r\n")]
-        public async void HttpClientHandler_IncorrectCredentials_Fails(string serverResponse)
+        public static IEnumerable<object[]> Authentication_SocketsHttpHandler_TestData()
         {
-            var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password };
-            await LoopbackServer.CreateServerAsync(async (server, url) =>
-            {
-                HttpClientHandler handler = CreateHttpClientHandler();
-                Task serverTask = LoopbackServer.ReadRequestAndAuthenticateAsync(server, serverResponse, options);
-                await TestHelper.WhenAllCompletedOrAnyFailed(_createAndValidateRequest(handler, url, HttpStatusCode.Unauthorized, new NetworkCredential("wronguser", "wrongpassword")), serverTask);
-            }, options);
-        }
-
-        public static IEnumerable<object[]> Authentication_TestData()
-        {
-            yield return new object[] { "Basic realm=\"testrealm\"", true };
-            yield return new object[] { $"Digest realm=\"testrealm\", nonce=\"{Convert.ToBase64String(Encoding.UTF8.GetBytes($"{DateTimeOffset.UtcNow}:XMh;z+$5|`i6Hx}}\""))}\"", true };
-            yield return new object[] { "Digest realm=\"api@example.org\", qop=\"auth\", algorithm=SHA-256-sess, nonce=\"5TsQWLVdgBdmrQ0XsxbDODV+57QdFR34I9HAbC/RVvkK\", " +
-                "opaque=\"HRPCssKJSGjCrkzDg8OhwpzCiGPChXYjwrI2QmXDnsOS\", charset=UTF-8, userhash=true", true };
-            yield return new object[] { $"Basic realm=\"testrealm\", " +
-                $"Digest realm=\"testrealm\", nonce=\"{Convert.ToBase64String(Encoding.UTF8.GetBytes($"{DateTimeOffset.UtcNow}:XMh;z+$5|`i6Hx}}"))}\"", true };
-            yield return new object[] { $"Digest realm=\"testrealm\", nonce=\"{Convert.ToBase64String(Encoding.UTF8.GetBytes($"{DateTimeOffset.UtcNow}:XMh;z+$5|`i6Hx}}"))}\", " +
-                $"Basic realm=\"testrealm\"", true };
-            yield return new object[] { "Basic ", true };
-            yield return new object[] { "Basic realm=withoutquotes", true };
+            // These test cases pass on SocketsHttpHandler, fail everywhere else.
             yield return new object[] { "Basic realm=\"testrealm1\" basic realm=\"testrealm1\"", true };
             yield return new object[] { "Basic something digest something", true };
-
             yield return new object[] { "Digest ", false };
             yield return new object[] { "Digest realm=withoutquotes, nonce=withoutquotes", false };
             yield return new object[] { "Digest realm=\"testrealm\", nonce=\"testnonce\", algorithm=\"myown\"", false };
-            yield return new object[] { "Digest realm=\"testrealm\" nonce=\"testnonce\"", false };
-            yield return new object[] { "Basic something, Digest something", false };
-            yield return new object[] { "Digest realm=\"testrealm1\", nonce=\"testnonce1\" Digest realm=\"testrealm2\", nonce=\"testnonce2\"", false };
         }
     }
 
index 523c9d8..4adf68b 100644 (file)
@@ -79,6 +79,7 @@
     <Compile Include="DelegatingHandlerTest.cs" />
     <Compile Include="FakeDiagnosticSourceListenerObserver.cs" />
     <Compile Include="FormUrlEncodedContentTest.cs" />
+    <Compile Include="HttpClientHandlerTest.Authentication.cs" />
     <Compile Include="HttpClientHandlerTest.cs" />
     <Compile Include="HttpClientHandlerTest.Cancellation.cs" />
     <Compile Include="HttpClientHandlerTest.ClientCertificates.cs" />
   <ItemGroup Condition="'$(TargetsOSX)'=='true'">
     <TestCommandLines Include="ulimit -Sn 4096" />
   </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
   <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
 </Project>