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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWeb
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.Tools", "src\Components\WebAssembly\Sdk\tools\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj", "{175E5CD8-92D4-46BB-882E-3A930D3302D4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Watch.BrowserRefresh", "src\Tools\dotnet-watch\BrowserRefresh\src\Microsoft.AspNetCore.Watch.BrowserRefresh.csproj", "{A5CE25E9-89E1-4F2C-9B89-0C161707E700}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Watch.BrowserRefresh.Tests", "src\Tools\dotnet-watch\BrowserRefresh\test\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests.csproj", "{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -6843,6 +6847,30 @@ Global
{175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x64.Build.0 = Release|Any CPU
{175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x86.ActiveCfg = Release|Any CPU
{175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x86.Build.0 = Release|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x64.ActiveCfg = Debug|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x64.Build.0 = Debug|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x86.ActiveCfg = Debug|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Debug|x86.Build.0 = Debug|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|Any CPU.Build.0 = Release|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x64.ActiveCfg = Release|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x64.Build.0 = Release|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x86.ActiveCfg = Release|Any CPU
{A5CE25E9-89E1-4F2C-9B89-0C161707E700}.Release|x86.Build.0 = Release|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x64.ActiveCfg = Debug|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x64.Build.0 = Debug|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x86.ActiveCfg = Debug|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Debug|x86.Build.0 = Debug|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|Any CPU.Build.0 = Release|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x64.ActiveCfg = Release|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x64.Build.0 = Release|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x86.ActiveCfg = Release|Any CPU
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -7567,6 +7595,8 @@ Global
{83371889-9A3E-4D16-AE77-EB4F83BC6374} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE}
{525EBCB4-A870-470B-BC90-845306C337D1} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE}
{175E5CD8-92D4-46BB-882E-3A930D3302D4} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE}
{A5CE25E9-89E1-4F2C-9B89-0C161707E700} = {B6118E15-C37A-4B05-B4DF-97FE99790417}
{E6A23627-8D63-4DF1-A4F2-8881172C1FE6} = {B6118E15-C37A-4B05-B4DF-97FE99790417}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
<KnownFrameworkReference Condition="'$(UseAspNetCoreSharedRuntime)' != 'true'" Remove="Microsoft.AspNetCore.App" />
<KnownFrameworkReference Remove="Microsoft.WindowsDesktop.App" />

<KnownFrameworkReference Condition="'$(UseAspNetCoreSharedRuntime)' == 'true'" Update="Microsoft.AspNetCore.App">
<KnownFrameworkReference Condition="'$(UseAspNetCoreSharedRuntime)' == 'true' AND '$(DoNotApplyWorkaroundsToMicrosoftAspNetCoreApp)' != 'true'" Update="Microsoft.AspNetCore.App">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI @wtgodbe

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remind me what the motivation was for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wanted a middleware that targeted netcoreapp3.1 and compiled against M.AspNetCore.App. UseAspNetCoreSharedRuntime is required to keep the shared runtime reference around, but I didn't want to compile against 5.0.0-dev.

<LatestRuntimeFrameworkVersion>$(SharedFxVersion)</LatestRuntimeFrameworkVersion>
<DefaultRuntimeFrameworkVersion Condition="'$(IsServicingBuild)' != 'true'">$(SharedFxVersion)</DefaultRuntimeFrameworkVersion>
<TargetingPackVersion Condition="'$(IsServicingBuild)' != 'true'">$(SharedFxVersion)</TargetingPackVersion>
Expand Down
51 changes: 51 additions & 0 deletions THIRD-PARTY-NOTICES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,54 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

License notice for West Wind Live Reload ASP.NET Core Middleware
=============================================


MIT License
-----------

Copyright (c) 2019-2020 West Wind Technologies

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

License notice for cli-spinners
=============================================

MIT License

Copyright (c) Sindre Sorhus <[email protected]> (https://sindresorhus.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
1 change: 1 addition & 0 deletions eng/ProjectReferences.props
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
<ProjectReferenceProvider Include="Microsoft.AspNetCore.CookiePolicy" ProjectPath="$(RepoRoot)src\Security\CookiePolicy\src\Microsoft.AspNetCore.CookiePolicy.csproj" />
<ProjectReferenceProvider Include="Microsoft.Web.Xdt.Extensions" ProjectPath="$(RepoRoot)src\SiteExtensions\Microsoft.Web.Xdt.Extensions\src\Microsoft.Web.Xdt.Extensions.csproj" />
<ProjectReferenceProvider Include="dotnet-getdocument" ProjectPath="$(RepoRoot)src\Tools\dotnet-getdocument\src\dotnet-getdocument.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Watch.BrowserRefresh" ProjectPath="$(RepoRoot)src\Tools\dotnet-watch\BrowserRefresh\src\Microsoft.AspNetCore.Watch.BrowserRefresh.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Client" ProjectPath="$(RepoRoot)src\Tools\Extensions.ApiDescription.Client\src\Microsoft.Extensions.ApiDescription.Client.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Server" ProjectPath="$(RepoRoot)src\Tools\Extensions.ApiDescription.Server\src\Microsoft.Extensions.ApiDescription.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" ProjectPath="$(RepoRoot)src\Tools\FirstRunCertGenerator\src\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj" />
Expand Down
2 changes: 1 addition & 1 deletion eng/Workarounds.targets
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
The web sdk adds an implicit framework reference. This removes it until we can update our build to use framework references.
-->
<ItemGroup>
<FrameworkReference Remove="Microsoft.AspNetCore.App" />
<FrameworkReference Remove="Microsoft.AspNetCore.App" Condition="'$(DoNotApplyWorkaroundsToMicrosoftAspNetCoreApp)' != 'true'" />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI @wtgodbe

<!-- Required because the Razor SDK will generate attributes -->
<Reference Include="Microsoft.AspNetCore.Mvc" Condition="'$(UsingMicrosoftNETSdkWeb)' == 'true' AND '$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(GenerateRazorAssemblyInfo)' == 'true'" />
</ItemGroup>
Expand Down
6 changes: 5 additions & 1 deletion src/Tools/Tools.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
"src\\Tools\\dotnet-user-secrets\\src\\dotnet-user-secrets.csproj",
"src\\Tools\\dotnet-user-secrets\\test\\dotnet-user-secrets.Tests.csproj",
"src\\Tools\\dotnet-watch\\src\\dotnet-watch.csproj",
"src\\Tools\\dotnet-watch\\test\\dotnet-watch.Tests.csproj"
"src\\Tools\\dotnet-watch\\BrowserRefresh\\src\\Microsoft.AspNetCore.Watch.BrowserRefresh.csproj",
"src\\Tools\\dotnet-watch\\BrowserRefresh\\test\\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests.csproj",
"src\\Tools\\dotnet-watch\\test\\dotnet-watch.Tests.csproj",
"src\\Middleware\\StaticFiles\\src\\Microsoft.AspNetCore.StaticFiles.csproj",
"src\\Hosting\\TestHost\\src\\Microsoft.AspNetCore.TestHost.csproj"
]
}
}
107 changes: 107 additions & 0 deletions src/Tools/dotnet-watch/BrowserRefresh/src/BrowserRefreshMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;

