Пример #1
0
        /// <summary>
        ///     Receive a message from the language server.
        /// </summary>
        /// <returns>
        ///     A <see cref="ServerMessage"/> representing the message,
        /// </returns>
        async Task <ServerMessage> ReceiveMessage()
        {
            Log.LogDebug("Reading response headers...");

            byte[] headerBuffer = new byte[HeaderBufferSize];
            int    bytesRead    = await _input.ReadAsync(headerBuffer, 0, MinimumHeaderLength, _cancellation);

            Log.LogDebug("Read {ByteCount} bytes from input stream.", bytesRead);

            if (bytesRead == 0)
            {
                return(null); // Stream closed.
            }
            const byte CR = (byte)'\r';
            const byte LF = (byte)'\n';

            while (bytesRead < MinimumHeaderLength ||
                   headerBuffer[bytesRead - 4] != CR || headerBuffer[bytesRead - 3] != LF ||
                   headerBuffer[bytesRead - 2] != CR || headerBuffer[bytesRead - 1] != LF)
            {
                Log.LogDebug("Reading additional data from input stream...");

                // Read single bytes until we've got a valid end-of-header sequence.
                var additionalBytesRead = await _input.ReadAsync(headerBuffer, bytesRead, 1, _cancellation);

                if (additionalBytesRead == 0)
                {
                    return(null); // no more _input, mitigates endless loop here.
                }
                Log.LogDebug("Read {ByteCount} bytes of additional data from input stream.", additionalBytesRead);

                bytesRead += additionalBytesRead;
            }

            string headers = HeaderEncoding.GetString(headerBuffer, 0, bytesRead);

            Log.LogDebug("Got raw headers: {Headers}", headers);

            if (string.IsNullOrWhiteSpace(headers))
            {
                return(null); // Stream closed.
            }
            Log.LogDebug("Read response headers {Headers}.", headers);

            Dictionary <string, string> parsedHeaders = ParseHeaders(headers);

            if (!parsedHeaders.TryGetValue("Content-Length", out var contentLengthHeader))
            {
                Log.LogDebug("Invalid request headers (missing 'Content-Length' header).");

                return(null);
            }

            var contentLength = int.Parse(contentLengthHeader);

            Log.LogDebug("Reading response body ({ExpectedByteCount} bytes expected).", contentLength);

            var requestBuffer = new byte[contentLength];
            var received      = 0;

            while (received < contentLength)
            {
                Log.LogDebug("Reading segment of incoming request body ({ReceivedByteCount} of {TotalByteCount} bytes so far)...", received, contentLength);

                var payloadBytesRead = await _input.ReadAsync(requestBuffer, received, requestBuffer.Length - received, _cancellation);

                if (payloadBytesRead == 0)
                {
                    Log.LogWarning("Bailing out of reading payload (no_more_input after {ByteCount} bytes)...", received);

                    return(null);
                }
                received += payloadBytesRead;

                Log.LogDebug("Read segment of incoming request body ({ReceivedByteCount} of {TotalByteCount} bytes so far).", received, contentLength);
            }

            Log.LogDebug("Received entire payload ({ReceivedByteCount} bytes).", received);

            string        responseBody = PayloadEncoding.GetString(requestBuffer);
            ServerMessage message      = JsonConvert.DeserializeObject <ServerMessage>(responseBody, Serializer.Settings);

            Log.LogDebug("Read response body {ResponseBody}.", responseBody);

            return(message);
        }
