diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 86a87beb59..3acc059820 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -14,7 +14,7 @@
/src/Hosting/ @tratcher @anurse
/src/Http/ @tratcher @jkotalik @anurse
/src/Middleware/ @tratcher @anurse
-/src/ProjectTemplates/ @ryanbrandenburg
+# /src/ProjectTemplates/ @ryanbrandenburg
/src/Security/ @tratcher @anurse
/src/Servers/ @tratcher @jkotalik @anurse @halter73
/src/Middleware/Rewrite @jkotalik @anurse
diff --git a/Directory.Build.props b/Directory.Build.props
index 06e288aa81..4b87f1845a 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -43,7 +43,7 @@
true
- netcoreapp$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion)
+ netcoreapp$(TFMNetCoreMajorVersion).$(TFMNetCoreMinorVersion)
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 6bbaaaf86f..a2b48e7e15 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -9,25 +9,25 @@
-->
-
+
https://github.com/aspnet/Blazor
- 498a843f7854f9a2a5820e55cd2c7d7654429682
+ b2c48dd8c9099f71908fac26089cbea2c76d06a1
-
+
https://github.com/aspnet/AspNetCore-Tooling
- 3e788675fd3dab5fddd7b9aba9225e5b5ebbd946
+ 448a88e86d20fd9315901f663318d64c9c6841bf
-
+
https://github.com/aspnet/AspNetCore-Tooling
- 3e788675fd3dab5fddd7b9aba9225e5b5ebbd946
+ 448a88e86d20fd9315901f663318d64c9c6841bf
-
+
https://github.com/aspnet/AspNetCore-Tooling
- 3e788675fd3dab5fddd7b9aba9225e5b5ebbd946
+ 448a88e86d20fd9315901f663318d64c9c6841bf
-
+
https://github.com/aspnet/AspNetCore-Tooling
- 3e788675fd3dab5fddd7b9aba9225e5b5ebbd946
+ 448a88e86d20fd9315901f663318d64c9c6841bf
https://github.com/aspnet/EntityFrameworkCore
diff --git a/eng/Versions.props b/eng/Versions.props
index 648fe409f8..9141b1a63c 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -6,12 +6,12 @@
-->
- 3
+ 5
0
0
- 9
- preview$(PreReleasePreviewNumber)
- Preview $(PreReleasePreviewNumber)
+ 1
+ alpha$(PreReleasePreviewNumber)
+ Alpha $(PreReleasePreviewNumber)
$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion)
false
@@ -38,6 +38,11 @@
false
+
+
+ 3
+ 0
+
3.0.0-preview9.19409.17
- 3.0.0-preview9.19405.1
+ 5.0.0-alpha1.19405.2
3.0.0-preview9.19411.2
3.0.0-preview9.19411.2
@@ -157,10 +162,10 @@
3.0.0-preview9.19412.3
3.0.0-preview9.19412.3
- 3.0.0-preview9.19412.2
- 3.0.0-preview9.19412.2
- 3.0.0-preview9.19412.2
- 3.0.0-preview9.19412.2
+ 5.0.0-alpha1.19407.1
+ 5.0.0-alpha1.19407.1
+ 5.0.0-alpha1.19407.1
+ 5.0.0-alpha1.19407.1
+
+
+
+ <_MonoLinkerDotNetPath>$(DOTNET_HOST_PATH)
+ <_MonoLinkerDotNetPath Condition="'$(_MonoLinkerDotNetPath)' == ''">dotnet
+
-
+
diff --git a/src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj b/src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj
index 8272cd539b..0f3cdaa077 100644
--- a/src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj
+++ b/src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/src/Framework/test/Microsoft.AspNetCore.App.UnitTests.csproj b/src/Framework/test/Microsoft.AspNetCore.App.UnitTests.csproj
index 8cde3262d9..9b5fd65bc0 100644
--- a/src/Framework/test/Microsoft.AspNetCore.App.UnitTests.csproj
+++ b/src/Framework/test/Microsoft.AspNetCore.App.UnitTests.csproj
@@ -5,6 +5,8 @@
Microsoft.AspNetCore
false
+
+ true
diff --git a/src/Middleware/SpaServices.Extensions/ref/Microsoft.AspNetCore.SpaServices.Extensions.netcoreapp3.0.cs b/src/Middleware/SpaServices.Extensions/ref/Microsoft.AspNetCore.SpaServices.Extensions.netcoreapp3.0.cs
index 97f23b740d..6691b79a64 100644
--- a/src/Middleware/SpaServices.Extensions/ref/Microsoft.AspNetCore.SpaServices.Extensions.netcoreapp3.0.cs
+++ b/src/Middleware/SpaServices.Extensions/ref/Microsoft.AspNetCore.SpaServices.Extensions.netcoreapp3.0.cs
@@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.SpaServices
public SpaOptions() { }
public Microsoft.AspNetCore.Http.PathString DefaultPage { get { throw null; } set { } }
public Microsoft.AspNetCore.Builder.StaticFileOptions DefaultPageStaticFileOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public string PackageManagerCommand { get { throw null; } set { } }
public string SourcePath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.TimeSpan StartupTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
diff --git a/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliBuilder.cs b/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliBuilder.cs
index fa3e9cc449..24f0ba430f 100644
--- a/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliBuilder.cs
+++ b/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliBuilder.cs
@@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
{
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine
- private readonly string _npmScriptName;
+ private readonly string _scriptName;
///
/// Constructs an instance of .
@@ -35,12 +35,13 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
throw new ArgumentException("Cannot be null or empty.", nameof(npmScript));
}
- _npmScriptName = npmScript;
+ _scriptName = npmScript;
}
///
public async Task Build(ISpaBuilder spaBuilder)
{
+ var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand;
var sourcePath = spaBuilder.Options.SourcePath;
if (string.IsNullOrEmpty(sourcePath))
{
@@ -50,32 +51,33 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
var logger = LoggerFinder.GetOrCreateLogger(
spaBuilder.ApplicationBuilder,
nameof(AngularCliBuilder));
- var npmScriptRunner = new NpmScriptRunner(
+ var scriptRunner = new NodeScriptRunner(
sourcePath,
- _npmScriptName,
+ _scriptName,
"--watch",
- null);
- npmScriptRunner.AttachToLogger(logger);
+ null,
+ pkgManagerCommand);
+ scriptRunner.AttachToLogger(logger);
- using (var stdOutReader = new EventedStreamStringReader(npmScriptRunner.StdOut))
- using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr))
+ using (var stdOutReader = new EventedStreamStringReader(scriptRunner.StdOut))
+ using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr))
{
try
{
- await npmScriptRunner.StdOut.WaitForMatch(
+ await scriptRunner.StdOut.WaitForMatch(
new Regex("Date", RegexOptions.None, RegexMatchTimeout));
}
catch (EndOfStreamException ex)
{
throw new InvalidOperationException(
- $"The NPM script '{_npmScriptName}' exited without indicating success.\n" +
+ $"The {pkgManagerCommand} script '{_scriptName}' exited without indicating success.\n" +
$"Output was: {stdOutReader.ReadAsString()}\n" +
$"Error output was: {stdErrReader.ReadAsString()}", ex);
}
catch (OperationCanceledException ex)
{
throw new InvalidOperationException(
- $"The NPM script '{_npmScriptName}' timed out without indicating success. " +
+ $"The {pkgManagerCommand} script '{_scriptName}' timed out without indicating success. " +
$"Output was: {stdOutReader.ReadAsString()}\n" +
$"Error output was: {stdErrReader.ReadAsString()}", ex);
}
diff --git a/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliMiddleware.cs b/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliMiddleware.cs
index 9090f7738b..c4e109b8f7 100644
--- a/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliMiddleware.cs
+++ b/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliMiddleware.cs
@@ -23,23 +23,24 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
public static void Attach(
ISpaBuilder spaBuilder,
- string npmScriptName)
+ string scriptName)
{
+ var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand;
var sourcePath = spaBuilder.Options.SourcePath;
if (string.IsNullOrEmpty(sourcePath))
{
throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
}
- if (string.IsNullOrEmpty(npmScriptName))
+ if (string.IsNullOrEmpty(scriptName))
{
- throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
+ throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
}
// Start Angular CLI and attach to middleware pipeline
var appBuilder = spaBuilder.ApplicationBuilder;
var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
- var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, npmScriptName, logger);
+ var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, scriptName, pkgManagerCommand, logger);
// Everything we proxy is hardcoded to target http://localhost because:
// - the requests are always from the local machine (we're not accepting remote
@@ -62,27 +63,27 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
}
private static async Task StartAngularCliServerAsync(
- string sourcePath, string npmScriptName, ILogger logger)
+ string sourcePath, string scriptName, string pkgManagerCommand, ILogger logger)
{
var portNumber = TcpPortFinder.FindAvailablePort();
logger.LogInformation($"Starting @angular/cli on port {portNumber}...");
- var npmScriptRunner = new NpmScriptRunner(
- sourcePath, npmScriptName, $"--port {portNumber}", null);
- npmScriptRunner.AttachToLogger(logger);
+ var scriptRunner = new NodeScriptRunner(
+ sourcePath, scriptName, $"--port {portNumber}", null, pkgManagerCommand);
+ scriptRunner.AttachToLogger(logger);
Match openBrowserLine;
- using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr))
+ using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr))
{
try
{
- openBrowserLine = await npmScriptRunner.StdOut.WaitForMatch(
+ openBrowserLine = await scriptRunner.StdOut.WaitForMatch(
new Regex("open your browser on (http\\S+)", RegexOptions.None, RegexMatchTimeout));
}
catch (EndOfStreamException ex)
{
throw new InvalidOperationException(
- $"The NPM script '{npmScriptName}' exited without indicating that the " +
+ $"The {pkgManagerCommand} script '{scriptName}' exited without indicating that the " +
$"Angular CLI was listening for requests. The error output was: " +
$"{stdErrReader.ReadAsString()}", ex);
}
diff --git a/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliMiddlewareExtensions.cs b/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliMiddlewareExtensions.cs
index 28e63c8e35..8f8176447b 100644
--- a/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliMiddlewareExtensions.cs
+++ b/src/Middleware/SpaServices.Extensions/src/AngularCli/AngularCliMiddlewareExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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 Microsoft.AspNetCore.Builder;
diff --git a/src/Middleware/SpaServices.Extensions/src/Npm/NpmScriptRunner.cs b/src/Middleware/SpaServices.Extensions/src/Npm/NodeScriptRunner.cs
similarity index 77%
rename from src/Middleware/SpaServices.Extensions/src/Npm/NpmScriptRunner.cs
rename to src/Middleware/SpaServices.Extensions/src/Npm/NodeScriptRunner.cs
index 378ec5f9fa..f08abeb19c 100644
--- a/src/Middleware/SpaServices.Extensions/src/Npm/NpmScriptRunner.cs
+++ b/src/Middleware/SpaServices.Extensions/src/Npm/NodeScriptRunner.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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 Microsoft.Extensions.Logging;
@@ -16,14 +16,14 @@ namespace Microsoft.AspNetCore.NodeServices.Npm
/// Executes the script entries defined in a package.json file,
/// capturing any output written to stdio.
///
- internal class NpmScriptRunner
+ internal class NodeScriptRunner
{
public EventedStreamReader StdOut { get; }
public EventedStreamReader StdErr { get; }
private static Regex AnsiColorRegex = new Regex("\x001b\\[[0-9;]*m", RegexOptions.None, TimeSpan.FromSeconds(1));
- public NpmScriptRunner(string workingDirectory, string scriptName, string arguments, IDictionary envVars)
+ public NodeScriptRunner(string workingDirectory, string scriptName, string arguments, IDictionary envVars, string pkgManagerCommand)
{
if (string.IsNullOrEmpty(workingDirectory))
{
@@ -35,18 +35,23 @@ namespace Microsoft.AspNetCore.NodeServices.Npm
throw new ArgumentException("Cannot be null or empty.", nameof(scriptName));
}
- var npmExe = "npm";
+ if (string.IsNullOrEmpty(pkgManagerCommand))
+ {
+ throw new ArgumentException("Cannot be null or empty.", nameof(pkgManagerCommand));
+ }
+
+ var exeToRun = pkgManagerCommand;
var completeArguments = $"run {scriptName} -- {arguments ?? string.Empty}";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- // On Windows, the NPM executable is a .cmd file, so it can't be executed
+ // On Windows, the node executable is a .cmd file, so it can't be executed
// directly (except with UseShellExecute=true, but that's no good, because
// it prevents capturing stdio). So we need to invoke it via "cmd /c".
- npmExe = "cmd";
- completeArguments = $"/c npm {completeArguments}";
+ exeToRun = "cmd";
+ completeArguments = $"/c {pkgManagerCommand} {completeArguments}";
}
- var processStartInfo = new ProcessStartInfo(npmExe)
+ var processStartInfo = new ProcessStartInfo(exeToRun)
{
Arguments = completeArguments,
UseShellExecute = false,
@@ -64,19 +69,19 @@ namespace Microsoft.AspNetCore.NodeServices.Npm
}
}
- var process = LaunchNodeProcess(processStartInfo);
+ var process = LaunchNodeProcess(processStartInfo, pkgManagerCommand);
StdOut = new EventedStreamReader(process.StandardOutput);
StdErr = new EventedStreamReader(process.StandardError);
}
public void AttachToLogger(ILogger logger)
{
- // When the NPM task emits complete lines, pass them through to the real logger
+ // When the node task emits complete lines, pass them through to the real logger
StdOut.OnReceivedLine += line =>
{
if (!string.IsNullOrWhiteSpace(line))
{
- // NPM tasks commonly emit ANSI colors, but it wouldn't make sense to forward
+ // Node tasks commonly emit ANSI colors, but it wouldn't make sense to forward
// those to loggers (because a logger isn't necessarily any kind of terminal)
logger.LogInformation(StripAnsiColors(line));
}
@@ -106,7 +111,7 @@ namespace Microsoft.AspNetCore.NodeServices.Npm
private static string StripAnsiColors(string line)
=> AnsiColorRegex.Replace(line, string.Empty);
- private static Process LaunchNodeProcess(ProcessStartInfo startInfo)
+ private static Process LaunchNodeProcess(ProcessStartInfo startInfo, string commandName)
{
try
{
@@ -119,8 +124,8 @@ namespace Microsoft.AspNetCore.NodeServices.Npm
}
catch (Exception ex)
{
- var message = $"Failed to start 'npm'. To resolve this:.\n\n"
- + "[1] Ensure that 'npm' is installed and can be found in one of the PATH directories.\n"
+ var message = $"Failed to start '{commandName}'. To resolve this:.\n\n"
+ + $"[1] Ensure that '{commandName}' is installed and can be found in one of the PATH directories.\n"
+ $" Current PATH enviroment variable is: { Environment.GetEnvironmentVariable("PATH") }\n"
+ " Make sure the executable is in one of those directories, or update your PATH.\n\n"
+ "[2] See the InnerException for further details of the cause.";
diff --git a/src/Middleware/SpaServices.Extensions/src/ReactDevelopmentServer/ReactDevelopmentServerMiddleware.cs b/src/Middleware/SpaServices.Extensions/src/ReactDevelopmentServer/ReactDevelopmentServerMiddleware.cs
index 78a7b4f03f..6566fef706 100644
--- a/src/Middleware/SpaServices.Extensions/src/ReactDevelopmentServer/ReactDevelopmentServerMiddleware.cs
+++ b/src/Middleware/SpaServices.Extensions/src/ReactDevelopmentServer/ReactDevelopmentServerMiddleware.cs
@@ -22,23 +22,24 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
public static void Attach(
ISpaBuilder spaBuilder,
- string npmScriptName)
+ string scriptName)
{
+ var pkgManagerCommand = spaBuilder.Options.PackageManagerCommand;
var sourcePath = spaBuilder.Options.SourcePath;
if (string.IsNullOrEmpty(sourcePath))
{
throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
}
- if (string.IsNullOrEmpty(npmScriptName))
+ if (string.IsNullOrEmpty(scriptName))
{
- throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
+ throw new ArgumentException("Cannot be null or empty", nameof(scriptName));
}
// Start create-react-app and attach to middleware pipeline
var appBuilder = spaBuilder.ApplicationBuilder;
var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
- var portTask = StartCreateReactAppServerAsync(sourcePath, npmScriptName, logger);
+ var portTask = StartCreateReactAppServerAsync(sourcePath, scriptName, pkgManagerCommand, logger);
// Everything we proxy is hardcoded to target http://localhost because:
// - the requests are always from the local machine (we're not accepting remote
@@ -61,7 +62,7 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
}
private static async Task StartCreateReactAppServerAsync(
- string sourcePath, string npmScriptName, ILogger logger)
+ string sourcePath, string scriptName, string pkgManagerCommand, ILogger logger)
{
var portNumber = TcpPortFinder.FindAvailablePort();
logger.LogInformation($"Starting create-react-app server on port {portNumber}...");
@@ -71,11 +72,11 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
{ "PORT", portNumber.ToString() },
{ "BROWSER", "none" }, // We don't want create-react-app to open its own extra browser window pointing to the internal dev server port
};
- var npmScriptRunner = new NpmScriptRunner(
- sourcePath, npmScriptName, null, envVars);
- npmScriptRunner.AttachToLogger(logger);
+ var scriptRunner = new NodeScriptRunner(
+ sourcePath, scriptName, null, envVars, pkgManagerCommand);
+ scriptRunner.AttachToLogger(logger);
- using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr))
+ using (var stdErrReader = new EventedStreamStringReader(scriptRunner.StdErr))
{
try
{
@@ -83,13 +84,13 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
// it doesn't do so until it's finished compiling, and even then only if there were
// no compiler warnings. So instead of waiting for that, consider it ready as soon
// as it starts listening for requests.
- await npmScriptRunner.StdOut.WaitForMatch(
+ await scriptRunner.StdOut.WaitForMatch(
new Regex("Starting the development server", RegexOptions.None, RegexMatchTimeout));
}
catch (EndOfStreamException ex)
{
throw new InvalidOperationException(
- $"The NPM script '{npmScriptName}' exited without indicating that the " +
+ $"The {pkgManagerCommand} script '{scriptName}' exited without indicating that the " +
$"create-react-app server was listening for requests. The error output was: " +
$"{stdErrReader.ReadAsString()}", ex);
}
diff --git a/src/Middleware/SpaServices.Extensions/src/ReactDevelopmentServer/ReactDevelopmentServerMiddlewareExtensions.cs b/src/Middleware/SpaServices.Extensions/src/ReactDevelopmentServer/ReactDevelopmentServerMiddlewareExtensions.cs
index f58a6d1a9d..346e839046 100644
--- a/src/Middleware/SpaServices.Extensions/src/ReactDevelopmentServer/ReactDevelopmentServerMiddlewareExtensions.cs
+++ b/src/Middleware/SpaServices.Extensions/src/ReactDevelopmentServer/ReactDevelopmentServerMiddlewareExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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 Microsoft.AspNetCore.Builder;
diff --git a/src/Middleware/SpaServices.Extensions/src/SpaOptions.cs b/src/Middleware/SpaServices.Extensions/src/SpaOptions.cs
index b2823396dc..59ccc1eda4 100644
--- a/src/Middleware/SpaServices.Extensions/src/SpaOptions.cs
+++ b/src/Middleware/SpaServices.Extensions/src/SpaOptions.cs
@@ -1,11 +1,10 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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.AspNetCore.Http;
-using Microsoft.Extensions.FileProviders;
-using System;
namespace Microsoft.AspNetCore.SpaServices
{
@@ -15,6 +14,7 @@ namespace Microsoft.AspNetCore.SpaServices
public class SpaOptions
{
private PathString _defaultPage = "/index.html";
+ private string _packageManagerCommand = "npm";
///
/// Constructs a new instance of .
@@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.SpaServices
internal SpaOptions(SpaOptions copyFromOptions)
{
_defaultPage = copyFromOptions.DefaultPage;
+ _packageManagerCommand = copyFromOptions.PackageManagerCommand;
DefaultPageStaticFileOptions = copyFromOptions.DefaultPageStaticFileOptions;
SourcePath = copyFromOptions.SourcePath;
}
@@ -69,6 +70,26 @@ namespace Microsoft.AspNetCore.SpaServices
///
public string SourcePath { get; set; }
+ ///
+ /// Gets or sets the name of the package manager executible, (e.g npm,
+ /// yarn) to run the SPA.
+ ///
+ /// The default value is 'npm'.
+ ///
+ public string PackageManagerCommand
+ {
+ get => _packageManagerCommand;
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ throw new ArgumentException($"The value for {nameof(PackageManagerCommand)} cannot be null or empty.");
+ }
+
+ _packageManagerCommand = value;
+ }
+ }
+
///
/// Gets or sets the maximum duration that a request will wait for the SPA
/// to become ready to serve to the client.
diff --git a/src/Middleware/StaticFiles/ref/Microsoft.AspNetCore.StaticFiles.netcoreapp3.0.cs b/src/Middleware/StaticFiles/ref/Microsoft.AspNetCore.StaticFiles.netcoreapp3.0.cs
index 424801bb14..155d816cb8 100644
--- a/src/Middleware/StaticFiles/ref/Microsoft.AspNetCore.StaticFiles.netcoreapp3.0.cs
+++ b/src/Middleware/StaticFiles/ref/Microsoft.AspNetCore.StaticFiles.netcoreapp3.0.cs
@@ -120,12 +120,14 @@ namespace Microsoft.AspNetCore.StaticFiles.Infrastructure
{
public SharedOptions() { }
public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public bool RedirectToAppendTrailingSlash { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public Microsoft.AspNetCore.Http.PathString RequestPath { get { throw null; } set { } }
}
public abstract partial class SharedOptionsBase
{
protected SharedOptionsBase(Microsoft.AspNetCore.StaticFiles.Infrastructure.SharedOptions sharedOptions) { }
public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { get { throw null; } set { } }
+ public bool RedirectToAppendTrailingSlash { get { throw null; } set { } }
public Microsoft.AspNetCore.Http.PathString RequestPath { get { throw null; } set { } }
protected Microsoft.AspNetCore.StaticFiles.Infrastructure.SharedOptions SharedOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
diff --git a/src/Middleware/StaticFiles/src/DefaultFilesMiddleware.cs b/src/Middleware/StaticFiles/src/DefaultFilesMiddleware.cs
index 3aabe2fe65..b3b4755789 100644
--- a/src/Middleware/StaticFiles/src/DefaultFilesMiddleware.cs
+++ b/src/Middleware/StaticFiles/src/DefaultFilesMiddleware.cs
@@ -80,17 +80,13 @@ namespace Microsoft.AspNetCore.StaticFiles
{
// If the path matches a directory but does not end in a slash, redirect to add the slash.
// This prevents relative links from breaking.
- if (!Helpers.PathEndsInSlash(context.Request.Path))
+ if (!Helpers.PathEndsInSlash(context.Request.Path) && _options.RedirectToAppendTrailingSlash)
{
- context.Response.StatusCode = StatusCodes.Status301MovedPermanently;
- var request = context.Request;
- var redirect = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path + "/", request.QueryString);
- context.Response.Headers[HeaderNames.Location] = redirect;
+ Helpers.RedirectToPathWithSlash(context);
return Task.CompletedTask;
}
-
// Match found, re-write the url. A later middleware will actually serve the file.
- context.Request.Path = new PathString(context.Request.Path.Value + defaultFile);
+ context.Request.Path = new PathString(Helpers.GetPathValueWithSlash(context.Request.Path) + defaultFile);
break;
}
}
diff --git a/src/Middleware/StaticFiles/src/DirectoryBrowserMiddleware.cs b/src/Middleware/StaticFiles/src/DirectoryBrowserMiddleware.cs
index 2d0a07b509..e689b309e4 100644
--- a/src/Middleware/StaticFiles/src/DirectoryBrowserMiddleware.cs
+++ b/src/Middleware/StaticFiles/src/DirectoryBrowserMiddleware.cs
@@ -87,12 +87,9 @@ namespace Microsoft.AspNetCore.StaticFiles
{
// If the path matches a directory but does not end in a slash, redirect to add the slash.
// This prevents relative links from breaking.
- if (!Helpers.PathEndsInSlash(context.Request.Path))
+ if (!Helpers.PathEndsInSlash(context.Request.Path) && _options.RedirectToAppendTrailingSlash)
{
- context.Response.StatusCode = StatusCodes.Status301MovedPermanently;
- var request = context.Request;
- var redirect = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path + "/", request.QueryString);
- context.Response.Headers[HeaderNames.Location] = redirect;
+ Helpers.RedirectToPathWithSlash(context);
return Task.CompletedTask;
}
diff --git a/src/Middleware/StaticFiles/src/Helpers.cs b/src/Middleware/StaticFiles/src/Helpers.cs
index a7a49e9070..d9b29c082f 100644
--- a/src/Middleware/StaticFiles/src/Helpers.cs
+++ b/src/Middleware/StaticFiles/src/Helpers.cs
@@ -2,9 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.FileProviders;
+using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.StaticFiles
{
@@ -12,7 +15,8 @@ namespace Microsoft.AspNetCore.StaticFiles
{
internal static IFileProvider ResolveFileProvider(IWebHostEnvironment hostingEnv)
{
- if (hostingEnv.WebRootFileProvider == null) {
+ if (hostingEnv.WebRootFileProvider == null)
+ {
throw new InvalidOperationException("Missing FileProvider.");
}
return hostingEnv.WebRootFileProvider;
@@ -28,6 +32,23 @@ namespace Microsoft.AspNetCore.StaticFiles
return path.Value.EndsWith("/", StringComparison.Ordinal);
}
+ internal static string GetPathValueWithSlash(PathString path)
+ {
+ if (!PathEndsInSlash(path))
+ {
+ return path.Value + "/";
+ }
+ return path.Value;
+ }
+
+ internal static void RedirectToPathWithSlash(HttpContext context)
+ {
+ context.Response.StatusCode = StatusCodes.Status301MovedPermanently;
+ var request = context.Request;
+ var redirect = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path + "/", request.QueryString);
+ context.Response.Headers[HeaderNames.Location] = redirect;
+ }
+
internal static bool TryMatchPath(HttpContext context, PathString matchUrl, bool forDirectory, out PathString subpath)
{
var path = context.Request.Path;
diff --git a/src/Middleware/StaticFiles/src/Infrastructure/SharedOptions.cs b/src/Middleware/StaticFiles/src/Infrastructure/SharedOptions.cs
index 1c1cc80ad5..d6f08129a1 100644
--- a/src/Middleware/StaticFiles/src/Infrastructure/SharedOptions.cs
+++ b/src/Middleware/StaticFiles/src/Infrastructure/SharedOptions.cs
@@ -42,5 +42,10 @@ namespace Microsoft.AspNetCore.StaticFiles.Infrastructure
/// The file system used to locate resources
///
public IFileProvider FileProvider { get; set; }
+
+ ///
+ /// Indicates whether to redirect to add a trailing slash at the end of path. Relative resource links may require this.
+ ///
+ public bool RedirectToAppendTrailingSlash { get; set; } = true;
}
}
diff --git a/src/Middleware/StaticFiles/src/Infrastructure/SharedOptionsBase.cs b/src/Middleware/StaticFiles/src/Infrastructure/SharedOptionsBase.cs
index 16900ec6fb..9e41b96cdc 100644
--- a/src/Middleware/StaticFiles/src/Infrastructure/SharedOptionsBase.cs
+++ b/src/Middleware/StaticFiles/src/Infrastructure/SharedOptionsBase.cs
@@ -48,5 +48,14 @@ namespace Microsoft.AspNetCore.StaticFiles.Infrastructure
get { return SharedOptions.FileProvider; }
set { SharedOptions.FileProvider = value; }
}
+
+ ///
+ /// Indicates whether to redirect to add a trailing slash at the end of path. Relative resource links may require this.
+ ///
+ public bool RedirectToAppendTrailingSlash
+ {
+ get { return SharedOptions.RedirectToAppendTrailingSlash; }
+ set { SharedOptions.RedirectToAppendTrailingSlash = value; }
+ }
}
}
diff --git a/src/Middleware/StaticFiles/test/UnitTests/DefaultFilesMiddlewareTests.cs b/src/Middleware/StaticFiles/test/UnitTests/DefaultFilesMiddlewareTests.cs
index ed6947593b..9207ae844d 100644
--- a/src/Middleware/StaticFiles/test/UnitTests/DefaultFilesMiddlewareTests.cs
+++ b/src/Middleware/StaticFiles/test/UnitTests/DefaultFilesMiddlewareTests.cs
@@ -38,9 +38,14 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("/subdir", @".", "/subdir/missing.dir")]
[InlineData("/subdir", @".", "/subdir/missing.dir/")]
[InlineData("", @"./", "/missing.dir")]
- public async Task NoMatch_PassesThrough_All(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("", @".", "/missing.dir", false)]
+ [InlineData("", @".", "/missing.dir/", false)]
+ [InlineData("/subdir", @".", "/subdir/missing.dir", false)]
+ [InlineData("/subdir", @".", "/subdir/missing.dir/", false)]
+ [InlineData("", @"./", "/missing.dir", false)]
+ public async Task NoMatch_PassesThrough_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl);
+ await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
[ConditionalTheory]
@@ -48,12 +53,14 @@ namespace Microsoft.AspNetCore.StaticFiles
[OSSkipCondition(OperatingSystems.MacOSX)]
[InlineData("", @".\", "/missing.dir")]
[InlineData("", @".\", "/Missing.dir")]
- public async Task NoMatch_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("", @".\", "/missing.dir", false)]
+ [InlineData("", @".\", "/Missing.dir", false)]
+ public async Task NoMatch_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl);
+ await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
- private async Task NoMatch_PassesThrough(string baseUrl, string baseDir, string requestUrl)
+ private async Task NoMatch_PassesThrough(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
{
@@ -62,7 +69,8 @@ namespace Microsoft.AspNetCore.StaticFiles
app.UseDefaultFiles(new DefaultFilesOptions
{
RequestPath = new PathString(baseUrl),
- FileProvider = fileProvider
+ FileProvider = fileProvider,
+ RedirectToAppendTrailingSlash = appendTrailingSlash
});
app.Run(context => context.Response.WriteAsync(context.Request.Path.Value));
});
@@ -102,7 +110,7 @@ namespace Microsoft.AspNetCore.StaticFiles
FileProvider = fileProvider
});
- app.UseEndpoints(endpoints => {});
+ app.UseEndpoints(endpoints => { });
},
services => { services.AddDirectoryBrowser(); services.AddRouting(); });
@@ -118,9 +126,19 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("", @"./SubFolder", "/")]
[InlineData("", @"./SubFolder", "/你好/")]
[InlineData("", @"./SubFolder", "/你好/世界/")]
- public async Task FoundDirectoryWithDefaultFile_PathModified_All(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("", @".", "/SubFolder/", false)]
+ [InlineData("", @"./", "/SubFolder/", false)]
+ [InlineData("", @"./SubFolder", "/", false)]
+ [InlineData("", @"./SubFolder", "/你好/", false)]
+ [InlineData("", @"./SubFolder", "/你好/世界/", false)]
+ [InlineData("", @".", "/SubFolder", false)]
+ [InlineData("", @"./", "/SubFolder", false)]
+ [InlineData("", @"./SubFolder", "", false)]
+ [InlineData("", @"./SubFolder", "/你好", false)]
+ [InlineData("", @"./SubFolder", "/你好/世界", false)]
+ public async Task FoundDirectoryWithDefaultFile_PathModified_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await FoundDirectoryWithDefaultFile_PathModified(baseUrl, baseDir, requestUrl);
+ await FoundDirectoryWithDefaultFile_PathModified(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
[ConditionalTheory]
@@ -130,12 +148,20 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("", @".\subFolder", "/")]
[InlineData("", @".\SubFolder", "/你好/")]
[InlineData("", @".\SubFolder", "/你好/世界/")]
- public async Task FoundDirectoryWithDefaultFile_PathModified_Windows(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("", @".\", "/SubFolder/", false)]
+ [InlineData("", @".\subFolder", "/", false)]
+ [InlineData("", @".\SubFolder", "/你好/", false)]
+ [InlineData("", @".\SubFolder", "/你好/世界/", false)]
+ [InlineData("", @".\", "/SubFolder", false)]
+ [InlineData("", @".\subFolder", "", false)]
+ [InlineData("", @".\SubFolder", "/你好", false)]
+ [InlineData("", @".\SubFolder", "/你好/世界", false)]
+ public async Task FoundDirectoryWithDefaultFile_PathModified_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await FoundDirectoryWithDefaultFile_PathModified(baseUrl, baseDir, requestUrl);
+ await FoundDirectoryWithDefaultFile_PathModified(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
- private async Task FoundDirectoryWithDefaultFile_PathModified(string baseUrl, string baseDir, string requestUrl)
+ private async Task FoundDirectoryWithDefaultFile_PathModified(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
{
@@ -144,14 +170,17 @@ namespace Microsoft.AspNetCore.StaticFiles
app.UseDefaultFiles(new DefaultFilesOptions
{
RequestPath = new PathString(baseUrl),
- FileProvider = fileProvider
+ FileProvider = fileProvider,
+ RedirectToAppendTrailingSlash = appendTrailingSlash
});
app.Run(context => context.Response.WriteAsync(context.Request.Path.Value));
});
var response = await server.CreateClient().GetAsync(requestUrl);
+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- Assert.Equal(requestUrl + "default.html", await response.Content.ReadAsStringAsync()); // Should be modified
+ var requestUrlWithSlash = requestUrl.EndsWith("/") ? requestUrl : requestUrl + "/";
+ Assert.Equal(requestUrlWithSlash + "default.html", await response.Content.ReadAsStringAsync()); // Should be modified and be valid path to file
}
}
@@ -202,9 +231,17 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("/SubFolder", @".", "/somedir/")]
[InlineData("", @"./SubFolder", "/")]
[InlineData("", @"./SubFolder/", "/")]
- public async Task PostDirectory_PassesThrough_All(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("/SubFolder", @"./", "/SubFolder/", false)]
+ [InlineData("/SubFolder", @".", "/somedir/", false)]
+ [InlineData("", @"./SubFolder", "/", false)]
+ [InlineData("", @"./SubFolder/", "/", false)]
+ [InlineData("/SubFolder", @"./", "/SubFolder", false)]
+ [InlineData("/SubFolder", @".", "/somedir", false)]
+ [InlineData("", @"./SubFolder", "", false)]
+ [InlineData("", @"./SubFolder/", "", false)]
+ public async Task PostDirectory_PassesThrough_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl);
+ await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
[ConditionalTheory]
@@ -213,24 +250,37 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("/SubFolder", @".\", "/SubFolder/")]
[InlineData("", @".\SubFolder", "/")]
[InlineData("", @".\SubFolder\", "/")]
- public async Task PostDirectory_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("/SubFolder", @".\", "/SubFolder/", false)]
+ [InlineData("", @".\SubFolder", "/", false)]
+ [InlineData("", @".\SubFolder\", "/", false)]
+ [InlineData("/SubFolder", @".\", "/SubFolder", false)]
+ [InlineData("", @".\SubFolder", "", false)]
+ [InlineData("", @".\SubFolder\", "", false)]
+ public async Task PostDirectory_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl);
+ await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
- private async Task PostDirectory_PassesThrough(string baseUrl, string baseDir, string requestUrl)
+ private async Task PostDirectory_PassesThrough(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
{
var server = StaticFilesTestServer.Create(app => app.UseDefaultFiles(new DefaultFilesOptions
{
RequestPath = new PathString(baseUrl),
- FileProvider = fileProvider
+ FileProvider = fileProvider,
+ RedirectToAppendTrailingSlash = appendTrailingSlash
}));
var response = await server.CreateRequest(requestUrl).GetAsync();
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); // Passed through
}
}
+
+ [Fact]
+ public void Options_AppendTrailingSlashByDefault()
+ {
+ Assert.True(new DefaultFilesOptions().RedirectToAppendTrailingSlash);
+ }
}
}
diff --git a/src/Middleware/StaticFiles/test/UnitTests/DirectoryBrowserMiddlewareTests.cs b/src/Middleware/StaticFiles/test/UnitTests/DirectoryBrowserMiddlewareTests.cs
index 3bbdabfe30..8cf6d0c34c 100644
--- a/src/Middleware/StaticFiles/test/UnitTests/DirectoryBrowserMiddlewareTests.cs
+++ b/src/Middleware/StaticFiles/test/UnitTests/DirectoryBrowserMiddlewareTests.cs
@@ -56,9 +56,14 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("/subdir", @".", "/subdir/missing.dir")]
[InlineData("/subdir", @".", "/subdir/missing.dir/")]
[InlineData("", @"./", "/missing.dir")]
- public async Task NoMatch_PassesThrough_All(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("", @".", "/missing.dir", false)]
+ [InlineData("", @".", "/missing.dir/", false)]
+ [InlineData("/subdir", @".", "/subdir/missing.dir", false)]
+ [InlineData("/subdir", @".", "/subdir/missing.dir/", false)]
+ [InlineData("", @"./", "/missing.dir", false)]
+ public async Task NoMatch_PassesThrough_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl);
+ await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
[ConditionalTheory]
@@ -66,12 +71,14 @@ namespace Microsoft.AspNetCore.StaticFiles
[OSSkipCondition(OperatingSystems.MacOSX)]
[InlineData("", @".\", "/missing.dir")]
[InlineData("", @".\", "/Missing.dir")]
- public async Task NoMatch_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("", @".\", "/missing.dir", false)]
+ [InlineData("", @".\", "/Missing.dir", false)]
+ public async Task NoMatch_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl);
+ await NoMatch_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
- private async Task NoMatch_PassesThrough(string baseUrl, string baseDir, string requestUrl)
+ private async Task NoMatch_PassesThrough(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
{
@@ -79,7 +86,8 @@ namespace Microsoft.AspNetCore.StaticFiles
app => app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
RequestPath = new PathString(baseUrl),
- FileProvider = fileProvider
+ FileProvider = fileProvider,
+ RedirectToAppendTrailingSlash = appendTrailingSlash
}),
services => services.AddDirectoryBrowser());
var response = await server.CreateRequest(requestUrl).GetAsync();
@@ -117,7 +125,7 @@ namespace Microsoft.AspNetCore.StaticFiles
FileProvider = fileProvider
});
- app.UseEndpoints(endpoints => {});
+ app.UseEndpoints(endpoints => { });
},
services => { services.AddDirectoryBrowser(); services.AddRouting(); });
@@ -133,9 +141,19 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("/somedir", @".", "/somedir/")]
[InlineData("/somedir", @"./", "/somedir/")]
[InlineData("/somedir", @".", "/somedir/SubFolder/")]
- public async Task FoundDirectory_Served_All(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("", @".", "/", false)]
+ [InlineData("", @".", "/SubFolder/", false)]
+ [InlineData("/somedir", @".", "/somedir/", false)]
+ [InlineData("/somedir", @"./", "/somedir/", false)]
+ [InlineData("/somedir", @".", "/somedir/SubFolder/", false)]
+ [InlineData("", @".", "", false)]
+ [InlineData("", @".", "/SubFolder", false)]
+ [InlineData("/somedir", @".", "/somedir", false)]
+ [InlineData("/somedir", @"./", "/somedir", false)]
+ [InlineData("/somedir", @".", "/somedir/SubFolder", false)]
+ public async Task FoundDirectory_Served_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await FoundDirectory_Served(baseUrl, baseDir, requestUrl);
+ await FoundDirectory_Served(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
[ConditionalTheory]
@@ -143,12 +161,16 @@ namespace Microsoft.AspNetCore.StaticFiles
[OSSkipCondition(OperatingSystems.MacOSX)]
[InlineData("/somedir", @".\", "/somedir/")]
[InlineData("/somedir", @".", "/somedir/subFolder/")]
- public async Task FoundDirectory_Served_Windows(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("/somedir", @".\", "/somedir/", false)]
+ [InlineData("/somedir", @".", "/somedir/subFolder/", false)]
+ [InlineData("/somedir", @".\", "/somedir", false)]
+ [InlineData("/somedir", @".", "/somedir/subFolder", false)]
+ public async Task FoundDirectory_Served_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await FoundDirectory_Served(baseUrl, baseDir, requestUrl);
+ await FoundDirectory_Served(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
- private async Task FoundDirectory_Served(string baseUrl, string baseDir, string requestUrl)
+ private async Task FoundDirectory_Served(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
{
@@ -156,7 +178,8 @@ namespace Microsoft.AspNetCore.StaticFiles
app => app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
RequestPath = new PathString(baseUrl),
- FileProvider = fileProvider
+ FileProvider = fileProvider,
+ RedirectToAppendTrailingSlash = appendTrailingSlash,
}),
services => services.AddDirectoryBrowser());
var response = await server.CreateRequest(requestUrl).GetAsync();
@@ -215,21 +238,31 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("", @".", "/SubFolder/")]
[InlineData("/somedir", @".", "/somedir/")]
[InlineData("/somedir", @".", "/somedir/SubFolder/")]
- public async Task PostDirectory_PassesThrough_All(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("", @".", "/", false)]
+ [InlineData("", @".", "/SubFolder/", false)]
+ [InlineData("/somedir", @".", "/somedir/", false)]
+ [InlineData("/somedir", @".", "/somedir/SubFolder/", false)]
+ [InlineData("", @".", "", false)]
+ [InlineData("", @".", "/SubFolder", false)]
+ [InlineData("/somedir", @".", "/somedir", false)]
+ [InlineData("/somedir", @".", "/somedir/SubFolder", false)]
+ public async Task PostDirectory_PassesThrough_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl);
+ await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
[ConditionalTheory]
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[InlineData("/somedir", @".", "/somedir/subFolder/")]
- public async Task PostDirectory_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("/somedir", @".", "/somedir/subFolder/", false)]
+ [InlineData("/somedir", @".", "/somedir/subFolder", false)]
+ public async Task PostDirectory_PassesThrough_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl);
+ await PostDirectory_PassesThrough(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
- private async Task PostDirectory_PassesThrough(string baseUrl, string baseDir, string requestUrl)
+ private async Task PostDirectory_PassesThrough(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
{
@@ -237,7 +270,8 @@ namespace Microsoft.AspNetCore.StaticFiles
app => app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
RequestPath = new PathString(baseUrl),
- FileProvider = fileProvider
+ FileProvider = fileProvider,
+ RedirectToAppendTrailingSlash = appendTrailingSlash
}),
services => services.AddDirectoryBrowser());
@@ -251,21 +285,31 @@ namespace Microsoft.AspNetCore.StaticFiles
[InlineData("", @".", "/SubFolder/")]
[InlineData("/somedir", @".", "/somedir/")]
[InlineData("/somedir", @".", "/somedir/SubFolder/")]
- public async Task HeadDirectory_HeadersButNotBodyServed_All(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("", @".", "/", false)]
+ [InlineData("", @".", "/SubFolder/", false)]
+ [InlineData("/somedir", @".", "/somedir/", false)]
+ [InlineData("/somedir", @".", "/somedir/SubFolder/", false)]
+ [InlineData("", @".", "", false)]
+ [InlineData("", @".", "/SubFolder", false)]
+ [InlineData("/somedir", @".", "/somedir", false)]
+ [InlineData("/somedir", @".", "/somedir/SubFolder", false)]
+ public async Task HeadDirectory_HeadersButNotBodyServed_All(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await HeadDirectory_HeadersButNotBodyServed(baseUrl, baseDir, requestUrl);
+ await HeadDirectory_HeadersButNotBodyServed(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
[ConditionalTheory]
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[InlineData("/somedir", @".", "/somedir/subFolder/")]
- public async Task HeadDirectory_HeadersButNotBodyServed_Windows(string baseUrl, string baseDir, string requestUrl)
+ [InlineData("/somedir", @".", "/somedir/subFolder/", false)]
+ [InlineData("/somedir", @".", "/somedir/subFolder", false)]
+ public async Task HeadDirectory_HeadersButNotBodyServed_Windows(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
- await HeadDirectory_HeadersButNotBodyServed(baseUrl, baseDir, requestUrl);
+ await HeadDirectory_HeadersButNotBodyServed(baseUrl, baseDir, requestUrl, appendTrailingSlash);
}
- private async Task HeadDirectory_HeadersButNotBodyServed(string baseUrl, string baseDir, string requestUrl)
+ private async Task HeadDirectory_HeadersButNotBodyServed(string baseUrl, string baseDir, string requestUrl, bool appendTrailingSlash = true)
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, baseDir)))
{
@@ -273,7 +317,8 @@ namespace Microsoft.AspNetCore.StaticFiles
app => app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
RequestPath = new PathString(baseUrl),
- FileProvider = fileProvider
+ FileProvider = fileProvider,
+ RedirectToAppendTrailingSlash = appendTrailingSlash
}),
services => services.AddDirectoryBrowser());
@@ -285,5 +330,11 @@ namespace Microsoft.AspNetCore.StaticFiles
Assert.Empty((await response.Content.ReadAsByteArrayAsync()));
}
}
+
+ [Fact]
+ public void Options_AppendTrailingSlashByDefault()
+ {
+ Assert.True(new DirectoryBrowserOptions().RedirectToAppendTrailingSlash);
+ }
}
}
diff --git a/src/Mvc/Mvc.Core/src/Routing/UrlHelperFactory.cs b/src/Mvc/Mvc.Core/src/Routing/UrlHelperFactory.cs
index dfe16c2421..5fffa77974 100644
--- a/src/Mvc/Mvc.Core/src/Routing/UrlHelperFactory.cs
+++ b/src/Mvc/Mvc.Core/src/Routing/UrlHelperFactory.cs
@@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
{
if (context == null)
{
- throw new ArgumentNullException(Resources.ArgumentCannotBeNullOrEmpty, (nameof(context)));
+ throw new ArgumentNullException(nameof(context));
}
var httpContext = context.HttpContext;
@@ -69,4 +69,4 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return urlHelper;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ProjectTemplates/scripts/Run-Angular-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Angular-Locally.ps1
index a807e5260c..ec8993da7e 100644
--- a/src/ProjectTemplates/scripts/Run-Angular-Locally.ps1
+++ b/src/ProjectTemplates/scripts/Run-Angular-Locally.ps1
@@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot\Test-Template.ps1
-Test-Template "angular" "angular" "Microsoft.DotNet.Web.Spa.ProjectTemplates.3.0.3.0.0-dev.nupkg" $true
+Test-Template "angular" "angular" "Microsoft.DotNet.Web.Spa.ProjectTemplates.5.0.5.0.0-dev.nupkg" $true
diff --git a/src/ProjectTemplates/scripts/Run-Blazor-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Blazor-Locally.ps1
index a7062f82a5..575036e9c7 100644
--- a/src/ProjectTemplates/scripts/Run-Blazor-Locally.ps1
+++ b/src/ProjectTemplates/scripts/Run-Blazor-Locally.ps1
@@ -10,4 +10,4 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot\Test-Template.ps1
-Test-Template "blazorserver" "blazorserver" "Microsoft.DotNet.Web.ProjectTemplates.3.0.3.0.0-dev.nupkg" $false
+Test-Template "blazorserver" "blazorserver" "Microsoft.DotNet.Web.ProjectTemplates.5.0.5.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-EmptyWeb-Locally.ps1 b/src/ProjectTemplates/scripts/Run-EmptyWeb-Locally.ps1
index b5684535cc..d6859c6f72 100644
--- a/src/ProjectTemplates/scripts/Run-EmptyWeb-Locally.ps1
+++ b/src/ProjectTemplates/scripts/Run-EmptyWeb-Locally.ps1
@@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot\Test-Template.ps1
-Test-Template "web" "web" "Microsoft.DotNet.Web.ProjectTemplates.3.0.3.0.0-dev.nupkg" $false
+Test-Template "web" "web" "Microsoft.DotNet.Web.ProjectTemplates.5.0.5.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-Razor-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Razor-Locally.ps1
index 72f84ad9cc..ecba4fbd8f 100644
--- a/src/ProjectTemplates/scripts/Run-Razor-Locally.ps1
+++ b/src/ProjectTemplates/scripts/Run-Razor-Locally.ps1
@@ -6,4 +6,4 @@ param()
. $PSScriptRoot\Test-Template.ps1
-Test-Template "webapp" "webapp -au Individual" "Microsoft.DotNet.Web.ProjectTemplates.3.0.3.0.0-dev.nupkg" $false
+Test-Template "webapp" "webapp -au Individual" "Microsoft.DotNet.Web.ProjectTemplates.5.0.5.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-React-Locally.ps1 b/src/ProjectTemplates/scripts/Run-React-Locally.ps1
index 28bca93b11..8308860edc 100644
--- a/src/ProjectTemplates/scripts/Run-React-Locally.ps1
+++ b/src/ProjectTemplates/scripts/Run-React-Locally.ps1
@@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot\Test-Template.ps1
-Test-Template "react" "react" "Microsoft.DotNet.Web.Spa.ProjectTemplates.3.0.3.0.0-dev.nupkg" $true
+Test-Template "react" "react" "Microsoft.DotNet.Web.Spa.ProjectTemplates.5.0.5.0.0-dev.nupkg" $true
diff --git a/src/ProjectTemplates/scripts/Run-ReactRedux-Locally.ps1 b/src/ProjectTemplates/scripts/Run-ReactRedux-Locally.ps1
index 19c1621dde..6100d7cacd 100644
--- a/src/ProjectTemplates/scripts/Run-ReactRedux-Locally.ps1
+++ b/src/ProjectTemplates/scripts/Run-ReactRedux-Locally.ps1
@@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot\Test-Template.ps1
-Test-Template "reactredux" "reactredux" "Microsoft.DotNet.Web.Spa.ProjectTemplates.3.0.3.0.0-dev.nupkg" $true
+Test-Template "reactredux" "reactredux" "Microsoft.DotNet.Web.Spa.ProjectTemplates.5.0.5.0.0-dev.nupkg" $true
diff --git a/src/ProjectTemplates/scripts/Run-Starterweb-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Starterweb-Locally.ps1
index c7adbbbf24..61ad47fbdc 100644
--- a/src/ProjectTemplates/scripts/Run-Starterweb-Locally.ps1
+++ b/src/ProjectTemplates/scripts/Run-Starterweb-Locally.ps1
@@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot\Test-Template.ps1
-Test-Template "mvc" "mvc -au Individual" "Microsoft.DotNet.Web.ProjectTemplates.3.0.3.0.0-dev.nupkg" $false
+Test-Template "mvc" "mvc -au Individual" "Microsoft.DotNet.Web.ProjectTemplates.5.0.5.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-Worker-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Worker-Locally.ps1
index 867750b6e9..e6ff856e7e 100644
--- a/src/ProjectTemplates/scripts/Run-Worker-Locally.ps1
+++ b/src/ProjectTemplates/scripts/Run-Worker-Locally.ps1
@@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot\Test-Template.ps1
-Test-Template "worker" "worker" "Microsoft.DotNet.Web.ProjectTemplates.3.0.3.0.0-dev.nupkg" $false
+Test-Template "worker" "worker" "Microsoft.DotNet.Web.ProjectTemplates.5.0.5.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/test/Helpers/TemplatePackageInstaller.cs b/src/ProjectTemplates/test/Helpers/TemplatePackageInstaller.cs
index c36a9d5dda..b22d743ed0 100644
--- a/src/ProjectTemplates/test/Helpers/TemplatePackageInstaller.cs
+++ b/src/ProjectTemplates/test/Helpers/TemplatePackageInstaller.cs
@@ -31,9 +31,11 @@ namespace Templates.Test.Helpers
"Microsoft.DotNet.Web.ProjectTemplates.2.1",
"Microsoft.DotNet.Web.ProjectTemplates.2.2",
"Microsoft.DotNet.Web.ProjectTemplates.3.0",
+ "Microsoft.DotNet.Web.ProjectTemplates.5.0",
"Microsoft.DotNet.Web.Spa.ProjectTemplates.2.1",
"Microsoft.DotNet.Web.Spa.ProjectTemplates.2.2",
"Microsoft.DotNet.Web.Spa.ProjectTemplates.3.0",
+ "Microsoft.DotNet.Web.Spa.ProjectTemplates.5.0",
"Microsoft.DotNet.Web.Spa.ProjectTemplates"
};
diff --git a/src/Servers/Kestrel/Core/src/BadHttpRequestException.cs b/src/Servers/Kestrel/Core/src/BadHttpRequestException.cs
index 929a408778..16f7ab0fce 100644
--- a/src/Servers/Kestrel/Core/src/BadHttpRequestException.cs
+++ b/src/Servers/Kestrel/Core/src/BadHttpRequestException.cs
@@ -139,6 +139,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
BadHttpRequestException ex;
switch (reason)
{
+ case RequestRejectionReason.TlsOverHttpError:
+ ex = new BadHttpRequestException(CoreStrings.HttpParserTlsOverHttpError, StatusCodes.Status400BadRequest, reason);
+ break;
case RequestRejectionReason.InvalidRequestLine:
ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_InvalidRequestLine_Detail(detail), StatusCodes.Status400BadRequest, reason);
break;
diff --git a/src/Servers/Kestrel/Core/src/CoreStrings.resx b/src/Servers/Kestrel/Core/src/CoreStrings.resx
index 0f49dedc81..20af4f4a9c 100644
--- a/src/Servers/Kestrel/Core/src/CoreStrings.resx
+++ b/src/Servers/Kestrel/Core/src/CoreStrings.resx
@@ -617,4 +617,7 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
A new stream was refused because this connection has too many streams that haven't finished processing. This may happen if many streams are aborted but not yet cleaned up.
-
+
+ Detected a TLS handshake to an endpoint that does not have TLS enabled.
+
+
\ No newline at end of file
diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs
index 2fe5dfdb36..ce63ec989f 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs
@@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private const byte ByteTab = (byte)'\t';
private const byte ByteQuestionMark = (byte)'?';
private const byte BytePercentage = (byte)'%';
+ private const int MinTlsRequestSize = 1; // We need at least 1 byte to check for a proper TLS request line
public unsafe bool ParseRequestLine(TRequestHandler handler, in ReadOnlySequence buffer, out SequencePosition consumed, out SequencePosition examined)
{
@@ -415,9 +416,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return new Span(data, methodLength);
}
+ private unsafe bool IsTlsHandshake(byte* data, int length)
+ {
+ const byte SslRecordTypeHandshake = (byte)0x16;
+
+ // Make sure we can check at least for the existence of a TLS handshake - we check the first byte
+ // See https://serializethoughts.com/2014/07/27/dissecting-tls-client-hello-message/
+
+ return (length >= MinTlsRequestSize && data[0] == SslRecordTypeHandshake);
+ }
+
[StackTraceHidden]
private unsafe void RejectRequestLine(byte* requestLine, int length)
- => throw GetInvalidRequestException(RequestRejectionReason.InvalidRequestLine, requestLine, length);
+ {
+ // Check for incoming TLS handshake over HTTP
+ if (IsTlsHandshake(requestLine, length))
+ {
+ throw GetInvalidRequestException(RequestRejectionReason.TlsOverHttpError, requestLine, length);
+ }
+ else
+ {
+ throw GetInvalidRequestException(RequestRejectionReason.InvalidRequestLine, requestLine, length);
+ }
+ }
[StackTraceHidden]
private unsafe void RejectRequestHeader(byte* headerLine, int length)
diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/RequestRejectionReason.cs b/src/Servers/Kestrel/Core/src/Internal/Http/RequestRejectionReason.cs
index fce21b6210..23dc6c67c6 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Http/RequestRejectionReason.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/Http/RequestRejectionReason.cs
@@ -1,10 +1,11 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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.
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
internal enum RequestRejectionReason
{
+ TlsOverHttpError,
UnrecognizedHTTPVersion,
InvalidRequestLine,
InvalidRequestHeader,
diff --git a/src/Servers/Kestrel/Core/test/HttpParserTests.cs b/src/Servers/Kestrel/Core/test/HttpParserTests.cs
index 7ce8587743..82d69d8b4d 100644
--- a/src/Servers/Kestrel/Core/test/HttpParserTests.cs
+++ b/src/Servers/Kestrel/Core/test/HttpParserTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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;
@@ -394,6 +394,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Equal(buffer.End, examined);
}
+ [Fact]
+ public void ParseRequestLineTlsOverHttp()
+ {
+ var parser = CreateParser(_nullTrace);
+ var buffer = ReadOnlySequenceFactory.CreateSegments(new byte[] { 0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0xfc, 0x03, 0x03, 0x03, 0xca, 0xe0, 0xfd, 0x0a });
+
+ var requestHandler = new RequestHandler();
+
+ var badHttpRequestException = Assert.Throws(() =>
+ {
+ parser.ParseRequestLine(requestHandler, buffer, out var consumed, out var examined);
+ });
+
+ Assert.Equal(badHttpRequestException.StatusCode, StatusCodes.Status400BadRequest);
+ Assert.Equal(RequestRejectionReason.TlsOverHttpError, badHttpRequestException.Reason);
+ }
+
[Fact]
public void ParseHeadersWithGratuitouslySplitBuffers()
{
diff --git a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj
index 736295fb4c..e287d4c869 100644
--- a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj
+++ b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj
@@ -32,7 +32,7 @@
-
+
diff --git a/src/SignalR/clients/ts/signalr-protocol-msgpack/package.json b/src/SignalR/clients/ts/signalr-protocol-msgpack/package.json
index 3d262cc056..d53083aa27 100644
--- a/src/SignalR/clients/ts/signalr-protocol-msgpack/package.json
+++ b/src/SignalR/clients/ts/signalr-protocol-msgpack/package.json
@@ -1,6 +1,6 @@
{
"name": "@microsoft/signalr-protocol-msgpack",
- "version": "3.0.0-dev",
+ "version": "5.0.0-dev",
"description": "MsgPack Protocol support for ASP.NET Core SignalR",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
diff --git a/src/SignalR/clients/ts/signalr/package.json b/src/SignalR/clients/ts/signalr/package.json
index 6b50f24d35..7fcfc9fb29 100644
--- a/src/SignalR/clients/ts/signalr/package.json
+++ b/src/SignalR/clients/ts/signalr/package.json
@@ -1,6 +1,6 @@
{
"name": "@microsoft/signalr",
- "version": "3.0.0-dev",
+ "version": "5.0.0-dev",
"description": "ASP.NET Core SignalR Client",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
diff --git a/src/SignalR/common/Http.Connections.Common/src/Microsoft.AspNetCore.Http.Connections.Common.csproj b/src/SignalR/common/Http.Connections.Common/src/Microsoft.AspNetCore.Http.Connections.Common.csproj
index 2f2b3f3256..f5bff681e6 100644
--- a/src/SignalR/common/Http.Connections.Common/src/Microsoft.AspNetCore.Http.Connections.Common.csproj
+++ b/src/SignalR/common/Http.Connections.Common/src/Microsoft.AspNetCore.Http.Connections.Common.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/src/SignalR/common/Protocols.Json/src/Microsoft.AspNetCore.SignalR.Protocols.Json.csproj b/src/SignalR/common/Protocols.Json/src/Microsoft.AspNetCore.SignalR.Protocols.Json.csproj
index 4c10f93b02..006e72b164 100644
--- a/src/SignalR/common/Protocols.Json/src/Microsoft.AspNetCore.SignalR.Protocols.Json.csproj
+++ b/src/SignalR/common/Protocols.Json/src/Microsoft.AspNetCore.SignalR.Protocols.Json.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/src/SignalR/common/SignalR.Common/src/Microsoft.AspNetCore.SignalR.Common.csproj b/src/SignalR/common/SignalR.Common/src/Microsoft.AspNetCore.SignalR.Common.csproj
index 7a50628e44..83db221ce4 100644
--- a/src/SignalR/common/SignalR.Common/src/Microsoft.AspNetCore.SignalR.Common.csproj
+++ b/src/SignalR/common/SignalR.Common/src/Microsoft.AspNetCore.SignalR.Common.csproj
@@ -29,7 +29,7 @@
-
+
diff --git a/src/SiteExtensions/LoggingAggregate/src/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj b/src/SiteExtensions/LoggingAggregate/src/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj
index 7054d0908d..7b880e889b 100644
--- a/src/SiteExtensions/LoggingAggregate/src/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj
+++ b/src/SiteExtensions/LoggingAggregate/src/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj
@@ -23,8 +23,8 @@
-
-
+
+