async Task ListenerShutdownWithPendingAcceptsTest(EndpointTestType endpointTestType) { HybridConnectionListener listener = null; try { listener = GetHybridConnectionListener(endpointTestType); var client = GetHybridConnectionClient(endpointTestType); TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(TimeSpan.FromSeconds(20)); var acceptTasks = new List <Task <HybridConnectionStream> >(600); TestUtility.Log($"Calling listener.AcceptConnectionAsync() {acceptTasks.Capacity} times"); for (int i = 0; i < acceptTasks.Capacity; i++) { acceptTasks.Add(listener.AcceptConnectionAsync()); Assert.False(acceptTasks[i].IsCompleted); } TestUtility.Log($"Closing {listener}"); await listener.CloseAsync(TimeSpan.FromSeconds(10)); listener = null; for (int i = 0; i < acceptTasks.Count; i++) { Assert.True(acceptTasks[i].Wait(TimeSpan.FromSeconds(5))); Assert.Null(acceptTasks[i].Result); } } finally { await this.SafeCloseAsync(listener); } }
async Task RawWebSocketSenderTest(EndpointTestType endpointTestType) { HybridConnectionListener listener = null; try { listener = GetHybridConnectionListener(endpointTestType); var clientWebSocket = new ClientWebSocket(); var wssUri = await GetWebSocketConnectionUri(endpointTestType); using (var cancelSource = new CancellationTokenSource(TimeSpan.FromMinutes(1))) { TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(cancelSource.Token); await clientWebSocket.ConnectAsync(wssUri, cancelSource.Token); var listenerStream = await listener.AcceptConnectionAsync(); TestUtility.Log("Client and Listener are connected!"); Assert.Null(clientWebSocket.SubProtocol); byte[] sendBuffer = this.CreateBuffer(1024, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); await clientWebSocket.SendAsync(new ArraySegment <byte>(sendBuffer), WebSocketMessageType.Binary, true, cancelSource.Token); TestUtility.Log($"clientWebSocket wrote {sendBuffer.Length} bytes"); byte[] readBuffer = new byte[sendBuffer.Length]; await this.ReadCountBytesAsync(listenerStream, readBuffer, 0, readBuffer.Length, TimeSpan.FromSeconds(30)); Assert.Equal(sendBuffer, readBuffer); TestUtility.Log("Calling clientStream.CloseAsync"); var clientStreamCloseTask = clientWebSocket.CloseAsync(WebSocketCloseStatus.EndpointUnavailable, "From Test Code", cancelSource.Token); TestUtility.Log("Reading from listenerStream"); int bytesRead = await this.SafeReadAsync(listenerStream, readBuffer, 0, readBuffer.Length); TestUtility.Log($"listenerStream.Read returned {bytesRead} bytes"); Assert.Equal(0, bytesRead); TestUtility.Log("Calling listenerStream.CloseAsync"); var listenerStreamCloseTask = listenerStream.CloseAsync(cancelSource.Token); await listenerStreamCloseTask; TestUtility.Log("Calling listenerStream.CloseAsync completed"); await clientStreamCloseTask; TestUtility.Log("Calling clientStream.CloseAsync completed"); TestUtility.Log($"Closing {listener}"); await listener.CloseAsync(TimeSpan.FromSeconds(10)); listener = null; } } finally { await this.SafeCloseAsync(listener); } }
async Task WriteLargeDataSetTest(EndpointTestType endpointTestType, int kilobytesToSend = 1024) { HybridConnectionListener listener = null; try { listener = GetHybridConnectionListener(endpointTestType); var client = GetHybridConnectionClient(endpointTestType); TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(TimeSpan.FromSeconds(30)); var clientStream = await client.CreateConnectionAsync(); var listenerStream = await listener.AcceptConnectionAsync(); TestUtility.Log($"clientStream and listenerStream connected! {listenerStream}"); byte[] sendBuffer = this.CreateBuffer(kilobytesToSend * 1024, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); byte[] readBuffer = new byte[sendBuffer.Length]; TestUtility.Log($"Sending {sendBuffer.Length} bytes from client->listener"); var sendTask = clientStream.WriteAsync(sendBuffer, 0, sendBuffer.Length); var readTask = this.ReadCountBytesAsync(listenerStream, readBuffer, 0, readBuffer.Length, TimeSpan.FromSeconds(30)); await Task.WhenAll(sendTask, readTask); TestUtility.Log($"Sending and Reading complete"); Assert.Equal(sendBuffer, readBuffer); TestUtility.Log($"Sending {sendBuffer.Length} bytes from listener->client"); sendTask = listenerStream.WriteAsync(sendBuffer, 0, sendBuffer.Length); readTask = this.ReadCountBytesAsync(clientStream, readBuffer, 0, readBuffer.Length, TimeSpan.FromSeconds(30)); await Task.WhenAll(sendTask, readTask); TestUtility.Log($"Sending and Reading complete"); Assert.Equal(sendBuffer, readBuffer); TestUtility.Log("Calling clientStream.Shutdown"); clientStream.Shutdown(); int bytesRead = await this.SafeReadAsync(listenerStream, readBuffer, 0, readBuffer.Length); TestUtility.Log($"listenerStream.Read returned {bytesRead} bytes"); Assert.Equal(0, bytesRead); TestUtility.Log("Calling listenerStream.Dispose"); listenerStream.Dispose(); TestUtility.Log("Calling clientStream.Dispose"); clientStream.Dispose(); } finally { await this.SafeCloseAsync(listener); } }
async Task EmptyRequestEmptyResponse(EndpointTestType endpointTestType) { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); HybridConnectionListener listener = this.GetHybridConnectionListener(endpointTestType); try { await listener.OpenAsync(cts.Token); listener.RequestHandler = async(context) => { TestUtility.Log($"RequestHandler: {context.Request.HttpMethod} {context.Request.Url}"); await context.Response.CloseAsync(); }; RelayConnectionStringBuilder connectionString = GetConnectionStringBuilder(endpointTestType); Uri endpointUri = connectionString.Endpoint; Uri hybridHttpUri = new UriBuilder("https://", endpointUri.Host, endpointUri.Port, connectionString.EntityPath).Uri; using (var client = new HttpClient { BaseAddress = hybridHttpUri }) { client.DefaultRequestHeaders.ExpectContinue = false; var getRequest = new HttpRequestMessage(); getRequest.Method = HttpMethod.Get; await AddAuthorizationHeader(connectionString, getRequest, hybridHttpUri); LogRequest(getRequest, client); using (HttpResponseMessage response = await client.SendAsync(getRequest)) { LogResponse(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(0, response.Content.ReadAsStreamAsync().Result.Length); } var postRequest = new HttpRequestMessage(); postRequest.Method = HttpMethod.Post; await AddAuthorizationHeader(connectionString, postRequest, hybridHttpUri); LogRequest(postRequest, client); using (HttpResponseMessage response = await client.SendAsync(postRequest)) { LogResponse(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(0, response.Content.ReadAsStreamAsync().Result.Length); } } await listener.CloseAsync(cts.Token); } finally { await SafeCloseAsync(listener); } }
async Task ClientShutdownTest(EndpointTestType endpointTestType) { HybridConnectionListener listener = null; try { listener = GetHybridConnectionListener(endpointTestType); var client = GetHybridConnectionClient(endpointTestType); TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(TimeSpan.FromSeconds(30)); var clientStream = await client.CreateConnectionAsync(); var listenerStream = await listener.AcceptConnectionAsync(); TestUtility.Log("Client and Listener HybridStreams are connected!"); byte[] sendBuffer = this.CreateBuffer(1024, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); await clientStream.WriteAsync(sendBuffer, 0, sendBuffer.Length); TestUtility.Log($"clientStream wrote {sendBuffer.Length} bytes"); byte[] readBuffer = new byte[sendBuffer.Length]; await this.ReadCountBytesAsync(listenerStream, readBuffer, 0, readBuffer.Length, TimeSpan.FromSeconds(30)); Assert.Equal(sendBuffer, readBuffer); TestUtility.Log("Calling clientStream.Shutdown"); clientStream.Shutdown(); int bytesRead = await this.SafeReadAsync(listenerStream, readBuffer, 0, readBuffer.Length); TestUtility.Log($"listenerStream.Read returned {bytesRead} bytes"); Assert.Equal(0, bytesRead); TestUtility.Log("Calling listenerStream.Shutdown and Dispose"); listenerStream.Shutdown(); listenerStream.Dispose(); bytesRead = await this.SafeReadAsync(clientStream, readBuffer, 0, readBuffer.Length); TestUtility.Log($"clientStream.Read returned {bytesRead} bytes"); Assert.Equal(0, bytesRead); TestUtility.Log("Calling clientStream.Dispose"); clientStream.Dispose(); TestUtility.Log($"Closing {listener}"); await listener.CloseAsync(TimeSpan.FromSeconds(10)); listener = null; } finally { await this.SafeCloseAsync(listener); } }
async Task HybridConnectionTest(EndpointTestType endpointTestType, bool useBuiltInClientWebSocket) { HybridConnectionListener listener = null; try { listener = this.GetHybridConnectionListener(endpointTestType); listener.UseBuiltInClientWebSocket = useBuiltInClientWebSocket; var client = GetHybridConnectionClient(endpointTestType); client.UseBuiltInClientWebSocket = useBuiltInClientWebSocket; TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(TimeSpan.FromSeconds(30)); var clientStream = await client.CreateConnectionAsync(); var listenerStream = await listener.AcceptConnectionAsync(); TestUtility.Log("Client and Listener HybridStreams are connected!"); byte[] sendBuffer = this.CreateBuffer(1024, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); await clientStream.WriteAsync(sendBuffer, 0, sendBuffer.Length); TestUtility.Log($"clientStream wrote {sendBuffer.Length} bytes"); byte[] readBuffer = new byte[sendBuffer.Length]; await this.ReadCountBytesAsync(listenerStream, readBuffer, 0, readBuffer.Length, TimeSpan.FromSeconds(30)); Assert.Equal(sendBuffer, readBuffer); TestUtility.Log("Calling clientStream.CloseAsync"); var clientStreamCloseTask = clientStream.CloseAsync(new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token); TestUtility.Log("Reading from listenerStream"); int bytesRead = await this.SafeReadAsync(listenerStream, readBuffer, 0, readBuffer.Length); TestUtility.Log($"listenerStream.Read returned {bytesRead} bytes"); Assert.Equal(0, bytesRead); TestUtility.Log("Calling listenerStream.CloseAsync"); var listenerStreamCloseTask = listenerStream.CloseAsync(new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token); await listenerStreamCloseTask; TestUtility.Log("Calling listenerStream.CloseAsync completed"); await clientStreamCloseTask; TestUtility.Log("Calling clientStream.CloseAsync completed"); TestUtility.Log($"Closing {listener}"); await listener.CloseAsync(TimeSpan.FromSeconds(10)); listener = null; } finally { await this.SafeCloseAsync(listener); } }
async Task LargeRequestEmptyResponse(EndpointTestType endpointTestType) { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); HybridConnectionListener listener = this.GetHybridConnectionListener(endpointTestType); try { await listener.OpenAsync(cts.Token); string requestBody = null; listener.RequestHandler = async(context) => { TestUtility.Log("HybridConnectionListener.RequestHandler invoked with Request:"); TestUtility.Log($"{context.Request.HttpMethod} {context.Request.Url}"); context.Request.Headers.AllKeys.ToList().ForEach((k) => TestUtility.Log($"{k}: {context.Request.Headers[k]}")); requestBody = StreamToString(context.Request.InputStream); TestUtility.Log($"Body Length: {requestBody.Length}"); await context.Response.CloseAsync(); }; RelayConnectionStringBuilder connectionString = GetConnectionStringBuilder(endpointTestType); Uri endpointUri = connectionString.Endpoint; Uri hybridHttpUri = new UriBuilder("https://", endpointUri.Host, endpointUri.Port, connectionString.EntityPath).Uri; using (var client = new HttpClient { BaseAddress = hybridHttpUri }) { client.DefaultRequestHeaders.ExpectContinue = false; var postRequest = new HttpRequestMessage(); await AddAuthorizationHeader(connectionString, postRequest, hybridHttpUri); postRequest.Method = HttpMethod.Post; var body = new string('y', 65 * 1024); postRequest.Content = new StringContent(body); LogRequest(postRequest, client, showBody: false); using (HttpResponseMessage response = await client.SendAsync(postRequest, cts.Token)) { LogResponse(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(0, response.Content.ReadAsStreamAsync().Result.Length); Assert.Equal(body.Length, requestBody.Length); } } await listener.CloseAsync(cts.Token); } finally { await SafeCloseAsync(listener); } }
async Task RawWebSocketSenderTest(EndpointTestType endpointTestType) { HybridConnectionListener listener = null; try { listener = GetHybridConnectionListener(endpointTestType); await TestRawWebSocket(listener, endpointTestType); } finally { await this.SafeCloseAsync(listener); } }
/// <summary> /// Returns a HybridConnectionListener based on the EndpointTestType (authenticated/unauthenticated). /// </summary> protected HybridConnectionListener GetHybridConnectionListener(EndpointTestType endpointTestType) { // Even if the endpoint is unauthenticated, the *listener* still needs to be authenticated if (endpointTestType == EndpointTestType.Unauthenticated) { var connectionStringBuilder = new RelayConnectionStringBuilder(this.ConnectionString) { EntityPath = Constants.UnauthenticatedEntityPath }; return(new HybridConnectionListener(connectionStringBuilder.ToString())); } return(new HybridConnectionListener(GetConnectionString(endpointTestType))); }
async Task NonExistantNamespaceTest(EndpointTestType endpointTestType) { string badAddress = $"sb://fakeendpoint.{Guid.NewGuid()}.com"; HybridConnectionListener listener = null; try { TestUtility.Log($"Setting ConnectionStringBuilder.Endpoint to '{badAddress}'"); var fakeEndpointConnectionStringBuilder = new RelayConnectionStringBuilder(this.ConnectionString) { Endpoint = new Uri(badAddress) }; if (endpointTestType == EndpointTestType.Authenticated) { fakeEndpointConnectionStringBuilder.EntityPath = Constants.AuthenticatedEntityPath; } else { fakeEndpointConnectionStringBuilder.EntityPath = Constants.UnauthenticatedEntityPath; } var fakeEndpointConnectionString = fakeEndpointConnectionStringBuilder.ToString(); listener = new HybridConnectionListener(fakeEndpointConnectionString); var client = new HybridConnectionClient(fakeEndpointConnectionString); TestUtility.Log($"Opening Listener"); var relayException = await Assert.ThrowsAsync <RelayException>(() => listener.OpenAsync()); TestUtility.Log($"Received Exception {relayException.ToStringWithoutCallstack()}"); Assert.True(relayException.IsTransient, "Listener.Open() should return a transient Exception"); TestUtility.Log($"Opening Client"); relayException = await Assert.ThrowsAsync <RelayException>(() => client.CreateConnectionAsync()); TestUtility.Log($"Received Exception {relayException.ToStringWithoutCallstack()}"); Assert.True(relayException.IsTransient, "Client.Open() should return a transient Exception"); } finally { await this.SafeCloseAsync(listener); } }
/// <summary> /// Since these tests all share a common connection string, this method will modify the /// endpoint / shared access keys as needed based on the EndpointTestType. /// </summary> protected RelayConnectionStringBuilder GetConnectionStringBuilder(EndpointTestType endpointTestType) { var connectionStringBuilder = new RelayConnectionStringBuilder(this.ConnectionString); if (endpointTestType == EndpointTestType.Unauthenticated) { connectionStringBuilder.EntityPath = Constants.UnauthenticatedEntityPath; connectionStringBuilder.SharedAccessKey = string.Empty; connectionStringBuilder.SharedAccessKeyName = string.Empty; connectionStringBuilder.SharedAccessSignature = string.Empty; } else { connectionStringBuilder.EntityPath = Constants.AuthenticatedEntityPath; } return(connectionStringBuilder); }
async Task NonExistantNamespaceTest(EndpointTestType endpointTestType) { HybridConnectionListener listener = null; try { TestUtility.Log("Setting ConnectionStringBuilder.Endpoint to 'sb://fakeendpoint.com'"); var fakeEndpointConnectionStringBuilder = new RelayConnectionStringBuilder(this.ConnectionString) { Endpoint = new Uri("sb://fakeendpoint.com") }; if (endpointTestType == EndpointTestType.Authenticated) { fakeEndpointConnectionStringBuilder.EntityPath = Constants.AuthenticatedEntityPath; } else { fakeEndpointConnectionStringBuilder.EntityPath = Constants.UnauthenticatedEntityPath; } var fakeEndpointConnectionString = fakeEndpointConnectionStringBuilder.ToString(); listener = new HybridConnectionListener(fakeEndpointConnectionString); var client = new HybridConnectionClient(fakeEndpointConnectionString); // TODO: Remove this once .NET Core supports inspecting the StatusCode/Description. https://github.com/dotnet/corefx/issues/13773 #if NET451 await Assert.ThrowsAsync <EndpointNotFoundException>(() => listener.OpenAsync()); await Assert.ThrowsAsync <EndpointNotFoundException>(() => client.CreateConnectionAsync()); #else await Assert.ThrowsAsync <RelayException>(() => listener.OpenAsync()); await Assert.ThrowsAsync <RelayException>(() => client.CreateConnectionAsync()); #endif } finally { await this.SafeCloseAsync(listener); } }
/// <summary> /// Since these tests all share a common connection string, this method will modify the /// endpoint / shared access keys as needed based on the EndpointTestType, and return a WebSocket URI. /// </summary> async Task <Uri> GetWebSocketConnectionUri(EndpointTestType endpointTestType) { var clientConnectionString = GetConnectionString(endpointTestType); var connectionStringBuilder = new RelayConnectionStringBuilder(clientConnectionString); var connectionUriString = $"wss://{connectionStringBuilder.Endpoint.Host}/$hc/{connectionStringBuilder.EntityPath}"; if (endpointTestType == EndpointTestType.Authenticated) { var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider( connectionStringBuilder.SharedAccessKeyName, connectionStringBuilder.SharedAccessKey); var token = await tokenProvider.GetTokenAsync(connectionStringBuilder.Endpoint.ToString(), TimeSpan.FromMinutes(10)); connectionUriString += $"?sb-hc-token={WebUtility.UrlEncode(token.TokenString)}"; } return(new Uri(connectionUriString)); }
async Task CustomWebSocketTest() { HybridConnectionListener listener = null; try { EndpointTestType endpointTestType = EndpointTestType.Authenticated; listener = GetHybridConnectionListener(endpointTestType); CustomClientWebSocketFactory factory = new CustomClientWebSocketFactory(); listener.ClientWebSocketFactory = factory; await TestRawWebSocket(listener, endpointTestType); Assert.True(factory.WasCreateCalled); } finally { await this.SafeCloseAsync(listener); } }
async Task ConcurrentClientsTest(EndpointTestType endpointTestType) { const int ClientCount = 100; HybridConnectionListener listener = null; try { listener = GetHybridConnectionListener(endpointTestType); var client = GetHybridConnectionClient(endpointTestType); TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(TimeSpan.FromSeconds(10)); TestUtility.Log($"Opening {ClientCount} connections quickly"); var createConnectionTasks = new List <Task <HybridConnectionStream> >(); for (var i = 0; i < ClientCount; i++) { createConnectionTasks.Add(client.CreateConnectionAsync()); } var senderTasks = new List <Task>(); for (var i = 0; i < ClientCount; i++) { this.AcceptEchoListener(listener); senderTasks.Add(this.RunEchoClientAsync(await createConnectionTasks[i], i + 1)); } await Task.WhenAll(senderTasks); TestUtility.Log($"Closing {listener}"); await listener.CloseAsync(TimeSpan.FromSeconds(10)); listener = null; } finally { await this.SafeCloseAsync(listener); } }
async Task NonExistantNamespaceTest(EndpointTestType endpointTestType) { HybridConnectionListener listener = null; try { TestUtility.Log("Setting ConnectionStringBuilder.Endpoint to 'sb://fakeendpoint.com'"); var fakeEndpointConnectionStringBuilder = new RelayConnectionStringBuilder(this.ConnectionString) { Endpoint = new Uri("sb://fakeendpoint.com") }; if (endpointTestType == EndpointTestType.Authenticated) { fakeEndpointConnectionStringBuilder.EntityPath = Constants.AuthenticatedEntityPath; } else { fakeEndpointConnectionStringBuilder.EntityPath = Constants.UnauthenticatedEntityPath; } var fakeEndpointConnectionString = fakeEndpointConnectionStringBuilder.ToString(); listener = new HybridConnectionListener(fakeEndpointConnectionString); var client = new HybridConnectionClient(fakeEndpointConnectionString); await Assert.ThrowsAsync <EndpointNotFoundException>(() => listener.OpenAsync()); await Assert.ThrowsAsync <EndpointNotFoundException>(() => client.CreateConnectionAsync()); } finally { await this.SafeCloseAsync(listener); } }
async Task ListenerAbortWhileClientReadingTest(EndpointTestType endpointTestType) { HybridConnectionListener listener = null; try { listener = GetHybridConnectionListener(endpointTestType); var client = GetHybridConnectionClient(endpointTestType); TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(TimeSpan.FromSeconds(30)); var clientStream = await client.CreateConnectionAsync(); var listenerStream = await listener.AcceptConnectionAsync(); TestUtility.Log("Client and Listener HybridStreams are connected!"); using (var cancelSource = new CancellationTokenSource()) { TestUtility.Log("Aborting listener WebSocket"); cancelSource.Cancel(); await listenerStream.CloseAsync(cancelSource.Token); } byte[] readBuffer = new byte[1024]; await Assert.ThrowsAsync <RelayException>(() => clientStream.ReadAsync(readBuffer, 0, readBuffer.Length)); TestUtility.Log("Calling clientStream.Close"); var clientCloseTask = clientStream.CloseAsync(CancellationToken.None); } finally { await this.SafeCloseAsync(listener); } }
async Task SmallRequestSmallResponse(EndpointTestType endpointTestType) { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); HybridConnectionListener listener = null; try { listener = this.GetHybridConnectionListener(endpointTestType); RelayConnectionStringBuilder connectionString = GetConnectionStringBuilder(endpointTestType); Uri endpointUri = connectionString.Endpoint; string expectedResponse = "{ \"a\" : true }"; HttpStatusCode expectedStatusCode = HttpStatusCode.OK; listener.RequestHandler = (context) => { TestUtility.Log("HybridConnectionListener.RequestHandler invoked with Request:"); TestUtility.Log($"{context.Request.HttpMethod} {context.Request.Url}"); context.Request.Headers.AllKeys.ToList().ForEach((k) => TestUtility.Log($"{k}: {context.Request.Headers[k]}")); TestUtility.Log(StreamToString(context.Request.InputStream)); context.Response.StatusCode = expectedStatusCode; byte[] responseBytes = Encoding.UTF8.GetBytes(expectedResponse); context.Response.OutputStream.Write(responseBytes, 0, responseBytes.Length); context.Response.Close(); }; TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(cts.Token); Uri hybridHttpUri = new UriBuilder("https://", endpointUri.Host, endpointUri.Port, connectionString.EntityPath).Uri; using (var client = new HttpClient { BaseAddress = hybridHttpUri }) { client.DefaultRequestHeaders.ExpectContinue = false; var getRequest = new HttpRequestMessage(); await AddAuthorizationHeader(connectionString, getRequest, hybridHttpUri); getRequest.Method = HttpMethod.Get; LogHttpRequest(getRequest, client); using (HttpResponseMessage response = await client.SendAsync(getRequest, cts.Token)) { LogHttpResponse(response); Assert.Equal(expectedStatusCode, response.StatusCode); Assert.Equal("OK", response.ReasonPhrase); Assert.Equal(expectedResponse, await response.Content.ReadAsStringAsync()); } var postRequest = new HttpRequestMessage(); await AddAuthorizationHeader(connectionString, postRequest, hybridHttpUri); postRequest.Method = HttpMethod.Post; string body = "{ \"a\": 11, \"b\" :22, \"c\":\"test\", \"d\":true}"; postRequest.Content = new StringContent(body); postRequest.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); LogHttpRequest(postRequest, client); using (HttpResponseMessage response = await client.SendAsync(postRequest, cts.Token)) { LogHttpResponse(response); Assert.Equal(expectedStatusCode, response.StatusCode); Assert.Equal("OK", response.ReasonPhrase); Assert.Equal(expectedResponse, await response.Content.ReadAsStringAsync()); } } TestUtility.Log($"Closing {listener}"); await listener.CloseAsync(cts.Token); listener = null; } finally { cts.Dispose(); await this.SafeCloseAsync(listener); } }
async Task RequestHeadersTest(EndpointTestType endpointTestType) { HybridConnectionListener listener = null; try { listener = this.GetHybridConnectionListener(endpointTestType); var client = GetHybridConnectionClient(endpointTestType); var listenerRequestTcs = new TaskCompletionSource <IDictionary <string, string> >(); listener.AcceptHandler = (context) => { try { var headers = new Dictionary <string, string>(); foreach (string headerName in context.Request.Headers.AllKeys) { headers[headerName] = context.Request.Headers[headerName]; } listenerRequestTcs.SetResult(headers); return(Task.FromResult(true)); } catch (Exception e) { listenerRequestTcs.TrySetException(e); throw; } }; TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(TimeSpan.FromSeconds(30)); const string ExpectedRequestHeaderName = "CustomHeader1"; const string ExpectedRequestHeaderValue = "Some value here; other-value/here."; var clientRequestHeaders = new Dictionary <string, string>(); clientRequestHeaders[ExpectedRequestHeaderName] = ExpectedRequestHeaderValue; var clientStream = await client.CreateConnectionAsync(clientRequestHeaders); var listenerStream = await listener.AcceptConnectionAsync(); TestUtility.Log("Client and Listener HybridStreams are connected!"); Assert.True(listenerRequestTcs.Task.Wait(TimeSpan.FromSeconds(5)), "AcceptHandler should have been invoked by now."); IDictionary <string, string> listenerRequestHeaders = await listenerRequestTcs.Task; Assert.True(listenerRequestHeaders.ContainsKey(ExpectedRequestHeaderName), "Expected header name should be present."); Assert.Equal(ExpectedRequestHeaderValue, listenerRequestHeaders[ExpectedRequestHeaderName]); byte[] sendBuffer = this.CreateBuffer(1025, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); await clientStream.WriteAsync(sendBuffer, 0, sendBuffer.Length); TestUtility.Log($"clientStream wrote {sendBuffer.Length} bytes"); byte[] readBuffer = new byte[sendBuffer.Length]; await this.ReadCountBytesAsync(listenerStream, readBuffer, 0, sendBuffer.Length, TimeSpan.FromSeconds(30)); Assert.Equal(sendBuffer, readBuffer); var clientStreamCloseTask = clientStream.CloseAsync(new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token); await listenerStream.CloseAsync(new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token); await clientStreamCloseTask; TestUtility.Log($"Closing {listener}"); await listener.CloseAsync(TimeSpan.FromSeconds(10)); listener = null; } finally { await this.SafeCloseAsync(listener); } }
/// <summary> /// Returns a HybridConnectionClient based on the EndpointTestType (authenticated/unauthenticated). /// </summary> protected HybridConnectionClient GetHybridConnectionClient(EndpointTestType endpointTestType) { var connectionString = GetConnectionString(endpointTestType); return(new HybridConnectionClient(connectionString)); }
async Task LoadBalancedListeners(EndpointTestType endpointTestType) { HybridConnectionListener listener1 = null; HybridConnectionListener listener2 = null; var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); try { listener1 = this.GetHybridConnectionListener(endpointTestType); listener2 = this.GetHybridConnectionListener(endpointTestType); RelayConnectionStringBuilder connectionString = GetConnectionStringBuilder(endpointTestType); Uri endpointUri = connectionString.Endpoint; TestUtility.Log("Opening HybridConnectionListeners"); await Task.WhenAll( listener1.OpenAsync(cts.Token), listener2.OpenAsync(cts.Token)); const int TotalExpectedRequestCount = 50; long listenerRequestCount1 = 0; long listenerRequestCount2 = 0; listener1.RequestHandler = async(context) => { Interlocked.Increment(ref listenerRequestCount1); context.Response.OutputStream.WriteByte(1); await context.Response.CloseAsync(); }; listener2.RequestHandler = async(context) => { Interlocked.Increment(ref listenerRequestCount2); context.Response.OutputStream.WriteByte(2); await context.Response.CloseAsync(); }; Uri hybridHttpUri = new UriBuilder("https://", endpointUri.Host, endpointUri.Port, connectionString.EntityPath).Uri; await Enumerable.Range(0, TotalExpectedRequestCount).ParallelBatchAsync( batchSize: 10, parallelTasksCount: 10, asyncTask: async(indexes) => { string messageIndexes = string.Join(",", indexes); TestUtility.Log($"Messages {messageIndexes} starting"); try { foreach (var index in indexes) { var httpRequest = (HttpWebRequest)WebRequest.Create(hybridHttpUri); using (var abortRegistration = cts.Token.Register(() => httpRequest.Abort())) { httpRequest.Method = HttpMethod.Get.Method; await AddAuthorizationHeader(connectionString, httpRequest.Headers, hybridHttpUri); using (var httpResponse = (HttpWebResponse)(await httpRequest.GetResponseAsync())) { Assert.Equal(HttpStatusCode.OK, httpResponse.StatusCode); } } } } catch (WebException webException) { TestUtility.Log($"Messages {messageIndexes} Error: {webException}"); throw; } TestUtility.Log($"Messages {messageIndexes} complete"); }); TestUtility.Log($"Listener1 Request Count: {listenerRequestCount1}"); TestUtility.Log($"Listener2 Request Count: {listenerRequestCount2}"); Assert.Equal(TotalExpectedRequestCount, listenerRequestCount1 + listenerRequestCount2); Assert.True(listenerRequestCount1 > 0, "Listener 1 should have received some of the events."); Assert.True(listenerRequestCount2 > 0, "Listener 2 should have received some of the events."); await Task.WhenAll( listener1.CloseAsync(cts.Token), listener2.CloseAsync(cts.Token)); } finally { TestUtility.Log("Shutting down"); cts.Dispose(); await Task.WhenAll( SafeCloseAsync(listener1), SafeCloseAsync(listener2)); } }
/// <summary> /// Since these tests all share a common connection string, this method will modify the /// endpoint / shared access keys as needed based on the EndpointTestType. /// </summary> protected string GetConnectionString(EndpointTestType endpointTestType) { var connectionStringBuilder = this.GetConnectionStringBuilder(endpointTestType); return(connectionStringBuilder.ToString()); }
async Task MultiValueHeader(EndpointTestType endpointTestType) { HybridConnectionListener listener = this.GetHybridConnectionListener(endpointTestType); try { RelayConnectionStringBuilder connectionString = GetConnectionStringBuilder(endpointTestType); Uri endpointUri = connectionString.Endpoint; TestUtility.Log("Calling HybridConnectionListener.Open"); await listener.OpenAsync(TimeSpan.FromSeconds(30)); const string CustomHeaderName = "X-CustomHeader"; string requestHeaderValue = string.Empty; listener.RequestHandler = (context) => { TestUtility.Log("HybridConnectionListener.RequestHandler invoked with Request:"); TestUtility.Log($"{context.Request.HttpMethod} {context.Request.Url}"); context.Request.Headers.AllKeys.ToList().ForEach((k) => TestUtility.Log($"{k}: {context.Request.Headers[k]}")); TestUtility.Log(StreamToString(context.Request.InputStream)); requestHeaderValue = context.Request.Headers[CustomHeaderName]; context.Response.Headers.Add(CustomHeaderName, "responseValue1"); context.Response.Headers.Add(CustomHeaderName, "responseValue2"); context.Response.OutputStream.Close(); }; Uri hybridHttpUri = new UriBuilder("https://", endpointUri.Host, endpointUri.Port, connectionString.EntityPath).Uri; using (var client = new HttpClient { BaseAddress = hybridHttpUri }) { client.DefaultRequestHeaders.ExpectContinue = false; var getRequest = new HttpRequestMessage(); getRequest.Method = HttpMethod.Get; await AddAuthorizationHeader(connectionString, getRequest, hybridHttpUri); getRequest.Headers.Add(CustomHeaderName, "requestValue1"); getRequest.Headers.Add(CustomHeaderName, "requestValue2"); LogRequest(getRequest, client); using (HttpResponseMessage response = await client.SendAsync(getRequest)) { LogResponse(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("requestValue1, requestValue2", requestHeaderValue); string[] responseHeaders = string.Join(",", response.Headers.GetValues(CustomHeaderName)).Split(new[] { ',' }); for (int i = 0; i < responseHeaders.Length; i++) { responseHeaders[i] = responseHeaders[i].Trim(' ', '\t'); } Assert.Equal(new [] { "responseValue1", "responseValue2" }, responseHeaders); } } await listener.CloseAsync(TimeSpan.FromSeconds(10)); } finally { await SafeCloseAsync(listener); } }
async Task QueryString(EndpointTestType endpointTestType) { HybridConnectionListener listener = this.GetHybridConnectionListener(endpointTestType); try { RelayConnectionStringBuilder connectionString = GetConnectionStringBuilder(endpointTestType); Uri endpointUri = connectionString.Endpoint; TestUtility.Log("Calling HybridConnectionListener.Open"); await listener.OpenAsync(TimeSpan.FromSeconds(30)); var queryStringTests = new[] { new { Original = "?foo=bar", Output = "?foo=bar" }, new { Original = "?sb-hc-id=1", Output = "" }, new { Original = "?sb-hc-id=1&custom=value", Output = "?custom=value" }, new { Original = "?custom=value&sb-hc-id=1", Output = "?custom=value" }, new { Original = "?sb-hc-undefined=true", Output = "" }, new { Original = "? Key = Value With Space ", Output = "?+Key+=++Value+With+Space" }, // HttpUtility.ParseQueryString.ToString() in the cloud service changes this new { Original = "?key='value'", Output = "?key=%27value%27" }, // HttpUtility.ParseQueryString.ToString() in the cloud service changes this new { Original = "?key=\"value\"", Output = "?key=%22value%22" }, // HttpUtility.ParseQueryString.ToString() in the cloud service changes this new { Original = "?key=<value>", Output = "?key=%3cvalue%3e" }, #if PENDING_SERVICE_UPDATE new { Original = "?foo=bar&", Output = "?foo=bar&" }, new { Original = "?&foo=bar", Output = "?foo=bar" }, // HttpUtility.ParseQueryString.ToString() in the cloud service changes this new { Original = "?sb-hc-undefined", Output = "?sb-hc-undefined" }, new { Original = "?CustomValue", Output = "?CustomValue" }, new { Original = "?custom-Value", Output = "?custom-Value" }, new { Original = "?custom&value", Output = "?custom&value" }, new { Original = "?&custom&value&", Output = "?&custom&value&" }, new { Original = "?&&value&&", Output = "?&&value&&" }, new { Original = "?+", Output = "?+" }, new { Original = "?%20", Output = "?+" }, // HttpUtility.ParseQueryString.ToString() in the cloud service changes this new { Original = "? Not a key value pair ", Output = "?+Not+a+key+value+pair" }, // HttpUtility.ParseQueryString.ToString() in the cloud service changes this new { Original = "?&+&", Output = "?&+&" }, new { Original = "?%2f%3a%3d%26", Output = "?%2f%3a%3d%26" }, #endif }; RelayedHttpListenerContext actualRequestContext = null; listener.RequestHandler = async(context) => { TestUtility.Log($"RequestHandler {context.Request.HttpMethod} {context.Request.Url}"); actualRequestContext = context; await context.Response.CloseAsync(); }; foreach (var queryStringTest in queryStringTests) { string originalQueryString = queryStringTest.Original; string expectedQueryString = queryStringTest.Output; actualRequestContext = null; TestUtility.Log($"Testing Query String '{originalQueryString}'"); Uri hybridHttpUri = new UriBuilder("https://", endpointUri.Host, endpointUri.Port, connectionString.EntityPath, originalQueryString).Uri; using (var client = new HttpClient { BaseAddress = hybridHttpUri }) { client.DefaultRequestHeaders.ExpectContinue = false; var getRequest = new HttpRequestMessage(); getRequest.Method = HttpMethod.Get; await AddAuthorizationHeader(connectionString, getRequest, hybridHttpUri); LogRequestLine(getRequest, client); using (HttpResponseMessage response = await client.SendAsync(getRequest)) { LogResponseLine(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(expectedQueryString, actualRequestContext.Request.Url.Query); } } } await listener.CloseAsync(TimeSpan.FromSeconds(10)); } finally { await SafeCloseAsync(listener); } }
async Task RequestHandlerErrors(EndpointTestType endpointTestType) { HybridConnectionListener listener = this.GetHybridConnectionListener(endpointTestType); try { RelayConnectionStringBuilder connectionString = GetConnectionStringBuilder(endpointTestType); Uri endpointUri = connectionString.Endpoint; await listener.OpenAsync(TimeSpan.FromSeconds(30)); Uri hybridHttpUri = new UriBuilder("https://", endpointUri.Host, endpointUri.Port, connectionString.EntityPath).Uri; using (var client = new HttpClient { BaseAddress = hybridHttpUri }) { client.DefaultRequestHeaders.ExpectContinue = false; var request = new HttpRequestMessage(); request.Method = HttpMethod.Get; await AddAuthorizationHeader(connectionString, request, hybridHttpUri); LogRequestLine(request, client); using (HttpResponseMessage response = await client.SendAsync(request)) { LogResponse(response); Assert.Equal(HttpStatusCode.NotImplemented, response.StatusCode); Assert.Contains("RequestHandler has not been configured", response.ReasonPhrase); Assert.Contains("TrackingId", response.ReasonPhrase); Assert.Contains(hybridHttpUri.Host, response.ReasonPhrase); Assert.Contains(connectionString.EntityPath, response.ReasonPhrase); string viaHeader = response.Headers.Via.ToString(); Assert.Contains(hybridHttpUri.Host, viaHeader); } string userExceptionMessage = "User Error"; listener.RequestHandler = (context) => { throw new InvalidOperationException(userExceptionMessage); }; request = new HttpRequestMessage(); request.Method = HttpMethod.Get; await AddAuthorizationHeader(connectionString, request, hybridHttpUri); LogRequestLine(request, client); using (HttpResponseMessage response = await client.SendAsync(request)) { LogResponse(response); Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); Assert.Contains("RequestHandler", response.ReasonPhrase); Assert.Contains("exception", response.ReasonPhrase, StringComparison.OrdinalIgnoreCase); Assert.Contains("TrackingId", response.ReasonPhrase); Assert.Contains(hybridHttpUri.Host, response.ReasonPhrase); Assert.Contains(connectionString.EntityPath, response.ReasonPhrase); string viaHeader = response.Headers.Via.ToString(); Assert.Contains(hybridHttpUri.Host, viaHeader); // Details of the Exception in the Listener must not be sent Assert.DoesNotContain("InvalidOperationException", response.ReasonPhrase); Assert.DoesNotContain(userExceptionMessage, response.ReasonPhrase); } } await listener.CloseAsync(TimeSpan.FromSeconds(10)); } finally { await SafeCloseAsync(listener); } }
async Task ResponseHeadersAfterBody(EndpointTestType endpointTestType) { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); var listener = this.GetHybridConnectionListener(endpointTestType); try { await listener.OpenAsync(cts.Token); var requestHandlerComplete = new TaskCompletionSource <object>(); listener.RequestHandler = (context) => { TestUtility.Log($"RequestHandler: {context.Request.HttpMethod} {context.Request.Url}"); try { // Begin writing to the output stream context.Response.OutputStream.WriteByte((byte)'X'); // Now try to change some things which are no longer mutable var exception = Assert.Throws <InvalidOperationException>(() => context.Response.StatusCode = HttpStatusCode.Found); Assert.Contains("TrackingId", exception.Message, StringComparison.OrdinalIgnoreCase); exception = Assert.Throws <InvalidOperationException>(() => context.Response.StatusDescription = "Test Description"); Assert.Contains("TrackingId", exception.Message, StringComparison.OrdinalIgnoreCase); exception = Assert.Throws <InvalidOperationException>(() => context.Response.Headers["CustomHeader"] = "Header value"); Assert.Contains("TrackingId", exception.Message, StringComparison.OrdinalIgnoreCase); context.Response.OutputStream.Close(); requestHandlerComplete.TrySetResult(null); } catch (Exception e) { requestHandlerComplete.TrySetException(e); } }; RelayConnectionStringBuilder connectionString = GetConnectionStringBuilder(endpointTestType); Uri endpointUri = connectionString.Endpoint; Uri hybridHttpUri = new UriBuilder("https://", endpointUri.Host, endpointUri.Port, connectionString.EntityPath).Uri; using (var client = new HttpClient { BaseAddress = hybridHttpUri }) { client.DefaultRequestHeaders.ExpectContinue = false; var getRequest = new HttpRequestMessage(); await AddAuthorizationHeader(connectionString, getRequest, hybridHttpUri); LogRequest(getRequest, client); var sendTask = client.SendAsync(getRequest); // This will throw if anything failed in the RequestHandler await requestHandlerComplete.Task; using (HttpResponseMessage response = await sendTask) { LogResponse(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(1, (await response.Content.ReadAsStreamAsync()).Length); } } await listener.CloseAsync(cts.Token); } finally { await SafeCloseAsync(listener); } }
async Task AcceptHandlerTest(EndpointTestType endpointTestType) { HybridConnectionListener listener = null; try { listener = GetHybridConnectionListener(endpointTestType); TestUtility.Log($"Opening {listener}"); await listener.OpenAsync(TimeSpan.FromSeconds(30)); // Install a Custom AcceptHandler which allows using Headers to control the StatusCode, StatusDescription, // and whether to accept or reject the client. listener.AcceptHandler = TestAcceptHandler; var clientConnectionString = GetConnectionString(endpointTestType); var connectionStringBuilder = new RelayConnectionStringBuilder(clientConnectionString); var wssUri = await GetWebSocketConnectionUri(endpointTestType); TestUtility.Log($"Using WebSocket address {wssUri}"); using (var cancelSource = new CancellationTokenSource(TimeSpan.FromMinutes(1))) { TestUtility.Log("Testing HybridConnectionListener.AcceptHandler accepting with only returning true"); AcceptEchoListener(listener); var clientWebSocket = new ClientWebSocket(); clientWebSocket.Options.SetRequestHeader(TestAcceptHandlerResultHeader, true.ToString()); await clientWebSocket.ConnectAsync(wssUri, cancelSource.Token); await clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test closing web socket", cancelSource.Token); TestUtility.Log("Testing HybridConnectionListener.AcceptHandler accepting and setting response header"); AcceptEchoListener(listener); clientWebSocket = new ClientWebSocket(); clientWebSocket.Options.SetRequestHeader(TestAcceptHandlerResultHeader, true.ToString()); clientWebSocket.Options.SetRequestHeader(TestAcceptHandlerSetResponseHeader, "X-CustomHeader: Test value"); await clientWebSocket.ConnectAsync(wssUri, cancelSource.Token); await clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test closing web socket", cancelSource.Token); TestUtility.Log("Testing HybridConnectionListener.AcceptHandler rejecting with only returning false"); AcceptEchoListener(listener); HttpStatusCode expectedStatusCode = HttpStatusCode.BadRequest; string expectedStatusDescription = "Rejected by user code"; clientWebSocket = new ClientWebSocket(); clientWebSocket.Options.SetRequestHeader(TestAcceptHandlerResultHeader, false.ToString()); var webSocketException = await Assert.ThrowsAsync <WebSocketException>(() => clientWebSocket.ConnectAsync(wssUri, cancelSource.Token)); VerifyHttpStatusCodeAndDescription(webSocketException, expectedStatusCode, expectedStatusDescription); TestUtility.Log("Testing HybridConnectionListener.AcceptHandler rejecting with setting status code and returning false"); AcceptEchoListener(listener); expectedStatusCode = HttpStatusCode.Unauthorized; expectedStatusDescription = "Unauthorized"; clientWebSocket = new ClientWebSocket(); clientWebSocket.Options.SetRequestHeader(TestAcceptHandlerResultHeader, false.ToString()); clientWebSocket.Options.SetRequestHeader(TestAcceptHandlerStatusCodeHeader, ((int)expectedStatusCode).ToString()); webSocketException = await Assert.ThrowsAsync <WebSocketException>(() => clientWebSocket.ConnectAsync(wssUri, cancelSource.Token)); VerifyHttpStatusCodeAndDescription(webSocketException, expectedStatusCode, expectedStatusDescription); TestUtility.Log("Testing HybridConnectionListener.AcceptHandler rejecting with setting status code+description and returning false"); AcceptEchoListener(listener); expectedStatusCode = HttpStatusCode.Unauthorized; expectedStatusDescription = "Status Description from test"; clientWebSocket = new ClientWebSocket(); clientWebSocket.Options.SetRequestHeader(TestAcceptHandlerResultHeader, false.ToString()); clientWebSocket.Options.SetRequestHeader(TestAcceptHandlerStatusCodeHeader, ((int)expectedStatusCode).ToString()); clientWebSocket.Options.SetRequestHeader(TestAcceptHandlerStatusDescriptionHeader, expectedStatusDescription); webSocketException = await Assert.ThrowsAsync <WebSocketException>(() => clientWebSocket.ConnectAsync(wssUri, cancelSource.Token)); VerifyHttpStatusCodeAndDescription(webSocketException, expectedStatusCode, expectedStatusDescription); TestUtility.Log("Testing HybridConnectionListener.AcceptHandler with a custom handler that returns null instead of a valid Task<bool>"); listener.AcceptHandler = context => null; AcceptEchoListener(listener); expectedStatusCode = HttpStatusCode.BadGateway; expectedStatusDescription = "The Listener's custom AcceptHandler threw an exception. See Listener logs for details."; clientWebSocket = new ClientWebSocket(); webSocketException = await Assert.ThrowsAsync <WebSocketException>(() => clientWebSocket.ConnectAsync(wssUri, cancelSource.Token)); VerifyHttpStatusCodeAndDescription(webSocketException, expectedStatusCode, expectedStatusDescription, false); } } finally { await SafeCloseAsync(listener); } }