Add IServerVariablesFeature

Enables fetching variables directly from IIS when using in-process
hosting. This is not available for out-of-process hosting.

Other changes:
 - Update tests to only run if IIS Express has been updated to support
 the new schema for hostingModel
 - Add a simpler test fixture for in-proc testing
This commit is contained in:
Nate McMaster 2018-01-27 23:57:13 -08:00
parent 6f54ed1c68
commit 011cf720e6
25 changed files with 432 additions and 29 deletions

View File

@ -6,6 +6,8 @@ branches:
- release
- dev
- /^(.*\/)?ci-.*$/
install:
- ps: .\tools\update_schema.ps1
build_script:
- ps: .\run.ps1 default-build
clone_depth: 1
@ -15,4 +17,4 @@ environment:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
test: off
deploy: off
os: Visual Studio 2017
os: Visual Studio 2017

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2020
VisualStudioVersion = 15.0.27130.2026
MinimumVisualStudioVersion = 15.0.26730.03
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04B1EDB6-E967-4D25-89B9-E6F8304038CD}"
ProjectSection(SolutionItems) = preProject

View File

@ -9,13 +9,13 @@
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<!-- x86 -->
<None Include="..\..\src\RequestHandler\bin\$(Configuration)\Win32\aspnetcorerh.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" Link="x86\aspnetcorerh.dll" />
<None Include="..\..\src\AspNetCore\bin\$(Configuration)\Win32\aspnetcore.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" Link="x86\aspnetcore.dll" />
<None Include="..\..\src\RequestHandler\bin\$(Configuration)\Win32\*.*" CopyToOutputDirectory="PreserveNewest" Visible="false" Link="x86\%(FileName)%(Extension)" />
<None Include="..\..\src\AspNetCore\bin\$(Configuration)\Win32\*.*" CopyToOutputDirectory="PreserveNewest" Visible="false" Link="x86\%(FileName)%(Extension)" />
<!-- x64 -->
<None Include="..\..\src\RequestHandler\bin\$(Configuration)\x64\aspnetcorerh.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" Link="x64\aspnetcorerh.dll" />
<None Include="..\..\src\AspNetCore\bin\$(Configuration)\x64\aspnetcore.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" Link="x64\aspnetcore.dll" />
<None Include="..\..\src\RequestHandler\bin\$(Configuration)\x64\*.*" CopyToOutputDirectory="PreserveNewest" Visible="false" Link="x64\%(FileName)%(Extension)" />
<None Include="..\..\src\AspNetCore\bin\$(Configuration)\x64\*.*" CopyToOutputDirectory="PreserveNewest" Visible="false" Link="x64\%(FileName)%(Extension)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />
</ItemGroup>

View File

