private async Task <SimpleSocket> SpawnServerFromService(string url) { // 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 <SimpleSocket> serviceTCS; lock (registeredServices) { if (!registeredServices.TryGetValue(urlWithoutParameters, out serviceTCS)) { serviceTCS = new TaskCompletionSource <SimpleSocket>(); 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 paradoxVersion = urlSegments[1]; var serviceExe = urlSegments[2]; var paradoxSdkDir = RouterHelper.FindParadoxSdkDir(paradoxVersion); if (paradoxSdkDir == null) { Log.Error("{0} action URL {1} references a Paradox version which is not installed", RouterMessage.ClientRequestServer, url); throw new InvalidOperationException(); } var servicePath = Path.Combine(paradoxSdkDir, @"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); } // Notify service that we want it to establish back a new connection to us for this client await service.WriteStream.WriteInt16Async((short)RouterMessage.ServiceRequestServer); await service.WriteStream.WriteStringAsync(url); await service.WriteStream.WriteGuidAsync(guid); await service.WriteStream.FlushAsync(); // 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(); 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); 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; } }