Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<Project>
<PropertyGroup>
<LibraryTargetFrameworks>netstandard2.1;netstandard2.0;net8.0;net10.0</LibraryTargetFrameworks>
<TestTargetFrameworks>net10.0</TestTargetFrameworks>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
Expand Down
242 changes: 242 additions & 0 deletions architecture.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Client/Client.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>OmniSharp.Extensions.LanguageClient</AssemblyName>
<RootNamespace>OmniSharp.Extensions.LanguageServer.Client</RootNamespace>
Expand Down
2 changes: 1 addition & 1 deletion src/Client/LanguageClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ private Supports<T> UseOrTryAndFindCapability<T>(Supports<T> supports) where T :

public IObservable<InitializeResult> Start => _initializeComplete.AsObservable();

bool IResponseRouter.TryGetRequest(long id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
bool IResponseRouter.TryGetRequest(object id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
{
return _responseRouter.TryGetRequest(id, out method, out pendingTask);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Dap.Client/Dap.Client.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>OmniSharp.Extensions.DebugAdapter.Client</AssemblyName>
<RootNamespace>OmniSharp.Extensions.DebugAdapter.Client</RootNamespace>
Expand Down
2 changes: 1 addition & 1 deletion src/Dap.Protocol.Proposals/Dap.Protocol.Proposals.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>OmniSharp.Extensions.DebugAdapter.Proposals</AssemblyName>
<RootNamespace>OmniSharp.Extensions.DebugAdapter.Protocol</RootNamespace>
Expand Down
2 changes: 1 addition & 1 deletion src/Dap.Protocol/Dap.Protocol.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>OmniSharp.Extensions.DebugAdapter</AssemblyName>
<RootNamespace>OmniSharp.Extensions.DebugAdapter.Protocol</RootNamespace>
Expand Down
2 changes: 1 addition & 1 deletion src/Dap.Protocol/IDebugAdapterProtocolProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public Task<TResponse> SendRequest<TResponse>(IRequest<TResponse> request, Cance
return _responseRouter.SendRequest(request, cancellationToken);
}

bool IResponseRouter.TryGetRequest(long id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
bool IResponseRouter.TryGetRequest(object id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
{
return _responseRouter.TryGetRequest(id, out method, out pendingTask);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Dap.Server/Dap.Server.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>OmniSharp.Extensions.DebugAdapter.Server</AssemblyName>
<RootNamespace>OmniSharp.Extensions.DebugAdapter.Server</RootNamespace>
Expand Down
2 changes: 1 addition & 1 deletion src/Dap.Shared/Dap.Shared.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>OmniSharp.Extensions.DebugAdapter.Shared</AssemblyName>
<RootNamespace>OmniSharp.Extensions.DebugAdapter.Shared</RootNamespace>
Expand Down
6 changes: 3 additions & 3 deletions src/Dap.Shared/DapResponseRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public class DapResponseRouter : IResponseRouter
internal readonly IOutputHandler OutputHandler;
internal readonly ISerializer Serializer;

internal readonly ConcurrentDictionary<long, (string method, TaskCompletionSource<JToken> pendingTask)> Requests =
new ConcurrentDictionary<long, (string method, TaskCompletionSource<JToken> pendingTask)>();
internal readonly ConcurrentDictionary<object, (string method, TaskCompletionSource<JToken> pendingTask)> Requests =
new ConcurrentDictionary<object, (string method, TaskCompletionSource<JToken> pendingTask)>();

internal static readonly ConcurrentDictionary<Type, string> MethodCache =
new ConcurrentDictionary<Type, string>();
Expand Down Expand Up @@ -69,7 +69,7 @@ public IResponseRouterReturns SendRequest<T>(string method, T @params)
return new ResponseRouterReturnsImpl(this, method, @params);
}

public bool TryGetRequest(long id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
public bool TryGetRequest(object id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
{
var result = Requests.TryGetValue(id, out var source);
method = source.method;
Expand Down
2 changes: 1 addition & 1 deletion src/Dap.Testing/Dap.Testing.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>OmniSharp.Extensions.DebugAdapter.Testing</AssemblyName>
<RootNamespace>OmniSharp.Extensions.DebugAdapter.Testing</RootNamespace>
Expand Down
2 changes: 1 addition & 1 deletion src/JsonRpc.Testing/JsonRpc.Testing.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>OmniSharp.Extensions.JsonRpc.Testing</AssemblyName>
<RootNamespace>OmniSharp.Extensions.JsonRpc.Testing</RootNamespace>
Expand Down
2 changes: 1 addition & 1 deletion src/JsonRpc/DefaultJsonRpcServerFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public Task<TResponse> SendRequest<TResponse>(IRequest<TResponse> request, Cance
return _responseRouter.SendRequest(request, cancellationToken);
}

bool IResponseRouter.TryGetRequest(long id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
bool IResponseRouter.TryGetRequest(object id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
{
return _responseRouter.TryGetRequest(id, out method, out pendingTask);
}
Expand Down
2 changes: 1 addition & 1 deletion src/JsonRpc/IResponseRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ public interface IResponseRouter
IResponseRouterReturns SendRequest<T>(string method, T @params);
IResponseRouterReturns SendRequest(string method);
Task<TResponse> SendRequest<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken);
bool TryGetRequest(long id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask);
bool TryGetRequest(object id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask);
}
}
132 changes: 92 additions & 40 deletions src/JsonRpc/InputHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ internal async Task ProcessInputStream(CancellationToken cancellationToken)
try
{
var headersParsed = false;
var invalidContentType = false;
long length = 0;
do
{
Expand All @@ -337,6 +338,7 @@ internal async Task ProcessInputStream(CancellationToken cancellationToken)
{
if (TryParseHeaders(ref buffer, out var line))
{
invalidContentType = !IsSupportedContentType(line);
if (TryParseContentLength(ref line, out length))
{
headersParsed = true;
Expand All @@ -346,7 +348,16 @@ internal async Task ProcessInputStream(CancellationToken cancellationToken)

if (headersParsed && length == 0)
{
HandleRequest(new ReadOnlySequence<byte>(Array.Empty<byte>()));
if (invalidContentType)
{
_outputHandler.Send(new ParseError(string.Empty));
invalidContentType = false;
}
else
{
HandleRequest(new ReadOnlySequence<byte>(Array.Empty<byte>()));
}

headersParsed = false;
}

Expand All @@ -356,7 +367,16 @@ internal async Task ProcessInputStream(CancellationToken cancellationToken)
{
headersParsed = false;
length = 0;
HandleRequest(line);
if (invalidContentType)
{
_outputHandler.Send(new ParseError(string.Empty));
invalidContentType = false;
}
else
{
HandleRequest(line);
}

dataParsed = true;
}
}
Expand Down Expand Up @@ -384,6 +404,41 @@ internal async Task ProcessInputStream(CancellationToken cancellationToken)
}
}

private static bool IsSupportedContentType(ReadOnlySequence<byte> headers)
{
var headerText = Encoding.ASCII.GetString(headers.ToArray());
foreach (var header in headerText.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
{
var colonIndex = header.IndexOf(':');
if (colonIndex < 0)
{
continue;
}

var name = header.Substring(0, colonIndex).Trim();
if (!string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase))
{
continue;
}

var value = header.Substring(colonIndex + 1);
foreach (var part in value.Split(';').Skip(1))
{
var pair = part.Split(new[] { '=' }, 2);
if (pair.Length != 2 || !string.Equals(pair[0].Trim(), "charset", StringComparison.OrdinalIgnoreCase))
{
continue;
}

var charset = pair[1].Trim().Trim('"');
return string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase) ||
string.Equals(charset, "utf8", StringComparison.OrdinalIgnoreCase);
}
}

return true;
}

private void HandleRequest(in ReadOnlySequence<byte> request)
{
JToken payload;
Expand All @@ -407,51 +462,46 @@ private void HandleRequest(in ReadOnlySequence<byte> request)

// using (_logger.TimeDebug("InputHandler is handling the request"))
// {
var (requests, hasResponse) = _receiver.GetRequests(payload);
if (hasResponse)
var (requests, _) = _receiver.GetRequests(payload);
var items = requests as Renor[] ?? requests.ToArray();
foreach (var response in items.Where(x => x.IsResponse).Select(x => x.Response!))
{
foreach (var response in requests.Where(x => x.IsResponse).Select(x => x.Response!))
// _logger.LogDebug("Handling Response for request {ResponseId}", response.Id);
if (response.Id is null)
{
// _logger.LogDebug("Handling Response for request {ResponseId}", response.Id);
var id = response.Id is string s ? long.Parse(s) : response.Id is long l ? l : -1;
if (id < 0)
{
// _logger.LogDebug("Id was out of range, skipping request {ResponseId}", response.Id);
continue;
}
continue;
}

if (!_responseRouter.TryGetRequest(id, out var method, out var tcs))
{
// _logger.LogDebug("Request {ResponseId} was not found in the response router, unable to complete", response.Id);
continue;
}
if (!_responseRouter.TryGetRequest(response.Id, out var method, out var tcs) &&
!(response.Id is string s && long.TryParse(s, out var numericId) && _responseRouter.TryGetRequest(numericId, out method, out tcs)))
{
// _logger.LogDebug("Request {ResponseId} was not found in the response router, unable to complete", response.Id);
continue;
}

_inputQueue.OnNext(
Observable.Create<Unit>(
observer =>
_inputQueue.OnNext(
Observable.Create<Unit>(
observer =>
{
if (response is ServerResponse serverResponse)
{
if (response is ServerResponse serverResponse)
{
// _logger.LogDebug("Setting successful Response for {ResponseId}", response.Id);
tcs.TrySetResult(serverResponse.Result);
}
else if (response is ServerError serverError)
{
// _logger.LogDebug("Setting error for {ResponseId}", response.Id);
tcs.TrySetException(DefaultErrorParser(method, serverError, _getException));
}

observer.OnCompleted();
return Disposable.Empty;
// _logger.LogDebug("Setting successful Response for {ResponseId}", response.Id);
tcs.TrySetResult(serverResponse.Result);
}
else if (response is ServerError serverError)
{
// _logger.LogDebug("Setting error for {ResponseId}", response.Id);
tcs.TrySetException(DefaultErrorParser(method, serverError, _getException));
}
)
);
}

return;
observer.OnCompleted();
return Disposable.Empty;
}
)
);
}

foreach (var item in requests)
foreach (var item in items)
{
if (item.IsRequest && item.Request != null)
{
Expand All @@ -463,7 +513,7 @@ private void HandleRequest(in ReadOnlySequence<byte> request)
{
_logger.LogDebug("Request handler was not found (or not setup) {Method} {ResponseId}", item.Request.Method, item.Request.Id);
_outputHandler.Send(new MethodNotFound(item.Request.Id, item.Request.Method));
return;
continue;
}

var requestHandle = _requestInvoker.InvokeRequest(descriptor, item.Request);
Expand Down Expand Up @@ -514,7 +564,7 @@ private void HandleRequest(in ReadOnlySequence<byte> request)
_logger.LogDebug("Notification handler was not found (or not setup) {Method}", item.Notification.Method);
// TODO: Figure out a good way to send this feedback back.
// _outputHandler.Send(new RpcError(null, new ErrorMessage(-32601, $"Method not found - {item.Notification.Method}")));
return;
continue;
}

_requestInvoker.InvokeNotification(descriptor, item.Notification);
Expand Down Expand Up @@ -548,6 +598,8 @@ private static Exception DefaultErrorParser(string? method, ServerError error, C
ErrorCodes.ParseError => new ParseErrorException(error.Id),
ErrorCodes.RequestCancelled => new RequestCancelledException(error.Id),
ErrorCodes.ContentModified => new ContentModifiedException(error.Id),
ErrorCodes.ServerCancelled => new ServerCancelledException(error.Id),
ErrorCodes.RequestFailed => new RequestFailedException(error.Id),
ErrorCodes.UnknownErrorCode => new UnknownErrorException(error.Id),
ErrorCodes.Exception => new JsonRpcException(ErrorCodes.Exception, error.Id, error.Error.Message, error.Error.Data?.ToString()),
_ => customHandler?.Invoke(error, method ?? "UNKNOWN") ??
Expand Down
2 changes: 1 addition & 1 deletion src/JsonRpc/JsonRpc.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>OmniSharp.Extensions.JsonRpc</AssemblyName>
<RootNamespace>OmniSharp.Extensions.JsonRpc</RootNamespace>
Expand Down
2 changes: 1 addition & 1 deletion src/JsonRpc/JsonRpcServerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public IResponseRouterReturns SendRequest(string method)
return ResponseRouter.SendRequest(method);
}

bool IResponseRouter.TryGetRequest(long id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
bool IResponseRouter.TryGetRequest(object id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
{
return ResponseRouter.TryGetRequest(id, out method, out pendingTask);
}
Expand Down
2 changes: 1 addition & 1 deletion src/JsonRpc/NoopResponseRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public Task<TResponse> SendRequest<TResponse>(IRequest<TResponse> request, Cance
return Task.FromResult<TResponse>(default!);
}

bool IResponseRouter.TryGetRequest(long id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
bool IResponseRouter.TryGetRequest(object id, [NotNullWhen(true)] out string? method, [NotNullWhen(true)] out TaskCompletionSource<JToken>? pendingTask)
{
method = default!;
pendingTask = default!;
Expand Down
Loading
Loading