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 }