/// <summary>Matches the route.</summary> /// <param name="routes">The routes.</param> /// <param name="method">The method.</param> /// <param name="requestPath">The request path.</param> /// <returns></returns> public async Task <ApiRoutingItem> MatchRoute( IApiRoutingTable routes, string method, string requestPath) { var potentialRoutes = routes?.GetRoutes() .Where(r => r.Location != null) .Where(r => string.Compare(r.HttpMethod, method, true) == 0); potentialRoutes = potentialRoutes ?? new List <ApiRoutingItem>(); RouteMatch result; foreach (var route in potentialRoutes) { result = await this.ResolveRoute(route.Template, requestPath).ConfigureAwait(false); if (result?.IsMatch ?? false) { var newRoute = CloneRoutingItem(route); newRoute.RouteVariables = result.RouteVariables; return(newRoute); } } return(null); }
/// <summary>Discovers the routes.</summary> /// <param name="builder">The builder.</param> /// <param name="routingTable">The routing table.</param> /// <param name="config">The configuration.</param> private static void DiscoverRoutes(IApplicationBuilder builder, IApiRoutingTable routingTable, IDeepSleepServiceConfiguration config) { var discoveryStrategies = config?.DiscoveryStrategies == null || config.DiscoveryStrategies.Count == 0 ? DiscoveryStrategies.Default() : config.DiscoveryStrategies.Where(d => d != null).ToList(); foreach (var strategy in discoveryStrategies) { if (strategy == null) { continue; } var task = Task.Run(async() => { using (var scope = builder.ApplicationServices.CreateScope()) { return(await strategy.DiscoverRoutes(scope.ServiceProvider).ConfigureAwait(false)); } }); var registrations = task.Result; foreach (var registration in registrations) { if (registration == null) { continue; } routingTable.AddRoute(registration); } } }
/// <summary>Gets the template information.</summary> /// <param name="context">The context.</param> /// <param name="resolver">The resolver.</param> /// <param name="routes">The routes.</param> /// <returns></returns> private static async Task <ApiRoutingTemplate> GetRoutingTemplate( this ApiRequestContext context, IUriRouteResolver resolver, IApiRoutingTable routes) { RouteMatch result; ApiRoutingTemplate template = null; foreach (var route in routes.GetRoutes()) { result = await resolver.ResolveRoute(route.Template, context.Request.Path).ConfigureAwait(false); if (result?.IsMatch ?? false) { if (template == null) { template = new ApiRoutingTemplate(route.Template); } template.Locations.Add(new ApiEndpointLocation( controller: route.Location.Controller, methodInfo: route.Location.MethodInfo, httpMethod: route.Location.HttpMethod, bodyParameterType: route.Location.BodyParameterType, uriParameterType: route.Location.UriParameterType, simpleParameters: route.Location.SimpleParameters, methodReturnType: route.Location.MethodReturnType)); } } return(template); }
/// <summary>Processes the HTTP request method.</summary> /// <param name="context">The context.</param> /// <param name="routes">The routes.</param> /// <param name="resolver">The resolver.</param> /// <param name="defaultRequestConfiguration">The default request configuration.</param> /// <returns></returns> internal static async Task <bool> ProcessHttpRequestMethod(this ApiRequestContext context, IApiRoutingTable routes, IUriRouteResolver resolver, IDeepSleepRequestConfiguration defaultRequestConfiguration) { if (!context.RequestAborted.IsCancellationRequested) { // Templates exist for thies route if ((context.Routing?.Template?.Locations?.Count ?? 0) > 0) { // A route was not found for the template if (context.Routing.Route == null) { var methods = context.Routing.Template.Locations .Where(e => !string.IsNullOrWhiteSpace(e?.HttpMethod)) .Select(e => e.HttpMethod.ToUpper()) .Distinct() .ToList(); if (methods.Contains("GET") && !methods.Contains("HEAD")) { if (resolver != null) { var match = await resolver.MatchRoute( routes, "GET", context.Request.Path).ConfigureAwait(false); if (match != null) { var enableHeadForGetRequests = match.Configuration?.EnableHeadForGetRequests ?? defaultRequestConfiguration?.EnableHeadForGetRequests ?? ApiRequestContext.GetDefaultRequestConfiguration().EnableHeadForGetRequests ?? true; if (enableHeadForGetRequests) { methods.Add("HEAD"); } } } } context.Runtime.Internals.IsMethodNotFound = true; context.Response.AddHeader("Allow", string.Join(", ", methods)); context.Response.StatusCode = 405; return(false); } } return(true); } return(false); }
/// <summary>Writes the deepsleep to console.</summary> /// <param name="routingTable">The routing table.</param> /// <param name="defaultConfiguration">The default configuration.</param> internal static void WriteDeepSleepToConsole(IApiRoutingTable routingTable, IDeepSleepRequestConfiguration defaultConfiguration) { var systemConfiguration = ApiRequestContext.GetDefaultRequestConfiguration(); var existingColor = Console.ForegroundColor; var deepSleepAssemblyInfo = GetAssemplyInfo(typeof(ApiRequestContext)); var deepSleepWebAssemblyInfo = GetAssemplyInfo(typeof(ApiCoreHttpExtensionMethods)); var deepSleepOpenApiAssemblyInfo = GetAssemplyInfo(Type.GetType("DeepSleep.OpenApi.DeepSleepOasGenerator, deepsleep.openapi", false, false)); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($@""); Console.WriteLine($@" ____ _ "); Console.WriteLine($@"| \ ___ ___ ___ ___| |___ ___ ___ "); Console.WriteLine($@"| | | -_| -_| . |_ -| | -_| -_| . |"); Console.WriteLine($@"|____/|___|___| _|___|_|___|___| _|"); Console.Write($@" |_| |_| "); Console.ForegroundColor = existingColor; Console.WriteLine($" v{deepSleepAssemblyInfo.version}"); Console.WriteLine($""); if (!string.IsNullOrWhiteSpace(deepSleepWebAssemblyInfo.framework)) { Console.WriteLine($""); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"Target(s): "); Console.ForegroundColor = existingColor; Console.WriteLine($"------------------------------------------------"); Console.ForegroundColor = existingColor; Console.WriteLine($" {deepSleepAssemblyInfo.name}, {deepSleepAssemblyInfo.version}, {deepSleepAssemblyInfo.framework}"); if (!string.IsNullOrWhiteSpace(deepSleepOpenApiAssemblyInfo.name)) { Console.WriteLine($" {deepSleepOpenApiAssemblyInfo.name}, {deepSleepOpenApiAssemblyInfo.version}, {deepSleepOpenApiAssemblyInfo.framework}"); } Console.WriteLine($" {deepSleepWebAssemblyInfo.name}, {deepSleepWebAssemblyInfo.version}, {deepSleepWebAssemblyInfo.framework}"); Console.WriteLine($""); } Console.ForegroundColor = ConsoleColor.Yellow; Console.Write($"Endpoints: "); Console.ForegroundColor = existingColor; Console.WriteLine($"{routingTable?.GetRoutes()?.Count ?? 0}"); Console.WriteLine($"------------------------------------------------"); Console.WriteLine($""); var routes = (routingTable?.GetRoutes() ?? new List <ApiRoutingItem>()) .OrderBy(r => r.Template) .ToList(); Action <string, string, bool> writeRoute = (string method, string template, bool isAutoHead) => { existingColor = Console.ForegroundColor; Console.ForegroundColor = isAutoHead ? ConsoleColor.Gray : ConsoleColor.Yellow; Console.Write($" {method.ToUpper(),-9}"); Console.ForegroundColor = existingColor; WriteEndpointTemplate(template, isAutoHead); }; routes.ForEach(r => { writeRoute(r.HttpMethod, r.Template, false); if (string.Compare(r.HttpMethod, "GET", true) == 0) { var enableGet = r.Configuration?.EnableHeadForGetRequests ?? defaultConfiguration?.EnableHeadForGetRequests ?? systemConfiguration?.EnableHeadForGetRequests ?? true; if (enableGet) { var matchingHead = routes .Where(m => m.HttpMethod.ToLowerInvariant() == "head") .Where(m => m.Template.ToLowerInvariant() == r.Template.ToLowerInvariant()) .FirstOrDefault(); if (matchingHead == null) { writeRoute("HEAD", r.Template, true); } } } }); Console.WriteLine(""); var now = DateTime.Now; if (now.Month == 5 && now.Day == 4) { MayTheFourth(); } Console.WriteLine(); }
/// <summary>Processes the HTTP request routing.</summary> /// <param name="context">The context.</param> /// <param name="routes">The routes.</param> /// <param name="resolver">The resolver.</param> /// <param name="defaultRequestConfig">The default request configuration.</param> /// <returns></returns> internal static async Task <bool> ProcessHttpRequestRouting(this ApiRequestContext context, IApiRoutingTable routes, IUriRouteResolver resolver, IDeepSleepRequestConfiguration defaultRequestConfig) { if (!context.RequestAborted.IsCancellationRequested) { if (routes != null && resolver != null) { context.Routing.Route = await context.GetRoutingItem(resolver, routes, defaultRequestConfig).ConfigureAwait(false); context.Routing.Template = await context.GetRoutingTemplate(resolver, routes).ConfigureAwait(false); } context.Configuration = MergeConfigurations( serviceProvider: context?.RequestServices, defaultConfig: defaultRequestConfig, endpointConfig: context.Routing?.Route?.Configuration); if (context.Routing.Route?.Location?.MethodInfo != null) { var attributes = context.Routing.Route.Location.MethodInfo.GetCustomAttributes(); // Find any attribute request pipeline components // and add the to the final configuration of the request. attributes .Where(a => a as IRequestPipelineComponent != null) .Select(a => a as IRequestPipelineComponent) .ToList() .ForEach(p => context.Configuration.PipelineComponents.Add(p)); // Find any attribute validator components // and add the to the final configuration of the request. attributes .Where(a => a as IEndpointValidatorComponent != null) .Select(a => a as IEndpointValidatorComponent) .ToList() .ForEach(v => context.Configuration.Validators.Add(v)); // Find any authentication components // and add the to the final configuration of the request. var authenticationComponents = attributes .Where(a => a as IAuthenticationComponent != null) .Select(a => a as IAuthenticationComponent) .ToList(); if (authenticationComponents.Count > 0) { context.Configuration.AuthenticationProviders = authenticationComponents; } // Find any authorization components // and add the to the final configuration of the request. var authorizationComponents = attributes .Where(a => a as IAuthorizationComponent != null) .Select(a => a as IAuthorizationComponent) .ToList(); if (authorizationComponents.Count > 0) { context.Configuration.AuthorizationProviders = authorizationComponents; } } if (context.Configuration?.RequestValidation?.MaxRequestLength != null && context.ConfigureMaxRequestLength != null) { try { context.ConfigureMaxRequestLength(context.Configuration.RequestValidation.MaxRequestLength.Value); } catch { } } return(true); } return(false); }
/// <summary>Gets the route information.</summary> /// <param name="context">The context.</param> /// <param name="resolver">The resolver.</param> /// <param name="routes">The routes.</param> /// <param name="defaultRequestConfiguration">The default request configuration.</param> /// <returns></returns> private static async Task <ApiRoutingItem> GetRoutingItem(this ApiRequestContext context, IUriRouteResolver resolver, IApiRoutingTable routes, IDeepSleepRequestConfiguration defaultRequestConfiguration) { // ----------------------------------------------------------------- // We want to trick the routing engine to treat HEAD requests as GET // http://tools.ietf.org/html/rfc7231#section-4.3.2 // ----------------------------------------------------------------- ApiRoutingItem routeInfo; if (context.Request.Method.In(StringComparison.InvariantCultureIgnoreCase, "HEAD")) { routeInfo = await resolver.MatchRoute( routes, "HEAD", context.Request.Path).ConfigureAwait(false); if (routeInfo == null) { routeInfo = await resolver.MatchRoute( routes, "GET", context.Request.Path).ConfigureAwait(false); if (routeInfo != null) { var enableHeadForGetRequests = routeInfo.Configuration?.EnableHeadForGetRequests ?? defaultRequestConfiguration?.EnableHeadForGetRequests ?? ApiRequestContext.GetDefaultRequestConfiguration().EnableHeadForGetRequests ?? true; if (!enableHeadForGetRequests) { routeInfo = null; } } } } else if (context.Request.IsCorsPreflightRequest()) { if (context.Request.CrossOriginRequest.AccessControlRequestMethod.In(StringComparison.InvariantCultureIgnoreCase, "HEAD")) { routeInfo = await resolver.MatchRoute( routes, context.Request.CrossOriginRequest.AccessControlRequestMethod, context.Request.Path).ConfigureAwait(false); if (routeInfo == null) { routeInfo = await resolver.MatchRoute( routes, "GET", context.Request.Path).ConfigureAwait(false); if (routeInfo != null) { var enableHeadForGetRequests = routeInfo.Configuration?.EnableHeadForGetRequests ?? defaultRequestConfiguration?.EnableHeadForGetRequests ?? ApiRequestContext.GetDefaultRequestConfiguration().EnableHeadForGetRequests ?? true; if (!enableHeadForGetRequests) { routeInfo = null; } } } } else { routeInfo = await resolver.MatchRoute( routes, context.Request.CrossOriginRequest.AccessControlRequestMethod, context.Request.Path).ConfigureAwait(false); } } else { routeInfo = await resolver.MatchRoute( routes, context.Request.Method, context.Request.Path).ConfigureAwait(false); } return(routeInfo); }
/// <summary>Processes the HTTP request cross origin resource sharing preflight.</summary> /// <param name="context">The context.</param> /// <param name="routes">The routes.</param> /// <param name="resolver">The resolver.</param> /// <param name="defaultRequestConfiguration">The default request configuration.</param> /// <returns></returns> internal static async Task <bool> ProcessHttpRequestCrossOriginResourceSharingPreflight( this ApiRequestContext context, IApiRoutingTable routes, IUriRouteResolver resolver, IDeepSleepRequestConfiguration defaultRequestConfiguration) { if (!context.RequestAborted.IsCancellationRequested) { if (context.Request?.IsCorsPreflightRequest() ?? false) { var methods = (context.Routing?.Template?.Locations ?? new List <ApiEndpointLocation>()) .Where(r => !string.IsNullOrWhiteSpace(r.HttpMethod)) .Select(r => r.HttpMethod.ToUpper()) .Distinct() .ToList(); // Need to include the auto-enabled HEAD method if configured on the get endpoint (if available) var hasGet = methods.FirstOrDefault(m => m.Equals("GET", StringComparison.InvariantCultureIgnoreCase)) != null; if (hasGet) { var hasHead = methods.FirstOrDefault(m => m.Equals("HEAD", StringComparison.InvariantCultureIgnoreCase)) != null; if (!hasHead) { if (resolver != null) { var getMatch = await resolver.MatchRoute( routes, "GET", context.Request.Path).ConfigureAwait(false); if (getMatch != null) { var enableHeadForGetRequests = getMatch.Configuration?.EnableHeadForGetRequests ?? defaultRequestConfiguration?.EnableHeadForGetRequests ?? ApiRequestContext.GetDefaultRequestConfiguration().EnableHeadForGetRequests ?? true; if (enableHeadForGetRequests) { methods.Add("HEAD"); } } } } } context.Response.StatusCode = 200; context.Response.AddHeader("Access-Control-Allow-Methods", string.Join(", ", methods).Trim()); if (!string.IsNullOrWhiteSpace(context.Request?.CrossOriginRequest?.AccessControlRequestHeaders)) { var allowHeaders = (context.Configuration?.CrossOriginConfig?.AllowedHeaders ?? new string[] { }) .Distinct() .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => i.Trim()) .ToList(); if (allowHeaders.Count > 0 && allowHeaders.Contains("*")) { context.Response.AddHeader("Access-Control-Allow-Headers", context.Request.CrossOriginRequest.AccessControlRequestHeaders); } else { context.Response.AddHeader("Access-Control-Allow-Headers", string.Join(", ", allowHeaders)); } } if (context.Configuration?.CrossOriginConfig?.MaxAgeSeconds.HasValue ?? false) { context.Response.AddHeader("Access-Control-Max-Age", $"{context.Configuration.CrossOriginConfig.MaxAgeSeconds.Value}"); } return(false); } return(true); } return(false); }