diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/EndpointRoutingBenchmarkBase.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/EndpointRoutingBenchmarkBase.cs new file mode 100644 index 0000000000..440c6b3860 --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/EndpointRoutingBenchmarkBase.cs @@ -0,0 +1,145 @@ +// 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.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Matching; +using Microsoft.AspNetCore.Routing.Patterns; +using Microsoft.AspNetCore.Routing.Template; +using Microsoft.AspNetCore.Routing.Tree; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.ObjectPool; + +namespace Microsoft.AspNetCore.Routing +{ + public abstract class EndpointRoutingBenchmarkBase + { + private protected MatcherEndpoint[] Endpoints; + private protected HttpContext[] Requests; + + private protected void SetupEndpoints(params MatcherEndpoint[] endpoints) + { + Endpoints = endpoints; + } + + // The older routing implementations retrieve services when they first execute. + private protected IServiceProvider CreateServices() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddLogging(); + services.AddOptions(); + services.AddRouting(); + + services.TryAddEnumerable( + ServiceDescriptor.Singleton(new DefaultEndpointDataSource(Endpoints))); + + return services.BuildServiceProvider(); + } + + private protected DfaMatcherBuilder CreateDfaMatcherBuilder() + { + return CreateServices().GetRequiredService(); + } + + private protected static int[] SampleRequests(int endpointCount, int count) + { + // This isn't very high tech, but it's at least regular distribution. + // We sort the route templates by precedence, so this should result in + // an even distribution of the 'complexity' of the routes that are exercised. + var frequency = endpointCount / count; + if (frequency < 2) + { + throw new InvalidOperationException( + "The sample count is too high. This won't produce an accurate sampling" + + "of the request data."); + } + + var samples = new int[count]; + for (var i = 0; i < samples.Length; i++) + { + samples[i] = i * frequency; + } + + return samples; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private protected void Validate(HttpContext httpContext, Endpoint expected, Endpoint actual) + { + if (!object.ReferenceEquals(expected, actual)) + { + var message = new StringBuilder(); + message.AppendLine($"Validation failed for request {Array.IndexOf(Requests, httpContext)}"); + message.AppendLine($"{httpContext.Request.Method} {httpContext.Request.Path}"); + message.AppendLine($"expected: '{((MatcherEndpoint)expected)?.DisplayName ?? "null"}'"); + message.AppendLine($"actual: '{((MatcherEndpoint)actual)?.DisplayName ?? "null"}'"); + throw new InvalidOperationException(message.ToString()); + } + } + + protected void AssertUrl(string expectedUrl, string actualUrl) + { + AssertUrl(expectedUrl, actualUrl, StringComparison.Ordinal); + } + + protected void AssertUrl(string expectedUrl, string actualUrl, StringComparison stringComparison) + { + if (!string.Equals(expectedUrl, actualUrl, stringComparison)) + { + throw new InvalidOperationException($"Expected: {expectedUrl}, Actual: {actualUrl}"); + } + } + + protected MatcherEndpoint CreateEndpoint( + string template, + object defaults = null, + object constraints = null, + object requiredValues = null, + int order = 0, + string displayName = null, + string routeName = null, + params object[] metadata) + { + var endpointMetadata = new List(metadata ?? Array.Empty()); + endpointMetadata.Add(new RouteValuesAddressMetadata(routeName, new RouteValueDictionary(requiredValues))); + + return new MatcherEndpoint( + MatcherEndpoint.EmptyInvoker, + RoutePatternFactory.Parse(template, defaults, constraints), + order, + new EndpointMetadataCollection(endpointMetadata), + displayName); + } + + protected (HttpContext httpContext, RouteValueDictionary ambientValues) CreateCurrentRequestContext( + object ambientValues = null) + { + var feature = new EndpointFeature { Values = new RouteValueDictionary(ambientValues) }; + var context = new DefaultHttpContext(); + context.Features.Set(feature); + + return (context, feature.Values); + } + + protected void CreateOutboundRouteEntry(TreeRouteBuilder treeRouteBuilder, MatcherEndpoint endpoint) + { + var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata(); + var requiredValues = routeValuesAddressMetadata?.RequiredValues ?? new RouteValueDictionary(); + + treeRouteBuilder.MapOutbound( + NullRouter.Instance, + new RouteTemplate(RoutePatternFactory.Parse( + endpoint.RoutePattern.RawText, + defaults: endpoint.RoutePattern.Defaults, + constraints: null)), + requiredLinkValues: new RouteValueDictionary(requiredValues), + routeName: null, + order: 0); + } + } +} diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/LinkGenerationGithubBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/LinkGenerationGithubBenchmark.cs new file mode 100644 index 0000000000..b7309a159e --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/LinkGenerationGithubBenchmark.cs @@ -0,0 +1,67 @@ +// 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 BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Tree; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Routing.LinkGeneration +{ + public partial class LinkGenerationGithubBenchmark + { + private LinkGenerator _linkGenerator; + private TreeRouter _treeRouter; + private (HttpContext HttpContext, RouteValueDictionary AmbientValues) _requestContext; + private RouteValueDictionary _lookUpValues; + + [GlobalSetup] + public void Setup() + { + SetupEndpoints(); + + var services = CreateServices(); + _linkGenerator = services.GetRequiredService(); + + // Attribute routing related + var treeRouteBuilder = services.GetRequiredService(); + foreach (var endpoint in Endpoints) + { + CreateOutboundRouteEntry(treeRouteBuilder, endpoint); + } + _treeRouter = treeRouteBuilder.Build(); + + _requestContext = CreateCurrentRequestContext(); + + // Get the endpoint to test and pre-populate the lookup values with the defaults + // (as they are dynamically generated) and update with other required parameter values. + // /repos/{owner}/{repo}/issues/comments/{commentId} + var endpointToTest = Endpoints[176]; + _lookUpValues = new RouteValueDictionary(endpointToTest.RoutePattern.Defaults); + _lookUpValues["owner"] = "aspnet"; + _lookUpValues["repo"] = "routing"; + _lookUpValues["commentId"] = "20202"; + } + + [Benchmark(Baseline = true)] + public void TreeRouter() + { + var virtualPathData = _treeRouter.GetVirtualPath(new VirtualPathContext( + _requestContext.HttpContext, + ambientValues: _requestContext.AmbientValues, + values: new RouteValueDictionary(_lookUpValues))); + + AssertUrl("/repos/aspnet/routing/issues/comments/20202", virtualPathData?.VirtualPath); + } + + [Benchmark] + public void EndpointRouting() + { + var actualUrl = _linkGenerator.GetLink( + _requestContext.HttpContext, + values: new RouteValueDictionary(_lookUpValues)); + + AssertUrl("/repos/aspnet/routing/issues/comments/20202", actualUrl); + } + } +} diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/LinkGenerationGithubBenchmark.generated.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/LinkGenerationGithubBenchmark.generated.cs new file mode 100644 index 0000000000..14e3ecb9d7 --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/LinkGenerationGithubBenchmark.generated.cs @@ -0,0 +1,1519 @@ +// 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.Collections.Generic; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Matching; + +namespace Microsoft.AspNetCore.Routing.LinkGeneration +{ + // This code was generated by the Swaggatherer + public partial class LinkGenerationGithubBenchmark : EndpointRoutingBenchmarkBase + { + private const int EndpointCount = 243; + + private void SetupEndpoints() + { + Endpoints = new MatcherEndpoint[243]; + Endpoints[0] = CreateEndpoint("/emojis", "Controller1", "Action1", "GET"); + Endpoints[1] = CreateEndpoint("/events", "Controller2", "Action1", "GET"); + Endpoints[2] = CreateEndpoint("/feeds", "Controller3", "Action1", "GET"); + Endpoints[3] = CreateEndpoint("/gists", "Controller4", "Action1", "GET"); + Endpoints[4] = CreateEndpoint("/gists", "Controller4", "Action2", "POST"); + Endpoints[5] = CreateEndpoint("/issues", "Controller5", "Action1", "GET"); + Endpoints[6] = CreateEndpoint("/markdown", "Controller6", "Action1", "POST"); + Endpoints[7] = CreateEndpoint("/meta", "Controller7", "Action1", "GET"); + Endpoints[8] = CreateEndpoint("/notifications", "Controller8", "Action1", "GET"); + Endpoints[9] = CreateEndpoint("/notifications", "Controller8", "Action2", "PUT"); + Endpoints[10] = CreateEndpoint("/rate_limit", "Controller9", "Action1", "GET"); + Endpoints[11] = CreateEndpoint("/repositories", "Controller10", "Action1", "GET"); + Endpoints[12] = CreateEndpoint("/user", "Controller11", "Action1", "GET"); + Endpoints[13] = CreateEndpoint("/user", "Controller11", "Action2", "PATCH"); + Endpoints[14] = CreateEndpoint("/users", "Controller12", "Action1", "GET"); + Endpoints[15] = CreateEndpoint("/gists/public", "Controller13", "Action1", "GET"); + Endpoints[16] = CreateEndpoint("/gists/starred", "Controller14", "Action1", "GET"); + Endpoints[17] = CreateEndpoint("/gitignore/templates", "Controller15", "Action1", "GET"); + Endpoints[18] = CreateEndpoint("/markdown/raw", "Controller16", "Action1", "POST"); + Endpoints[19] = CreateEndpoint("/search/code", "Controller17", "Action1", "GET"); + Endpoints[20] = CreateEndpoint("/search/issues", "Controller18", "Action1", "GET"); + Endpoints[21] = CreateEndpoint("/search/repositories", "Controller19", "Action1", "GET"); + Endpoints[22] = CreateEndpoint("/search/users", "Controller20", "Action1", "GET"); + Endpoints[23] = CreateEndpoint("/user/emails", "Controller21", "Action1", "GET"); + Endpoints[24] = CreateEndpoint("/user/emails", "Controller21", "Action2", "DELETE"); + Endpoints[25] = CreateEndpoint("/user/emails", "Controller21", "Action3", "POST"); + Endpoints[26] = CreateEndpoint("/user/followers", "Controller22", "Action1", "GET"); + Endpoints[27] = CreateEndpoint("/user/following", "Controller23", "Action1", "GET"); + Endpoints[28] = CreateEndpoint("/user/issues", "Controller24", "Action1", "GET"); + Endpoints[29] = CreateEndpoint("/user/keys", "Controller25", "Action1", "POST"); + Endpoints[30] = CreateEndpoint("/user/keys", "Controller25", "Action2", "GET"); + Endpoints[31] = CreateEndpoint("/user/orgs", "Controller26", "Action1", "GET"); + Endpoints[32] = CreateEndpoint("/user/repos", "Controller27", "Action1", "GET"); + Endpoints[33] = CreateEndpoint("/user/repos", "Controller27", "Action2", "POST"); + Endpoints[34] = CreateEndpoint("/user/starred", "Controller28", "Action1", "GET"); + Endpoints[35] = CreateEndpoint("/user/subscriptions", "Controller29", "Action1", "GET"); + Endpoints[36] = CreateEndpoint("/user/teams", "Controller30", "Action1", "GET"); + Endpoints[37] = CreateEndpoint("/legacy/repos/search/{keyword}", "Controller31", "Action1", "GET"); + Endpoints[38] = CreateEndpoint("/legacy/user/email/{email}", "Controller32", "Action1", "GET"); + Endpoints[39] = CreateEndpoint("/legacy/user/search/{keyword}", "Controller33", "Action1", "GET"); + Endpoints[40] = CreateEndpoint("/legacy/issues/search/{owner}/{repository}/{state}/{keyword}", "Controller34", "Action1", "GET"); + Endpoints[41] = CreateEndpoint("/gitignore/templates/{language}", "Controller35", "Action1", "GET"); + Endpoints[42] = CreateEndpoint("/notifications/threads/{id}", "Controller36", "Action1", "GET"); + Endpoints[43] = CreateEndpoint("/notifications/threads/{id}", "Controller36", "Action2", "PATCH"); + Endpoints[44] = CreateEndpoint("/user/following/{username}", "Controller37", "Action1", "DELETE"); + Endpoints[45] = CreateEndpoint("/user/following/{username}", "Controller37", "Action2", "GET"); + Endpoints[46] = CreateEndpoint("/user/following/{username}", "Controller37", "Action3", "PUT"); + Endpoints[47] = CreateEndpoint("/user/keys/{keyId}", "Controller38", "Action1", "DELETE"); + Endpoints[48] = CreateEndpoint("/user/keys/{keyId}", "Controller38", "Action2", "GET"); + Endpoints[49] = CreateEndpoint("/notifications/threads/{id}/subscription", "Controller39", "Action1", "DELETE"); + Endpoints[50] = CreateEndpoint("/notifications/threads/{id}/subscription", "Controller39", "Action2", "GET"); + Endpoints[51] = CreateEndpoint("/notifications/threads/{id}/subscription", "Controller39", "Action3", "PUT"); + Endpoints[52] = CreateEndpoint("/user/starred/{owner}/{repo}", "Controller40", "Action1", "PUT"); + Endpoints[53] = CreateEndpoint("/user/starred/{owner}/{repo}", "Controller40", "Action2", "GET"); + Endpoints[54] = CreateEndpoint("/user/starred/{owner}/{repo}", "Controller40", "Action3", "DELETE"); + Endpoints[55] = CreateEndpoint("/user/subscriptions/{owner}/{repo}", "Controller41", "Action1", "PUT"); + Endpoints[56] = CreateEndpoint("/user/subscriptions/{owner}/{repo}", "Controller41", "Action2", "GET"); + Endpoints[57] = CreateEndpoint("/user/subscriptions/{owner}/{repo}", "Controller41", "Action3", "DELETE"); + Endpoints[58] = CreateEndpoint("/gists/{id}", "Controller42", "Action1", "GET"); + Endpoints[59] = CreateEndpoint("/gists/{id}", "Controller42", "Action2", "PATCH"); + Endpoints[60] = CreateEndpoint("/gists/{id}", "Controller42", "Action3", "DELETE"); + Endpoints[61] = CreateEndpoint("/orgs/{org}", "Controller43", "Action1", "PATCH"); + Endpoints[62] = CreateEndpoint("/orgs/{org}", "Controller43", "Action2", "GET"); + Endpoints[63] = CreateEndpoint("/teams/{teamId}", "Controller44", "Action1", "PATCH"); + Endpoints[64] = CreateEndpoint("/teams/{teamId}", "Controller44", "Action2", "GET"); + Endpoints[65] = CreateEndpoint("/teams/{teamId}", "Controller44", "Action3", "DELETE"); + Endpoints[66] = CreateEndpoint("/users/{username}", "Controller45", "Action1", "GET"); + Endpoints[67] = CreateEndpoint("/gists/{id}/comments", "Controller46", "Action1", "GET"); + Endpoints[68] = CreateEndpoint("/gists/{id}/comments", "Controller46", "Action2", "POST"); + Endpoints[69] = CreateEndpoint("/gists/{id}/forks", "Controller47", "Action1", "POST"); + Endpoints[70] = CreateEndpoint("/gists/{id}/star", "Controller48", "Action1", "DELETE"); + Endpoints[71] = CreateEndpoint("/gists/{id}/star", "Controller48", "Action2", "GET"); + Endpoints[72] = CreateEndpoint("/gists/{id}/star", "Controller48", "Action3", "PUT"); + Endpoints[73] = CreateEndpoint("/orgs/{org}/events", "Controller49", "Action1", "GET"); + Endpoints[74] = CreateEndpoint("/orgs/{org}/issues", "Controller50", "Action1", "GET"); + Endpoints[75] = CreateEndpoint("/orgs/{org}/members", "Controller51", "Action1", "GET"); + Endpoints[76] = CreateEndpoint("/orgs/{org}/public_members", "Controller52", "Action1", "GET"); + Endpoints[77] = CreateEndpoint("/orgs/{org}/repos", "Controller53", "Action1", "GET"); + Endpoints[78] = CreateEndpoint("/orgs/{org}/repos", "Controller53", "Action2", "POST"); + Endpoints[79] = CreateEndpoint("/orgs/{org}/teams", "Controller54", "Action1", "POST"); + Endpoints[80] = CreateEndpoint("/orgs/{org}/teams", "Controller54", "Action2", "GET"); + Endpoints[81] = CreateEndpoint("/teams/{teamId}/members", "Controller55", "Action1", "GET"); + Endpoints[82] = CreateEndpoint("/teams/{teamId}/repos", "Controller56", "Action1", "GET"); + Endpoints[83] = CreateEndpoint("/users/{username}/events", "Controller57", "Action1", "GET"); + Endpoints[84] = CreateEndpoint("/users/{username}/followers", "Controller58", "Action1", "GET"); + Endpoints[85] = CreateEndpoint("/users/{username}/gists", "Controller59", "Action1", "GET"); + Endpoints[86] = CreateEndpoint("/users/{username}/keys", "Controller60", "Action1", "GET"); + Endpoints[87] = CreateEndpoint("/users/{username}/orgs", "Controller61", "Action1", "GET"); + Endpoints[88] = CreateEndpoint("/users/{username}/received_events", "Controller62", "Action1", "GET"); + Endpoints[89] = CreateEndpoint("/users/{username}/repos", "Controller63", "Action1", "GET"); + Endpoints[90] = CreateEndpoint("/users/{username}/starred", "Controller64", "Action1", "GET"); + Endpoints[91] = CreateEndpoint("/users/{username}/subscriptions", "Controller65", "Action1", "GET"); + Endpoints[92] = CreateEndpoint("/users/{username}/received_events/public", "Controller66", "Action1", "GET"); + Endpoints[93] = CreateEndpoint("/users/{username}/events/orgs/{org}", "Controller67", "Action1", "GET"); + Endpoints[94] = CreateEndpoint("/gists/{id}/comments/{commentId}", "Controller68", "Action1", "DELETE"); + Endpoints[95] = CreateEndpoint("/gists/{id}/comments/{commentId}", "Controller68", "Action2", "GET"); + Endpoints[96] = CreateEndpoint("/gists/{id}/comments/{commentId}", "Controller68", "Action3", "PATCH"); + Endpoints[97] = CreateEndpoint("/orgs/{org}/members/{username}", "Controller69", "Action1", "DELETE"); + Endpoints[98] = CreateEndpoint("/orgs/{org}/members/{username}", "Controller69", "Action2", "GET"); + Endpoints[99] = CreateEndpoint("/orgs/{org}/public_members/{username}", "Controller70", "Action1", "PUT"); + Endpoints[100] = CreateEndpoint("/orgs/{org}/public_members/{username}", "Controller70", "Action2", "GET"); + Endpoints[101] = CreateEndpoint("/orgs/{org}/public_members/{username}", "Controller70", "Action3", "DELETE"); + Endpoints[102] = CreateEndpoint("/teams/{teamId}/members/{username}", "Controller71", "Action1", "GET"); + Endpoints[103] = CreateEndpoint("/teams/{teamId}/members/{username}", "Controller71", "Action2", "PUT"); + Endpoints[104] = CreateEndpoint("/teams/{teamId}/members/{username}", "Controller71", "Action3", "DELETE"); + Endpoints[105] = CreateEndpoint("/teams/{teamId}/memberships/{username}", "Controller72", "Action1", "DELETE"); + Endpoints[106] = CreateEndpoint("/teams/{teamId}/memberships/{username}", "Controller72", "Action2", "PUT"); + Endpoints[107] = CreateEndpoint("/teams/{teamId}/memberships/{username}", "Controller72", "Action3", "GET"); + Endpoints[108] = CreateEndpoint("/users/{username}/following/{targetUser}", "Controller73", "Action1", "GET"); + Endpoints[109] = CreateEndpoint("/teams/{teamId}/repos/{org}/{repo}", "Controller74", "Action1", "PUT"); + Endpoints[110] = CreateEndpoint("/teams/{teamId}/repos/{owner}/{repo}", "Controller75", "Action1", "DELETE"); + Endpoints[111] = CreateEndpoint("/teams/{teamId}/repos/{owner}/{repo}", "Controller75", "Action2", "GET"); + Endpoints[112] = CreateEndpoint("/repos/{owner}/{repo}", "Controller76", "Action1", "PATCH"); + Endpoints[113] = CreateEndpoint("/repos/{owner}/{repo}", "Controller76", "Action2", "DELETE"); + Endpoints[114] = CreateEndpoint("/repos/{owner}/{repo}", "Controller76", "Action3", "GET"); + Endpoints[115] = CreateEndpoint("/networks/{owner}/{repo}/events", "Controller77", "Action1", "GET"); + Endpoints[116] = CreateEndpoint("/repos/{owner}/{repo}/assignees", "Controller78", "Action1", "GET"); + Endpoints[117] = CreateEndpoint("/repos/{owner}/{repo}/branches", "Controller79", "Action1", "GET"); + Endpoints[118] = CreateEndpoint("/repos/{owner}/{repo}/collaborators", "Controller80", "Action1", "GET"); + Endpoints[119] = CreateEndpoint("/repos/{owner}/{repo}/comments", "Controller81", "Action1", "GET"); + Endpoints[120] = CreateEndpoint("/repos/{owner}/{repo}/commits", "Controller82", "Action1", "GET"); + Endpoints[121] = CreateEndpoint("/repos/{owner}/{repo}/contributors", "Controller83", "Action1", "GET"); + Endpoints[122] = CreateEndpoint("/repos/{owner}/{repo}/deployments", "Controller84", "Action1", "GET"); + Endpoints[123] = CreateEndpoint("/repos/{owner}/{repo}/deployments", "Controller84", "Action2", "POST"); + Endpoints[124] = CreateEndpoint("/repos/{owner}/{repo}/downloads", "Controller85", "Action1", "GET"); + Endpoints[125] = CreateEndpoint("/repos/{owner}/{repo}/events", "Controller86", "Action1", "GET"); + Endpoints[126] = CreateEndpoint("/repos/{owner}/{repo}/forks", "Controller87", "Action1", "GET"); + Endpoints[127] = CreateEndpoint("/repos/{owner}/{repo}/forks", "Controller87", "Action2", "POST"); + Endpoints[128] = CreateEndpoint("/repos/{owner}/{repo}/hooks", "Controller88", "Action1", "POST"); + Endpoints[129] = CreateEndpoint("/repos/{owner}/{repo}/hooks", "Controller88", "Action2", "GET"); + Endpoints[130] = CreateEndpoint("/repos/{owner}/{repo}/issues", "Controller89", "Action1", "GET"); + Endpoints[131] = CreateEndpoint("/repos/{owner}/{repo}/issues", "Controller89", "Action2", "POST"); + Endpoints[132] = CreateEndpoint("/repos/{owner}/{repo}/keys", "Controller90", "Action1", "GET"); + Endpoints[133] = CreateEndpoint("/repos/{owner}/{repo}/keys", "Controller90", "Action2", "POST"); + Endpoints[134] = CreateEndpoint("/repos/{owner}/{repo}/labels", "Controller91", "Action1", "GET"); + Endpoints[135] = CreateEndpoint("/repos/{owner}/{repo}/labels", "Controller91", "Action2", "POST"); + Endpoints[136] = CreateEndpoint("/repos/{owner}/{repo}/languages", "Controller92", "Action1", "GET"); + Endpoints[137] = CreateEndpoint("/repos/{owner}/{repo}/merges", "Controller93", "Action1", "POST"); + Endpoints[138] = CreateEndpoint("/repos/{owner}/{repo}/milestones", "Controller94", "Action1", "GET"); + Endpoints[139] = CreateEndpoint("/repos/{owner}/{repo}/milestones", "Controller94", "Action2", "POST"); + Endpoints[140] = CreateEndpoint("/repos/{owner}/{repo}/notifications", "Controller95", "Action1", "PUT"); + Endpoints[141] = CreateEndpoint("/repos/{owner}/{repo}/notifications", "Controller95", "Action2", "GET"); + Endpoints[142] = CreateEndpoint("/repos/{owner}/{repo}/pulls", "Controller96", "Action1", "POST"); + Endpoints[143] = CreateEndpoint("/repos/{owner}/{repo}/pulls", "Controller96", "Action2", "GET"); + Endpoints[144] = CreateEndpoint("/repos/{owner}/{repo}/readme", "Controller97", "Action1", "GET"); + Endpoints[145] = CreateEndpoint("/repos/{owner}/{repo}/releases", "Controller98", "Action1", "POST"); + Endpoints[146] = CreateEndpoint("/repos/{owner}/{repo}/releases", "Controller98", "Action2", "GET"); + Endpoints[147] = CreateEndpoint("/repos/{owner}/{repo}/stargazers", "Controller99", "Action1", "GET"); + Endpoints[148] = CreateEndpoint("/repos/{owner}/{repo}/subscribers", "Controller100", "Action1", "GET"); + Endpoints[149] = CreateEndpoint("/repos/{owner}/{repo}/subscription", "Controller101", "Action1", "PUT"); + Endpoints[150] = CreateEndpoint("/repos/{owner}/{repo}/subscription", "Controller101", "Action2", "GET"); + Endpoints[151] = CreateEndpoint("/repos/{owner}/{repo}/subscription", "Controller101", "Action3", "DELETE"); + Endpoints[152] = CreateEndpoint("/repos/{owner}/{repo}/tags", "Controller102", "Action1", "GET"); + Endpoints[153] = CreateEndpoint("/repos/{owner}/{repo}/teams", "Controller103", "Action1", "GET"); + Endpoints[154] = CreateEndpoint("/repos/{owner}/{repo}/watchers", "Controller104", "Action1", "GET"); + Endpoints[155] = CreateEndpoint("/repos/{owner}/{repo}/git/blobs", "Controller105", "Action1", "POST"); + Endpoints[156] = CreateEndpoint("/repos/{owner}/{repo}/git/commits", "Controller106", "Action1", "POST"); + Endpoints[157] = CreateEndpoint("/repos/{owner}/{repo}/git/refs", "Controller107", "Action1", "GET"); + Endpoints[158] = CreateEndpoint("/repos/{owner}/{repo}/git/refs", "Controller107", "Action2", "POST"); + Endpoints[159] = CreateEndpoint("/repos/{owner}/{repo}/git/tags", "Controller108", "Action1", "POST"); + Endpoints[160] = CreateEndpoint("/repos/{owner}/{repo}/git/trees", "Controller109", "Action1", "POST"); + Endpoints[161] = CreateEndpoint("/repos/{owner}/{repo}/issues/comments", "Controller110", "Action1", "GET"); + Endpoints[162] = CreateEndpoint("/repos/{owner}/{repo}/issues/events", "Controller111", "Action1", "GET"); + Endpoints[163] = CreateEndpoint("/repos/{owner}/{repo}/pulls/comments", "Controller112", "Action1", "GET"); + Endpoints[164] = CreateEndpoint("/repos/{owner}/{repo}/stats/code_frequency", "Controller113", "Action1", "GET"); + Endpoints[165] = CreateEndpoint("/repos/{owner}/{repo}/stats/commit_activity", "Controller114", "Action1", "GET"); + Endpoints[166] = CreateEndpoint("/repos/{owner}/{repo}/stats/contributors", "Controller115", "Action1", "GET"); + Endpoints[167] = CreateEndpoint("/repos/{owner}/{repo}/stats/participation", "Controller116", "Action1", "GET"); + Endpoints[168] = CreateEndpoint("/repos/{owner}/{repo}/stats/punch_card", "Controller117", "Action1", "GET"); + Endpoints[169] = CreateEndpoint("/repos/{owner}/{repo}/git/blobs/{shaCode}", "Controller118", "Action1", "GET"); + Endpoints[170] = CreateEndpoint("/repos/{owner}/{repo}/git/commits/{shaCode}", "Controller119", "Action1", "GET"); + Endpoints[171] = CreateEndpoint("/repos/{owner}/{repo}/git/refs/{ref}", "Controller120", "Action1", "DELETE"); + Endpoints[172] = CreateEndpoint("/repos/{owner}/{repo}/git/refs/{ref}", "Controller120", "Action2", "PATCH"); + Endpoints[173] = CreateEndpoint("/repos/{owner}/{repo}/git/refs/{ref}", "Controller120", "Action3", "GET"); + Endpoints[174] = CreateEndpoint("/repos/{owner}/{repo}/git/tags/{shaCode}", "Controller121", "Action1", "GET"); + Endpoints[175] = CreateEndpoint("/repos/{owner}/{repo}/git/trees/{shaCode}", "Controller122", "Action1", "GET"); + Endpoints[176] = CreateEndpoint("/repos/{owner}/{repo}/issues/comments/{commentId}", "Controller123", "Action1", "GET"); + Endpoints[177] = CreateEndpoint("/repos/{owner}/{repo}/issues/comments/{commentId}", "Controller123", "Action2", "PATCH"); + Endpoints[178] = CreateEndpoint("/repos/{owner}/{repo}/issues/comments/{commentId}", "Controller123", "Action3", "DELETE"); + Endpoints[179] = CreateEndpoint("/repos/{owner}/{repo}/issues/events/{eventId}", "Controller124", "Action1", "GET"); + Endpoints[180] = CreateEndpoint("/repos/{owner}/{repo}/pulls/comments/{commentId}", "Controller125", "Action1", "PATCH"); + Endpoints[181] = CreateEndpoint("/repos/{owner}/{repo}/pulls/comments/{commentId}", "Controller125", "Action2", "GET"); + Endpoints[182] = CreateEndpoint("/repos/{owner}/{repo}/pulls/comments/{commentId}", "Controller125", "Action3", "DELETE"); + Endpoints[183] = CreateEndpoint("/repos/{owner}/{repo}/releases/assets/{id}", "Controller126", "Action1", "PATCH"); + Endpoints[184] = CreateEndpoint("/repos/{owner}/{repo}/releases/assets/{id}", "Controller126", "Action2", "DELETE"); + Endpoints[185] = CreateEndpoint("/repos/{owner}/{repo}/releases/assets/{id}", "Controller126", "Action3", "GET"); + Endpoints[186] = CreateEndpoint("/repos/{owner}/{repo}/assignees/{assignee}", "Controller127", "Action1", "GET"); + Endpoints[187] = CreateEndpoint("/repos/{owner}/{repo}/branches/{branch}", "Controller128", "Action1", "GET"); + Endpoints[188] = CreateEndpoint("/repos/{owner}/{repo}/collaborators/{user}", "Controller129", "Action1", "PUT"); + Endpoints[189] = CreateEndpoint("/repos/{owner}/{repo}/collaborators/{user}", "Controller129", "Action2", "DELETE"); + Endpoints[190] = CreateEndpoint("/repos/{owner}/{repo}/collaborators/{user}", "Controller129", "Action3", "GET"); + Endpoints[191] = CreateEndpoint("/repos/{owner}/{repo}/comments/{commentId}", "Controller130", "Action1", "DELETE"); + Endpoints[192] = CreateEndpoint("/repos/{owner}/{repo}/comments/{commentId}", "Controller130", "Action2", "GET"); + Endpoints[193] = CreateEndpoint("/repos/{owner}/{repo}/comments/{commentId}", "Controller130", "Action3", "PATCH"); + Endpoints[194] = CreateEndpoint("/repos/{owner}/{repo}/commits/{shaCode}", "Controller131", "Action1", "GET"); + Endpoints[195] = CreateEndpoint("/repos/{owner}/{repo}/contents/{path}", "Controller132", "Action1", "GET"); + Endpoints[196] = CreateEndpoint("/repos/{owner}/{repo}/contents/{path}", "Controller132", "Action2", "DELETE"); + Endpoints[197] = CreateEndpoint("/repos/{owner}/{repo}/contents/{path}", "Controller132", "Action3", "PUT"); + Endpoints[198] = CreateEndpoint("/repos/{owner}/{repo}/downloads/{downloadId}", "Controller133", "Action1", "GET"); + Endpoints[199] = CreateEndpoint("/repos/{owner}/{repo}/downloads/{downloadId}", "Controller133", "Action2", "DELETE"); + Endpoints[200] = CreateEndpoint("/repos/{owner}/{repo}/hooks/{hookId}", "Controller134", "Action1", "DELETE"); + Endpoints[201] = CreateEndpoint("/repos/{owner}/{repo}/hooks/{hookId}", "Controller134", "Action2", "GET"); + Endpoints[202] = CreateEndpoint("/repos/{owner}/{repo}/hooks/{hookId}", "Controller134", "Action3", "PATCH"); + Endpoints[203] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}", "Controller135", "Action1", "GET"); + Endpoints[204] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}", "Controller135", "Action2", "PATCH"); + Endpoints[205] = CreateEndpoint("/repos/{owner}/{repo}/keys/{keyId}", "Controller136", "Action1", "GET"); + Endpoints[206] = CreateEndpoint("/repos/{owner}/{repo}/keys/{keyId}", "Controller136", "Action2", "DELETE"); + Endpoints[207] = CreateEndpoint("/repos/{owner}/{repo}/labels/{name}", "Controller137", "Action1", "GET"); + Endpoints[208] = CreateEndpoint("/repos/{owner}/{repo}/labels/{name}", "Controller137", "Action2", "PATCH"); + Endpoints[209] = CreateEndpoint("/repos/{owner}/{repo}/labels/{name}", "Controller137", "Action3", "DELETE"); + Endpoints[210] = CreateEndpoint("/repos/{owner}/{repo}/milestones/{number}", "Controller138", "Action1", "PATCH"); + Endpoints[211] = CreateEndpoint("/repos/{owner}/{repo}/milestones/{number}", "Controller138", "Action2", "GET"); + Endpoints[212] = CreateEndpoint("/repos/{owner}/{repo}/milestones/{number}", "Controller138", "Action3", "DELETE"); + Endpoints[213] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}", "Controller139", "Action1", "GET"); + Endpoints[214] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}", "Controller139", "Action2", "PATCH"); + Endpoints[215] = CreateEndpoint("/repos/{owner}/{repo}/releases/{id}", "Controller140", "Action1", "PATCH"); + Endpoints[216] = CreateEndpoint("/repos/{owner}/{repo}/releases/{id}", "Controller140", "Action2", "GET"); + Endpoints[217] = CreateEndpoint("/repos/{owner}/{repo}/releases/{id}", "Controller140", "Action3", "DELETE"); + Endpoints[218] = CreateEndpoint("/repos/{owner}/{repo}/statuses/{ref}", "Controller141", "Action1", "GET"); + Endpoints[219] = CreateEndpoint("/repos/{owner}/{repo}/statuses/{ref}", "Controller141", "Action2", "POST"); + Endpoints[220] = CreateEndpoint("/repos/{owner}/{repo}/commits/{ref}/status", "Controller142", "Action1", "GET"); + Endpoints[221] = CreateEndpoint("/repos/{owner}/{repo}/commits/{shaCode}/comments", "Controller143", "Action1", "GET"); + Endpoints[222] = CreateEndpoint("/repos/{owner}/{repo}/commits/{shaCode}/comments", "Controller143", "Action2", "POST"); + Endpoints[223] = CreateEndpoint("/repos/{owner}/{repo}/deployments/{id}/statuses", "Controller144", "Action1", "GET"); + Endpoints[224] = CreateEndpoint("/repos/{owner}/{repo}/deployments/{id}/statuses", "Controller144", "Action2", "POST"); + Endpoints[225] = CreateEndpoint("/repos/{owner}/{repo}/hooks/{hookId}/tests", "Controller145", "Action1", "POST"); + Endpoints[226] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/comments", "Controller146", "Action1", "POST"); + Endpoints[227] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/comments", "Controller146", "Action2", "GET"); + Endpoints[228] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/events", "Controller147", "Action1", "GET"); + Endpoints[229] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/labels", "Controller148", "Action1", "POST"); + Endpoints[230] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/labels", "Controller148", "Action2", "GET"); + Endpoints[231] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/labels", "Controller148", "Action3", "PUT"); + Endpoints[232] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/labels", "Controller148", "Action4", "DELETE"); + Endpoints[233] = CreateEndpoint("/repos/{owner}/{repo}/milestones/{number}/labels", "Controller149", "Action1", "GET"); + Endpoints[234] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/comments", "Controller150", "Action1", "GET"); + Endpoints[235] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/comments", "Controller150", "Action2", "POST"); + Endpoints[236] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/commits", "Controller151", "Action1", "GET"); + Endpoints[237] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/files", "Controller152", "Action1", "GET"); + Endpoints[238] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/merge", "Controller153", "Action1", "PUT"); + Endpoints[239] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/merge", "Controller153", "Action2", "GET"); + Endpoints[240] = CreateEndpoint("/repos/{owner}/{repo}/releases/{id}/assets", "Controller154", "Action1", "GET"); + Endpoints[241] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/labels/{name}", "Controller155", "Action1", "DELETE"); + Endpoints[242] = CreateEndpoint("/repos/{owner}/{repo}/{archive_format}/{path}", "Controller156", "Action1", "GET"); + } + + private void SetupRequests() + { + Requests = new HttpContext[243]; + Requests[0] = new DefaultHttpContext(); + Requests[0].RequestServices = CreateServices(); + Requests[0].Request.Method = "GET"; + Requests[0].Request.Path = "/emojis"; + Requests[1] = new DefaultHttpContext(); + Requests[1].RequestServices = CreateServices(); + Requests[1].Request.Method = "GET"; + Requests[1].Request.Path = "/events"; + Requests[2] = new DefaultHttpContext(); + Requests[2].RequestServices = CreateServices(); + Requests[2].Request.Method = "GET"; + Requests[2].Request.Path = "/feeds"; + Requests[3] = new DefaultHttpContext(); + Requests[3].RequestServices = CreateServices(); + Requests[3].Request.Method = "GET"; + Requests[3].Request.Path = "/gists"; + Requests[4] = new DefaultHttpContext(); + Requests[4].RequestServices = CreateServices(); + Requests[4].Request.Method = "POST"; + Requests[4].Request.Path = "/gists"; + Requests[5] = new DefaultHttpContext(); + Requests[5].RequestServices = CreateServices(); + Requests[5].Request.Method = "GET"; + Requests[5].Request.Path = "/issues"; + Requests[6] = new DefaultHttpContext(); + Requests[6].RequestServices = CreateServices(); + Requests[6].Request.Method = "POST"; + Requests[6].Request.Path = "/markdown"; + Requests[7] = new DefaultHttpContext(); + Requests[7].RequestServices = CreateServices(); + Requests[7].Request.Method = "GET"; + Requests[7].Request.Path = "/meta"; + Requests[8] = new DefaultHttpContext(); + Requests[8].RequestServices = CreateServices(); + Requests[8].Request.Method = "GET"; + Requests[8].Request.Path = "/notifications"; + Requests[9] = new DefaultHttpContext(); + Requests[9].RequestServices = CreateServices(); + Requests[9].Request.Method = "PUT"; + Requests[9].Request.Path = "/notifications"; + Requests[10] = new DefaultHttpContext(); + Requests[10].RequestServices = CreateServices(); + Requests[10].Request.Method = "GET"; + Requests[10].Request.Path = "/rate_limit"; + Requests[11] = new DefaultHttpContext(); + Requests[11].RequestServices = CreateServices(); + Requests[11].Request.Method = "GET"; + Requests[11].Request.Path = "/repositories"; + Requests[12] = new DefaultHttpContext(); + Requests[12].RequestServices = CreateServices(); + Requests[12].Request.Method = "GET"; + Requests[12].Request.Path = "/user"; + Requests[13] = new DefaultHttpContext(); + Requests[13].RequestServices = CreateServices(); + Requests[13].Request.Method = "PATCH"; + Requests[13].Request.Path = "/user"; + Requests[14] = new DefaultHttpContext(); + Requests[14].RequestServices = CreateServices(); + Requests[14].Request.Method = "GET"; + Requests[14].Request.Path = "/users"; + Requests[15] = new DefaultHttpContext(); + Requests[15].RequestServices = CreateServices(); + Requests[15].Request.Method = "GET"; + Requests[15].Request.Path = "/gists/public"; + Requests[16] = new DefaultHttpContext(); + Requests[16].RequestServices = CreateServices(); + Requests[16].Request.Method = "GET"; + Requests[16].Request.Path = "/gists/starred"; + Requests[17] = new DefaultHttpContext(); + Requests[17].RequestServices = CreateServices(); + Requests[17].Request.Method = "GET"; + Requests[17].Request.Path = "/gitignore/templates"; + Requests[18] = new DefaultHttpContext(); + Requests[18].RequestServices = CreateServices(); + Requests[18].Request.Method = "POST"; + Requests[18].Request.Path = "/markdown/raw"; + Requests[19] = new DefaultHttpContext(); + Requests[19].RequestServices = CreateServices(); + Requests[19].Request.Method = "GET"; + Requests[19].Request.Path = "/search/code"; + Requests[20] = new DefaultHttpContext(); + Requests[20].RequestServices = CreateServices(); + Requests[20].Request.Method = "GET"; + Requests[20].Request.Path = "/search/issues"; + Requests[21] = new DefaultHttpContext(); + Requests[21].RequestServices = CreateServices(); + Requests[21].Request.Method = "GET"; + Requests[21].Request.Path = "/search/repositories"; + Requests[22] = new DefaultHttpContext(); + Requests[22].RequestServices = CreateServices(); + Requests[22].Request.Method = "GET"; + Requests[22].Request.Path = "/search/users"; + Requests[23] = new DefaultHttpContext(); + Requests[23].RequestServices = CreateServices(); + Requests[23].Request.Method = "GET"; + Requests[23].Request.Path = "/user/emails"; + Requests[24] = new DefaultHttpContext(); + Requests[24].RequestServices = CreateServices(); + Requests[24].Request.Method = "DELETE"; + Requests[24].Request.Path = "/user/emails"; + Requests[25] = new DefaultHttpContext(); + Requests[25].RequestServices = CreateServices(); + Requests[25].Request.Method = "POST"; + Requests[25].Request.Path = "/user/emails"; + Requests[26] = new DefaultHttpContext(); + Requests[26].RequestServices = CreateServices(); + Requests[26].Request.Method = "GET"; + Requests[26].Request.Path = "/user/followers"; + Requests[27] = new DefaultHttpContext(); + Requests[27].RequestServices = CreateServices(); + Requests[27].Request.Method = "GET"; + Requests[27].Request.Path = "/user/following"; + Requests[28] = new DefaultHttpContext(); + Requests[28].RequestServices = CreateServices(); + Requests[28].Request.Method = "GET"; + Requests[28].Request.Path = "/user/issues"; + Requests[29] = new DefaultHttpContext(); + Requests[29].RequestServices = CreateServices(); + Requests[29].Request.Method = "POST"; + Requests[29].Request.Path = "/user/keys"; + Requests[30] = new DefaultHttpContext(); + Requests[30].RequestServices = CreateServices(); + Requests[30].Request.Method = "GET"; + Requests[30].Request.Path = "/user/keys"; + Requests[31] = new DefaultHttpContext(); + Requests[31].RequestServices = CreateServices(); + Requests[31].Request.Method = "GET"; + Requests[31].Request.Path = "/user/orgs"; + Requests[32] = new DefaultHttpContext(); + Requests[32].RequestServices = CreateServices(); + Requests[32].Request.Method = "GET"; + Requests[32].Request.Path = "/user/repos"; + Requests[33] = new DefaultHttpContext(); + Requests[33].RequestServices = CreateServices(); + Requests[33].Request.Method = "POST"; + Requests[33].Request.Path = "/user/repos"; + Requests[34] = new DefaultHttpContext(); + Requests[34].RequestServices = CreateServices(); + Requests[34].Request.Method = "GET"; + Requests[34].Request.Path = "/user/starred"; + Requests[35] = new DefaultHttpContext(); + Requests[35].RequestServices = CreateServices(); + Requests[35].Request.Method = "GET"; + Requests[35].Request.Path = "/user/subscriptions"; + Requests[36] = new DefaultHttpContext(); + Requests[36].RequestServices = CreateServices(); + Requests[36].Request.Method = "GET"; + Requests[36].Request.Path = "/user/teams"; + Requests[37] = new DefaultHttpContext(); + Requests[37].RequestServices = CreateServices(); + Requests[37].Request.Method = "GET"; + Requests[37].Request.Path = "/legacy/repos/search/d50aacc"; + Requests[38] = new DefaultHttpContext(); + Requests[38].RequestServices = CreateServices(); + Requests[38].Request.Method = "GET"; + Requests[38].Request.Path = "/legacy/user/email/8341f"; + Requests[39] = new DefaultHttpContext(); + Requests[39].RequestServices = CreateServices(); + Requests[39].Request.Method = "GET"; + Requests[39].Request.Path = "/legacy/user/search/24fa852"; + Requests[40] = new DefaultHttpContext(); + Requests[40].RequestServices = CreateServices(); + Requests[40].Request.Method = "GET"; + Requests[40].Request.Path = "/legacy/issues/search/f8f51/b60908a1-4/7e0c7/30ea8fe"; + Requests[41] = new DefaultHttpContext(); + Requests[41].RequestServices = CreateServices(); + Requests[41].Request.Method = "GET"; + Requests[41].Request.Path = "/gitignore/templates/a2ba346f"; + Requests[42] = new DefaultHttpContext(); + Requests[42].RequestServices = CreateServices(); + Requests[42].Request.Method = "GET"; + Requests[42].Request.Path = "/notifications/threads/1df78"; + Requests[43] = new DefaultHttpContext(); + Requests[43].RequestServices = CreateServices(); + Requests[43].Request.Method = "PATCH"; + Requests[43].Request.Path = "/notifications/threads/24a82"; + Requests[44] = new DefaultHttpContext(); + Requests[44].RequestServices = CreateServices(); + Requests[44].Request.Method = "DELETE"; + Requests[44].Request.Path = "/user/following/770fc247"; + Requests[45] = new DefaultHttpContext(); + Requests[45].RequestServices = CreateServices(); + Requests[45].Request.Method = "GET"; + Requests[45].Request.Path = "/user/following/5ac246a4"; + Requests[46] = new DefaultHttpContext(); + Requests[46].RequestServices = CreateServices(); + Requests[46].Request.Method = "PUT"; + Requests[46].Request.Path = "/user/following/20904d4a"; + Requests[47] = new DefaultHttpContext(); + Requests[47].RequestServices = CreateServices(); + Requests[47].Request.Method = "DELETE"; + Requests[47].Request.Path = "/user/keys/2cb10"; + Requests[48] = new DefaultHttpContext(); + Requests[48].RequestServices = CreateServices(); + Requests[48].Request.Method = "GET"; + Requests[48].Request.Path = "/user/keys/37adb"; + Requests[49] = new DefaultHttpContext(); + Requests[49].RequestServices = CreateServices(); + Requests[49].Request.Method = "DELETE"; + Requests[49].Request.Path = "/notifications/threads/6d635/subscription"; + Requests[50] = new DefaultHttpContext(); + Requests[50].RequestServices = CreateServices(); + Requests[50].Request.Method = "GET"; + Requests[50].Request.Path = "/notifications/threads/8117f/subscription"; + Requests[51] = new DefaultHttpContext(); + Requests[51].RequestServices = CreateServices(); + Requests[51].Request.Method = "PUT"; + Requests[51].Request.Path = "/notifications/threads/6ca24/subscription"; + Requests[52] = new DefaultHttpContext(); + Requests[52].RequestServices = CreateServices(); + Requests[52].Request.Method = "PUT"; + Requests[52].Request.Path = "/user/starred/348ac/b0848"; + Requests[53] = new DefaultHttpContext(); + Requests[53].RequestServices = CreateServices(); + Requests[53].Request.Method = "GET"; + Requests[53].Request.Path = "/user/starred/92490/2ff1b"; + Requests[54] = new DefaultHttpContext(); + Requests[54].RequestServices = CreateServices(); + Requests[54].Request.Method = "DELETE"; + Requests[54].Request.Path = "/user/starred/0670e/5ca5b"; + Requests[55] = new DefaultHttpContext(); + Requests[55].RequestServices = CreateServices(); + Requests[55].Request.Method = "PUT"; + Requests[55].Request.Path = "/user/subscriptions/a7cee/6440a"; + Requests[56] = new DefaultHttpContext(); + Requests[56].RequestServices = CreateServices(); + Requests[56].Request.Method = "GET"; + Requests[56].Request.Path = "/user/subscriptions/0f395/79a07"; + Requests[57] = new DefaultHttpContext(); + Requests[57].RequestServices = CreateServices(); + Requests[57].Request.Method = "DELETE"; + Requests[57].Request.Path = "/user/subscriptions/6df5e/ee72b"; + Requests[58] = new DefaultHttpContext(); + Requests[58].RequestServices = CreateServices(); + Requests[58].Request.Method = "GET"; + Requests[58].Request.Path = "/gists/bc994"; + Requests[59] = new DefaultHttpContext(); + Requests[59].RequestServices = CreateServices(); + Requests[59].Request.Method = "PATCH"; + Requests[59].Request.Path = "/gists/16fd7"; + Requests[60] = new DefaultHttpContext(); + Requests[60].RequestServices = CreateServices(); + Requests[60].Request.Method = "DELETE"; + Requests[60].Request.Path = "/gists/26a18"; + Requests[61] = new DefaultHttpContext(); + Requests[61].RequestServices = CreateServices(); + Requests[61].Request.Method = "PATCH"; + Requests[61].Request.Path = "/orgs/0843c"; + Requests[62] = new DefaultHttpContext(); + Requests[62].RequestServices = CreateServices(); + Requests[62].Request.Method = "GET"; + Requests[62].Request.Path = "/orgs/58d34"; + Requests[63] = new DefaultHttpContext(); + Requests[63].RequestServices = CreateServices(); + Requests[63].Request.Method = "PATCH"; + Requests[63].Request.Path = "/teams/9daee4"; + Requests[64] = new DefaultHttpContext(); + Requests[64].RequestServices = CreateServices(); + Requests[64].Request.Method = "GET"; + Requests[64].Request.Path = "/teams/9814e3"; + Requests[65] = new DefaultHttpContext(); + Requests[65].RequestServices = CreateServices(); + Requests[65].Request.Method = "DELETE"; + Requests[65].Request.Path = "/teams/1b5067"; + Requests[66] = new DefaultHttpContext(); + Requests[66].RequestServices = CreateServices(); + Requests[66].Request.Method = "GET"; + Requests[66].Request.Path = "/users/16a15e92"; + Requests[67] = new DefaultHttpContext(); + Requests[67].RequestServices = CreateServices(); + Requests[67].Request.Method = "GET"; + Requests[67].Request.Path = "/gists/8bfc9/comments"; + Requests[68] = new DefaultHttpContext(); + Requests[68].RequestServices = CreateServices(); + Requests[68].Request.Method = "POST"; + Requests[68].Request.Path = "/gists/8a24e/comments"; + Requests[69] = new DefaultHttpContext(); + Requests[69].RequestServices = CreateServices(); + Requests[69].Request.Method = "POST"; + Requests[69].Request.Path = "/gists/b269a/forks"; + Requests[70] = new DefaultHttpContext(); + Requests[70].RequestServices = CreateServices(); + Requests[70].Request.Method = "DELETE"; + Requests[70].Request.Path = "/gists/6fa8c/star"; + Requests[71] = new DefaultHttpContext(); + Requests[71].RequestServices = CreateServices(); + Requests[71].Request.Method = "GET"; + Requests[71].Request.Path = "/gists/31eb2/star"; + Requests[72] = new DefaultHttpContext(); + Requests[72].RequestServices = CreateServices(); + Requests[72].Request.Method = "PUT"; + Requests[72].Request.Path = "/gists/cf969/star"; + Requests[73] = new DefaultHttpContext(); + Requests[73].RequestServices = CreateServices(); + Requests[73].Request.Method = "GET"; + Requests[73].Request.Path = "/orgs/b92c3/events"; + Requests[74] = new DefaultHttpContext(); + Requests[74].RequestServices = CreateServices(); + Requests[74].Request.Method = "GET"; + Requests[74].Request.Path = "/orgs/afa76/issues"; + Requests[75] = new DefaultHttpContext(); + Requests[75].RequestServices = CreateServices(); + Requests[75].Request.Method = "GET"; + Requests[75].Request.Path = "/orgs/81b06/members"; + Requests[76] = new DefaultHttpContext(); + Requests[76].RequestServices = CreateServices(); + Requests[76].Request.Method = "GET"; + Requests[76].Request.Path = "/orgs/e9b92/public_members"; + Requests[77] = new DefaultHttpContext(); + Requests[77].RequestServices = CreateServices(); + Requests[77].Request.Method = "GET"; + Requests[77].Request.Path = "/orgs/96108/repos"; + Requests[78] = new DefaultHttpContext(); + Requests[78].RequestServices = CreateServices(); + Requests[78].Request.Method = "POST"; + Requests[78].Request.Path = "/orgs/7ef10/repos"; + Requests[79] = new DefaultHttpContext(); + Requests[79].RequestServices = CreateServices(); + Requests[79].Request.Method = "POST"; + Requests[79].Request.Path = "/orgs/91838/teams"; + Requests[80] = new DefaultHttpContext(); + Requests[80].RequestServices = CreateServices(); + Requests[80].Request.Method = "GET"; + Requests[80].Request.Path = "/orgs/c1602/teams"; + Requests[81] = new DefaultHttpContext(); + Requests[81].RequestServices = CreateServices(); + Requests[81].Request.Method = "GET"; + Requests[81].Request.Path = "/teams/ae1fcf/members"; + Requests[82] = new DefaultHttpContext(); + Requests[82].RequestServices = CreateServices(); + Requests[82].Request.Method = "GET"; + Requests[82].Request.Path = "/teams/82e12b/repos"; + Requests[83] = new DefaultHttpContext(); + Requests[83].RequestServices = CreateServices(); + Requests[83].Request.Method = "GET"; + Requests[83].Request.Path = "/users/c5b5ee96/events"; + Requests[84] = new DefaultHttpContext(); + Requests[84].RequestServices = CreateServices(); + Requests[84].Request.Method = "GET"; + Requests[84].Request.Path = "/users/3ebe3c63/followers"; + Requests[85] = new DefaultHttpContext(); + Requests[85].RequestServices = CreateServices(); + Requests[85].Request.Method = "GET"; + Requests[85].Request.Path = "/users/7eb8ee04/gists"; + Requests[86] = new DefaultHttpContext(); + Requests[86].RequestServices = CreateServices(); + Requests[86].Request.Method = "GET"; + Requests[86].Request.Path = "/users/ce5247f6/keys"; + Requests[87] = new DefaultHttpContext(); + Requests[87].RequestServices = CreateServices(); + Requests[87].Request.Method = "GET"; + Requests[87].Request.Path = "/users/4e212684/orgs"; + Requests[88] = new DefaultHttpContext(); + Requests[88].RequestServices = CreateServices(); + Requests[88].Request.Method = "GET"; + Requests[88].Request.Path = "/users/25bd0823/received_events"; + Requests[89] = new DefaultHttpContext(); + Requests[89].RequestServices = CreateServices(); + Requests[89].Request.Method = "GET"; + Requests[89].Request.Path = "/users/b5cdb182/repos"; + Requests[90] = new DefaultHttpContext(); + Requests[90].RequestServices = CreateServices(); + Requests[90].Request.Method = "GET"; + Requests[90].Request.Path = "/users/0926f1ee/starred"; + Requests[91] = new DefaultHttpContext(); + Requests[91].RequestServices = CreateServices(); + Requests[91].Request.Method = "GET"; + Requests[91].Request.Path = "/users/52fd945f/subscriptions"; + Requests[92] = new DefaultHttpContext(); + Requests[92].RequestServices = CreateServices(); + Requests[92].Request.Method = "GET"; + Requests[92].Request.Path = "/users/c079f122/received_events/public"; + Requests[93] = new DefaultHttpContext(); + Requests[93].RequestServices = CreateServices(); + Requests[93].Request.Method = "GET"; + Requests[93].Request.Path = "/users/f3d3f90a/events/orgs/b877e"; + Requests[94] = new DefaultHttpContext(); + Requests[94].RequestServices = CreateServices(); + Requests[94].Request.Method = "DELETE"; + Requests[94].Request.Path = "/gists/1425f/comments/3a1b3cd3-"; + Requests[95] = new DefaultHttpContext(); + Requests[95].RequestServices = CreateServices(); + Requests[95].Request.Method = "GET"; + Requests[95].Request.Path = "/gists/f885b/comments/1f24dd6a-"; + Requests[96] = new DefaultHttpContext(); + Requests[96].RequestServices = CreateServices(); + Requests[96].Request.Method = "PATCH"; + Requests[96].Request.Path = "/gists/0b9be/comments/3f237cca-"; + Requests[97] = new DefaultHttpContext(); + Requests[97].RequestServices = CreateServices(); + Requests[97].Request.Method = "DELETE"; + Requests[97].Request.Path = "/orgs/97bbf/members/3b77a2e7"; + Requests[98] = new DefaultHttpContext(); + Requests[98].RequestServices = CreateServices(); + Requests[98].Request.Method = "GET"; + Requests[98].Request.Path = "/orgs/c7fdb/members/76a43729"; + Requests[99] = new DefaultHttpContext(); + Requests[99].RequestServices = CreateServices(); + Requests[99].Request.Method = "PUT"; + Requests[99].Request.Path = "/orgs/191ee/public_members/119c87f1"; + Requests[100] = new DefaultHttpContext(); + Requests[100].RequestServices = CreateServices(); + Requests[100].Request.Method = "GET"; + Requests[100].Request.Path = "/orgs/ee407/public_members/8ff12154"; + Requests[101] = new DefaultHttpContext(); + Requests[101].RequestServices = CreateServices(); + Requests[101].Request.Method = "DELETE"; + Requests[101].Request.Path = "/orgs/4d9da/public_members/446d97e9"; + Requests[102] = new DefaultHttpContext(); + Requests[102].RequestServices = CreateServices(); + Requests[102].Request.Method = "GET"; + Requests[102].Request.Path = "/teams/fa75fc/members/978bd8d5"; + Requests[103] = new DefaultHttpContext(); + Requests[103].RequestServices = CreateServices(); + Requests[103].Request.Method = "PUT"; + Requests[103].Request.Path = "/teams/cbcdd2/members/d55986df"; + Requests[104] = new DefaultHttpContext(); + Requests[104].RequestServices = CreateServices(); + Requests[104].Request.Method = "DELETE"; + Requests[104].Request.Path = "/teams/cade8a/members/f83610eb"; + Requests[105] = new DefaultHttpContext(); + Requests[105].RequestServices = CreateServices(); + Requests[105].Request.Method = "DELETE"; + Requests[105].Request.Path = "/teams/1d5e29/memberships/5f9d213c"; + Requests[106] = new DefaultHttpContext(); + Requests[106].RequestServices = CreateServices(); + Requests[106].Request.Method = "PUT"; + Requests[106].Request.Path = "/teams/17d43f/memberships/5a2f444c"; + Requests[107] = new DefaultHttpContext(); + Requests[107].RequestServices = CreateServices(); + Requests[107].Request.Method = "GET"; + Requests[107].Request.Path = "/teams/12e975/memberships/8bb2fd20"; + Requests[108] = new DefaultHttpContext(); + Requests[108].RequestServices = CreateServices(); + Requests[108].Request.Method = "GET"; + Requests[108].Request.Path = "/users/c2ccdc27/following/85519b02-f"; + Requests[109] = new DefaultHttpContext(); + Requests[109].RequestServices = CreateServices(); + Requests[109].Request.Method = "PUT"; + Requests[109].Request.Path = "/teams/0c0a5a/repos/1f88c/6b60f"; + Requests[110] = new DefaultHttpContext(); + Requests[110].RequestServices = CreateServices(); + Requests[110].Request.Method = "DELETE"; + Requests[110].Request.Path = "/teams/6529ef/repos/ac003/62dd8"; + Requests[111] = new DefaultHttpContext(); + Requests[111].RequestServices = CreateServices(); + Requests[111].Request.Method = "GET"; + Requests[111].Request.Path = "/teams/101239/repos/51df9/aa3f3"; + Requests[112] = new DefaultHttpContext(); + Requests[112].RequestServices = CreateServices(); + Requests[112].Request.Method = "PATCH"; + Requests[112].Request.Path = "/repos/df7c9/ffcbe"; + Requests[113] = new DefaultHttpContext(); + Requests[113].RequestServices = CreateServices(); + Requests[113].Request.Method = "DELETE"; + Requests[113].Request.Path = "/repos/1f316/0f4ec"; + Requests[114] = new DefaultHttpContext(); + Requests[114].RequestServices = CreateServices(); + Requests[114].Request.Method = "GET"; + Requests[114].Request.Path = "/repos/08c95/7adde"; + Requests[115] = new DefaultHttpContext(); + Requests[115].RequestServices = CreateServices(); + Requests[115].Request.Method = "GET"; + Requests[115].Request.Path = "/networks/468be/29a19/events"; + Requests[116] = new DefaultHttpContext(); + Requests[116].RequestServices = CreateServices(); + Requests[116].Request.Method = "GET"; + Requests[116].Request.Path = "/repos/db561/2d460/assignees"; + Requests[117] = new DefaultHttpContext(); + Requests[117].RequestServices = CreateServices(); + Requests[117].Request.Method = "GET"; + Requests[117].Request.Path = "/repos/0eb0c/ec96b/branches"; + Requests[118] = new DefaultHttpContext(); + Requests[118].RequestServices = CreateServices(); + Requests[118].Request.Method = "GET"; + Requests[118].Request.Path = "/repos/8d58d/a31da/collaborators"; + Requests[119] = new DefaultHttpContext(); + Requests[119].RequestServices = CreateServices(); + Requests[119].Request.Method = "GET"; + Requests[119].Request.Path = "/repos/9d638/100e1/comments"; + Requests[120] = new DefaultHttpContext(); + Requests[120].RequestServices = CreateServices(); + Requests[120].Request.Method = "GET"; + Requests[120].Request.Path = "/repos/2afda/40624/commits"; + Requests[121] = new DefaultHttpContext(); + Requests[121].RequestServices = CreateServices(); + Requests[121].Request.Method = "GET"; + Requests[121].Request.Path = "/repos/583f1/f2f56/contributors"; + Requests[122] = new DefaultHttpContext(); + Requests[122].RequestServices = CreateServices(); + Requests[122].Request.Method = "GET"; + Requests[122].Request.Path = "/repos/96246/cf112/deployments"; + Requests[123] = new DefaultHttpContext(); + Requests[123].RequestServices = CreateServices(); + Requests[123].Request.Method = "POST"; + Requests[123].Request.Path = "/repos/af325/068fe/deployments"; + Requests[124] = new DefaultHttpContext(); + Requests[124].RequestServices = CreateServices(); + Requests[124].Request.Method = "GET"; + Requests[124].Request.Path = "/repos/726c3/22a16/downloads"; + Requests[125] = new DefaultHttpContext(); + Requests[125].RequestServices = CreateServices(); + Requests[125].Request.Method = "GET"; + Requests[125].Request.Path = "/repos/bc0e4/44660/events"; + Requests[126] = new DefaultHttpContext(); + Requests[126].RequestServices = CreateServices(); + Requests[126].Request.Method = "GET"; + Requests[126].Request.Path = "/repos/e8481/456ae/forks"; + Requests[127] = new DefaultHttpContext(); + Requests[127].RequestServices = CreateServices(); + Requests[127].Request.Method = "POST"; + Requests[127].Request.Path = "/repos/fa493/a87f8/forks"; + Requests[128] = new DefaultHttpContext(); + Requests[128].RequestServices = CreateServices(); + Requests[128].Request.Method = "POST"; + Requests[128].Request.Path = "/repos/e5328/77db0/hooks"; + Requests[129] = new DefaultHttpContext(); + Requests[129].RequestServices = CreateServices(); + Requests[129].Request.Method = "GET"; + Requests[129].Request.Path = "/repos/d219e/a06cf/hooks"; + Requests[130] = new DefaultHttpContext(); + Requests[130].RequestServices = CreateServices(); + Requests[130].Request.Method = "GET"; + Requests[130].Request.Path = "/repos/7a830/48c99/issues"; + Requests[131] = new DefaultHttpContext(); + Requests[131].RequestServices = CreateServices(); + Requests[131].Request.Method = "POST"; + Requests[131].Request.Path = "/repos/62103/40680/issues"; + Requests[132] = new DefaultHttpContext(); + Requests[132].RequestServices = CreateServices(); + Requests[132].Request.Method = "GET"; + Requests[132].Request.Path = "/repos/6b1aa/de4d3/keys"; + Requests[133] = new DefaultHttpContext(); + Requests[133].RequestServices = CreateServices(); + Requests[133].Request.Method = "POST"; + Requests[133].Request.Path = "/repos/47645/65364/keys"; + Requests[134] = new DefaultHttpContext(); + Requests[134].RequestServices = CreateServices(); + Requests[134].Request.Method = "GET"; + Requests[134].Request.Path = "/repos/f252f/d0dc5/labels"; + Requests[135] = new DefaultHttpContext(); + Requests[135].RequestServices = CreateServices(); + Requests[135].Request.Method = "POST"; + Requests[135].Request.Path = "/repos/8436b/fea30/labels"; + Requests[136] = new DefaultHttpContext(); + Requests[136].RequestServices = CreateServices(); + Requests[136].Request.Method = "GET"; + Requests[136].Request.Path = "/repos/d5058/668a7/languages"; + Requests[137] = new DefaultHttpContext(); + Requests[137].RequestServices = CreateServices(); + Requests[137].Request.Method = "POST"; + Requests[137].Request.Path = "/repos/a9546/9b556/merges"; + Requests[138] = new DefaultHttpContext(); + Requests[138].RequestServices = CreateServices(); + Requests[138].Request.Method = "GET"; + Requests[138].Request.Path = "/repos/5258b/6f2cd/milestones"; + Requests[139] = new DefaultHttpContext(); + Requests[139].RequestServices = CreateServices(); + Requests[139].Request.Method = "POST"; + Requests[139].Request.Path = "/repos/5f70d/513b2/milestones"; + Requests[140] = new DefaultHttpContext(); + Requests[140].RequestServices = CreateServices(); + Requests[140].Request.Method = "PUT"; + Requests[140].Request.Path = "/repos/90aa5/bdde5/notifications"; + Requests[141] = new DefaultHttpContext(); + Requests[141].RequestServices = CreateServices(); + Requests[141].Request.Method = "GET"; + Requests[141].Request.Path = "/repos/5f672/af6ff/notifications"; + Requests[142] = new DefaultHttpContext(); + Requests[142].RequestServices = CreateServices(); + Requests[142].Request.Method = "POST"; + Requests[142].Request.Path = "/repos/9277e/8e49c/pulls"; + Requests[143] = new DefaultHttpContext(); + Requests[143].RequestServices = CreateServices(); + Requests[143].Request.Method = "GET"; + Requests[143].Request.Path = "/repos/a4973/6dbf0/pulls"; + Requests[144] = new DefaultHttpContext(); + Requests[144].RequestServices = CreateServices(); + Requests[144].Request.Method = "GET"; + Requests[144].Request.Path = "/repos/a53d8/0627b/readme"; + Requests[145] = new DefaultHttpContext(); + Requests[145].RequestServices = CreateServices(); + Requests[145].Request.Method = "POST"; + Requests[145].Request.Path = "/repos/2bc2b/0a7aa/releases"; + Requests[146] = new DefaultHttpContext(); + Requests[146].RequestServices = CreateServices(); + Requests[146].Request.Method = "GET"; + Requests[146].Request.Path = "/repos/4bb1f/8220c/releases"; + Requests[147] = new DefaultHttpContext(); + Requests[147].RequestServices = CreateServices(); + Requests[147].Request.Method = "GET"; + Requests[147].Request.Path = "/repos/7a4e8/d54f8/stargazers"; + Requests[148] = new DefaultHttpContext(); + Requests[148].RequestServices = CreateServices(); + Requests[148].Request.Method = "GET"; + Requests[148].Request.Path = "/repos/63547/0d37c/subscribers"; + Requests[149] = new DefaultHttpContext(); + Requests[149].RequestServices = CreateServices(); + Requests[149].Request.Method = "PUT"; + Requests[149].Request.Path = "/repos/6cdda/a402c/subscription"; + Requests[150] = new DefaultHttpContext(); + Requests[150].RequestServices = CreateServices(); + Requests[150].Request.Method = "GET"; + Requests[150].Request.Path = "/repos/60320/4381b/subscription"; + Requests[151] = new DefaultHttpContext(); + Requests[151].RequestServices = CreateServices(); + Requests[151].Request.Method = "DELETE"; + Requests[151].Request.Path = "/repos/ed808/292c1/subscription"; + Requests[152] = new DefaultHttpContext(); + Requests[152].RequestServices = CreateServices(); + Requests[152].Request.Method = "GET"; + Requests[152].Request.Path = "/repos/74160/84ba7/tags"; + Requests[153] = new DefaultHttpContext(); + Requests[153].RequestServices = CreateServices(); + Requests[153].Request.Method = "GET"; + Requests[153].Request.Path = "/repos/ef45e/30a80/teams"; + Requests[154] = new DefaultHttpContext(); + Requests[154].RequestServices = CreateServices(); + Requests[154].Request.Method = "GET"; + Requests[154].Request.Path = "/repos/02825/d5092/watchers"; + Requests[155] = new DefaultHttpContext(); + Requests[155].RequestServices = CreateServices(); + Requests[155].Request.Method = "POST"; + Requests[155].Request.Path = "/repos/be33a/5f87e/git/blobs"; + Requests[156] = new DefaultHttpContext(); + Requests[156].RequestServices = CreateServices(); + Requests[156].Request.Method = "POST"; + Requests[156].Request.Path = "/repos/1a46d/093a4/git/commits"; + Requests[157] = new DefaultHttpContext(); + Requests[157].RequestServices = CreateServices(); + Requests[157].Request.Method = "GET"; + Requests[157].Request.Path = "/repos/14024/d64d2/git/refs"; + Requests[158] = new DefaultHttpContext(); + Requests[158].RequestServices = CreateServices(); + Requests[158].Request.Method = "POST"; + Requests[158].Request.Path = "/repos/d361c/fea9a/git/refs"; + Requests[159] = new DefaultHttpContext(); + Requests[159].RequestServices = CreateServices(); + Requests[159].Request.Method = "POST"; + Requests[159].Request.Path = "/repos/9a774/6a8ce/git/tags"; + Requests[160] = new DefaultHttpContext(); + Requests[160].RequestServices = CreateServices(); + Requests[160].Request.Method = "POST"; + Requests[160].Request.Path = "/repos/630e1/6a452/git/trees"; + Requests[161] = new DefaultHttpContext(); + Requests[161].RequestServices = CreateServices(); + Requests[161].Request.Method = "GET"; + Requests[161].Request.Path = "/repos/27ccf/49922/issues/comments"; + Requests[162] = new DefaultHttpContext(); + Requests[162].RequestServices = CreateServices(); + Requests[162].Request.Method = "GET"; + Requests[162].Request.Path = "/repos/1e557/77fad/issues/events"; + Requests[163] = new DefaultHttpContext(); + Requests[163].RequestServices = CreateServices(); + Requests[163].Request.Method = "GET"; + Requests[163].Request.Path = "/repos/297e9/11542/pulls/comments"; + Requests[164] = new DefaultHttpContext(); + Requests[164].RequestServices = CreateServices(); + Requests[164].Request.Method = "GET"; + Requests[164].Request.Path = "/repos/3c261/5719e/stats/code_frequency"; + Requests[165] = new DefaultHttpContext(); + Requests[165].RequestServices = CreateServices(); + Requests[165].Request.Method = "GET"; + Requests[165].Request.Path = "/repos/74b31/45543/stats/commit_activity"; + Requests[166] = new DefaultHttpContext(); + Requests[166].RequestServices = CreateServices(); + Requests[166].Request.Method = "GET"; + Requests[166].Request.Path = "/repos/909d8/56712/stats/contributors"; + Requests[167] = new DefaultHttpContext(); + Requests[167].RequestServices = CreateServices(); + Requests[167].Request.Method = "GET"; + Requests[167].Request.Path = "/repos/b8ecd/2a142/stats/participation"; + Requests[168] = new DefaultHttpContext(); + Requests[168].RequestServices = CreateServices(); + Requests[168].Request.Method = "GET"; + Requests[168].Request.Path = "/repos/72fcd/e0d41/stats/punch_card"; + Requests[169] = new DefaultHttpContext(); + Requests[169].RequestServices = CreateServices(); + Requests[169].Request.Method = "GET"; + Requests[169].Request.Path = "/repos/f8e07/b0719/git/blobs/a82e41d"; + Requests[170] = new DefaultHttpContext(); + Requests[170].RequestServices = CreateServices(); + Requests[170].Request.Method = "GET"; + Requests[170].Request.Path = "/repos/22ba5/326c5/git/commits/9fd2dd7"; + Requests[171] = new DefaultHttpContext(); + Requests[171].RequestServices = CreateServices(); + Requests[171].Request.Method = "DELETE"; + Requests[171].Request.Path = "/repos/6052c/2b5e3/git/refs/f9a94"; + Requests[172] = new DefaultHttpContext(); + Requests[172].RequestServices = CreateServices(); + Requests[172].Request.Method = "PATCH"; + Requests[172].Request.Path = "/repos/2de37/f89e9/git/refs/cda61"; + Requests[173] = new DefaultHttpContext(); + Requests[173].RequestServices = CreateServices(); + Requests[173].Request.Method = "GET"; + Requests[173].Request.Path = "/repos/66069/dcb46/git/refs/f682e"; + Requests[174] = new DefaultHttpContext(); + Requests[174].RequestServices = CreateServices(); + Requests[174].Request.Method = "GET"; + Requests[174].Request.Path = "/repos/cab0e/a64fa/git/tags/507f4b6"; + Requests[175] = new DefaultHttpContext(); + Requests[175].RequestServices = CreateServices(); + Requests[175].Request.Method = "GET"; + Requests[175].Request.Path = "/repos/76633/6d2b3/git/trees/a45672d"; + Requests[176] = new DefaultHttpContext(); + Requests[176].RequestServices = CreateServices(); + Requests[176].Request.Method = "GET"; + Requests[176].Request.Path = "/repos/611e5/14e81/issues/comments/70d5316a-"; + Requests[177] = new DefaultHttpContext(); + Requests[177].RequestServices = CreateServices(); + Requests[177].Request.Method = "PATCH"; + Requests[177].Request.Path = "/repos/d4bb1/0e97d/issues/comments/f03032b3-"; + Requests[178] = new DefaultHttpContext(); + Requests[178].RequestServices = CreateServices(); + Requests[178].Request.Method = "DELETE"; + Requests[178].Request.Path = "/repos/221b6/f1cdb/issues/comments/a769f3b6-"; + Requests[179] = new DefaultHttpContext(); + Requests[179].RequestServices = CreateServices(); + Requests[179].Request.Method = "GET"; + Requests[179].Request.Path = "/repos/7ad51/f85f2/issues/events/cec2e88"; + Requests[180] = new DefaultHttpContext(); + Requests[180].RequestServices = CreateServices(); + Requests[180].Request.Method = "PATCH"; + Requests[180].Request.Path = "/repos/5ad14/2c490/pulls/comments/0ce89290-"; + Requests[181] = new DefaultHttpContext(); + Requests[181].RequestServices = CreateServices(); + Requests[181].Request.Method = "GET"; + Requests[181].Request.Path = "/repos/13ba8/30bb3/pulls/comments/e643400f-"; + Requests[182] = new DefaultHttpContext(); + Requests[182].RequestServices = CreateServices(); + Requests[182].Request.Method = "DELETE"; + Requests[182].Request.Path = "/repos/cd3a3/d6224/pulls/comments/c225e938-"; + Requests[183] = new DefaultHttpContext(); + Requests[183].RequestServices = CreateServices(); + Requests[183].Request.Method = "PATCH"; + Requests[183].Request.Path = "/repos/33c72/2f2bd/releases/assets/d06f4"; + Requests[184] = new DefaultHttpContext(); + Requests[184].RequestServices = CreateServices(); + Requests[184].Request.Method = "DELETE"; + Requests[184].Request.Path = "/repos/18b7c/c3d06/releases/assets/97762"; + Requests[185] = new DefaultHttpContext(); + Requests[185].RequestServices = CreateServices(); + Requests[185].Request.Method = "GET"; + Requests[185].Request.Path = "/repos/38549/7ae63/releases/assets/2498e"; + Requests[186] = new DefaultHttpContext(); + Requests[186].RequestServices = CreateServices(); + Requests[186].Request.Method = "GET"; + Requests[186].Request.Path = "/repos/2cd8e/bb684/assignees/04058a9c"; + Requests[187] = new DefaultHttpContext(); + Requests[187].RequestServices = CreateServices(); + Requests[187].Request.Method = "GET"; + Requests[187].Request.Path = "/repos/ab58d/4a13e/branches/c48dc6"; + Requests[188] = new DefaultHttpContext(); + Requests[188].RequestServices = CreateServices(); + Requests[188].Request.Method = "PUT"; + Requests[188].Request.Path = "/repos/a856e/bf188/collaborators/5af36"; + Requests[189] = new DefaultHttpContext(); + Requests[189].RequestServices = CreateServices(); + Requests[189].Request.Method = "DELETE"; + Requests[189].Request.Path = "/repos/b17c0/6051b/collaborators/0b415"; + Requests[190] = new DefaultHttpContext(); + Requests[190].RequestServices = CreateServices(); + Requests[190].Request.Method = "GET"; + Requests[190].Request.Path = "/repos/e4fc3/95a37/collaborators/9bd1b"; + Requests[191] = new DefaultHttpContext(); + Requests[191].RequestServices = CreateServices(); + Requests[191].Request.Method = "DELETE"; + Requests[191].Request.Path = "/repos/fa150/ca148/comments/02b69539-"; + Requests[192] = new DefaultHttpContext(); + Requests[192].RequestServices = CreateServices(); + Requests[192].Request.Method = "GET"; + Requests[192].Request.Path = "/repos/6d3cc/5ad75/comments/c6a0df3f-"; + Requests[193] = new DefaultHttpContext(); + Requests[193].RequestServices = CreateServices(); + Requests[193].Request.Method = "PATCH"; + Requests[193].Request.Path = "/repos/883ad/d79a0/comments/edac8612-"; + Requests[194] = new DefaultHttpContext(); + Requests[194].RequestServices = CreateServices(); + Requests[194].Request.Method = "GET"; + Requests[194].Request.Path = "/repos/59b5d/9ffbe/commits/d6d94ac"; + Requests[195] = new DefaultHttpContext(); + Requests[195].RequestServices = CreateServices(); + Requests[195].Request.Method = "GET"; + Requests[195].Request.Path = "/repos/19be8/61e28/contents/f4c5a"; + Requests[196] = new DefaultHttpContext(); + Requests[196].RequestServices = CreateServices(); + Requests[196].Request.Method = "DELETE"; + Requests[196].Request.Path = "/repos/6e29a/43f51/contents/52ffe"; + Requests[197] = new DefaultHttpContext(); + Requests[197].RequestServices = CreateServices(); + Requests[197].Request.Method = "PUT"; + Requests[197].Request.Path = "/repos/a3605/bf28b/contents/a8fe5"; + Requests[198] = new DefaultHttpContext(); + Requests[198].RequestServices = CreateServices(); + Requests[198].Request.Method = "GET"; + Requests[198].Request.Path = "/repos/a869c/dd5fa/downloads/f1808e48-0"; + Requests[199] = new DefaultHttpContext(); + Requests[199].RequestServices = CreateServices(); + Requests[199].Request.Method = "DELETE"; + Requests[199].Request.Path = "/repos/94d90/ae933/downloads/f9e25b5f-6"; + Requests[200] = new DefaultHttpContext(); + Requests[200].RequestServices = CreateServices(); + Requests[200].Request.Method = "DELETE"; + Requests[200].Request.Path = "/repos/0fe83/e2353/hooks/a83a19"; + Requests[201] = new DefaultHttpContext(); + Requests[201].RequestServices = CreateServices(); + Requests[201].Request.Method = "GET"; + Requests[201].Request.Path = "/repos/648c3/fc66e/hooks/04b461"; + Requests[202] = new DefaultHttpContext(); + Requests[202].RequestServices = CreateServices(); + Requests[202].Request.Method = "PATCH"; + Requests[202].Request.Path = "/repos/3ac61/92269/hooks/79e4df"; + Requests[203] = new DefaultHttpContext(); + Requests[203].RequestServices = CreateServices(); + Requests[203].Request.Method = "GET"; + Requests[203].Request.Path = "/repos/8d523/ba527/issues/9a222a"; + Requests[204] = new DefaultHttpContext(); + Requests[204].RequestServices = CreateServices(); + Requests[204].Request.Method = "PATCH"; + Requests[204].Request.Path = "/repos/9ed96/f6553/issues/4df9d5"; + Requests[205] = new DefaultHttpContext(); + Requests[205].RequestServices = CreateServices(); + Requests[205].Request.Method = "GET"; + Requests[205].Request.Path = "/repos/472ec/16a4a/keys/84edc"; + Requests[206] = new DefaultHttpContext(); + Requests[206].RequestServices = CreateServices(); + Requests[206].Request.Method = "DELETE"; + Requests[206].Request.Path = "/repos/1a88d/4c019/keys/b3e45"; + Requests[207] = new DefaultHttpContext(); + Requests[207].RequestServices = CreateServices(); + Requests[207].Request.Method = "GET"; + Requests[207].Request.Path = "/repos/334fe/0c9d7/labels/4c1ee"; + Requests[208] = new DefaultHttpContext(); + Requests[208].RequestServices = CreateServices(); + Requests[208].Request.Method = "PATCH"; + Requests[208].Request.Path = "/repos/5dcde/f973d/labels/80435"; + Requests[209] = new DefaultHttpContext(); + Requests[209].RequestServices = CreateServices(); + Requests[209].Request.Method = "DELETE"; + Requests[209].Request.Path = "/repos/00a37/12e4b/labels/d7158"; + Requests[210] = new DefaultHttpContext(); + Requests[210].RequestServices = CreateServices(); + Requests[210].Request.Method = "PATCH"; + Requests[210].Request.Path = "/repos/e24fa/a168e/milestones/a38b20"; + Requests[211] = new DefaultHttpContext(); + Requests[211].RequestServices = CreateServices(); + Requests[211].Request.Method = "GET"; + Requests[211].Request.Path = "/repos/24592/f8af1/milestones/8e871e"; + Requests[212] = new DefaultHttpContext(); + Requests[212].RequestServices = CreateServices(); + Requests[212].Request.Method = "DELETE"; + Requests[212].Request.Path = "/repos/c5ee0/38414/milestones/da7976"; + Requests[213] = new DefaultHttpContext(); + Requests[213].RequestServices = CreateServices(); + Requests[213].Request.Method = "GET"; + Requests[213].Request.Path = "/repos/912e9/c81d6/pulls/ce677a"; + Requests[214] = new DefaultHttpContext(); + Requests[214].RequestServices = CreateServices(); + Requests[214].Request.Method = "PATCH"; + Requests[214].Request.Path = "/repos/aef6c/5ec7e/pulls/633b87"; + Requests[215] = new DefaultHttpContext(); + Requests[215].RequestServices = CreateServices(); + Requests[215].Request.Method = "PATCH"; + Requests[215].Request.Path = "/repos/6085e/ff8aa/releases/dcda3"; + Requests[216] = new DefaultHttpContext(); + Requests[216].RequestServices = CreateServices(); + Requests[216].Request.Method = "GET"; + Requests[216].Request.Path = "/repos/4d7f4/ceafa/releases/97fd7"; + Requests[217] = new DefaultHttpContext(); + Requests[217].RequestServices = CreateServices(); + Requests[217].Request.Method = "DELETE"; + Requests[217].Request.Path = "/repos/1f81e/89e6a/releases/6b744"; + Requests[218] = new DefaultHttpContext(); + Requests[218].RequestServices = CreateServices(); + Requests[218].Request.Method = "GET"; + Requests[218].Request.Path = "/repos/8b417/ad4a5/statuses/08833"; + Requests[219] = new DefaultHttpContext(); + Requests[219].RequestServices = CreateServices(); + Requests[219].Request.Method = "POST"; + Requests[219].Request.Path = "/repos/b7600/1d17b/statuses/9de98"; + Requests[220] = new DefaultHttpContext(); + Requests[220].RequestServices = CreateServices(); + Requests[220].Request.Method = "GET"; + Requests[220].Request.Path = "/repos/ceff2/ff6ae/commits/ae429/status"; + Requests[221] = new DefaultHttpContext(); + Requests[221].RequestServices = CreateServices(); + Requests[221].Request.Method = "GET"; + Requests[221].Request.Path = "/repos/13588/5ba9b/commits/534a9aa/comments"; + Requests[222] = new DefaultHttpContext(); + Requests[222].RequestServices = CreateServices(); + Requests[222].Request.Method = "POST"; + Requests[222].Request.Path = "/repos/ba059/a94ca/commits/dfc6938/comments"; + Requests[223] = new DefaultHttpContext(); + Requests[223].RequestServices = CreateServices(); + Requests[223].Request.Method = "GET"; + Requests[223].Request.Path = "/repos/39532/6e5f6/deployments/f41d0/statuses"; + Requests[224] = new DefaultHttpContext(); + Requests[224].RequestServices = CreateServices(); + Requests[224].Request.Method = "POST"; + Requests[224].Request.Path = "/repos/18373/23ac8/deployments/392c1/statuses"; + Requests[225] = new DefaultHttpContext(); + Requests[225].RequestServices = CreateServices(); + Requests[225].Request.Method = "POST"; + Requests[225].Request.Path = "/repos/73148/9d526/hooks/59adb8/tests"; + Requests[226] = new DefaultHttpContext(); + Requests[226].RequestServices = CreateServices(); + Requests[226].Request.Method = "POST"; + Requests[226].Request.Path = "/repos/19a36/91f69/issues/765c82/comments"; + Requests[227] = new DefaultHttpContext(); + Requests[227].RequestServices = CreateServices(); + Requests[227].Request.Method = "GET"; + Requests[227].Request.Path = "/repos/ffa54/61a37/issues/710513/comments"; + Requests[228] = new DefaultHttpContext(); + Requests[228].RequestServices = CreateServices(); + Requests[228].Request.Method = "GET"; + Requests[228].Request.Path = "/repos/fcb0f/0c8e2/issues/de0d38/events"; + Requests[229] = new DefaultHttpContext(); + Requests[229].RequestServices = CreateServices(); + Requests[229].Request.Method = "POST"; + Requests[229].Request.Path = "/repos/158d7/00bed/issues/9bc6b6/labels"; + Requests[230] = new DefaultHttpContext(); + Requests[230].RequestServices = CreateServices(); + Requests[230].Request.Method = "GET"; + Requests[230].Request.Path = "/repos/67c56/41fd8/issues/6b6858/labels"; + Requests[231] = new DefaultHttpContext(); + Requests[231].RequestServices = CreateServices(); + Requests[231].Request.Method = "PUT"; + Requests[231].Request.Path = "/repos/b8194/06cc6/issues/effa3d/labels"; + Requests[232] = new DefaultHttpContext(); + Requests[232].RequestServices = CreateServices(); + Requests[232].Request.Method = "DELETE"; + Requests[232].Request.Path = "/repos/57423/979fd/issues/6d3fce/labels"; + Requests[233] = new DefaultHttpContext(); + Requests[233].RequestServices = CreateServices(); + Requests[233].Request.Method = "GET"; + Requests[233].Request.Path = "/repos/d799e/53e56/milestones/80ebe2/labels"; + Requests[234] = new DefaultHttpContext(); + Requests[234].RequestServices = CreateServices(); + Requests[234].Request.Method = "GET"; + Requests[234].Request.Path = "/repos/55311/56e4a/pulls/666fa7/comments"; + Requests[235] = new DefaultHttpContext(); + Requests[235].RequestServices = CreateServices(); + Requests[235].Request.Method = "POST"; + Requests[235].Request.Path = "/repos/c9cd2/f3e91/pulls/ae901f/comments"; + Requests[236] = new DefaultHttpContext(); + Requests[236].RequestServices = CreateServices(); + Requests[236].Request.Method = "GET"; + Requests[236].Request.Path = "/repos/3195c/e552e/pulls/6b6d2b/commits"; + Requests[237] = new DefaultHttpContext(); + Requests[237].RequestServices = CreateServices(); + Requests[237].Request.Method = "GET"; + Requests[237].Request.Path = "/repos/2d6b6/77639/pulls/3423c6/files"; + Requests[238] = new DefaultHttpContext(); + Requests[238].RequestServices = CreateServices(); + Requests[238].Request.Method = "PUT"; + Requests[238].Request.Path = "/repos/6c6d3/3a6b0/pulls/1da47f/merge"; + Requests[239] = new DefaultHttpContext(); + Requests[239].RequestServices = CreateServices(); + Requests[239].Request.Method = "GET"; + Requests[239].Request.Path = "/repos/b144c/c707e/pulls/a5fcb2/merge"; + Requests[240] = new DefaultHttpContext(); + Requests[240].RequestServices = CreateServices(); + Requests[240].Request.Method = "GET"; + Requests[240].Request.Path = "/repos/81459/8618d/releases/65289/assets"; + Requests[241] = new DefaultHttpContext(); + Requests[241].RequestServices = CreateServices(); + Requests[241].Request.Method = "DELETE"; + Requests[241].Request.Path = "/repos/3c5a4/a7525/issues/e93fff/labels/cd7b0"; + Requests[242] = new DefaultHttpContext(); + Requests[242].RequestServices = CreateServices(); + Requests[242].Request.Method = "GET"; + Requests[242].Request.Path = "/repos/85bd9/70da2/2dc44fe9-83b9-/01762"; + } + + private Matcher SetupMatcher(MatcherBuilder builder) + { + builder.AddEndpoint(Endpoints[0]); + builder.AddEndpoint(Endpoints[1]); + builder.AddEndpoint(Endpoints[2]); + builder.AddEndpoint(Endpoints[3]); + builder.AddEndpoint(Endpoints[4]); + builder.AddEndpoint(Endpoints[5]); + builder.AddEndpoint(Endpoints[6]); + builder.AddEndpoint(Endpoints[7]); + builder.AddEndpoint(Endpoints[8]); + builder.AddEndpoint(Endpoints[9]); + builder.AddEndpoint(Endpoints[10]); + builder.AddEndpoint(Endpoints[11]); + builder.AddEndpoint(Endpoints[12]); + builder.AddEndpoint(Endpoints[13]); + builder.AddEndpoint(Endpoints[14]); + builder.AddEndpoint(Endpoints[15]); + builder.AddEndpoint(Endpoints[16]); + builder.AddEndpoint(Endpoints[17]); + builder.AddEndpoint(Endpoints[18]); + builder.AddEndpoint(Endpoints[19]); + builder.AddEndpoint(Endpoints[20]); + builder.AddEndpoint(Endpoints[21]); + builder.AddEndpoint(Endpoints[22]); + builder.AddEndpoint(Endpoints[23]); + builder.AddEndpoint(Endpoints[24]); + builder.AddEndpoint(Endpoints[25]); + builder.AddEndpoint(Endpoints[26]); + builder.AddEndpoint(Endpoints[27]); + builder.AddEndpoint(Endpoints[28]); + builder.AddEndpoint(Endpoints[29]); + builder.AddEndpoint(Endpoints[30]); + builder.AddEndpoint(Endpoints[31]); + builder.AddEndpoint(Endpoints[32]); + builder.AddEndpoint(Endpoints[33]); + builder.AddEndpoint(Endpoints[34]); + builder.AddEndpoint(Endpoints[35]); + builder.AddEndpoint(Endpoints[36]); + builder.AddEndpoint(Endpoints[37]); + builder.AddEndpoint(Endpoints[38]); + builder.AddEndpoint(Endpoints[39]); + builder.AddEndpoint(Endpoints[40]); + builder.AddEndpoint(Endpoints[41]); + builder.AddEndpoint(Endpoints[42]); + builder.AddEndpoint(Endpoints[43]); + builder.AddEndpoint(Endpoints[44]); + builder.AddEndpoint(Endpoints[45]); + builder.AddEndpoint(Endpoints[46]); + builder.AddEndpoint(Endpoints[47]); + builder.AddEndpoint(Endpoints[48]); + builder.AddEndpoint(Endpoints[49]); + builder.AddEndpoint(Endpoints[50]); + builder.AddEndpoint(Endpoints[51]); + builder.AddEndpoint(Endpoints[52]); + builder.AddEndpoint(Endpoints[53]); + builder.AddEndpoint(Endpoints[54]); + builder.AddEndpoint(Endpoints[55]); + builder.AddEndpoint(Endpoints[56]); + builder.AddEndpoint(Endpoints[57]); + builder.AddEndpoint(Endpoints[58]); + builder.AddEndpoint(Endpoints[59]); + builder.AddEndpoint(Endpoints[60]); + builder.AddEndpoint(Endpoints[61]); + builder.AddEndpoint(Endpoints[62]); + builder.AddEndpoint(Endpoints[63]); + builder.AddEndpoint(Endpoints[64]); + builder.AddEndpoint(Endpoints[65]); + builder.AddEndpoint(Endpoints[66]); + builder.AddEndpoint(Endpoints[67]); + builder.AddEndpoint(Endpoints[68]); + builder.AddEndpoint(Endpoints[69]); + builder.AddEndpoint(Endpoints[70]); + builder.AddEndpoint(Endpoints[71]); + builder.AddEndpoint(Endpoints[72]); + builder.AddEndpoint(Endpoints[73]); + builder.AddEndpoint(Endpoints[74]); + builder.AddEndpoint(Endpoints[75]); + builder.AddEndpoint(Endpoints[76]); + builder.AddEndpoint(Endpoints[77]); + builder.AddEndpoint(Endpoints[78]); + builder.AddEndpoint(Endpoints[79]); + builder.AddEndpoint(Endpoints[80]); + builder.AddEndpoint(Endpoints[81]); + builder.AddEndpoint(Endpoints[82]); + builder.AddEndpoint(Endpoints[83]); + builder.AddEndpoint(Endpoints[84]); + builder.AddEndpoint(Endpoints[85]); + builder.AddEndpoint(Endpoints[86]); + builder.AddEndpoint(Endpoints[87]); + builder.AddEndpoint(Endpoints[88]); + builder.AddEndpoint(Endpoints[89]); + builder.AddEndpoint(Endpoints[90]); + builder.AddEndpoint(Endpoints[91]); + builder.AddEndpoint(Endpoints[92]); + builder.AddEndpoint(Endpoints[93]); + builder.AddEndpoint(Endpoints[94]); + builder.AddEndpoint(Endpoints[95]); + builder.AddEndpoint(Endpoints[96]); + builder.AddEndpoint(Endpoints[97]); + builder.AddEndpoint(Endpoints[98]); + builder.AddEndpoint(Endpoints[99]); + builder.AddEndpoint(Endpoints[100]); + builder.AddEndpoint(Endpoints[101]); + builder.AddEndpoint(Endpoints[102]); + builder.AddEndpoint(Endpoints[103]); + builder.AddEndpoint(Endpoints[104]); + builder.AddEndpoint(Endpoints[105]); + builder.AddEndpoint(Endpoints[106]); + builder.AddEndpoint(Endpoints[107]); + builder.AddEndpoint(Endpoints[108]); + builder.AddEndpoint(Endpoints[109]); + builder.AddEndpoint(Endpoints[110]); + builder.AddEndpoint(Endpoints[111]); + builder.AddEndpoint(Endpoints[112]); + builder.AddEndpoint(Endpoints[113]); + builder.AddEndpoint(Endpoints[114]); + builder.AddEndpoint(Endpoints[115]); + builder.AddEndpoint(Endpoints[116]); + builder.AddEndpoint(Endpoints[117]); + builder.AddEndpoint(Endpoints[118]); + builder.AddEndpoint(Endpoints[119]); + builder.AddEndpoint(Endpoints[120]); + builder.AddEndpoint(Endpoints[121]); + builder.AddEndpoint(Endpoints[122]); + builder.AddEndpoint(Endpoints[123]); + builder.AddEndpoint(Endpoints[124]); + builder.AddEndpoint(Endpoints[125]); + builder.AddEndpoint(Endpoints[126]); + builder.AddEndpoint(Endpoints[127]); + builder.AddEndpoint(Endpoints[128]); + builder.AddEndpoint(Endpoints[129]); + builder.AddEndpoint(Endpoints[130]); + builder.AddEndpoint(Endpoints[131]); + builder.AddEndpoint(Endpoints[132]); + builder.AddEndpoint(Endpoints[133]); + builder.AddEndpoint(Endpoints[134]); + builder.AddEndpoint(Endpoints[135]); + builder.AddEndpoint(Endpoints[136]); + builder.AddEndpoint(Endpoints[137]); + builder.AddEndpoint(Endpoints[138]); + builder.AddEndpoint(Endpoints[139]); + builder.AddEndpoint(Endpoints[140]); + builder.AddEndpoint(Endpoints[141]); + builder.AddEndpoint(Endpoints[142]); + builder.AddEndpoint(Endpoints[143]); + builder.AddEndpoint(Endpoints[144]); + builder.AddEndpoint(Endpoints[145]); + builder.AddEndpoint(Endpoints[146]); + builder.AddEndpoint(Endpoints[147]); + builder.AddEndpoint(Endpoints[148]); + builder.AddEndpoint(Endpoints[149]); + builder.AddEndpoint(Endpoints[150]); + builder.AddEndpoint(Endpoints[151]); + builder.AddEndpoint(Endpoints[152]); + builder.AddEndpoint(Endpoints[153]); + builder.AddEndpoint(Endpoints[154]); + builder.AddEndpoint(Endpoints[155]); + builder.AddEndpoint(Endpoints[156]); + builder.AddEndpoint(Endpoints[157]); + builder.AddEndpoint(Endpoints[158]); + builder.AddEndpoint(Endpoints[159]); + builder.AddEndpoint(Endpoints[160]); + builder.AddEndpoint(Endpoints[161]); + builder.AddEndpoint(Endpoints[162]); + builder.AddEndpoint(Endpoints[163]); + builder.AddEndpoint(Endpoints[164]); + builder.AddEndpoint(Endpoints[165]); + builder.AddEndpoint(Endpoints[166]); + builder.AddEndpoint(Endpoints[167]); + builder.AddEndpoint(Endpoints[168]); + builder.AddEndpoint(Endpoints[169]); + builder.AddEndpoint(Endpoints[170]); + builder.AddEndpoint(Endpoints[171]); + builder.AddEndpoint(Endpoints[172]); + builder.AddEndpoint(Endpoints[173]); + builder.AddEndpoint(Endpoints[174]); + builder.AddEndpoint(Endpoints[175]); + builder.AddEndpoint(Endpoints[176]); + builder.AddEndpoint(Endpoints[177]); + builder.AddEndpoint(Endpoints[178]); + builder.AddEndpoint(Endpoints[179]); + builder.AddEndpoint(Endpoints[180]); + builder.AddEndpoint(Endpoints[181]); + builder.AddEndpoint(Endpoints[182]); + builder.AddEndpoint(Endpoints[183]); + builder.AddEndpoint(Endpoints[184]); + builder.AddEndpoint(Endpoints[185]); + builder.AddEndpoint(Endpoints[186]); + builder.AddEndpoint(Endpoints[187]); + builder.AddEndpoint(Endpoints[188]); + builder.AddEndpoint(Endpoints[189]); + builder.AddEndpoint(Endpoints[190]); + builder.AddEndpoint(Endpoints[191]); + builder.AddEndpoint(Endpoints[192]); + builder.AddEndpoint(Endpoints[193]); + builder.AddEndpoint(Endpoints[194]); + builder.AddEndpoint(Endpoints[195]); + builder.AddEndpoint(Endpoints[196]); + builder.AddEndpoint(Endpoints[197]); + builder.AddEndpoint(Endpoints[198]); + builder.AddEndpoint(Endpoints[199]); + builder.AddEndpoint(Endpoints[200]); + builder.AddEndpoint(Endpoints[201]); + builder.AddEndpoint(Endpoints[202]); + builder.AddEndpoint(Endpoints[203]); + builder.AddEndpoint(Endpoints[204]); + builder.AddEndpoint(Endpoints[205]); + builder.AddEndpoint(Endpoints[206]); + builder.AddEndpoint(Endpoints[207]); + builder.AddEndpoint(Endpoints[208]); + builder.AddEndpoint(Endpoints[209]); + builder.AddEndpoint(Endpoints[210]); + builder.AddEndpoint(Endpoints[211]); + builder.AddEndpoint(Endpoints[212]); + builder.AddEndpoint(Endpoints[213]); + builder.AddEndpoint(Endpoints[214]); + builder.AddEndpoint(Endpoints[215]); + builder.AddEndpoint(Endpoints[216]); + builder.AddEndpoint(Endpoints[217]); + builder.AddEndpoint(Endpoints[218]); + builder.AddEndpoint(Endpoints[219]); + builder.AddEndpoint(Endpoints[220]); + builder.AddEndpoint(Endpoints[221]); + builder.AddEndpoint(Endpoints[222]); + builder.AddEndpoint(Endpoints[223]); + builder.AddEndpoint(Endpoints[224]); + builder.AddEndpoint(Endpoints[225]); + builder.AddEndpoint(Endpoints[226]); + builder.AddEndpoint(Endpoints[227]); + builder.AddEndpoint(Endpoints[228]); + builder.AddEndpoint(Endpoints[229]); + builder.AddEndpoint(Endpoints[230]); + builder.AddEndpoint(Endpoints[231]); + builder.AddEndpoint(Endpoints[232]); + builder.AddEndpoint(Endpoints[233]); + builder.AddEndpoint(Endpoints[234]); + builder.AddEndpoint(Endpoints[235]); + builder.AddEndpoint(Endpoints[236]); + builder.AddEndpoint(Endpoints[237]); + builder.AddEndpoint(Endpoints[238]); + builder.AddEndpoint(Endpoints[239]); + builder.AddEndpoint(Endpoints[240]); + builder.AddEndpoint(Endpoints[241]); + builder.AddEndpoint(Endpoints[242]); + return builder.Build(); + } + + private MatcherEndpoint CreateEndpoint(string template, string controllerName, string actionName, string httpMethod) + { + var requiredValues = new + { + area = (string)null, + controller = controllerName, + action = actionName, + page = (string)null + }; + var defaults = new + { + area = (string)null, + controller = controllerName, + action = actionName, + page = (string)null + }; + + var metadata = new List(); + if (httpMethod != null) + { + metadata.Add(new HttpMethodMetadata(new string[] { httpMethod })); + } + + return CreateEndpoint( + template, + defaults: defaults, + requiredValues: requiredValues, + metadata: metadata, + routeName: controllerName); + } + } +} \ No newline at end of file diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteRouteValuesBasedEndpointFinderBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteRouteValuesBasedEndpointFinderBenchmark.cs new file mode 100644 index 0000000000..46241462ac --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteRouteValuesBasedEndpointFinderBenchmark.cs @@ -0,0 +1,74 @@ +// 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.Collections.Generic; +using BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Routing.LinkGeneration +{ + public class SingleRouteRouteValuesBasedEndpointFinderBenchmark : EndpointRoutingBenchmarkBase + { + private IEndpointFinder _finder; + private TestEndpointFinder _baseFinder; + private (HttpContext HttpContext, RouteValueDictionary AmbientValues) _requestContext; + + [GlobalSetup] + public void Setup() + { + var template = "Products/Details"; + var defaults = new { controller = "Products", action = "Details" }; + var requiredValues = new { controller = "Products", action = "Details" }; + + SetupEndpoints(CreateEndpoint(template, defaults, requiredValues: requiredValues, routeName: "ProductDetails")); + var services = CreateServices(); + _finder = services.GetRequiredService>(); + _baseFinder = new TestEndpointFinder(Endpoints[0]); + + _requestContext = CreateCurrentRequestContext(); + } + + [Benchmark(Baseline = true)] + public void Baseline() + { + var actual = _baseFinder.FindEndpoints(address: 0); + } + + [Benchmark] + public void RouteValues() + { + var actual = _finder.FindEndpoints(new RouteValuesAddress + { + AmbientValues = _requestContext.AmbientValues, + ExplicitValues = new RouteValueDictionary(new { controller = "Products", action = "Details" }), + RouteName = null + }); + } + + [Benchmark] + public void RouteName() + { + var actual = _finder.FindEndpoints(new RouteValuesAddress + { + AmbientValues = _requestContext.AmbientValues, + RouteName = "ProductDetails" + }); + } + + private class TestEndpointFinder : IEndpointFinder + { + private readonly Endpoint _endpoint; + + public TestEndpointFinder(Endpoint endpoint) + { + _endpoint = endpoint; + } + + public IEnumerable FindEndpoints(int address) + { + return new[] { _endpoint }; + } + } + } +} diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteWithConstraintsBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteWithConstraintsBenchmark.cs new file mode 100644 index 0000000000..271263bc7f --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteWithConstraintsBenchmark.cs @@ -0,0 +1,74 @@ +// 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 BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Tree; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Routing.LinkGeneration +{ + public class SingleRouteWithConstraintsBenchmark : EndpointRoutingBenchmarkBase + { + private TreeRouter _treeRouter; + private LinkGenerator _linkGenerator; + private (HttpContext HttpContext, RouteValueDictionary AmbientValues) _requestContext; + + [GlobalSetup] + public void Setup() + { + var template = "Customers/Details/{category}/{region}/{id:int}"; + var defaults = new { controller = "Customers", action = "Details" }; + var requiredValues = new { controller = "Customers", action = "Details" }; + + // Endpoint routing related + SetupEndpoints(CreateEndpoint(template, defaults, requiredValues: requiredValues)); + var services = CreateServices(); + _linkGenerator = services.GetRequiredService(); + + // Attribute routing related + var treeRouteBuilder = services.GetRequiredService(); + CreateOutboundRouteEntry(treeRouteBuilder, Endpoints[0]); + _treeRouter = treeRouteBuilder.Build(); + + _requestContext = CreateCurrentRequestContext(); + } + + [Benchmark(Baseline = true)] + public void TreeRouter() + { + var virtualPathData = _treeRouter.GetVirtualPath(new VirtualPathContext( + _requestContext.HttpContext, + ambientValues: _requestContext.AmbientValues, + values: new RouteValueDictionary( + new + { + controller = "Customers", + action = "Details", + category = "Administration", + region = "US", + id = 10 + }))); + + AssertUrl("/Customers/Details/Administration/US/10", virtualPathData?.VirtualPath); + } + + [Benchmark] + public void EndpointRouting() + { + var actualUrl = _linkGenerator.GetLink( + _requestContext.HttpContext, + values: new RouteValueDictionary( + new + { + controller = "Customers", + action = "Details", + category = "Administration", + region = "US", + id = 10 + })); + + AssertUrl("/Customers/Details/Administration/US/10", actualUrl); + } + } +} diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteWithNoParametersBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteWithNoParametersBenchmark.cs new file mode 100644 index 0000000000..0b2e30dc64 --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteWithNoParametersBenchmark.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 BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Tree; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Routing.LinkGeneration +{ + public class SingleRouteWithNoParametersBenchmark : EndpointRoutingBenchmarkBase + { + private TreeRouter _treeRouter; + private LinkGenerator _linkGenerator; + private (HttpContext HttpContext, RouteValueDictionary AmbientValues) _requestContext; + + [GlobalSetup] + public void Setup() + { + var template = "Products/Details"; + var defaults = new { controller = "Products", action = "Details" }; + var requiredValues = new { controller = "Products", action = "Details" }; + + // Endpoint routing related + SetupEndpoints(CreateEndpoint(template, defaults, requiredValues: requiredValues)); + var services = CreateServices(); + _linkGenerator = services.GetRequiredService(); + + // Attribute routing related + var treeRouteBuilder = services.GetRequiredService(); + CreateOutboundRouteEntry(treeRouteBuilder, Endpoints[0]); + _treeRouter = treeRouteBuilder.Build(); + + _requestContext = CreateCurrentRequestContext(); + } + + [Benchmark(Baseline = true)] + public void TreeRouter() + { + var virtualPathData = _treeRouter.GetVirtualPath(new VirtualPathContext( + _requestContext.HttpContext, + ambientValues: _requestContext.AmbientValues, + values: new RouteValueDictionary( + new + { + controller = "Products", + action = "Details", + }))); + + AssertUrl("/Products/Details", virtualPathData?.VirtualPath); + } + + [Benchmark] + public void EndpointRouting() + { + var actualUrl = _linkGenerator.GetLink( + _requestContext.HttpContext, + values: new RouteValueDictionary( + new + { + controller = "Products", + action = "Details", + })); + + AssertUrl("/Products/Details", actualUrl); + } + } +} diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteWithParametersBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteWithParametersBenchmark.cs new file mode 100644 index 0000000000..6ca5d74fbe --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/LinkGeneration/SingleRouteWithParametersBenchmark.cs @@ -0,0 +1,74 @@ +// 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 BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Tree; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Routing.LinkGeneration +{ + public class SingleRouteWithParametersBenchmark : EndpointRoutingBenchmarkBase + { + private TreeRouter _treeRouter; + private LinkGenerator _linkGenerator; + private (HttpContext HttpContext, RouteValueDictionary AmbientValues) _requestContext; + + [GlobalSetup] + public void Setup() + { + var template = "Customers/Details/{category}/{region}/{id}"; + var defaults = new { controller = "Customers", action = "Details" }; + var requiredValues = new { controller = "Customers", action = "Details" }; + + // Endpoint routing related + SetupEndpoints(CreateEndpoint(template, defaults, requiredValues: requiredValues)); + var services = CreateServices(); + _linkGenerator = services.GetRequiredService(); + + // Attribute routing related + var treeRouteBuilder = services.GetRequiredService(); + CreateOutboundRouteEntry(treeRouteBuilder, Endpoints[0]); + _treeRouter = treeRouteBuilder.Build(); + + _requestContext = CreateCurrentRequestContext(); + } + + [Benchmark(Baseline = true)] + public void TreeRouter() + { + var virtualPathData = _treeRouter.GetVirtualPath(new VirtualPathContext( + _requestContext.HttpContext, + ambientValues: _requestContext.AmbientValues, + values: new RouteValueDictionary( + new + { + controller = "Customers", + action = "Details", + category = "Administration", + region = "US", + id = 10 + }))); + + AssertUrl("/Customers/Details/Administration/US/10", virtualPathData?.VirtualPath); + } + + [Benchmark] + public void EndpointRouting() + { + var actualUrl = _linkGenerator.GetLink( + _requestContext.HttpContext, + values: new RouteValueDictionary( + new + { + controller = "Customers", + action = "Details", + category = "Administration", + region = "US", + id = 10 + })); + + AssertUrl("/Customers/Details/Administration/US/10", actualUrl); + } + } +} diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.cs index 1cd5b350f9..cf3a41c31f 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.cs @@ -7,7 +7,7 @@ using BenchmarkDotNet.Attributes; namespace Microsoft.AspNetCore.Routing.Matching { // Generated from https://github.com/Azure/azure-rest-api-specs - public partial class MatcherAzureBenchmark : MatcherBenchmarkBase + public partial class MatcherAzureBenchmark : EndpointRoutingBenchmarkBase { private const int SampleCount = 100; diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.generated.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.generated.cs index 0a2c549bfd..f486eb23e1 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.generated.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.generated.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Matching { // This code was generated by the Swaggatherer - public partial class MatcherAzureBenchmark : MatcherBenchmarkBase + public partial class MatcherAzureBenchmark : EndpointRoutingBenchmarkBase { private const int EndpointCount = 5160; diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherBenchmarkBase.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherBenchmarkBase.cs deleted file mode 100644 index 4b030f84dc..0000000000 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherBenchmarkBase.cs +++ /dev/null @@ -1,87 +0,0 @@ -// 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.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing.Patterns; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.AspNetCore.Routing.Matching -{ - public abstract class MatcherBenchmarkBase - { - private protected MatcherEndpoint[] Endpoints; - private protected HttpContext[] Requests; - - // The older routing implementations retrieve services when they first execute. - private protected static IServiceProvider CreateServices() - { - var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); - services.AddRouting(); - return services.BuildServiceProvider(); - } - - private protected DfaMatcherBuilder CreateDfaMatcherBuilder() - { - return CreateServices().GetRequiredService(); - } - - private protected static MatcherEndpoint CreateEndpoint(string template, string httpMethod = null) - { - var metadata = new List(); - if (httpMethod != null) - { - metadata.Add(new HttpMethodMetadata(new string[] { httpMethod, })); - } - - return new MatcherEndpoint( - MatcherEndpoint.EmptyInvoker, - RoutePatternFactory.Parse(template), - 0, - new EndpointMetadataCollection(metadata), - template); - } - - private protected static int[] SampleRequests(int endpointCount, int count) - { - // This isn't very high tech, but it's at least regular distribution. - // We sort the route templates by precedence, so this should result in - // an even distribution of the 'complexity' of the routes that are exercised. - var frequency = endpointCount / count; - if (frequency < 2) - { - throw new InvalidOperationException( - "The sample count is too high. This won't produce an accurate sampling" + - "of the request data."); - } - - var samples = new int[count]; - for (var i = 0; i < samples.Length; i++) - { - samples[i] = i * frequency; - } - - return samples; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private protected void Validate(HttpContext httpContext, Endpoint expected, Endpoint actual) - { - if (!object.ReferenceEquals(expected, actual)) - { - var message = new StringBuilder(); - message.AppendLine($"Validation failed for request {Array.IndexOf(Requests, httpContext)}"); - message.AppendLine($"{httpContext.Request.Method} {httpContext.Request.Path}"); - message.AppendLine($"expected: '{((MatcherEndpoint)expected)?.DisplayName ?? "null"}'"); - message.AppendLine($"actual: '{((MatcherEndpoint)actual)?.DisplayName ?? "null"}'"); - throw new InvalidOperationException(message.ToString()); - } - } - } -} diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetAzureBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetAzureBenchmark.cs index 96f3d2afcc..2f54d1bdbb 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetAzureBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetAzureBenchmark.cs @@ -7,7 +7,7 @@ using BenchmarkDotNet.Attributes; namespace Microsoft.AspNetCore.Routing.Matching { // Generated from https://github.com/Azure/azure-rest-api-specs - public partial class MatcherFindCandidateSetAzureBenchmark : MatcherBenchmarkBase + public partial class MatcherFindCandidateSetAzureBenchmark : EndpointRoutingBenchmarkBase { // SegmentCount should be max-segments + 1, but we don't have a good way to compute // it here, so using 16 as a safe guess. diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetAzureBenchmark.generated.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetAzureBenchmark.generated.cs index b7ee1d1e64..bd633b706c 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetAzureBenchmark.generated.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetAzureBenchmark.generated.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Matching { // This code was generated by the Swaggatherer - public partial class MatcherFindCandidateSetAzureBenchmark : MatcherBenchmarkBase + public partial class MatcherFindCandidateSetAzureBenchmark : EndpointRoutingBenchmarkBase { private const int EndpointCount = 3517; diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetGithubBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetGithubBenchmark.cs index a8eb4b1e8f..bc6751f0ca 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetGithubBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetGithubBenchmark.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Generated from https://github.com/APIs-guru/openapi-directory // Use https://editor2.swagger.io/ to convert from yaml to json- - public partial class MatcherFindCandidateSetGithubBenchmark : MatcherBenchmarkBase + public partial class MatcherFindCandidateSetGithubBenchmark : EndpointRoutingBenchmarkBase { // SegmentCount should be max-segments + 1, but we don't have a good way to compute // it here, so using 16 as a safe guess. diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetGithubBenchmark.generated.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetGithubBenchmark.generated.cs index 8bc1316747..ee94d2c67f 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetGithubBenchmark.generated.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetGithubBenchmark.generated.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Matching { // This code was generated by the Swaggatherer - public partial class MatcherFindCandidateSetGithubBenchmark : MatcherBenchmarkBase + public partial class MatcherFindCandidateSetGithubBenchmark : EndpointRoutingBenchmarkBase { private const int EndpointCount = 155; diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSingleEntryBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSingleEntryBenchmark.cs index f47a3c2c99..cf08ed656b 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSingleEntryBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSingleEntryBenchmark.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Matching { - public class MatcherFindCandidateSetSingleEntryBenchmark : MatcherBenchmarkBase + public class MatcherFindCandidateSetSingleEntryBenchmark : EndpointRoutingBenchmarkBase { // SegmentCount should be max-segments + 1 private const int SegmentCount = 2; diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSmallEntryCountBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSmallEntryCountBenchmark.cs index 4a194725b9..4bcb08517b 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSmallEntryCountBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSmallEntryCountBenchmark.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Matching { - public class MatcherFindCandidateSetSmallEntryCountBenchmark : MatcherBenchmarkBase + public class MatcherFindCandidateSetSmallEntryCountBenchmark : EndpointRoutingBenchmarkBase { // SegmentCount should be max-segments + 1 private const int SegmentCount = 6; diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.cs index 5444f94a0b..723bcbd549 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.cs @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Generated from https://github.com/APIs-guru/openapi-directory // Use https://editor2.swagger.io/ to convert from yaml to json- - public partial class MatcherGithubBenchmark : MatcherBenchmarkBase + public partial class MatcherGithubBenchmark : EndpointRoutingBenchmarkBase { private BarebonesMatcher _baseline; private Matcher _dfa; diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.generated.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.generated.cs index 0f5ecbc165..b6bc1ba4ff 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.generated.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.generated.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Matching { // This code was generated by the Swaggatherer - public partial class MatcherGithubBenchmark : MatcherBenchmarkBase + public partial class MatcherGithubBenchmark : EndpointRoutingBenchmarkBase { private const int EndpointCount = 243; diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherSingleEntryBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherSingleEntryBenchmark.cs index a51c4af375..f38883658a 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherSingleEntryBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherSingleEntryBenchmark.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Matching { // Just like TechEmpower Plaintext - public partial class MatcherSingleEntryBenchmark : MatcherBenchmarkBase + public partial class MatcherSingleEntryBenchmark : EndpointRoutingBenchmarkBase { private const int SampleCount = 100; diff --git a/benchmarks/Swaggatherer/Template.cs b/benchmarks/Swaggatherer/Template.cs index a48ecb7749..13704b22b1 100644 --- a/benchmarks/Swaggatherer/Template.cs +++ b/benchmarks/Swaggatherer/Template.cs @@ -10,51 +10,81 @@ namespace Swaggatherer { public static string Execute(IReadOnlyList entries) { + var controllerCount = 0; + var templatesVisited = new Dictionary( + StringComparer.OrdinalIgnoreCase); + var setupEndpointsLines = new List(); for (var i = 0; i < entries.Count; i++) - { + { var entry = entries[i]; - var httpMethodText = entry.Method == null ? string.Empty : $", \"{entry.Method.ToUpperInvariant()}\""; - setupEndpointsLines.Add($" _endpoints[{i}] = CreateEndpoint(\"{entry.Template.TemplateText}\"{httpMethodText});"); + + // In attribute routing, same template is used for all actions within that controller. The following + // simulates that where we only increment the controller count when a new endpoint for a new template + // is being created. + var template = entry.Template.TemplateText; + if (!templatesVisited.TryGetValue(template, out var visitedTemplateInfo)) + { + controllerCount++; + visitedTemplateInfo = (controllerCount, 0); + } + + // Increment the action count within a controller template + visitedTemplateInfo.ActionIndex++; + templatesVisited[template] = visitedTemplateInfo; + + var controllerName = $"Controller{visitedTemplateInfo.ControllerIndex}"; + var actionName = $"Action{visitedTemplateInfo.ActionIndex}"; + + var httpMethodText = entry.Method == null ? "httpMethod: null" : $"\"{entry.Method.ToUpperInvariant()}\""; + setupEndpointsLines.Add($" Endpoints[{i}] = CreateEndpoint(\"{template}\", \"{controllerName}\", \"{actionName}\", {httpMethodText});"); } var setupRequestsLines = new List(); for (var i = 0; i < entries.Count; i++) { - setupRequestsLines.Add($" _requests[{i}] = new DefaultHttpContext();"); - setupRequestsLines.Add($" _requests[{i}].RequestServices = CreateServices();"); - setupRequestsLines.Add($" _requests[{i}].Request.Method = \"{entries[i].Method.ToUpperInvariant()}\";"); - setupRequestsLines.Add($" _requests[{i}].Request.Path = \"{entries[i].RequestUrl}\";"); + var entry = entries[i]; + setupRequestsLines.Add($" Requests[{i}] = new DefaultHttpContext();"); + setupRequestsLines.Add($" Requests[{i}].RequestServices = CreateServices();"); + + if (entry.Method != null) + { + setupRequestsLines.Add($" Requests[{i}].Request.Method = \"{entries[i].Method.ToUpperInvariant()}\";"); + } + + setupRequestsLines.Add($" Requests[{i}].Request.Path = \"{entries[i].RequestUrl}\";"); } var setupMatcherLines = new List(); for (var i = 0; i < entries.Count; i++) { - setupMatcherLines.Add($" builder.AddEndpoint(_endpoints[{i}]);"); + setupMatcherLines.Add($" builder.AddEndpoint(Endpoints[{i}]);"); } return string.Format(@" // 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.Collections.Generic; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Matching; -namespace Microsoft.AspNetCore.Routing.Matching +namespace Microsoft.AspNetCore.Routing {{ // This code was generated by the Swaggatherer - public partial class GeneratedBenchmark : MatcherBenchmarkBase + public partial class GeneratedBenchmark : EndpointRoutingBenchmarkBase {{ private const int EndpointCount = {3}; private void SetupEndpoints() {{ - _endpoints = new MatcherEndpoint[{3}]; + Endpoints = new MatcherEndpoint[{3}]; {0} }} private void SetupRequests() {{ - _requests = new HttpContext[{3}]; + Requests = new HttpContext[{3}]; {1} }} @@ -63,8 +93,39 @@ namespace Microsoft.AspNetCore.Routing.Matching {2} return builder.Build(); }} + + private MatcherEndpoint CreateEndpoint(string template, string controllerName, string actionName, string httpMethod) + {{ + var requiredValues = new + {{ + area = (string)null, + controller = controllerName, + action = actionName, + page = (string)null + }}; + var defaults = new + {{ + area = (string)null, + controller = controllerName, + action = actionName, + page = (string)null + }}; + + var metadata = new List(); + if (httpMethod != null) + {{ + metadata.Add(new HttpMethodMetadata(new string[] {{ httpMethod }})); + }} + + return CreateEndpoint( + template, + defaults: defaults, + requiredValues: requiredValues, + metadata: metadata, + routeName: controllerName); + }} }} -}}", +}}", string.Join(Environment.NewLine, setupEndpointsLines), string.Join(Environment.NewLine, setupRequestsLines), string.Join(Environment.NewLine, setupMatcherLines), diff --git a/build/dependencies.props b/build/dependencies.props index c6ef7197d4..2e29b921e1 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -42,6 +42,6 @@ 2.3.1 2.4.0 - + diff --git a/samples/RoutingSample.Web/UseEndpointRoutingStartup.cs b/samples/RoutingSample.Web/UseEndpointRoutingStartup.cs index d4f072352c..24a92f19e5 100644 --- a/samples/RoutingSample.Web/UseEndpointRoutingStartup.cs +++ b/samples/RoutingSample.Web/UseEndpointRoutingStartup.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Internal; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; namespace RoutingSample.Web { @@ -78,21 +79,55 @@ namespace RoutingSample.Web }, "/withoptionalconstraints/{id:endsWith(_001)?}", "withoptionalconstraints"); - builder.MapEndpoint( - (next) => (httpContext) => - { - using (var writer = new StreamWriter(httpContext.Response.Body, Encoding.UTF8, 1024, leaveOpen: true)) + builder.MapEndpoint( + (next) => (httpContext) => { - var graphWriter = httpContext.RequestServices.GetRequiredService(); - var dataSource = httpContext.RequestServices.GetRequiredService(); - graphWriter.Write(dataSource, writer); - } + using (var writer = new StreamWriter(httpContext.Response.Body, Encoding.UTF8, 1024, leaveOpen: true)) + { + var graphWriter = httpContext.RequestServices.GetRequiredService(); + var dataSource = httpContext.RequestServices.GetRequiredService(); + graphWriter.Write(dataSource, writer); + } - return Task.CompletedTask; - }, - "/graph", - "DFA Graph", - new object[] { new HttpMethodMetadata(new[] { "GET", }) }); + return Task.CompletedTask; + }, + "/graph", + "DFA Graph", + new object[] { new HttpMethodMetadata(new[] { "GET", }) }); + builder.MapEndpoint( + (next) => (httpContext) => + { + var linkGenerator = httpContext.RequestServices.GetRequiredService(); + + var response = httpContext.Response; + response.StatusCode = 200; + response.ContentType = "text/plain"; + return response.WriteAsync( + "Link: " + linkGenerator.GetLink(httpContext, "WithSingleAsteriskCatchAll", new { })); + }, + "/WithSingleAsteriskCatchAll/{*path}", + "WithSingleAsteriskCatchAll", + new object[] + { + new RouteValuesAddressMetadata(name: "WithSingleAsteriskCatchAll", requiredValues: new RouteValueDictionary()), + }); + builder.MapEndpoint( + (next) => (httpContext) => + { + var linkGenerator = httpContext.RequestServices.GetRequiredService(); + + var response = httpContext.Response; + response.StatusCode = 200; + response.ContentType = "text/plain"; + return response.WriteAsync( + "Link: " + linkGenerator.GetLink(httpContext, "WithDoubleAsteriskCatchAll", new { })); + }, + "/WithDoubleAsteriskCatchAll/{**path}", + "WithDoubleAsteriskCatchAll", + new object[] + { + new RouteValuesAddressMetadata(name: "WithDoubleAsteriskCatchAll", requiredValues: new RouteValueDictionary()) + }); }); // Imagine some more stuff here... diff --git a/src/Microsoft.AspNetCore.Routing/Internal/UriBuildingContext.cs b/src/Microsoft.AspNetCore.Routing/Internal/UriBuildingContext.cs index 3b78fe8c78..3b7c3b743d 100644 --- a/src/Microsoft.AspNetCore.Routing/Internal/UriBuildingContext.cs +++ b/src/Microsoft.AspNetCore.Routing/Internal/UriBuildingContext.cs @@ -43,6 +43,11 @@ namespace Microsoft.AspNetCore.Routing.Internal public TextWriter Writer { get; } public bool Accept(string value) + { + return Accept(value, encodeSlashes: true); + } + + public bool Accept(string value, bool encodeSlashes) { if (string.IsNullOrEmpty(value)) { @@ -67,7 +72,7 @@ namespace Microsoft.AspNetCore.Routing.Internal { if (_buffer[i].RequiresEncoding) { - _urlEncoder.Encode(Writer, _buffer[i].Value); + EncodeValue(_buffer[i].Value); } else { @@ -88,16 +93,17 @@ namespace Microsoft.AspNetCore.Routing.Internal UriState = SegmentState.Inside; _lastValueOffset = _uri.Length; + // Allow the first segment to have a leading slash. // This prevents the leading slash from PathString segments from being encoded. if (_uri.Length == 0 && value.Length > 0 && value[0] == '/') { _uri.Append("/"); - _urlEncoder.Encode(Writer, value, 1, value.Length - 1); + EncodeValue(value, 1, value.Length - 1, encodeSlashes); } else { - _urlEncoder.Encode(Writer, value); + EncodeValue(value, encodeSlashes); } return true; @@ -197,6 +203,44 @@ namespace Microsoft.AspNetCore.Routing.Internal return _uri.ToString(); } + private void EncodeValue(string value) + { + EncodeValue(value, encodeSlashes: true); + } + + private void EncodeValue(string value, bool encodeSlashes) + { + EncodeValue(value, start: 0, characterCount: value.Length, encodeSlashes); + } + + // For testing + internal void EncodeValue(string value, int start, int characterCount, bool encodeSlashes) + { + // Just encode everything if its ok to encode slashes + if (encodeSlashes) + { + _urlEncoder.Encode(Writer, value, start, characterCount); + } + else + { + int end; + int length = start + characterCount; + while ((end = value.IndexOf('/', start, characterCount)) >= 0) + { + _urlEncoder.Encode(Writer, value, start, end - start); + _uri.Append("/"); + + start = end + 1; + characterCount = length - start; + } + + if (end < 0 && characterCount >= 0) + { + _urlEncoder.Encode(Writer, value, start, length - start); + } + } + } + private string DebuggerToString() { return string.Format("{{Accepted: '{0}' Buffered: '{1}'}}", _uri, string.Join("", _buffer)); diff --git a/src/Microsoft.AspNetCore.Routing/Patterns/RouteParameterParser.cs b/src/Microsoft.AspNetCore.Routing/Patterns/RouteParameterParser.cs index 885f77481c..8c0132bc06 100644 --- a/src/Microsoft.AspNetCore.Routing/Patterns/RouteParameterParser.cs +++ b/src/Microsoft.AspNetCore.Routing/Patterns/RouteParameterParser.cs @@ -27,9 +27,17 @@ namespace Microsoft.AspNetCore.Routing.Patterns var startIndex = 0; var endIndex = parameter.Length - 1; + var encodeSlashes = true; var parameterKind = RoutePatternParameterKind.Standard; - if (parameter[0] == '*') + + if (parameter.StartsWith("**", StringComparison.Ordinal)) + { + encodeSlashes = false; + parameterKind = RoutePatternParameterKind.CatchAll; + startIndex += 2; + } + else if (parameter[0] == '*') { parameterKind = RoutePatternParameterKind.CatchAll; startIndex++; @@ -79,7 +87,12 @@ namespace Microsoft.AspNetCore.Routing.Patterns defaultValue = parameter.Substring(currentIndex + 1, endIndex - currentIndex); } - return new RoutePatternParameterPart(parameterName, defaultValue, parameterKind, parseResults.Constraints.ToArray()); + return new RoutePatternParameterPart( + parameterName, + defaultValue, + parameterKind, + parseResults.Constraints.ToArray(), + encodeSlashes); } private static ConstraintParseResults ParseConstraints( @@ -237,7 +250,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns public readonly int CurrentIndex; public readonly IReadOnlyList Constraints; - + public ConstraintParseResults(int currentIndex, IReadOnlyList constraints) { CurrentIndex = currentIndex; diff --git a/src/Microsoft.AspNetCore.Routing/Patterns/RoutePatternFactory.cs b/src/Microsoft.AspNetCore.Routing/Patterns/RoutePatternFactory.cs index 54a34c9d4c..44dd675543 100644 --- a/src/Microsoft.AspNetCore.Routing/Patterns/RoutePatternFactory.cs +++ b/src/Microsoft.AspNetCore.Routing/Patterns/RoutePatternFactory.cs @@ -284,7 +284,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns { var segment = VisitSegment(updatedSegments[i]); updatedSegments[i] = segment; - + for (var j = 0; j < segment.Parts.Count; j++) { if (segment.Parts[j] is RoutePatternParameterPart parameter) @@ -339,7 +339,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns @default = newDefault; } - + if (parameter.Default != null) { updatedDefaults.Add(parameter.Name, parameter.Default); @@ -361,7 +361,8 @@ namespace Microsoft.AspNetCore.Routing.Patterns parameter.Name, @default, parameter.ParameterKind, - (IEnumerable)parameterConstraints ?? Array.Empty()); + (IEnumerable)parameterConstraints ?? Array.Empty(), + parameter.EncodeSlashes); } } @@ -624,7 +625,22 @@ namespace Microsoft.AspNetCore.Routing.Patterns RoutePatternParameterKind parameterKind, IEnumerable constraints) { - return new RoutePatternParameterPart(parameterName, @default, parameterKind, constraints.ToArray()); + return ParameterPartCore(parameterName, @default, parameterKind, constraints, encodeSlashes: true); + } + + private static RoutePatternParameterPart ParameterPartCore( + string parameterName, + object @default, + RoutePatternParameterKind parameterKind, + IEnumerable constraints, + bool encodeSlashes) + { + return new RoutePatternParameterPart( + parameterName, + @default, + parameterKind, + constraints.ToArray(), + encodeSlashes); } /// diff --git a/src/Microsoft.AspNetCore.Routing/Patterns/RoutePatternParameterPart.cs b/src/Microsoft.AspNetCore.Routing/Patterns/RoutePatternParameterPart.cs index 04f9056573..df060de9d9 100644 --- a/src/Microsoft.AspNetCore.Routing/Patterns/RoutePatternParameterPart.cs +++ b/src/Microsoft.AspNetCore.Routing/Patterns/RoutePatternParameterPart.cs @@ -19,6 +19,16 @@ namespace Microsoft.AspNetCore.Routing.Patterns object @default, RoutePatternParameterKind parameterKind, RoutePatternConstraintReference[] constraints) + : this(parameterName, @default, parameterKind, constraints, encodeSlashes: true) + { + } + + internal RoutePatternParameterPart( + string parameterName, + object @default, + RoutePatternParameterKind parameterKind, + RoutePatternConstraintReference[] constraints, + bool encodeSlashes) : base(RoutePatternPartKind.Parameter) { // See #475 - this code should have some asserts, but it can't because of the design of RouteParameterParser. @@ -27,6 +37,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns Default = @default; ParameterKind = parameterKind; Constraints = constraints; + EncodeSlashes = encodeSlashes; } /// @@ -34,6 +45,11 @@ namespace Microsoft.AspNetCore.Routing.Patterns /// public IReadOnlyList Constraints { get; } + /// + /// Gets the value indicating if slashes in current parameter's value should be encoded. + /// + public bool EncodeSlashes { get; } + /// /// Gets the default value of this route parameter. May be null. /// diff --git a/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs b/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs index d9df9f30f6..c96f540133 100644 --- a/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs +++ b/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.Extensions.ObjectPool; @@ -223,12 +224,12 @@ namespace Microsoft.AspNetCore.Routing.Template // If the value is not accepted, it is null or empty value in the // middle of the segment. We accept this if the parameter is an // optional parameter and it is preceded by an optional seperator. - // I this case, we need to remove the optional seperator that we + // In this case, we need to remove the optional seperator that we // have added to the URI // Example: template = {id}.{format?}. parameters: id=5 // In this case after we have generated "5.", we wont find any value // for format, so we remove '.' and generate 5. - if (!context.Accept(converted)) + if (!context.Accept(converted, parameterPart.EncodeSlashes)) { if (j != 0 && parameterPart.IsOptional && (separatorPart = segment.Parts[j - 1] as RoutePatternSeparatorPart) != null) { diff --git a/test/Microsoft.AspNetCore.Routing.FunctionalTests/EndpointRoutingSampleTest.cs b/test/Microsoft.AspNetCore.Routing.FunctionalTests/EndpointRoutingSampleTest.cs index 820841a9f4..db27235e70 100644 --- a/test/Microsoft.AspNetCore.Routing.FunctionalTests/EndpointRoutingSampleTest.cs +++ b/test/Microsoft.AspNetCore.Routing.FunctionalTests/EndpointRoutingSampleTest.cs @@ -149,6 +149,54 @@ namespace Microsoft.AspNetCore.Routing.FunctionalTests Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } + [Theory] + [InlineData("/WithSingleAsteriskCatchAll/a/b/c", "Link: /WithSingleAsteriskCatchAll/a%2Fb%2Fc")] + [InlineData("/WithSingleAsteriskCatchAll/a/b b1/c c1", "Link: /WithSingleAsteriskCatchAll/a%2Fb%20b1%2Fc%20c1")] + public async Task GeneratesLink_ToEndpointWithSingleAsteriskCatchAllParameter_EncodesValue( + string url, + string expected) + { + // Arrange & Act + var response = await _client.GetAsync(url); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + var actualContent = await response.Content.ReadAsStringAsync(); + Assert.Equal(expected, actualContent); + } + + [Theory] + [InlineData("/WithDoubleAsteriskCatchAll/a/b/c", "Link: /WithDoubleAsteriskCatchAll/a/b/c")] + [InlineData("/WithDoubleAsteriskCatchAll/a/b/c/", "Link: /WithDoubleAsteriskCatchAll/a/b/c/")] + [InlineData("/WithDoubleAsteriskCatchAll/a//b/c", "Link: /WithDoubleAsteriskCatchAll/a//b/c")] + public async Task GeneratesLink_ToEndpointWithDoubleAsteriskCatchAllParameter_DoesNotEncodeSlashes( + string url, + string expected) + { + // Arrange & Act + var response = await _client.GetAsync(url); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + var actualContent = await response.Content.ReadAsStringAsync(); + Assert.Equal(expected, actualContent); + } + + [Fact] + public async Task GeneratesLink_ToEndpointWithDoubleAsteriskCatchAllParameter_EncodesContentOtherThanSlashes() + { + // Arrange & Act + var response = await _client.GetAsync("/WithDoubleAsteriskCatchAll/a/b b1/c c1"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + var actualContent = await response.Content.ReadAsStringAsync(); + Assert.Equal("Link: /WithDoubleAsteriskCatchAll/a/b%20b1/c%20c1", actualContent); + } + public void Dispose() { _testServer.Dispose(); diff --git a/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs index 84e9701cd8..1fe785ca8f 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs @@ -98,6 +98,76 @@ namespace Microsoft.AspNetCore.Routing Assert.Equal("/Home/Index", link); } + [Fact] + public void GetLink_EncodesIntermediate_DefaultValues() + { + // Arrange + var endpoint = EndpointFactory.CreateMatcherEndpoint("{p1}/{p2=a b}/{p3=foo}"); + var linkGenerator = CreateLinkGenerator(endpoint); + + // Act + var link = linkGenerator.GetLink(new { p1 = "Home", p3 = "bar" }); + + // Assert + Assert.Equal("/Home/a%20b/bar", link); + } + + [Theory] + [InlineData("a/b/c", "/Home/Index/a%2Fb%2Fc")] + [InlineData("a/b b1/c c1", "/Home/Index/a%2Fb%20b1%2Fc%20c1")] + public void GetLink_EncodesValue_OfSingleAsteriskCatchAllParameter(string routeValue, string expected) + { + // Arrange + var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{*path}"); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); + + // Act + var link = linkGenerator.GetLink(httpContext, new { path = routeValue }); + + // Assert + Assert.Equal(expected, link); + } + + [Theory] + [InlineData("/", "/Home/Index//")] + [InlineData("a", "/Home/Index/a")] + [InlineData("a/", "/Home/Index/a/")] + [InlineData("a/b", "/Home/Index/a/b")] + [InlineData("a/b/c", "/Home/Index/a/b/c")] + [InlineData("a/b/cc", "/Home/Index/a/b/cc")] + [InlineData("a/b/c/", "/Home/Index/a/b/c/")] + [InlineData("a/b/c//", "/Home/Index/a/b/c//")] + [InlineData("a//b//c", "/Home/Index/a//b//c")] + public void GetLink_DoesNotEncodeSlashes_OfDoubleAsteriskCatchAllParameter(string routeValue, string expected) + { + // Arrange + var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{**path}"); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); + + // Act + var link = linkGenerator.GetLink(httpContext, new { path = routeValue }); + + // Assert + Assert.Equal(expected, link); + } + + [Fact] + public void GetLink_EncodesContentOtherThanSlashes_OfDoubleAsteriskCatchAllParameter() + { + // Arrange + var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{**path}"); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); + + // Act + var link = linkGenerator.GetLink(httpContext, new { path = "a/b b1/c c1" }); + + // Assert + Assert.Equal("/Home/Index/a/b%20b1/c%20c1", link); + } + [Fact] public void GetLink_EncodesValues() { diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Internal/UriBuildingContextTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Internal/UriBuildingContextTest.cs new file mode 100644 index 0000000000..137520051c --- /dev/null +++ b/test/Microsoft.AspNetCore.Routing.Tests/Internal/UriBuildingContextTest.cs @@ -0,0 +1,69 @@ +// 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.Extensions.WebEncoders.Testing; +using Xunit; + +namespace Microsoft.AspNetCore.Routing.Internal +{ + public class UriBuildingContextTest + { + [Fact] + public void EncodeValue_EncodesEntireValue_WhenEncodeSlashes_IsFalse() + { + // Arrange + var urlTestEncoder = new UrlTestEncoder(); + var value = "a/b b1/c"; + var expected = "/UrlEncode[[a/b b1/c]]"; + var uriBuilldingContext = new UriBuildingContext(urlTestEncoder); + + // Act + uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: true); + + // Assert + Assert.Equal(expected, uriBuilldingContext.ToString()); + } + + [Fact] + public void EncodeValue_EncodesOnlySlashes_WhenEncodeSlashes_IsFalse() + { + // Arrange + var urlTestEncoder = new UrlTestEncoder(); + var value = "a/b b1/c"; + var expected = "/UrlEncode[[a]]/UrlEncode[[b b1]]/UrlEncode[[c]]"; + var uriBuilldingContext = new UriBuildingContext(urlTestEncoder); + + // Act + uriBuilldingContext.EncodeValue(value, 0, value.Length, encodeSlashes: false); + + // Assert + Assert.Equal(expected, uriBuilldingContext.ToString()); + } + + [Theory] + [InlineData("a/b b1/c", 0, 2, "/UrlEncode[[a]]/")] + [InlineData("a/b b1/c", 3, 4, "/UrlEncode[[ b1]]/")] + [InlineData("a/b b1/c", 3, 5, "/UrlEncode[[ b1]]/UrlEncode[[c]]")] + [InlineData("a/b b1/c/", 8, 1, "/")] + [InlineData("/", 0, 1, "/")] + [InlineData("/a", 0, 2, "/UrlEncode[[a]]")] + [InlineData("a", 0, 1, "/UrlEncode[[a]]")] + [InlineData("a/", 0, 2, "/UrlEncode[[a]]/")] + public void EncodeValue_EncodesOnlySlashes_WithinSubsegment_WhenEncodeSlashes_IsFalse( + string value, + int startIndex, + int characterCount, + string expected) + { + // Arrange + var urlTestEncoder = new UrlTestEncoder(); + var uriBuilldingContext = new UriBuildingContext(urlTestEncoder); + + // Act + uriBuilldingContext.EncodeValue(value, startIndex, characterCount, encodeSlashes: false); + + // Assert + Assert.Equal(expected, uriBuilldingContext.ToString()); + } + } +} diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Patterns/InlineRouteParameterParserTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Patterns/InlineRouteParameterParserTest.cs index 87ab942b23..0056b1c534 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Patterns/InlineRouteParameterParserTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Patterns/InlineRouteParameterParserTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; +using Microsoft.AspNetCore.Routing.Constraints; using Xunit; namespace Microsoft.AspNetCore.Routing.Patterns @@ -919,6 +920,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns [InlineData("", "")] [InlineData("?", "")] [InlineData("*", "")] + [InlineData("**", "")] [InlineData(" ", " ")] [InlineData("\t", "\t")] [InlineData("#!@#$%Q@#@%", "#!@#$%Q@#@%")] @@ -936,6 +938,134 @@ namespace Microsoft.AspNetCore.Routing.Patterns Assert.Null(templatePart.Default); } + [Fact] + public void ParseRouteParameter_WithSingleAsteriskCatchAll_IsParsedCorrectly() + { + // Arrange & Act + var parameterPart = ParseParameter("*path"); + + // Assert + Assert.Equal("path", parameterPart.Name); + Assert.True(parameterPart.IsCatchAll); + Assert.Equal(RoutePatternParameterKind.CatchAll, parameterPart.ParameterKind); + Assert.True(parameterPart.EncodeSlashes); + } + + [Fact] + public void ParseRouteParameter_WithSingleAsteriskCatchAll_AndDefaultValue_IsParsedCorrectly() + { + // Arrange & Act + var parameterPart = ParseParameter("*path=a/b/c"); + + // Assert + Assert.Equal("path", parameterPart.Name); + Assert.True(parameterPart.IsCatchAll); + Assert.NotNull(parameterPart.Default); + Assert.Equal("a/b/c", parameterPart.Default.ToString()); + Assert.Equal(RoutePatternParameterKind.CatchAll, parameterPart.ParameterKind); + Assert.True(parameterPart.EncodeSlashes); + } + + [Fact] + public void ParseRouteParameter_WithSingleAsteriskCatchAll_AndConstraints_IsParsedCorrectly() + { + // Arrange + var constraintContent = "regex(^(/[^/ ]*)+/?$)"; + + // Act + var parameterPart = ParseParameter($"*path:{constraintContent}"); + + // Assert + Assert.Equal("path", parameterPart.Name); + Assert.True(parameterPart.IsCatchAll); + Assert.Equal(RoutePatternParameterKind.CatchAll, parameterPart.ParameterKind); + var constraintReference = Assert.Single(parameterPart.Constraints); + Assert.Equal(constraintContent, constraintReference.Content); + Assert.True(parameterPart.EncodeSlashes); + } + + [Fact] + public void ParseRouteParameter_WithSingleAsteriskCatchAll_AndConstraints_AndDefaultValue_IsParsedCorrectly() + { + // Arrange + var constraintContent = "regex(^(/[^/ ]*)+/?$)"; + + // Act + var parameterPart = ParseParameter($"*path:{constraintContent}=a/b/c"); + + // Assert + Assert.Equal("path", parameterPart.Name); + Assert.True(parameterPart.IsCatchAll); + Assert.Equal(RoutePatternParameterKind.CatchAll, parameterPart.ParameterKind); + var constraintReference = Assert.Single(parameterPart.Constraints); + Assert.Equal(constraintContent, constraintReference.Content); + Assert.NotNull(parameterPart.Default); + Assert.Equal("a/b/c", parameterPart.Default.ToString()); + Assert.True(parameterPart.EncodeSlashes); + } + + [Fact] + public void ParseRouteParameter_WithDoubleAsteriskCatchAll_IsParsedCorrectly() + { + // Arrange & Act + var parameterPart = ParseParameter("**path"); + + // Assert + Assert.Equal("path", parameterPart.Name); + Assert.True(parameterPart.IsCatchAll); + Assert.False(parameterPart.EncodeSlashes); + } + + [Fact] + public void ParseRouteParameter_WithDoubleAsteriskCatchAll_AndDefaultValue_IsParsedCorrectly() + { + // Arrange & Act + var parameterPart = ParseParameter("**path=a/b/c"); + + // Assert + Assert.Equal("path", parameterPart.Name); + Assert.True(parameterPart.IsCatchAll); + Assert.NotNull(parameterPart.Default); + Assert.Equal("a/b/c", parameterPart.Default.ToString()); + Assert.False(parameterPart.EncodeSlashes); + } + + [Fact] + public void ParseRouteParameter_WithDoubleAsteriskCatchAll_AndConstraints_IsParsedCorrectly() + { + // Arrange + var constraintContent = "regex(^(/[^/ ]*)+/?$)"; + + // Act + var parameterPart = ParseParameter($"**path:{constraintContent}"); + + // Assert + Assert.Equal("path", parameterPart.Name); + Assert.True(parameterPart.IsCatchAll); + Assert.False(parameterPart.EncodeSlashes); + var constraintReference = Assert.Single(parameterPart.Constraints); + Assert.Equal(constraintContent, constraintReference.Content); + } + + [Fact] + public void ParseRouteParameter_WithDoubleAsteriskCatchAll_AndConstraints_AndDefaultValue_IsParsedCorrectly() + { + // Arrange + var constraintContent = "regex(^(/[^/ ]*)+/?$)"; + + // Act + var parameterPart = ParseParameter($"**path:{constraintContent}=a/b/c"); + + // Assert + Assert.Equal("path", parameterPart.Name); + Assert.True(parameterPart.IsCatchAll); + Assert.False(parameterPart.EncodeSlashes); + var constraintReference = Assert.Single(parameterPart.Constraints); + Assert.Equal(constraintContent, constraintReference.Content); + Assert.NotNull(parameterPart.Default); + Assert.Equal("a/b/c", parameterPart.Default.ToString()); + } + private RoutePatternParameterPart ParseParameter(string routeParameter) { // See: #475 - these tests don't pass the 'whole' text. diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Patterns/RoutePatternParserTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Patterns/RoutePatternParserTest.cs index 760544801e..0dfc68d072 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Patterns/RoutePatternParserTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Patterns/RoutePatternParserTest.cs @@ -470,7 +470,6 @@ namespace Microsoft.AspNetCore.Routing.Patterns } [Theory] - [InlineData("{**}", "*")] [InlineData("{a*}", "a*")] [InlineData("{*a*}", "a*")] [InlineData("{*a*:int}", "a*")] diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Template/TemplateParserTests.cs b/test/Microsoft.AspNetCore.Routing.Tests/Template/TemplateParserTests.cs index eabc3ec25e..39f2a1159e 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Template/TemplateParserTests.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Template/TemplateParserTests.cs @@ -626,7 +626,6 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests } [Theory] - [InlineData("{**}", "*")] [InlineData("{a*}", "a*")] [InlineData("{*a*}", "a*")] [InlineData("{*a*:int}", "a*")]