Exemple #1
0
 protected override void AddResponseBody(BinaryWriter writer)
 {
     writer.Write(this.ReturnCode);
     writer.Write(this.Utf8Output);
     BuildProtocolConstants.WriteLengthPrefixedString(writer, this.Output);
     BuildProtocolConstants.WriteLengthPrefixedString(writer, this.ErrorOutput);
 }
Exemple #2
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 <BuildResponse> ReadAsync(Stream stream, CancellationToken cancellationToken)
        {
            CompilerServerLogger.Log("Reading response length");
            // Read the response length
            var lengthBuffer = new byte[4];
            await BuildProtocolConstants.ReadAllAsync(stream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false);

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

            // Read the response
            CompilerServerLogger.Log("Reading response of length {0}", length);
            var responseBuffer = new byte[length];
            await BuildProtocolConstants.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(CompletedBuildResponse.Create(reader));

                case ResponseType.MismatchedVersion:
                    return(MismatchedVersionBuildResponse.Create(reader));

                default:
                    throw new InvalidOperationException("Received invalid response type from server.");
                }
            }
        }
Exemple #3
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 <BuildResponse> ReadAsync(PipeStream stream, CancellationToken cancellationToken)
        {
            CompilerServerLogger.Log("Reading response length");
            // Read the response length
            var lengthBuffer = new byte[4];
            await BuildProtocolConstants.ReadAllAsync(stream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false);

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

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

            using (var reader = new BinaryReader(new MemoryStream(responseBuffer), Encoding.Unicode))
            {
                int    returnCode  = reader.ReadInt32();
                string output      = BuildProtocolConstants.ReadLengthPrefixedString(reader);
                string errorOutput = BuildProtocolConstants.ReadLengthPrefixedString(reader);

                return(new BuildResponse(returnCode, output, errorOutput));
            }
        }
Exemple #4
0
        public static int Main(string[] args)
        {
            CompilerServerLogger.Initialize("SRV");
            CompilerServerLogger.Log("Process started");

            var keepAliveTimeout = GetKeepAliveTimeout();

            // Pipename should be passed as the first and only argument to the server process
            // and it must have the form "-pipename:name". Otherwise, exit with a non-zero
            // exit code
            const string pipeArgPrefix = "-pipename:";

            if (args.Length != 1 ||
                args[0].Length <= pipeArgPrefix.Length ||
                !args[0].StartsWith(pipeArgPrefix))
            {
                return(CommonCompiler.Failed);
            }

            var pipeName        = args[0].Substring(pipeArgPrefix.Length);
            var serverMutexName = BuildProtocolConstants.GetServerMutexName(pipeName);

            // VBCSCompiler is installed in the same directory as csc.exe and vbc.exe which is also the
            // location of the response files.
            var clientDirectory      = AppDomain.CurrentDomain.BaseDirectory;
            var sdkDirectory         = RuntimeEnvironment.GetRuntimeDirectory();
            var compilerServerHost   = new DesktopCompilerServerHost(clientDirectory, sdkDirectory);
            var clientConnectionHost = new NamedPipeClientConnectionHost(compilerServerHost, pipeName);

            return(Run(serverMutexName, clientConnectionHost, keepAliveTimeout));
        }
Exemple #5
0
            public static Argument ReadFromBinaryReader(BinaryReader reader)
            {
                uint   argId    = reader.ReadUInt32();
                uint   argIndex = reader.ReadUInt32();
                string value    = BuildProtocolConstants.ReadLengthPrefixedString(reader);

                return(new Argument(argId, argIndex, value));
            }
        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));
            }
        }
Exemple #7
0
        public static CompletedBuildResponse Create(BinaryReader reader)
        {
            var returnCode  = reader.ReadInt32();
            var utf8Output  = reader.ReadBoolean();
            var output      = BuildProtocolConstants.ReadLengthPrefixedString(reader);
            var errorOutput = BuildProtocolConstants.ReadLengthPrefixedString(reader);

            return(new CompletedBuildResponse(returnCode, utf8Output, output, errorOutput));
        }
