Exemple #1
0
        /// <summary>
        /// May throw exceptions if there are pipe problems.
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task <ServerResponse> ReadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
        {
            ServerLogger.Log("Reading response length");
            // Read the response length
            var lengthBuffer = new byte[4];
            await ServerProtocol.ReadAllAsync(stream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false);

            var length = BitConverter.ToUInt32(lengthBuffer, 0);

            // Read the response
            ServerLogger.Log("Reading response of length {0}", length);
            var responseBuffer = new byte[length];
            await ServerProtocol.ReadAllAsync(
                stream,
                responseBuffer,
                responseBuffer.Length,
                cancellationToken)
            .ConfigureAwait(false);

            using (var reader = new BinaryReader(new MemoryStream(responseBuffer), Encoding.Unicode))
            {
                var responseType = (ResponseType)reader.ReadInt32();

                switch (responseType)
                {
                case ResponseType.Completed:
                    return(CompletedServerResponse.Create(reader));

                case ResponseType.MismatchedVersion:
                    return(new MismatchedVersionServerResponse());

                case ResponseType.Shutdown:
                    return(ShutdownServerResponse.Create(reader));

                case ResponseType.Rejected:
                    return(new RejectedServerResponse());

                default:
                    throw new InvalidOperationException("Received invalid response type from server.");
                }
            }
        }
        public async Task ShutdownResponse_WriteRead_RoundtripsProperly()
        {
            // Arrange & Act 1
            var memoryStream = new MemoryStream();
            var response     = new ShutdownServerResponse(42);

            // Assert 1
            Assert.Equal(ServerResponse.ResponseType.Shutdown, response.Type);

            // Act 2
            await response.WriteAsync(memoryStream, CancellationToken.None);

            // Assert 2
            memoryStream.Position = 0;
            var read = await ServerResponse.ReadAsync(memoryStream, CancellationToken.None);

            Assert.Equal(ServerResponse.ResponseType.Shutdown, read.Type);
            var typed = (ShutdownServerResponse)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.
                ServerLogger.LogException(ex, "Error creating client named pipe");
                return(new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted));
            }

            try
            {
                using (connection)
                {
                    ServerRequest request;
                    try
                    {
                        ServerLogger.Log("Begin reading request.");
                        request = await ServerRequest.ReadAsync(connection.Stream, cancellationToken).ConfigureAwait(false);

                        ServerLogger.Log("End reading request.");
                    }
                    catch (Exception e)
                    {
                        ServerLogger.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 ShutdownServerResponse(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 RejectedServerResponse();
                        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
                            {
                                ServerLogger.Log("Begin writing response.");
                                await response.WriteAsync(connection.Stream, cancellationToken);

                                ServerLogger.Log("End writing response.");

                                reason = ConnectionResult.Reason.CompilationCompleted;

                                _eventBus.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)
            {
                ServerLogger.LogException(ex, "Error handling connection");
                return(new ConnectionResult(ConnectionResult.Reason.ClientException));
            }
        }