You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
14
mcs/class/referencesource/System.Web/Routing/BoundUrl.cs
Normal file
14
mcs/class/referencesource/System.Web/Routing/BoundUrl.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace System.Web.Routing {
|
||||
// Represents a URL generated from a ParsedRoute
|
||||
internal class BoundUrl {
|
||||
public string Url {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public RouteValueDictionary Values {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
// Represents a segment of a URL that is not a separator. It contains subsegments such as literals and parameters.
|
||||
internal sealed class ContentPathSegment : PathSegment {
|
||||
public ContentPathSegment(IList<PathSubsegment> subsegments) {
|
||||
Subsegments = subsegments;
|
||||
}
|
||||
|
||||
public bool IsCatchAll {
|
||||
get {
|
||||
//
|
||||
return Subsegments.Any<PathSubsegment>(seg => (seg is ParameterSubsegment) && (((ParameterSubsegment)seg).IsCatchAll));
|
||||
}
|
||||
}
|
||||
|
||||
public IList<PathSubsegment> Subsegments {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#if ROUTE_DEBUGGING
|
||||
public override string LiteralText {
|
||||
get {
|
||||
List<string> s = new List<string>();
|
||||
foreach (PathSubsegment subsegment in Subsegments) {
|
||||
s.Add(subsegment.LiteralText);
|
||||
}
|
||||
return String.Join(String.Empty, s.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
List<string> s = new List<string>();
|
||||
foreach (PathSubsegment subsegment in Subsegments) {
|
||||
s.Add(subsegment.ToString());
|
||||
}
|
||||
return "[ " + String.Join(", ", s.ToArray()) + " ]";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
namespace System.Web.Routing {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public class HttpMethodConstraint : IRouteConstraint {
|
||||
|
||||
public HttpMethodConstraint(params string[] allowedMethods) {
|
||||
if (allowedMethods == null) {
|
||||
throw new ArgumentNullException("allowedMethods");
|
||||
}
|
||||
|
||||
AllowedMethods = allowedMethods.ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
public ICollection<string> AllowedMethods {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected virtual bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
|
||||
if (httpContext == null) {
|
||||
throw new ArgumentNullException("httpContext");
|
||||
}
|
||||
if (route == null) {
|
||||
throw new ArgumentNullException("route");
|
||||
}
|
||||
if (parameterName == null) {
|
||||
throw new ArgumentNullException("parameterName");
|
||||
}
|
||||
if (values == null) {
|
||||
throw new ArgumentNullException("values");
|
||||
}
|
||||
|
||||
switch (routeDirection) {
|
||||
case RouteDirection.IncomingRequest:
|
||||
return AllowedMethods.Any(method => String.Equals(method, httpContext.Request.HttpMethod,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
case RouteDirection.UrlGeneration:
|
||||
// We need to see if the user specified the HTTP method explicitly. Consider these two routes:
|
||||
//
|
||||
// a) Route: Url = "/{foo}", Constraints = { httpMethod = new HttpMethodConstraint("GET") }
|
||||
// b) Route: Url = "/{foo}", Constraints = { httpMethod = new HttpMethodConstraint("POST") }
|
||||
//
|
||||
// A user might know ahead of time that a URL he is generating might be used with a particular HTTP
|
||||
// method. If a URL will be used for an HTTP POST but we match on (a) while generating the URL, then
|
||||
// the HTTP GET-specific route will be used for URL generation, which might have undesired behavior.
|
||||
// To prevent this, a user might call RouteCollection.GetVirtualPath(..., { httpMethod = "POST" }) to
|
||||
// signal that he is generating a URL that will be used for an HTTP POST, so he wants the URL
|
||||
// generation to be performed by the (b) route instead of the (a) route, consistent with what would
|
||||
// happen on incoming requests.
|
||||
|
||||
object parameterValue;
|
||||
if (!values.TryGetValue(parameterName, out parameterValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
string parameterValueString = parameterValue as string;
|
||||
if (parameterValueString == null) {
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.HttpMethodConstraint_ParameterValueMustBeString), parameterName, route.Url));
|
||||
}
|
||||
|
||||
return AllowedMethods.Any(method => String.Equals(method, parameterValueString,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#region IRouteConstraint Members
|
||||
bool IRouteConstraint.Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
|
||||
return Match(httpContext, route, parameterName, values, routeDirection);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Web;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public interface IRouteConstraint {
|
||||
bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public interface IRouteHandler {
|
||||
IHttpHandler GetHttpHandler(RequestContext requestContext);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace System.Web.Routing {
|
||||
|
||||
// Represents a literal subsegment of a ContentPathSegment
|
||||
internal sealed class LiteralSubsegment : PathSubsegment {
|
||||
public LiteralSubsegment(string literal) {
|
||||
Literal = literal;
|
||||
}
|
||||
|
||||
public string Literal {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#if ROUTE_DEBUGGING
|
||||
public override string LiteralText {
|
||||
get {
|
||||
return Literal;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "\"" + Literal + "\"";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
114
mcs/class/referencesource/System.Web/Routing/PageRouteHandler.cs
Normal file
114
mcs/class/referencesource/System.Web/Routing/PageRouteHandler.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebFormRouteHandler.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Routing {
|
||||
|
||||
using System;
|
||||
using System.Web.UI;
|
||||
using System.Web.Compilation;
|
||||
using System.Web.Security;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Security.Principal;
|
||||
|
||||
public class PageRouteHandler : IRouteHandler {
|
||||
public PageRouteHandler(string virtualPath)
|
||||
: this(virtualPath, true) {
|
||||
}
|
||||
|
||||
public PageRouteHandler(string virtualPath, bool checkPhysicalUrlAccess) {
|
||||
if (string.IsNullOrEmpty(virtualPath) || !virtualPath.StartsWith("~/", StringComparison.OrdinalIgnoreCase)) {
|
||||
throw new ArgumentException(SR.GetString(SR.PageRouteHandler_InvalidVirtualPath), "virtualPath");
|
||||
}
|
||||
|
||||
this.VirtualPath = virtualPath;
|
||||
this.CheckPhysicalUrlAccess = checkPhysicalUrlAccess;
|
||||
_useRouteVirtualPath = VirtualPath.Contains("{");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is the full virtual path (using tilde syntax) to the WebForm page.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Needs to be thread safe so this is only settable via ctor.
|
||||
/// </remarks>
|
||||
public string VirtualPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Because we're not actually rewriting the URL, ASP.NET's URL Auth will apply
|
||||
/// to the incoming request URL and not the URL of the physical WebForm page.
|
||||
/// Setting this to true (default) will apply URL access rules against the
|
||||
/// physical file.
|
||||
/// </summary>
|
||||
/// <value>True by default</value>
|
||||
public bool CheckPhysicalUrlAccess { get; private set; }
|
||||
|
||||
private bool _useRouteVirtualPath;
|
||||
private Route _routeVirtualPath;
|
||||
private Route RouteVirtualPath {
|
||||
get {
|
||||
if (_routeVirtualPath == null) {
|
||||
//Trim off ~/
|
||||
_routeVirtualPath = new Route(VirtualPath.Substring(2), this);
|
||||
}
|
||||
return _routeVirtualPath;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckUrlAccess(string virtualPath, RequestContext requestContext) {
|
||||
IPrincipal user = requestContext.HttpContext.User;
|
||||
// If there's no authenticated user, use the default identity
|
||||
if (user == null) {
|
||||
user = new GenericPrincipal(new GenericIdentity(String.Empty, String.Empty), new string[0]);
|
||||
}
|
||||
return CheckUrlAccessWithAssert(virtualPath, requestContext, user);
|
||||
}
|
||||
|
||||
[SecurityPermission(SecurityAction.Assert, Unrestricted = true)]
|
||||
private bool CheckUrlAccessWithAssert(string virtualPath, RequestContext requestContext, IPrincipal user) {
|
||||
return UrlAuthorizationModule.CheckUrlAccessForPrincipal(virtualPath, user, requestContext.HttpContext.Request.HttpMethod);
|
||||
}
|
||||
|
||||
public virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
|
||||
if (requestContext == null) {
|
||||
throw new ArgumentNullException("requestContext");
|
||||
}
|
||||
|
||||
string virtualPath = GetSubstitutedVirtualPath(requestContext);
|
||||
// Virtual Path ----s up with query strings, so we need to strip them off
|
||||
int qmark = virtualPath.IndexOf('?');
|
||||
if (qmark != -1) {
|
||||
virtualPath = virtualPath.Substring(0, qmark);
|
||||
}
|
||||
if (this.CheckPhysicalUrlAccess && !CheckUrlAccess(virtualPath, requestContext)) {
|
||||
return new UrlAuthFailureHandler();
|
||||
}
|
||||
|
||||
Page page = BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page)) as Page;
|
||||
return page;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the virtual path to the resource after applying substitutions based on route data.
|
||||
/// </summary>
|
||||
/// <param name="requestContext"></param>
|
||||
/// <returns></returns>
|
||||
public string GetSubstitutedVirtualPath(RequestContext requestContext) {
|
||||
if (requestContext == null) {
|
||||
throw new ArgumentNullException("requestContext");
|
||||
}
|
||||
|
||||
if (!_useRouteVirtualPath)
|
||||
return VirtualPath;
|
||||
|
||||
VirtualPathData vpd = RouteVirtualPath.GetVirtualPath(requestContext, requestContext.RouteData.Values);
|
||||
//
|
||||
if (vpd == null)
|
||||
return VirtualPath;
|
||||
return "~/" + vpd.VirtualPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace System.Web.Routing {
|
||||
|
||||
// Represents a parameter subsegment of a ContentPathSegment
|
||||
internal sealed class ParameterSubsegment : PathSubsegment {
|
||||
public ParameterSubsegment(string parameterName) {
|
||||
if (parameterName.StartsWith("*", StringComparison.Ordinal)) {
|
||||
ParameterName = parameterName.Substring(1);
|
||||
IsCatchAll = true;
|
||||
}
|
||||
else {
|
||||
ParameterName = parameterName;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCatchAll {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string ParameterName {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#if ROUTE_DEBUGGING
|
||||
public override string LiteralText {
|
||||
get {
|
||||
return "{" + (IsCatchAll ? "*" : String.Empty) + ParameterName + "}";
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "{" + (IsCatchAll ? "*" : String.Empty) + ParameterName + "}";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
672
mcs/class/referencesource/System.Web/Routing/ParsedRoute.cs
Normal file
672
mcs/class/referencesource/System.Web/Routing/ParsedRoute.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
mcs/class/referencesource/System.Web/Routing/PathSegment.cs
Normal file
11
mcs/class/referencesource/System.Web/Routing/PathSegment.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace System.Web.Routing {
|
||||
|
||||
// Represents a segment of a URL such as a separator or content
|
||||
internal abstract class PathSegment {
|
||||
#if ROUTE_DEBUGGING
|
||||
public abstract string LiteralText {
|
||||
get;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace System.Web.Routing {
|
||||
|
||||
// Represents a subsegment of a ContentPathSegment such as a parameter or a literal.
|
||||
internal abstract class PathSubsegment {
|
||||
#if ROUTE_DEBUGGING
|
||||
public abstract string LiteralText {
|
||||
get;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public class RequestContext {
|
||||
public RequestContext() {
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
|
||||
Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
|
||||
public RequestContext(HttpContextBase httpContext, RouteData routeData) {
|
||||
if (httpContext == null) {
|
||||
throw new ArgumentNullException("httpContext");
|
||||
}
|
||||
if (routeData == null) {
|
||||
throw new ArgumentNullException("routeData");
|
||||
}
|
||||
HttpContext = httpContext;
|
||||
RouteData = routeData;
|
||||
}
|
||||
|
||||
public virtual HttpContextBase HttpContext {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public virtual RouteData RouteData {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
186
mcs/class/referencesource/System.Web/Routing/Route.cs
Normal file
186
mcs/class/referencesource/System.Web/Routing/Route.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
namespace System.Web.Routing {
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public class Route : RouteBase {
|
||||
private const string HttpMethodParameterName = "httpMethod";
|
||||
|
||||
private string _url;
|
||||
private ParsedRoute _parsedRoute;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "This is a URL template with special characters, not just a regular valid URL.")]
|
||||
public Route(string url, IRouteHandler routeHandler) {
|
||||
Url = url;
|
||||
RouteHandler = routeHandler;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "This is a URL template with special characters, not just a regular valid URL.")]
|
||||
public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) {
|
||||
Url = url;
|
||||
Defaults = defaults;
|
||||
RouteHandler = routeHandler;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "This is a URL template with special characters, not just a regular valid URL.")]
|
||||
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) {
|
||||
Url = url;
|
||||
Defaults = defaults;
|
||||
Constraints = constraints;
|
||||
RouteHandler = routeHandler;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "This is a URL template with special characters, not just a regular valid URL.")]
|
||||
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) {
|
||||
Url = url;
|
||||
Defaults = defaults;
|
||||
Constraints = constraints;
|
||||
DataTokens = dataTokens;
|
||||
RouteHandler = routeHandler;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
|
||||
Justification = "This property is settable so people can use object initializers.")]
|
||||
public RouteValueDictionary Constraints {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
|
||||
Justification = "This property is settable so people can use object initializers.")]
|
||||
public RouteValueDictionary DataTokens {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
|
||||
Justification = "This property is settable so people can use object initializers.")]
|
||||
public RouteValueDictionary Defaults {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public IRouteHandler RouteHandler {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings",
|
||||
Justification = "This is a URL template with special characters, not just a regular valid URL.")]
|
||||
public string Url {
|
||||
get {
|
||||
return _url ?? String.Empty;
|
||||
}
|
||||
set {
|
||||
// The parser will throw for invalid routes. We don't have to worry
|
||||
// about _parsedRoute getting out of [....] with _url since the latter
|
||||
// won't get set unless we can parse the route.
|
||||
_parsedRoute = RouteParser.Parse(value);
|
||||
|
||||
// If we passed the parsing stage, save the original URL value
|
||||
_url = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override RouteData GetRouteData(HttpContextBase httpContext) {
|
||||
// Parse incoming URL (we trim off the first two chars since they're always "~/")
|
||||
string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
|
||||
|
||||
RouteValueDictionary values = _parsedRoute.Match(requestPath, Defaults);
|
||||
|
||||
if (values == null) {
|
||||
// If we got back a null value set, that means the URL did not match
|
||||
return null;
|
||||
}
|
||||
|
||||
RouteData routeData = new RouteData(this, RouteHandler);
|
||||
|
||||
// Validate the values
|
||||
if (!ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Copy the matched values
|
||||
foreach (var value in values) {
|
||||
routeData.Values.Add(value.Key, value.Value);
|
||||
}
|
||||
|
||||
// Copy the DataTokens from the Route to the RouteData
|
||||
if (DataTokens != null) {
|
||||
foreach (var prop in DataTokens) {
|
||||
routeData.DataTokens[prop.Key] = prop.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return routeData;
|
||||
}
|
||||
|
||||
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) {
|
||||
// Try to generate a URL that represents the values passed in based on current
|
||||
// values from the RouteData and new values using the specified Route.
|
||||
|
||||
BoundUrl result = _parsedRoute.Bind(requestContext.RouteData.Values, values, Defaults, Constraints);
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Verify that the route matches the validation rules
|
||||
if (!ProcessConstraints(requestContext.HttpContext, result.Values, RouteDirection.UrlGeneration)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VirtualPathData vpd = new VirtualPathData(this, result.Url);
|
||||
|
||||
// Add the DataTokens from the Route to the VirtualPathData
|
||||
if (DataTokens != null) {
|
||||
foreach (var prop in DataTokens) {
|
||||
vpd.DataTokens[prop.Key] = prop.Value;
|
||||
}
|
||||
}
|
||||
return vpd;
|
||||
}
|
||||
|
||||
protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
|
||||
IRouteConstraint customConstraint = constraint as IRouteConstraint;
|
||||
if (customConstraint != null) {
|
||||
return customConstraint.Match(httpContext, this, parameterName, values, routeDirection);
|
||||
}
|
||||
|
||||
// If there was no custom constraint, then treat the constraint as a string which represents a Regex.
|
||||
string constraintsRule = constraint as string;
|
||||
if (constraintsRule == null) {
|
||||
throw new InvalidOperationException(String.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.Route_ValidationMustBeStringOrCustomConstraint),
|
||||
parameterName,
|
||||
Url));
|
||||
}
|
||||
|
||||
object parameterValue;
|
||||
values.TryGetValue(parameterName, out parameterValue);
|
||||
string parameterValueString = Convert.ToString(parameterValue, CultureInfo.InvariantCulture);
|
||||
string constraintsRegEx = "^(" + constraintsRule + ")$";
|
||||
return Regex.IsMatch(parameterValueString, constraintsRegEx,
|
||||
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection) {
|
||||
if (Constraints != null) {
|
||||
foreach (var constraintsItem in Constraints) {
|
||||
if (!ProcessConstraint(httpContext, constraintsItem.Value, constraintsItem.Key, values, routeDirection)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
mcs/class/referencesource/System.Web/Routing/RouteBase.cs
Normal file
21
mcs/class/referencesource/System.Web/Routing/RouteBase.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public abstract class RouteBase {
|
||||
public abstract RouteData GetRouteData(HttpContextBase httpContext);
|
||||
public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
|
||||
|
||||
// Default needs to be true to avoid breaking change
|
||||
private bool _routeExistingFiles = true;
|
||||
public bool RouteExistingFiles {
|
||||
get {
|
||||
return _routeExistingFiles;
|
||||
}
|
||||
set {
|
||||
_routeExistingFiles = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
405
mcs/class/referencesource/System.Web/Routing/RouteCollection.cs
Normal file
405
mcs/class/referencesource/System.Web/Routing/RouteCollection.cs
Normal file
@@ -0,0 +1,405 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Web.Hosting;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public class RouteCollection : Collection<RouteBase> {
|
||||
private Dictionary<string, RouteBase> _namedMap = new Dictionary<string, RouteBase>(StringComparer.OrdinalIgnoreCase);
|
||||
private VirtualPathProvider _vpp;
|
||||
|
||||
private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
|
||||
|
||||
public RouteCollection() {
|
||||
}
|
||||
|
||||
public RouteCollection(VirtualPathProvider virtualPathProvider) {
|
||||
VPP = virtualPathProvider;
|
||||
}
|
||||
|
||||
public bool AppendTrailingSlash {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool LowercaseUrls {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool RouteExistingFiles {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private VirtualPathProvider VPP {
|
||||
get {
|
||||
if (_vpp == null) {
|
||||
return HostingEnvironment.VirtualPathProvider;
|
||||
}
|
||||
return _vpp;
|
||||
}
|
||||
set {
|
||||
_vpp = value;
|
||||
}
|
||||
}
|
||||
|
||||
public RouteBase this[string name] {
|
||||
get {
|
||||
if (String.IsNullOrEmpty(name)) {
|
||||
return null;
|
||||
}
|
||||
RouteBase route;
|
||||
if (_namedMap.TryGetValue(name, out route)) {
|
||||
return route;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(string name, RouteBase item) {
|
||||
if (item == null) {
|
||||
throw new ArgumentNullException("item");
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(name)) {
|
||||
if (_namedMap.ContainsKey(name)) {
|
||||
throw new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.RouteCollection_DuplicateName),
|
||||
name),
|
||||
"name");
|
||||
}
|
||||
}
|
||||
|
||||
Add(item);
|
||||
if (!String.IsNullOrEmpty(name)) {
|
||||
_namedMap[name] = item;
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "Warning was suppressed for consistency with existing similar routing API")]
|
||||
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile) {
|
||||
return MapPageRoute(routeName, routeUrl, physicalFile, true /* checkPhysicalUrlAccess */, null, null, null);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "Warning was suppressed for consistency with existing similar routing API")]
|
||||
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess) {
|
||||
return MapPageRoute(routeName, routeUrl, physicalFile, checkPhysicalUrlAccess, null, null, null);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "Warning was suppressed for consistency with existing similar routing API")]
|
||||
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults) {
|
||||
return MapPageRoute(routeName, routeUrl, physicalFile, checkPhysicalUrlAccess, defaults, null, null);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "Warning was suppressed for consistency with existing similar routing API")]
|
||||
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints) {
|
||||
return MapPageRoute(routeName, routeUrl, physicalFile, checkPhysicalUrlAccess, defaults, constraints, null);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "Warning was suppressed for consistency with existing similar routing API")]
|
||||
public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens) {
|
||||
if (routeUrl == null) {
|
||||
throw new ArgumentNullException("routeUrl");
|
||||
}
|
||||
Route route = new Route(routeUrl, defaults, constraints, dataTokens, new PageRouteHandler(physicalFile, checkPhysicalUrlAccess));
|
||||
Add(routeName, route);
|
||||
return route;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
protected override void ClearItems() {
|
||||
_namedMap.Clear();
|
||||
base.ClearItems();
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Not worth a breaking change.")]
|
||||
public IDisposable GetReadLock() {
|
||||
_rwLock.EnterReadLock();
|
||||
return new ReadLockDisposable(_rwLock);
|
||||
}
|
||||
|
||||
private RequestContext GetRequestContext(RequestContext requestContext) {
|
||||
if (requestContext != null) {
|
||||
return requestContext;
|
||||
}
|
||||
HttpContext httpContext = HttpContext.Current;
|
||||
if (httpContext == null) {
|
||||
throw new InvalidOperationException(SR.GetString(SR.RouteCollection_RequiresContext));
|
||||
}
|
||||
return new RequestContext(new HttpContextWrapper(httpContext), new RouteData());
|
||||
}
|
||||
|
||||
// Returns true if this is a request to an existing file
|
||||
private bool IsRouteToExistingFile(HttpContextBase httpContext) {
|
||||
string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
|
||||
return ((requestPath != "~/") &&
|
||||
(VPP != null) &&
|
||||
(VPP.FileExists(requestPath) ||
|
||||
VPP.DirectoryExists(requestPath)));
|
||||
}
|
||||
|
||||
public RouteData GetRouteData(HttpContextBase httpContext) {
|
||||
if (httpContext == null) {
|
||||
throw new ArgumentNullException("httpContext");
|
||||
}
|
||||
if (httpContext.Request == null) {
|
||||
throw new ArgumentException(SR.GetString(SR.RouteTable_ContextMissingRequest), "httpContext");
|
||||
}
|
||||
|
||||
// Optimize performance when the route collection is empty. The main improvement is that we avoid taking
|
||||
// a read lock when the collection is empty. Without this check, the UrlRoutingModule causes a 25%-50%
|
||||
// regression in HelloWorld RPS due to lock contention. The UrlRoutingModule is now in the root web.config,
|
||||
// so we need to ensure the module is performant, especially when you are not using routing.
|
||||
// This check does introduce a slight bug, in that if a writer clears the collection as part of a write
|
||||
// transaction, a reader may see the collection when it's empty, which the read lock is supposed to prevent.
|
||||
// We will investigate a better fix in Dev10 Beta2. The Beta1 bug is Dev10 652986.
|
||||
if (Count == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isRouteToExistingFile = false;
|
||||
bool doneRouteCheck = false; // We only want to do the route check once
|
||||
if (!RouteExistingFiles) {
|
||||
isRouteToExistingFile = IsRouteToExistingFile(httpContext);
|
||||
doneRouteCheck = true;
|
||||
if (isRouteToExistingFile) {
|
||||
// If we're not routing existing files and the file exists, we stop processing routes
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Go through all the configured routes and find the first one that returns a match
|
||||
using (GetReadLock()) {
|
||||
foreach (RouteBase route in this) {
|
||||
RouteData routeData = route.GetRouteData(httpContext);
|
||||
if (routeData != null) {
|
||||
// If we're not routing existing files on this route and the file exists, we also stop processing routes
|
||||
if (!route.RouteExistingFiles) {
|
||||
if (!doneRouteCheck) {
|
||||
isRouteToExistingFile = IsRouteToExistingFile(httpContext);
|
||||
doneRouteCheck = true;
|
||||
}
|
||||
if (isRouteToExistingFile) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return routeData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1307:SpecifyStringComparison", MessageId = "System.String.EndsWith(System.String)", Justification = @"okay")]
|
||||
private string NormalizeVirtualPath(RequestContext requestContext, string virtualPath) {
|
||||
string url = System.Web.UI.Util.GetUrlWithApplicationPath(requestContext.HttpContext, virtualPath);
|
||||
|
||||
if (LowercaseUrls || AppendTrailingSlash) {
|
||||
int iqs = url.IndexOfAny(new char[] { '?', '#' });
|
||||
string urlWithoutQs;
|
||||
string qs;
|
||||
if (iqs >= 0) {
|
||||
urlWithoutQs = url.Substring(0, iqs);
|
||||
qs = url.Substring(iqs);
|
||||
}
|
||||
else {
|
||||
urlWithoutQs = url;
|
||||
qs = "";
|
||||
}
|
||||
|
||||
// Don't lowercase the query string
|
||||
if (LowercaseUrls) {
|
||||
urlWithoutQs = urlWithoutQs.ToLowerInvariant();
|
||||
}
|
||||
|
||||
if (AppendTrailingSlash && !urlWithoutQs.EndsWith("/")) {
|
||||
urlWithoutQs += "/";
|
||||
}
|
||||
|
||||
url = urlWithoutQs + qs;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) {
|
||||
requestContext = GetRequestContext(requestContext);
|
||||
|
||||
// Go through all the configured routes and find the first one that returns a match
|
||||
using (GetReadLock()) {
|
||||
foreach (RouteBase route in this) {
|
||||
VirtualPathData vpd = route.GetVirtualPath(requestContext, values);
|
||||
if (vpd != null) {
|
||||
vpd.VirtualPath = NormalizeVirtualPath(requestContext, vpd.VirtualPath);
|
||||
return vpd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values) {
|
||||
requestContext = GetRequestContext(requestContext);
|
||||
|
||||
if (!String.IsNullOrEmpty(name)) {
|
||||
RouteBase namedRoute;
|
||||
bool routeFound;
|
||||
using (GetReadLock()) {
|
||||
routeFound = _namedMap.TryGetValue(name, out namedRoute);
|
||||
}
|
||||
if (routeFound) {
|
||||
VirtualPathData vpd = namedRoute.GetVirtualPath(requestContext, values);
|
||||
if (vpd != null) {
|
||||
vpd.VirtualPath = NormalizeVirtualPath(requestContext, vpd.VirtualPath);
|
||||
return vpd;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
throw new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.RouteCollection_NameNotFound),
|
||||
name),
|
||||
"name");
|
||||
}
|
||||
}
|
||||
else {
|
||||
return GetVirtualPath(requestContext, values);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Not worth a breaking change.")]
|
||||
public IDisposable GetWriteLock() {
|
||||
_rwLock.EnterWriteLock();
|
||||
return new WriteLockDisposable(_rwLock);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "This is not a regular URL as it may contain special routing characters.")]
|
||||
public void Ignore(string url) {
|
||||
Ignore(url, null /* constraints */);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
|
||||
Justification = "This is not a regular URL as it may contain special routing characters.")]
|
||||
public void Ignore(string url, object constraints) {
|
||||
if (url == null) {
|
||||
throw new ArgumentNullException("url");
|
||||
}
|
||||
|
||||
IgnoreRouteInternal route = new IgnoreRouteInternal(url) {
|
||||
Constraints = new RouteValueDictionary(constraints)
|
||||
};
|
||||
|
||||
Add(route);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
protected override void InsertItem(int index, RouteBase item) {
|
||||
if (item == null) {
|
||||
throw new ArgumentNullException("item");
|
||||
}
|
||||
|
||||
if (Contains(item)) {
|
||||
throw new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.GetString(SR.RouteCollection_DuplicateEntry)),
|
||||
"item");
|
||||
}
|
||||
base.InsertItem(index, item);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
protected override void RemoveItem(int index) {
|
||||
RemoveRouteName(index);
|
||||
base.RemoveItem(index);
|
||||
}
|
||||
|
||||
private void RemoveRouteName(int index) {
|
||||
// Search for the specified route and clear out its name if we have one
|
||||
RouteBase route = this[index];
|
||||
foreach (KeyValuePair<string, RouteBase> namedRoute in _namedMap) {
|
||||
if (namedRoute.Value == route) {
|
||||
_namedMap.Remove(namedRoute.Key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
protected override void SetItem(int index, RouteBase item) {
|
||||
if (item == null) {
|
||||
throw new ArgumentNullException("item");
|
||||
}
|
||||
|
||||
if (Contains(item)) {
|
||||
throw new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.GetString(SR.RouteCollection_DuplicateEntry)),
|
||||
"item");
|
||||
}
|
||||
RemoveRouteName(index);
|
||||
base.SetItem(index, item);
|
||||
}
|
||||
|
||||
private class ReadLockDisposable : IDisposable {
|
||||
|
||||
private ReaderWriterLockSlim _rwLock;
|
||||
|
||||
public ReadLockDisposable(ReaderWriterLockSlim rwLock) {
|
||||
_rwLock = rwLock;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly",
|
||||
Justification = "Type does not have a finalizer.")]
|
||||
void IDisposable.Dispose() {
|
||||
_rwLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
private class WriteLockDisposable : IDisposable {
|
||||
|
||||
private ReaderWriterLockSlim _rwLock;
|
||||
|
||||
public WriteLockDisposable(ReaderWriterLockSlim rwLock) {
|
||||
_rwLock = rwLock;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly",
|
||||
Justification = "Type does not have a finalizer.")]
|
||||
void IDisposable.Dispose() {
|
||||
_rwLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class IgnoreRouteInternal : Route {
|
||||
public IgnoreRouteInternal(string url)
|
||||
: base(url, new StopRoutingHandler()) {
|
||||
}
|
||||
|
||||
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary routeValues) {
|
||||
// Never match during route generation. This avoids the scenario where an IgnoreRoute with
|
||||
// fairly relaxed constraints ends up eagerly matching all generated URLs.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
mcs/class/referencesource/System.Web/Routing/RouteData.cs
Normal file
62
mcs/class/referencesource/System.Web/Routing/RouteData.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
namespace System.Web.Routing {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public class RouteData {
|
||||
private IRouteHandler _routeHandler;
|
||||
private RouteValueDictionary _values = new RouteValueDictionary();
|
||||
private RouteValueDictionary _dataTokens = new RouteValueDictionary();
|
||||
|
||||
public RouteData() {
|
||||
}
|
||||
|
||||
public RouteData(RouteBase route, IRouteHandler routeHandler) {
|
||||
Route = route;
|
||||
RouteHandler = routeHandler;
|
||||
}
|
||||
|
||||
public RouteValueDictionary DataTokens {
|
||||
get {
|
||||
return _dataTokens;
|
||||
}
|
||||
}
|
||||
|
||||
public RouteBase Route {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public IRouteHandler RouteHandler {
|
||||
get {
|
||||
return _routeHandler;
|
||||
}
|
||||
set {
|
||||
_routeHandler = value;
|
||||
}
|
||||
}
|
||||
|
||||
public RouteValueDictionary Values {
|
||||
get {
|
||||
return _values;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetRequiredString(string valueName) {
|
||||
object value;
|
||||
if (Values.TryGetValue(valueName, out value)) {
|
||||
string valueString = value as string;
|
||||
if (!String.IsNullOrEmpty(valueString)) {
|
||||
return valueString;
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.RouteData_RequiredValue),
|
||||
valueName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public enum RouteDirection {
|
||||
IncomingRequest,
|
||||
UrlGeneration
|
||||
}
|
||||
}
|
||||
339
mcs/class/referencesource/System.Web/Routing/RouteParser.cs
Normal file
339
mcs/class/referencesource/System.Web/Routing/RouteParser.cs
Normal file
@@ -0,0 +1,339 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
internal static class RouteParser {
|
||||
private static string GetLiteral(string segmentLiteral) {
|
||||
// Scan for errant single { and } and convert double {{ to { and double }} to }
|
||||
|
||||
// First we eliminate all escaped braces and then check if any other braces are remaining
|
||||
string newLiteral = segmentLiteral.Replace("{{", "").Replace("}}", "");
|
||||
if (newLiteral.Contains("{") || newLiteral.Contains("}")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If it's a valid format, we unescape the braces
|
||||
return segmentLiteral.Replace("{{", "{").Replace("}}", "}");
|
||||
}
|
||||
|
||||
private static int IndexOfFirstOpenParameter(string segment, int startIndex) {
|
||||
// Find the first unescaped open brace
|
||||
while (true) {
|
||||
startIndex = segment.IndexOf('{', startIndex);
|
||||
if (startIndex == -1) {
|
||||
// If there are no more open braces, stop
|
||||
return -1;
|
||||
}
|
||||
if ((startIndex + 1 == segment.Length) ||
|
||||
((startIndex + 1 < segment.Length) && (segment[startIndex + 1] != '{'))) {
|
||||
// If we found an open brace that is followed by a non-open brace, it's
|
||||
// a parameter delimiter.
|
||||
// It's also a delimiter if the open brace is the last character - though
|
||||
// it ends up being being called out as invalid later on.
|
||||
return startIndex;
|
||||
}
|
||||
// Increment by two since we want to skip both the open brace that
|
||||
// we're on as well as the subsequent character since we know for
|
||||
// sure that it is part of an escape sequence.
|
||||
startIndex += 2;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsSeparator(string s) {
|
||||
return String.Equals(s, "/", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static bool IsValidParameterName(string parameterName) {
|
||||
if (parameterName.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameterName.Length; i++) {
|
||||
char c = parameterName[i];
|
||||
if (c == '/' || c == '{' || c == '}') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool IsInvalidRouteUrl(string routeUrl) {
|
||||
return (routeUrl.StartsWith("~", StringComparison.Ordinal) ||
|
||||
routeUrl.StartsWith("/", StringComparison.Ordinal) ||
|
||||
(routeUrl.IndexOf('?') != -1));
|
||||
}
|
||||
|
||||
public static ParsedRoute Parse(string routeUrl) {
|
||||
if (routeUrl == null) {
|
||||
routeUrl = String.Empty;
|
||||
}
|
||||
|
||||
if (IsInvalidRouteUrl(routeUrl)) {
|
||||
throw new ArgumentException(SR.GetString(SR.Route_InvalidRouteUrl), "routeUrl");
|
||||
}
|
||||
|
||||
IList<string> urlParts = SplitUrlToPathSegmentStrings(routeUrl);
|
||||
Exception ex = ValidateUrlParts(urlParts);
|
||||
if (ex != null) {
|
||||
throw ex;
|
||||
}
|
||||
|
||||
IList<PathSegment> pathSegments = SplitUrlToPathSegments(urlParts);
|
||||
|
||||
Debug.Assert(urlParts.Count == pathSegments.Count, "The number of string segments should be the same as the number of path segments");
|
||||
|
||||
return new ParsedRoute(pathSegments);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly",
|
||||
Justification = "The exceptions are just constructed here, but they are thrown from a method that does have those parameter names.")]
|
||||
private static IList<PathSubsegment> ParseUrlSegment(string segment, out Exception exception) {
|
||||
int startIndex = 0;
|
||||
|
||||
List<PathSubsegment> pathSubsegments = new List<PathSubsegment>();
|
||||
|
||||
while (startIndex < segment.Length) {
|
||||
int nextParameterStart = IndexOfFirstOpenParameter(segment, startIndex);
|
||||
if (nextParameterStart == -1) {
|
||||
// If there are no more parameters in the segment, capture the remainder as a literal and stop
|
||||
string lastLiteralPart = GetLiteral(segment.Substring(startIndex));
|
||||
if (lastLiteralPart == null) {
|
||||
exception = new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.Route_MismatchedParameter),
|
||||
segment
|
||||
),
|
||||
"routeUrl");
|
||||
return null;
|
||||
}
|
||||
if (lastLiteralPart.Length > 0) {
|
||||
pathSubsegments.Add(new LiteralSubsegment(lastLiteralPart));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int nextParameterEnd = segment.IndexOf('}', nextParameterStart + 1);
|
||||
if (nextParameterEnd == -1) {
|
||||
exception = new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.Route_MismatchedParameter),
|
||||
segment
|
||||
),
|
||||
"routeUrl");
|
||||
return null;
|
||||
}
|
||||
|
||||
string literalPart = GetLiteral(segment.Substring(startIndex, nextParameterStart - startIndex));
|
||||
if (literalPart == null) {
|
||||
exception = new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.Route_MismatchedParameter),
|
||||
segment
|
||||
),
|
||||
"routeUrl");
|
||||
return null;
|
||||
}
|
||||
if (literalPart.Length > 0) {
|
||||
pathSubsegments.Add(new LiteralSubsegment(literalPart));
|
||||
}
|
||||
|
||||
string parameterName = segment.Substring(nextParameterStart + 1, nextParameterEnd - nextParameterStart - 1);
|
||||
pathSubsegments.Add(new ParameterSubsegment(parameterName));
|
||||
|
||||
startIndex = nextParameterEnd + 1;
|
||||
}
|
||||
|
||||
exception = null;
|
||||
return pathSubsegments;
|
||||
}
|
||||
|
||||
private static IList<PathSegment> SplitUrlToPathSegments(IList<string> urlParts) {
|
||||
List<PathSegment> pathSegments = new List<PathSegment>();
|
||||
|
||||
foreach (string pathSegment in urlParts) {
|
||||
bool isCurrentPartSeparator = IsSeparator(pathSegment);
|
||||
if (isCurrentPartSeparator) {
|
||||
pathSegments.Add(new SeparatorPathSegment());
|
||||
}
|
||||
else {
|
||||
Exception exception;
|
||||
IList<PathSubsegment> subsegments = ParseUrlSegment(pathSegment, out exception);
|
||||
Debug.Assert(exception == null, "This only gets called after the path has been validated, so there should never be an exception here");
|
||||
pathSegments.Add(new ContentPathSegment(subsegments));
|
||||
}
|
||||
}
|
||||
return pathSegments;
|
||||
}
|
||||
|
||||
internal static IList<string> SplitUrlToPathSegmentStrings(string url) {
|
||||
List<string> parts = new List<string>();
|
||||
|
||||
if (String.IsNullOrEmpty(url)) {
|
||||
return parts;
|
||||
}
|
||||
|
||||
int currentIndex = 0;
|
||||
|
||||
// Split the incoming URL into individual parts
|
||||
while (currentIndex < url.Length) {
|
||||
int indexOfNextSeparator = url.IndexOf('/', currentIndex);
|
||||
if (indexOfNextSeparator == -1) {
|
||||
// If there are no more separators, the rest of the string is the last part
|
||||
string finalPart = url.Substring(currentIndex);
|
||||
if (finalPart.Length > 0) {
|
||||
parts.Add(finalPart);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
string nextPart = url.Substring(currentIndex, indexOfNextSeparator - currentIndex);
|
||||
if (nextPart.Length > 0) {
|
||||
parts.Add(nextPart);
|
||||
}
|
||||
Debug.Assert(url[indexOfNextSeparator] == '/', "The separator char itself should always be a '/'.");
|
||||
parts.Add("/");
|
||||
currentIndex = indexOfNextSeparator + 1;
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly",
|
||||
Justification = "The exceptions are just constructed here, but they are thrown from a method that does have those parameter names.")]
|
||||
private static Exception ValidateUrlParts(IList<string> pathSegments) {
|
||||
Debug.Assert(pathSegments != null, "The value should always come from SplitUrl(), and that function should never return null.");
|
||||
|
||||
HashSet<string> usedParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
bool? isPreviousPartSeparator = null;
|
||||
|
||||
bool foundCatchAllParameter = false;
|
||||
|
||||
foreach (string pathSegment in pathSegments) {
|
||||
if (foundCatchAllParameter) {
|
||||
// If we ever start an iteration of the loop and we've already found a
|
||||
// catchall parameter then we have an invalid URL format.
|
||||
return new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.GetString(SR.Route_CatchAllMustBeLast)
|
||||
),
|
||||
"routeUrl");
|
||||
}
|
||||
|
||||
bool isCurrentPartSeparator;
|
||||
if (isPreviousPartSeparator == null) {
|
||||
// Prime the loop with the first value
|
||||
isPreviousPartSeparator = IsSeparator(pathSegment);
|
||||
isCurrentPartSeparator = isPreviousPartSeparator.Value;
|
||||
}
|
||||
else {
|
||||
isCurrentPartSeparator = IsSeparator(pathSegment);
|
||||
|
||||
// If both the previous part and the current part are separators, it's invalid
|
||||
if (isCurrentPartSeparator && isPreviousPartSeparator.Value) {
|
||||
return new ArgumentException(SR.GetString(SR.Route_CannotHaveConsecutiveSeparators), "routeUrl");
|
||||
}
|
||||
Debug.Assert(isCurrentPartSeparator != isPreviousPartSeparator.Value, "This assert should only happen if both the current and previous parts are non-separators. This should never happen because consecutive non-separators are always parsed as a single part.");
|
||||
isPreviousPartSeparator = isCurrentPartSeparator;
|
||||
}
|
||||
|
||||
// If it's not a separator, parse the segment for parameters and validate it
|
||||
if (!isCurrentPartSeparator) {
|
||||
Exception exception;
|
||||
IList<PathSubsegment> subsegments = ParseUrlSegment(pathSegment, out exception);
|
||||
if (exception != null) {
|
||||
return exception;
|
||||
}
|
||||
exception = ValidateUrlSegment(subsegments, usedParameterNames, pathSegment);
|
||||
if (exception != null) {
|
||||
return exception;
|
||||
}
|
||||
|
||||
foundCatchAllParameter = subsegments.Any<PathSubsegment>(seg => (seg is ParameterSubsegment) && (((ParameterSubsegment)seg).IsCatchAll));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly",
|
||||
Justification = "The exceptions are just constructed here, but they are thrown from a method that does have those parameter names.")]
|
||||
private static Exception ValidateUrlSegment(IList<PathSubsegment> pathSubsegments, HashSet<string> usedParameterNames, string pathSegment) {
|
||||
bool segmentContainsCatchAll = false;
|
||||
|
||||
Type previousSegmentType = null;
|
||||
|
||||
foreach (PathSubsegment subsegment in pathSubsegments) {
|
||||
if (previousSegmentType != null) {
|
||||
if (previousSegmentType == subsegment.GetType()) {
|
||||
return new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.GetString(SR.Route_CannotHaveConsecutiveParameters)
|
||||
),
|
||||
"routeUrl");
|
||||
}
|
||||
}
|
||||
previousSegmentType = subsegment.GetType();
|
||||
|
||||
LiteralSubsegment literalSubsegment = subsegment as LiteralSubsegment;
|
||||
if (literalSubsegment != null) {
|
||||
// Nothing to validate for literals - everything is valid
|
||||
}
|
||||
else {
|
||||
ParameterSubsegment parameterSubsegment = subsegment as ParameterSubsegment;
|
||||
if (parameterSubsegment != null) {
|
||||
string parameterName = parameterSubsegment.ParameterName;
|
||||
|
||||
if (parameterSubsegment.IsCatchAll) {
|
||||
segmentContainsCatchAll = true;
|
||||
}
|
||||
|
||||
// Check for valid characters in the parameter name
|
||||
if (!IsValidParameterName(parameterName)) {
|
||||
return new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.Route_InvalidParameterName),
|
||||
parameterName
|
||||
),
|
||||
"routeUrl");
|
||||
}
|
||||
|
||||
if (usedParameterNames.Contains(parameterName)) {
|
||||
return new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
SR.GetString(SR.Route_RepeatedParameter),
|
||||
parameterName
|
||||
),
|
||||
"routeUrl");
|
||||
}
|
||||
else {
|
||||
usedParameterNames.Add(parameterName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.Fail("Invalid path subsegment type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (segmentContainsCatchAll && (pathSubsegments.Count != 1)) {
|
||||
return new ArgumentException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.GetString(SR.Route_CannotHaveCatchAllInMultiSegment)
|
||||
),
|
||||
"routeUrl");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
mcs/class/referencesource/System.Web/Routing/RouteTable.cs
Normal file
16
mcs/class/referencesource/System.Web/Routing/RouteTable.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors",
|
||||
Justification = "There is no explicit public constructor defined, and we don't want to add a private one.")]
|
||||
public class RouteTable {
|
||||
private static RouteCollection _instance = new RouteCollection();
|
||||
|
||||
public static RouteCollection Routes {
|
||||
get {
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
namespace System.Web.Routing {
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
|
||||
public class RouteValueDictionary : IDictionary<string, object> {
|
||||
private Dictionary<string, object> _dictionary;
|
||||
|
||||
public RouteValueDictionary() {
|
||||
_dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public RouteValueDictionary(object values) {
|
||||
_dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
AddValues(values);
|
||||
}
|
||||
|
||||
public RouteValueDictionary(IDictionary<string, object> dictionary) {
|
||||
_dictionary = new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
public int Count {
|
||||
get {
|
||||
return _dictionary.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, object>.KeyCollection Keys {
|
||||
get {
|
||||
return _dictionary.Keys;
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, object>.ValueCollection Values {
|
||||
get {
|
||||
return _dictionary.Values;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
public object this[string key] {
|
||||
get {
|
||||
object value;
|
||||
TryGetValue(key, out value);
|
||||
return value;
|
||||
}
|
||||
set {
|
||||
_dictionary[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
public void Add(string key, object value) {
|
||||
_dictionary.Add(key, value);
|
||||
}
|
||||
|
||||
private void AddValues(object values) {
|
||||
if (values != null) {
|
||||
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(values);
|
||||
foreach (PropertyDescriptor prop in props) {
|
||||
object val = prop.GetValue(values);
|
||||
Add(prop.Name, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
public void Clear() {
|
||||
_dictionary.Clear();
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
public bool ContainsKey(string key) {
|
||||
return _dictionary.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool ContainsValue(object value) {
|
||||
return _dictionary.ContainsValue(value);
|
||||
}
|
||||
|
||||
public Dictionary<string, object>.Enumerator GetEnumerator() {
|
||||
return _dictionary.GetEnumerator();
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
public bool Remove(string key) {
|
||||
return _dictionary.Remove(key);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
public bool TryGetValue(string key, out object value) {
|
||||
return _dictionary.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
#region IDictionary<string,object> Members
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
ICollection<string> IDictionary<string, object>.Keys {
|
||||
get {
|
||||
return _dictionary.Keys;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
ICollection<object> IDictionary<string, object>.Values {
|
||||
get {
|
||||
return _dictionary.Values;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ICollection<KeyValuePair<string,object>> Members
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item) {
|
||||
((ICollection<KeyValuePair<string, object>>)_dictionary).Add(item);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) {
|
||||
return ((ICollection<KeyValuePair<string, object>>)_dictionary).Contains(item);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) {
|
||||
((ICollection<KeyValuePair<string, object>>)_dictionary).CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
bool ICollection<KeyValuePair<string, object>>.IsReadOnly {
|
||||
get {
|
||||
return ((ICollection<KeyValuePair<string, object>>)_dictionary).IsReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item) {
|
||||
return ((ICollection<KeyValuePair<string, object>>)_dictionary).Remove(item);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<KeyValuePair<string,object>> Members
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator() {
|
||||
return GetEnumerator();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return GetEnumerator();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user