namespace Microsoft.AspNetCore.Watch.BrowserRefresh
{
public class BrowserRefreshMiddleware
{
private static readonly MediaTypeHeaderValue _textHtmlMediaType = new MediaTypeHeaderValue("text/html");
private readonly RequestDelegate _next;
private readonly ILogger _logger;

public BrowserRefreshMiddleware(RequestDelegate next, ILogger<BrowserRefreshMiddleware> logger) =>
(_next, _logger) = (next, logger);

public async Task InvokeAsync(HttpContext context)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I disable this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are environment switches for dotnet-watch to turn this off:

DOTNET_WATCH_SUPPRESS_BROWSER_REFRESH=0. I should update the documentation for this.

{
// We only need to support this for requests that could be initiated by a browser.
if (IsBrowserRequest(context))
{
// Use a custom StreamWrapper to rewrite output on Write/WriteAsync
using var responseStreamWrapper = new ResponseStreamWrapper(context, _logger);
var originalBodyFeature = context.Features.Get<IHttpResponseBodyFeature>();
context.Features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(responseStreamWrapper));

try
{
await _next(context);
}
finally
{
context.Features.Set(originalBodyFeature);
}

if (responseStreamWrapper.IsHtmlResponse && _logger.IsEnabled(LogLevel.Debug))
{
if (responseStreamWrapper.ScriptInjectionPerformed)
{
Log.BrowserConfiguredForRefreshes(_logger);
}
else
{
Log.FailedToConfiguredForRefreshes(_logger);
}
}
}
else
{
await _next(context);
}
}

internal static bool IsBrowserRequest(HttpContext context)
{
var request = context.Request;
if (!HttpMethods.IsGet(request.Method) && !HttpMethods.IsPost(request.Method))
{
return false;
}

var typedHeaders = request.GetTypedHeaders();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you do a fast path check for a text\html content type

if (!(typedHeaders.Accept is IList<MediaTypeHeaderValue> acceptHeaders))
{
return false;
}

for (var i = 0; i < acceptHeaders.Count; i++)
{
if (acceptHeaders[i].IsSubsetOf(_textHtmlMediaType))
{
return true;
}
}

return false;
}

internal static class Log
{
private static readonly Action<ILogger, Exception?> _setupResponseForBrowserRefresh = LoggerMessage.Define(
LogLevel.Debug,
new EventId(1, "SetUpResponseForBrowserRefresh"),
"Response markup is scheduled to include browser refresh script injection.");

private static readonly Action<ILogger, Exception?> _browserConfiguredForRefreshes = LoggerMessage.Define(
LogLevel.Debug,
new EventId(2, "BrowserConfiguredForRefreshes"),
"Response markup was updated to include browser refresh script injection.");

private static readonly Action<ILogger, Exception?> _failedToConfigureForRefreshes = LoggerMessage.Define(
LogLevel.Debug,
new EventId(3, "FailedToConfiguredForRefreshes"),
"Unable to configure browser refresh script injection on the response.");

public static void SetupResponseForBrowserRefresh(ILogger logger) => _setupResponseForBrowserRefresh(logger, null);
public static void BrowserConfiguredForRefreshes(ILogger logger) => _browserConfiguredForRefreshes(logger, null);
public static void FailedToConfiguredForRefreshes(ILogger logger) => _failedToConfigureForRefreshes(logger, null);
}
}
}
30 changes: 30 additions & 0 deletions src/Tools/dotnet-watch/BrowserRefresh/src/HostingFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

[assembly: HostingStartup(typeof(Microsoft.AspNetCore.Watch.BrowserRefresh.HostingStartup))]

namespace Microsoft.AspNetCore.Watch.BrowserRefresh
{
internal sealed class HostingStartup : IHostingStartup, IStartupFilter
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices(services => services.TryAddEnumerable(ServiceDescriptor.Singleton<IStartupFilter>(this)));
}

public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseMiddleware<BrowserRefreshMiddleware>();
next(app);
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- This feature is supported in projects targeting 3.1 or later.-->
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<IsShipping>false</IsShipping>
<UseAspNetCoreSharedRuntime>true</UseAspNetCoreSharedRuntime>
<DoNotApplyWorkaroundsToMicrosoftAspNetCoreApp>true</DoNotApplyWorkaroundsToMicrosoftAspNetCoreApp>
<ExcludeFromSourceBuild>false</ExcludeFromSourceBuild>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<EmbeddedResource Include="WebSocketScriptInjection.js" />
</ItemGroup>

</Project>
Loading