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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.server.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.webassembly.js

Large diffs are not rendered by default.

33 changes: 23 additions & 10 deletions src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade
return BINDING.js_to_mono_obj(Promise.resolve(0));
}

const lazyResources: {
assemblies?: (ArrayBuffer | null)[],
pdbs?: (ArrayBuffer | null)[]
} = {};
window['Blazor']._internal.getLazyAssemblies = (assembliesToLoadDotNetArray: System_Array<System_String>): System_Object => {
const assembliesToLoad = BINDING.mono_array_to_js_array<System_String, string>(assembliesToLoadDotNetArray);
const lazyAssemblies = resourceLoader.bootConfig.resources.lazyAssembly;
Expand Down Expand Up @@ -364,31 +368,40 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade
.map(assembly => resourceLoader.loadResource(assembly, `_framework/${assembly}`, lazyAssemblies[assembly], 'assembly'))
.map(async resource => (await resource.response).arrayBuffer()));


return BINDING.js_to_mono_obj(
Promise.all([resourcePromises, pdbPromises]).then(values => {
const resourcesToLoad = values[0];
const pdbsToLoad = values[1];
if (resourcesToLoad.length) {
lazyResources["assemblies"] = values[0];
lazyResources["pdbs"] = values[1];
if (lazyResources["assemblies"].length) {
window['Blazor']._internal.readLazyAssemblies = () => {
const assemblyBytes = BINDING.mono_obj_array_new(resourcesToLoad.length);
for (let i = 0; i < resourcesToLoad.length; i++) {
const assembly = resourcesToLoad[i] as ArrayBuffer;
const { assemblies } = lazyResources;
if (!assemblies) {
return BINDING.mono_obj_array_new(0);
}
const assemblyBytes = BINDING.mono_obj_array_new(assemblies.length);
for (let i = 0; i < assemblies.length; i++) {
const assembly = assemblies[i] as ArrayBuffer;
BINDING.mono_obj_array_set(assemblyBytes, i, BINDING.js_typed_array_to_array(new Uint8Array(assembly)));
}
return assemblyBytes;
};

window['Blazor']._internal.readLazyPdbs = () => {
const pdbBytes = BINDING.mono_obj_array_new(resourcesToLoad.length);
for (let i = 0; i < resourcesToLoad.length; i++) {
const pdb = pdbsToLoad && pdbsToLoad[i] ? new Uint8Array(pdbsToLoad[i] as ArrayBufferLike) : new Uint8Array();
const { assemblies, pdbs } = lazyResources;
if (!assemblies) {
return BINDING.mono_obj_array_new(0);
}
const pdbBytes = BINDING.mono_obj_array_new(assemblies.length);
for (let i = 0; i < assemblies.length; i++) {
const pdb = pdbs && pdbs[i] ? new Uint8Array(pdbs[i] as ArrayBufferLike) : new Uint8Array();
BINDING.mono_obj_array_set(pdbBytes, i, BINDING.js_typed_array_to_array(pdb));
}
return pdbBytes;
};
}

return resourcesToLoad.length;
return lazyResources["assemblies"].length;
}));
}
});
Expand Down
39 changes: 36 additions & 3 deletions src/Components/test/E2ETest/Tests/WebAssemblyLazyLoadTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void CanLazyLoadOnRouteChange()
// Visit the route for the lazy-loaded assembly
SetUrlViaPushState("/WithLazyAssembly");

var button = app.FindElement(By.Id("use-package-button"));
var button = Browser.Exists(By.Id("use-package-button"));

// Now we should have requested the DLL
Assert.True(HasLoadedAssembly("Newtonsoft.Json.dll"));
Expand Down Expand Up @@ -100,11 +100,11 @@ public void CanLazyLoadAssemblyWithRoutes()
// Now the assembly has been loaded
Assert.True(HasLoadedAssembly("LazyTestContentPackage.dll"));

var button = app.FindElement(By.Id("go-to-lazy-route"));
var button = Browser.Exists(By.Id("go-to-lazy-route"));
button.Click();

// Navigating the lazy-loaded route should show its content
var renderedElement = app.FindElement(By.Id("lazy-page"));
var renderedElement = Browser.Exists(By.Id("lazy-page"));
Assert.True(renderedElement.Displayed);
}

Expand All @@ -123,6 +123,39 @@ public void ThrowsErrorForUnavailableAssemblies()
AssertLogContainsCriticalMessages("DoesNotExist.dll must be marked with 'BlazorWebAssemblyLazyLoad' item group in your project file to allow lazy-loading.");
}

