1 module hunt.http.routing.impl.RouterImpl; 2 3 import hunt.http.routing.Router; 4 import hunt.http.routing.RoutingContext; 5 6 import hunt.http.HttpMethod; 7 import hunt.http.routing.Matcher; 8 import hunt.http.routing.Router; 9 10 import hunt.http.routing.impl.RouterManagerImpl; 11 12 import hunt.collection; 13 import hunt.logging; 14 import hunt.Exceptions; 15 import hunt.text; 16 import hunt.util.Comparator; 17 18 import std.algorithm; 19 import std.array; 20 import std.conv; 21 import std.path; 22 import std.string; 23 24 alias MatchType = Matcher.MatchType; 25 alias MatchResult = Matcher.MatchResult; 26 27 /** 28 * 29 */ 30 class RouterImpl : Router { 31 32 private int id; 33 private RouterManagerImpl routerManager; 34 private Set!(MatchType) matchTypes; 35 36 // private RouteHandler _handler; 37 private RoutingHandler _routingHandler; 38 private bool _isEnable = true; 39 private List!(string) urlList; 40 41 this(int id, RouterManagerImpl routerManager) { 42 this.id = id; 43 this.routerManager = routerManager; 44 matchTypes = new HashSet!(MatchType)(); 45 urlList = new ArrayList!(string)(); 46 } 47 48 override 49 Router path(string url) { 50 url = url.strip(); 51 checkPath(url); 52 53 if (url.length == 1) { 54 switch (url.charAt(0)) { 55 case '/': 56 routerManager.getPrecisePathMather().add(url, this); 57 break; 58 case '*': 59 routerManager.getPatternPathMatcher().add(url, this); 60 break; 61 default: 62 throw new IllegalArgumentException("the url: [" ~ url ~ "] format error"); 63 } 64 } else { 65 if (url.charAt(0) != '/') { 66 throw new IllegalArgumentException("the path must start with '/'"); 67 } 68 69 if (url.canFind("*")) { 70 routerManager.getPatternPathMatcher().add(url, this); 71 } else { 72 if (url[$ -1] != '/') { 73 url = url ~ "/"; 74 } 75 76 string[] paths = std.path.pathSplitter(url).array; // PathUtils.split(url); 77 if (isParameterPath(paths)) { 78 routerManager.getParameterPathMatcher().add(url, this); 79 } else { 80 routerManager.getPrecisePathMather().add(url, this); 81 } 82 } 83 } 84 urlList.add(url); 85 matchTypes.add(MatchType.PATH); 86 return this; 87 } 88 89 override 90 Router paths(string[] urlList) { 91 foreach(string u; urlList) { 92 this.path(u); 93 } 94 95 return this; 96 } 97 98 private void checkPath(string url) { 99 if (url.empty()) { 100 throw new IllegalArgumentException("the url is empty"); 101 } 102 103 if (urlList.contains(url)) { 104 throw new IllegalArgumentException("the url " ~ url ~ " exists"); 105 } 106 } 107 108 private bool isParameterPath(string[] paths) { 109 foreach (string p ; paths) { 110 version(HUNT_HTTP_DEBUG_MORE) trace("path: ", p); 111 if (p[0] == ':' || p[0] == '{') { 112 return true; 113 } 114 } 115 return false; 116 } 117 118 override 119 Router pathRegex(string regex) { 120 checkPath(regex); 121 regex = regex.strip(); 122 routerManager.getRegexPathMatcher().add(regex, this); 123 urlList.add(regex); 124 matchTypes.add(MatchType.PATH); 125 return this; 126 } 127 128 override 129 Router method(HttpMethod httpMethod) { 130 return method(httpMethod.asString()); 131 } 132 133 override 134 Router method(string method) { 135 routerManager.getHttpMethodMatcher().add(method, this); 136 matchTypes.add(MatchType.METHOD); 137 return this; 138 } 139 140 override 141 Router get(string url) { 142 return method(HttpMethod.GET).path(url); 143 } 144 145 override 146 Router post(string url) { 147 return method(HttpMethod.POST).path(url); 148 } 149 150 override 151 Router put(string url) { 152 return method(HttpMethod.PUT).path(url); 153 } 154 155 override 156 Router del(string url) { 157 return method(HttpMethod.DELETE).path(url); 158 } 159 160 override 161 Router consumes(string contentType) { 162 // if (!contentType.canFind("*")) { 163 // routerManager.getContentTypePreciseMatcher().add(contentType, this); 164 // } else { 165 // routerManager.getContentTypePatternMatcher().add(contentType, this); 166 // } 167 // matchTypes.add(MatchType.CONTENT_TYPE); 168 return this; 169 } 170 171 override 172 Router produces(string accept) { 173 // routerManager.getAcceptHeaderMatcher().add(accept, this); 174 // matchTypes.add(MatchType.ACCEPT); 175 return this; 176 } 177 178 Router handler(RouteHandler handler) { 179 if(handler !is null) { 180 this._routingHandler = (RoutingContext ctx) { 181 version(HUNT_HTTP_DEBUG) trace("Current route handler: ", typeid(cast(Object)handler)); 182 handler.handle(ctx); 183 }; 184 } else { 185 version(HUNT_HTTP_DEBUG) warning("The handler is null"); 186 } 187 return this; 188 } 189 190 Router handler(RoutingHandler h) { 191 assert(h !is null); 192 this._routingHandler = h; 193 return this; 194 } 195 196 void handle(RoutingContext context) { 197 if(_routingHandler !is null) { 198 _routingHandler(context); 199 } 200 } 201 202 override 203 Router enable() { 204 _isEnable = true; 205 return this; 206 } 207 208 override 209 Router disable() { 210 _isEnable = false; 211 return this; 212 } 213 214 override 215 int getId() { 216 return id; 217 } 218 219 override 220 bool isEnable() { 221 return _isEnable; 222 } 223 224 override 225 Set!(MatchType) getMatchTypes() { 226 return matchTypes; 227 } 228 229 // RouteHandler getHandler() { 230 // return _handler; 231 // } 232 233 int opCmp(Router o) { 234 return compare(id, o.getId()); 235 } 236 237 override int opCmp(Object o) { 238 Router r = cast(Router)o; 239 if(r is null) 240 throw new NullPointerException(); 241 return opCmp(r); 242 } 243 244 245 override 246 bool opEquals(Object o) { 247 if (this is o) return true; 248 RouterImpl router = cast(RouterImpl) o; 249 if(router is null) 250 return false; 251 return id == router.id; 252 } 253 254 override 255 size_t toHash() @trusted nothrow { 256 return hashOf(id); 257 } 258 259 override 260 string toString() { 261 return "Router {id=" ~ id.to!string() ~ 262 ", matchTypes=" ~ matchTypes.toString() ~ 263 ", url=" ~ urlList.toString() ~ "}"; 264 } 265 }