/// <summary> /// Handles ClientRequestServer messages. /// It will try to find a matching service (spawn it if not started yet), and ask it to establish a new "server" connection back to us. /// </summary> /// <param name="clientSocket">The client socket context.</param> /// <returns></returns> private async Task HandleMessageClientRequestServer(SimpleSocket clientSocket) { // Check for an existing server // TODO: Proper Url parsing (query string) var url = await clientSocket.ReadStream.ReadStringAsync(); string[] urlSegments; string urlParameters; RouterHelper.ParseUrl(url, out urlSegments, out urlParameters); if (urlSegments.Length == 0) { throw new InvalidOperationException("No URL Segments"); } SimpleSocket serverSocket = null; ExceptionDispatchInfo serverSocketCapturedException = null; try { // For now, we handle only "service" URL switch (urlSegments[0]) { case "service": { // From the URL, start service (if not started yet) and ask it to provide a server serverSocket = await SpawnServerFromService(url, false); break; } case "task": // From the URL, start service (if not started yet) and ask it to provide a server serverSocket = await SpawnServerFromService(url, true); break; default: throw new InvalidOperationException("This type of URL is not supported"); } } catch (Exception e) { serverSocketCapturedException = ExceptionDispatchInfo.Capture(e); } if (serverSocketCapturedException != null) { try { // Notify client that there was an error await clientSocket.WriteStream.WriteInt16Async((short)RouterMessage.ClientServerStarted); await clientSocket.WriteStream.WriteInt32Async(1); // error code Failure await clientSocket.WriteStream.WriteStringAsync(serverSocketCapturedException.SourceException.Message); await clientSocket.WriteStream.FlushAsync(); } finally { serverSocketCapturedException.Throw(); } } try { // Notify client that we've found a server for it await clientSocket.WriteStream.WriteInt16Async((short)RouterMessage.ClientServerStarted); await clientSocket.WriteStream.WriteInt32Async(0); // error code OK await clientSocket.WriteStream.FlushAsync(); // Let's forward clientSocketContext and serverSocketContext await await Task.WhenAny( ForwardSocket(clientSocket, serverSocket), ForwardSocket(serverSocket, clientSocket)); } catch { serverSocket.Dispose(); throw; } }
private async Task <SimpleSocket> SpawnServerFromService(string url, bool task) { // Ideally we would like to reuse Uri (or some other similar code), but it doesn't work without a Host var parameterIndex = url.IndexOf('?'); var urlWithoutParameters = parameterIndex != -1 ? url.Substring(0, parameterIndex) : url; string[] urlSegments; string urlParameters; RouterHelper.ParseUrl(url, out urlSegments, out urlParameters); // Find a matching server TaskCompletionSource <Service> serviceTcs; lock (registeredServices) { if (!registeredServices.TryGetValue(urlWithoutParameters, out serviceTcs)) { if (task) { throw new Exception("ConnectionRouter task not found, a task won't spawn a new service on demand instead must be started explicitly."); } serviceTcs = new TaskCompletionSource <Service>(); registeredServices.Add(urlWithoutParameters, serviceTcs); } if (!serviceTcs.Task.IsCompleted) { if (urlSegments.Length < 3) { Log.Error("{0} action URL {1} is invalid", RouterMessage.ClientRequestServer, url); throw new InvalidOperationException(); } var xenkoVersion = urlSegments[1]; var serviceExe = urlSegments[2]; var xenkoSdkDir = RouterHelper.FindXenkoSdkDir(xenkoVersion); if (xenkoSdkDir == null) { Log.Error("{0} action URL {1} references a Xenko version which is not installed", RouterMessage.ClientRequestServer, url); throw new InvalidOperationException(); } var servicePath = Path.Combine(xenkoSdkDir, @"Bin\Windows-Direct3D11", serviceExe); RunServiceProcessAndLog(servicePath); } } var service = await serviceTcs.Task; // Generate connection Guid var guid = Guid.NewGuid(); var serverSocketTcs = new TaskCompletionSource <SimpleSocket>(); lock (pendingServers) { pendingServers.Add(guid, serverSocketTcs); } await service.SendLock.WaitAsync(); try { // Notify service that we want it to establish back a new connection to us for this client await service.Socket.WriteStream.WriteInt16Async((short)RouterMessage.ServiceRequestServer); await service.Socket.WriteStream.WriteStringAsync(url); await service.Socket.WriteStream.WriteGuidAsync(guid); await service.Socket.WriteStream.FlushAsync(); } finally { service.SendLock.Release(); } // Should answer within 2 sec var ct = new CancellationTokenSource(2000); ct.Token.Register(() => serverSocketTcs.TrySetException(new TimeoutException("Server could not connect back in time"))); // Wait for such a server to be available return(await serverSocketTcs.Task); }
/// <summary> /// Handles ClientRequestServer messages. /// It will try to find a matching service (spawn it if not started yet), and ask it to establish a new "server" connection back to us. /// </summary> /// <param name="clientSocket">The client socket context.</param> /// <returns></returns> private async Task HandleMessageClientRequestServer(SimpleSocket clientSocket) { // Check for an existing server // TODO: Proper Url parsing (query string) var url = await clientSocket.ReadStream.ReadStringAsync(); Log.Info("Client {0}:{1} sent message ClientRequestServer with URL {2}", clientSocket.RemoteAddress, clientSocket.RemotePort, url); string[] urlSegments; string urlParameters; RouterHelper.ParseUrl(url, out urlSegments, out urlParameters); if (urlSegments.Length == 0) { throw new InvalidOperationException("No URL Segments"); } SimpleSocket serverSocket = null; ExceptionDispatchInfo serverSocketCapturedException = null; try { // For now, we handle only "service" URL switch (urlSegments[0]) { case "service": { // From the URL, start service (if not started yet) and ask it to provide a server serverSocket = await SpawnServerFromService(url, false); break; } case "task": { // From the URL, start service (if not started yet) and ask it to provide a server serverSocket = await SpawnServerFromService(url, true); } break; case "redirect": { // Redirect to a IP/port serverSocket = new SimpleSocket(); var host = urlSegments[1]; var port = int.Parse(urlSegments[2]); // Note: for security reasons, we currently use a whitelist if (host == "XenkoBuild.siliconstudio.co.jp" && port == 1832) { await serverSocket.StartClient(host, port, false); } else { throw new InvalidOperationException("Trying to redirect to a non-whitelisted host/port"); } break; } default: throw new InvalidOperationException("This type of URL is not supported"); } } catch (Exception e) { serverSocketCapturedException = ExceptionDispatchInfo.Capture(e); } if (serverSocketCapturedException != null) { try { // Notify client that there was an error await clientSocket.WriteStream.WriteInt16Async((short)RouterMessage.ClientServerStarted); await clientSocket.WriteStream.WriteInt32Async(1); // error code Failure await clientSocket.WriteStream.WriteStringAsync(serverSocketCapturedException.SourceException.Message); await clientSocket.WriteStream.FlushAsync(); } finally { serverSocketCapturedException.Throw(); } } try { // Notify client that we've found a server for it await clientSocket.WriteStream.WriteInt16Async((short)RouterMessage.ClientServerStarted); await clientSocket.WriteStream.WriteInt32Async(0); // error code OK await clientSocket.WriteStream.FlushAsync(); // Let's forward clientSocketContext and serverSocketContext await await Task.WhenAny( ForwardSocket(clientSocket, serverSocket), ForwardSocket(serverSocket, clientSocket)); } catch { serverSocket.Dispose(); throw; } }