@ -7,19 +7,20 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.IIS;
using Microsoft.AspNetCore.Server.IISIntegration;
namespace NativeIISSample
{
public class Startup
{
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAuthenticationSchemeProvider authSchemeProvider)
{
app.Run(async (context) =>
{
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("Hello World - " + DateTimeOffset.Now + Environment.NewLine);
await context.Response.WriteAsync(Environment.NewLine);
@ -60,9 +61,29 @@ namespace NativeIISSample
await context.Response.WriteAsync(key + ": " + value + Environment.NewLine);
}
await context.Response.WriteAsync(Environment.NewLine);
// accessing IIS server variables
await context.Response.WriteAsync("Server Variables:" + Environment.NewLine);
foreach (var varName in IISServerVarNames)
{
await context.Response.WriteAsync(varName + ": " + context.GetIISServerVariable(varName) + Environment.NewLine);
}
});
}
private static readonly string[] IISServerVarNames =
{
"AUTH_TYPE",
"AUTH_USER",
"CONTENT_TYPE",
"HTTP_HOST",
"HTTPS",
"REMOTE_PORT",
"REMOTE_USER",
"REQUEST_METHOD",
};
public static void Main(string[] args)
{
var host = new WebHostBuilder()

View File

@ -0,0 +1,38 @@
// 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.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Server.IIS
{
/// <summary>
/// Extensions to <see cref="HttpContext"/> that enable access to IIS features.
/// </summary>
public static class IISHttpContextExtensions
{
/// <summary>
/// Gets the value of a server variable for the current request.
/// </summary>
/// <param name="context">The http context for the request.</param>
/// <param name="variableName">The name of the variable.</param>
/// <returns>
/// <c>null</c> if the feature does not support the <see cref="IServerVariablesFeature"/> feature.
/// May return null or empty if the variable does not exist or is not set.
/// </returns>
/// <remarks>
/// For a list of common server variables available in IIS, see http://go.microsoft.com/fwlink/?LinkId=52471.
/// </remarks>
public static string GetIISServerVariable(this HttpContext context, string variableName)
{
var feature = context.Features.Get<IServerVariablesFeature>();
if (feature == null)
{
return null;
}
return feature[variableName];
}
}
}

View File

@ -0,0 +1,24 @@
// 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.Http.Features
{
/// <summary>
/// This feature provides access to request server variables set.
/// <para>
/// This feature is only available when hosting ASP.NET Core in-process with IIS or IIS Express.
/// </para>
/// </summary>
/// <remarks>
/// For a list of common server variables available in IIS, see http://go.microsoft.com/fwlink/?LinkId=52471.
/// </remarks>
public interface IServerVariablesFeature
{
/// <summary>
/// Gets the value of a server variable for the current request.
/// </summary>
/// <param name="variableName">The variable name</param>
/// <returns>May return null or empty if the variable does not exist or is not set.</returns>
string this[string variableName] { get; }
}
}

View File

@ -77,6 +77,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
[DllImport(AspNetCoreModuleDll)]
public unsafe static extern int http_get_application_properties(ref IISConfigurationData iiConfigData);
[DllImport(AspNetCoreModuleDll)]
public static extern int http_get_server_variable(IntPtr pInProcessHandler, [MarshalAs(UnmanagedType.AnsiBStr)] string variableName, [MarshalAs(UnmanagedType.BStr)] out string value);
[DllImport(AspNetCoreModuleDll)]
public unsafe static extern bool http_shutdown();

View File

@ -25,7 +25,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
IHttpConnectionFeature,
IHttpRequestLifetimeFeature,
IHttpRequestIdentifierFeature,
IHttpAuthenticationFeature
IHttpAuthenticationFeature,
IServerVariablesFeature
{
// NOTE: When feature interfaces are added to or removed from this HttpProtocol implementation,
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
@ -234,6 +235,20 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
public IAuthenticationHandler Handler { get; set; }
string IServerVariablesFeature.this[string variableName]
{
get
{
if (string.IsNullOrEmpty(variableName))
{
return null;
}
int hr = NativeMethods.http_get_server_variable(_pInProcessHandler, variableName, out var value);
return hr == 0 ? value : null;
}
}
object IFeatureCollection.this[Type key]
{
get => FastFeatureGet(key);

View File

@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
@ -28,6 +26,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
private static readonly Type IHttpBodyControlFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpBodyControlFeature);
private static readonly Type IHttpSendFileFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
private static readonly Type IISHttpContextType = typeof(IISHttpContext);
private static readonly Type IServerVariablesFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IServerVariablesFeature);
private object _currentIHttpRequestFeature;
private object _currentIHttpResponseFeature;
@ -49,6 +48,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
private object _currentISessionFeature;
private object _currentIHttpBodyControlFeature;
private object _currentIHttpSendFileFeature;
private object _currentIServerVariablesFeature;
private void Initialize()
{
@ -63,6 +63,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
_currentIHttpMinResponseDataRateFeature = this;
_currentIHttpBodyControlFeature = this;
_currentIHttpAuthenticationFeature = this;
_currentIServerVariablesFeature = this;
}
internal object FastFeatureGet(Type key)
@ -139,6 +140,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{
return this;
}
if (key == IServerVariablesFeature)
{
return this;
}
return ExtraFeatureGet(key);
}
@ -232,6 +237,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
_currentIHttpSendFileFeature = feature;
return;
}
if (key == IServerVariablesFeature)
{
_currentIServerVariablesFeature = feature;
return;
}
if (key == IISHttpContextType)
{
throw new InvalidOperationException("Cannot set IISHttpContext in feature collection");
@ -309,6 +319,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{
yield return new KeyValuePair<Type, object>(IHttpSendFileFeatureType, _currentIHttpSendFileFeature as global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
}
if (_currentIServerVariablesFeature != null)
{
yield return new KeyValuePair<Type, object>(IServerVariablesFeature, _currentIServerVariablesFeature as global::Microsoft.AspNetCore.Http.Features.IServerVariablesFeature);
}
if (MaybeExtra != null)
{

View File

@ -43,6 +43,37 @@ http_get_raw_response(
return pInProcessHandler->QueryHttpContext()->GetResponse()->GetRawHttpResponse();
}
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
HRESULT
http_get_server_variable(
_In_ IN_PROCESS_HANDLER* pInProcessHandler,
_In_ PCSTR pszVariableName,
_Out_ BSTR* pwszReturn
)
{
PCWSTR pszVariableValue;
DWORD cbLength;
HRESULT hr = pInProcessHandler
->QueryHttpContext()
->GetServerVariable(pszVariableName, &pszVariableValue, &cbLength);
if (FAILED(hr) || cbLength == 0)
{
goto Finished;
}
*pwszReturn = SysAllocString(pszVariableValue);
if (*pwszReturn == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
Finished:
return hr;
}
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT VOID http_set_response_status_code(
_In_ IN_PROCESS_HANDLER* pInProcessHandler,
_In_ USHORT statusCode,
@ -392,4 +423,4 @@ http_get_authentication_information(
return S_OK;
}
// End of export
// End of export

View File

@ -4,14 +4,13 @@
#if NET461
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
@ -26,7 +25,7 @@ namespace IISIntegration.IISServerFunctionalTests
{
}
[Fact(Skip = "See https://github.com/aspnet/IISIntegration/issues/424")]
[ConditionalFact]
public Task Authentication_InProcess_IISExpress()
{
return Authentication();

View File

@ -8,6 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
@ -22,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
}
[Theory(Skip = "See https://github.com/aspnet/IISIntegration/issues/424")]
[ConditionalTheory]
[InlineData("SetRequestFeatures")]
[InlineData("SetResponseFeatures")]
[InlineData("SetConnectionFeatures")]

View File

@ -2,11 +2,10 @@
// 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.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
@ -21,7 +20,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
}
[Fact(Skip = "See https://github.com/aspnet/IISIntegration/issues/515")]
[ConditionalFact]
public Task HelloWorld_InProcess_IISExpress_CoreClr_X64_Portable()
{
return HelloWorld(RuntimeFlavor.CoreClr, ApplicationType.Portable);

View File

@ -2,6 +2,8 @@
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
<RootNamespace>Microsoft.AspNetCore.Server.IIS.FunctionalTests</RootNamespace>
<AssemblyName>Microsoft.AspNetCore.Server.IIS.FunctionalTests</AssemblyName>
</PropertyGroup>
<ItemGroup>

View File

@ -2,12 +2,10 @@
// 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.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
@ -23,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
}
[Theory(Skip = "See https://github.com/aspnet/IISIntegration/issues/424")]
[ConditionalTheory]
[InlineData(10000)]
[InlineData(100000)]
[InlineData(1000000)]
@ -84,7 +82,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
}
[Fact (Skip = "See https://github.com/aspnet/IISIntegration/issues/424")]
[ConditionalFact]
public Task LargeFileResponseBodyInternalCheck()
{
return LargeResponseBodyFromFile(RuntimeFlavor.CoreClr, ApplicationType.Portable);
@ -106,7 +104,12 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
SiteName = "HttpTestSite", // This is configured in the Http.config
TargetFramework = runtimeFlavor == RuntimeFlavor.Clr ? "net461" : "netcoreapp2.0",
ApplicationType = applicationType,
Configuration =
#if DEBUG
"Debug"
#else
"Release"
#endif
};
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))

View File

@ -0,0 +1,5 @@
// 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.
// All functional tests in this project require a version of IIS express with an updated schema
[assembly: Microsoft.AspNetCore.Server.IIS.FunctionalTests.IISExpressSupportsInProcessHosting]

View File

@ -2,13 +2,12 @@
// 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.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Net.Http.Headers;
@ -24,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
}
[Fact(Skip = "See https://github.com/aspnet/IISIntegration/issues/424")]
[ConditionalFact]
public Task AddResponseHeaders_HeaderValuesAreSetCorrectly()
{
return RunResponseHeaders(ApplicationType.Portable);

View File

@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
@ -19,7 +20,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
}
[Theory(Skip = "See https://github.com/aspnet/IISIntegration/issues/424")]
[ConditionalTheory]
[InlineData("SetStatusCodeAfterWrite")]
[InlineData("SetHeaderAfterWrite")]
public Task ResponseInvalidOrderingTests_ExpectFailure(string path)

