コード例 #1
0
        public async Task CorrectlyHandlesQueryStringWhenAppendingNegotiateToUrl(string requestedUrl, string expectedNegotiate)
        {
            var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false);

            var negotiateUrlTcs = new TaskCompletionSource <string>();

            testHttpHandler.OnLongPoll(cancellationToken => ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
            testHttpHandler.OnLongPollDelete(cancellationToken => ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
            testHttpHandler.OnNegotiate((request, cancellationToken) =>
            {
                negotiateUrlTcs.TrySetResult(request.RequestUri.ToString());
                return(ResponseUtils.CreateResponse(HttpStatusCode.OK,
                                                    ResponseUtils.CreateNegotiationContent()));
            });

            using (var noErrorScope = new VerifyNoErrorsScope())
            {
                await WithConnectionAsync(
                    CreateConnection(testHttpHandler, url : requestedUrl, loggerFactory : noErrorScope.LoggerFactory),
                    async (connection) =>
                {
                    await connection.StartAsync().DefaultTimeout();
                });
            }

            Assert.Equal(expectedNegotiate, await negotiateUrlTcs.Task.DefaultTimeout());
        }
コード例 #2
0
        public async Task HttpConnectionFailsAfterFirstRetryFailsLongPolling()
        {
            var testHttpHandler      = new TestHttpMessageHandler(autoNegotiate: false);
            var accessTokenCallCount = 0;

            testHttpHandler.OnNegotiate((_, cancellationToken) =>
            {
                return(ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
            });

            testHttpHandler.OnLongPoll(_ =>
            {
                return(ResponseUtils.CreateResponse(HttpStatusCode.Unauthorized));
            });

            Task <string> AccessTokenProvider()
            {
                accessTokenCallCount++;
                return(Task.FromResult(accessTokenCallCount.ToString(CultureInfo.InvariantCulture)));
            }

            await WithConnectionAsync(
                CreateConnection(testHttpHandler, transportType : HttpTransportType.LongPolling, accessTokenProvider : AccessTokenProvider),
                async (connection) =>
            {
                await connection.StartAsync().DefaultTimeout();
                await Assert.ThrowsAsync <HttpRequestException>(async() => await connection.Transport.Input.ReadAllAsync());
            });

            // 1 negotiate + 1 retry initial poll
            Assert.Equal(2, accessTokenCallCount);
        }
コード例 #3
0
    public async Task HttpOptionsSetOntoHttpClientHandler()
    {
        var testHttpHandler = TestHttpMessageHandler.CreateDefault();

        var negotiateUrlTcs = new TaskCompletionSource <string>();

        testHttpHandler.OnNegotiate((request, cancellationToken) =>
        {
            negotiateUrlTcs.TrySetResult(request.RequestUri.ToString());
            return(ResponseUtils.CreateResponse(HttpStatusCode.OK,
                                                ResponseUtils.CreateNegotiationContent()));
        });

        HttpClientHandler httpClientHandler = null;

        var httpOptions = new HttpConnectionOptions();

        httpOptions.Url = new Uri("http://fakeuri.org/");
        httpOptions.HttpMessageHandlerFactory = inner =>
        {
            httpClientHandler = (HttpClientHandler)inner;
            return(testHttpHandler);
        };
        httpOptions.Cookies.Add(new Cookie("Name", "Value", string.Empty, "fakeuri.org"));
        var clientCertificate = new X509Certificate(Array.Empty <byte>());

        httpOptions.ClientCertificates.Add(clientCertificate);
        httpOptions.UseDefaultCredentials = false;
        httpOptions.Credentials           = Mock.Of <ICredentials>();
        httpOptions.Proxy      = Mock.Of <IWebProxy>();
        httpOptions.Transports = HttpTransportType.LongPolling;

        await WithConnectionAsync(
            CreateConnection(httpOptions),
            async (connection) =>
        {
            await connection.StartAsync().DefaultTimeout();
        });

        Assert.NotNull(httpClientHandler);
        Assert.Equal(1, httpClientHandler.CookieContainer.Count);
        Assert.Single(httpClientHandler.ClientCertificates);
        Assert.Same(clientCertificate, httpClientHandler.ClientCertificates[0]);
        Assert.False(httpClientHandler.UseDefaultCredentials);
        Assert.Same(httpOptions.Proxy, httpClientHandler.Proxy);
        Assert.Same(httpOptions.Credentials, httpClientHandler.Credentials);
    }
コード例 #4
0
        public async Task HttpConnectionSetsAccessTokenOnAllRequests(HttpTransportType transportType)
        {
            var testHttpHandler  = new TestHttpMessageHandler(autoNegotiate: false);
            var requestsExecuted = false;
            var callCount        = 0;

            testHttpHandler.OnNegotiate((_, cancellationToken) =>
            {
                return(ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
            });

            testHttpHandler.OnRequest(async(request, next, token) =>
            {
                Assert.Equal("Bearer", request.Headers.Authorization.Scheme);

                // Call count increments with each call and is used as the access token
                Assert.Equal(callCount.ToString(CultureInfo.InvariantCulture), request.Headers.Authorization.Parameter);

                requestsExecuted = true;

                return(await next());
            });

            testHttpHandler.OnRequest((request, next, token) =>
            {
                return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent)));
            });

            Task <string> AccessTokenProvider()
            {
                callCount++;
                return(Task.FromResult(callCount.ToString(CultureInfo.InvariantCulture)));
            }

            await WithConnectionAsync(
                CreateConnection(testHttpHandler, transportType : transportType, accessTokenProvider : AccessTokenProvider),
                async (connection) =>
            {
                await connection.StartAsync().DefaultTimeout();
                await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello world 1"));
                await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello world 2"));
            });

            // Fail safe in case the code is modified and some requests don't execute as a result
            Assert.True(requestsExecuted);
            Assert.Equal(1, callCount);
        }