Exemple #8
0
        /// <summary>
        /// Read a Request from the given stream.
        ///
        /// The total request size must be less than 1MB.
        /// </summary>
        /// <returns>null if the Request was too large, the Request otherwise.</returns>
        public static async Task <BuildRequest> ReadAsync(Stream inStream, CancellationToken cancellationToken)
        {
            // Read the length of the request
            var lengthBuffer = new byte[4];

            CompilerServerLogger.Log("Reading length of request");
            await BuildProtocolConstants.ReadAllAsync(inStream,
                                                      lengthBuffer,
                                                      4,
                                                      cancellationToken).ConfigureAwait(false);

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

            // Back out if the request is > 1MB
            if (length > 0x100000)
            {
                CompilerServerLogger.Log("Request is over 1MB in length, cancelling read.");
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            // Read the full request
            var responseBuffer = new byte[length];
            await BuildProtocolConstants.ReadAllAsync(inStream,
                                                      responseBuffer,
                                                      length,
                                                      cancellationToken).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested();

            CompilerServerLogger.Log("Parsing request");
            // Parse the request into the Request data structure.
            using (var reader = new BinaryReader(new MemoryStream(responseBuffer), Encoding.Unicode))
            {
                var  protocolVersion = reader.ReadUInt32();
                var  language        = (BuildProtocolConstants.RequestLanguage)reader.ReadUInt32();
                uint argumentCount   = reader.ReadUInt32();

                var argumentsBuilder = ImmutableArray.CreateBuilder <Argument>((int)argumentCount);

                for (int i = 0; i < argumentCount; i++)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    argumentsBuilder.Add(BuildRequest.Argument.ReadFromBinaryReader(reader));
                }

                return(new BuildRequest(protocolVersion,
                                        language,
                                        argumentsBuilder.ToImmutableArray()));
            }
        }
        /// <summary>
        /// Main entry point for the process. Initialize the server dispatcher
        /// and wait for connections.
        /// </summary>
        public static int Main(string[] args)
        {
            CompilerServerLogger.Initialize("SRV");
            CompilerServerLogger.Log("Process started");

            TimeSpan?keepAliveTimeout = null;

            // VBCSCompiler is installed in the same directory as csc.exe and vbc.exe which is also the
            // location of the response files.
            var compilerExeDirectory = AppDomain.CurrentDomain.BaseDirectory;

            try
            {
                int    keepAliveValue;
                string keepAliveStr = ConfigurationManager.AppSettings["keepalive"];
                if (int.TryParse(keepAliveStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out keepAliveValue) &&
                    keepAliveValue >= 0)
                {
                    if (keepAliveValue == 0)
                    {
                        // This is a one time server entry.
                        keepAliveTimeout = null;
                    }
                    else
                    {
                        keepAliveTimeout = TimeSpan.FromSeconds(keepAliveValue);
                    }
                }
                else
                {
                    keepAliveTimeout = s_defaultServerKeepAlive;
                }
            }
            catch (ConfigurationErrorsException e)
            {
                keepAliveTimeout = s_defaultServerKeepAlive;
                CompilerServerLogger.LogException(e, "Could not read AppSettings");
            }

            CompilerServerLogger.Log("Keep alive timeout is: {0} milliseconds.", keepAliveTimeout?.TotalMilliseconds ?? 0);
            FatalError.Handler = FailFast.OnFatalException;

            var dispatcher = new ServerDispatcher(new CompilerRequestHandler(compilerExeDirectory), new EmptyDiagnosticListener());

            dispatcher.ListenAndDispatchConnections(
                BuildProtocolConstants.GetPipeName(compilerExeDirectory),
                keepAliveTimeout,
                watchAnalyzerFiles: true);
            return(0);
        }
