| | | 1 | | /******************************************************************************** |
| | | 2 | | * RequestPipeline.cs * |
| | | 3 | | * * |
| | | 4 | | * Author: Denes Solti * |
| | | 5 | | ********************************************************************************/ |
| | | 6 | | using System; |
| | | 7 | | using System.Net; |
| | | 8 | | using System.Net.Http; |
| | | 9 | | using System.Threading; |
| | | 10 | | using System.Threading.Tasks; |
| | | 11 | | |
| | | 12 | | namespace NanoRoute.Internals |
| | | 13 | | { |
| | | 14 | | using Properties; |
| | | 15 | | |
| | 2 | 16 | | internal sealed class RequestPipeline(RouteNode root, MatchingPrecedence matchingPrecedence, HttpRequestMessage requ |
| | | 17 | | { |
| | | 18 | | #region Private |
| | 2 | 19 | | private readonly RouteMatchCursor _matches = new |
| | 2 | 20 | | ( |
| | 2 | 21 | | root, |
| | 2 | 22 | | ParseVerb(request), |
| | 2 | 23 | | request.RequestUri, |
| | 2 | 24 | | services, |
| | 2 | 25 | | matchingPrecedence, |
| | 2 | 26 | | cancellation |
| | 2 | 27 | | ); |
| | | 28 | | |
| | | 29 | | private static HttpVerb ParseVerb(HttpRequestMessage request) |
| | 2 | 30 | | { |
| | 2 | 31 | | if (!Enum.TryParse(request.Method.Method, ignoreCase: true, out HttpVerb verb)) |
| | 1 | 32 | | throw new ArgumentException |
| | 1 | 33 | | ( |
| | 1 | 34 | | string.Format(Resources.Culture, Resources.ERR_INVALID_VERB, request.Method.Method), nameof(request) |
| | 1 | 35 | | ); |
| | | 36 | | |
| | 2 | 37 | | return verb; |
| | 2 | 38 | | } |
| | | 39 | | |
| | | 40 | | private Task<HttpResponseMessage> CallNextHandler() |
| | 2 | 41 | | { |
| | 2 | 42 | | ValueTask<bool> matched = _matches.MoveNextAsync(); |
| | | 43 | | |
| | 2 | 44 | | if (!matched.IsCompletedSuccessfully) |
| | 0 | 45 | | return CallNextHandlerAwaitedAsync(matched); |
| | | 46 | | |
| | 2 | 47 | | if (!matched.Result) |
| | 1 | 48 | | ThrowNotFound(); |
| | | 49 | | |
| | 2 | 50 | | return InvokeCurrentHandler(); |
| | 2 | 51 | | } |
| | | 52 | | |
| | | 53 | | private async Task<HttpResponseMessage> CallNextHandlerAwaitedAsync(ValueTask<bool> matched) |
| | | 54 | | { |
| | | 55 | | if (!await matched) |
| | | 56 | | ThrowNotFound(); |
| | | 57 | | |
| | | 58 | | return await InvokeCurrentHandler(); |
| | | 59 | | } |
| | | 60 | | |
| | | 61 | | private Task<HttpResponseMessage> InvokeCurrentHandler() |
| | 2 | 62 | | { |
| | 2 | 63 | | RouteMatch match = _matches.Current; |
| | | 64 | | |
| | 2 | 65 | | RouterEventSource.Info.Write("MatchingHandler", static (request, match) => new |
| | 2 | 66 | | { |
| | 2 | 67 | | RequestUri = request.RequestUri.OriginalString, |
| | 2 | 68 | | Verb = request.Method.Method, |
| | 2 | 69 | | Pattern = match.HandlerRegistration.Pattern, |
| | 2 | 70 | | ParameterCount = match.AttachedParameters.Count |
| | 2 | 71 | | }, request, match); |
| | | 72 | | |
| | 2 | 73 | | RequestContext requestContext = new() |
| | 2 | 74 | | { |
| | 2 | 75 | | Parameters = match.AttachedParameters, |
| | 2 | 76 | | Services = services, |
| | 2 | 77 | | Request = request, |
| | 2 | 78 | | Cancellation = cancellation |
| | 2 | 79 | | }; |
| | | 80 | | |
| | 2 | 81 | | return match.HandlerRegistration.Handler(requestContext, CallNextHandler); |
| | 2 | 82 | | } |
| | | 83 | | |
| | | 84 | | private void ThrowNotFound() |
| | 1 | 85 | | { |
| | 1 | 86 | | RouterEventSource.Info.Write("NoMatchingHandler", static request => new |
| | 1 | 87 | | { |
| | 1 | 88 | | RequestUri = request.RequestUri.OriginalString, |
| | 1 | 89 | | Verb = request.Method.Method |
| | 1 | 90 | | }, request); |
| | | 91 | | |
| | 1 | 92 | | HttpRequestException.Throw(HttpStatusCode.NotFound, Resources.ERR_NOT_FOUND); |
| | | 93 | | } |
| | | 94 | | #endregion |
| | | 95 | | |
| | 2 | 96 | | public ValueTask DisposeAsync() => _matches.DisposeAsync(); |
| | | 97 | | |
| | 2 | 98 | | public Task<HttpResponseMessage> RunAsync() => CallNextHandler(); |
| | | 99 | | } |
| | | 100 | | } |