コード例 #5
0
        public async Task HttpConnectionSetsUserAgentOnAllRequests(HttpTransportType transportType)
        {
            var testHttpHandler  = new TestHttpMessageHandler(autoNegotiate: false);
            var requestsExecuted = false;


            testHttpHandler.OnNegotiate((_, cancellationToken) =>
            {
                return(ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
            });

            testHttpHandler.OnRequest(async(request, next, token) =>
            {
                var userAgentHeader = request.Headers.UserAgent.ToString();

                Assert.NotNull(userAgentHeader);
                Assert.StartsWith("Microsoft SignalR/", userAgentHeader);

                // user agent version should come from version embedded in assembly metadata
                var assemblyVersion = typeof(Constants)
                                      .Assembly
                                      .GetCustomAttribute <AssemblyInformationalVersionAttribute>();

                Assert.Contains(assemblyVersion.InformationalVersion, userAgentHeader);

                requestsExecuted = true;

                return(await next());
            });

            testHttpHandler.OnRequest((request, next, token) =>
            {
                return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent)));
            });

            await WithConnectionAsync(
                CreateConnection(testHttpHandler, transportType : transportType),
                async (connection) =>
            {
                await connection.StartAsync().DefaultTimeout();
                await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
            });

            // Fail safe in case the code is modified and some requests don't execute as a result
            Assert.True(requestsExecuted);
        }
コード例 #6
0
        public async Task HttpConnectionFailsAfterFirstRetryFailsServerSentEvents()
        {
            var testHttpHandler      = new TestHttpMessageHandler(autoNegotiate: false);
            var accessTokenCallCount = 0;

            testHttpHandler.OnNegotiate((_, cancellationToken) =>
            {
                return(ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
            });

            testHttpHandler.OnSocketSend((_, _) =>
            {
                return(ResponseUtils.CreateResponse(HttpStatusCode.Unauthorized));
            });

            var tcs    = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
            var stream = new BlockingStream(tcs);

            testHttpHandler.OnRequest((request, next, token) =>
            {
                return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.OK, new StreamContent(stream))));
            });

            Task <string> AccessTokenProvider()
            {
                accessTokenCallCount++;
                return(Task.FromResult(accessTokenCallCount.ToString(CultureInfo.InvariantCulture)));
            }

            await WithConnectionAsync(
                CreateConnection(testHttpHandler, transportType : HttpTransportType.ServerSentEvents, accessTokenProvider : AccessTokenProvider),
                async (connection) =>
            {
                await connection.StartAsync().DefaultTimeout();
                await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello world 1"));
                await Assert.ThrowsAsync <HttpRequestException>(async() => await connection.Transport.Input.ReadAllAsync());
            });

            // 1 negotiate + 1 retry stream request
            Assert.Equal(2, accessTokenCallCount);
        }
