1 /* 2 * Router - 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 archttp.Archttp; 18 19 import std.stdio; 20 21 import std.regex : regex, match, matchAll; 22 import std.array : replaceFirst; 23 import std.uri : decode; 24 25 class Router(RoutingHandler, MiddlewareHandler) 26 { 27 private 28 { 29 Route!(RoutingHandler, MiddlewareHandler)[string] _routes; 30 Route!(RoutingHandler, MiddlewareHandler)[string] _regexRoutes; 31 MiddlewareHandler[] _middlewareHandlers; 32 Archttp _app; 33 } 34 35 Router add(string path, HttpMethod method, RoutingHandler handler) 36 { 37 auto route = _routes.get(path, null); 38 39 if (route is null) 40 { 41 route = _regexRoutes.get(path, null); 42 } 43 44 if (route is null) 45 { 46 route = CreateRoute(path, method, handler); 47 48 if (route.regular) 49 { 50 _regexRoutes[path] = route; 51 } 52 else 53 { 54 _routes[path] = route; 55 } 56 } 57 else 58 { 59 route.bindMethod(method, handler); 60 } 61 62 return this; 63 } 64 65 Router get(string route, RoutingHandler handler) 66 { 67 add(route, HttpMethod.GET, handler); 68 return this; 69 } 70 71 Router post(string route, RoutingHandler handler) 72 { 73 add(route, HttpMethod.POST, handler); 74 return this; 75 } 76 77 Router put(string route, RoutingHandler handler) 78 { 79 add(route, HttpMethod.PUT, handler); 80 return this; 81 } 82 83 Router Delete(string route, RoutingHandler handler) 84 { 85 add(route, HttpMethod.DELETE, handler); 86 return this; 87 } 88 89 Router use(MiddlewareHandler handler) 90 { 91 _middlewareHandlers ~= handler; 92 return this; 93 } 94 95 void onMount(Archttp app) 96 { 97 _app = app; 98 } 99 100 MiddlewareHandler[] middlewareHandlers() 101 { 102 return _middlewareHandlers; 103 } 104 105 private Route!(RoutingHandler, MiddlewareHandler) CreateRoute(string path, HttpMethod method, RoutingHandler handler) 106 { 107 auto route = new Route!(RoutingHandler, MiddlewareHandler)(path, method, handler); 108 109 auto matches = path.matchAll(regex(`\{(\w+)(:([^\}]+))?\}`)); 110 if (matches) 111 { 112 string[uint] paramKeys; 113 int paramCount = 0; 114 string pattern = path; 115 string urlTemplate = path; 116 117 foreach (m; matches) 118 { 119 paramKeys[paramCount] = m[1]; 120 string reg = m[3].length ? m[3] : "\\w+"; 121 pattern = pattern.replaceFirst(m[0], "(" ~ reg ~ ")"); 122 urlTemplate = urlTemplate.replaceFirst(m[0], "{" ~ m[1] ~ "}"); 123 paramCount++; 124 } 125 126 route.pattern = pattern; 127 route.paramKeys = paramKeys; 128 route.regular = true; 129 route.urlTemplate = urlTemplate; 130 } 131 132 return route; 133 } 134 135 RoutingHandler match(string path, HttpMethod method, ref MiddlewareHandler[] middlewareHandlers, ref string[string] params) 136 { 137 auto route = _routes.get(path, null); 138 139 if (route is null) 140 { 141 foreach ( r ; _regexRoutes ) 142 { 143 auto matched = path.match(regex(r.pattern)); 144 145 if (matched) 146 { 147 route = r; 148 149 foreach ( i, key ; route.paramKeys ) 150 { 151 params[key] = decode(matched.captures[i + 1]); 152 } 153 } 154 } 155 } 156 157 if (route is null) 158 { 159 writeln(path, " is Not Found."); 160 return cast(RoutingHandler) null; 161 } 162 163 RoutingHandler handler; 164 handler = route.find(method); 165 166 if (handler is null) 167 { 168 writeln("Request: ", path, " method ", method, " is Not Allowed."); 169 return cast(RoutingHandler) null; 170 } 171 172 middlewareHandlers = route.middlewareHandlers(); 173 174 return handler; 175 } 176 }