private async Task <ConnectionData> HandleShutdownRequest(CancellationToken cancellationToken) { var id = Process.GetCurrentProcess().Id; var response = new ShutdownBuildResponse(id); await response.WriteAsync(_stream, cancellationToken).ConfigureAwait(false); return(new ConnectionData(CompletionReason.ClientShutdownRequest)); }
internal async Task <CompletionData> ProcessAsync( Task <IClientConnection> clientConnectionTask, bool allowCompilationRequests = true, CancellationToken cancellationToken = default) { try { return(await ProcessCore().ConfigureAwait(false)); } catch (Exception ex) { CompilerServerLogger.LogException(ex, $"Error processing request for client"); return(CompletionData.RequestError); } async Task <CompletionData> ProcessCore() { using var clientConnection = await clientConnectionTask.ConfigureAwait(false); var request = await clientConnection.ReadBuildRequestAsync(cancellationToken).ConfigureAwait(false); if (request.ProtocolVersion != BuildProtocolConstants.ProtocolVersion) { var response = new MismatchedVersionBuildResponse(); await clientConnection.WriteBuildResponseAsync(response, cancellationToken).ConfigureAwait(false); return(CompletionData.RequestCompleted); } if (!string.Equals(request.CompilerHash, BuildProtocolConstants.GetCommitHash(), StringComparison.OrdinalIgnoreCase)) { var response = new IncorrectHashBuildResponse(); await clientConnection.WriteBuildResponseAsync(response, cancellationToken).ConfigureAwait(false); return(CompletionData.RequestCompleted); } if (request.Arguments.Count == 1 && request.Arguments[0].ArgumentId == BuildProtocolConstants.ArgumentId.Shutdown) { var id = Process.GetCurrentProcess().Id; var response = new ShutdownBuildResponse(id); await clientConnection.WriteBuildResponseAsync(response, cancellationToken).ConfigureAwait(false); return(new CompletionData(CompletionReason.RequestCompleted, shutdownRequested: true)); } if (!allowCompilationRequests) { var response = new RejectedBuildResponse("Compilation not allowed at this time"); await clientConnection.WriteBuildResponseAsync(response, cancellationToken).ConfigureAwait(false); return(CompletionData.RequestCompleted); } return(await ProcessCompilationRequestAsync(clientConnection, request, cancellationToken).ConfigureAwait(false)); } }
public async Task ShutdownResponseWriteRead() { var response = new ShutdownBuildResponse(42); Assert.Equal(BuildResponse.ResponseType.Shutdown, response.Type); var memoryStream = new MemoryStream(); await response.WriteAsync(memoryStream, CancellationToken.None); memoryStream.Position = 0; var read = await BuildResponse.ReadAsync(memoryStream, CancellationToken.None); Assert.Equal(BuildResponse.ResponseType.Shutdown, read.Type); var typed = (ShutdownBuildResponse)read; Assert.Equal(42, typed.ServerProcessId); }
internal async Task <ConnectionResult> AcceptConnection(Task <Connection> task, bool accept, CancellationToken cancellationToken) { Connection connection; try { connection = await task; } catch (Exception ex) { // Unable to establish a connection with the client. The client is responsible for // handling this case. Nothing else for us to do here. CompilerServerLogger.LogException(ex, "Error creating client named pipe"); return(new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted)); } try { using (connection) { BuildRequest request; try { CompilerServerLogger.Log("Begin reading request."); request = await BuildRequest.ReadAsync(connection.Stream, cancellationToken).ConfigureAwait(false); CompilerServerLogger.Log("End reading request."); } catch (Exception e) { CompilerServerLogger.LogException(e, "Error reading build request."); return(new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted)); } if (request.IsShutdownRequest()) { // Reply with the PID of this process so that the client can wait for it to exit. var response = new ShutdownBuildResponse(Process.GetCurrentProcess().Id); await response.WriteAsync(connection.Stream, cancellationToken); // We can safely disconnect the client, then when this connection gets cleaned up by the event loop // the server will go to a shutdown state. return(new ConnectionResult(ConnectionResult.Reason.ClientShutdownRequest)); } else if (!accept) { // We're already in shutdown mode, respond gracefully so the client can run in-process. var response = new RejectedBuildResponse(); await response.WriteAsync(connection.Stream, cancellationToken).ConfigureAwait(false); return(new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted)); } else { // If we get here then this is a real request that we will accept and process. // // Kick off both the compilation and a task to monitor the pipe for closing. var buildCancelled = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var watcher = connection.WaitForDisconnectAsync(buildCancelled.Token); var worker = ExecuteRequestAsync(request, buildCancelled.Token); // await will end when either the work is complete or the connection is closed. await Task.WhenAny(worker, watcher); // Do an 'await' on the completed task, preference being compilation, to force // any exceptions to be realized in this method for logging. ConnectionResult.Reason reason; if (worker.IsCompleted) { var response = await worker; try { CompilerServerLogger.Log("Begin writing response."); await response.WriteAsync(connection.Stream, cancellationToken); CompilerServerLogger.Log("End writing response."); reason = ConnectionResult.Reason.CompilationCompleted; } catch { reason = ConnectionResult.Reason.ClientDisconnect; } } else { await watcher; reason = ConnectionResult.Reason.ClientDisconnect; } // Begin the tear down of the Task which didn't complete. buildCancelled.Cancel(); return(new ConnectionResult(reason, request.KeepAlive)); } } } catch (Exception ex) { CompilerServerLogger.LogException(ex, "Error handling connection"); return(new ConnectionResult(ConnectionResult.Reason.ClientException)); } }