View File

@ -0,0 +1,35 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class ServerVariablesTest
{
private readonly IISTestSiteFixture _fixture;
public ServerVariablesTest(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task ProvidesAccessToServerVariables()
{
var port = _fixture.Client.BaseAddress.Port;
Assert.Equal("SERVER_PORT: " + port, await _fixture.Client.GetStringAsync("/ServerVariable?q=SERVER_PORT"));
Assert.Equal("QUERY_STRING: q=QUERY_STRING", await _fixture.Client.GetStringAsync("/ServerVariable?q=QUERY_STRING"));
}
[ConditionalFact]
public async Task ReturnsNullForUndefinedServerVariable()
{
var port = _fixture.Client.BaseAddress.Port;
Assert.Equal("THIS_VAR_IS_UNDEFINED: (null)", await _fixture.Client.GetStringAsync("/ServerVariable?q=THIS_VAR_IS_UNDEFINED"));
}
}
}

View File

@ -0,0 +1,62 @@
// 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.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Microsoft.AspNetCore.Testing.xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Assembly | AttributeTargets.Class)]
public sealed class IISExpressSupportsInProcessHostingAttribute : Attribute, ITestCondition
{
public bool IsMet => AncmSchema.SupportsInProcessHosting;
public string SkipReason => AncmSchema.SkipReason;
private class AncmSchema
{
public static bool SupportsInProcessHosting { get; }
public static string SkipReason { get; } = "IIS Express must be upgraded to support in-process hosting.";
static AncmSchema()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
SkipReason = "IIS Express tests can only be run on Windows";
return;
}
var ancmConfigPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
"IIS Express", "config", "schema", "aspnetcore_schema.xml");
if (!File.Exists(ancmConfigPath))
{
SkipReason = "IIS Express is not installed.";
return;
}
XDocument ancmConfig;
try
{
ancmConfig = XDocument.Load(ancmConfigPath);
}
catch
{
SkipReason = "Could not read ANCM schema configuration";
return;
}
SupportsInProcessHosting = ancmConfig
.Root
.Descendants("attribute")
.Any(n => "hostingModel".Equals(n.Attribute("name")?.Value, StringComparison.Ordinal));
}
}
}
}

