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); } }
public ServiceConnectionForwarder( string serviceNamespace, string ruleName, string ruleKey, string targetHost, string targetHostAlias, string allowedPortsString) { this.targetHost = targetHost; noPortConstraints = false; allowedPorts = new List <int>(); allowedPortsString = allowedPortsString.Trim(); if (allowedPortsString == "*") { noPortConstraints = true; } else { noPortConstraints = false; string[] portList = allowedPortsString.Split(','); for (int i = 0; i < portList.Length; i++) { allowedPorts.Add(int.Parse(portList[i].Trim())); } } endpointVia = new UriBuilder("sb", serviceNamespace, -1, targetHostAlias).Uri; tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(ruleName, ruleKey); relayListener = new HybridConnectionListener(endpointVia, tokenProvider); relayListener.Online += (s, e) => Trace.TraceInformation("{0} Online", relayListener); relayListener.Connecting += (s, e) => Trace.TraceWarning("{0} Re-connecting! {1}: {2}", relayListener, relayListener.LastError.GetType(), relayListener.LastError.Message); relayListener.Offline += (s, e) => Trace.TraceError("{0} Offline! {1}: {2}", relayListener, relayListener.LastError.GetType(), relayListener.LastError.Message); }
static async void RunAcceptPump(HybridConnectionListener listener) { while (true) { try { HybridConnectionStream stream = await listener.AcceptConnectionAsync(); if (stream == null) { return; } string connectionName = $"L:HybridConnectionStream({stream.TrackingContext.TrackingId})"; RelayTraceSource.TraceInfo($"{connectionName} accepted"); RunConnectionPump(stream, connectionName, echoBytes: true); } catch (Exception exception) { RelayTraceSource.TraceException(exception, nameof(RunAcceptPump)); await Task.Delay(TimeSpan.FromMilliseconds(100)); } } }
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); } }
public RelayEpoxyListener( RelayEpoxyTransport parentTransport, string listenEndpoint, TokenProvider tokenProvider, Logger logger, Metrics metrics) : base(logger, metrics) { Debug.Assert(parentTransport != null); Debug.Assert(listenEndpoint != null); this.parentTransport = parentTransport; // will be null if not using TLS this.tokenProvider = tokenProvider; var endpoint = new Uri(listenEndpoint); listener = new HybridConnectionListener(endpoint, tokenProvider); serviceHost = new ServiceHost(logger); connections = new HashSet <RelayEpoxyConnection>(); shutdownTokenSource = new CancellationTokenSource(); ListenEndpoint = endpoint; }
static async Task RunAsync(string[] args) { var cts = new CancellationTokenSource(); if (args.Length < 4) { Console.WriteLine("server [ns] [hc] [keyname] [key]"); return; } var ns = args[0]; var hc = args[1]; var keyname = args[2]; var key = args[3]; // Create a new hybrid connection listener to listen for // incoming connections. var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(keyname, key); var listener = new HybridConnectionListener(new Uri(String.Format("sb://{0}/{1}", ns, hc)), tokenProvider); // Subscribe to the status events listener.Connecting += (o, e) => { Console.WriteLine("Connecting"); }; listener.Offline += (o, e) => { Console.WriteLine("Offline"); }; listener.Online += (o, e) => { Console.WriteLine("Online"); }; // Opening the listener will establish the control channel to // the Azure Relay service. The control channel will be continuously // maintained and reestablished when connectivity is disrupted. await listener.OpenAsync(cts.Token); Console.WriteLine("Server listening"); // trigger cancellation when the user presses enter. Not awaited. #pragma warning disable CS4014 cts.Token.Register(() => listener.CloseAsync(CancellationToken.None)); Task.Run(() => Console.In.ReadLineAsync().ContinueWith((s) => { cts.Cancel(); })); #pragma warning restore CS4014 do { // Accept the next available, pending connection request. // Shutting down the listener will allow a clean exit with // this method returning null var relayConnection = await listener.AcceptConnectionAsync(); if (relayConnection == null) { break; } // The following task processes a new session. We turn off the // warning here since we intentially don't 'await' // this call, but rather let the task handdling the connection // run out on its own without holding for it #pragma warning disable CS4014 Task.Run(async() => { Console.WriteLine("New session"); // The connection is a fully bidrectional stream. // We put a stream reader and a stream writer over it // that allows us to read UTF-8 text data that comes from // the sender and to write text replies back. var reader = new StreamReader(relayConnection); var writer = new StreamWriter(relayConnection) { AutoFlush = true }; do { // Read a line of input until a newline is encountered string line = await reader.ReadLineAsync(); if (String.IsNullOrEmpty(line)) { // If there's no input data, we will signal that // we will no longer send data on this connection // and then break out of the processing loop. await relayConnection.ShutdownAsync(cts.Token); break; } // Output the line on the console Console.WriteLine(line); // Write the line back to the client, prepending "Echo:" await writer.WriteLineAsync("Echo: " + line); }while (!cts.IsCancellationRequested); Console.WriteLine("End session"); // closing the connection from this end await relayConnection.CloseAsync(cts.Token); }); #pragma warning restore CS4014 }while (true); // close the listener after we exit the processing loop await listener.CloseAsync(cts.Token); }
private ServiceForwarder(ILogger logger, MetricsRegistry registry, int forwarderIdx, HybridConnectionListener listener, RelayMetadata metadata, string entityPath) { _log = logger.ForContext(GetType()); _forwarderIdx = forwarderIdx; _relayListener = listener; _metadata = metadata; var tags = new MetricTags(nameof(entityPath), entityPath); var metrics = new TunnelMetrics(registry, tags); _tunnelFactory = new RelayTunnelFactory(logger, metrics); }
public async Task StartAsync(CancellationToken cancellationToken) { configuration.InstrumentationKey = "ed24685e-d783-4306-bdff-be8887d21cd7"; configuration.TelemetryInitializers.Add(new HttpDependenciesParsingTelemetryInitializer()); var telemetryClient = new TelemetryClient(configuration); telemetryClient.TrackTrace("Hello World!"); logger.LogInformation("HERE"); var module = new DependencyTrackingTelemetryModule(); // prevent Correlation Id to be sent to certain endpoints. You may add other domains as needed. module.ExcludeComponentCorrelationHttpHeadersOnDomains.Add("core.windows.net"); //... // enable known dependency tracking, note that in future versions, we will extend this list. // please check default settings in https://github.com/Microsoft/ApplicationInsights-dotnet-server/blob/develop/Src/DependencyCollector/DependencyCollector/ApplicationInsights.config.install.xdt module.IncludeDiagnosticSourceActivities.Add("Microsoft.Azure.ServiceBus"); module.IncludeDiagnosticSourceActivities.Add("Microsoft.Azure.EventHubs"); //.... // initialize the module module.Initialize(configuration); var cts = new CancellationTokenSource(); var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(KeyName, Key); listener = new HybridConnectionListener(new Uri(string.Format("sb://{0}/{1}", RelayNamespace, ConnectionName)), tokenProvider); // Subscribe to the status events. listener.Connecting += (o, e) => { logger.LogInformation("Connecting"); }; listener.Offline += (o, e) => { logger.LogInformation("Offline"); }; listener.Online += (o, e) => { logger.LogInformation("Online"); }; using (InitializeDependencyTracking(configuration)) { // Provide an HTTP request handler listener.RequestHandler = (context) => { telemetryClient.TrackTrace("Hello World!"); // Do something with context.Request.Url, HttpMethod, Headers, InputStream... context.Response.StatusCode = HttpStatusCode.OK; context.Response.StatusDescription = "OK, This is pretty neat"; using (var sw = new StreamWriter(context.Response.OutputStream)) { sw.WriteLine("hello! I have come from the server"); } logger.LogInformation("Server Responded"); logger.LogInformation(context.TrackingContext.Address); // The context MUST be closed here context.Response.Close(); using (var httpClient = new HttpClient()) { // Http dependency is automatically tracked! httpClient.GetAsync("https://google.com").Wait(); } // before exit, flush the remaining data telemetryClient.Flush(); // flush is not blocking so wait a bit Task.Delay(5000).Wait(); }; } // Opening the listener establishes the control channel to // the Azure Relay service. The control channel is continuously // maintained, and is reestablished when connectivity is disrupted. await listener.OpenAsync(); logger.LogInformation("Server listening"); }
public static async Task <int> VerifyListenAsync(TraceSource traceSource, RelayConnectionStringBuilder connectionString, string responseBody, int responseChunkLength, HttpStatusCode statusCode, string statusDescription) { var namespaceManager = new RelayNamespaceManager(connectionString.ToString()); bool createdHybridConnection = await EnsureHybridConnectionExists(traceSource, connectionString, namespaceManager); HybridConnectionListener listener = null; try { listener = new HybridConnectionListener(connectionString.ToString()); listener.Connecting += (s, e) => RelayTraceSource.TraceException(listener.LastError, TraceEventType.Warning, "HybridConnectionListener Re-Connecting"); listener.Online += (s, e) => RelayTraceSource.Instance.TraceEvent(TraceEventType.Information, (int)ConsoleColor.Green, "HybridConnectionListener is online"); EventHandler offlineHandler = (s, e) => RelayTraceSource.TraceException(listener.LastError, "HybridConnectionListener is OFFLINE"); listener.Offline += offlineHandler; var responseBytes = Encoding.UTF8.GetBytes(responseBody); listener.RequestHandler = async(context) => { try { var stopwatch = new Stopwatch(); LogHttpRequest(context, traceSource); context.Response.StatusCode = statusCode; if (statusDescription != null) { context.Response.StatusDescription = statusDescription; } context.Response.Headers[HttpResponseHeader.ContentType] = "text/html"; int bytesWritten = 0; do { int countToWrite = Math.Min(responseBody.Length - bytesWritten, responseChunkLength); stopwatch.Restart(); await context.Response.OutputStream.WriteAsync(responseBytes, bytesWritten, countToWrite); bytesWritten += countToWrite; traceSource.TraceEvent(TraceEventType.Verbose, 0, "Sent {0} bytes in {1} ms", countToWrite, stopwatch.ElapsedMilliseconds); }while (bytesWritten < responseBody.Length); stopwatch.Restart(); await context.Response.CloseAsync(); traceSource.TraceEvent(TraceEventType.Verbose, 0, "Close completed in {0} ms", stopwatch.ElapsedMilliseconds); } catch (Exception exception) { RelayTraceSource.TraceException(exception, $"RequestHandler Error"); } }; traceSource.TraceInformation($"Opening {listener}"); await listener.OpenAsync(); RunAcceptPump(listener); traceSource.TraceInformation("Press <ENTER> to close the listener "); Console.ReadLine(); traceSource.TraceInformation($"Closing {listener}"); listener.Offline -= offlineHandler; // Avoid a spurious trace on expected shutdown. await listener.CloseAsync(); traceSource.TraceInformation("Closed"); return(0); } catch (Exception) { listener?.CloseAsync(); throw; } finally { if (createdHybridConnection) { try { traceSource.TraceEvent(TraceEventType.Information, (int)ConsoleColor.White, $"Deleting HybridConnection '{connectionString.EntityPath}'"); await namespaceManager.DeleteHybridConnectionAsync(connectionString.EntityPath); traceSource.TraceInformation("Deleted"); } catch (Exception exception) { RelayTraceSource.TraceException(exception, "Deleting HybridConnection"); } } } }
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); } }
public ListenerInfo(HybridConnectionListener listener, int index) { this.Listener = listener; this.Index = index; }
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 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 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)); } }
public HybridConnectionListenerServerTransport(HybridConnectionListener listener) { this.listener = 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); } }
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); } }
/// <summary> /// Start accepting incoming requests. /// </summary> public void Start() { CheckDisposed(); LogHelper.LogInfo(Logger, "Start"); // Make sure there are no race conditions between Start/Stop/Abort/Close/Dispose. // Start needs to setup all resources. Abort/Stop must not interfere while Start is // allocating those resources. lock (_internalLock) { try { CheckDisposed(); if (_state == State.Started) { return; } try { foreach (var urlPrefix in Options.UrlPrefixes) { RelayConnectionStringBuilder rcb = new RelayConnectionStringBuilder(); var relayListener = new HybridConnectionListener( new UriBuilder(urlPrefix.FullPrefix) { Scheme = "sb", Port = -1 }.Uri, urlPrefix.TokenProvider != null ? urlPrefix.TokenProvider : Options.TokenProvider); relayListener.RequestHandler = (ctx) => requestHandler(new RequestContext(ctx, new Uri(urlPrefix.FullPrefix))); relayListener.AcceptHandler = WebSocketAcceptHandler; _relayListeners.Add(relayListener); } } catch (Exception exception) { LogHelper.LogException(Logger, ".Ctor", exception); throw; } foreach (var listener in _relayListeners) { listener.OpenAsync().GetAwaiter().GetResult(); //listener.AcceptConnectionAsync().ContinueWith((t) => { // Console.WriteLine(t); //}); } _state = State.Started; } catch (Exception exception) { // Make sure the HttpListener instance can't be used if Start() failed. _state = State.Disposed; LogHelper.LogException(Logger, "Start", exception); throw; } } }
/// <summary> /// Start accepting incoming requests. /// </summary> public void Start() { CheckDisposed(); LogHelper.LogInfo(Logger, nameof(Start)); // Make sure there are no race conditions between Start/Stop/Abort/Close/Dispose. // Start needs to setup all resources. Abort/Stop must not interfere while Start is // allocating those resources. lock (_internalLock) { try { CheckDisposed(); if (_state == State.Started) { return; } try { foreach (var urlPrefix in Options.UrlPrefixes) { var rcb = new RelayConnectionStringBuilder(); var tokenProvider = urlPrefix.TokenProvider != null ? urlPrefix.TokenProvider : Options.TokenProvider; if (tokenProvider == null) { throw new InvalidOperationException("No relay token provider defined."); } var relayListener = new HybridConnectionListener( new UriBuilder(urlPrefix.FullPrefix) { Scheme = "sb", Port = -1 }.Uri, tokenProvider); relayListener.RequestHandler = (ctx) => requestHandler(new RequestContext(ctx, new Uri(urlPrefix.FullPrefix))); // TODO: CR: An accept handler which simply returns true is the same as no handler at all. // Would returning false and rejecting relayed connection requests be better? relayListener.AcceptHandler = WebSocketAcceptHandler; _relayListeners.Add(relayListener); } } catch (Exception exception) { LogHelper.LogException(Logger, ".Ctor", exception); throw; } foreach (var listener in _relayListeners) { listener.OpenAsync().GetAwaiter().GetResult(); } _state = State.Started; } catch (Exception exception) { // Make sure the HttpListener instance can't be used if Start() failed. _state = State.Disposed; LogHelper.LogException(Logger, nameof(Start), exception); throw; } } }
public ServiceConnectionForwarder( string serviceNamespace, string ruleName, string ruleKey, string targetHost, string targetHostAlias, string allowedPortsString, string allowedPipesString) { this.targetHost = targetHost; noPipeConstraints = false; noPortConstraints = false; allowedPipes = new List <string>(); allowedPorts = new List <int>(); allowedPortsString = allowedPortsString.Trim(); if (allowedPortsString == "*") { noPortConstraints = true; } else { noPortConstraints = false; string[] portList = allowedPortsString.Split(','); for (int i = 0; i < portList.Length; i++) { allowedPorts.Add(int.Parse(portList[i].Trim())); } } allowedPipesString = allowedPipesString.Trim(); if (allowedPipesString == "*") { noPipeConstraints = true; } else { noPipeConstraints = false; string[] pipeList = allowedPipesString.Split(','); for (int i = 0; i < pipeList.Length; i++) { string pipeName = pipeList[i].Trim(); if (pipeName.StartsWith("\\", StringComparison.OrdinalIgnoreCase)) { if (!pipeName.StartsWith(localPipePrefix, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException( string.Format("Invalid pipe name in allowedPipesString. Only relative and local paths permitted: {0}", pipeName), "allowedPipesString"); } pipeName = pipeName.Substring(localPipePrefix.Length); } allowedPipes.Add(pipeName); } } endpointVia = new UriBuilder("sb", serviceNamespace, -1, targetHostAlias).Uri; tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(ruleName, ruleKey); relayListener = new HybridConnectionListener(endpointVia, tokenProvider); }
private HybridConnectionListener CreateHybridListener() { //var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(connectionStringBuilder.SharedAccessKeyName, connectionStringBuilder.SharedAccessKey); //var uri = new Uri(string.Format("https://{0}/{1}", connectionStringBuilder.Endpoint.Host, hybridConnectionName)); listener = new HybridConnectionListener(connectionItems.ToString()); // Subscribe to the status events. listener.Connecting += (o, e) => { System.Diagnostics.Debug.WriteLine("Connecting"); }; listener.Offline += (o, e) => { System.Diagnostics.Debug.WriteLine("Offline"); }; listener.Online += (o, e) => { System.Diagnostics.Debug.WriteLine("Online"); }; // Provide an HTTP request handler listener.RequestHandler = (context) => { //TODO if string then handle response IActorMessage msg = null; if (typeof(MsgType) == typeof(string)) { var reader = new StreamReader(context.Request.InputStream); string msgString = reader.ReadToEnd(); msg = msgString.ToActorMessage(); } else if (typeof(MsgType) == typeof(byte[])) { var reader = new BinaryReader(context.Request.InputStream); byte[] msgBytes = reader.ReadBytes(1024); msg = msgBytes.ToActorMessage(); } else { byte[] msgBytes = new byte[context.Request.InputStream.Length]; var reader = new BinaryReader(context.Request.InputStream); reader.Read(msgBytes, 0, msgBytes.Length); msg = Telegraph.Instance.Ask(new DeserializeMessage <IActorMessage>(msgBytes)).Result; } if (null == msg.Status) { msg.Status = new TaskCompletionSource <IActorMessage>(); } // Store the message in the global queue, // call Telegraphy.Net.TPLExtentions Task.Then to send the result back to the relay // after we are done processing it msg.Status.Task.Then(() => { // Do something with context.Request.Url, HttpMethod, Headers, InputStream... context.Response.StatusCode = HttpStatusCode.OK; context.Response.StatusDescription = "OK"; // The context MUST be closed here context.Response.Close(); //using (var sw = new StreamWriter(context.Response.OutputStream)) { var relay = new RecieveResponseFromRequest <MsgType, MsgType>(connectionItems.ToString()); relay.Tell(msg.Status.Task.Result); // send the answer back to the calling application. //sw.Write(responseMessage); } }); NextMessage = msg; }; listener.OpenAsync().Wait(); System.Diagnostics.Debug.WriteLine("Hybrid Connection Operator Listening"); return(listener); }
async Task StatusCodes() { var endpointTestType = EndpointTestType.Authenticated; 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 httpHandler = new HttpClientHandler { AllowAutoRedirect = false }; Uri hybridHttpUri = new UriBuilder("https://", endpointUri.Host, endpointUri.Port, connectionString.EntityPath).Uri; using (var client = new HttpClient(httpHandler) { BaseAddress = hybridHttpUri }) { client.DefaultRequestHeaders.ExpectContinue = false; HttpStatusCode[] expectedStatusCodes = new HttpStatusCode[] { HttpStatusCode.Accepted, HttpStatusCode.Ambiguous, HttpStatusCode.BadGateway, HttpStatusCode.BadRequest, HttpStatusCode.Conflict, /*HttpStatusCode.Continue,*/ HttpStatusCode.Created, HttpStatusCode.ExpectationFailed, HttpStatusCode.Forbidden, HttpStatusCode.GatewayTimeout, HttpStatusCode.Gone, HttpStatusCode.HttpVersionNotSupported, HttpStatusCode.InternalServerError, HttpStatusCode.LengthRequired, HttpStatusCode.MethodNotAllowed, HttpStatusCode.MovedPermanently, HttpStatusCode.MultipleChoices, HttpStatusCode.NoContent, HttpStatusCode.NonAuthoritativeInformation, HttpStatusCode.NotAcceptable, HttpStatusCode.NotFound, HttpStatusCode.NotImplemented, HttpStatusCode.NotModified, HttpStatusCode.PartialContent, HttpStatusCode.PaymentRequired, HttpStatusCode.PreconditionFailed, HttpStatusCode.ProxyAuthenticationRequired, HttpStatusCode.Redirect, HttpStatusCode.TemporaryRedirect, HttpStatusCode.RedirectMethod, HttpStatusCode.RequestedRangeNotSatisfiable, HttpStatusCode.RequestEntityTooLarge, HttpStatusCode.RequestTimeout, HttpStatusCode.RequestUriTooLong, HttpStatusCode.ResetContent, HttpStatusCode.ServiceUnavailable, /*HttpStatusCode.SwitchingProtocols,*/ HttpStatusCode.Unauthorized, HttpStatusCode.UnsupportedMediaType, HttpStatusCode.Unused, HttpStatusCode.UpgradeRequired, HttpStatusCode.UseProxy, (HttpStatusCode)418, (HttpStatusCode)450 }; foreach (HttpStatusCode expectedStatusCode in expectedStatusCodes) { TestUtility.Log($"Testing HTTP Status Code: {(int)expectedStatusCode} {expectedStatusCode}"); listener.RequestHandler = (context) => { TestUtility.Log($"RequestHandler: {context.Request.HttpMethod} {context.Request.Url}"); context.Response.StatusCode = expectedStatusCode; context.Response.Close(); }; var getRequest = new HttpRequestMessage(); getRequest.Method = HttpMethod.Get; await AddAuthorizationHeader(connectionString, getRequest, hybridHttpUri); LogRequestLine(getRequest, client); using (HttpResponseMessage response = await client.SendAsync(getRequest)) { TestUtility.Log($"Response: HTTP/{response.Version} {(int)response.StatusCode} {response.ReasonPhrase}"); Assert.Equal(expectedStatusCode, response.StatusCode); } var postRequest = new HttpRequestMessage(); postRequest.Method = HttpMethod.Post; await AddAuthorizationHeader(connectionString, postRequest, hybridHttpUri); string body = "{ \"a\": 11, \"b\" :22, \"c\":\"test\", \"d\":true}"; postRequest.Content = new StringContent(body); postRequest.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); LogRequestLine(postRequest, client); using (HttpResponseMessage response = await client.SendAsync(postRequest)) { TestUtility.Log($"Response: HTTP/{response.Version} {(int)response.StatusCode} {response.ReasonPhrase}"); Assert.Equal(expectedStatusCode, response.StatusCode); } } } await listener.CloseAsync(TimeSpan.FromSeconds(10)); } finally { await SafeCloseAsync(listener); } }
public static async Task <int> RunTestsAsync(RelayConnectionStringBuilder connectionString, TraceSource traceSource) { if (string.IsNullOrEmpty(connectionString.EntityPath)) { connectionString.EntityPath = HybridConnectionCommands.DefaultPath; } var namespaceManager = new RelayNamespaceManager(connectionString.ToString()); namespaceManager.Settings.OperationTimeout = TimeSpan.FromSeconds(5); HybridConnectionListener listener = null; bool createdHybridConnection = false; int returnCode = 0; var testResults = new List <TestResult>(); var cts = new CancellationTokenSource(TimeSpan.FromSeconds(70)); try { createdHybridConnection = await EnsureHybridConnectionExists(traceSource, connectionString, namespaceManager); listener = new HybridConnectionListener(connectionString.ToString()); traceSource.TraceInformation($"Opening {listener}"); await listener.OpenAsync(cts.Token); traceSource.TraceInformation("Listener Opened"); Uri hybridHttpUri = new Uri($"https://{connectionString.Endpoint.GetComponents(UriComponents.HostAndPort, UriFormat.SafeUnescaped)}/{connectionString.EntityPath}"); var token = await listener.TokenProvider.GetTokenAsync(hybridHttpUri.AbsoluteUri, TimeSpan.FromMinutes(20)); using (var client = new HttpClient { BaseAddress = hybridHttpUri }) { client.DefaultRequestHeaders.ExpectContinue = false; await RunTestAsync("TestPostLargeRequestSmallResponse", null, testResults, () => TestPostLargeRequestSmallResponse(listener, token, client, traceSource)); await RunTestAsync("TestPostLargeRequestWithLargeResponse", null, testResults, () => TestPostLargeRequestWithLargeResponse(listener, token, client, traceSource)); await RunTestAsync("TestGetLargeResponse", null, testResults, () => TestGetLargeResponse(listener, token, client, traceSource)); await RunTestAsync("TestGetSmallResponse", null, testResults, () => TestGetSmallResponse(listener, token, client, traceSource)); } await RunTestAsync("TestStreaming", null, testResults, () => TestStreaming(listener, connectionString, traceSource)); returnCode = ReportTestResults(testResults); } catch (Exception exception) { traceSource.TraceError("FAILURE WHILE RUNNING TESTS"); RelayTraceSource.TraceException(exception, nameof(HybridConnectionTests)); returnCode = exception.HResult; } finally { cts.Dispose(); var cleanupCancelSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); try { if (listener != null) { traceSource.TraceInformation($"Closing {listener}"); await listener.CloseAsync(cleanupCancelSource.Token); traceSource.TraceInformation("Listener Closed"); } if (createdHybridConnection) { traceSource.TraceEvent(TraceEventType.Information, (int)ConsoleColor.White, $"Deleting HybridConnection '{connectionString.EntityPath}'"); await namespaceManager.DeleteHybridConnectionAsync(connectionString.EntityPath); } } catch (Exception cleanupException) { traceSource.TraceWarning($"Error during cleanup: {cleanupException.GetType()}: {cleanupException.Message}"); } finally { cleanupCancelSource.Dispose(); } } return(returnCode); }
private void StartListener() { this.listener = CreateHybridListener(); }
RelayListenerInitializeAsync( string relayNamespace, string connectionName, string keyName, string key, TimeSpan?timeout = null, CancellationTokenSource cancellationTokenSource = null) { if (_isInitialized) { throw new RelayListenerException("Relay Listener can only be initialized once. Create a new instance if multiple listeners are needed."); } _isInitialized = true; // Set default timout to 10 seconds. var to = timeout ?? TimeSpan.FromSeconds(10); var cts = cancellationTokenSource ?? new CancellationTokenSource(); var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(keyName, key); var listener = new HybridConnectionListener(new Uri($"sb://{relayNamespace}/{connectionName}"), tokenProvider); // Subscribe to the connection status events. listener.Connecting += ConnectingHandler; listener.Offline += OfflineHandler; listener.Online += OnlineHandler; await listener.OpenAsync(cts.Token); _relayStateObserver.OnNext(RelayListenerConnectionState.Listening); var observableRelayMessages = Observable.Create <IMessage>(obs => { IDisposable disposableRelayMessages = null; var disposableConnections = Observable.While( () => !cts.IsCancellationRequested, Observable.FromAsync(() => listener.AcceptConnectionAsync())) .Subscribe(connection => { if (connection != null) { disposableRelayMessages = _observableRelayStringLine(connection, cts.Token, to) .Subscribe(obs.OnNext, obs.OnError); } }, ex => { _relayStateObserver.OnError(ex); obs.OnError(ex); }, () => { _relayStateObserver.OnCompleted(); obs.OnCompleted(); }); return(Disposable.Create(() => { disposableConnections?.Dispose(); disposableRelayMessages?.Dispose(); _relayStateObserver.OnNext(RelayListenerConnectionState.ExitingListener); try { listener.CloseAsync(CancellationToken.None).Wait(new CancellationTokenSource(to).Token); } catch (Exception ex) { _relayStateObserver.OnError(ex); } finally { cts?.Dispose(); } listener.Connecting -= ConnectingHandler; listener.Offline -= OfflineHandler; listener.Online -= OnlineHandler; _relayStateObserver.OnCompleted(); })); }).Publish().RefCount(); return(_relayStateObservable, observableRelayMessages); }
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); } }
static async Task Main(string[] args) { string hostAddress; string hybridConnectionName; string clientId = null; string tenantId = null; string clientSecret = null; RbacAuthenticationOption option; if (args.Length == 2) { option = RbacAuthenticationOption.ManagedIdentity; hostAddress = args[0]; hybridConnectionName = args[1]; } else if (args.Length == 3) { option = RbacAuthenticationOption.UserAssignedIdentity; hostAddress = args[0]; hybridConnectionName = args[1]; clientId = args[2]; } else if (args.Length == 5) { option = RbacAuthenticationOption.AAD; hostAddress = args[0]; hybridConnectionName = args[1]; clientId = args[2]; tenantId = args[3]; clientSecret = args[4]; } else { Console.WriteLine("Please run with parameters of the following format for the corresponding RBAC authentication method:"); Console.WriteLine("System Managed Identity: [HostAddress] [HybridConnectionName]"); Console.WriteLine("User Assigned Identity: [HostAddress] [HybridConnectionName] [ClientId]"); Console.WriteLine("Azure Active Directory: [HostAddress] [HybridConnectionName] [ClientId] [TenantId] [ClientSecret]"); Console.WriteLine("Press <Enter> to exit..."); Console.ReadLine(); return; } TokenProvider tokenProvider = null; switch (option) { case RbacAuthenticationOption.ManagedIdentity: tokenProvider = TokenProvider.CreateManagedIdentityTokenProvider(); break; case RbacAuthenticationOption.UserAssignedIdentity: var managedCredential = new ManagedIdentityCredential(clientId); tokenProvider = TokenProvider.CreateManagedIdentityTokenProvider(managedCredential); break; case RbacAuthenticationOption.AAD: tokenProvider = GetAadTokenProvider(clientId, tenantId, clientSecret); break; } var hybridConnectionUri = new Uri($"{hostAddress}/{hybridConnectionName}"); HybridConnectionListener listener = null; try { // The HybridConnection should be already created through Azure Portal or other means Console.WriteLine($"Creating the Relay listener instance with RBAC option: {option}"); listener = new HybridConnectionListener(hybridConnectionUri, tokenProvider); await listener.OpenAsync(TimeSpan.FromSeconds(10)); Console.WriteLine("Created and connected the Relay listener instance."); Console.WriteLine($"Creating the Relay sender instance with RBAC option: {option}"); var sender = new HybridConnectionClient(hybridConnectionUri, tokenProvider); var createSenderTask = sender.CreateConnectionAsync(); var listenerAcceptTask = listener.AcceptConnectionAsync(); using (HybridConnectionStream senderStream = await createSenderTask) using (HybridConnectionStream listenerStream = await listenerAcceptTask) { Console.WriteLine("Created and connected the Relay sender instance."); var senderCloseTask = senderStream.CloseAsync(CancellationToken.None); await listenerStream.CloseAsync(CancellationToken.None); await senderCloseTask; } // Configure a RequestHandler for HTTP request/response mode listener.RequestHandler = (context) => { context.Response.StatusCode = HttpStatusCode.OK; using (var sw = new StreamWriter(context.Response.OutputStream)) { sw.WriteLine("hello!"); } // The context must be closed to complete sending the response context.Response.Close(); }; Console.WriteLine($"Sending a HTTP request by setting the token in request header with RBAC option: {option}"); SecurityToken token = await tokenProvider.GetTokenAsync(hybridConnectionUri.AbsoluteUri, TimeSpan.FromMinutes(30)); var request = new HttpRequestMessage(); request.Headers.Add(HttpRequestHeader.Authorization.ToString(), token.TokenString); var requestUri = new UriBuilder(hybridConnectionUri) { Scheme = "https" }.Uri; using (HttpClient client = new HttpClient { BaseAddress = requestUri }) { using (var response = await client.SendAsync(request)) { Console.WriteLine($"Response status code: {response.StatusCode}. Response reason phrase: {response.ReasonPhrase}"); } } Console.WriteLine($"Sending a HTTP request by setting the token in query string with RBAC option: {option}"); token = await tokenProvider.GetTokenAsync(hybridConnectionUri.AbsoluteUri, TimeSpan.FromMinutes(30)); request = new HttpRequestMessage(); requestUri = new UriBuilder(requestUri) { Query = $"?sb-hc-token={token.TokenString}" }.Uri; using (HttpClient client = new HttpClient { BaseAddress = requestUri }) { using (var response = await client.SendAsync(request)) { Console.WriteLine($"Response status code: {response.StatusCode}. Response reason phrase: {response.ReasonPhrase}"); } } Console.WriteLine("Press <Enter> to exit..."); Console.ReadLine(); } finally { if (listener != null) { await listener.CloseAsync(); } } }