Exemple #10
0
        public BuildRequest(uint protocolVersion,
                            BuildProtocolConstants.RequestLanguage language,
                            ImmutableArray<Argument> arguments)
        {
            ProtocolVersion = protocolVersion;
            Language = language;

            if (arguments.Length > ushort.MaxValue)
            {
                throw new ArgumentOutOfRangeException(nameof(arguments),
                    "Too many arguments: maximum of "
                    + ushort.MaxValue + " arguments allowed.");
            }
            Arguments = arguments;
        }
        public async Task <ConnectionData> HandleConnection(bool allowCompilationRequests = true, CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                BuildRequest request;
                try
                {
                    Log("Begin reading request.");
                    request = await BuildRequest.ReadAsync(_stream, cancellationToken).ConfigureAwait(false);

                    ValidateBuildRequest(request);
                    Log("End reading request.");
                }
                catch (Exception e)
                {
                    LogException(e, "Error reading build request.");
                    return(new ConnectionData(CompletionReason.CompilationNotStarted));
                }

                if (request.ProtocolVersion != BuildProtocolConstants.ProtocolVersion)
                {
                    return(await HandleMismatchedVersionRequest(cancellationToken).ConfigureAwait(false));
                }
                else if (!string.Equals(request.CompilerHash, BuildProtocolConstants.GetCommitHash(), StringComparison.OrdinalIgnoreCase))
                {
                    return(await HandleIncorrectHashRequest(cancellationToken).ConfigureAwait(false));
                }
                else if (IsShutdownRequest(request))
                {
                    return(await HandleShutdownRequest(cancellationToken).ConfigureAwait(false));
                }
                else if (!allowCompilationRequests)
                {
                    return(await HandleRejectedRequest(cancellationToken).ConfigureAwait(false));
                }
                else
                {
                    return(await HandleCompilationRequest(request, cancellationToken).ConfigureAwait(false));
                }
            }
            finally
            {
                Close();
            }
        }
Exemple #12
0
        internal static int RunServer(string pipeName, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (string.IsNullOrEmpty(pipeName))
            {
                return(CommonCompiler.Failed);
            }

            var keepAliveTimeout = GetKeepAliveTimeout();
            var serverMutexName  = BuildProtocolConstants.GetServerMutexName(pipeName);

            // VBCSCompiler is installed in the same directory as csc.exe and vbc.exe which is also the
            // location of the response files.
            var clientDirectory      = AppDomain.CurrentDomain.BaseDirectory;
            var sdkDirectory         = RuntimeEnvironment.GetRuntimeDirectory();
            var compilerServerHost   = new DesktopCompilerServerHost(clientDirectory, sdkDirectory);
            var clientConnectionHost = new NamedPipeClientConnectionHost(compilerServerHost, pipeName);

            return(Run(serverMutexName, clientConnectionHost, keepAliveTimeout, cancellationToken));
        }
Exemple #13
0
        public async Task WriteAsync(PipeStream outStream, CancellationToken cancellationToken)
        {
            using (var writer = new BinaryWriter(new MemoryStream(), Encoding.Unicode))
            {
                // Format the response
                CompilerServerLogger.Log("Formatting Response");
                writer.Write(this.ReturnCode);
                BuildProtocolConstants.WriteLengthPrefixedString(writer, this.Output);
                BuildProtocolConstants.WriteLengthPrefixedString(writer, this.ErrorOutput);
                writer.Flush();

                cancellationToken.ThrowIfCancellationRequested();

                // Send the response to the client

                // Grab the MemoryStream and its internal buffer to prevent
                // making another copy.
                var stream = (MemoryStream)writer.BaseStream;
                // Write the length of the response
                uint length = (uint)stream.Length;
                CompilerServerLogger.Log("Writing response length");
                // There is no way to know the number of bytes written to
                // the pipe stream. We just have to assume all of them are written.
                await outStream.WriteAsync(BitConverter.GetBytes(length),
                                           0,
                                           4,
                                           cancellationToken).ConfigureAwait(false);

                // Write the response
                CompilerServerLogger.Log("Writing response of size {0}", length);
                // There is no way to know the number of bytes written to
                // the pipe stream. We just have to assume all of them are written.
                await outStream.WriteAsync(stream.GetBuffer(),
                                           0,
                                           (int)length,
                                           cancellationToken).ConfigureAwait(false);
            }
        }
Exemple #14
0
 public Argument(BuildProtocolConstants.ArgumentId argumentId,
                 uint argumentIndex,
                 string value)
 {
     this.ArgumentId = argumentId;
     this.ArgumentIndex = argumentIndex;
     this.Value = value;
 }
Exemple #15
0
 public void WriteToBinaryWriter(BinaryWriter writer)
 {
     writer.Write(this.ArgumentId);
     writer.Write(this.ArgumentIndex);
     BuildProtocolConstants.WriteLengthPrefixedString(writer, this.Value);
 }
