Merge branch 'blazor-wasm' into prkrishn/purge
This commit is contained in:
commit
9450e95f2e
|
|
@ -57,235 +57,249 @@
|
||||||
<Uri>https://github.com/dotnet/efcore</Uri>
|
<Uri>https://github.com/dotnet/efcore</Uri>
|
||||||
<Sha>7ae89d6adb7433b6ae9c37091e57a1226092ef46</Sha>
|
<Sha>7ae89d6adb7433b6ae9c37091e57a1226092ef46</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.AspNetCore.Analyzer.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.AspNetCore.Analyzer.Testing" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.2" Pinned="true">
|
||||||
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
|
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.1.0" Pinned="true">
|
||||||
|
<Uri>https://github.com/aspnet/Extensions</Uri>
|
||||||
|
<Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
|
||||||
|
</Dependency>
|
||||||
|
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.1.0" Pinned="true">
|
||||||
|
<Uri>https://github.com/aspnet/Extensions</Uri>
|
||||||
|
<Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
|
||||||
|
</Dependency>
|
||||||
|
<Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.1.0" Pinned="true">
|
||||||
|
<Uri>https://github.com/aspnet/Extensions</Uri>
|
||||||
|
<Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
|
||||||
|
</Dependency>
|
||||||
|
<Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.0" Pinned="true">
|
||||||
|
<Uri>https://github.com/aspnet/Extensions</Uri>
|
||||||
|
<Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
|
||||||
|
</Dependency>
|
||||||
|
<Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Configuration" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.DependencyInjection" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.DependencyInjection" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Hosting" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Hosting" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Http" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Http" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Localization" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Localization" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Logging.Console" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.Console" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Logging.Debug" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.Debug" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="3.1.2" Pinned="true">
|
||||||
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
|
<Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Logging" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Logging" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Options" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Options" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.Primitives" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Primitives" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.JSInterop" Version="3.1.2" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Mono.WebAssembly.Interop" Version="3.1.2-preview4.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.JSInterop" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Mono.WebAssembly.Interop" Version="3.1.2-preview4.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
|
|
@ -379,7 +393,7 @@
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.DependencyModel" Version="3.1.2" CoherentParentDependency="Microsoft.Extensions.Logging">
|
<Dependency Name="Microsoft.Extensions.DependencyModel" Version="3.1.2" CoherentParentDependency="Microsoft.Extensions.Logging">
|
||||||
<Uri>https://github.com/dotnet/core-setup</Uri>
|
<Uri>https://github.com/dotnet/core-setup</Uri>
|
||||||
<Sha>916b5cba268e1e1e803243004f4276cf40b2dda8</Sha>
|
<Sha>65f04fb6db7a5e198d05dbebd5c4ad21eb018f89</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<!--
|
<!--
|
||||||
Win-x64 is used here because we have picked an arbitrary runtime identifier to flow the version of the latest NETCore.App runtime.
|
Win-x64 is used here because we have picked an arbitrary runtime identifier to flow the version of the latest NETCore.App runtime.
|
||||||
|
|
@ -413,7 +427,7 @@
|
||||||
<Uri>https://github.com/dotnet/corefx</Uri>
|
<Uri>https://github.com/dotnet/corefx</Uri>
|
||||||
<Sha>0f7f38c4fd323b26da10cce95f857f77f0f09b48</Sha>
|
<Sha>0f7f38c4fd323b26da10cce95f857f77f0f09b48</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Internal.AspNetCore.Analyzers" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Internal.AspNetCore.Analyzers" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
|
|
@ -429,7 +443,7 @@
|
||||||
<Uri>https://github.com/dotnet/arcade</Uri>
|
<Uri>https://github.com/dotnet/arcade</Uri>
|
||||||
<Sha>4d80b9cfa53e309c8f685abff3512f60c3d8a3d1</Sha>
|
<Sha>4d80b9cfa53e309c8f685abff3512f60c3d8a3d1</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.AspNetCore.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
|
<Dependency Name="Microsoft.AspNetCore.Testing" Version="3.1.2-servicing.20067.6" Pinned="true">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
@ -12,7 +11,9 @@ using System.Runtime.InteropServices;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using WsProxy;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using WebAssembly.Net.Debugging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Builder
|
namespace Microsoft.AspNetCore.Builder
|
||||||
{
|
{
|
||||||
|
|
@ -50,7 +51,8 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
|
|
||||||
if (requestPath.Equals("/_framework/debug/ws-proxy", StringComparison.OrdinalIgnoreCase))
|
if (requestPath.Equals("/_framework/debug/ws-proxy", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return DebugWebSocketProxyRequest(context);
|
var loggerFactory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
|
||||||
|
return DebugWebSocketProxyRequest(loggerFactory, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestPath.Equals("/_framework/debug", StringComparison.OrdinalIgnoreCase))
|
if (requestPath.Equals("/_framework/debug", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
@ -111,7 +113,8 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
var proxiedTabInfos = availableTabs.Select(tab =>
|
var proxiedTabInfos = availableTabs.Select(tab =>
|
||||||
{
|
{
|
||||||
var underlyingV8Endpoint = tab.WebSocketDebuggerUrl;
|
var underlyingV8Endpoint = tab.WebSocketDebuggerUrl;
|
||||||
var proxiedV8Endpoint = $"ws://{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
|
var proxiedScheme = request.IsHttps ? "wss" : "ws";
|
||||||
|
var proxiedV8Endpoint = $"{proxiedScheme}://{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
description = "",
|
description = "",
|
||||||
|
|
@ -129,10 +132,42 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
}
|
}
|
||||||
else if (requestPath.Equals("/json/version", StringComparison.OrdinalIgnoreCase))
|
else if (requestPath.Equals("/json/version", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var browserVersionJson = await GetBrowserVersionInfoAsync();
|
// VS Code's "js-debug" nightly extension, when configured to use the "pwa-chrome"
|
||||||
|
// debug type, uses the /json/version endpoint to find the websocket endpoint for
|
||||||
|
// debugging the browser that listens on a user-specified port.
|
||||||
|
//
|
||||||
|
// To make this flow work with the Mono debug proxy, we pass the request through
|
||||||
|
// to the underlying browser (to get its actual version info) but then overwrite
|
||||||
|
// the "webSocketDebuggerUrl" with the URL to the proxy.
|
||||||
|
//
|
||||||
|
// This whole connection flow isn't very good because it doesn't have any way
|
||||||
|
// to specify the debug port for the underlying browser. So, we end up assuming
|
||||||
|
// the default port 9222 in all cases. This is good enough for a manual "attach"
|
||||||
|
// but isn't good enough if the IDE is responsible for launching the browser,
|
||||||
|
// as it will be on a random port. So,
|
||||||
|
//
|
||||||
|
// - VS isn't going to use this. Instead it will use a configured "debugEndpoint"
|
||||||
|
// property from which it can construct the proxy URL directly (including adding
|
||||||
|
// a "browser" querystring value to specify the underlying endpoint), bypassing
|
||||||
|
// /json/version altogether
|
||||||
|
// - We will need to update the VS Code debug adapter to make it do the same as VS
|
||||||
|
// if there is a "debugEndpoint" property configured
|
||||||
|
//
|
||||||
|
// Once both VS and VS Code support the "debugEndpoint" flow, we should be able to
|
||||||
|
// remove this /json/version code altogether. We should check that in-browser
|
||||||
|
// debugging still works at that point.
|
||||||
|
|
||||||
|
var browserVersionJsonStream = await GetBrowserVersionInfoAsync();
|
||||||
|
var browserVersion = await JsonSerializer.DeserializeAsync<Dictionary<string, object>>(browserVersionJsonStream);
|
||||||
|
|
||||||
|
if (browserVersion.TryGetValue("webSocketDebuggerUrl", out var browserEndpoint))
|
||||||
|
{
|
||||||
|
var proxyEndpoint = GetProxyEndpoint(request, ((JsonElement)browserEndpoint).GetString());
|
||||||
|
browserVersion["webSocketDebuggerUrl"] = proxyEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
context.Response.ContentType = "application/json";
|
context.Response.ContentType = "application/json";
|
||||||
await context.Response.WriteAsync(browserVersionJson);
|
await JsonSerializer.SerializeAsync(context.Response.Body, browserVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -142,7 +177,7 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task DebugWebSocketProxyRequest(HttpContext context)
|
private static async Task DebugWebSocketProxyRequest(ILoggerFactory loggerFactory, HttpContext context)
|
||||||
{
|
{
|
||||||
if (!context.WebSockets.IsWebSocketRequest)
|
if (!context.WebSockets.IsWebSocketRequest)
|
||||||
{
|
{
|
||||||
|
|
@ -152,7 +187,7 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
|
|
||||||
var browserUri = new Uri(context.Request.Query["browser"]);
|
var browserUri = new Uri(context.Request.Query["browser"]);
|
||||||
var ideSocket = await context.WebSockets.AcceptWebSocketAsync();
|
var ideSocket = await context.WebSockets.AcceptWebSocketAsync();
|
||||||
await new MonoProxy().Run(browserUri, ideSocket);
|
await new MonoProxy(loggerFactory).Run(browserUri, ideSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task DebugHome(HttpContext context)
|
private static async Task DebugHome(HttpContext context)
|
||||||
|
|
@ -235,13 +270,30 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
// page and redirect there
|
// page and redirect there
|
||||||
var tabToDebug = matchingTabs.Single();
|
var tabToDebug = matchingTabs.Single();
|
||||||
var underlyingV8Endpoint = tabToDebug.WebSocketDebuggerUrl;
|
var underlyingV8Endpoint = tabToDebug.WebSocketDebuggerUrl;
|
||||||
var proxyEndpoint = $"{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
|
var proxyEndpoint = GetProxyEndpoint(request, underlyingV8Endpoint);
|
||||||
var devToolsUrlAbsolute = new Uri(debuggerHost + tabToDebug.DevtoolsFrontendUrl);
|
var devToolsUrlAbsolute = new Uri(debuggerHost + tabToDebug.DevtoolsFrontendUrl);
|
||||||
var wsParamName = request.IsHttps ? "wss" : "ws";
|
var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{proxyEndpoint.Scheme}={proxyEndpoint.Authority}{proxyEndpoint.PathAndQuery}";
|
||||||
var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{wsParamName}={proxyEndpoint}";
|
|
||||||
context.Response.Redirect(devToolsUrlWithProxy);
|
context.Response.Redirect(devToolsUrlWithProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Uri GetProxyEndpoint(HttpRequest incomingRequest, string browserEndpoint)
|
||||||
|
{
|
||||||
|
var builder = new UriBuilder(
|
||||||
|
schemeName: incomingRequest.IsHttps ? "wss" : "ws",
|
||||||
|
hostName: incomingRequest.Host.Host)
|
||||||
|
{
|
||||||
|
Path = $"{incomingRequest.PathBase}/_framework/debug/ws-proxy",
|
||||||
|
Query = $"browser={WebUtility.UrlEncode(browserEndpoint)}"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (incomingRequest.Host.Port.HasValue)
|
||||||
|
{
|
||||||
|
builder.Port = incomingRequest.Host.Port.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Uri;
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetLaunchChromeInstructions(string appRootUrl)
|
private static string GetLaunchChromeInstructions(string appRootUrl)
|
||||||
{
|
{
|
||||||
var profilePath = Path.Combine(Path.GetTempPath(), "blazor-chrome-debug");
|
var profilePath = Path.Combine(Path.GetTempPath(), "blazor-chrome-debug");
|
||||||
|
|
@ -289,11 +341,12 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> GetBrowserVersionInfoAsync()
|
private static async Task<Stream> GetBrowserVersionInfoAsync()
|
||||||
{
|
{
|
||||||
using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
|
using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
|
||||||
var debuggerHost = GetDebuggerHost();
|
var debuggerHost = GetDebuggerHost();
|
||||||
return await httpClient.GetStringAsync($"{debuggerHost}/json/version");
|
var response = await httpClient.GetAsync($"{debuggerHost}/json/version");
|
||||||
|
return await response.Content.ReadAsStreamAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IEnumerable<BrowserTab>> GetOpenedBrowserTabs()
|
private static async Task<IEnumerable<BrowserTab>> GetOpenedBrowserTabs()
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,10 @@ using System.Net.Http;
|
||||||
using Mono.Cecil.Pdb;
|
using Mono.Cecil.Pdb;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace WsProxy {
|
namespace WebAssembly.Net.Debugging {
|
||||||
internal class BreakPointRequest {
|
internal class BreakPointRequest {
|
||||||
public string Assembly { get; private set; }
|
public string Assembly { get; private set; }
|
||||||
public string File { get; private set; }
|
public string File { get; private set; }
|
||||||
|
|
@ -23,13 +25,15 @@ namespace WsProxy {
|
||||||
|
|
||||||
public static BreakPointRequest Parse (JObject args, DebugStore store)
|
public static BreakPointRequest Parse (JObject args, DebugStore store)
|
||||||
{
|
{
|
||||||
if (args == null)
|
// Events can potentially come out of order, so DebugStore may not be initialized
|
||||||
|
// The BP being set in these cases are JS ones, which we can safely ignore
|
||||||
|
if (args == null || store == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var url = args? ["url"]?.Value<string> ();
|
var url = args? ["url"]?.Value<string> ();
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
var urlRegex = args?["urlRegex"].Value<string>();
|
var urlRegex = args?["urlRegex"].Value<string>();
|
||||||
var sourceFile = store.GetFileByUrlRegex (urlRegex);
|
var sourceFile = store?.GetFileByUrlRegex (urlRegex);
|
||||||
|
|
||||||
url = sourceFile?.DotNetUrl;
|
url = sourceFile?.DotNetUrl;
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +76,6 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class VarInfo {
|
internal class VarInfo {
|
||||||
public VarInfo (VariableDebugInformation v)
|
public VarInfo (VariableDebugInformation v)
|
||||||
{
|
{
|
||||||
|
|
@ -85,10 +88,10 @@ namespace WsProxy {
|
||||||
this.Name = p.Name;
|
this.Name = p.Name;
|
||||||
this.Index = (p.Index + 1) * -1;
|
this.Index = (p.Index + 1) * -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
public int Index { get; private set; }
|
public int Index { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
public override string ToString ()
|
public override string ToString ()
|
||||||
{
|
{
|
||||||
return $"(var-info [{Index}] '{Name}')";
|
return $"(var-info [{Index}] '{Name}')";
|
||||||
|
|
@ -97,18 +100,14 @@ namespace WsProxy {
|
||||||
|
|
||||||
|
|
||||||
internal class CliLocation {
|
internal class CliLocation {
|
||||||
|
|
||||||
private MethodInfo method;
|
|
||||||
private int offset;
|
|
||||||
|
|
||||||
public CliLocation (MethodInfo method, int offset)
|
public CliLocation (MethodInfo method, int offset)
|
||||||
{
|
{
|
||||||
this.method = method;
|
Method = method;
|
||||||
this.offset = offset;
|
Offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodInfo Method { get => method; }
|
public MethodInfo Method { get; private set; }
|
||||||
public int Offset { get => offset; }
|
public int Offset { get; private set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -282,7 +281,6 @@ namespace WsProxy {
|
||||||
.Where (v => !v.IsDebuggerHidden)
|
.Where (v => !v.IsDebuggerHidden)
|
||||||
.Select (v => new VarInfo (v)));
|
.Select (v => new VarInfo (v)));
|
||||||
|
|
||||||
|
|
||||||
return res.ToArray ();
|
return res.ToArray ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -294,20 +292,21 @@ namespace WsProxy {
|
||||||
Dictionary<int, MethodInfo> methods = new Dictionary<int, MethodInfo> ();
|
Dictionary<int, MethodInfo> methods = new Dictionary<int, MethodInfo> ();
|
||||||
Dictionary<string, string> sourceLinkMappings = new Dictionary<string, string>();
|
Dictionary<string, string> sourceLinkMappings = new Dictionary<string, string>();
|
||||||
readonly List<SourceFile> sources = new List<SourceFile>();
|
readonly List<SourceFile> sources = new List<SourceFile>();
|
||||||
|
internal string Url { get; private set; }
|
||||||
|
|
||||||
public AssemblyInfo (byte[] assembly, byte[] pdb)
|
public AssemblyInfo (string url, byte[] assembly, byte[] pdb)
|
||||||
{
|
{
|
||||||
lock (typeof (AssemblyInfo)) {
|
lock (typeof (AssemblyInfo)) {
|
||||||
this.id = ++next_id;
|
this.id = ++next_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
Url = url;
|
||||||
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
|
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
|
||||||
if (pdb != null) {
|
rp.ReadSymbols = true;
|
||||||
rp.ReadSymbols = true;
|
rp.SymbolReaderProvider = new PdbReaderProvider ();
|
||||||
rp.SymbolReaderProvider = new PortablePdbReaderProvider ();
|
if (pdb != null)
|
||||||
rp.SymbolStream = new MemoryStream (pdb);
|
rp.SymbolStream = new MemoryStream (pdb);
|
||||||
}
|
|
||||||
|
|
||||||
rp.ReadingMode = ReadingMode.Immediate;
|
rp.ReadingMode = ReadingMode.Immediate;
|
||||||
rp.InMemory = true;
|
rp.InMemory = true;
|
||||||
|
|
@ -315,13 +314,16 @@ namespace WsProxy {
|
||||||
this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp);
|
this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp);
|
||||||
} catch (BadImageFormatException ex) {
|
} catch (BadImageFormatException ex) {
|
||||||
Console.WriteLine ($"Failed to read assembly as portable PDB: {ex.Message}");
|
Console.WriteLine ($"Failed to read assembly as portable PDB: {ex.Message}");
|
||||||
|
} catch (ArgumentNullException) {
|
||||||
|
if (pdb != null)
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.image == null) {
|
if (this.image == null) {
|
||||||
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
|
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
|
||||||
if (pdb != null) {
|
if (pdb != null) {
|
||||||
rp.ReadSymbols = true;
|
rp.ReadSymbols = true;
|
||||||
rp.SymbolReaderProvider = new NativePdbReaderProvider ();
|
rp.SymbolReaderProvider = new PdbReaderProvider ();
|
||||||
rp.SymbolStream = new MemoryStream (pdb);
|
rp.SymbolStream = new MemoryStream (pdb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -491,59 +493,70 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DebugStore {
|
internal class DebugStore {
|
||||||
|
// MonoProxy proxy; - commenting out because never gets assigned
|
||||||
List<AssemblyInfo> assemblies = new List<AssemblyInfo> ();
|
List<AssemblyInfo> assemblies = new List<AssemblyInfo> ();
|
||||||
|
HttpClient client = new HttpClient ();
|
||||||
|
|
||||||
public DebugStore (string [] loaded_files)
|
class DebugItem {
|
||||||
|
public string Url { get; set; }
|
||||||
|
public Task<byte[][]> Data { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Load (SessionId sessionId, string [] loaded_files, CancellationToken token)
|
||||||
{
|
{
|
||||||
bool MatchPdb (string asm, string pdb)
|
static bool MatchPdb (string asm, string pdb)
|
||||||
{
|
=> Path.ChangeExtension (asm, "pdb") == pdb;
|
||||||
return Path.ChangeExtension (asm, "pdb") == pdb;
|
|
||||||
}
|
|
||||||
|
|
||||||
var asm_files = new List<string> ();
|
var asm_files = new List<string> ();
|
||||||
var pdb_files = new List<string> ();
|
var pdb_files = new List<string> ();
|
||||||
foreach (var f in loaded_files) {
|
foreach (var file_name in loaded_files) {
|
||||||
var file_name = f;
|
|
||||||
if (file_name.EndsWith (".pdb", StringComparison.OrdinalIgnoreCase))
|
if (file_name.EndsWith (".pdb", StringComparison.OrdinalIgnoreCase))
|
||||||
pdb_files.Add (file_name);
|
pdb_files.Add (file_name);
|
||||||
else
|
else
|
||||||
asm_files.Add (file_name);
|
asm_files.Add (file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
//FIXME make this parallel
|
List<DebugItem> steps = new List<DebugItem> ();
|
||||||
foreach (var p in asm_files) {
|
foreach (var url in asm_files) {
|
||||||
try {
|
try {
|
||||||
var pdb = pdb_files.FirstOrDefault (n => MatchPdb (p, n));
|
var pdb = pdb_files.FirstOrDefault (n => MatchPdb (url, n));
|
||||||
HttpClient h = new HttpClient ();
|
steps.Add (
|
||||||
var assembly_bytes = h.GetByteArrayAsync (p).Result;
|
new DebugItem {
|
||||||
byte [] pdb_bytes = null;
|
Url = url,
|
||||||
if (pdb != null)
|
Data = Task.WhenAll (client.GetByteArrayAsync (url), pdb != null ? client.GetByteArrayAsync (pdb) : Task.FromResult<byte []> (null))
|
||||||
pdb_bytes = h.GetByteArrayAsync (pdb).Result;
|
});
|
||||||
|
|
||||||
this.assemblies.Add (new AssemblyInfo (assembly_bytes, pdb_bytes));
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Console.WriteLine ($"Failed to read {p} ({e.Message})");
|
Console.WriteLine ($"Failed to read {url} ({e.Message})");
|
||||||
|
var o = JObject.FromObject (new {
|
||||||
|
entry = new {
|
||||||
|
source = "other",
|
||||||
|
level = "warning",
|
||||||
|
text = $"Failed to read {url} ({e.Message})"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// proxy.SendEvent (sessionId, "Log.entryAdded", o, token); - commenting out because `proxy` would always be null
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var step in steps) {
|
||||||
|
try {
|
||||||
|
var bytes = await step.Data;
|
||||||
|
assemblies.Add (new AssemblyInfo (step.Url, bytes[0], bytes[1]));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Console.WriteLine ($"Failed to Load {step.Url} ({e.Message})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<SourceFile> AllSources ()
|
public IEnumerable<SourceFile> AllSources ()
|
||||||
{
|
=> assemblies.SelectMany (a => a.Sources);
|
||||||
foreach (var a in assemblies) {
|
|
||||||
foreach (var s in a.Sources)
|
|
||||||
yield return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SourceFile GetFileById (SourceId id)
|
public SourceFile GetFileById (SourceId id)
|
||||||
{
|
=> AllSources ().FirstOrDefault (f => f.SourceId.Equals (id));
|
||||||
return AllSources ().FirstOrDefault (f => f.SourceId.Equals (id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public AssemblyInfo GetAssemblyByName (string name)
|
public AssemblyInfo GetAssemblyByName (string name)
|
||||||
{
|
=> assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
|
||||||
return assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
V8 uses zero based indexing for both line and column.
|
V8 uses zero based indexing for both line and column.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Threading;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace WebAssembly.Net.Debugging {
|
||||||
|
internal class DevToolsClient: IDisposable {
|
||||||
|
ClientWebSocket socket;
|
||||||
|
List<Task> pending_ops = new List<Task> ();
|
||||||
|
TaskCompletionSource<bool> side_exit = new TaskCompletionSource<bool> ();
|
||||||
|
List<byte []> pending_writes = new List<byte []> ();
|
||||||
|
Task current_write;
|
||||||
|
|
||||||
|
public DevToolsClient () {
|
||||||
|
}
|
||||||
|
|
||||||
|
~DevToolsClient() {
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Close (CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (socket.State == WebSocketState.Open)
|
||||||
|
await socket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose (bool disposing) {
|
||||||
|
if (disposing)
|
||||||
|
socket.Dispose ();
|
||||||
|
}
|
||||||
|
|
||||||
|
Task Pump (Task task, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (task != current_write)
|
||||||
|
return null;
|
||||||
|
current_write = null;
|
||||||
|
|
||||||
|
pending_writes.RemoveAt (0);
|
||||||
|
|
||||||
|
if (pending_writes.Count > 0) {
|
||||||
|
current_write = socket.SendAsync (new ArraySegment<byte> (pending_writes [0]), WebSocketMessageType.Text, true, token);
|
||||||
|
return current_write;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<string> ReadOne (CancellationToken token)
|
||||||
|
{
|
||||||
|
byte [] buff = new byte [4000];
|
||||||
|
var mem = new MemoryStream ();
|
||||||
|
while (true) {
|
||||||
|
var result = await this.socket.ReceiveAsync (new ArraySegment<byte> (buff), token);
|
||||||
|
if (result.MessageType == WebSocketMessageType.Close) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.EndOfMessage) {
|
||||||
|
mem.Write (buff, 0, result.Count);
|
||||||
|
return Encoding.UTF8.GetString (mem.GetBuffer (), 0, (int)mem.Length);
|
||||||
|
} else {
|
||||||
|
mem.Write (buff, 0, result.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Send (byte [] bytes, CancellationToken token)
|
||||||
|
{
|
||||||
|
pending_writes.Add (bytes);
|
||||||
|
if (pending_writes.Count == 1) {
|
||||||
|
if (current_write != null)
|
||||||
|
throw new Exception ("Internal state is bad. current_write must be null if there are no pending writes");
|
||||||
|
|
||||||
|
current_write = socket.SendAsync (new ArraySegment<byte> (bytes), WebSocketMessageType.Text, true, token);
|
||||||
|
pending_ops.Add (current_write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task MarkCompleteAfterward (Func<CancellationToken, Task> send, CancellationToken token)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
await send(token);
|
||||||
|
side_exit.SetResult (true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
side_exit.SetException (e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<bool> ConnectWithMainLoops(
|
||||||
|
Uri uri,
|
||||||
|
Func<string, CancellationToken, Task> receive,
|
||||||
|
Func<CancellationToken, Task> send,
|
||||||
|
CancellationToken token) {
|
||||||
|
|
||||||
|
Console.WriteLine ("connecting to {0}", uri);
|
||||||
|
this.socket = new ClientWebSocket ();
|
||||||
|
this.socket.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
|
||||||
|
|
||||||
|
await this.socket.ConnectAsync (uri, token);
|
||||||
|
pending_ops.Add (ReadOne (token));
|
||||||
|
pending_ops.Add (side_exit.Task);
|
||||||
|
pending_ops.Add (MarkCompleteAfterward (send, token));
|
||||||
|
|
||||||
|
while (!token.IsCancellationRequested) {
|
||||||
|
var task = await Task.WhenAny (pending_ops);
|
||||||
|
if (task == pending_ops [0]) { //pending_ops[0] is for message reading
|
||||||
|
var msg = ((Task<string>)task).Result;
|
||||||
|
pending_ops [0] = ReadOne (token);
|
||||||
|
Task tsk = receive (msg, token);
|
||||||
|
if (tsk != null)
|
||||||
|
pending_ops.Add (tsk);
|
||||||
|
} else if (task == pending_ops [1]) {
|
||||||
|
var res = ((Task<bool>)task).Result;
|
||||||
|
//it might not throw if exiting successfull
|
||||||
|
return res;
|
||||||
|
} else { //must be a background task
|
||||||
|
pending_ops.Remove (task);
|
||||||
|
var tsk = Pump (task, token);
|
||||||
|
if (tsk != null)
|
||||||
|
pending_ops.Add (tsk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Log (string priority, string msg)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,8 +8,16 @@ using System.Threading;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace WsProxy {
|
namespace WebAssembly.Net.Debugging {
|
||||||
|
internal class SessionId {
|
||||||
|
public string sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MessageId : SessionId {
|
||||||
|
public int id;
|
||||||
|
}
|
||||||
|
|
||||||
internal struct Result {
|
internal struct Result {
|
||||||
public JObject Value { get; private set; }
|
public JObject Value { get; private set; }
|
||||||
|
|
@ -26,6 +34,7 @@ namespace WsProxy {
|
||||||
|
|
||||||
public static Result FromJson (JObject obj)
|
public static Result FromJson (JObject obj)
|
||||||
{
|
{
|
||||||
|
//Log ("protocol", $"from result: {obj}");
|
||||||
return new Result (obj ["result"] as JObject, obj ["error"] as JObject);
|
return new Result (obj ["result"] as JObject, obj ["error"] as JObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,28 +48,30 @@ namespace WsProxy {
|
||||||
return new Result (null, err);
|
return new Result (null, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JObject ToJObject (int id) {
|
public JObject ToJObject (MessageId target) {
|
||||||
if (IsOk) {
|
if (IsOk) {
|
||||||
return JObject.FromObject (new {
|
return JObject.FromObject (new {
|
||||||
id = id,
|
target.id,
|
||||||
|
target.sessionId,
|
||||||
result = Value
|
result = Value
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return JObject.FromObject (new {
|
return JObject.FromObject (new {
|
||||||
id = id,
|
target.id,
|
||||||
|
target.sessionId,
|
||||||
error = Error
|
error = Error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WsQueue {
|
class DevToolsQueue {
|
||||||
Task current_send;
|
Task current_send;
|
||||||
List<byte []> pending;
|
List<byte []> pending;
|
||||||
|
|
||||||
public WebSocket Ws { get; private set; }
|
public WebSocket Ws { get; private set; }
|
||||||
public Task CurrentSend { get { return current_send; } }
|
public Task CurrentSend { get { return current_send; } }
|
||||||
public WsQueue (WebSocket sock)
|
public DevToolsQueue (WebSocket sock)
|
||||||
{
|
{
|
||||||
this.Ws = sock;
|
this.Ws = sock;
|
||||||
pending = new List<byte []> ();
|
pending = new List<byte []> ();
|
||||||
|
|
@ -71,7 +82,7 @@ namespace WsProxy {
|
||||||
pending.Add (bytes);
|
pending.Add (bytes);
|
||||||
if (pending.Count == 1) {
|
if (pending.Count == 1) {
|
||||||
if (current_send != null)
|
if (current_send != null)
|
||||||
throw new Exception ("UNEXPECTED, current_send MUST BE NULL IF THERE'S no pending send");
|
throw new Exception ("current_send MUST BE NULL IF THERE'S no pending send");
|
||||||
//Console.WriteLine ("sending {0} bytes", bytes.Length);
|
//Console.WriteLine ("sending {0} bytes", bytes.Length);
|
||||||
current_send = Ws.SendAsync (new ArraySegment<byte> (bytes), WebSocketMessageType.Text, true, token);
|
current_send = Ws.SendAsync (new ArraySegment<byte> (bytes), WebSocketMessageType.Text, true, token);
|
||||||
return current_send;
|
return current_send;
|
||||||
|
|
@ -86,7 +97,7 @@ namespace WsProxy {
|
||||||
|
|
||||||
if (pending.Count > 0) {
|
if (pending.Count > 0) {
|
||||||
if (current_send != null)
|
if (current_send != null)
|
||||||
throw new Exception ("UNEXPECTED, current_send MUST BE NULL IF THERE'S no pending send");
|
throw new Exception ("current_send MUST BE NULL IF THERE'S no pending send");
|
||||||
//Console.WriteLine ("sending more {0} bytes", pending[0].Length);
|
//Console.WriteLine ("sending more {0} bytes", pending[0].Length);
|
||||||
current_send = Ws.SendAsync (new ArraySegment<byte> (pending [0]), WebSocketMessageType.Text, true, token);
|
current_send = Ws.SendAsync (new ArraySegment<byte> (pending [0]), WebSocketMessageType.Text, true, token);
|
||||||
return current_send;
|
return current_send;
|
||||||
|
|
@ -95,22 +106,28 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class WsProxy {
|
internal class DevToolsProxy {
|
||||||
TaskCompletionSource<bool> side_exception = new TaskCompletionSource<bool> ();
|
TaskCompletionSource<bool> side_exception = new TaskCompletionSource<bool> ();
|
||||||
TaskCompletionSource<bool> client_initiated_close = new TaskCompletionSource<bool> ();
|
TaskCompletionSource<bool> client_initiated_close = new TaskCompletionSource<bool> ();
|
||||||
List<(int, TaskCompletionSource<Result>)> pending_cmds = new List<(int, TaskCompletionSource<Result>)> ();
|
List<(MessageId, TaskCompletionSource<Result>)> pending_cmds = new List<(MessageId, TaskCompletionSource<Result>)> ();
|
||||||
ClientWebSocket browser;
|
ClientWebSocket browser;
|
||||||
WebSocket ide;
|
WebSocket ide;
|
||||||
int next_cmd_id;
|
int next_cmd_id;
|
||||||
List<Task> pending_ops = new List<Task> ();
|
List<Task> pending_ops = new List<Task> ();
|
||||||
List<WsQueue> queues = new List<WsQueue> ();
|
List<DevToolsQueue> queues = new List<DevToolsQueue> ();
|
||||||
|
readonly ILogger logger;
|
||||||
|
|
||||||
protected virtual Task<bool> AcceptEvent (string method, JObject args, CancellationToken token)
|
public DevToolsProxy(ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
logger = loggerFactory.CreateLogger<DevToolsProxy>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual Task<bool> AcceptEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
return Task.FromResult (false);
|
return Task.FromResult (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Task<bool> AcceptCommand (int id, string method, JObject args, CancellationToken token)
|
protected virtual Task<bool> AcceptCommand (MessageId id, string method, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
return Task.FromResult (false);
|
return Task.FromResult (false);
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +139,7 @@ namespace WsProxy {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
if (socket.State != WebSocketState.Open) {
|
if (socket.State != WebSocketState.Open) {
|
||||||
Console.WriteLine ($"WSProxy: Socket is no longer open.");
|
Log ("error", $"DevToolsProxy: Socket is no longer open.");
|
||||||
client_initiated_close.TrySetResult (true);
|
client_initiated_close.TrySetResult (true);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -133,51 +150,53 @@ namespace WsProxy {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.EndOfMessage) {
|
mem.Write (buff, 0, result.Count);
|
||||||
mem.Write (buff, 0, result.Count);
|
|
||||||
|
if (result.EndOfMessage)
|
||||||
return Encoding.UTF8.GetString (mem.GetBuffer (), 0, (int)mem.Length);
|
return Encoding.UTF8.GetString (mem.GetBuffer (), 0, (int)mem.Length);
|
||||||
} else {
|
|
||||||
mem.Write (buff, 0, result.Count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WsQueue GetQueueForSocket (WebSocket ws)
|
DevToolsQueue GetQueueForSocket (WebSocket ws)
|
||||||
{
|
{
|
||||||
return queues.FirstOrDefault (q => q.Ws == ws);
|
return queues.FirstOrDefault (q => q.Ws == ws);
|
||||||
}
|
}
|
||||||
|
|
||||||
WsQueue GetQueueForTask (Task task) {
|
DevToolsQueue GetQueueForTask (Task task)
|
||||||
|
{
|
||||||
return queues.FirstOrDefault (q => q.CurrentSend == task);
|
return queues.FirstOrDefault (q => q.CurrentSend == task);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Send (WebSocket to, JObject o, CancellationToken token)
|
void Send (WebSocket to, JObject o, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
var sender = browser == to ? "Send-browser" : "Send-ide";
|
||||||
|
Log ("protocol", $"{sender}: {o}");
|
||||||
var bytes = Encoding.UTF8.GetBytes (o.ToString ());
|
var bytes = Encoding.UTF8.GetBytes (o.ToString ());
|
||||||
|
|
||||||
var queue = GetQueueForSocket (to);
|
var queue = GetQueueForSocket (to);
|
||||||
|
|
||||||
var task = queue.Send (bytes, token);
|
var task = queue.Send (bytes, token);
|
||||||
if (task != null)
|
if (task != null)
|
||||||
pending_ops.Add (task);
|
pending_ops.Add (task);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task OnEvent (string method, JObject args, CancellationToken token)
|
async Task OnEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (!await AcceptEvent (method, args, token)) {
|
if (!await AcceptEvent (sessionId, method, args, token)) {
|
||||||
//Console.WriteLine ("proxy browser: {0}::{1}",method, args);
|
//Console.WriteLine ("proxy browser: {0}::{1}",method, args);
|
||||||
SendEventInternal (method, args, token);
|
SendEventInternal (sessionId, method, args, token);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
side_exception.TrySetException (e);
|
side_exception.TrySetException (e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task OnCommand (int id, string method, JObject args, CancellationToken token)
|
async Task OnCommand (MessageId id, string method, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (!await AcceptCommand (id, method, args, token)) {
|
if (!await AcceptCommand (id, method, args, token)) {
|
||||||
var res = await SendCommandInternal (method, args, token);
|
var res = await SendCommandInternal (id, method, args, token);
|
||||||
SendResponseInternal (id, res, token);
|
SendResponseInternal (id, res, token);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
@ -185,10 +204,11 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnResponse (int id, Result result)
|
void OnResponse (MessageId id, Result result)
|
||||||
{
|
{
|
||||||
//Console.WriteLine ("got id {0} res {1}", id, result);
|
//Console.WriteLine ("got id {0} res {1}", id, result);
|
||||||
var idx = pending_cmds.FindIndex (e => e.Item1 == id);
|
// Fixme
|
||||||
|
var idx = pending_cmds.FindIndex (e => e.Item1.id == id.id && e.Item1.sessionId == id.sessionId);
|
||||||
var item = pending_cmds [idx];
|
var item = pending_cmds [idx];
|
||||||
pending_cmds.RemoveAt (idx);
|
pending_cmds.RemoveAt (idx);
|
||||||
|
|
||||||
|
|
@ -197,68 +217,74 @@ namespace WsProxy {
|
||||||
|
|
||||||
void ProcessBrowserMessage (string msg, CancellationToken token)
|
void ProcessBrowserMessage (string msg, CancellationToken token)
|
||||||
{
|
{
|
||||||
// Debug ($"browser: {msg}");
|
Log ("protocol", $"browser: {msg}");
|
||||||
var res = JObject.Parse (msg);
|
var res = JObject.Parse (msg);
|
||||||
|
|
||||||
if (res ["id"] == null)
|
if (res ["id"] == null)
|
||||||
pending_ops.Add (OnEvent (res ["method"].Value<string> (), res ["params"] as JObject, token));
|
pending_ops.Add (OnEvent (new SessionId { sessionId = res ["sessionId"]?.Value<string> () }, res ["method"].Value<string> (), res ["params"] as JObject, token));
|
||||||
else
|
else
|
||||||
OnResponse (res ["id"].Value<int> (), Result.FromJson (res));
|
OnResponse (new MessageId { id = res ["id"].Value<int> (), sessionId = res ["sessionId"]?.Value<string> () }, Result.FromJson (res));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessIdeMessage (string msg, CancellationToken token)
|
void ProcessIdeMessage (string msg, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
Log ("protocol", $"ide: {msg}");
|
||||||
if (!string.IsNullOrEmpty (msg)) {
|
if (!string.IsNullOrEmpty (msg)) {
|
||||||
var res = JObject.Parse (msg);
|
var res = JObject.Parse (msg);
|
||||||
pending_ops.Add (OnCommand (res ["id"].Value<int> (), res ["method"].Value<string> (), res ["params"] as JObject, token));
|
pending_ops.Add (OnCommand (new MessageId { id = res ["id"].Value<int> (), sessionId = res ["sessionId"]?.Value<string> () }, res ["method"].Value<string> (), res ["params"] as JObject, token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Result> SendCommand (string method, JObject args, CancellationToken token) {
|
internal async Task<Result> SendCommand (SessionId id, string method, JObject args, CancellationToken token) {
|
||||||
// Debug ($"sending command {method}: {args}");
|
//Log ("verbose", $"sending command {method}: {args}");
|
||||||
return await SendCommandInternal (method, args, token);
|
return await SendCommandInternal (id, method, args, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
Task<Result> SendCommandInternal (string method, JObject args, CancellationToken token)
|
Task<Result> SendCommandInternal (SessionId sessionId, string method, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
int id = ++next_cmd_id;
|
int id = ++next_cmd_id;
|
||||||
|
|
||||||
var o = JObject.FromObject (new {
|
var o = JObject.FromObject (new {
|
||||||
id = id,
|
sessionId.sessionId,
|
||||||
method = method,
|
id,
|
||||||
|
method,
|
||||||
@params = args
|
@params = args
|
||||||
});
|
});
|
||||||
var tcs = new TaskCompletionSource<Result> ();
|
var tcs = new TaskCompletionSource<Result> ();
|
||||||
//Console.WriteLine ("add cmd id {0}", id);
|
|
||||||
pending_cmds.Add ((id, tcs));
|
|
||||||
|
var msgId = new MessageId { id = id, sessionId = sessionId.sessionId };
|
||||||
|
//Log ("verbose", $"add cmd id {sessionId}-{id}");
|
||||||
|
pending_cmds.Add ((msgId , tcs));
|
||||||
|
|
||||||
Send (this.browser, o, token);
|
Send (this.browser, o, token);
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendEvent (string method, JObject args, CancellationToken token)
|
public void SendEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
//Debug ($"sending event {method}: {args}");
|
//Log ("verbose", $"sending event {method}: {args}");
|
||||||
SendEventInternal (method, args, token);
|
SendEventInternal (sessionId, method, args, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendEventInternal (string method, JObject args, CancellationToken token)
|
void SendEventInternal (SessionId sessionId, string method, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
var o = JObject.FromObject (new {
|
var o = JObject.FromObject (new {
|
||||||
method = method,
|
sessionId.sessionId,
|
||||||
|
method,
|
||||||
@params = args
|
@params = args
|
||||||
});
|
});
|
||||||
|
|
||||||
Send (this.ide, o, token);
|
Send (this.ide, o, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SendResponse (int id, Result result, CancellationToken token)
|
internal void SendResponse (MessageId id, Result result, CancellationToken token)
|
||||||
{
|
{
|
||||||
//Debug ($"sending response: {id}: {result.ToJObject (id)}");
|
//Log ("verbose", $"sending response: {id}: {result.ToJObject (id)}");
|
||||||
SendResponseInternal (id, result, token);
|
SendResponseInternal (id, result, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendResponseInternal (int id, Result result, CancellationToken token)
|
void SendResponseInternal (MessageId id, Result result, CancellationToken token)
|
||||||
{
|
{
|
||||||
JObject o = result.ToJObject (id);
|
JObject o = result.ToJObject (id);
|
||||||
|
|
||||||
|
|
@ -268,16 +294,16 @@ namespace WsProxy {
|
||||||
// , HttpContext context)
|
// , HttpContext context)
|
||||||
public async Task Run (Uri browserUri, WebSocket ideSocket)
|
public async Task Run (Uri browserUri, WebSocket ideSocket)
|
||||||
{
|
{
|
||||||
Debug ($"WsProxy Starting on {browserUri}");
|
Log ("info", $"DevToolsProxy: Starting on {browserUri}");
|
||||||
using (this.ide = ideSocket) {
|
using (this.ide = ideSocket) {
|
||||||
Debug ($"WsProxy: IDE waiting for connection on {browserUri}");
|
Log ("verbose", $"DevToolsProxy: IDE waiting for connection on {browserUri}");
|
||||||
queues.Add (new WsQueue (this.ide));
|
queues.Add (new DevToolsQueue (this.ide));
|
||||||
using (this.browser = new ClientWebSocket ()) {
|
using (this.browser = new ClientWebSocket ()) {
|
||||||
this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
|
this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
|
||||||
await this.browser.ConnectAsync (browserUri, CancellationToken.None);
|
await this.browser.ConnectAsync (browserUri, CancellationToken.None);
|
||||||
queues.Add (new WsQueue (this.browser));
|
queues.Add (new DevToolsQueue (this.browser));
|
||||||
|
|
||||||
Debug ($"WsProxy: Client connected on {browserUri}");
|
Log ("verbose", $"DevToolsProxy: Client connected on {browserUri}");
|
||||||
var x = new CancellationTokenSource ();
|
var x = new CancellationTokenSource ();
|
||||||
|
|
||||||
pending_ops.Add (ReadOne (browser, x.Token));
|
pending_ops.Add (ReadOne (browser, x.Token));
|
||||||
|
|
@ -306,7 +332,7 @@ namespace WsProxy {
|
||||||
throw new Exception ("side task must always complete with an exception, what's going on???");
|
throw new Exception ("side task must always complete with an exception, what's going on???");
|
||||||
} else if (task == pending_ops [3]) {
|
} else if (task == pending_ops [3]) {
|
||||||
var res = ((Task<bool>)task).Result;
|
var res = ((Task<bool>)task).Result;
|
||||||
Debug ($"WsProxy: Client initiated close from {browserUri}");
|
Log ("verbose", $"DevToolsProxy: Client initiated close from {browserUri}");
|
||||||
x.Cancel ();
|
x.Cancel ();
|
||||||
} else {
|
} else {
|
||||||
//must be a background task
|
//must be a background task
|
||||||
|
|
@ -320,7 +346,7 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Debug ($"WsProxy::Run: Exception {e}");
|
Log ("error", $"DevToolsProxy::Run: Exception {e}");
|
||||||
//throw;
|
//throw;
|
||||||
} finally {
|
} finally {
|
||||||
if (!x.IsCancellationRequested)
|
if (!x.IsCancellationRequested)
|
||||||
|
|
@ -330,14 +356,28 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Debug (string msg)
|
protected void Log (string priority, string msg)
|
||||||
{
|
{
|
||||||
Console.WriteLine (msg);
|
switch (priority) {
|
||||||
}
|
case "protocol":
|
||||||
|
logger.LogTrace (msg);
|
||||||
protected void Info (string msg)
|
break;
|
||||||
{
|
case "verbose":
|
||||||
Console.WriteLine (msg);
|
logger.LogDebug (msg);
|
||||||
|
break;
|
||||||
|
case "info":
|
||||||
|
logger.LogInformation(msg);
|
||||||
|
break;
|
||||||
|
case "warning":
|
||||||
|
logger.LogWarning(msg);
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
logger.LogError (msg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.LogError(msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,14 +3,13 @@ using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace WsProxy {
|
namespace WebAssembly.Net.Debugging {
|
||||||
|
|
||||||
internal class MonoCommands {
|
internal class MonoCommands {
|
||||||
public const string GET_CALL_STACK = "MONO.mono_wasm_get_call_stack()";
|
public const string GET_CALL_STACK = "MONO.mono_wasm_get_call_stack()";
|
||||||
|
|
@ -29,10 +28,10 @@ namespace WsProxy {
|
||||||
BpNotFound = 100000,
|
BpNotFound = 100000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class MonoConstants {
|
internal class MonoConstants {
|
||||||
public const string RUNTIME_IS_READY = "mono_wasm_runtime_ready";
|
public const string RUNTIME_IS_READY = "mono_wasm_runtime_ready";
|
||||||
}
|
}
|
||||||
|
|
||||||
class Frame {
|
class Frame {
|
||||||
public Frame (MethodInfo method, SourceLocation location, int id)
|
public Frame (MethodInfo method, SourceLocation location, int id)
|
||||||
{
|
{
|
||||||
|
|
@ -73,7 +72,7 @@ namespace WsProxy {
|
||||||
Over
|
Over
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class MonoProxy : WsProxy {
|
internal class MonoProxy : DevToolsProxy {
|
||||||
DebugStore store;
|
DebugStore store;
|
||||||
List<Breakpoint> breakpoints = new List<Breakpoint> ();
|
List<Breakpoint> breakpoints = new List<Breakpoint> ();
|
||||||
List<Frame> current_callstack;
|
List<Frame> current_callstack;
|
||||||
|
|
@ -82,9 +81,9 @@ namespace WsProxy {
|
||||||
int ctx_id;
|
int ctx_id;
|
||||||
JObject aux_ctx_data;
|
JObject aux_ctx_data;
|
||||||
|
|
||||||
public MonoProxy () { }
|
public MonoProxy (ILoggerFactory loggerFactory) : base(loggerFactory) { }
|
||||||
|
|
||||||
protected override async Task<bool> AcceptEvent (string method, JObject args, CancellationToken token)
|
protected override async Task<bool> AcceptEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case "Runtime.executionContextCreated": {
|
case "Runtime.executionContextCreated": {
|
||||||
|
|
@ -93,8 +92,8 @@ namespace WsProxy {
|
||||||
if (aux_data != null) {
|
if (aux_data != null) {
|
||||||
var is_default = aux_data ["isDefault"]?.Value<bool> ();
|
var is_default = aux_data ["isDefault"]?.Value<bool> ();
|
||||||
if (is_default == true) {
|
if (is_default == true) {
|
||||||
var ctx_id = ctx ["id"].Value<int> ();
|
var id = new MessageId { id = ctx ["id"].Value<int> (), sessionId = sessionId.sessionId };
|
||||||
await OnDefaultContext (ctx_id, aux_data, token);
|
await OnDefaultContext (id, aux_data, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -103,11 +102,11 @@ namespace WsProxy {
|
||||||
//TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack
|
//TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack
|
||||||
var top_func = args? ["callFrames"]? [0]? ["functionName"]?.Value<string> ();
|
var top_func = args? ["callFrames"]? [0]? ["functionName"]?.Value<string> ();
|
||||||
if (top_func == "mono_wasm_fire_bp" || top_func == "_mono_wasm_fire_bp") {
|
if (top_func == "mono_wasm_fire_bp" || top_func == "_mono_wasm_fire_bp") {
|
||||||
await OnBreakPointHit (args, token);
|
await OnBreakPointHit (sessionId, args, token);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (top_func == MonoConstants.RUNTIME_IS_READY) {
|
if (top_func == MonoConstants.RUNTIME_IS_READY) {
|
||||||
await OnRuntimeReady (token);
|
await OnRuntimeReady (new SessionId { sessionId = sessionId.sessionId }, token);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -119,24 +118,33 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "Debugger.enabled": {
|
||||||
|
await LoadStore (new SessionId { sessionId = args? ["sessionId"]?.Value<string> () }, token);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override async Task<bool> AcceptCommand (int id, string method, JObject args, CancellationToken token)
|
protected override async Task<bool> AcceptCommand (MessageId id, string method, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
switch (method) {
|
switch (method) {
|
||||||
|
case "Target.attachToTarget": {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Target.attachToBrowserTarget": {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "Debugger.getScriptSource": {
|
case "Debugger.getScriptSource": {
|
||||||
var script_id = args? ["scriptId"]?.Value<string> ();
|
var script_id = args? ["scriptId"]?.Value<string> ();
|
||||||
if (script_id.StartsWith ("dotnet://", StringComparison.InvariantCultureIgnoreCase)) {
|
if (script_id.StartsWith ("dotnet://", StringComparison.InvariantCultureIgnoreCase)) {
|
||||||
await OnGetScriptSource (id, script_id, token);
|
await OnGetScriptSource (id, script_id, token);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "Runtime.compileScript": {
|
case "Runtime.compileScript": {
|
||||||
var exp = args? ["expression"]?.Value<string> ();
|
var exp = args? ["expression"]?.Value<string> ();
|
||||||
if (exp.StartsWith ("//dotnet:", StringComparison.InvariantCultureIgnoreCase)) {
|
if (exp.StartsWith ("//dotnet:", StringComparison.InvariantCultureIgnoreCase)) {
|
||||||
|
|
@ -156,7 +164,7 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "Debugger.setBreakpointByUrl": {
|
case "Debugger.setBreakpointByUrl": {
|
||||||
Info ($"BP req {args}");
|
Log ("info", $"BP req {args}");
|
||||||
var bp_req = BreakPointRequest.Parse (args, store);
|
var bp_req = BreakPointRequest.Parse (args, store);
|
||||||
if (bp_req != null) {
|
if (bp_req != null) {
|
||||||
await SetBreakPoint (id, bp_req, token);
|
await SetBreakPoint (id, bp_req, token);
|
||||||
|
|
@ -164,9 +172,10 @@ namespace WsProxy {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "Debugger.removeBreakpoint": {
|
case "Debugger.removeBreakpoint": {
|
||||||
return await RemoveBreakpoint (id, args, token);
|
return await RemoveBreakpoint (id, args, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
case "Debugger.resume": {
|
case "Debugger.resume": {
|
||||||
await OnResume (token);
|
await OnResume (token);
|
||||||
|
|
@ -199,16 +208,24 @@ namespace WsProxy {
|
||||||
|
|
||||||
case "Runtime.getProperties": {
|
case "Runtime.getProperties": {
|
||||||
var objId = args? ["objectId"]?.Value<string> ();
|
var objId = args? ["objectId"]?.Value<string> ();
|
||||||
if (objId.StartsWith ("dotnet:scope:", StringComparison.InvariantCulture)) {
|
if (objId.StartsWith ("dotnet:")) {
|
||||||
await GetScopeProperties (id, int.Parse (objId.Substring ("dotnet:scope:".Length)), token);
|
var parts = objId.Split (new char [] { ':' });
|
||||||
return true;
|
if (parts.Length < 3)
|
||||||
}
|
return true;
|
||||||
if (objId.StartsWith("dotnet:", StringComparison.InvariantCulture))
|
switch (parts[1]) {
|
||||||
{
|
case "scope": {
|
||||||
if (objId.StartsWith("dotnet:object:", StringComparison.InvariantCulture))
|
await GetScopeProperties (id, int.Parse (parts[2]), token);
|
||||||
await GetDetails(id, int.Parse(objId.Substring("dotnet:object:".Length)), token, MonoCommands.GET_OBJECT_PROPERTIES);
|
break;
|
||||||
if (objId.StartsWith("dotnet:array:", StringComparison.InvariantCulture))
|
}
|
||||||
await GetDetails(id, int.Parse(objId.Substring("dotnet:array:".Length)), token, MonoCommands.GET_ARRAY_VALUES);
|
case "object": {
|
||||||
|
await GetDetails (id, int.Parse (parts[2]), token, MonoCommands.GET_OBJECT_PROPERTIES);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "array": {
|
||||||
|
await GetDetails (id, int.Parse (parts[2]), token, MonoCommands.GET_ARRAY_VALUES);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -218,15 +235,16 @@ namespace WsProxy {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task OnRuntimeReady (CancellationToken token)
|
async Task OnRuntimeReady (SessionId sessionId, CancellationToken token)
|
||||||
{
|
{
|
||||||
Info ("RUNTIME READY, PARTY TIME");
|
Log ("info", "RUNTIME READY, PARTY TIME");
|
||||||
await RuntimeReady (token);
|
await RuntimeReady (sessionId, token);
|
||||||
await SendCommand ("Debugger.resume", new JObject (), token);
|
await SendCommand (sessionId, "Debugger.resume", new JObject (), token);
|
||||||
SendEvent ("Mono.runtimeReady", new JObject (), token);
|
SendEvent (sessionId, "Mono.runtimeReady", new JObject (), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task OnBreakPointHit (JObject args, CancellationToken token)
|
//static int frame_id=0;
|
||||||
|
async Task OnBreakPointHit (SessionId sessionId, JObject args, CancellationToken token)
|
||||||
{
|
{
|
||||||
//FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime
|
//FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime
|
||||||
var o = JObject.FromObject (new {
|
var o = JObject.FromObject (new {
|
||||||
|
|
@ -238,11 +256,11 @@ namespace WsProxy {
|
||||||
});
|
});
|
||||||
|
|
||||||
var orig_callframes = args? ["callFrames"]?.Values<JObject> ();
|
var orig_callframes = args? ["callFrames"]?.Values<JObject> ();
|
||||||
var res = await SendCommand ("Runtime.evaluate", o, token);
|
var res = await SendCommand (sessionId, "Runtime.evaluate", o, token);
|
||||||
|
|
||||||
if (res.IsErr) {
|
if (res.IsErr) {
|
||||||
//Give up and send the original call stack
|
//Give up and send the original call stack
|
||||||
SendEvent ("Debugger.paused", args, token);
|
SendEvent (sessionId, "Debugger.paused", args, token);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,16 +268,16 @@ namespace WsProxy {
|
||||||
var res_value = res.Value? ["result"]? ["value"];
|
var res_value = res.Value? ["result"]? ["value"];
|
||||||
if (res_value == null || res_value is JValue) {
|
if (res_value == null || res_value is JValue) {
|
||||||
//Give up and send the original call stack
|
//Give up and send the original call stack
|
||||||
SendEvent ("Debugger.paused", args, token);
|
SendEvent (sessionId, "Debugger.paused", args, token);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug ($"call stack (err is {res.Error} value is:\n{res.Value}");
|
Log ("verbose", $"call stack (err is {res.Error} value is:\n{res.Value}");
|
||||||
var bp_id = res_value? ["breakpoint_id"]?.Value<int> ();
|
var bp_id = res_value? ["breakpoint_id"]?.Value<int> ();
|
||||||
Debug ($"We just hit bp {bp_id}");
|
Log ("verbose", $"We just hit bp {bp_id}");
|
||||||
if (!bp_id.HasValue) {
|
if (!bp_id.HasValue) {
|
||||||
//Give up and send the original call stack
|
//Give up and send the original call stack
|
||||||
SendEvent ("Debugger.paused", args, token);
|
SendEvent (sessionId, "Debugger.paused", args, token);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var bp = this.breakpoints.FirstOrDefault (b => b.RemoteId == bp_id.Value);
|
var bp = this.breakpoints.FirstOrDefault (b => b.RemoteId == bp_id.Value);
|
||||||
|
|
@ -282,14 +300,14 @@ namespace WsProxy {
|
||||||
|
|
||||||
var asm = store.GetAssemblyByName (assembly_name);
|
var asm = store.GetAssemblyByName (assembly_name);
|
||||||
if (asm == null) {
|
if (asm == null) {
|
||||||
Info ($"Unable to find assembly: {assembly_name}");
|
Log ("info",$"Unable to find assembly: {assembly_name}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var method = asm.GetMethodByToken (method_token);
|
var method = asm.GetMethodByToken (method_token);
|
||||||
|
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
Info ($"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
|
Log ("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,8 +321,8 @@ namespace WsProxy {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Info ($"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
|
Log ("info", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
|
||||||
Info ($"\tmethod {method.Name} location: {location}");
|
Log ("info", $"\tmethod {method.Name} location: {location}");
|
||||||
frames.Add (new Frame (method, location, frame_id));
|
frames.Add (new Frame (method, location, frame_id));
|
||||||
|
|
||||||
callFrames.Add (JObject.FromObject (new {
|
callFrames.Add (JObject.FromObject (new {
|
||||||
|
|
@ -351,12 +369,12 @@ namespace WsProxy {
|
||||||
hitBreakpoints = bp_list,
|
hitBreakpoints = bp_list,
|
||||||
});
|
});
|
||||||
|
|
||||||
SendEvent ("Debugger.paused", o, token);
|
SendEvent (sessionId, "Debugger.paused", o, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task OnDefaultContext (int ctx_id, JObject aux_data, CancellationToken token)
|
async Task OnDefaultContext (MessageId ctx_id, JObject aux_data, CancellationToken token)
|
||||||
{
|
{
|
||||||
Debug ("Default context created, clearing state and sending events");
|
Log ("verbose", "Default context created, clearing state and sending events");
|
||||||
|
|
||||||
//reset all bps
|
//reset all bps
|
||||||
foreach (var b in this.breakpoints){
|
foreach (var b in this.breakpoints){
|
||||||
|
|
@ -371,16 +389,16 @@ namespace WsProxy {
|
||||||
silent = false,
|
silent = false,
|
||||||
returnByValue = true
|
returnByValue = true
|
||||||
});
|
});
|
||||||
this.ctx_id = ctx_id;
|
this.ctx_id = ctx_id.id;
|
||||||
this.aux_ctx_data = aux_data;
|
this.aux_ctx_data = aux_data;
|
||||||
|
|
||||||
Debug ("checking if the runtime is ready");
|
Log ("verbose", "checking if the runtime is ready");
|
||||||
var res = await SendCommand ("Runtime.evaluate", o, token);
|
var res = await SendCommand (ctx_id, "Runtime.evaluate", o, token);
|
||||||
var is_ready = res.Value? ["result"]? ["value"]?.Value<bool> ();
|
var is_ready = res.Value? ["result"]? ["value"]?.Value<bool> ();
|
||||||
//Debug ($"\t{is_ready}");
|
//Log ("verbose", $"\t{is_ready}");
|
||||||
if (is_ready.HasValue && is_ready.Value == true) {
|
if (is_ready.HasValue && is_ready.Value == true) {
|
||||||
Debug ("RUNTIME LOOK READY. GO TIME!");
|
Log ("verbose", "RUNTIME LOOK READY. GO TIME!");
|
||||||
await OnRuntimeReady (token);
|
await OnRuntimeReady (ctx_id, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,7 +410,7 @@ namespace WsProxy {
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task Step (int msg_id, StepKind kind, CancellationToken token)
|
async Task Step (MessageId msg_id, StepKind kind, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
|
||||||
var o = JObject.FromObject (new {
|
var o = JObject.FromObject (new {
|
||||||
|
|
@ -403,16 +421,16 @@ namespace WsProxy {
|
||||||
returnByValue = true,
|
returnByValue = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var res = await SendCommand ("Runtime.evaluate", o, token);
|
var res = await SendCommand (msg_id, "Runtime.evaluate", o, token);
|
||||||
|
|
||||||
SendResponse (msg_id, Result.Ok (new JObject ()), token);
|
SendResponse (msg_id, Result.Ok (new JObject ()), token);
|
||||||
|
|
||||||
this.current_callstack = null;
|
this.current_callstack = null;
|
||||||
|
|
||||||
await SendCommand ("Debugger.resume", new JObject (), token);
|
await SendCommand (msg_id, "Debugger.resume", new JObject (), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task GetDetails(int msg_id, int object_id, CancellationToken token, string command)
|
async Task GetDetails(MessageId msg_id, int object_id, CancellationToken token, string command)
|
||||||
{
|
{
|
||||||
var o = JObject.FromObject(new
|
var o = JObject.FromObject(new
|
||||||
{
|
{
|
||||||
|
|
@ -423,7 +441,7 @@ namespace WsProxy {
|
||||||
returnByValue = true,
|
returnByValue = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var res = await SendCommand("Runtime.evaluate", o, token);
|
var res = await SendCommand(msg_id, "Runtime.evaluate", o, token);
|
||||||
|
|
||||||
//if we fail we just buble that to the IDE (and let it panic over it)
|
//if we fail we just buble that to the IDE (and let it panic over it)
|
||||||
if (res.IsErr)
|
if (res.IsErr)
|
||||||
|
|
@ -461,14 +479,13 @@ namespace WsProxy {
|
||||||
{
|
{
|
||||||
result = var_list
|
result = var_list
|
||||||
});
|
});
|
||||||
} catch (Exception) {
|
} catch (Exception e) {
|
||||||
Debug ($"failed to parse {res.Value}");
|
Log ("verbose", $"failed to parse {res.Value} - {e.Message}");
|
||||||
}
|
}
|
||||||
SendResponse(msg_id, Result.Ok(o), token);
|
SendResponse(msg_id, Result.Ok(o), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async Task GetScopeProperties (MessageId msg_id, int scope_id, CancellationToken token)
|
||||||
async Task GetScopeProperties (int msg_id, int scope_id, CancellationToken token)
|
|
||||||
{
|
{
|
||||||
var scope = this.current_callstack.FirstOrDefault (s => s.Id == scope_id);
|
var scope = this.current_callstack.FirstOrDefault (s => s.Id == scope_id);
|
||||||
var vars = scope.Method.GetLiveVarsAt (scope.Location.CliLocation.Offset);
|
var vars = scope.Method.GetLiveVarsAt (scope.Location.CliLocation.Offset);
|
||||||
|
|
@ -484,7 +501,7 @@ namespace WsProxy {
|
||||||
returnByValue = true,
|
returnByValue = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var res = await SendCommand ("Runtime.evaluate", o, token);
|
var res = await SendCommand (msg_id, "Runtime.evaluate", o, token);
|
||||||
|
|
||||||
//if we fail we just buble that to the IDE (and let it panic over it)
|
//if we fail we just buble that to the IDE (and let it panic over it)
|
||||||
if (res.IsErr) {
|
if (res.IsErr) {
|
||||||
|
|
@ -533,13 +550,13 @@ namespace WsProxy {
|
||||||
result = var_list
|
result = var_list
|
||||||
});
|
});
|
||||||
SendResponse (msg_id, Result.Ok (o), token);
|
SendResponse (msg_id, Result.Ok (o), token);
|
||||||
}
|
} catch (Exception exception) {
|
||||||
catch (Exception) {
|
Log ("verbose", $"Error resolving scope properties {exception.Message}");
|
||||||
SendResponse (msg_id, res, token);
|
SendResponse (msg_id, res, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<Result> EnableBreakPoint (Breakpoint bp, CancellationToken token)
|
async Task<Result> EnableBreakPoint (SessionId sessionId, Breakpoint bp, CancellationToken token)
|
||||||
{
|
{
|
||||||
var asm_name = bp.Location.CliLocation.Method.Assembly.Name;
|
var asm_name = bp.Location.CliLocation.Method.Assembly.Name;
|
||||||
var method_token = bp.Location.CliLocation.Method.Token;
|
var method_token = bp.Location.CliLocation.Method.Token;
|
||||||
|
|
@ -553,21 +570,20 @@ namespace WsProxy {
|
||||||
returnByValue = true,
|
returnByValue = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var res = await SendCommand ("Runtime.evaluate", o, token);
|
var res = await SendCommand (sessionId, "Runtime.evaluate", o, token);
|
||||||
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
|
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
|
||||||
|
|
||||||
if (ret_code.HasValue) {
|
if (ret_code.HasValue) {
|
||||||
bp.RemoteId = ret_code.Value;
|
bp.RemoteId = ret_code.Value;
|
||||||
bp.State = BreakPointState.Active;
|
bp.State = BreakPointState.Active;
|
||||||
//Debug ($"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}");
|
//Log ("verbose", $"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task RuntimeReady (CancellationToken token)
|
async Task LoadStore (SessionId sessionId, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
|
||||||
var o = JObject.FromObject (new {
|
var o = JObject.FromObject (new {
|
||||||
expression = MonoCommands.GET_LOADED_FILES,
|
expression = MonoCommands.GET_LOADED_FILES,
|
||||||
objectGroup = "mono_debugger",
|
objectGroup = "mono_debugger",
|
||||||
|
|
@ -575,10 +591,19 @@ namespace WsProxy {
|
||||||
silent = false,
|
silent = false,
|
||||||
returnByValue = true,
|
returnByValue = true,
|
||||||
});
|
});
|
||||||
var loaded_pdbs = await SendCommand ("Runtime.evaluate", o, token);
|
|
||||||
|
var loaded_pdbs = await SendCommand (sessionId, "Runtime.evaluate", o, token);
|
||||||
var the_value = loaded_pdbs.Value? ["result"]? ["value"];
|
var the_value = loaded_pdbs.Value? ["result"]? ["value"];
|
||||||
var the_pdbs = the_value?.ToObject<string[]> ();
|
var the_pdbs = the_value?.ToObject<string[]> ();
|
||||||
this.store = new DebugStore (the_pdbs);
|
|
||||||
|
store = new DebugStore ();
|
||||||
|
await store.Load(sessionId, the_pdbs, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task RuntimeReady (SessionId sessionId, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (store == null)
|
||||||
|
await LoadStore (sessionId, token);
|
||||||
|
|
||||||
foreach (var s in store.AllSources ()) {
|
foreach (var s in store.AllSources ()) {
|
||||||
var ok = JObject.FromObject (new {
|
var ok = JObject.FromObject (new {
|
||||||
|
|
@ -587,13 +612,13 @@ namespace WsProxy {
|
||||||
executionContextId = this.ctx_id,
|
executionContextId = this.ctx_id,
|
||||||
hash = s.DocHashCode,
|
hash = s.DocHashCode,
|
||||||
executionContextAuxData = this.aux_ctx_data,
|
executionContextAuxData = this.aux_ctx_data,
|
||||||
dotNetUrl = s.DotNetUrl
|
dotNetUrl = s.DotNetUrl,
|
||||||
});
|
});
|
||||||
//Debug ($"\tsending {s.Url}");
|
//Log ("verbose", $"\tsending {s.Url}");
|
||||||
SendEvent ("Debugger.scriptParsed", ok, token);
|
SendEvent (sessionId, "Debugger.scriptParsed", ok, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
o = JObject.FromObject (new {
|
var o = JObject.FromObject (new {
|
||||||
expression = MonoCommands.CLEAR_ALL_BREAKPOINTS,
|
expression = MonoCommands.CLEAR_ALL_BREAKPOINTS,
|
||||||
objectGroup = "mono_debugger",
|
objectGroup = "mono_debugger",
|
||||||
includeCommandLineAPI = false,
|
includeCommandLineAPI = false,
|
||||||
|
|
@ -601,9 +626,9 @@ namespace WsProxy {
|
||||||
returnByValue = true,
|
returnByValue = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var clear_result = await SendCommand ("Runtime.evaluate", o, token);
|
var clear_result = await SendCommand (sessionId, "Runtime.evaluate", o, token);
|
||||||
if (clear_result.IsErr) {
|
if (clear_result.IsErr) {
|
||||||
Debug ($"Failed to clear breakpoints due to {clear_result}");
|
Log ("verbose", $"Failed to clear breakpoints due to {clear_result}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -612,19 +637,19 @@ namespace WsProxy {
|
||||||
foreach (var bp in breakpoints) {
|
foreach (var bp in breakpoints) {
|
||||||
if (bp.State != BreakPointState.Pending)
|
if (bp.State != BreakPointState.Pending)
|
||||||
continue;
|
continue;
|
||||||
var res = await EnableBreakPoint (bp, token);
|
var res = await EnableBreakPoint (sessionId, bp, token);
|
||||||
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
|
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
|
||||||
|
|
||||||
//if we fail we just buble that to the IDE (and let it panic over it)
|
//if we fail we just buble that to the IDE (and let it panic over it)
|
||||||
if (!ret_code.HasValue) {
|
if (!ret_code.HasValue) {
|
||||||
//FIXME figure out how to inform the IDE of that.
|
//FIXME figure out how to inform the IDE of that.
|
||||||
Info ($"FAILED TO ENABLE BP {bp.LocalId}");
|
Log ("info", $"FAILED TO ENABLE BP {bp.LocalId}");
|
||||||
bp.State = BreakPointState.Disabled;
|
bp.State = BreakPointState.Disabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<bool> RemoveBreakpoint(int msg_id, JObject args, CancellationToken token) {
|
async Task<bool> RemoveBreakpoint(MessageId msg_id, JObject args, CancellationToken token) {
|
||||||
var bpid = args? ["breakpointId"]?.Value<string> ();
|
var bpid = args? ["breakpointId"]?.Value<string> ();
|
||||||
if (bpid?.StartsWith ("dotnet:") != true)
|
if (bpid?.StartsWith ("dotnet:") != true)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -633,19 +658,19 @@ namespace WsProxy {
|
||||||
|
|
||||||
var bp = breakpoints.FirstOrDefault (b => b.LocalId == the_id);
|
var bp = breakpoints.FirstOrDefault (b => b.LocalId == the_id);
|
||||||
if (bp == null) {
|
if (bp == null) {
|
||||||
Info ($"Could not find dotnet bp with id {the_id}");
|
Log ("info", $"Could not find dotnet bp with id {the_id}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
breakpoints.Remove (bp);
|
breakpoints.Remove (bp);
|
||||||
//FIXME verify result (and log?)
|
//FIXME verify result (and log?)
|
||||||
var res = await RemoveBreakPoint (bp, token);
|
var res = await RemoveBreakPoint (msg_id, bp, token);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async Task<Result> RemoveBreakPoint (Breakpoint bp, CancellationToken token)
|
async Task<Result> RemoveBreakPoint (SessionId sessionId, Breakpoint bp, CancellationToken token)
|
||||||
{
|
{
|
||||||
var o = JObject.FromObject (new {
|
var o = JObject.FromObject (new {
|
||||||
expression = string.Format (MonoCommands.REMOVE_BREAK_POINT, bp.RemoteId),
|
expression = string.Format (MonoCommands.REMOVE_BREAK_POINT, bp.RemoteId),
|
||||||
|
|
@ -655,7 +680,7 @@ namespace WsProxy {
|
||||||
returnByValue = true,
|
returnByValue = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
var res = await SendCommand ("Runtime.evaluate", o, token);
|
var res = await SendCommand (sessionId, "Runtime.evaluate", o, token);
|
||||||
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
|
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
|
||||||
|
|
||||||
if (ret_code.HasValue) {
|
if (ret_code.HasValue) {
|
||||||
|
|
@ -666,13 +691,13 @@ namespace WsProxy {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task SetBreakPoint (int msg_id, BreakPointRequest req, CancellationToken token)
|
async Task SetBreakPoint (MessageId msg_id, BreakPointRequest req, CancellationToken token)
|
||||||
{
|
{
|
||||||
var bp_loc = store.FindBestBreakpoint (req);
|
var bp_loc = store?.FindBestBreakpoint (req);
|
||||||
Info ($"BP request for '{req}' runtime ready {runtime_ready} location '{bp_loc}'");
|
Log ("info", $"BP request for '{req}' runtime ready {runtime_ready} location '{bp_loc}'");
|
||||||
if (bp_loc == null) {
|
if (bp_loc == null) {
|
||||||
|
|
||||||
Info ($"Could not resolve breakpoint request: {req}");
|
Log ("info", $"Could not resolve breakpoint request: {req}");
|
||||||
SendResponse (msg_id, Result.Err(JObject.FromObject (new {
|
SendResponse (msg_id, Result.Err(JObject.FromObject (new {
|
||||||
code = (int)MonoErrorCodes.BpNotFound,
|
code = (int)MonoErrorCodes.BpNotFound,
|
||||||
message = $"C# Breakpoint at {req} not found."
|
message = $"C# Breakpoint at {req} not found."
|
||||||
|
|
@ -686,7 +711,7 @@ namespace WsProxy {
|
||||||
} else {
|
} else {
|
||||||
bp = new Breakpoint (bp_loc, local_breakpoint_id++, BreakPointState.Disabled);
|
bp = new Breakpoint (bp_loc, local_breakpoint_id++, BreakPointState.Disabled);
|
||||||
|
|
||||||
var res = await EnableBreakPoint (bp, token);
|
var res = await EnableBreakPoint (msg_id, bp, token);
|
||||||
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
|
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
|
||||||
|
|
||||||
//if we fail we just buble that to the IDE (and let it panic over it)
|
//if we fail we just buble that to the IDE (and let it panic over it)
|
||||||
|
|
@ -714,7 +739,7 @@ namespace WsProxy {
|
||||||
SendResponse (msg_id, Result.Ok (ok), token);
|
SendResponse (msg_id, Result.Ok (ok), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetPossibleBreakpoints (int msg_id, SourceLocation start, SourceLocation end, CancellationToken token)
|
bool GetPossibleBreakpoints (MessageId msg_id, SourceLocation start, SourceLocation end, CancellationToken token)
|
||||||
{
|
{
|
||||||
var bps = store.FindPossibleBreakpoints (start, end);
|
var bps = store.FindPossibleBreakpoints (start, end);
|
||||||
if (bps == null)
|
if (bps == null)
|
||||||
|
|
@ -734,15 +759,14 @@ namespace WsProxy {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnCompileDotnetScript (int msg_id, CancellationToken token)
|
void OnCompileDotnetScript (MessageId msg_id, CancellationToken token)
|
||||||
{
|
{
|
||||||
var o = JObject.FromObject (new { });
|
var o = JObject.FromObject (new { });
|
||||||
|
|
||||||
SendResponse (msg_id, Result.Ok (o), token);
|
SendResponse (msg_id, Result.Ok (o), token);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task OnGetScriptSource (int msg_id, string script_id, CancellationToken token)
|
async Task OnGetScriptSource (MessageId msg_id, string script_id, CancellationToken token)
|
||||||
{
|
{
|
||||||
var id = new SourceId (script_id);
|
var id = new SourceId (script_id);
|
||||||
var src_file = store.GetFileById (id);
|
var src_file = store.GetFileById (id);
|
||||||
|
|
@ -753,6 +777,16 @@ namespace WsProxy {
|
||||||
try {
|
try {
|
||||||
var uri = new Uri (src_file.Url);
|
var uri = new Uri (src_file.Url);
|
||||||
if (uri.IsFile && File.Exists(uri.LocalPath)) {
|
if (uri.IsFile && File.Exists(uri.LocalPath)) {
|
||||||
|
using (var f = new StreamReader (File.Open (uri.LocalPath, FileMode.Open))) {
|
||||||
|
await res.WriteAsync (await f.ReadToEndAsync ());
|
||||||
|
}
|
||||||
|
|
||||||
|
var o = JObject.FromObject (new {
|
||||||
|
scriptSource = res.ToString ()
|
||||||
|
});
|
||||||
|
|
||||||
|
SendResponse (msg_id, Result.Ok (o), token);
|
||||||
|
} else if (src_file.SourceUri.IsFile && File.Exists(src_file.SourceUri.LocalPath)) {
|
||||||
using (var f = new StreamReader (File.Open (src_file.SourceUri.LocalPath, FileMode.Open))) {
|
using (var f = new StreamReader (File.Open (src_file.SourceUri.LocalPath, FileMode.Open))) {
|
||||||
await res.WriteAsync (await f.ReadToEndAsync ());
|
await res.WriteAsync (await f.ReadToEndAsync ());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue