Repository: dotnet/roslyn
File: src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs (C#)

Code before:

                    // Restore our activity id so that logging/tracking works across asynchronous calls.
                    Trace.CorrelationManager.ActivityId = activityId;

                    using var loggerScope = _logger.CreateContext(work.MethodName);

                    // Serially in the queue determine which language is appropriate for handling the request (based on the request URI).
                    //
                    // The client can send us the language associated with a URI in the didOpen notification.  It is important that all prior didOpen
                    // notifications have been completed by the time we attempt to determine the language, so we have the up to date map of URI to language.
                    // Since didOpen notifications are marked as mutating, the queue will not advance to the next request until the server has finished processing
                    // the didOpen, ensuring that this line will only run once all prior didOpens have completed.
                    var didGetLanguage = _languageServer.TryGetLanguageForRequest(work.MethodName, work.SerializedRequest, out var language);

                    using var languageScope = _logger.CreateLanguageContext(language);

                    // Now that we know the actual language, we can deserialize the request and start creating the request context.
                    var (metadata, handler, methodInfo) = GetHandlerForRequest(work, language ?? LanguageServerConstants.DefaultLanguageName);

                    // We had an issue determining the language.  Generally this is very rare and only occurs
                    // when a client sends us requests for files where we haven't saved the languageId.
                    // We should only crash if this was a mutating method.
                    //
                    // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didClose
                    // > Note that a server’s ability to fulfill requests is independent of whether a text document is open or closed.
                    if (!didGetLanguage && handler.MutatesSolutionState)
                    {
                        throw new InvalidOperationException($"Failed to get language for {work.MethodName}");
                    }

                    // We now have the actual handler and language, so we can process the work item using the concrete types defined by the metadata.
                    await InvokeProcessCoreAsync(work, metadata, handler, methodInfo, concurrentlyExecutingTasks, currentWorkCts, cancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    // Explicitly ignore this exception as cancellation occurred as a result of our linked cancellation token.
                    // This means either the queue is shutting down or the request itself was cancelled.
                    //   1.  If the queue is shutting down, then while loop will exit before the next iteration since it checks for cancellation.
                    //   2.  Request cancellations are normal so no need to report anything there.
                }
            }
        }
        catch (Exception ex)
        {
            // We encountered an unexpected exception in processing the queue or in a mutating request.
            // Log it, shutdown the queue, and exit the loop.
            _logger.LogException(ex);
            var message = $"Error occurred processing queue: {ex.Message}.";
            if (lspServices is not null)
            {
                await _languageServer.ShutdownAsync(message).ConfigureAwait(false);

Review comment (suggestion):
Personally, I've long been of the opinion that having our LSP server be really fussy about the shape of requests it will handle makes for a great self-DDOS machine, so I love the fact that predictable actions like this don't bring down the server any more.

Having said that, I think its still appropriate to return from this method, and not attempt to handle the request, if its non-mutating. And probably log something.

ie:
```
if (!didGetLanguage)
{
	if (handler.MutatesSolutionState)
    {
        throw new ....
    }

    logger.LogWarning("Refusing to handle {method} because we couldn't determine a language for {uri}");
    return;
}

Code after:

                    // Restore our activity id so that logging/tracking works across asynchronous calls.
                    Trace.CorrelationManager.ActivityId = activityId;

                    using var loggerScope = _logger.CreateContext(work.MethodName);

                    // Serially in the queue determine which language is appropriate for handling the request (based on the request URI).
                    //
                    // The client can send us the language associated with a URI in the didOpen notification.  It is important that all prior didOpen
                    // notifications have been completed by the time we attempt to determine the language, so we have the up to date map of URI to language.
                    // Since didOpen notifications are marked as mutating, the queue will not advance to the next request until the server has finished processing
                    // the didOpen, ensuring that this line will only run once all prior didOpens have completed.
                    var didGetLanguage = _languageServer.TryGetLanguageForRequest(work.MethodName, work.SerializedRequest, out var language);

                    using var languageScope = _logger.CreateLanguageContext(language);

                    // Now that we know the actual language, we can deserialize the request and start creating the request context.
                    var (metadata, handler, methodInfo) = GetHandlerForRequest(work, language ?? LanguageServerConstants.DefaultLanguageName);

                    // We had an issue determining the language.  Generally this is very rare and only occurs
                    // when a client sends us requests for files where we haven't saved the languageId.
                    // We should only crash if this was a mutating method.
                    //
                    // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_didClose
                    // > Note that a server’s ability to fulfill requests is independent of whether a text document is open or closed.
                    if (!didGetLanguage)
                    {
                        var message = $"Failed to get language for {work.MethodName}";
                        if (handler.MutatesSolutionState)
                        {
                            throw new InvalidOperationException(message);
                        }
                        else
                        {
                            work.FailRequest(message);
                            continue;
                        }
                    }

                    // We now have the actual handler and language, so we can process the work item using the concrete types defined by the metadata.
                    await InvokeProcessCoreAsync(work, metadata, handler, methodInfo, concurrentlyExecutingTasks, currentWorkCts, cancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    // Explicitly ignore this exception as cancellation occurred as a result of our linked cancellation token.
                    // This means either the queue is shutting down or the request itself was cancelled.
                    //   1.  If the queue is shutting down, then while loop will exit before the next iteration since it checks for cancellation.
                    //   2.  Request cancellations are normal so no need to report anything there.
                }
            }
        }