diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..bdaa5ba982 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,50 @@ +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain + +*.jpg binary +*.png binary +*.gif binary + +*.cs text=auto diff=csharp +*.vb text=auto +*.resx text=auto +*.c text=auto +*.cpp text=auto +*.cxx text=auto +*.h text=auto +*.hxx text=auto +*.py text=auto +*.rb text=auto +*.java text=auto +*.html text=auto +*.htm text=auto +*.css text=auto +*.scss text=auto +*.sass text=auto +*.less text=auto +*.js text=auto +*.lisp text=auto +*.clj text=auto +*.sql text=auto +*.php text=auto +*.lua text=auto +*.m text=auto +*.asm text=auto +*.erl text=auto +*.fs text=auto +*.fsx text=auto +*.hs text=auto + +*.csproj text=auto +*.vbproj text=auto +*.fsproj text=auto +*.dbproj text=auto +*.sln text=auto eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..566f474fe8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +[Oo]bj/ +[Bb]in/ +TestResults/ +.nuget/ +*.sln.ide/ +_ReSharper.*/ +packages/ +artifacts/ +PublishProfiles/ +*.user +*.suo +*.cache +*.docstates +_ReSharper.* +nuget.exe +project.lock.json +*net45.csproj +*net451.csproj +*k10.csproj +*.psess +*.vsp +*.pidb +*.userprefs +*DS_Store +*.ncrunchsolution +*.*sdf +*.ipch +.vs/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..947bf868ee --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: csharp +sudo: false +script: + - ./build.sh --quiet verify \ No newline at end of file diff --git a/BasicMiddleware.sln b/BasicMiddleware.sln new file mode 100644 index 0000000000..ace965aa26 --- /dev/null +++ b/BasicMiddleware.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23018.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.HttpOverrides", "src\Microsoft.AspNet.HttpOverrides\Microsoft.AspNet.HttpOverrides.xproj", "{517308C3-B477-4B01-B461-CAB9C10B6928}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5076D28-FA7E-4606-9410-FEDD0D603527}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8437B0F3-3894-4828-A945-A9187F37631D}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.HttpOverrides.Tests", "test\Microsoft.AspNet.HttpOverrides.Tests\Microsoft.AspNet.HttpOverrides.Tests.xproj", "{D6341B92-3416-4F11-8DF4-CB274296175F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99B72A07-32D6-434D-B44D-D064E3C13E08}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {517308C3-B477-4B01-B461-CAB9C10B6928}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {517308C3-B477-4B01-B461-CAB9C10B6928}.Debug|Any CPU.Build.0 = Debug|Any CPU + {517308C3-B477-4B01-B461-CAB9C10B6928}.Release|Any CPU.ActiveCfg = Release|Any CPU + {517308C3-B477-4B01-B461-CAB9C10B6928}.Release|Any CPU.Build.0 = Release|Any CPU + {D6341B92-3416-4F11-8DF4-CB274296175F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6341B92-3416-4F11-8DF4-CB274296175F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6341B92-3416-4F11-8DF4-CB274296175F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6341B92-3416-4F11-8DF4-CB274296175F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {517308C3-B477-4B01-B461-CAB9C10B6928} = {A5076D28-FA7E-4606-9410-FEDD0D603527} + {D6341B92-3416-4F11-8DF4-CB274296175F} = {8437B0F3-3894-4828-A945-A9187F37631D} + EndGlobalSection +EndGlobal diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 0000000000..da57d47267 --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..636a7618d3 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,7 @@ +init: + - git config --global core.autocrlf true +build_script: + - build.cmd --quiet verify +clone_depth: 1 +test: off +deploy: off \ No newline at end of file diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000000..41025afb26 --- /dev/null +++ b/build.cmd @@ -0,0 +1,28 @@ +@echo off +cd %~dp0 + +SETLOCAL +SET CACHED_NUGET=%LocalAppData%\NuGet\NuGet.exe + +IF EXIST %CACHED_NUGET% goto copynuget +echo Downloading latest version of NuGet.exe... +IF NOT EXIST %LocalAppData%\NuGet md %LocalAppData%\NuGet +@powershell -NoProfile -ExecutionPolicy unrestricted -Command "$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest 'https://www.nuget.org/nuget.exe' -OutFile '%CACHED_NUGET%'" + +:copynuget +IF EXIST .nuget\nuget.exe goto restore +md .nuget +copy %CACHED_NUGET% .nuget\nuget.exe > nul + +:restore +IF EXIST packages\KoreBuild goto run +.nuget\NuGet.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre +.nuget\NuGet.exe install Sake -version 0.2 -o packages -ExcludeVersion + +IF "%SKIP_DNX_INSTALL%"=="1" goto run +CALL packages\KoreBuild\build\dnvm upgrade -runtime CLR -arch x86 +CALL packages\KoreBuild\build\dnvm install default -runtime CoreCLR -arch x86 + +:run +CALL packages\KoreBuild\build\dnvm use default -runtime CLR -arch x86 +packages\Sake\tools\Sake.exe -I packages\KoreBuild\build -f makefile.shade %* diff --git a/build.sh b/build.sh new file mode 100644 index 0000000000..3ef874f9bd --- /dev/null +++ b/build.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +if test `uname` = Darwin; then + cachedir=~/Library/Caches/KBuild +else + if [ -z $XDG_DATA_HOME ]; then + cachedir=$HOME/.local/share + else + cachedir=$XDG_DATA_HOME; + fi +fi +mkdir -p $cachedir + +url=https://www.nuget.org/nuget.exe + +if test ! -f $cachedir/nuget.exe; then + wget -O $cachedir/nuget.exe $url 2>/dev/null || curl -o $cachedir/nuget.exe --location $url /dev/null +fi + +if test ! -e .nuget; then + mkdir .nuget + cp $cachedir/nuget.exe .nuget/nuget.exe +fi + +if test ! -d packages/KoreBuild; then + mono .nuget/nuget.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre + mono .nuget/nuget.exe install Sake -version 0.2 -o packages -ExcludeVersion +fi + +if ! type dnvm > /dev/null 2>&1; then + source packages/KoreBuild/build/dnvm.sh +fi + +if ! type dnx > /dev/null 2>&1; then + dnvm upgrade +fi + +mono packages/Sake/tools/Sake.exe -I packages/KoreBuild/build -f makefile.shade "$@" + diff --git a/global.json b/global.json new file mode 100644 index 0000000000..b0323e4281 --- /dev/null +++ b/global.json @@ -0,0 +1,3 @@ +{ + "projects": [ "src" ] +} \ No newline at end of file diff --git a/makefile.shade b/makefile.shade new file mode 100644 index 0000000000..a612302092 --- /dev/null +++ b/makefile.shade @@ -0,0 +1,8 @@ +use namespace="System.Net" + +var VERSION='0.1' +var FULL_VERSION='0.1' +var AUTHORS='Microsoft Open Technologies, Inc.' + +use-standard-lifecycle +k-standard-goals \ No newline at end of file diff --git a/src/Microsoft.AspNet.HttpOverrides/ForwardedHeaders.cs b/src/Microsoft.AspNet.HttpOverrides/ForwardedHeaders.cs new file mode 100644 index 0000000000..5b5916212e --- /dev/null +++ b/src/Microsoft.AspNet.HttpOverrides/ForwardedHeaders.cs @@ -0,0 +1,17 @@ +// 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; + +namespace Microsoft.AspNet.HttpOverrides +{ + [Flags] + public enum ForwardedHeaders + { + None = 0, + XForwardedFor = 1 << 0, + XForwardedHost = 1 << 1, + XForwardedProto = 1 << 2, + All = XForwardedFor | XForwardedHost | XForwardedProto + } +} diff --git a/src/Microsoft.AspNet.HttpOverrides/HttpMethodOverrideExtensions.cs b/src/Microsoft.AspNet.HttpOverrides/HttpMethodOverrideExtensions.cs new file mode 100644 index 0000000000..42ae6e93b8 --- /dev/null +++ b/src/Microsoft.AspNet.HttpOverrides/HttpMethodOverrideExtensions.cs @@ -0,0 +1,31 @@ +// 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.AspNet.HttpOverrides; + +namespace Microsoft.AspNet.Builder +{ + public static class HttpMethodOverrideExtensions + { + /// + /// Allows incoming POST request to override method type with type specified in header. + /// + /// + /// + public static IApplicationBuilder UseHttpMethodOverride(this IApplicationBuilder builder) + { + return builder.Use(next => new HttpMethodOverrideMiddleware(next).Invoke); + } + + /// + /// Allows incoming POST request to override method type with type specified in form. + /// + /// + /// Denotes the element that contains the name of the resulting method type. + /// + public static IApplicationBuilder UseHttpMethodOverride(this IApplicationBuilder builder, string formFieldInput) + { + return builder.Use(next => new HttpMethodOverrideMiddleware(next, formFieldInput).Invoke); + } + } +} diff --git a/src/Microsoft.AspNet.HttpOverrides/HttpMethodOverrideMiddleware.cs b/src/Microsoft.AspNet.HttpOverrides/HttpMethodOverrideMiddleware.cs new file mode 100644 index 0000000000..8d805d80b8 --- /dev/null +++ b/src/Microsoft.AspNet.HttpOverrides/HttpMethodOverrideMiddleware.cs @@ -0,0 +1,51 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.HttpOverrides +{ + public class HttpMethodOverrideMiddleware + { + private const string xHttpMethodOverride = "X-Http-Method-Override"; + private readonly RequestDelegate _next; + private readonly string _formFieldName; + + public HttpMethodOverrideMiddleware(RequestDelegate next, string formFieldName = null) + { + _next = next; + _formFieldName = formFieldName; + } + + public async Task Invoke(HttpContext context) + { + if (string.Equals(context.Request.Method,"POST", StringComparison.OrdinalIgnoreCase)) + { + if (_formFieldName != null) + { + if (context.Request.HasFormContentType) + { + var form = await context.Request.ReadFormAsync(); + var methodType = form[_formFieldName]; + if (!string.IsNullOrEmpty(methodType)) + { + context.Request.Method = methodType; + } + } + } + else + { + var xHttpMethodOverrideValue = context.Request.Headers.Get(xHttpMethodOverride); + if (!string.IsNullOrEmpty(xHttpMethodOverrideValue)) + { + context.Request.Method = xHttpMethodOverrideValue; + } + } + } + await _next(context); + } + } +} diff --git a/src/Microsoft.AspNet.HttpOverrides/Microsoft.AspNet.HttpOverrides.xproj b/src/Microsoft.AspNet.HttpOverrides/Microsoft.AspNet.HttpOverrides.xproj new file mode 100644 index 0000000000..0037db59b2 --- /dev/null +++ b/src/Microsoft.AspNet.HttpOverrides/Microsoft.AspNet.HttpOverrides.xproj @@ -0,0 +1,17 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 517308c3-b477-4b01-b461-cab9c10b6928 + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.HttpOverrides/OverrideHeaderExtensions.cs b/src/Microsoft.AspNet.HttpOverrides/OverrideHeaderExtensions.cs new file mode 100644 index 0000000000..04acee970c --- /dev/null +++ b/src/Microsoft.AspNet.HttpOverrides/OverrideHeaderExtensions.cs @@ -0,0 +1,21 @@ +// 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.AspNet.HttpOverrides; + +namespace Microsoft.AspNet.Builder +{ + public static class OverrideHeaderExtensions + { + /// + /// Forwards proxied headers onto current request + /// + /// + /// Enables the different override options. + /// + public static IApplicationBuilder UseOverrideHeaders(this IApplicationBuilder builder, OverrideHeaderMiddlewareOptions options) + { + return builder.Use(next => new OverrideHeaderMiddleware(next, options).Invoke); + } + } +} diff --git a/src/Microsoft.AspNet.HttpOverrides/OverrideHeaderMiddleware.cs b/src/Microsoft.AspNet.HttpOverrides/OverrideHeaderMiddleware.cs new file mode 100644 index 0000000000..0071ac0da5 --- /dev/null +++ b/src/Microsoft.AspNet.HttpOverrides/OverrideHeaderMiddleware.cs @@ -0,0 +1,68 @@ +// 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.Net; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; + +namespace Microsoft.AspNet.HttpOverrides +{ + public class OverrideHeaderMiddleware + { + private const string XForwardedForHeaderName = "X-Forwarded-For"; + private const string XForwardedHostHeaderName = "X-Forwarded-Host"; + private const string XForwardedProtoHeaderName = "X-Forwarded-Proto"; + private readonly OverrideHeaderMiddlewareOptions _options; + private readonly RequestDelegate _next; + + public OverrideHeaderMiddleware([NotNull] RequestDelegate next, [NotNull] OverrideHeaderMiddlewareOptions options) + { + _options = options; + _next = next; + } + + public Task Invoke(HttpContext context) + { + if ((_options.ForwardedOptions & ForwardedHeaders.XForwardedFor) != 0) + { + var xForwardedForHeaderValue = context.Request.Headers.GetCommaSeparatedValues(XForwardedForHeaderName); + if (xForwardedForHeaderValue != null && xForwardedForHeaderValue.Count > 0) + { + IPAddress originalIPAddress; + if (IPAddress.TryParse(xForwardedForHeaderValue[0], out originalIPAddress)) + { + if (context.Connection.RemoteIpAddress != null) + { + var ipList = context.Request.Headers.Get(XForwardedForHeaderName); + context.Request.Headers.Set(XForwardedForHeaderName, (ipList + "," + context.Connection.RemoteIpAddress.ToString())); + } + context.Connection.RemoteIpAddress = originalIPAddress; + } + } + } + + if ((_options.ForwardedOptions & ForwardedHeaders.XForwardedHost) != 0) + { + var xForwardHostHeaderValue = context.Request.Headers.Get(XForwardedHostHeaderName); + if (!string.IsNullOrEmpty(xForwardHostHeaderValue)) + { + context.Request.Host = HostString.FromUriComponent(xForwardHostHeaderValue); + } + } + + if ((_options.ForwardedOptions & ForwardedHeaders.XForwardedProto) != 0) + { + var xForwardProtoHeaderValue = context.Request.Headers.Get(XForwardedProtoHeaderName); + if (!string.IsNullOrEmpty(xForwardProtoHeaderValue)) + { + context.Request.Scheme = xForwardProtoHeaderValue; + } + } + + return _next(context); + } + } +} diff --git a/src/Microsoft.AspNet.HttpOverrides/OverrideHeaderOptions.cs b/src/Microsoft.AspNet.HttpOverrides/OverrideHeaderOptions.cs new file mode 100644 index 0000000000..428408ab25 --- /dev/null +++ b/src/Microsoft.AspNet.HttpOverrides/OverrideHeaderOptions.cs @@ -0,0 +1,10 @@ +// 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.AspNet.HttpOverrides +{ + public class OverrideHeaderMiddlewareOptions + { + public ForwardedHeaders ForwardedOptions { get; set; } + } +} diff --git a/src/Microsoft.AspNet.HttpOverrides/project.json b/src/Microsoft.AspNet.HttpOverrides/project.json new file mode 100644 index 0000000000..39c4937409 --- /dev/null +++ b/src/Microsoft.AspNet.HttpOverrides/project.json @@ -0,0 +1,19 @@ +{ + "version": "1.0.0-*", + "description": "XForward and Method override middlewares for ASP.NET", + "dependencies": { + "Microsoft.CSharp": "4.0.0-*", + "Microsoft.AspNet.Http.Abstractions": "1.0.0-*", + "Microsoft.Framework.NotNullAttribute.Sources": { "version": "1.0.0-*", "type": "build" } + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Collections": "4.0.10-*", + "System.Linq": "4.0.0-*", + "System.Threading": "4.0.10-*" + } + } + } +} diff --git a/test/Microsoft.AspNet.HttpOverrides.Tests/HttpMethodOverrideMiddlewareTest.cs b/test/Microsoft.AspNet.HttpOverrides.Tests/HttpMethodOverrideMiddlewareTest.cs new file mode 100644 index 0000000000..de987a4e63 --- /dev/null +++ b/test/Microsoft.AspNet.HttpOverrides.Tests/HttpMethodOverrideMiddlewareTest.cs @@ -0,0 +1,73 @@ +// 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.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Xunit; + +namespace Microsoft.AspNet.HttpOverrides +{ + public class HttpMethodOverrideMiddlewareTest + { + [Fact] + public async Task XHttpMethodOverrideHeaderAvaiableChangesRequestMethod() + { + var assertsExecuted = false; + var server = TestServer.Create(app => + { + app.UseHttpMethodOverride(); + app.Run(context => + { + assertsExecuted = true; + Assert.Equal("DELETE", context.Request.Method); + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Post, ""); + req.Headers.Add("X-Http-Method-Override", "DELETE"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public async Task XHttpMethodOverrideHeaderUnavaiableDoesntChangeRequestMethod() + { + var assertsExecuted = false; + var server = TestServer.Create(app => + { + app.UseHttpMethodOverride(); + app.Run(context => + { + Assert.Equal("POST",context.Request.Method); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + var req = new HttpRequestMessage(HttpMethod.Post, ""); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public async Task XHttpMethodOverrideFromGetRequestDoesntChangeMethodType() + { + var assertsExecuted = false; + var server = TestServer.Create(app => + { + app.UseHttpMethodOverride(); + app.Run(context => + { + Assert.Equal("GET", context.Request.Method); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + var req = new HttpRequestMessage(HttpMethod.Get, ""); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + } +} diff --git a/test/Microsoft.AspNet.HttpOverrides.Tests/Microsoft.AspNet.HttpOverrides.Tests.xproj b/test/Microsoft.AspNet.HttpOverrides.Tests/Microsoft.AspNet.HttpOverrides.Tests.xproj new file mode 100644 index 0000000000..9889be51ad --- /dev/null +++ b/test/Microsoft.AspNet.HttpOverrides.Tests/Microsoft.AspNet.HttpOverrides.Tests.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + d6341b92-3416-4f11-8df4-cb274296175f + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.HttpOverrides.Tests/OverrideHeaderMiddlewareTest.cs b/test/Microsoft.AspNet.HttpOverrides.Tests/OverrideHeaderMiddlewareTest.cs new file mode 100644 index 0000000000..003033f5a1 --- /dev/null +++ b/test/Microsoft.AspNet.HttpOverrides.Tests/OverrideHeaderMiddlewareTest.cs @@ -0,0 +1,202 @@ +// 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.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Xunit; + +namespace Microsoft.AspNet.HttpOverrides +{ + public class OverrideMiddlewareHeaderTests + { + [Fact] + public async Task XForwardedForOverrideChangesRemoteIp() + { + var assertsExecuted = false; + var options = new OverrideHeaderMiddlewareOptions(); + options.ForwardedOptions = ForwardedHeaders.XForwardedFor; + + var server = TestServer.Create(app => + { + app.UseOverrideHeaders(options); + app.Run(context => + { + Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString()); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-For", "11.111.111.11"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public async Task XForwardedForOverrideBadIpDoesntChangeRemoteIp() + { + var assertsExecuted = false; + var options = new OverrideHeaderMiddlewareOptions(); + options.ForwardedOptions = ForwardedHeaders.XForwardedFor; + + var server = TestServer.Create(app => + { + app.UseOverrideHeaders(options); + app.Run(context => + { + Assert.Null(context.Connection.RemoteIpAddress); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-For", "BAD-IP"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public async Task XForwardedHostOverrideChangesRequestHost() + { + var assertsExecuted = false; + var options = new OverrideHeaderMiddlewareOptions(); + options.ForwardedOptions = ForwardedHeaders.XForwardedHost; + + var server = TestServer.Create(app => + { + app.UseOverrideHeaders(options); + app.Run(context => + { + Assert.Equal("testHost", context.Request.Host.ToString()); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-Host", "testHost"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public async Task XForwardedProtoOverrideChangesRequestProtocol() + { + var assertsExecuted = false; + var options = new OverrideHeaderMiddlewareOptions(); + options.ForwardedOptions = ForwardedHeaders.XForwardedProto; + + var server = TestServer.Create(app => + { + app.UseOverrideHeaders(options); + app.Run(context => + { + Assert.Equal("TestProtocol", context.Request.Scheme); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-Proto", "TestProtocol"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public void AllForwardsDisabledByDefault() + { + var options = new OverrideHeaderMiddlewareOptions(); + Assert.True(options.ForwardedOptions == 0); + } + + [Fact] + public async Task AllForwardsEnabledChangeRequestRemoteIpHostandProtocol() + { + var assertsExecuted = false; + var options = new OverrideHeaderMiddlewareOptions(); + options.ForwardedOptions = ForwardedHeaders.All; + + var server = TestServer.Create(app => + { + app.UseOverrideHeaders(options); + app.Run(context => + { + Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString()); + Assert.Equal("testHost", context.Request.Host.ToString()); + Assert.Equal("Protocol", context.Request.Scheme); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-For", "11.111.111.11"); + req.Headers.Add("X-Forwarded-Host", "testHost"); + req.Headers.Add("X-Forwarded-Proto", "Protocol"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public async Task AllOptionsDisabledRequestDoesntChange() + { + var assertsExecuted = false; + var options = new OverrideHeaderMiddlewareOptions(); + options.ForwardedOptions = ForwardedHeaders.None; + + var server = TestServer.Create(app => + { + app.UseOverrideHeaders(options); + app.Run(context => + { + Assert.Null(context.Connection.RemoteIpAddress); + Assert.Equal("localhost", context.Request.Host.ToString()); + Assert.Equal("http", context.Request.Scheme); + assertsExecuted = true; + return Task.FromResult(0); + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-For", "11.111.111.11"); + req.Headers.Add("X-Forwarded-Host", "otherhost"); + req.Headers.Add("X-Forwarded-Proto", "Protocol"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + + [Fact] + public async Task PartiallyEnabledForwardsPartiallyChangesRequest() + { + var assertsExecuted = false; + var XForwardedForAndProto = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + var options = new OverrideHeaderMiddlewareOptions(); + options.ForwardedOptions = XForwardedForAndProto; + + var server = TestServer.Create(app => + { + app.UseOverrideHeaders(options); + app.Run(context => + { + Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString()); + Assert.Equal("localhost", context.Request.Host.ToString()); + Assert.Equal("Protocol", context.Request.Scheme); + assertsExecuted = true; + return Task.FromResult(0); + + }); + }); + + var req = new HttpRequestMessage(HttpMethod.Get, ""); + req.Headers.Add("X-Forwarded-For", "11.111.111.11"); + req.Headers.Add("X-Forwarded-Proto", "Protocol"); + await server.CreateClient().SendAsync(req); + Assert.True(assertsExecuted); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.HttpOverrides.Tests/project.json b/test/Microsoft.AspNet.HttpOverrides.Tests/project.json new file mode 100644 index 0000000000..a83e0c4bc7 --- /dev/null +++ b/test/Microsoft.AspNet.HttpOverrides.Tests/project.json @@ -0,0 +1,23 @@ +{ + "version": "1.0.0-*", + "dependencies": { + "Microsoft.AspNet.HttpOverrides": "1.0.0-*", + "Microsoft.AspNet.TestHost": "1.0.0-*", + "Microsoft.Framework.Logging.Testing": "1.0.0-*", + "xunit.runner.aspnet": "2.0.0-aspnet-*" + }, + "commands": { + "test": "xunit.runner.aspnet" + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { + "dependencies": { + "System.Collections": "4.0.10-*", + "System.Linq": "4.0.0-*", + "System.Threading": "4.0.10-*" + } + } + } +}