1 module hunt.http.server.HttpServer;
2 
3 import hunt.http.server.ClientAuth;
4 import hunt.http.server.GlobalSettings;
5 import hunt.http.server.Http1ServerDecoder;
6 import hunt.http.server.Http2ServerDecoder;
7 import hunt.http.server.Http2ServerRequestHandler;
8 import hunt.http.server.HttpRequestOptions;
9 import hunt.http.server.HttpServerConnection;
10 import hunt.http.server.HttpServerContext;
11 import hunt.http.server.HttpServerHandler;
12 import hunt.http.server.HttpServerOptions;
13 import hunt.http.server.HttpServerRequest;
14 import hunt.http.server.HttpServerResponse;
15 import hunt.http.server.ServerHttpHandler;
16 import hunt.http.server.ServerSessionListener;
17 import hunt.http.server.WebSocketHandler;
18 
19 import hunt.http.codec.CommonDecoder;
20 import hunt.http.codec.CommonEncoder;
21 import hunt.http.codec.websocket.decode.WebSocketDecoder;
22 import hunt.http.codec.websocket.frame;
23 
24 import hunt.http.routing;
25 import hunt.http.HttpConnection;
26 import hunt.http.HttpHeader;
27 import hunt.http.HttpMethod;
28 import hunt.http.HttpOutputStream;
29 import hunt.http.HttpRequest;
30 import hunt.http.HttpResponse;
31 import hunt.http.HttpStatus;
32 import hunt.http.WebSocketConnection;
33 import hunt.http.WebSocketPolicy;
34 
35 import hunt.io.BufferUtils;
36 import hunt.io.ByteBuffer;
37 import hunt.event.EventLoop;
38 import hunt.Exceptions;
39 import hunt.Functions;
40 import hunt.logging;
41 import hunt.net;
42 import hunt.util.DateTime;
43 import hunt.util.AbstractLifecycle;
44 
45 import core.time;
46 
47 import std.array;
48 import std.algorithm;
49 import std.file;
50 import std.path;
51 import std.string;
52 
53 
54 version(WITH_HUNT_TRACE) {
55     import hunt.net.util.HttpURI;
56     import hunt.trace.Constrants;
57     import hunt.trace.Endpoint;
58     import hunt.trace.HttpSender;
59     import hunt.trace.Span;
60     import hunt.trace.Tracer;
61     import hunt.trace.TracingOptions;
62 
63     import std.conv;
64     import std.format;
65 }
66         
67 alias BadRequestHandler = ActionN!(HttpServerContext); 
68 alias ServerErrorHandler = ActionN!(HttpServer, Exception);
69 
70 /**
71  * 
72  */
73 class HttpServer : AbstractLifecycle {
74 
75     private NetServer _server;
76     private NetServerOptions _serverOptions;
77     private HttpServerOptions _httpOptions;
78     private string host;
79     private int port;
80 
81     private Action _openSucceededHandler;
82     private ActionN!(Throwable) _openFailedHandler;
83     private ServerErrorHandler _errorHandler;
84 
85     this(HttpServerOptions httpOptions,
86             ServerHttpHandler serverHttpHandler, WebSocketHandler webSocketHandler) {
87         this(httpOptions, new Http2ServerRequestHandler(serverHttpHandler),
88                 serverHttpHandler, webSocketHandler);
89     }
90 
91     this(string host, ushort port, HttpServerOptions httpOptions,
92             ServerHttpHandler serverHttpHandler) {
93         httpOptions.setHost(host);
94         httpOptions.setPort(port);
95         this(httpOptions, new Http2ServerRequestHandler(serverHttpHandler),
96                 serverHttpHandler, new WebSocketHandlerAdapter());
97     }
98 
99     this(string host, ushort port, HttpServerOptions httpOptions,
100             ServerHttpHandler serverHttpHandler, WebSocketHandler webSocketHandler) {
101         if (host is null)
102             throw new IllegalArgumentException("the http server host is empty");
103         httpOptions.setHost(host);
104         httpOptions.setPort(port);
105         this(httpOptions, new Http2ServerRequestHandler(serverHttpHandler),
106                 serverHttpHandler, webSocketHandler);
107     }
108 
109     this(HttpServerOptions options, ServerSessionListener listener,
110             ServerHttpHandler serverHttpHandler, WebSocketHandler webSocketHandler) {
111         if (options is null)
112             throw new IllegalArgumentException("the http configuration is null");
113 
114         this.host = options.getHost();
115         this.port = options.getPort();
116         _serverOptions = options.getTcpConfiguration();
117         if(_serverOptions is null ) {
118             _serverOptions = new NetServerOptions();
119             options.setTcpConfiguration(_serverOptions);
120         }
121         this._httpOptions = options;
122         
123         // httpServerHandler = new HttpServerHandler(options, listener,
124         //         serverHttpHandler, webSocketHandler);
125 
126         if (options.isSecureConnectionEnabled()) {
127             version(WITH_HUNT_SECURITY) {
128                 import hunt.net.secure.SecureUtils;
129                 KeyCertOptions certOptions = options.getKeyCertOptions();
130                 if(certOptions is null) {
131                     warningf("No certificate files found. Using the defaults.");
132                 } else {
133                     SecureUtils.setServerCertificate(certOptions);
134                 }
135             } else {
136                 assert(false, "Please, add subConfigurations for hunt-http with TLS in dub.json.");
137             }
138         }
139 
140         _server = NetUtil.createNetServer!(ThreadMode.Single)(_serverOptions);
141 
142         // set codec
143         _server.setCodec(new class Codec {
144             private CommonEncoder encoder;
145             private CommonDecoder decoder;
146 
147             this() {
148                 encoder = new CommonEncoder();
149 
150                 Http1ServerDecoder httpServerDecoder = new Http1ServerDecoder(
151                             new WebSocketDecoder(),
152                             new Http2ServerDecoder());
153                 decoder = new CommonDecoder(httpServerDecoder);
154             }
155 
156             Encoder getEncoder() {
157                 return encoder;
158             }
159 
160             Decoder getDecoder() {
161                 return decoder;
162             }
163         });
164 
165         // set handler
166         HttpServerHandler serverHandler = new HttpServerHandler(options, listener, serverHttpHandler, webSocketHandler);
167 
168         serverHandler.onOpened((Connection conn) {
169             version(HUNT_HTTP_DEBUG) {
170                 infof("A new http connection %d opened: %s", conn.getId, typeid(conn));
171             }
172             version (HUNT_DEBUG) {
173                 if (options.isSecureConnectionEnabled())
174                     infof("Opend a secured http connection: %s", conn.getRemoteAddress());
175                 else
176                     infof("Opend a http connection: %s", conn.getRemoteAddress());
177             }
178 
179             // TODO: Tasks pending completion -@zhangxueping at 2020-05-18T16:27:44+08:00
180             // Pass the connection to _openSucceededHandler();
181             if(_openSucceededHandler !is null) 
182                 _openSucceededHandler();
183         });
184 
185         serverHandler.onOpenFailed((int id, Throwable ex) {
186             version(HUNT_HTTP_DEBUG) warning(ex.msg);
187             if(_openFailedHandler !is null) 
188                 _openFailedHandler(ex);
189            stop();
190         });
191 
192         _server.setHandler(serverHandler);
193         // _server.setHandler(new HttpServerHandler(options, listener, serverHttpHandler, webSocketHandler));
194 
195         // For test
196         // string responseString = `HTTP/1.1 000 
197         // Server: Hunt-HTTP/1.0
198         // Date: Tue, 11 Dec 2018 08:17:36 GMT
199         // Content-Type: text/plain
200         // Content-Length: 13
201         // Connection: keep-alive
202 
203         // Hello, World!`;
204     }
205 
206 
207     HttpServerOptions getHttpOptions() {
208         return _httpOptions;
209     }
210 
211     string getHost() {
212         return host;
213     }
214 
215     int getPort() {
216         return port;
217     }
218 
219     bool isOpen() {
220         return _server.isOpen();
221     }
222 
223     bool isTLS() {
224         return _httpOptions.isSecureConnectionEnabled();
225     }
226 
227     NetServer netServer() {
228         return _server;
229     }
230 
231     override protected void initialize() {
232         checkWorkingDirectory();
233         _server.listen(host, port);
234     }
235 
236     override protected void destroy() {
237 
238         if (_server !is null && _server.isOpen()) {
239             version(HUNT_DEBUG) warning("stopping the HttpServer...");
240             _server.close();
241         }
242 
243         // version(HUNT_DEBUG) warning("stopping the EventLoop...");
244         // NetUtil.stopEventLoop();
245     }
246 
247     private void checkWorkingDirectory() {
248         // FIXME: Needing refactor or cleanup -@zhangxueping at 2019-10-18T10:52:02+08:00
249         // make sure more directories existed.
250         HttpRequestOptions requestOptions = _httpOptions.requestOptions();
251         string path = requestOptions.getTempFileAbsolutePath();
252         
253         if (!path.exists())
254             path.mkdirRecurse();
255     }
256 
257     /* ----------------------------- connect events ----------------------------- */
258 
259     HttpServer onOpened(Action handler) {
260         _openSucceededHandler = handler;
261         return this;
262     }
263 
264     HttpServer onOpenFailed(ActionN!(Throwable) handler) {
265         _openFailedHandler = handler;
266         return this;
267     }
268 
269     HttpServer onError(ServerErrorHandler handler) {
270         _errorHandler = handler;
271         return this;
272     }
273 
274 
275     /* -------------------------------------------------------------------------- */
276     /*                                   Builder                                  */
277     /* -------------------------------------------------------------------------- */
278 
279     /**
280      * @return A builder that can be used to create an Undertow server instance
281      */
282     static Builder builder() {
283         return new Builder();
284     }
285 
286     static Builder builder(HttpServerOptions serverOptions) {
287         return new Builder(serverOptions);
288     }
289 
290     /**
291      * 
292      */
293     static final class Builder {
294         
295         enum PostMethod = HttpMethod.POST.asString();
296         enum GetMethod = HttpMethod.GET.asString();
297         enum PutMethod = HttpMethod.PUT.asString();
298         enum DeleteMethod = HttpMethod.DELETE.asString();
299 
300         private HttpServerOptions _httpOptions;
301         private RouterManager _routerManager;               // default route manager
302         private RouterManager[string] _hostManagerGroup; // route manager group for host
303         private RouterManager[string] _pathManagerGroup; // route manager group for path
304         private Router _currentRouter;
305         private WebSocketMessageHandler[string] _webSocketHandlers;
306         // private bool _canCopyBuffer = false;     
307         private Duration _resourceCacheTime = -1.seconds;  
308         
309         // SSL/TLS settings
310         private bool _isCertificateAuth = false;
311         private string _tlsCaFile;
312         private string _tlsCaPassworld;
313         private string _tlsCertificate;
314         private string _tlsPrivateKey;
315         private string _tlsCertPassword;
316         private string _tlsKeyPassword;
317 
318 
319         this() {
320             this(new HttpServerOptions());
321         }
322 
323         this(HttpServerOptions options) {
324             _httpOptions = options;
325             _routerManager = RouterManager.create();
326             
327             // set the server options as a global variable
328             GlobalSettings.httpServerOptions = _httpOptions;
329         }
330 
331         Builder setListener(ushort port) {
332             _httpOptions.setPort(port);
333             return this;
334         }
335 
336         Builder setListener(ushort port, string host) {
337             _httpOptions.setPort(port);
338             _httpOptions.setHost(host);
339             return this;
340         }
341 
342         Builder ioThreadSize(uint value) {
343             _httpOptions.getTcpConfiguration().ioThreadSize = value;
344             return this;
345         }
346 
347         Builder workerThreadSize(uint value) {
348             _httpOptions.getTcpConfiguration().workerThreadSize = value;
349             return this;
350         }
351 
352         Builder canUpgrade(bool value) {
353             _httpOptions.canUpgrade = value;
354             return this;
355         }
356 
357         Builder resourceCacheTime(Duration value) {
358             _resourceCacheTime = value;
359             return this;
360         }
361 
362         // Certificate Authority (CA) certificate
363         Builder setCaCert(string caFile, string caPassword) {
364             _tlsCaFile = caFile;
365             _tlsCaPassworld = caPassword;
366             return this;
367         }
368 
369         Builder setTLS(string certificate, string privateKey, string certPassword="", string keyPassword="") {
370             _isCertificateAuth = true;
371             _tlsCertificate = certificate;
372             _tlsPrivateKey = privateKey;
373             _tlsCertPassword = certPassword;
374             _tlsKeyPassword = keyPassword;
375             return this;
376         }
377 
378         Builder requiresClientAuth() {
379             _httpOptions.setClientAuth(ClientAuth.REQUIRED);
380             return this;
381         }
382 
383         Builder setHandler(RoutingHandler handler) {
384             addRoute(["*"], null, handler);
385             return this;
386         }
387 
388         /**
389          * register a new router
390          */
391         // Router newRouter() {
392         //     return _routerManager.register();
393         // }
394 
395         Builder addRoute(string path, RoutingHandler handler) {
396             return addRoute([path], cast(string[])null, handler);
397         }
398 
399         Builder addRoute(string path, HttpMethod method, RoutingHandler handler) {
400             return addRoute([path], [method.toString()], handler);
401         }
402         
403         Builder addRoute(string path, HttpMethod[] methods, RoutingHandler handler) {
404             string[] ms = map!((m) => m.asString())(methods).array;
405             return addRoute([path], ms, handler);
406         }
407 
408         // Builder addRoute(string path, string[] methods, RoutingHandler handler) {
409         //     return addRoute([path], methods, handler);
410         // }
411 
412         // Builder addRoute(string[] paths, HttpMethod[] methods, RoutingHandler handler) {
413         //     string[] ms = map!((m) => m.asString())(methods).array;
414         //     return addRoute(paths, ms, handler);
415         // }
416 
417         Builder addRoute(string[] paths, string[] methods, RoutingHandler handler, 
418                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host, 
419                 string attributeName = null, Object attributeObj = null) {
420             if(groupName.empty) {
421                 _currentRouter = _routerManager.register();
422             } else {
423                 _currentRouter = getGroupRouterManager(groupName, groupType).register();
424             }
425 
426             version(HUNT_HTTP_DEBUG) { 
427                 tracef("routeid: %d, paths: %s, group: %s", _currentRouter.getId(), paths, groupName);
428             }
429 
430             _currentRouter.paths(paths);
431             foreach(string m; methods) {
432                 _currentRouter.method(m);
433             }
434             _currentRouter.handler( (RoutingContext ctx) { 
435                 if(!attributeName.empty() && attributeObj !is null) {
436                     ctx.setAttribute(attributeName, attributeObj);
437                 }
438                 handlerWrap(handler, ctx); 
439             });
440             return this;
441         }
442 
443         Builder addRoute(string[] paths, string[] methods, RouteHandler handler, 
444                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host, 
445                 string attributeName = null, Object attributeObj = null) {
446             if(groupName.empty) {
447                 _currentRouter = _routerManager.register();
448             } else {
449                 _currentRouter = getGroupRouterManager(groupName, groupType).register();
450             }
451             version(HUNT_HTTP_DEBUG) tracef("routeid: %d, paths: %s", _currentRouter.getId(), paths);
452             _currentRouter.paths(paths);
453             foreach(string m; methods) {
454                 _currentRouter.method(m);
455             }
456             _currentRouter.handler( (RoutingContext ctx) { 
457                 if(!attributeName.empty() && attributeObj !is null) {
458                     ctx.setAttribute(attributeName, attributeObj);
459                 }
460                 handlerWrap(handler, ctx); 
461             });
462             return this;
463         }
464         
465         // Builder addRegexRoute(string regex, RoutingHandler handler) {
466         //     return addRegexRoute(regex, cast(string[])null, handler);
467         // }
468 
469         // Builder addRegexRoute(string regex, HttpMethod[] methods, RoutingHandler handler) {
470         //     string[] ms = map!((m) => m.asString())(methods).array;
471         //     return addRegexRoute(regex, ms, handler);
472         // }
473 
474         Builder addRegexRoute(string regex, string[] methods, RoutingHandler handler, 
475                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host) {
476             if(groupName.empty) {
477                 _currentRouter = _routerManager.register();
478             } else {
479                 _currentRouter = getGroupRouterManager(groupName, groupType).register();
480             }
481             version(HUNT_HTTP_DEBUG) tracef("routeid: %d, paths: %s", _currentRouter.getId(), regex);
482             _currentRouter.pathRegex(regex);
483             foreach(string m; methods) {
484                 _currentRouter.method(m);
485             }
486             _currentRouter.handler( (RoutingContext ctx) { handlerWrap(handler, ctx); });
487             return this;
488         }
489 
490         private RouterManager getGroupRouterManager(string groupName, RouteGroupType groupType) {
491             RouterManager manager;
492             if(groupType == RouteGroupType.Host) {
493                 auto itemPtr = groupName in _hostManagerGroup;
494                 if(itemPtr is null) {
495                     manager = RouterManager.create();
496                     _hostManagerGroup[groupName] = manager;
497                 } else {
498                     manager = *itemPtr;
499                 }
500             } else {
501                 auto itemPtr = groupName in _pathManagerGroup;
502                 if(itemPtr is null) {
503                     manager = RouterManager.create();
504                     _pathManagerGroup[groupName] = manager;
505                 } else {
506                     manager = *itemPtr;
507                 }
508             }
509             
510             return manager;
511         }
512 
513         Builder onGet(string path, RoutingHandler handler, 
514                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host) {
515             return addRoute([path], [GetMethod], handler, groupName, groupType);
516         }
517 
518         Builder onPost(string path, RoutingHandler handler, 
519                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host) {
520             return addRoute([path], [PostMethod], handler, groupName, groupType);
521         }
522 
523         Builder onPut(string path, RoutingHandler handler, 
524                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host) {
525             return addRoute([path], [PutMethod], handler, groupName, groupType);
526         }
527 
528         Builder onDelete(string path, RoutingHandler handler, 
529                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host) {
530             return addRoute([path], [DeleteMethod], handler, groupName, groupType);
531         }
532 
533         Builder onRequest(string method, string path, RoutingHandler handler, 
534                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host) {
535             return addRoute([path], [method], handler, groupName, groupType);
536         }
537 
538         Builder setDefaultRequest(RoutingHandler handler) {
539             _currentRouter = _routerManager.register(DEFAULT_LAST_ROUTER_ID-1);
540             version(HUNT_HTTP_DEBUG) tracef("routeid: %d, paths: %s", _currentRouter.getId(), "*");
541             _currentRouter.path("*").handler( (RoutingContext ctx) { 
542                 ctx.setStatus(HttpStatus.NOT_FOUND_404);
543                 handlerWrap(handler, ctx); 
544             });
545             return this;
546         }
547 
548         alias addNotFoundRoute = setDefaultRequest;
549 
550         Builder resource(string path, string localPath, bool canList = true,
551                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host) {
552             DefaultResourceHandler handler = new DefaultResourceHandler(amendingPath(path), localPath);
553             handler.isListingEnabled = canList;
554             handler.cacheTime = _resourceCacheTime;
555             
556             return resource(path, handler, groupName, groupType);
557         }
558 
559         private static string amendingPath(string path) {
560             if(path.empty)  return "";
561             
562             string r;
563             if(path[$-1] != '/') {
564                 r = path ~ "/";
565             } else {
566                 r = path;
567             }
568 
569             return r;
570         }
571 
572         Builder resource(string path, AbstractResourceHandler handler, 
573                 string groupName=null, RouteGroupType groupType = RouteGroupType.Host) {
574             path = path.strip();
575             assert(path.length > 0, "The path can't be empty");
576 
577             if(path[0] != '/')
578                 path = "/" ~ path;
579             
580             string staticPath;
581             if(path[$-1] != '/') {
582                 staticPath = path;
583                 path ~= "/";
584             } else {
585                 staticPath = path[0..$-1];
586             }
587 
588             path ~= "*";
589             version(HUNT_HTTP_DEBUG) {
590                 tracef("path: %s, staticPath: %s, canList: %s", path, staticPath, handler.isListingEnabled());
591             }
592 
593             if(path == staticPath || staticPath.empty()) {
594                 return addRoute([path], cast(string[])null, handler, groupName, groupType);
595             } else {
596                 return addRoute([path, staticPath], cast(string[])null, handler, groupName, groupType);
597             }
598         }
599 
600         Builder websocket(string path, WebSocketMessageHandler handler) {
601             auto itemPtr = path in _webSocketHandlers;
602             if(itemPtr !is null)
603                 throw new Exception("Handler registered on path: " ~ path);
604             
605             // WebSocket path should be skipped
606             Router webSocketRouter = _routerManager.register().path(path);
607             version(HUNT_HTTP_DEBUG) tracef("routeid: %d, paths: %s", webSocketRouter.getId(), path);
608             _webSocketHandlers[path] = handler;
609             
610             version(HUNT_HTTP_DEBUG) {
611                 webSocketRouter.handler( (ctx) {  
612                     infof("Skip a router path for WebSocket: %s", path);
613                 });
614             }
615 
616             return this;
617         }
618 
619         Builder enableLocalSessionStore() {
620             LocalHttpSessionHandler sessionHandler = new LocalHttpSessionHandler(_httpOptions);
621             _currentRouter = _routerManager.register();
622             version(HUNT_HTTP_DEBUG) tracef("routeid: %d, paths: *", _currentRouter.getId());
623             _currentRouter.path("*").handler(sessionHandler);
624             return this;
625         }
626 
627         private void handlerWrap(RouteHandler handler, RoutingContext ctx) {
628             try {
629                 if(handler !is null) handler.handle(ctx);
630             } catch (Exception ex) {
631                 errorf("route handler exception: %s", ex.msg);
632                 version(HUNT_HTTP_DEBUG) error(ex);
633                 if(!ctx.isCommitted()) {
634                     HttpServerResponse res = ctx.getResponse();
635                     if(res !is null) {
636                         res.setStatus(HttpStatus.BAD_REQUEST_400);
637                     }
638                     ctx.end(ex.msg);
639                 }
640                 ctx.fail(ex);
641             }
642         }
643 
644         private void handlerWrap(RoutingHandler handler, RoutingContext ctx) {
645             try {
646                 if(handler !is null) handler(ctx);
647             } catch (Exception ex) {
648                 errorf("route handler exception: %s", ex.msg);
649                 version(HUNT_DEBUG) error(ex);
650                 if(!ctx.isCommitted()) {
651                     HttpServerResponse res = ctx.getResponse();
652                     if(res !is null) {
653                         res.setStatus(HttpStatus.BAD_REQUEST_400);
654                     }
655                     ctx.end(ex.msg);
656                 }
657                 ctx.fail(ex);
658             }
659         }
660 
661         Builder onError(BadRequestHandler handler) {
662             _badRequestHandler = handler;
663             return this;
664         }
665 
666         private void onError(HttpServerContext context) {
667             if(_badRequestHandler !is null) {
668                 _badRequestHandler(context);
669             }
670 
671             if(!context.isCommitted())
672                 context.end();
673         }
674 
675         private BadRequestHandler _badRequestHandler;
676 
677     
678 version(WITH_HUNT_TRACE) {
679         private string _localServiceName;
680         private bool _isB3HeaderRequired = true;
681         // private TracingOptions _tracingOptions;
682 
683         Builder localServiceName(string name) {
684             _localServiceName = name;
685             return this;
686         }
687 
688         Builder isB3HeaderRequired(bool flag) {
689             _isB3HeaderRequired = flag;
690             return this;
691         }
692 
693         private void initializeTracer(HttpRequest request, HttpConnection connection) {
694             if(!isTraceEnabled) return;
695 
696             Tracer tracer;
697             string reqPath = request.getURI().getPath();
698             string b3 = request.header("b3");
699             if(b3.empty()) {
700                 // if(_isB3HeaderRequired) return;
701                 version(HUNT_HTTP_DEBUG) {
702                     warningf("No B3 headers found for %s, use the defaults now.", reqPath);
703                 }
704                 tracer = new Tracer(reqPath, KindOfServer);
705             } else {
706                 version(HUNT_HTTP_DEBUG) {
707                     warningf("initializing tracer for %s, with %s", reqPath, b3);
708                 }
709 
710                 tracer = new Tracer(reqPath, KindOfServer, b3);
711             }
712 
713             Span span = tracer.root;
714             span.localEndpoint = _localEndpoint;
715 
716             import std.socket;
717 
718             // 
719             Address remote = connection.getRemoteAddress;
720             EndPoint remoteEndpoint = new EndPoint();
721             remoteEndpoint.ipv4 = remote.toAddrString();
722             remoteEndpoint.port = remote.toPortString().to!int;
723             span.remoteEndpoint = remoteEndpoint;
724             //
725 
726             span.start();
727             request.tracer = tracer;
728         }
729 
730         private EndPoint _localEndpoint;
731 
732         private void endTraceSpan(HttpRequest request, int status, string message = null) {
733             Tracer tracer = request.tracer;
734             if(tracer is null) {
735                 version(HUNT_TRACE_DEBUG) warning("no tracer found");
736                 return;
737             }
738 
739             HttpURI uri = request.getURI();
740             string[string] tags;
741             tags[HTTP_HOST] = uri.getHost();
742             tags[HTTP_URL] = uri.getPathQuery();
743             tags[HTTP_PATH] = uri.getPath();
744             tags[HTTP_REQUEST_SIZE] = request.getContentLength().to!string();
745             tags[HTTP_METHOD] = request.getMethod();
746 
747             Span span = tracer.root;
748             if(span !is null) {
749                 tags[HTTP_STATUS_CODE] = to!string(status);
750                 traceSpanAfter(span, tags, message);
751                 httpSender().sendSpans(span);
752             }
753         }    
754 }  
755         private ServerHttpHandler buildServerHttpHandler() {
756             ServerHttpHandlerAdapter adapter = new ServerHttpHandlerAdapter(_httpOptions);
757 
758             adapter.acceptHttpTunnelConnection((request, response, ot, connection) {
759                     version(HUNT_HTTP_DEBUG) info("acceptHttpTunnelConnection!!!");
760                     // if (tunnel !is null) {
761                     //     tunnel(r, connection);
762                     // }
763                     return true;
764                 })
765                 .headerComplete((request, response, ot, connection) {
766                     version(HUNT_HTTP_DEBUG) info("headerComplete!");
767                     HttpServerRequest serverRequest = cast(HttpServerRequest)request;
768                     version(WITH_HUNT_TRACE) {
769                         initializeTracer(serverRequest, connection);
770                     }
771 
772                     HttpServerContext context = new HttpServerContext(
773                         serverRequest, cast(HttpServerResponse)response, 
774                         ot, cast(HttpServerConnection)connection);
775 
776                     serverRequest.onHeaderComplete();
777                     dispatchRoute(context);
778                     
779                     // request.setAttachment(context);
780 
781                     return false;
782                 })
783                 .content((buffer, request, response, ot, connection) {
784                     version(HUNT_HTTP_DEBUG) tracef("content: %s", BufferUtils.toDetailString(buffer));
785 
786                     HttpServerRequest serverRequest = cast(HttpServerRequest)request;
787                     serverRequest.onContent(buffer);
788                     return false;
789                 })
790                 .contentComplete((request, response, ot, connection)  {
791                     version(HUNT_HTTP_DEBUG) info("contentComplete!");
792                     HttpServerRequest serverRequest = cast(HttpServerRequest)request;
793                     serverRequest.onContentComplete();
794                     // HttpServerContext context = cast(HttpServerContext) request.getAttachment();
795                     // context.onContentComplete();
796                     // if (r.contentComplete !is null) {
797                     //     r.contentComplete(r);
798                     // }
799                     return false;
800                 })
801                 .messageComplete((request, response, ot, connection)  {
802                     version(HUNT_HTTP_DEBUG) info("messageComplete!");
803                     HttpServerRequest serverRequest = cast(HttpServerRequest)request;
804                     serverRequest.onMessageComplete();
805                     // if (!r.getResponse().isAsynchronous()) {
806                     //     IOUtils.close(r.getResponse());
807                     // }
808                     version(HUNT_HTTP_DEBUG) info("end of a request on ", request.getURI().getPath());
809                     version(WITH_HUNT_TRACE) endTraceSpan(request, response.getStatus());
810                     return true;
811                 })
812                 .badMessage((status, reason, request, response, ot, connection)  {
813                     version(HUNT_DEBUG) warningf("badMessage: status=%d reason=%s", status, reason);
814 
815                     version(HUNT_HTTP_DEBUG) tracef("response is null: %s", response is null);
816                     if(response is null) {
817                         response = new HttpServerResponse(status, reason, null, -1);
818                     }
819                     
820                     HttpServerContext context = new HttpServerContext(
821                         cast(HttpServerRequest) request, 
822                         cast(HttpServerResponse)response, 
823                         ot, cast(HttpServerConnection)connection);
824 
825                     version(WITH_HUNT_TRACE) endTraceSpan(request, status, reason);
826                     onError(context);
827                 })
828                 .earlyEOF((request, response, ot, connection)  {
829                     version(HUNT_HTTP_DEBUG) info("earlyEOF!");
830                 });
831 
832             return adapter;
833         }
834 
835         private WebSocketHandler buildWebSocketHandler() {
836             WebSocketHandlerAdapter adapter = new WebSocketHandlerAdapter();
837 
838             adapter
839             .onAcceptUpgrade((HttpRequest request, HttpResponse response, 
840                     HttpOutputStream output, HttpConnection connection) {
841                 string path = request.getURI().getPath();
842                 WebSocketMessageHandler handler = _webSocketHandlers.get(path, null);
843                 if (handler is null) {
844                     response.setStatus(HttpStatus.BAD_REQUEST_400);
845                     try {
846                         output.write(cast(byte[])("No websocket handler for url: " ~ path));
847                     } catch (IOException e) {
848                         version(HUNT_DEBUG) errorf("Write http message exception", e.msg);
849                     }
850                     return false;
851                 } else {
852                     // return handler.acceptUpgrade(request, response, output, connection);
853                     return true;
854                 }
855             }) 
856             .onOpen((connection) {
857                 string path = connection.getPath();
858                 WebSocketMessageHandler handler = _webSocketHandlers.get(path, null);
859                 if(handler !is null)
860                     handler.onOpen(connection);
861             })
862             .onFrame((WebSocketFrame frame, WebSocketConnection connection) {
863                 string path = connection.getPath();
864                 WebSocketMessageHandler handler = _webSocketHandlers.get(path, null);
865                 if(handler is null) {
866                     return;
867                 }
868 
869                 switch (frame.getType()) {
870                     case WebSocketFrameType.TEXT:
871                         handler.onText(connection, (cast(DataFrame) frame).getPayloadAsUTF8());
872                         break;
873                         
874                     case WebSocketFrameType.BINARY:
875                         handler.onBinary(connection, frame.getPayload());
876                         break;
877                         
878                     case WebSocketFrameType.CLOSE:
879                         handler.onClosed(connection);
880                         break;
881 
882                     case WebSocketFrameType.PING:
883                         handler.onPing(connection);
884                         break;
885 
886                     case WebSocketFrameType.PONG:
887                         handler.onPong(connection);
888                         break;
889 
890                     case WebSocketFrameType.CONTINUATION:
891                         handler.onContinuation(connection, frame.getPayload());
892                         break;
893 
894                     default: break;
895                 }
896             })
897             .onError((Exception ex, WebSocketConnection connection) {
898                 string path = connection.getPath();
899                 WebSocketMessageHandler handler = _webSocketHandlers.get(path, null);
900                 if(handler !is null)
901                     handler.onError(connection, ex);
902             });
903 
904             return adapter;
905         }
906 
907         private void dispatchRoute(HttpServerContext context) {
908             bool isHandled = false;
909             if(_hostManagerGroup.length > 0) {
910                 HttpServerRequest request = context.httpRequest();
911                 string host = request.host();
912                 if(!host.empty()) {
913                     host = split(host, ":")[0];
914                     auto itemPtr = host in _hostManagerGroup;
915                     if(itemPtr !is null) {
916                         isHandled = true;
917                         itemPtr.accept(context);
918                         version(HUNT_HTTP_DEBUG) {
919                             tracef("host group, host: %s, path: %s", host, request.path());
920                         }
921                     }
922                 }
923             }
924             
925             if(_pathManagerGroup.length > 0) {
926                 HttpServerRequest request = context.httpRequest();
927                 string path = request.originalPath();
928                 // if(path.length > 1 && path[$-1] != '/') {
929                 //     path ~= "/";
930                 // }
931 
932                 string groupPath = split(path, "/")[1];
933 
934                 // warningf("full path: %s, group path: %s", path, groupPath);
935                 auto itemPtr = groupPath in _pathManagerGroup;
936                 if(itemPtr !is null) {
937                     isHandled = true;
938                     path = path[groupPath.length + 1 .. $]; // skip the group path
939                     version(HUNT_HTTP_DEBUG) {
940                         tracef("full path: %s, group path: %s, new path: %s", request.path(), groupPath, path);
941                     }
942 
943                     request.path = path; // Reset the rquest path
944                     itemPtr.accept(context);
945                     version(HUNT_HTTP_DEBUG_MORE) {
946                         tracef("path group, original path: %s, revised path: %s, group path: %s", 
947                             request.originalPath(), request.path(), groupPath);
948                     }
949                 }
950             } 
951 
952             if(!isHandled) {
953                 _routerManager.accept(context);
954             }
955         }
956 
957         /* ---------------------------- Options operation --------------------------- */
958 
959         Builder maxRequestSize(int size) {
960             _httpOptions.requestOptions.setMaxRequestSize(size);
961             return this;
962         }
963 
964         Builder maxFileSize(int size) {
965             _httpOptions.requestOptions.setMaxFileSize(size);
966             return this;
967         }
968         
969         HttpServer build() { 
970 
971             string basePath = dirName(thisExePath);
972 
973             if(!_tlsCertificate.empty()) {
974                 PemKeyCertOptions certOptions = new PemKeyCertOptions(buildPath(basePath, _tlsCertificate),
975                     buildPath(basePath, _tlsPrivateKey), _tlsCertPassword, _tlsKeyPassword);
976                 
977                 if(!_tlsCaFile.empty()) {
978                     certOptions.setCaFile(buildPath(basePath, _tlsCaFile));
979                     certOptions.setCaPassword(_tlsCaPassworld);
980                 }
981                 _httpOptions.setKeyCertOptions(certOptions);
982             }
983 
984             _httpOptions.setSecureConnectionEnabled(_isCertificateAuth);
985             version(WITH_HUNT_TRACE) {
986                 _localEndpoint = Span.buildLocalEndPoint(_localServiceName);
987             }
988 
989             return new HttpServer(_httpOptions, buildServerHttpHandler(), buildWebSocketHandler());
990         }
991     }
992 }