Пример #2
0
        /// <summary>
        ///     Dispatch a request.
        /// </summary>
        /// <param name="requestMessage">
        ///     The request message.
        /// </param>
        private void DispatchRequest(ServerMessage requestMessage)
        {
            if (requestMessage == null)
            {
                throw new ArgumentNullException(nameof(requestMessage));
            }

            string requestId = requestMessage.Id.ToString();

            Log.LogDebug("Dispatching incoming {RequestMethod} request {RequestId}...", requestMessage.Method, requestId);

            var requestCancellation = CancellationTokenSource.CreateLinkedTokenSource(_cancellation);

            _requestCancellations.TryAdd(requestId, requestCancellation);

            Task <object> handlerTask = _dispatcher.TryHandleRequest(requestMessage.Method, requestMessage.Params, requestCancellation.Token);

            if (handlerTask == null)
            {
                Log.LogWarning("Unable to dispatch incoming {RequestMethod} request {RequestId} (no handler registered).", requestMessage.Method, requestId);

                _outgoing.TryAdd(
                    new JsonRpcMessages.MethodNotFound(requestMessage.Id, requestMessage.Method)
                    );

                return;
            }

#pragma warning disable CS4014 // Continuation does the work we need; no need to await it as this would tie up the dispatch loop.
            handlerTask.ContinueWith(_ =>
            {
                if (handlerTask.IsCanceled)
                {
                    Log.LogDebug("{RequestMethod} request {RequestId} canceled.", requestMessage.Method, requestId);
                }
                else if (handlerTask.IsFaulted)
                {
                    Exception handlerError = handlerTask.Exception.Flatten().InnerExceptions[0];

                    Log.LogError(handlerError, "{RequestMethod} request {RequestId} failed (unexpected error raised by handler).", requestMessage.Method, requestId);

                    _outgoing.TryAdd(new RpcError(requestId,
                                                  new JsonRpcMessages.ErrorMessage(
                                                      code: 500,
                                                      message: "Error processing request: " + handlerError.Message,
                                                      data: handlerError.ToString()
                                                      )
                                                  ));
                }
                else if (handlerTask.IsCompleted)
                {
                    Log.LogDebug("{RequestMethod} request {RequestId} complete (Result = {@Result}).", requestMessage.Method, requestId, handlerTask.Result);

                    _outgoing.TryAdd(new ClientMessage
                    {
                        Id     = requestMessage.Id,
                        Method = requestMessage.Method,
                        Result = handlerTask.Result != null ? JToken.FromObject(handlerTask.Result, Serializer.JsonSerializer) : null
                    });
                }

                _requestCancellations.TryRemove(requestId, out CancellationTokenSource cancellation);
                cancellation.Dispose();
            });
#pragma warning restore CS4014 // Continuation does the work we need; no need to await it as this would tie up the dispatch loop.

            Log.LogDebug("Dispatched incoming {RequestMethod} request {RequestId}.", requestMessage.Method, requestMessage.Id);
        }
Пример #3
0
        /// <summary>
        ///     The connection's message-receive loop.
        /// </summary>
        /// <returns>
        ///     A <see cref="Task"/> representing the loop's activity.
        /// </returns>
        async Task ReceiveLoop()
        {
            await Task.Yield();

            Log.LogInformation("Receive loop started.");

            try
            {
                while (!_cancellation.IsCancellationRequested && !_incoming.IsAddingCompleted)
                {
                    ServerMessage message = await ReceiveMessage();

                    if (message == null)
                    {
                        continue;
                    }

                    _cancellation.ThrowIfCancellationRequested();

                    try
                    {
                        if (message.Id != null)
                        {
                            // Request or response.
                            if (message.Params != null)
                            {
                                // Request.
                                Log.LogDebug("Received {RequestMethod} request {RequestId} from language server: {RequestParameters}",
                                             message.Method,
                                             message.Id,
                                             message.Params?.ToString(Formatting.None)
                                             );

                                // Publish.
                                if (!_incoming.IsAddingCompleted)
                                {
                                    _incoming.TryAdd(message);
                                }
                            }
                            else
                            {
                                // Response.
                                string requestId = message.Id.ToString();
                                if (_responseCompletions.TryGetValue(requestId, out var completion))
                                {
                                    if (message.Error != null)
                                    {
                                        Log.LogDebug("Received error response {RequestId} from language server: {@ErrorMessage}",
                                                     requestId,
                                                     message.Error
                                                     );

                                        Log.LogDebug("Faulting request {RequestId}.", requestId);

                                        completion.TrySetException(
                                            CreateLspException(message)
                                            );
                                    }
                                    else
                                    {
                                        Log.LogDebug("Received response {RequestId} from language server: {ResponseResult}",
                                                     requestId,
                                                     message.Result?.ToString(Formatting.None)
                                                     );

                                        Log.LogDebug("Completing request {RequestId}.", requestId);

                                        completion.TrySetResult(message);
                                    }
                                }
                                else
                                {
                                    Log.LogDebug("Received unexpected response {RequestId} from language server: {ResponseResult}",
                                                 requestId,
                                                 message.Result?.ToString(Formatting.None)
                                                 );
                                }
                            }
                        }
                        else
                        {
                            // Notification.
                            Log.LogDebug("Received {NotificationMethod} notification from language server: {NotificationParameters}",
                                         message.Method,
                                         message.Params?.ToString(Formatting.None)
                                         );

                            // Publish.
                            if (!_incoming.IsAddingCompleted)
                            {
                                _incoming.TryAdd(message);
                            }
                        }
                    }
                    catch (Exception dispatchError)
                    {
                        Log.LogError(dispatchError, "Unexpected error processing incoming message {@Message}.", message);
                    }
                }
            }
            catch (OperationCanceledException operationCanceled)
            {
                // Like tears in rain
                if (operationCanceled.CancellationToken != _cancellation)
                {
                    throw; // time to die
                }
            }
            finally
            {
                Log.LogInformation("Receive loop terminated.");
            }
        }