[Fact]
public void CanLazyLoadViaLinkChange()
{
// Navigate to a page without any lazy-loaded dependencies
SetUrlViaPushState("/");
var app = Browser.MountTestComponent<TestRouterWithLazyAssembly>();

// We start off with no lazy assemblies loaded
Assert.False(HasLoadedAssembly("LazyTestContentPackage.dll"));
Assert.False(HasLoadedAssembly("Newtonsoft.Json.dll"));

// Click the first link and verify that it worked as expected
var lazyAssemblyLink = Browser.Exists(By.Id("with-lazy-assembly"));
lazyAssemblyLink.Click();
var pkgButton = Browser.Exists(By.Id("use-package-button"));
Assert.True(HasLoadedAssembly("Newtonsoft.Json.dll"));
pkgButton.Click();

// Navigate to the next page and verify that it loaded its assembly
var lazyRoutesLink = Browser.Exists(By.Id("with-lazy-routes"));
lazyRoutesLink.Click();
Browser.Exists(By.Id("lazy-load-msg"));
Assert.True(HasLoadedAssembly("LazyTestContentPackage.dll"));

// Interact with that assembly to ensure it was loaded properly
var button = Browser.Exists(By.Id("go-to-lazy-route"));
button.Click();

// Navigating the lazy-loaded route should show its content
var renderedElement = Browser.Exists(By.Id("lazy-page"));
Assert.True(renderedElement.Displayed);
}