View File

@ -0,0 +1,16 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
/// <summary>
/// This type just maps collection names to available fixtures
/// </summary>
[CollectionDefinition(Name)]
public class IISTestSiteCollection : ICollectionFixture<IISTestSiteFixture>
{
public const string Name = nameof(IISTestSiteCollection);
}
}

View File

@ -0,0 +1,52 @@
// 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.IO;
using System.Net.Http;
using System.Threading;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
public class IISTestSiteFixture : IDisposable
{
private readonly IApplicationDeployer _deployer;
public IISTestSiteFixture()
{
var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(),
ServerType.IISExpress,
RuntimeFlavor.CoreClr,
RuntimeArchitecture.x64)
{
ServerConfigTemplateContent = File.ReadAllText("Http.config"),
SiteName = "HttpTestSite",
TargetFramework = "netcoreapp2.0",
ApplicationType = ApplicationType.Portable,
Configuration =
#if DEBUG
"Debug"
#else
"Release"
#endif
};
_deployer = ApplicationDeployerFactory.Create(deploymentParameters, NullLoggerFactory.Instance);
var deploymentResult = _deployer.DeployAsync().Result;
Client = deploymentResult.HttpClient;
BaseUri = deploymentResult.ApplicationBaseUri;
ShutdownToken = deploymentResult.HostShutdownToken;
}
public string BaseUri { get; }
public HttpClient Client { get; }
public CancellationToken ShutdownToken { get; }
public void Dispose()
{
_deployer.Dispose();
}
}
}

View File

@ -0,0 +1,26 @@
// 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;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.IIS;
namespace IISTestSite
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Map("/ServerVariable", ServerVariable);
}
private void ServerVariable(IApplicationBuilder app)
{
app.Run(async ctx =>
{
var varName = ctx.Request.Query["q"];
await ctx.Response.WriteAsync($"{varName}: {ctx.GetIISServerVariable(varName) ?? "(null)"}");
});
}
}
}

55
tools/update_schema.ps1 Normal file
View File

@ -0,0 +1,55 @@
<#
.DESCRIPTION
Updates aspnetcore_schema.xml to the latest version.
Requires admin privileges.
#>
[cmdletbinding(SupportsShouldProcess = $true)]
param()
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version 1
$schemaSource = Resolve-Path "$PSScriptRoot\..\src\AspNetCore\aspnetcore_schema.xml"
[bool]$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
if (-not $isAdmin -and -not $WhatIfPreference) {
if ($PSCmdlet.ShouldContinue("Continue as an admin?", "This script needs admin privileges to update IIS Express and IIS.")) {
$thisFile = Join-Path $PSScriptRoot $MyInvocation.MyCommand.Name
Start-Process `
-Verb runas `
-FilePath "powershell.exe" `
-ArgumentList $thisFile `
-Wait `
| Out-Null
if (-not $?) {
throw 'Update failed'
}
exit
}
else {
throw 'Requires admin privileges'
}
}
$destinations = @(
"${env:ProgramFiles(x86)}\IIS Express\config\schema\aspnetcore_schema.xml",
"${env:ProgramFiles}\IIS Express\config\schema\aspnetcore_schema.xml",
"${env:windir}\system32\inetsrv\config\schema\aspnetcore_schema.xml"
) | Get-Unique
foreach ($dest in $destinations) {
if (-not (Test-Path $dest)) {
Write-Host -ForegroundColor Yellow "Skipping $dest. File does not already exist."
continue
}
if ($PSCmdlet.ShouldProcess($dest, "Replace file")) {
Write-Host "Updated $dest"
Move-Item $dest "${dest}.bak" -ErrorAction Ignore
Copy-Item $schemaSource $dest
}
}