Implement ChangeCookieAction and add factory to parse from mod_rewrite.
Resolves #94
This commit is contained in:
parent
3cb0fc640a
commit
1db5a9e58f
|
|
@ -0,0 +1,120 @@
|
|||
// 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.Globalization;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite
|
||||
{
|
||||
public class CookieActionFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ChangeCookieAction" /> <see href="https://httpd.apache.org/docs/current/rewrite/flags.html#flag_co" /> for details.
|
||||
/// </summary>
|
||||
/// <param name="flagValue">The flag</param>
|
||||
/// <returns>The action</returns>
|
||||
public ChangeCookieAction Create(string flagValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(flagValue))
|
||||
{
|
||||
throw new ArgumentException(nameof(flagValue));
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var separator = ':';
|
||||
if (flagValue[0] == ';')
|
||||
{
|
||||
separator = ';';
|
||||
i++;
|
||||
}
|
||||
|
||||
ChangeCookieAction action = null;
|
||||
var currentField = Fields.Name;
|
||||
var start = i;
|
||||
for (; i < flagValue.Length; i++)
|
||||
{
|
||||
if (flagValue[i] == separator)
|
||||
{
|
||||
var length = i - start;
|
||||
SetActionOption(flagValue.Substring(start, length).Trim(), currentField, ref action);
|
||||
|
||||
currentField++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != start)
|
||||
{
|
||||
SetActionOption(flagValue.Substring(start).Trim(new[] { ' ', separator }), currentField, ref action);
|
||||
}
|
||||
|
||||
if (currentField < Fields.Domain)
|
||||
{
|
||||
throw new FormatException(Resources.FormatError_InvalidChangeCookieFlag(flagValue));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
private static void SetActionOption(string value, Fields tokenType, ref ChangeCookieAction action)
|
||||
{
|
||||
switch (tokenType)
|
||||
{
|
||||
case Fields.Name:
|
||||
action = new ChangeCookieAction(value);
|
||||
break;
|
||||
case Fields.Value:
|
||||
action.Value = value;
|
||||
break;
|
||||
case Fields.Domain:
|
||||
// despite what spec says, an empty domain field is allowed in mod_rewrite
|
||||
// by specifying NAME:VALUE:;
|
||||
action.Domain = string.IsNullOrEmpty(value) || value == ";"
|
||||
? null
|
||||
: value;
|
||||
break;
|
||||
case Fields.Lifetime:
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
uint minutes;
|
||||
if (!uint.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out minutes))
|
||||
{
|
||||
throw new FormatException(Resources.FormatError_CouldNotParseInteger(value));
|
||||
}
|
||||
|
||||
action.Lifetime = TimeSpan.FromMinutes(minutes);
|
||||
break;
|
||||
case Fields.Path:
|
||||
action.Path = value;
|
||||
break;
|
||||
case Fields.Secure:
|
||||
action.Secure = "secure".Equals(value, StringComparison.OrdinalIgnoreCase)
|
||||
|| "true".Equals(value, StringComparison.OrdinalIgnoreCase)
|
||||
|| value == "1";
|
||||
break;
|
||||
case Fields.HttpOnly:
|
||||
action.HttpOnly = "httponly".Equals(value, StringComparison.OrdinalIgnoreCase)
|
||||
|| "true".Equals(value, StringComparison.OrdinalIgnoreCase)
|
||||
|| value == "1";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// order matters
|
||||
// see https://httpd.apache.org/docs/current/rewrite/flags.html#flag_co
|
||||
private enum Fields
|
||||
{
|
||||
Name,
|
||||
Value,
|
||||
Domain,
|
||||
Lifetime,
|
||||
Path,
|
||||
Secure,
|
||||
HttpOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite
|
|||
{
|
||||
if (string.IsNullOrEmpty(flagString))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(flagString));
|
||||
throw new ArgumentException(nameof(flagString));
|
||||
}
|
||||
|
||||
// Check that flags are contained within []
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite
|
|||
private IList<Condition> _conditions;
|
||||
private IList<UrlAction> _actions = new List<UrlAction>();
|
||||
private UrlMatch _match;
|
||||
private CookieActionFactory _cookieActionFactory = new CookieActionFactory();
|
||||
|
||||
private readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
|
||||
|
||||
|
|
@ -172,8 +173,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite
|
|||
string flag;
|
||||
if (flags.GetValue(FlagType.Cookie, out flag))
|
||||
{
|
||||
// parse cookie
|
||||
_actions.Add(new ChangeCookieAction(flag));
|
||||
var action = _cookieActionFactory.Create(flag);
|
||||
_actions.Add(action);
|
||||
}
|
||||
|
||||
if (flags.GetValue(FlagType.Env, out flag))
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
|||
{
|
||||
if (string.IsNullOrEmpty(regex))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(regex));
|
||||
throw new ArgumentException(nameof(regex));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(replacement))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(replacement));
|
||||
throw new ArgumentException(nameof(replacement));
|
||||
}
|
||||
|
||||
InitialMatch = new Regex(regex, RegexOptions.Compiled | RegexOptions.CultureInvariant, _regexTimeout);
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
|||
{
|
||||
if (string.IsNullOrEmpty(regex))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(regex));
|
||||
throw new ArgumentException(nameof(regex));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(replacement))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(replacement));
|
||||
throw new ArgumentException(nameof(replacement));
|
||||
}
|
||||
|
||||
InitialMatch = new Regex(regex, RegexOptions.Compiled | RegexOptions.CultureInvariant, _regexTimeout);
|
||||
|
|
|
|||
|
|
@ -2,21 +2,74 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
|
||||
{
|
||||
public class ChangeCookieAction : UrlAction
|
||||
{
|
||||
public ChangeCookieAction(string cookie)
|
||||
private readonly Func<DateTimeOffset> _timeSource;
|
||||
private CookieOptions _cachedOptions;
|
||||
|
||||
public ChangeCookieAction(string name)
|
||||
: this(name, () => DateTimeOffset.UtcNow)
|
||||
{
|
||||
// TODO
|
||||
throw new NotImplementedException("Changing the cookie is not implemented");
|
||||
}
|
||||
|
||||
// for testing
|
||||
internal ChangeCookieAction(string name, Func<DateTimeOffset> timeSource)
|
||||
{
|
||||
_timeSource = timeSource;
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentException(nameof(name));
|
||||
}
|
||||
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string Value { get; set; }
|
||||
public string Domain { get; set; }
|
||||
public TimeSpan Lifetime { get; set; }
|
||||
public string Path { get; set; }
|
||||
public bool Secure { get; set; }
|
||||
public bool HttpOnly { get; set; }
|
||||
|
||||
public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
|
||||
{
|
||||
// modify the cookies
|
||||
throw new NotImplementedException("Changing the cookie is not implemented");
|
||||
var options = GetOrCreateOptions();
|
||||
context.HttpContext.Response.Cookies.Append(Name, Value ?? string.Empty, options);
|
||||
}
|
||||
|
||||
private CookieOptions GetOrCreateOptions()
|
||||
{
|
||||
if (Lifetime > TimeSpan.Zero)
|
||||
{
|
||||
var now = _timeSource();
|
||||
return new CookieOptions()
|
||||
{
|
||||
Domain = Domain,
|
||||
HttpOnly = HttpOnly,
|
||||
Secure = Secure,
|
||||
Path = Path,
|
||||
Expires = now.Add(Lifetime)
|
||||
};
|
||||
}
|
||||
|
||||
if (_cachedOptions == null)
|
||||
{
|
||||
_cachedOptions = new CookieOptions()
|
||||
{
|
||||
Domain = Domain,
|
||||
HttpOnly = HttpOnly,
|
||||
Secure = Secure,
|
||||
Path = Path
|
||||
};
|
||||
}
|
||||
|
||||
return _cachedOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,19 +11,35 @@ namespace Microsoft.AspNetCore.Rewrite
|
|||
= new ResourceManager("Microsoft.AspNetCore.Rewrite.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse the UrlRewrite file. Message: '{0}'. Line number '{1}': '{2}'.
|
||||
/// Error adding a mod_rewrite rule. The change environment flag is not supported.
|
||||
/// </summary>
|
||||
internal static string Error_UrlRewriteParseError
|
||||
internal static string Error_ChangeEnvironmentNotSupported
|
||||
{
|
||||
get { return GetString("Error_UrlRewriteParseError"); }
|
||||
get { return GetString("Error_ChangeEnvironmentNotSupported"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse the UrlRewrite file. Message: '{0}'. Line number '{1}': '{2}'.
|
||||
/// Error adding a mod_rewrite rule. The change environment flag is not supported.
|
||||
/// </summary>
|
||||
internal static string FormatError_UrlRewriteParseError(object p0, object p1, object p2)
|
||||
internal static string FormatError_ChangeEnvironmentNotSupported()
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_UrlRewriteParseError"), p0, p1, p2);
|
||||
return GetString("Error_ChangeEnvironmentNotSupported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse integer from value '{0}'.
|
||||
/// </summary>
|
||||
internal static string Error_CouldNotParseInteger
|
||||
{
|
||||
get { return GetString("Error_CouldNotParseInteger"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse integer from value '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatError_CouldNotParseInteger(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_CouldNotParseInteger"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -106,6 +122,38 @@ namespace Microsoft.AspNetCore.Rewrite
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InputParserUnrecognizedParameter"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Syntax error for integers in comparison.
|
||||
/// </summary>
|
||||
internal static string Error_IntegerMatch_FormatExceptionMessage
|
||||
{
|
||||
get { return GetString("Error_IntegerMatch_FormatExceptionMessage"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Syntax error for integers in comparison.
|
||||
/// </summary>
|
||||
internal static string FormatError_IntegerMatch_FormatExceptionMessage()
|
||||
{
|
||||
return GetString("Error_IntegerMatch_FormatExceptionMessage");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error parsing the mod_rewrite rule. The cookie flag (CO) has an incorrect format '{0}'.
|
||||
/// </summary>
|
||||
internal static string Error_InvalidChangeCookieFlag
|
||||
{
|
||||
get { return GetString("Error_InvalidChangeCookieFlag"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error parsing the mod_rewrite rule. The cookie flag (CO) has an incorrect format '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatError_InvalidChangeCookieFlag(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InvalidChangeCookieFlag"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse the mod_rewrite file. Message: '{0}'. Line number '{1}'.
|
||||
/// </summary>
|
||||
|
|
@ -139,35 +187,19 @@ namespace Microsoft.AspNetCore.Rewrite
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Syntax error for integers in comparison.
|
||||
/// Could not parse the UrlRewrite file. Message: '{0}'. Line number '{1}': '{2}'.
|
||||
/// </summary>
|
||||
internal static string Error_IntegerMatch_FormatExceptionMessage
|
||||
internal static string Error_UrlRewriteParseError
|
||||
{
|
||||
get { return GetString("Error_IntegerMatch_FormatExceptionMessage"); }
|
||||
get { return GetString("Error_UrlRewriteParseError"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Syntax error for integers in comparison.
|
||||
/// Could not parse the UrlRewrite file. Message: '{0}'. Line number '{1}': '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatError_IntegerMatch_FormatExceptionMessage()
|
||||
internal static string FormatError_UrlRewriteParseError(object p0, object p1, object p2)
|
||||
{
|
||||
return GetString("Error_IntegerMatch_FormatExceptionMessage");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error adding a mod_rewrite rule. The change environment flag is not supported.
|
||||
/// </summary>
|
||||
internal static string Error_ChangeEnvironmentNotSupported
|
||||
{
|
||||
get { return GetString("Error_ChangeEnvironmentNotSupported"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error adding a mod_rewrite rule. The change environment flag is not supported.
|
||||
/// </summary>
|
||||
internal static string FormatError_ChangeEnvironmentNotSupported()
|
||||
{
|
||||
return GetString("Error_ChangeEnvironmentNotSupported");
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_UrlRewriteParseError"), p0, p1, p2);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
|
|
|
|||
|
|
@ -117,8 +117,11 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Error_UrlRewriteParseError" xml:space="preserve">
|
||||
<value>Could not parse the UrlRewrite file. Message: '{0}'. Line number '{1}': '{2}'.</value>
|
||||
<data name="Error_ChangeEnvironmentNotSupported" xml:space="preserve">
|
||||
<value>Error adding a mod_rewrite rule. The change environment flag is not supported.</value>
|
||||
</data>
|
||||
<data name="Error_CouldNotParseInteger" xml:space="preserve">
|
||||
<value>Could not parse integer from value '{0}'.</value>
|
||||
</data>
|
||||
<data name="Error_InputParserIndexOutOfRange" xml:space="preserve">
|
||||
<value>Index out of range for backreference: '{0}' at string index: '{1}'</value>
|
||||
|
|
@ -135,16 +138,19 @@
|
|||
<data name="Error_InputParserUnrecognizedParameter" xml:space="preserve">
|
||||
<value>Unrecognized parameter type: '{0}', terminated at string index: '{1}'</value>
|
||||
</data>
|
||||
<data name="Error_IntegerMatch_FormatExceptionMessage" xml:space="preserve">
|
||||
<value>Syntax error for integers in comparison.</value>
|
||||
</data>
|
||||
<data name="Error_InvalidChangeCookieFlag" xml:space="preserve">
|
||||
<value>Error parsing the mod_rewrite rule. The cookie flag (CO) has an incorrect format '{0}'.</value>
|
||||
</data>
|
||||
<data name="Error_ModRewriteParseError" xml:space="preserve">
|
||||
<value>Could not parse the mod_rewrite file. Message: '{0}'. Line number '{1}'.</value>
|
||||
</data>
|
||||
<data name="Error_ModRewriteGeneralParseError" xml:space="preserve">
|
||||
<value>Could not parse the mod_rewrite file. Line number '{0}'.</value>
|
||||
</data>
|
||||
<data name="Error_IntegerMatch_FormatExceptionMessage" xml:space="preserve">
|
||||
<value>Syntax error for integers in comparison.</value>
|
||||
</data>
|
||||
<data name="Error_ChangeEnvironmentNotSupported" xml:space="preserve">
|
||||
<value>Error adding a mod_rewrite rule. The change environment flag is not supported.</value>
|
||||
<data name="Error_UrlRewriteParseError" xml:space="preserve">
|
||||
<value>Could not parse the UrlRewrite file. Message: '{0}'. Line number '{1}': '{2}'.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// 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.Rewrite.Internal.ApacheModRewrite;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Test
|
||||
{
|
||||
public class CookieActionFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public void Creates_OneCookie()
|
||||
{
|
||||
var cookie = new CookieActionFactory().Create("NAME:VALUE:DOMAIN:1440:path:secure:httponly");
|
||||
|
||||
Assert.Equal("NAME", cookie.Name);
|
||||
Assert.Equal("VALUE", cookie.Value);
|
||||
Assert.Equal("DOMAIN", cookie.Domain);
|
||||
Assert.Equal(TimeSpan.FromMinutes(1440), cookie.Lifetime);
|
||||
Assert.Equal("path", cookie.Path);
|
||||
Assert.True(cookie.Secure);
|
||||
Assert.True(cookie.HttpOnly);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Creates_OneCookie_AltSeparator()
|
||||
{
|
||||
var action = new CookieActionFactory().Create(";NAME;VALUE:WithColon;DOMAIN;1440;path;secure;httponly");
|
||||
|
||||
Assert.Equal("NAME", action.Name);
|
||||
Assert.Equal("VALUE:WithColon", action.Value);
|
||||
Assert.Equal("DOMAIN", action.Domain);
|
||||
Assert.Equal(TimeSpan.FromMinutes(1440), action.Lifetime);
|
||||
Assert.Equal("path", action.Path);
|
||||
Assert.True(action.Secure);
|
||||
Assert.True(action.HttpOnly);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Creates_HttpOnly()
|
||||
{
|
||||
var action = new CookieActionFactory().Create(";NAME;VALUE;DOMAIN;;;;httponly");
|
||||
|
||||
Assert.Equal("NAME", action.Name);
|
||||
Assert.Equal("VALUE", action.Value);
|
||||
Assert.Equal("DOMAIN", action.Domain);
|
||||
Assert.Equal(0, action.Lifetime.TotalSeconds);
|
||||
Assert.Equal(string.Empty, action.Path);
|
||||
Assert.False(action.Secure);
|
||||
Assert.True(action.HttpOnly);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("NAME::", "", null)]
|
||||
[InlineData("NAME::domain", "", "domain")]
|
||||
[InlineData("NAME:VALUE:;", "VALUE", null)] // special case with dangling ';'
|
||||
[InlineData("NAME:value:", "value", null)]
|
||||
[InlineData(" NAME : v : ", "v", null)] // trims values
|
||||
public void TrimsValues(string flagValue, string value, string domain)
|
||||
{
|
||||
var factory = new CookieActionFactory();
|
||||
var action = factory.Create(flagValue);
|
||||
Assert.Equal("NAME", action.Name);
|
||||
Assert.NotNull(action.Value);
|
||||
Assert.Equal(value, action.Value);
|
||||
Assert.Equal(domain, action.Domain);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("NAME")] // missing value and domain
|
||||
[InlineData("NAME: ")] // missing domain
|
||||
[InlineData("NAME:VALUE")] // missing domain
|
||||
[InlineData(";NAME;VAL:UE")] // missing domain
|
||||
public void ThrowsForInvalidFormat(string flagValue)
|
||||
{
|
||||
var factory = new CookieActionFactory();
|
||||
var ex = Assert.Throws<FormatException>(() => factory.Create(flagValue));
|
||||
Assert.Equal(Resources.FormatError_InvalidChangeCookieFlag(flagValue), ex.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bad_number")]
|
||||
[InlineData("-1")]
|
||||
[InlineData("0.9")]
|
||||
public void ThrowsForInvalidIntFormat(string badInt)
|
||||
{
|
||||
var factory = new CookieActionFactory();
|
||||
var ex = Assert.Throws<FormatException>(() => factory.Create("NAME:VALUE:DOMAIN:" + badInt));
|
||||
Assert.Equal(Resources.FormatError_CouldNotParseInteger(badInt), ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -62,8 +62,20 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
|||
[Fact]
|
||||
public void FlagParser_AssertArgumentExceptionWhenFlagsAreNullOrEmpty()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new FlagParser().Parse(null));
|
||||
Assert.Throws<ArgumentNullException>(() => new FlagParser().Parse(string.Empty));
|
||||
Assert.Throws<ArgumentException>(() => new FlagParser().Parse(null));
|
||||
Assert.Throws<ArgumentException>(() => new FlagParser().Parse(string.Empty));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("[CO=VAR:VAL]", "VAR:VAL")]
|
||||
[InlineData("[CO=!VAR]", "!VAR")]
|
||||
[InlineData("[CO=;NAME:VALUE;ABC:123]", ";NAME:VALUE;ABC:123")]
|
||||
public void Flag_ParserHandlesComplexFlags(string flagString, string expected)
|
||||
{
|
||||
var results = new FlagParser().Parse(flagString);
|
||||
string value;
|
||||
Assert.True(results.GetValue(FlagType.Cookie, out value));
|
||||
Assert.Equal(expected, value);
|
||||
}
|
||||
|
||||
public bool DictionaryContentsEqual<TKey, TValue>(IDictionary<TKey, TValue> dictionary, IDictionary<TKey, TValue> other)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
// 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.Http;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlActions
|
||||
{
|
||||
public class ChangeCookieActionTests
|
||||
{
|
||||
[Fact]
|
||||
public void SetsCookie()
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||
var action = new ChangeCookieAction("Cookie", () => now)
|
||||
{
|
||||
Value = "Chocolate Chip",
|
||||
Domain = "contoso.com",
|
||||
Lifetime = TimeSpan.FromMinutes(1440),
|
||||
Path = "/recipes",
|
||||
Secure = true,
|
||||
HttpOnly = true
|
||||
};
|
||||
|
||||
action.ApplyAction(context, null, null);
|
||||
|
||||
var cookieHeaders = context.HttpContext.Response.Headers[HeaderNames.SetCookie];
|
||||
var header = Assert.Single(cookieHeaders);
|
||||
Assert.Equal($"Cookie=Chocolate%20Chip; expires={HeaderUtilities.FormatDate(now.AddMinutes(1440))}; domain=contoso.com; path=/recipes; secure; httponly", header);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZeroLifetime()
|
||||
{
|
||||
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||
var action = new ChangeCookieAction("Cookie")
|
||||
{
|
||||
Value = "Chocolate Chip",
|
||||
};
|
||||
|
||||
action.ApplyAction(context, null, null);
|
||||
|
||||
var cookieHeaders = context.HttpContext.Response.Headers[HeaderNames.SetCookie];
|
||||
var header = Assert.Single(cookieHeaders);
|
||||
Assert.Equal($"Cookie=Chocolate%20Chip", header);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void UnsetCookie()
|
||||
{
|
||||
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||
var action = new ChangeCookieAction("Cookie");
|
||||
|
||||
action.ApplyAction(context, null, null);
|
||||
|
||||
var cookieHeaders = context.HttpContext.Response.Headers[HeaderNames.SetCookie];
|
||||
var header = Assert.Single(cookieHeaders);
|
||||
Assert.Equal($"Cookie=", header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlActions
|
|||
public void Forbidden_Verify403IsInStatusCode()
|
||||
{
|
||||
|
||||
var context = new RewriteContext {HttpContext = new DefaultHttpContext()};
|
||||
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||
var action = new ForbiddenAction();
|
||||
|
||||
action.ApplyAction(context, null, null);
|
||||
|
|
|
|||
Loading…
Reference in New Issue