1 module hunt.http.routing.impl.RouterManagerImpl; 2 3 import hunt.http.routing.impl.ContentTypePreciseMatcher; 4 import hunt.http.routing.impl.ContentTypePatternMatcher; 5 import hunt.http.routing.impl.AcceptHeaderMatcher; 6 import hunt.http.routing.impl.HttpMethodMatcher; 7 import hunt.http.routing.impl.PatternPathMatcher; 8 import hunt.http.routing.impl.PrecisePathMatcher; 9 import hunt.http.routing.impl.ParameterPathMatcher; 10 import hunt.http.routing.impl.RegexPathMatcher; 11 import hunt.http.routing.impl.RouterImpl; 12 import hunt.http.routing.impl.RoutingContextImpl; 13 import hunt.http.routing.handler.ErrorResponseHandler; 14 15 import hunt.http.HttpHeader; 16 import hunt.http.HttpMethod; 17 import hunt.http.HttpRequest; 18 import hunt.http.HttpStatus; 19 20 import hunt.http.server.HttpServerContext; 21 import hunt.http.routing.Matcher; 22 import hunt.http.routing.Router; 23 import hunt.http.routing.RouterManager; 24 import hunt.http.routing.RoutingContext; 25 26 import hunt.collection; 27 import hunt.Exceptions; 28 import hunt.logging; 29 30 /** 31 * 32 */ 33 class RouterManagerImpl : RouterManager { 34 35 private int idGenerator; 36 private Map!(Matcher.MatchType, List!(Matcher)) matcherMap; 37 private Matcher precisePathMather; 38 private Matcher patternPathMatcher; 39 private Matcher regexPathMatcher; 40 private Matcher parameterPathMatcher; 41 private Matcher httpMethodMatcher; 42 // private Matcher contentTypePreciseMatcher; 43 // private Matcher contentTypePatternMatcher; 44 // private Matcher acceptHeaderMatcher; 45 46 private Router _code405Router; 47 48 this() { 49 _code405Router = handleStatus405(); 50 matcherMap = new HashMap!(Matcher.MatchType, List!(Matcher))(); 51 precisePathMather = new PrecisePathMatcher(); 52 patternPathMatcher = new PatternPathMatcher(); 53 parameterPathMatcher = new ParameterPathMatcher(); 54 regexPathMatcher = new RegexPathMatcher(); 55 ArrayList!Matcher al = new ArrayList!Matcher([precisePathMather, patternPathMatcher, 56 parameterPathMatcher, regexPathMatcher]); 57 matcherMap.put(Matcher.MatchType.PATH, al); 58 59 httpMethodMatcher = new HttpMethodMatcher(); 60 matcherMap.put(Matcher.MatchType.METHOD, Collections.singletonList!(Matcher)(httpMethodMatcher)); 61 62 // contentTypePreciseMatcher = new ContentTypePreciseMatcher(); 63 // contentTypePatternMatcher = new ContentTypePatternMatcher(); 64 // al = new ArrayList!Matcher([contentTypePreciseMatcher, contentTypePatternMatcher]); 65 // matcherMap.put(Matcher.MatchType.CONTENT_TYPE, al); 66 67 // acceptHeaderMatcher = new AcceptHeaderMatcher(); 68 // matcherMap.put(Matcher.MatchType.ACCEPT, Collections.singletonList!(Matcher)(acceptHeaderMatcher)); 69 } 70 71 Matcher getHttpMethodMatcher() { 72 return httpMethodMatcher; 73 } 74 75 Matcher getPrecisePathMather() { 76 return precisePathMather; 77 } 78 79 Matcher getPatternPathMatcher() { 80 return patternPathMatcher; 81 } 82 83 Matcher getRegexPathMatcher() { 84 return regexPathMatcher; 85 } 86 87 Matcher getParameterPathMatcher() { 88 return parameterPathMatcher; 89 } 90 91 // Matcher getContentTypePreciseMatcher() { 92 // return contentTypePreciseMatcher; 93 // } 94 95 // Matcher getContentTypePatternMatcher() { 96 // return contentTypePatternMatcher; 97 // } 98 99 // Matcher getAcceptHeaderMatcher() { 100 // return acceptHeaderMatcher; 101 // } 102 103 override NavigableSet!RouterMatchResult findRouter(string method, 104 string path, string contentType, string accept) { 105 106 // warningf("method: %s, path: %s, accept: %s", method, path, accept); 107 108 Map!(Router, Set!(Matcher.MatchType)) routerMatchTypes = new HashMap!(Router, Set!(Matcher.MatchType))(); 109 Map!(Router, Map!(string, string)) routerParameters = new HashMap!(Router, Map!(string, string))(); 110 111 findRouter(method, Matcher.MatchType.METHOD, routerMatchTypes, routerParameters); 112 findRouter(path, Matcher.MatchType.PATH, routerMatchTypes, routerParameters); 113 // findRouter(contentType, Matcher.MatchType.CONTENT_TYPE, routerMatchTypes, routerParameters); 114 // findRouter(accept, Matcher.MatchType.ACCEPT, routerMatchTypes, routerParameters); 115 116 NavigableSet!(RouterMatchResult) ret = new TreeSet!(RouterMatchResult)(); 117 foreach(Router key, Set!(Matcher.MatchType) value; routerMatchTypes) { 118 // tracef("checking route id: %d", key.getId()); 119 if(!key.isEnable()) continue; 120 121 // infof("route: %s", (cast(Object)key).toString()); 122 // tracef("target MatchTypes: %s", value); 123 124 Set!(Matcher.MatchType) matchTypes = key.getMatchTypes(); 125 if(matchTypes == value) { 126 ret.add(new RouterMatchResult(key, routerParameters.get(key), value)); 127 // TODO: Tasks pending completion -@zhangxueping at 2020-08-03T16:50:45+08:00 128 // 129 // } else if(matchTypes.contains(Matcher.MatchType.METHOD) && matchTypes.contains(Matcher.MatchType.PATH)) { 130 // // 405 Method Not Allowed 131 // ret.add(new RouterMatchResult(_code405Router, null, value)); 132 } 133 } 134 return ret; 135 } 136 137 private void findRouter(string value, Matcher.MatchType matchType, 138 Map!(Router, Set!(Matcher.MatchType)) routerMatchTypes, 139 Map!(Router, Map!(string, string)) routerParameters) { 140 141 List!(Matcher) matchers = matcherMap.get(matchType); 142 int matchersSize = matchers.size(); 143 144 // FIXME: Needing refactor or cleanup -@zhangxueping at 2020-03-31T17:33:01+08:00 145 // https://forum.dlang.org/post/exknjzbuooofyulgeaen@forum.dlang.org 146 for(int i=0; i<matchersSize; i++) { 147 Matcher m = matchers.get(i); 148 149 // tracef("%d => %s", i, value); 150 151 // foreach(Matcher m; matchers) { 152 MatchResult mr = m.match(value); 153 if(mr is null) continue; 154 155 156 Set!(Router) routers = mr.getRouters(); 157 foreach(Router router; routers) { 158 159 // warningf("found router %d for %s", router.getId(), value); 160 161 routerMatchTypes.computeIfAbsent(router, k => new HashSet!(Matcher.MatchType)()) 162 .add(mr.getMatchType()); 163 Map!(Router, Map!(string, string)) parameters = mr.getParameters(); 164 165 if (parameters !is null && !parameters.isEmpty()) { 166 routerParameters.computeIfAbsent(router, k => new HashMap!(string, string)()) 167 .putAll(parameters.get(router)); 168 } 169 } 170 171 } 172 } 173 174 // 405 Method Not Allowed 175 private Router handleStatus405() { 176 import hunt.http.routing.handler.Error405ResponseHandler; 177 Router r = new RouterImpl(idGenerator + 10, this); 178 // FIXME: Needing refactor or cleanup -@zhangxueping at 2020-08-03T16:35:05+08:00 179 // 180 // r.handler(new Error405ResponseHandler()); 181 182 r.handler((context) { 183 renderErrorPage(context, HttpStatus.METHOD_NOT_ALLOWED_405, null); 184 }); 185 return r; 186 } 187 188 override Router register() { 189 return new RouterImpl(idGenerator++, this); 190 } 191 192 override Router register(int id) { 193 return new RouterImpl(id, this); 194 } 195 196 override void accept(HttpServerContext context) { 197 // TODO: Tasks pending completion -@zhangxueping at 2020-07-31T15:26:19+08:00 198 // Refactor this 199 HttpRequest request = context.httpRequest(); 200 NavigableSet!(RouterMatchResult) routers = findRouter( 201 request.getMethod(), 202 request.getURI().getPath(), 203 request.getFields().get(HttpHeader.CONTENT_TYPE), 204 request.getFields().get(HttpHeader.ACCEPT)); 205 RoutingContext routingContext = new RoutingContextImpl(context, routers); 206 routingContext.next(); 207 } 208 }