1 /* 2 * Archttp - A highly performant web framework written in D. 3 * 4 * Copyright (C) 2021-2022 Kerisy.com 5 * 6 * Website: https://www.kerisy.com 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module archttp.Router; 13 14 public import archttp.Route; 15 public import archttp.HttpMethod; 16 17 import std.stdio; 18 19 import std.regex : regex, match, matchAll; 20 import std.array : replaceFirst; 21 import std.uri : decode; 22 23 class Router(RoutingHandler) 24 { 25 private 26 { 27 Route!RoutingHandler[string] _routes; 28 Route!RoutingHandler[] _regexRoutes; 29 } 30 31 Router add(string path, HttpMethod method, RoutingHandler handler) 32 { 33 auto route = CreateRoute(path, method, handler); 34 35 if (route.regular) 36 { 37 _regexRoutes ~= route; 38 } 39 else 40 { 41 _routes[path] = route; 42 } 43 44 return this; 45 } 46 47 private Route!RoutingHandler CreateRoute(string path, HttpMethod method, RoutingHandler handler) 48 { 49 auto route = new Route!RoutingHandler(path, method, handler); 50 51 auto matches = path.matchAll(regex(`\{(\w+)(:([^\}]+))?\}`)); 52 if (matches) 53 { 54 string[uint] paramKeys; 55 int paramCount = 0; 56 string pattern = path; 57 string urlTemplate = path; 58 59 foreach (m; matches) 60 { 61 paramKeys[paramCount] = m[1]; 62 string reg = m[3].length ? m[3] : "\\w+"; 63 pattern = pattern.replaceFirst(m[0], "(" ~ reg ~ ")"); 64 urlTemplate = urlTemplate.replaceFirst(m[0], "{" ~ m[1] ~ "}"); 65 paramCount++; 66 } 67 68 route.pattern = pattern; 69 route.paramKeys = paramKeys; 70 route.regular = true; 71 route.urlTemplate = urlTemplate; 72 } 73 74 return route; 75 } 76 77 RoutingHandler match(string path, HttpMethod method, ref string[string] params) 78 { 79 auto route = _routes.get(path, null); 80 81 if (route is null) 82 { 83 foreach ( r ; _regexRoutes ) 84 { 85 auto matched = path.match(regex(r.pattern)); 86 87 if (matched) 88 { 89 route = r; 90 91 foreach ( i, key ; route.paramKeys ) 92 { 93 params[key] = decode(matched.captures[i + 1]); 94 } 95 } 96 } 97 } 98 99 if (route is null) 100 { 101 writeln(path, " is Not Found."); 102 return cast(RoutingHandler) null; 103 } 104 105 RoutingHandler handler; 106 handler = route.find(method); 107 108 if (handler is null) 109 { 110 writeln(method, " method is Not Allowed."); 111 return cast(RoutingHandler) null; 112 } 113 114 return handler; 115 } 116 }