Exemple #16
0
        /// <summary>
        /// Shutting down the server is an inherently racy operation.  The server can be started or stopped by
        /// external parties at any time.
        ///
        /// This function will return success if at any time in the function the server is determined to no longer
        /// be running.
        /// </summary>
        internal static async Task <int> RunShutdownAsync(string pipeName, bool waitForProcess = true, TimeSpan?timeout = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (string.IsNullOrEmpty(pipeName))
            {
                var clientDirectory = AppDomain.CurrentDomain.BaseDirectory;
                pipeName = DesktopBuildClient.GetPipeNameFromFileInfo(clientDirectory);
            }

            var mutexName = BuildProtocolConstants.GetServerMutexName(pipeName);

            if (!DesktopBuildClient.WasServerMutexOpen(mutexName))
            {
                // The server holds the mutex whenever it is running, if it's not open then the
                // server simply isn't running.
                return(CommonCompiler.Succeeded);
            }

            try
            {
                using (var client = new NamedPipeClientStream(pipeName))
                {
                    var realTimeout = timeout != null
                        ? (int)timeout.Value.TotalMilliseconds
                        : Timeout.Infinite;
                    client.Connect(realTimeout);

                    var request = BuildRequest.CreateShutdown();
                    await request.WriteAsync(client, cancellationToken).ConfigureAwait(false);

                    var response = await BuildResponse.ReadAsync(client, cancellationToken).ConfigureAwait(false);

                    var shutdownResponse = (ShutdownBuildResponse)response;

                    if (waitForProcess)
                    {
                        try
                        {
                            var process = Process.GetProcessById(shutdownResponse.ServerProcessId);
                            process.WaitForExit();
                        }
                        catch (Exception)
                        {
                            // There is an inherent race here with the server process.  If it has already shutdown
                            // by the time we try to access it then the operation has succeed.
                        }
                    }
                }

                return(CommonCompiler.Succeeded);
            }
            catch (Exception)
            {
                if (!DesktopBuildClient.WasServerMutexOpen(mutexName))
                {
                    // If the server was in the process of shutting down when we connected then it's reasonable
                    // for an exception to happen.  If the mutex has shutdown at this point then the server
                    // is shut down.
                    return(CommonCompiler.Succeeded);
                }

                return(CommonCompiler.Failed);
            }
        }
        /// <summary>
        /// Handles a client connection. The returned task here will never fail. Instead all exceptions will be wrapped
        /// in a <see cref="CompletionReason.RequestError"/>
        /// </summary>
        internal async Task <CompletionData> ProcessAsync(
            Task <IClientConnection> clientConnectionTask,
            bool allowCompilationRequests       = true,
            CancellationToken cancellationToken = default)
        {
            try
            {
                return(await ProcessCore().ConfigureAwait(false));
            }
            catch (Exception ex)
            {
                Logger.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);

                Logger.Log($"Received request {request.RequestId} of type {request.GetType()}");

                if (!string.Equals(request.CompilerHash, BuildProtocolConstants.GetCommitHash(), StringComparison.OrdinalIgnoreCase))
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               request.RequestId,
                               new IncorrectHashBuildResponse(),
                               CompletionData.RequestError,
                               cancellationToken).ConfigureAwait(false));
                }

                if (request.Arguments.Count == 1 && request.Arguments[0].ArgumentId == BuildProtocolConstants.ArgumentId.Shutdown)
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               request.RequestId,
                               new ShutdownBuildResponse(Process.GetCurrentProcess().Id),
                               new CompletionData(CompletionReason.RequestCompleted, shutdownRequested : true),
                               cancellationToken).ConfigureAwait(false));
                }

                if (!allowCompilationRequests)
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               request.RequestId,
                               new RejectedBuildResponse("Compilation not allowed at this time"),
                               CompletionData.RequestCompleted,
                               cancellationToken).ConfigureAwait(false));
                }

                if (!Environment.Is64BitProcess && !MemoryHelper.IsMemoryAvailable(Logger))
                {
                    return(await WriteBuildResponseAsync(
                               clientConnection,
                               request.RequestId,
                               new RejectedBuildResponse("Not enough resources to accept connection"),
                               CompletionData.RequestError,
                               cancellationToken).ConfigureAwait(false));
                }

                return(await ProcessCompilationRequestAsync(clientConnection, request, cancellationToken).ConfigureAwait(false));
            }
        }