private string SetUrlViaPushState(string relativeUri)
{
var pathBaseWithoutHash = ServerPathBase.Split('#')[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
<li><NavLink href="/subdir/WithParameters/Name/Abc/LastName/McDef">With more parameters</NavLink></li>
<li><NavLink href="/subdir/LongPage1">Long page 1</NavLink></li>
<li><NavLink href="/subdir/LongPage2">Long page 2</NavLink></li>
<li><NavLink href="/subdir/WithLazyAssembly">With lazy assembly</NavLink></li>
<li><NavLink href="/subdir/WithLazyAssembly" id="with-lazy-assembly">With lazy assembly</NavLink></li>
<li><NavLink href="/subdir/WithLazyLoadedRoutes" id="with-lazy-routes">With lazy loaded routes</NavLink></li>
<li><NavLink href="PreventDefaultCases">preventDefault cases</NavLink></li>
<li><NavLink>Null href never matches</NavLink></li>
</ul>
Expand Down
26 changes: 25 additions & 1 deletion src/Servers/HttpSys/src/AuthenticationSchemes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,39 @@

namespace Microsoft.AspNetCore.Server.HttpSys
{
// REVIEW: this appears to be very similar to System.Net.AuthenticationSchemes
/// <summary>
/// Specifies protocols for authentication.
/// </summary>
[Flags]
public enum AuthenticationSchemes
{
/// <summary>
/// No authentication is enabled. This should only be used when HttpSysOptions.Authentication.AllowAnonymous is enabled (see <see cref="AuthenticationManager.AllowAnonymous"/>).
/// </summary>
None = 0x0,

/// <summary>
/// Specifies basic authentication.
/// </summary>
Basic = 0x1,


// Digest = 0x2, // TODO: Verify this is no longer supported by Http.Sys

/// <summary>
/// Specifies NTLM authentication.
/// </summary>
NTLM = 0x4,

/// <summary>
/// Negotiates with the client to determine the authentication scheme. If both client and server support Kerberos, it is used;
/// otherwise, NTLM is used.
/// </summary>
Negotiate = 0x8,

/// <summary>
/// Specifies Kerberos authentication.
/// </summary>
Kerberos = 0x10
}
}
1 change: 1 addition & 0 deletions src/Servers/HttpSys/src/DelegationRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ internal DelegationRule(string queueName, string urlPrefix, ILogger logger)
Queue = new RequestQueue(queueName, UrlPrefix, _logger, receiver: true);
}

/// <inheritdoc />
public void Dispose()
{
Queue.UrlGroup?.Dispose();
Expand Down
3 changes: 3 additions & 0 deletions src/Servers/HttpSys/src/HttpSysDefaults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

namespace Microsoft.AspNetCore.Server.HttpSys
{
/// <summary>
/// Constants for HttpSys.
/// </summary>
public static class HttpSysDefaults
{
/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions src/Servers/HttpSys/src/HttpSysException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

namespace Microsoft.AspNetCore.Server.HttpSys
{
/// <summary>
/// Exception thrown by HttpSys when an error occurs
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")]
public class HttpSysException : Win32Exception
{
Expand All @@ -28,6 +31,7 @@ internal HttpSysException(int errorCode, string message)

// the base class returns the HResult with this property
// we need the Win32 Error Code, hence the override.
/// <inheritdoc />
public override int ErrorCode
{
get
Expand Down
6 changes: 6 additions & 0 deletions src/Servers/HttpSys/src/HttpSysOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

namespace Microsoft.AspNetCore.Server.HttpSys
{
/// <summary>
/// Contains the options used by HttpSys.
/// </summary>
public class HttpSysOptions
{
private const uint MaximumRequestQueueNameLength = 260;
Expand All @@ -26,6 +29,9 @@ public class HttpSysOptions
private long? _maxRequestBodySize = DefaultMaxRequestBodySize;
private string _requestQueueName;

/// <summary>
/// Initializes a new <see cref="HttpSysOptions"/>.
/// </summary>
public HttpSysOptions()
{
}
Expand Down
4 changes: 4 additions & 0 deletions src/Servers/HttpSys/src/IHttpSysRequestDelegationFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

namespace Microsoft.AspNetCore.Server.HttpSys
{
/// <summary>
/// Interface for delegating requests to other Http.Sys request queues.
/// </summary>
public interface IHttpSysRequestDelegationFeature
{
/// <summary>
Expand All @@ -15,6 +18,7 @@ public interface IHttpSysRequestDelegationFeature
/// must not be read nor the response started before this is invoked. Check <see cref="CanDelegate"/>
/// before invoking.
/// </summary>
/// <param name="destination">The rule maintaining the handle to the destination queue.</param>
void DelegateRequest(DelegationRule destination);
}
}
5 changes: 5 additions & 0 deletions src/Servers/HttpSys/src/IServerDelegationFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@

namespace Microsoft.AspNetCore.Server.HttpSys
{
/// <summary>
/// This exposes the creation of delegation rules on request queues owned by the server.
/// </summary>
public interface IServerDelegationFeature
{
/// <summary>
/// Create a delegation rule on request queue owned by the server.
/// </summary>
/// <param name="queueName">The name of the Http.Sys request queue.</param>
/// <param name="urlPrefix">The URL of the Http.Sys Url Prefix.</param>
/// <returns>
/// Creates a <see cref="DelegationRule"/> that can used to delegate individual requests.
/// </returns>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

<!-- Ignore platform compatibility warnings for this project. We know this only works on windows.-->
<NoWarn>$(NoWarn);CA1416</NoWarn>
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
39 changes: 39 additions & 0 deletions src/Servers/HttpSys/src/UrlPrefix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

namespace Microsoft.AspNetCore.Server.HttpSys
{
/// <summary>
/// A set of URL parameters used to listen for incoming requests.
/// </summary>
public class UrlPrefix
{
private UrlPrefix(bool isHttps, string scheme, string host, string port, int portValue, string path)
Expand Down Expand Up @@ -94,6 +97,10 @@ public static UrlPrefix Create(string scheme, string host, int? portValue, strin
return new UrlPrefix(isHttps, scheme, host, port, portValue.Value, path);
}

/// <summary>
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa364698(v=vs.85).aspx
/// </summary>
/// <param name="prefix">The string that the <see cref="UrlPrefix"/> will be created from.</param>
public static UrlPrefix Create(string prefix)
{
string scheme = null;
Expand Down Expand Up @@ -146,26 +153,58 @@ public static UrlPrefix Create(string prefix)
return Create(scheme, host, port, path);
}

/// <summary>
/// Gets a value that determines if the prefix's scheme is HTTPS.
/// </summary>
public bool IsHttps { get; }

/// <summary>
/// Gets the scheme used by the prefix.
/// </summary>
public string Scheme { get; }

/// <summary>
/// Gets the host domain name used by the prefix.
/// </summary>
public string Host { get; }

/// <summary>
/// Gets a string representation of the port used by the prefix.
/// </summary>
public string Port { get; }

internal string HostAndPort { get; }

/// <summary>
/// Gets an integer representation of the port used by the prefix.
/// </summary>
public int PortValue { get; }

/// <summary>
/// Gets the path component of the prefix.
/// </summary>
public string Path { get; }

internal string PathWithoutTrailingSlash { get; }

/// <summary>
/// Gets a string representation of the prefix
/// </summary>
public string FullPrefix { get; }

/// <inheritdoc />
public override bool Equals(object obj)
{
return string.Equals(FullPrefix, Convert.ToString(obj), StringComparison.OrdinalIgnoreCase);
}

/// <inheritdoc />
public override int GetHashCode()
{
return StringComparer.OrdinalIgnoreCase.GetHashCode(FullPrefix);
}

/// <inheritdoc />
public override string ToString()
{
return FullPrefix;
Expand Down
Loading