コード例 #7
0
        public async Task HttpConnectionSetsRequestedWithOnAllRequests(HttpTransportType transportType)
        {
            var testHttpHandler  = new TestHttpMessageHandler(autoNegotiate: false);
            var requestsExecuted = false;

            testHttpHandler.OnNegotiate((_, cancellationToken) =>
            {
                return(ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
            });

            testHttpHandler.OnRequest(async(request, next, token) =>
            {
                var requestedWithHeader = request.Headers.GetValues(HeaderNames.XRequestedWith);
                var requestedWithValue  = Assert.Single(requestedWithHeader);
                Assert.Equal("XMLHttpRequest", requestedWithValue);

                requestsExecuted = true;

                return(await next());
            });

            testHttpHandler.OnRequest((request, next, token) =>
            {
                return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent)));
            });

            await WithConnectionAsync(
                CreateConnection(testHttpHandler, transportType : transportType),
                async (connection) =>
            {
                await connection.StartAsync().DefaultTimeout();
                await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
            });

            // Fail safe in case the code is modified and some requests don't execute as a result
            Assert.True(requestsExecuted);
        }
コード例 #8
0
        public async Task LongPollingTransportCanBeCanceled()
        {
            using (StartVerifiableLog())
            {
                var cts = new CancellationTokenSource();

                var httpHandler = new TestHttpMessageHandler(autoNegotiate: false);
                httpHandler.OnNegotiate((request, cancellationToken) =>
                {
                    // Cancel token so that the first request poll will throw
                    cts.Cancel();
                    return(ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
                });

                var lp = new LongPollingTransport(new HttpClient(httpHandler));

                await WithConnectionAsync(
                    CreateConnection(httpHandler, transport : lp, transportType : HttpTransportType.LongPolling),
                    async (connection) =>
                {
                    var ex = await Assert.ThrowsAsync <AggregateException>(async() => await connection.StartAsync(cts.Token).DefaultTimeout());
                });
            }
        }
コード例 #9
0
    public TestHttpMessageHandler(ILoggerFactory loggerFactory, bool autoNegotiate = true, bool handleFirstPoll = true)
    {
        _logger = loggerFactory?.CreateLogger <TestHttpMessageHandler>() ?? NullLoggerFactory.Instance.CreateLogger <TestHttpMessageHandler>();

        if (autoNegotiate)
        {
            OnNegotiate((_, cancellationToken) => ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
        }

        if (handleFirstPoll)
        {
            var firstPoll = true;
            OnRequest(async(request, next, cancellationToken) =>
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (ResponseUtils.IsLongPollRequest(request) && firstPoll)
                {
                    firstPoll = false;
                    return(ResponseUtils.CreateResponse(HttpStatusCode.OK));
                }
                else
                {
                    return(await next());
                }
            });
        }
    }
コード例 #10
0
 public Task ConnectionCannotBeStartedIfNoTransportProvidedByServer()
 {
     return(RunInvalidNegotiateResponseTest <NoTransportSupportedException>(ResponseUtils.CreateNegotiationContent(transportTypes: HttpTransportType.None), "None of the transports supported by the client are supported by the server."));
 }
コード例 #11
0
 public Task ConnectionCannotBeStartedIfNoCommonTransportsBetweenClientAndServer()
 {
     return(RunInvalidNegotiateResponseTest <AggregateException>(ResponseUtils.CreateNegotiationContent(transportTypes: HttpTransportType.ServerSentEvents),
                                                                 "Unable to connect to the server with any of the available transports. (ServerSentEvents failed: The transport is disabled by the client.)"));
 }
コード例 #12
0
 public Task NegotiateResponseWithNegotiateVersionRequiresConnectionToken()
 {
     return(RunInvalidNegotiateResponseTest <InvalidDataException>(ResponseUtils.CreateNegotiationContent(negotiateVersion: 1, connectionToken: null), "Invalid negotiation response received."));
 }
コード例 #13
0
 public Task StartThrowsFormatExceptionIfNegotiationResponseHasNoConnectionId()
 {
     return(RunInvalidNegotiateResponseTest <FormatException>(ResponseUtils.CreateNegotiationContent(connectionId: string.Empty), "Invalid connection id."));
 }
コード例 #14
0
        public async Task HttpConnectionSetsInherentKeepAliveFeature(HttpTransportType transportType, bool expectedValue)
        {
            using (StartVerifiableLog())
            {
                var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false);

                testHttpHandler.OnNegotiate((_, cancellationToken) => ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));

                testHttpHandler.OnRequest((request, next, token) => Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent)));

                await WithConnectionAsync(
                    CreateConnection(testHttpHandler, transportType : transportType, loggerFactory : LoggerFactory),
                    async (connection) =>
                {
                    await connection.StartAsync().DefaultTimeout();

                    var feature = connection.Features.Get <IConnectionInherentKeepAliveFeature>();
                    Assert.NotNull(feature);
                    Assert.Equal(expectedValue, feature.HasInherentKeepAlive);
                });
            }
        }
