public Task StartThrowsFormatExceptionIfNegotiationResponseHasNoTransports() { return(RunInvalidNegotiateResponseTest <InvalidOperationException>(ResponseUtils.CreateNegotiationContent(transportTypes: 0), "Unable to connect to the server with any of the available transports.")); }
public Task ConnectionCannotBeStartedIfNoCommonTransportsBetweenClientAndServer(HttpTransportType serverTransports) { return(RunInvalidNegotiateResponseTest <InvalidOperationException>(ResponseUtils.CreateNegotiationContent(transportTypes: serverTransports), "Unable to connect to the server with any of the available transports.")); }
public async Task NegotiateThatReturnsUrlGetFollowedWithAccessToken() { var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false); var firstNegotiate = true; testHttpHandler.OnNegotiate((request, cancellationToken) => { if (firstNegotiate) { firstNegotiate = false; // The first negotiate requires an access token if (request.Headers.Authorization?.Parameter != "firstSecret") { return(ResponseUtils.CreateResponse(HttpStatusCode.Unauthorized)); } return(ResponseUtils.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(new { url = "https://another.domain.url/chat", accessToken = "secondSecret" }))); } // All other requests require an access token if (request.Headers.Authorization?.Parameter != "secondSecret") { return(ResponseUtils.CreateResponse(HttpStatusCode.Unauthorized)); } return(ResponseUtils.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(new { connectionId = "0rge0d00-0040-0030-0r00-000q00r00e00", availableTransports = new object[] { new { transport = "LongPolling", transferFormats = new[] { "Text" } }, } }))); }); testHttpHandler.OnLongPoll((request, token) => { // All other requests require an access token if (request.Headers.Authorization?.Parameter != "secondSecret") { return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.Unauthorized))); } var tcs = new TaskCompletionSource <HttpResponseMessage>(TaskCreationOptions.RunContinuationsAsynchronously); token.Register(() => tcs.TrySetResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent))); return(tcs.Task); }); testHttpHandler.OnLongPollDelete((token) => ResponseUtils.CreateResponse(HttpStatusCode.Accepted)); Task <string> AccessTokenProvider() => Task.FromResult <string>("firstSecret"); using (var noErrorScope = new VerifyNoErrorsScope()) { await WithConnectionAsync( CreateConnection(testHttpHandler, loggerFactory : noErrorScope.LoggerFactory, accessTokenProvider : AccessTokenProvider), async (connection) => { await connection.StartAsync(TransferFormat.Text).OrTimeout(); }); } Assert.Equal("http://fakeuri.org/negotiate", testHttpHandler.ReceivedRequests[0].RequestUri.ToString()); Assert.Equal("https://another.domain.url/chat/negotiate", testHttpHandler.ReceivedRequests[1].RequestUri.ToString()); Assert.Equal("https://another.domain.url/chat?id=0rge0d00-0040-0030-0r00-000q00r00e00", testHttpHandler.ReceivedRequests[2].RequestUri.ToString()); Assert.Equal("https://another.domain.url/chat?id=0rge0d00-0040-0030-0r00-000q00r00e00", testHttpHandler.ReceivedRequests[3].RequestUri.ToString()); // Delete request Assert.Equal(5, testHttpHandler.ReceivedRequests.Count); }
public Task StartThrowsFormatExceptionIfNegotiationResponseHasNoConnectionId() { return(RunInvalidNegotiateResponseTest <FormatException>(ResponseUtils.CreateNegotiationContent(connectionId: string.Empty), "Invalid connection id.")); }
public Task StartThrowsFormatExceptionIfNegotiationResponseHasNoTransports() { return(RunInvalidNegotiateResponseTest <FormatException>(ResponseUtils.CreateNegotiationContent(transportTypes: null), "No transports returned in negotiation response.")); }
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().OrTimeout(); var feature = connection.Features.Get <IConnectionInherentKeepAliveFeature>(); Assert.NotNull(feature); Assert.Equal(expectedValue, feature.HasInherentKeepAlive); }); } }
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.)")); }
public Task ConnectionCannotBeStartedIfNoCommonTransportsBetweenClientAndServer(TransportType serverTransports) { return(RunInvalidNegotiateResponseTest <InvalidOperationException>(ResponseUtils.CreateNegotiationContent(transportTypes: serverTransports), "No requested transports available on the server.")); }
public Task NegotiateResponseWithNegotiateVersionRequiresConnectionToken() { return(RunInvalidNegotiateResponseTest <InvalidDataException>(ResponseUtils.CreateNegotiationContent(negotiateVersion: 1, connectionToken: null), "Invalid negotiation response received.")); }
public async Task NegotiateThatReturnsRedirectUrlDoesNotAddAnotherNegotiateVersionQueryString() { var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false); var negotiateCount = 0; testHttpHandler.OnNegotiate((request, cancellationToken) => { negotiateCount++; if (negotiateCount == 1) { return(ResponseUtils.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(new { url = "https://another.domain.url/chat?negotiateVersion=1" }))); } else { return(ResponseUtils.CreateResponse(HttpStatusCode.OK, JsonConvert.SerializeObject(new { connectionId = "0rge0d00-0040-0030-0r00-000q00r00e00", availableTransports = new object[] { new { transport = "LongPolling", transferFormats = new[] { "Text" } }, } }))); } }); testHttpHandler.OnLongPoll((token) => { var tcs = new TaskCompletionSource <HttpResponseMessage>(TaskCreationOptions.RunContinuationsAsynchronously); token.Register(() => tcs.TrySetResult(ResponseUtils.CreateResponse(HttpStatusCode.NoContent))); return(tcs.Task); }); testHttpHandler.OnLongPollDelete((token) => ResponseUtils.CreateResponse(HttpStatusCode.Accepted)); using (var noErrorScope = new VerifyNoErrorsScope()) { await WithConnectionAsync( CreateConnection(testHttpHandler, loggerFactory : noErrorScope.LoggerFactory), async (connection) => { await connection.StartAsync().DefaultTimeout(); }); } Assert.Equal("http://fakeuri.org/negotiate?negotiateVersion=1", testHttpHandler.ReceivedRequests[0].RequestUri.ToString()); Assert.Equal("https://another.domain.url/chat/negotiate?negotiateVersion=1", testHttpHandler.ReceivedRequests[1].RequestUri.ToString()); Assert.Equal("https://another.domain.url/chat?negotiateVersion=1&id=0rge0d00-0040-0030-0r00-000q00r00e00", testHttpHandler.ReceivedRequests[2].RequestUri.ToString()); Assert.Equal("https://another.domain.url/chat?negotiateVersion=1&id=0rge0d00-0040-0030-0r00-000q00r00e00", testHttpHandler.ReceivedRequests[3].RequestUri.ToString()); Assert.Equal(5, testHttpHandler.ReceivedRequests.Count); }
public TestHttpMessageHandler(bool autoNegotiate = true) { _handler = (request, cancellationToken) => BaseHandler(request, cancellationToken); if (autoNegotiate) { OnNegotiate((_, cancellationToken) => ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent())); } }
public async Task LongPollingTransportSendsAvailableMessagesWhenTheyArrive() { var sentRequests = new List <byte[]>(); var tcs = new TaskCompletionSource <HttpResponseMessage>(); var firstPoll = true; var mockHttpHandler = new Mock <HttpMessageHandler>(); mockHttpHandler.Protected() .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()) .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) => { await Task.Yield(); if (request.Method == HttpMethod.Post) { // Build a new request object, but convert the entire payload to string sentRequests.Add(await request.Content.ReadAsByteArrayAsync()); } else if (request.Method == HttpMethod.Get) { // First poll completes immediately if (firstPoll) { firstPoll = false; return(ResponseUtils.CreateResponse(HttpStatusCode.OK)); } cancellationToken.Register(() => tcs.TrySetCanceled(cancellationToken)); // This is the poll task return(await tcs.Task); } else if (request.Method == HttpMethod.Delete) { return(ResponseUtils.CreateResponse(HttpStatusCode.Accepted)); } return(ResponseUtils.CreateResponse(HttpStatusCode.OK)); }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) { var longPollingTransport = new LongPollingTransport(httpClient); try { // Start the transport await longPollingTransport.StartAsync(TestUri, TransferFormat.Binary); longPollingTransport.Output.Write(Encoding.UTF8.GetBytes("Hello")); longPollingTransport.Output.Write(Encoding.UTF8.GetBytes("World")); await longPollingTransport.Output.FlushAsync(); longPollingTransport.Output.Complete(); await longPollingTransport.Running.DefaultTimeout(); await longPollingTransport.Input.ReadAllAsync(); Assert.Single(sentRequests); Assert.Equal(new[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)'W', (byte)'o', (byte)'r', (byte)'l', (byte)'d' }, sentRequests[0]); } finally { await longPollingTransport.StopAsync(); } } }
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()); } }); } }
public async Task SSETransportStopsWithErrorIfSendingMessageFails() { bool ExpectedErrors(WriteContext writeContext) { return(writeContext.LoggerName == typeof(ServerSentEventsTransport).FullName && writeContext.EventId.Name == "ErrorSending"); } var eventStreamTcs = new TaskCompletionSource(); var readTcs = new TaskCompletionSource <int>(); var mockHttpHandler = new Mock <HttpMessageHandler>(); mockHttpHandler.Protected() .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()) .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) => { await Task.Yield(); if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("text/event-stream")) == true) { // Receive loop started - allow stopping the transport eventStreamTcs.SetResult(); // returns unfinished task to block pipelines var mockStream = new Mock <Stream>(); mockStream .Setup(s => s.ReadAsync(It.IsAny <Memory <byte> >(), It.IsAny <CancellationToken>())) .Returns <Memory <byte>, CancellationToken>(async(data, ct) => { using (ct.Register(() => readTcs.TrySetCanceled())) { return(await readTcs.Task); } }); mockStream.Setup(s => s.CanRead).Returns(true); return(new HttpResponseMessage { Content = new StreamContent(mockStream.Object) }); } return(ResponseUtils.CreateResponse(System.Net.HttpStatusCode.InternalServerError)); }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) using (StartVerifiableLog(expectedErrorsFilter: ExpectedErrors)) { var sseTransport = new ServerSentEventsTransport(httpClient, LoggerFactory); await sseTransport.StartAsync( new Uri("http://fakeuri.org"), TransferFormat.Text).DefaultTimeout(); await eventStreamTcs.Task; await sseTransport.Output.WriteAsync(new byte[] { 0x42 }); var exception = await Assert.ThrowsAsync <HttpRequestException>(() => sseTransport.Input.ReadAllAsync().DefaultTimeout()); Assert.Contains("500", exception.Message); // Errors are only communicated through the pipe await sseTransport.Running.DefaultTimeout(); } }
public Task ConnectionCannotBeStartedIfNoTransportProvidedByServer() { return(RunInvalidNegotiateResponseTest <NoTransportSupportedException>(ResponseUtils.CreateNegotiationContent(transportTypes: HttpTransportType.None), "None of the transports supported by the client are supported by the server.")); }
public TestHttpMessageHandler(bool autoNegotiate = true, bool handleFirstPoll = true) { if (autoNegotiate) { OnNegotiate((_, cancellationToken) => ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent())); } if (handleFirstPoll) { var firstPoll = true; OnRequest(async(request, next, cancellationToken) => { if (ResponseUtils.IsLongPollRequest(request) && firstPoll) { firstPoll = false; return(ResponseUtils.CreateResponse(HttpStatusCode.OK)); } else { return(await next()); } }); } }
public Task StartThrowsFormatExceptionIfNegotiationResponseHasNoConnectionId() { return(RunInvalidNegotiateResponseTest <FormatException>(ResponseUtils.CreateNegotiationContent(connectionId: null), "Invalid connection id returned in negotiation response.")); }