Qore HttpServer Module Reference  0.3.6
 All Classes Namespaces Functions Variables Groups Pages
HttpServer.qm.dox.h
1 // -*- mode: c++; indent-tabs-mode: nil -*-
2 // @file HttpServer.qm HTTP multi-threaded server module definition
3 
4 /* HttpServer.qm Copyright 2012 David Nichols
5 
6  Permission is hereby granted, free of charge, to any person obtaining a
7  copy of this software and associated documentation files (the "Software"),
8  to deal in the Software without restriction, including without limitation
9  the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  and/or sell copies of the Software, and to permit persons to whom the
11  Software is furnished to do so, subject to the following conditions:
12 
13  The above copyright notice and this permission notice shall be included in
14  all copies or substantial portions of the Software.
15 
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  DEALINGS IN THE SOFTWARE.
23 */
24 
25 // need mime definitions
26 
27 
28 /* Version History
29  * 2012-12-18 v0.3.6: David Nichols <david@qore.org>
30  + fixed a race condition in tracking active requests in dynamic HTTP handlers and removing dynamic HTTP handlers
31 
32  * 2012-09-20 v0.3.5: David Nichols <david@qore.org>
33  + fixed bugs in regexes in the HttpServer::addListeners() and HttpServer::addListenersWithHandler() methods
34  + updated module example code in summary
35  + added "listener-id" to request context hash
36 
37  * 2012-06-01 v0.3.4: David Nichols <david@qore.org>
38  + added public parse_uri_query() function
39 
40  * 2012-05-24 v0.3.3: David Nichols <david@qore.org>
41  + updated to a user module
42  + added support for custom redirects from user handlers
43  + use the Mime module for mime definitions
44 
45  * v0.3.2: David Nichols <david@qore.org>
46  + added support for listeners with specific handler lists
47  + added the ability to manage dynamic content handlers
48  + implemented content-encoding handling flags in AbstractHttpRequestHandler
49  + implemented support for "identity" encoding (if anyone ever sends it)
50 
51  * v0.3.1: David Nichols <david@qore.org>
52  + added ipv6 support in qore 0.8.2
53 
54  * v0.3.0: David Nichols <david@qore.org>
55  + added the ability to start and stop listeners on demand
56 
57  * v0.2.9: David Nichols <david@qore.org>
58  + updates for new SSL and timeout behavior with with Socket class with qore 0.8.1+
59  + set socket encoding to UTF-8 by default
60  + add "charset=utf-8" to Content-Type header if not already present
61  + add "text/html" to Content-Type header if no content-type is given by the handler
62  + fixed setting X.509 certificate and private key for HTTPS listeners
63  + require qore >= 0.8.1 for new Socket features
64 
65  * v0.2.8: David Nichols <david@qore.org>
66  + converted to hard typing for use with %require-types
67  + require qore >= 0.8.0 for new Socket features
68 
69  * v0.2.7: David Nichols <david@qore.org>
70  + set TCP_NODELAY on all sockets to ensure that clients get all data before closing the socket, especially in case of errors
71  + require qore >= 0.7.4 for new Socket features
72 
73  * v0.2.6: David Nichols <david@qore.org>
74  + minor fixes for SOAP support
75  + improved handler matching
76 
77  * v0.2.5: David Nichols <david@qore.org>
78  + minor fixes for SOAP support
79 
80  * v0.2.4: David Nichols <david@qore.org>
81  + improved Content-Type handling
82  + improved URL/path support
83 
84  * v0.2.3: David Nichols <david@qore.org>
85  + bzip2 content-encoding support
86 
87  * v0.2.2: David Nichols <david@qore.org>
88  + basic authentication
89 
90  * v0.2.1: David Nichols <david@qore.org>
91  + implementing logic to handle "deflate" and "gzip" content-encoding
92  + chunked content-encoding supported for POSTs
93  + Date: header always sent as per HTTP 1.1 spec
94 
95  * v0.2.0: David Nichols <david@qore.org>
96  + modular/multiple listener support added
97  + https support added
98 
99  note that this server suffers from the following limitations, among many others:
100  + not totally HTTP 1.1 compliant although claims to be
101 */
102 
165 
166 
172 namespace HttpServer {
174 
178  string get_exception_string(hash ex);
179 
180 
182 
188  hash parse_uri_query(string path);
189 
190 };
191 
192 // class containing handler info
193 class HttpServer::HandlerInfo {
194 
195 public:
196  public :
197  string name;
198  AbstractHttpRequestHandler obj;
199  string url;
200  *string content;
201  *list shdr;
202 
203 public:
204 
205  constructor(string name, AbstractHttpRequestHandler obj, string url, *string content, *softlist shdr);
206 
207 };
208 
209 // class to implement handler-handling (private)
210 class HttpServer::HttpHandlerList {
211 
212 public:
213  public :
214  hash handlers;
215 
216 public:
217 
218 
219 private:
220  static checkSpecialHeaders(reference sh);
221 public:
222 
223 
225  setHandler(string name, string url, *string content, AbstractHttpRequestHandler obj, *softlist special_headers);
226 
227 
228  // matches a handler to the request
229  *HandlerInfo findHandler(*hash url, hash hdr, bool final = False);
230 
231 
232  bool empty();
233 
234 
235  int size();
236 
237 };
238 
239 // class containing dynamic handler info
240 class HttpServer::DynamicHandlerInfo : public HttpServer::HandlerInfo {
241 
242 public:
243  public :
244  Counter counter();
245 
246 public:
247 
248  constructor(string name, AbstractHttpRequestHandler obj, string url, *string content, *softlist shdr);
249 
250 };
251 
252 // maintains the request count for dynamic handlers (private)
253 class HttpServer::DynamicHandlerHelper {
254 
255 public:
256 private:
257 
258 public:
259 
260  private :
261  Counter c;
262 
263 public:
264 
265  constructor(Counter c);
266 
267 
268  destructor();
269 
270 };
271 
272 // for dynamic handler-handling (private)
273 class HttpServer::DynamicHttpHandlerList : public HttpServer::HttpHandlerList {
274 
275 public:
276  private :
277  RWLock dhl();
278 
279 public:
280 
282  setHandler(string name, string url, *string content_type, AbstractHttpRequestHandler obj, *softlist special_headers);
283 
284 
286  removeHandler(string name);
287 
288 
289  *DynamicHandlerInfo findHandler(*hash url, hash hdr, reference dhh);
290 
291 };
292 
294 
297 
298 public:
300 
302  bool requiresAuthentication();
303 
304 
306 
308  string getRealm();
309 
310 
312 
318  list authenticate(string user, string pass = "");
319 
320 
322 
328  list authenticateByIP(string ip, reference user);
329 
330 };
331 
333 
336 
337 public:
338  public :
341 
343  bool decompress = True;
344 
347 
348 public:
349 
351 
354 
355 
357 
380  hash handleRequest(hash cx, hash hdr, *data body);
381 
382 
384 
391  static *string getLogMessage(hash cx, hash api, reference params, *reference args);
392 
394 
397 
398 
400 
403 
404 };
405 
406 // Listener will be assigned to private members of the HttpServer; is not directly accessible from code outside the HttpServer (private)
407 class HttpServer::HttpListener : public Qore::Socket {
408 
409 public:
410  private :
411  HttpServer serv;
412  Sequence ss;
413  *SSLCertificate cert;
414  *SSLPrivateKey key;
415  bool ssl = False;
416  any socket;
417  hash socket_info;
418 
419  // connection counter
420  Counter cThreads();
421  bool exit = False;
422  bool stopped = False;
423  int id;
424 
425  // stop mutex
426  Mutex sm();
427 
428  // code references to external logging functions
429  *code logger;
430  *code errorlogger;
431 
432  // stop notification closure
433  *code stopc;
434 
435  string name;
436 
437  const PollInterval = 300ms;
438 
439 public:
440 
441  public :
442  // TID of the background listener thread
443  int tid;
444 
445  // listener-specific handlers
446  HttpHandlerList handlers();
447 
448 public:
449 
450  // params: server, id, session ID sequence object, socket, rbac obj, [cert, key]
451  constructor(HttpServer server, int id, Sequence ss, any sock, *SSLCertificate cert, *SSLPrivateKey key, *hash hi, *code logger, *code errorlogger, *code stopc, string name);
452 
453 
454  copy();
455 
456 
457  destructor();
458 
459 
460  string getName();
461 
462 
463  any getAddress();
464 
465 
466  int getID();
467 
468 
469  bool isSecure();
470 
471 
472  hash getInfo();
473 
474 
475  stopNoWait();
476 
477 
478  stop();
479 
480 
481  log();
482 
483 
484  logError();
485 
486 
487 
488 private:
489  mainThread();
490 public:
491 
492 
493  // thread for handling communication per connection
494 
495 private:
496  connectionThread(Socket s, hash info);
497 public:
498 
499 };
500 
503 
504 public:
505  public :
507  const Version = "0.3.6";
509  const ReadTimeout = 30000; // recvs timeout after 30 seconds
511  const PollTimeout = 5000; // check for exit every 5 seconds while waiting
512 
513  // logging options
514  const LP_LOGPARAMS = 1 << 16;
515  const LP_LEVELMASK = LP_LOGPARAMS - 1;
516 
519 
521  const HttpCodes =
522  (
523  // 100s: Informational
524  "100": "Continue",
525  "101": "Switching Protocols",
526 
527  // RFC 2518: WebDAV
528  "102": "Processing",
529 
530  // 200s: Success
531  "200": "OK",
532  "201": "Created",
533  "202": "Accepted",
534  "203": "Non-Authoritative Information",
535  "204": "No Content",
536  "205": "Reset Content",
537  "206": "Partial Content",
538 
539  // RFC 4918: WebDAV: The message body that follows is an XML message and can contain a number of separate response codes, depending on how many sub-requests were made
540  "207": "Multi-Status",
541 
542  // RFC 5842: WebDAV: The members of a DAV binding have already been enumerated in a previous reply to this request, and are not being included again
543  "208": "Already Reported",
544 
545  // RFC 3229
546  "226": "IM Used",
547 
548  // 300s: Redirection
549  "300": "Multiple Choices",
550  "301": "Moved Permanently",
551  "302": "Found",
552  "303": "See Other",
553  "304": "Not Modified",
554  "305": "Use Proxy",
555  //"306": "(Reserved)",
556  "307": "Temporary Redirect",
557 
558  // 400s: Client Errors
559  "400": "Bad Request",
560  "401": "Unauthorized",
561  "402": "Payment Required",
562  "403": "Forbidden",
563  "404": "Not Found",
564  "405": "Method Not Allowed",
565  "406": "Not Acceptable",
566  "407": "Proxy Authentication Required",
567  "408": "Request Timeout",
568  "409": "Conflict",
569  "410": "Gone",
570  "411": "Length Required",
571  "412": "Precondition Failed",
572  "413": "Request Entity Too Large",
573  "414": "Request-URI Too Long",
574  "415": "Unsupported Media Type",
575  "416": "Requested Range Not Satisfiable",
576  "417": "Expectation Failed",
577 
578  // RFC 2324: http://tools.ietf.org/html/rfc2324
579  "418": "I'm a teapot",
580 
581  // Returned by the Twitter Search and Trends API when the client is being rate limited
582  "420": "Enhance Yextern Calm",
583 
584  // RFC 4918: WebDAV: The request was well-formed but was unable to be followed due to semantic errors
585  "422": "Unprocessable Entity",
586 
587  // RFC 4918: WebDAV: The resource that is being accessed is locked
588  "423": "Locked",
589 
590  // RFC 4918: WebDAV: The request failed due to failure of a previous request (e.g. a PROPPATCH)
591  "424": "Failed Dependency",
592 
593  // Internet draft: Defined in drafts of "WebDAV Advanced Collections Protocol", but not present in "Web Distributed Authoring and Versioning (WebDAV) Ordered Collections Protocol"
594  "425": "Unordered Collection",
595 
596  // RFC 2817: The client should switch to a different protocol such as TLS/1.0
597  "426": "Upgrade Required",
598 
599  // RFC 6585: The origin server requires the request to be conditional. Intended to prevent "the 'lost update' problem, where a client GETs a resource's state, modifies it, and PUTs it back to the server, when meanwhile a third party has modified the state on the server, leading to a conflict."
600  "428": "Precondition Required",
601 
602  // RFC 6585: The user has sent too many requests in a given amount of time. Intended for use with rate limiting schemes
603  "429": "Too Many Requests",
604 
605  // RFC 6585
606  "431": "Request Header Fields Too Large",
607 
608  // 500s: Server Errors
609  "500": "Internal Server Error",
610  "501": "Not Implemented",
611  "502": "Bad Gateway",
612  "503": "Service Unavailable",
613  "504": "Gateway Timeout",
614  "505": "HTTP Version Not Supported",
615  "509": "Bandwidth Limit Exceeded",
616 
617  // RFC 2774: Further extensions to the request are required for the server to fulfill it
618  "510": "Not Extended",
619 
620  // RFC 6585: The client needs to authenticate to gain network access. Intended for use by intercepting proxies used to control access to the network (e.g. "captive portals" used to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot)
621  "511": "Network Authentication Required",
622  );
623 
624 public:
625 
627  private :
628  *code logfunc;
629  *code errlogfunc;
630 
631  // quit server flag
632  bool exit = False;
633 
634  // if True then verbose exception info will be logged
635  bool debug;
636 
637  Sequence seqSessions();
638  Sequence seqListeners();
639 
640  string httpserverstring;
641 
642  bool stopped = False;
643 
644  // permanent handlers; these handlers are never removed
645  HttpHandlerList handlers();
646 
647  // default handler
648  hash defaultHandler;
649 
650  // hash of listeners keyed by listener ID
651  hash listeners;
652 
653  // map of bind addresses to listener IDs
654  hash smap;
655 
656  // map of listener names to listener IDs
657  hash nmap;
658 
659  // listener Gate
660  Gate lm();
661 
662  // running listener counter
663  Counter c();
664 
665  // dynamic handlers
666  DynamicHttpHandlerList dhandlers();
667 
668 public:
670 
672 
679  constructor(*code logfunc, *code errlogfunc, bool dbg = False, string name = sprintf("Qore-HTTP-Server/%s", HttpServer::Version));
680 
681 
683  destructor();
684 
685 
687 
707  final list addListenersWithHandler(string hname, AbstractHttpRequestHandler handler, hash lp, *code logger, *code errorlogger, *code stopc, *string name);
708 
709 
711 
720  hash addListener(softstring sock, *string cert_path, *string key_path, *string name);
721 
722 
724 
733  softlist addListeners(softstring sock, *string cert_path, *string key_path, *string name);
734 
735 
737 
747  list addINETListeners(*string node, softstring service, *string cert_path, *string key_path, *string name);
748 
749 
751  copy();
752 
753 
755 
757  hash getListeners();
758 
759 
761 
778  hash getListenerInfo(softint id);
779 
780 
782 
799  hash getListenerInfoName(string name);
800 
801 
803  int getListenerCount();
804 
805 
807 
809  stopNoWait();
810 
811 
813  waitStop();
814 
815 
816  // only called from the listeners - do not call externally
817  listenerStopped(HttpListener l);
818 
819 
821 
823  stop();
824 
825 
827  stopListener(softstring bind);
828 
829 
831  stopListenerID(softint id);
832 
833 
835  int getListenerTID(softint id);
836 
837 
840 
841 
843  setHandler(string name, string url, *string content_type, AbstractHttpRequestHandler obj, *softlist special_headers);
844 
845 
847  setDynamicHandler(string name, string url, *string content_type, AbstractHttpRequestHandler obj, *softlist special_headers);
848 
849 
851  removeDynamicHandler(string name);
852 
853 
855  log();
856 
857 
859  logError();
860 
861 
863  logArgs(list args = ());
864 
865 
867  logErrorArgs(list args = ());
868 
869 
871  sendHttpError(Socket s, int code, string msg, bool close = False, *hash extra_hdrs, *string encoding);
872 
873 
875  static string getURLFromBind(softstring bind, *string host);
876 
878  setDebug(bool dbg = True);
879 
880 
882  bool getDebug();
883 
884 
886  // don't reimplement this method; fix/enhance it in the module
887 
888 private:
889  final HttpListener addListenerIntern(string sock, *SSLCertificate cert, *SSLPrivateKey key, *hash hi, *code logger, *code errorlogger, *code stopc, *string name);
890 public:
891 
892 
893  // don't reimplement this method; fix/enhance it in the module
894 
895 private:
896  static final hash getSSLObjects(string cert_path, *string key_path);
897 public:
898 
899 
900  // don't reimplement this method; fix/enhance it in the module
901 
902 private:
903  final list addINETListenersIntern(*string node, softstring service, hash sd, *hash lp, *code logger, *code errorlogger, *code stopc, *string name);
904 public:
905 
906 
907  // don't reimplement this method; fix/enhance it in the module
908 
909 private:
910  final hash noHandlerError(hash cx, hash hdr, any body);
911 public:
912 
913 
914  // handles an incoming request - do not call externally; this method is called by the listeners when a request is received
915  // don't reimplement this method; fix/enhance it in the module
916  final handleRequest(HttpListener listener, Socket s, reference cx, hash hdr, *data body, reference close, bool head = False);
917 
919 };