コード例 #15
0
        public async Task HttpConnectionRetriesAccessTokenProviderWhenAuthFailsServerSentEvents()
        {
            var testHttpHandler      = new TestHttpMessageHandler(autoNegotiate: false);
            var requestsExecuted     = false;
            var accessTokenCallCount = 0;

            testHttpHandler.OnNegotiate((_, cancellationToken) =>
            {
                return(ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
            });

            var sendRequestExecuted = false;
            var sendFinishedTcs     = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

            testHttpHandler.OnSocketSend((_, _) =>
            {
                if (!sendRequestExecuted)
                {
                    sendRequestExecuted = true;
                    return(ResponseUtils.CreateResponse(HttpStatusCode.Unauthorized));
                }
                sendFinishedTcs.SetResult();
                return(ResponseUtils.CreateResponse(HttpStatusCode.OK));
            });

            var tcs    = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
            var stream = new BlockingStream(tcs);

            testHttpHandler.OnRequest((request, next, token) =>
            {
                if (!requestsExecuted)
                {
                    requestsExecuted = true;
                    return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.Unauthorized)));
                }

                Assert.Equal("Bearer", request.Headers.Authorization.Scheme);

                Assert.Equal(accessTokenCallCount.ToString(CultureInfo.InvariantCulture), request.Headers.Authorization.Parameter);

                return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.OK, new StreamContent(stream))));
            });

            Task <string> AccessTokenProvider()
            {
                accessTokenCallCount++;
                return(Task.FromResult(accessTokenCallCount.ToString(CultureInfo.InvariantCulture)));
            }

            await WithConnectionAsync(
                CreateConnection(testHttpHandler, transportType : HttpTransportType.ServerSentEvents, accessTokenProvider : AccessTokenProvider),
                async (connection) =>
            {
                await connection.StartAsync().DefaultTimeout();
                await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello world 1"));
                await sendFinishedTcs.Task;
                tcs.TrySetResult();
                await connection.Transport.Input.ReadAllAsync();
            });

            // 1 negotiate + 1 retry stream request + 1 retry send
            Assert.Equal(3, accessTokenCallCount);
        }
コード例 #16
0
        public async Task HttpConnectionRetriesAccessTokenProviderWhenAuthFailsLongPolling()
        {
            var testHttpHandler      = new TestHttpMessageHandler(autoNegotiate: false);
            var requestsExecuted     = false;
            var accessTokenCallCount = 0;
            var pollCount            = 0;

            testHttpHandler.OnNegotiate((_, cancellationToken) =>
            {
                return(ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()));
            });

            var startSendTcs     = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
            var longPollTcs      = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
            var messageFragments = new[] { "This ", "is ", "a ", "test" };

            testHttpHandler.OnLongPoll(async _ =>
            {
                // fail every other request
                if (pollCount % 2 == 0)
                {
                    pollCount++;
                    return(ResponseUtils.CreateResponse(HttpStatusCode.Unauthorized));
                }
                if (pollCount / 2 >= messageFragments.Length)
                {
                    startSendTcs.SetResult();
                    await longPollTcs.Task;
                    return(ResponseUtils.CreateResponse(HttpStatusCode.NoContent));
                }

                var resp = ResponseUtils.CreateResponse(HttpStatusCode.OK, messageFragments[pollCount / 2]);
                pollCount++;
                return(resp);
            });

            var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

            testHttpHandler.OnRequest((request, next, token) =>
            {
                if (!requestsExecuted)
                {
                    requestsExecuted = true;
                    return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.Unauthorized)));
                }

                Assert.Equal("Bearer", request.Headers.Authorization.Scheme);

                Assert.Equal(accessTokenCallCount.ToString(CultureInfo.InvariantCulture), request.Headers.Authorization.Parameter);

                tcs.SetResult();

                return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.OK)));
            });

            Task <string> AccessTokenProvider()
            {
                accessTokenCallCount++;
                return(Task.FromResult(accessTokenCallCount.ToString(CultureInfo.InvariantCulture)));
            }

            await WithConnectionAsync(
                CreateConnection(testHttpHandler, transportType : HttpTransportType.LongPolling, accessTokenProvider : AccessTokenProvider),
                async (connection) =>
            {
                await connection.StartAsync().DefaultTimeout();
                var message = await connection.Transport.Input.ReadAtLeastAsync(14);
                Assert.Equal("This is a test", Encoding.UTF8.GetString(message.Buffer));
                await startSendTcs.Task;
                await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello world 1"));
                await tcs.Task;
                longPollTcs.SetResult();
            });

            // 1 negotiate + 4 (number of polls) + 1 for last poll + 1 for send
            Assert.Equal(7, accessTokenCallCount);
        }