Qore HttpServer Module Reference 1.1
Loading...
Searching...
No Matches
HttpServer.qm.dox.h
1// -*- mode: c++; indent-tabs-mode: nil -*-
3
4/* HttpServer.qm Copyright (C) 2012 - 2022 Qore Technologies, s.r.o.
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
321
327namespace HttpServer {
329
335 string get_exception_string(hash<auto> ex);
336
337
339public hashdecl HttpServerOptionInfo {
342
344 bool debug = False;
345
347 hash<auto> hdr = {
348 "X-Powered-By": "Qore/" + Qore::VersionString,
349 };;
350
352 *Logger logger;
353};
354
357
358public:
362 const ReadTimeout = HttpServer::ReadTimeout; # recvs timeout after 30 seconds
364 const PollTimeout = 250ms;
365
366 // logging options
369
371 const AIFlags = AI_PASSIVE | AI_ADDRCONFIG;
372
374 const HttpMethods = {
375 "HEAD": True,
376 "POST": True,
377 "PUT": True,
378 "DELETE": True,
379 "GET": True,
380 "OPTIONS": True,
381 "PATCH": True,
382 //"TRACE": True,
383 #"CONNECT": True,
384 };
385
388
390 const ContentEncodings = {
391 "gzip": "gzip",
392 "deflate": "deflate",
393 "bzip2": "bzip2",
394 "x-gzip": "gzip",
395 "x-deflate": "deflate",
396 "x-bzip2": "bzip2",
397 };
398
400 const DefaultIdleThreads = 10;
401
403 const CompressionThreshold = 1024;
404
415 const LLO_RECV_HEADERS = (1 << 0);
416
418 const LLO_RECV_BODY = (1 << 1);
419
421 const LLO_SEND_HEADERS = (1 << 2);
422
424 const LLO_SEND_BODY = (1 << 3);
426
428protected:
429 // for masking HTTP request log msgs
430 *code maskfunc;
431
432 // quit server flag
433 bool exit = False;
434
435 // if True then verbose exception info will be logged
436 bool debug;
437
438 Sequence seqSessions();
439 Sequence seqListeners();
440
441 bool stopped = False;
442
443 // permanent handlers; these handlers are never removed
444 HttpHandlerList handlers();
445
446 // default handler
447 hash<auto> defaultHandler;
448
449 // hash of listeners keyed by listener ID
450 hash<string, HttpListener> listeners;
451
452 // map of bind addresses to listener IDs
453 hash<string, int> smap;
454
455 // map of listener names to listener IDs
456 hash<string, int> nmap;
457
458 // listener Gate
459 Gate lm();
460
461 // running listener counter
462 Counter c();
463
464 // dynamic handlers
465 DynamicHttpHandlerList dhandlers();
466
467 // connection thread pool
468 ThreadPool threadPool(-1, DefaultIdleThreads);
469
470 // other misc response headers
471 hash<auto> hdr;
472
474
476 *string override_encoding;
477
479 string http_server_string;
480
482 hash<string, bool> http_methods = HttpMethods;
483
485 *Logger logger;
486
487public:
489
491
513 deprecated constructor(*code logfunc, *code errlogfunc, bool dbg = False,
514 string name = HttpServer::HttpServerString,
515 hash<auto> hdr = {"X-Powered-By": "Qore/" + Qore::VersionString}) {
516 http_server_string = name;
517 debug = dbg;
518 hdr = {
519 "Server": name,
520 } + hdr;
521
522 logger = new Logger(http_server_string);
523 if (logfunc || errlogfunc);
524 else {
525 logger.setLevel(LoggerLevel::INFO);
526 }
527 }
528
530
532 constructor(hash<HttpServerOptionInfo> opts);
533
534
537
538
541
542
544
546 addHttpMethod(string m);
547
548
551
552 setDefaultTextEncoding(string enc);
553
554
555 string getDefaultTextEncoding();
556
557
559 callListenerStopCallback(code stopc, string name, hash<auto> socket_info);
560
561
563
580 final list<hash<auto>> addListeners(string bind, hash<HttpListenerOptionInfo> info,
581 *reference<hash<string, string>> errmap) {
582 if (bind ==1);
583
584
585 // add node and service
586 (info.node, info.service) = (bind =~ x/(.+):(\w+)/);
587 return addINETListenersIntern(info, \errmap);
588 }
589
591
604 final list<hash<auto>> addListeners(hash<HttpListenerOptionInfo> info, *reference<hash<string, string>> errmap);
605
606
608
615 hash<auto> addListener(int port);
616
617
619
629 hash<auto> addListener(hash<HttpListenerOptionInfo> opts);
630
631
634
635
637
639 hash<string, hash<auto>> getListeners();
640
641
643
660 hash<auto> getListenerInfo(softint id);
661
662
664
686 hash<auto> getListenerInfoName(string name);
687
688
690
701
702
704
715
716
719
720
722
725
726
729
730
732
737 listenerStarted(int id, hash<auto> sinfo);
738
739
740 // only called from the listeners - do not call externally
741 listenerStopped(HttpListener l);
742
743
745
748
749
751
756 stopListener(softstring bind);
757
758
760
765 stopListenerID(softint id);
766
767
769
775
776
778 int getListenerTID(softint id);
779
780
783
784
786 setHandler(string name, hash<HttpHandlerConfigInfo> info);
787
788
790 setHandler(string name, string path, *softlist<softstring> content_types, HttpServer::AbstractHttpRequestHandler obj, *softlist<softstring> special_headers, bool isregex = True);
791
792
794 setDynamicHandler(string name, hash<HttpHandlerConfigInfo> info);
795
796
798 setDynamicHandler(string name, string path, *softlist<softstring> content_types,
799 HttpServer::AbstractHttpRequestHandler obj, *softlist<softstring> special_headers, bool isregex = True) {
800 dhandlers.setHandler(name, <HttpHandlerConfigInfo>{
801 "path": path,
802 "isregex": isregex,
803 "content_types": content_types,
804 "handler": obj,
805 "headers": special_headers,
806 });
807 }
808
811
812
815
816
818 addHandlerToListener(softstring bind, string name, hash<HttpHandlerConfigInfo> info);
819
820
822
828 addHandlerToListenerID(softint id, string name, hash<HttpHandlerConfigInfo> info);
829
830
832
842 addHandlerToListenerID(softint id, string name, string path, *softlist content_type,
843 HttpServer::AbstractHttpRequestHandler obj, *softlist special_headers, bool isregex = True) {
844 addHandlerToListenerID(id, name, <HttpHandlerConfigInfo>{
845 "path": path,
846 "isregex": isregex,
847 "content_types": content_type,
848 "handler": obj,
849 "headers": special_headers,
850 });
851 }
852
854 addHandlerToListener(softstring bind, string name, HttpServer::AbstractUrlHandler obj);
855
856
858
865
866
868
874 bool removeHandlerFromListenerID(softstring id, string handler_name);
875
876
878
885
886
888
898 setListenerLogOptions(softstring bind, softint code);
899
900
902
914 setListenerLogOptionsID(softint id, softint code);
915
916
918
923 int getListenerLogOptions(softstring bind);
924
925
927
932 int getListenerLogOptionsID(softint id);
933
934
936
939
940
942
944 removeDynamicHandler(string name, *bool force_immediate);
945
946
948 log(string fmt);
949
950
952 logError(string fmt);
953
954
956 sendHttpError(HttpListener listener, hash<auto> cx, Socket s, int code, *data msg, *InputStream chunked_msg, *hash<auto> extra_hdrs, *string encoding, bool head);
957
958
960
964 string maskData(string msg);
965
966
968
979 setMaskCode(code maskfunc);
980
981
983
988 static string getURLFromBind(softstring bind, *string host);
989
991 setDebug(bool dbg = True);
992
993
995 bool getDebug();
996
997
998 startConnection(code c);
999
1000
1002
1004protected:
1006public:
1007
1008
1010protected:
1011 setListenerLogOptionsUnlocked(softstring id, int code);
1012public:
1013
1014
1016protected:
1018public:
1019
1020
1022 static nothing setReplyHeaders(Socket s, hash<auto> cx, reference<hash<auto>> rv);
1023
1025 // don't reimplement this method; fix/enhance it in the module
1026 final private HttpListener addListenerIntern(*string node, *softstring service, *Qore::SSLCertificate cert,
1027 *Qore::SSLPrivateKey key, *hash<string, hash<HttpHandlerConfigInfo>> handler_info, *Logger logger,
1028 *code stopc, *string name, int family = AF_UNSPEC) {
1029 return addListenerIntern(<HttpListenerOptionInfo>{
1030 "node": node,
1031 "service": service,
1032 "cert": cert,
1033 "key": key,
1034 "handler_info": handler_info,
1035 "logger": logger,
1036 "stopc": stopc,
1037 "family": family,
1038 });
1039 }
1040
1041protected:
1042 final HttpListener addListenerIntern(hash<HttpListenerOptionInfo> info);
1043public:
1044
1045
1046 // don't reimplement this method; fix/enhance it in the module
1047 final private list<hash<auto>> addINETListenersIntern(hash<HttpListenerOptionInfo> info,
1048 *reference<hash<string, string>> errmap) {
1049 list<auto> al = getaddrinfo(info.node, info.service, info.family, AIFlags);
1050
1051 // sort ipv6 addresses first in list
1052 al = sort(al, int (hash<auto> l, hash<auto> r) {
1053 return l.family === AF_INET6 ? -1 : 0;
1054 });
1055
1056 list<hash<auto>> rv = ();
1057 foreach hash<auto> h in (al);
1058
1059
1060 return rv;
1061 }
1062
1063 // don't reimplement this method; fix/enhance it in the module
1064protected:
1065 final hash<HttpResponseInfo> noHandlerError(hash<auto> cx, hash<auto> hdr, auto body);
1066public:
1067
1068
1069 // handles an incoming request - do not call externally; this method is called by the listeners when a request is received
1070 // don't reimplement this method; fix/enhance it in the module
1071 final handleRequest(HttpListener listener, Socket s, reference<hash<auto>> cx, hash<auto> hdr, bool head = False,
1072 HttpPersistentHandlerInfo phi, hash<auto> info, reference<bool> dedicated) {
1073 if (!http_methods{hdr.method});
1074
1075
1076 // get first supported encoding for response
1077 foreach string enc in (cx.'header-info'.'accept-encoding');
1078
1079
1080 // convert "accept-encoding" to a hash
1081 cx."header-info"."accept-encoding" = map {$1: True}, cx."header-info"."accept-encoding";
1082
1083 // erase the encoding string on exit
1084 on_exit remove cx.encoding;
1085
1086 hash url = {
1087 "path": hdr.path,
1088 };
1089
1090 // add logging functions and url
1091 cx += {
1092 "logger": logger,
1093 "url": url,
1094 "debug": debug,
1095 };
1096
1098 // the dynamic handler helper maintains the request count for dynamic handlers so any call to remove the handler will only return after all in-progress requests are completed; the object is only allocated if needed
1099 DynamicHandlerHelper dhh;
1100
1101 if (phi.handler);
1102 else {
1103 // find a handler for the request
1104 *HandlerInfo hi;
1105 int score = 0;
1106 string root_path;
1107 if (listener.hasHandlers()) {
1108 hi = listener.findHandler(hdr, \score, True, \root_path);
1109 if (!hi) {
1110 if (listener.defaultHandler)
1111 hi = listener.defaultHandler;
1112 else {
1113 sendHttpError(listener, cx, s, 404, "Not found", NOTHING, NOTHING,
1114 cx."response-encoding", head);
1115 return;
1116 }
1117 }
1118 } else {
1119 hi = handlers.findHandler(hdr, \score, False, \root_path);
1120 if (score < 3) {
1121 *HandlerInfo dhi = dhandlers.findHandler(hdr, \score, \dhh, \root_path);
1122 if (dhi)
1123 hi = dhi;
1124 }
1125 }
1126
1127 if (hi) {
1128 handler = hi.obj;
1129 cx.handler_name = hi.name;
1130 if (root_path)
1131 cx.root_path = root_path;
1132 }
1133 }
1134
1135 try {
1136 if (!handler && defaultHandler) {
1137 handler = defaultHandler.obj;
1138 cx.handler_name = defaultHandler.name;
1139 }
1140
1141 if (handler && hdr.expect) {
1142 if (hdr.expect == "100-continue") {
1143 hash<HttpResponseInfo> resp = handler.handleExpectHeader(cx, hdr);
1144 sendReply(listener, s, handler, resp, \cx, hdr, head, \dedicated);
1145 } else {
1146 string etxt = sprintf("unrecognized 'Expect' header: %y", hdr.expect);
1147 string str = sprintf("%s: received from %s via %s (header: %y)", etxt,
1148 cx."peer-info".address_desc, cx."socket-info".address_desc, hdr);
1149 listener.logError(str);
1150 cx.close = True;
1151 sendHttpError(listener, cx, s, 400, etxt, NOTHING, NOTHING, cx."response-encoding",
1152 hdr.method == "HEAD");
1153 return;
1154 }
1155 }
1156
1157 *data body;
1158 // if we need to get a body
1159 if (hdr."content-length") {
1160 try {
1161 if (hdr."content-encoding" || hdr."content-type" == MimeTypeOctetStream)
1162 body = s.recvBinary(hdr."content-length", HttpServer::ReadTimeout);
1163 else
1164 body = s.recv(hdr."content-length", HttpServer::ReadTimeout);
1165 #printf("HTTP DEBUG: %s\n", body);
1166 } catch (hash<ExceptionInfo> ex) {
1167 string etxt = sprintf("error reading body in %s (Content-Length: %d): %s: %s", hdr.method,
1168 hdr."content-length", ex.err, ex.desc);
1169 string str = sprintf("%s: received from %s via %s (header: %y)", etxt,
1170 cx."peer-info".address_desc, cx."socket-info".address_desc, hdr);
1171 listener.logError(str);
1172 cx.close = True;
1173 sendHttpError(listener, cx, s, 400, etxt, NOTHING, NOTHING, cx."response-encoding",
1174 hdr.method == "HEAD");
1175 return;
1176 }
1177 }
1178
1179 # override assumed "iso-8859-1" character encoding if necessary
1180 if (body.typeCode() == NT_STRING && override_encoding && hdr."_qore_orig_content_type" !~ /charset=/) {
1181 body = force_encoding(body, override_encoding);
1182 }
1183
1184 listener.doLogRecvBody(body, cx, hdr, info);
1185
1186 hash<HttpResponseInfo> rv;
1187
1188 #printf("HTTP DEBUG: B: handler: %y strict-bool-eval: %y\n", cx.handler_name, get_parse_options() & PO_STRICT_BOOLEAN_EVAL);
1189 #printf("HTTP DEBUG: B: handler: %y: context: %y, hdr: %y, body: %y\n", cx.handler_name, cx, hdr, body);
1190 #printf("HTTP DEBUG: BEFORE handler=%s", dbg_node_info(handlers.(cx.handler_name)));
1191
1192 #printf("HTTP DEBUG: A: cid: %d handler: %y auth: %y\n", cx.id, cx.handler_name, boolean(handler.auth));
1193
1194 if (!handler) {
1195 cx.handler_name = "error";
1196 rv = noHandlerError(cx, hdr, body);
1197 } else {
1198 # check for authentication info
1199 #printf("HTTP DEBUG: handler: %y (auth: %y) hdr: %y\n", cx.handler_name, exists handler.auth && handler.auth.requiresAuthentication(), hdr);
1200
1201 if (handler.auth) {
1202 try {
1203 *hash<auto> ah = handler.auth.authenticateRequest(listener, hdr, \cx);
1204 # return an error if authentication failed
1205 if (ah) {
1206 if (!ah.code)
1207 ah.code = 401;
1208 if (!ah.body && !ah.hasKeyValue("chunked_body"))
1209 ah.body = "Authentication is required to access this server";
1210 cx.close = True;
1211 sendHttpError(listener, cx, s, ah.code, ah.body,
1212 ah.hasKey("chunked_body") ? ah.chunked_body : NOTHING, ah.hdr, cx."response-encoding",
1213 head);
1214 return;
1215 }
1216 } catch (hash<ExceptionInfo> ex) {
1217 # log the error
1218 string desc = !debug
1219 ? sprintf("%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc)
1221 desc += sprintf(": received from %s", cx."peer-info".address_desc);
1222 listener.logError("%s", desc);
1223 cx.close = True;
1224 sendHttpError(listener, cx, s, 501, "Server Authentication Error", NOTHING, NOTHING, NOTHING,
1225 head);
1226 return;
1227 }
1228 #listener.log("authenticated with user %y", cx.user);
1229 }
1230
1231 *hash<auto> tld_save = handler.saveThreadLocalData();
1232 on_exit handler.restoreThreadLocalData(tld_save);
1233
1234 # decode body if applicable
1235 if (body && hdr."content-encoding" && handler.decompress)
1236 body = AbstractHttpRequestHandler::decodeBody(hdr."content-encoding", body,
1237 handler.decompress_to_string ? s.getEncoding() : NOTHING);
1238
1239 rv = cast<hash<HttpResponseInfo>>(handler.handleRequest(listener, s, cx, hdr, body));
1240
1241 # manage persistent handler info, if applicable
1242 if (!phi.handler) {
1243 if (handler.isPersistent())
1244 phi.assign(dhh, handler);
1245 } else if (!handler.isPersistent())
1246 phi.clear();
1247
1248 if (rv.reply_sent) {
1249 return;
1250 }
1251 }
1252
1253 sendReply(listener, s, handler, rv, \cx, hdr, head, \dedicated);
1254 } catch (hash<ExceptionInfo> ex) {
1255 string desc = !debug
1256 ? sprintf("%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc)
1258
1259 string str = sprintf("handler: %s: %s", cx.handler_name, desc);
1260
1261 if (ex.err == "ENCODING-CONVERSION-ERROR") {
1262 s.setEncoding("utf8");
1263 cx."response-encoding" = "utf8";
1264 }
1265
1266 cx.close = True;
1267
1268 # if there is a pending chunked body that has not yet been read on exit, then read it and discard before sending any response
1269 on_exit if (s.pendingHttpChunkedBody())
1270 s.readHTTPChunkedBodyBinary(HttpServer::ReadTimeout);
1271
1272 sendHttpError(listener, cx, s, 500, str, NOTHING, NOTHING, cx."response-encoding", head);
1273 logError(str);
1274 }
1275 }
1276
1277 // sends a reply to a request
1278 // don't reimplement this method; fix/enhance it in the module
1279 final sendReply(HttpListener listener, Socket s, *HttpServer::AbstractHttpRequestHandler handler, hash<auto> rv,
1280 reference<hash> cx, hash<auto> hdr, bool head, reference<bool> dedicated) {
1281 if (exists rv.close)
1282 cx.close = boolean(rv.close);
1283
1284 rv.close = cx.close;
1285
1286 // if there is a pending chunked body that has not yet been read on exit, then read it and discard before sending any response
1287 on_exit if (s.pendingHttpChunkedBody())
1288 s.readHTTPChunkedBodyBinary(HttpServer::ReadTimeout);
1289
1290 if (!HttpCodes.(rv.code));
1291
1292
1293 //printf("HTTP DEBUG: handler %s returned: %n\n", cx.handler_name, rv);
1294
1295 // currently enforced by the handlers:
1296 // RFC 2616 section 4.3: http://tools.ietf.org/html/rfc2616#section-4.3
1297 // All 1xx (informational), 204 (no content), and 304 (not modified) responses
1298 // MUST NOT include a message-body. All other responses do include a
1299 // message-body, although it MAY be of zero length.
1300
1301 rv.hdr = self.hdr + rv.hdr;
1302
1303 if ((exists rv.body || (bool chunked_body = rv.hasKeyValue("chunked_body")))
1304 && (head
1305 || (rv.code < 200
1306 || (rv.code == 204
1307 || rv.code == 304)))) {
1308 remove rv.body;
1309 if (chunked_body);
1310
1311 }
1312
1314
1315 if (rv.code >= 300 && rv.code < 400);
1316 else if (rv.code >= 400);
1317 else {
1318 //printf("**** RESPONSE: %d ct: %s (te: %y) encoding: %y: %N\n", rv.code, rv.hdr."Content-Type",
1319 # rv.hdr."Transfer-Encoding", cx.encoding, rv.body);
1320
1321 # compress body if eligible for compression
1322 if (rv.body && !rv.hdr."Content-Encoding" && rv.body.size() > CompressionThreshold) {
1323 if (cx.encoding == "deflate") {
1324 rv.hdr."Content-Encoding" = "deflate";
1325 rv.body = compress(rv.body);
1326 } else if (cx.encoding == "gzip") {
1327 rv.hdr."Content-Encoding" = "gzip";
1328 rv.body = gzip(rv.body);
1329 } else if (cx.encoding == "bzip2") {
1330 rv.hdr."Content-Encoding" = "bzip2";
1331 rv.body = bzip2(rv.body);
1332 }
1333 }
1334
1335 doResponse(listener, s, cx, rv);
1336 }
1337
1338 if (rv.log)
1339 listener.log("cid %d: %s (from %s): %s", cx.id, cx.handler_name, cx."peer-info".address_desc, rv.log);
1340 if (rv.errlog)
1341 listener.logError("cid %d: %s (from %s): %s", cx.id, cx.handler_name, cx."peer-info".address_desc,
1342 rv.errlog);
1343
1344 if (!rv.close && rv.user_state);
1345
1346
1347 if (rv.code == 101);
1348
1349 }
1350
1351 // rv is "hash" to strip types
1352private:
1353 doResponse(HttpListener listener, Socket s, hash<auto> cx, hash rv);
1354public:
1355
1357};
1358
1359class HttpPersistentHandlerInfo {
1360
1361public:
1362 *DynamicHandlerHelper dhh;
1364
1365 destructor();
1366
1367
1368 assign(*DynamicHandlerHelper n_dhh, HttpServer::AbstractHttpRequestHandler n_handler);
1369
1370
1371 clear();
1372
1373};
1374
1376
1379
1380public:
1381protected:
1382 HttpServer serv;
1383 Sequence ss;
1384
1385 *hash<HttpListenerOptionInfo> opts;
1386
1387 //*SSLCertificate cert;
1388 //*SSLPrivateKey key;
1389 bool ssl = False;
1390 auto socket;
1391 hash socket_info;
1392
1393 // connection counter for normal connection threads
1394 Counter cThreads();
1395 // connection counter for dedicated socket threads
1396 Counter dThreads();
1397
1398 bool exit = False;
1399 bool stopped = False;
1400 int id;
1401
1402 // socket handler hash
1403 hash<string, AbstractHttpSocketHandlerInterface> shh;
1404
1405 // mutex
1406 Mutex m();
1407
1409 *Logger logger;
1410
1411 // stop notification closure
1412 *code stopc;
1413
1414 string name;
1415
1416 // log recv headers flag
1417 bool log_recv_headers = False;
1418
1419 // log recv body flag
1420 bool log_recv_body = False;
1421
1422 // log send headers flag
1423 bool log_send_headers = False;
1424
1425 // log send body flag
1426 bool log_send_body = False;
1427
1429 bool get_remote_certs = False;
1430
1432 int ssl_verify_flags = SSL_VERIFY_NONE;
1433
1436
1437 const PollInterval = 250ms;
1438 const ListenQueue = 100;
1439 const BodyLogLimit = 40;
1440
1441 // handler ref count
1442 int handler_ref_cnt = 0;
1443
1444 // listener-specific handlers
1445 HttpHandlerList handlers();
1446
1449 SSL_VERIFY_PEER: "SSL_VERIFY_PEER",
1450 SSL_VERIFY_FAIL_IF_NO_PEER_CERT: "SSL_VERIFY_FAIL_IF_NO_PEER_CERT",
1451 SSL_VERIFY_CLIENT_ONCE: "SSL_VERIFY_CLIENT_ONCE",
1452 };
1453
1454public:
1455
1456 // TID of the background listener thread
1457 int tid;
1458
1459 // default handler info
1460 *HandlerInfo defaultHandler;
1461
1463
1470 constructor(HttpServer server, string name, int id, Sequence ss, *hash<HttpListenerOptionInfo> opts);
1471
1472
1474
1482
1483
1485 addHandlers(hash<string, hash<HttpHandlerConfigInfo>> handler_info);
1486
1487
1491 removeHandler(string handler_name);
1492
1493
1498
1499
1500 setDefaultHandler(string name);
1501
1502
1503 *HandlerInfo findHandler(hash<auto> hdr, reference<int> score, bool finalv = False, *reference<string> root_path);
1504
1505
1506 bool hasHandlers();
1507
1508
1510
1514
1515 hash getListenerSocketInfo();
1516
1517
1518 copy();
1519
1520
1521 destructor();
1522
1523
1524 logRecvHeaders(softbool flag = True);
1525
1526
1527 logRecvBody(softbool flag = True);
1528
1529
1530 logSendHeaders(softbool flag = True);
1531
1532
1533 logSendBody(softbool flag = True);
1534
1535
1536 doLogRecvBody(*data body, hash<auto> cx, hash<auto> hdr, hash<auto> info);
1537
1538
1539 hash<auto> getLogOptions();
1540
1541
1542 string getName();
1543
1544
1545 auto getAddress();
1546
1547
1548 int getID();
1549
1550
1551 bool isSecure();
1552
1553
1554 hash<auto> getInfo();
1555
1556
1557protected:
1558 setupSslIntern();
1559public:
1560
1561
1562protected:
1563 softlist<string> getSslVerifyModeList();
1564public:
1565
1566
1567 auto removeUserThreadContext(*string k);
1568
1569
1570 addUserThreadContext(hash<auto> uctx);
1571
1572
1573 stopNoWait();
1574
1575
1576 stop();
1577
1578
1579 logResponse(hash<auto> cx, int code, *data body, *hash<auto> hdr);
1580
1581
1582 logResponse(hash<auto> cx, int code, InputStream body, *hash<auto> hdr);
1583
1584
1585 logResponse(hash<auto> cx, hash<auto> rv);
1586
1587
1588 log(string fmt);
1589
1590
1591 logError(string fmt);
1592
1593
1594protected:
1595 mainThread();
1596public:
1597
1598
1599 // thread for handling communication per connection
1600protected:
1601 connectionThread(Socket s);
1602public:
1603
1604
1605 bool registerDedicatedSocket(softstring id, HttpServer::AbstractHttpSocketHandlerInterface h, reference<bool> dedicated);
1606
1607
1608 removeDedicatedSocket(softstring id, HttpServer::AbstractHttpSocketHandlerInterface h);
1609
1610};
1611}
1612
1614namespace Priv {
1615// class containing handler info
1616class HandlerInfo {
1617
1618public:
1619 string name;
1621 string path;
1622 bool isregex;
1623 // content type map
1624 *hash<string, bool> content_type_map;
1625 *list<string> special_header_list;
1626
1628
1630 constructor(string name, hash<HttpHandlerConfigInfo> info);
1631
1632
1633 bool matchContentType(string ct);
1634
1635
1637 int matchRequest(hash<auto> hdr, int score);
1638
1639
1640 stop();
1641
1642};
1643
1644// class to implement handler-handling (private)
1645class HttpHandlerList {
1646
1647public:
1648 hash<string, HandlerInfo> handlers;
1649 TreeMap treeMap();
1650
1651 /*
1652 private static checkSpecialHeaders(reference sh) {
1653 foreach auto h in (\sh) {
1654 if (h.typeCode() != NT_STRING)
1655 throw "SETHANDLER-ERROR", sprintf("entry %d in the special header list is not a string; type: %s", $#, h.type());
1656 # make sure header is in lower case for matching
1657 h = tolower(h);
1658 }
1659 }
1660 */
1661
1663 setHandler(string name, hash<HttpHandlerConfigInfo> info);
1664
1665
1667
1670 removeHandler(string handler_name);
1671
1672
1674
1677 removeHandler(HttpServer::AbstractHttpRequestHandler handler);
1678
1679
1680 // matches a handler to the request
1681 *HandlerInfo findHandler(hash<auto> hdr, reference<int> score, bool finalv = False, *reference<string> root_path);
1682
1683
1684 bool empty();
1685
1686
1687 int size();
1688
1689};
1690
1691// class containing dynamic handler info
1692class DynamicHandlerInfo : public HandlerInfo {
1693
1694public:
1695 Counter counter();
1696
1697 constructor(string name, hash<HttpHandlerConfigInfo> info) ;
1698
1699};
1700
1701// maintains the request count for dynamic handlers (private)
1702class DynamicHandlerHelper {
1703
1704public:
1705
1706
1707protected:
1708 Counter c;
1709
1710public:
1711
1712 constructor(Counter n_c);
1713
1714
1715 destructor();
1716
1717};
1718
1719// for dynamic handler-handling (private)
1720class DynamicHttpHandlerList : public HttpHandlerList {
1721
1722public:
1723protected:
1724 RWLock dhl();
1725
1726 // issue #4173: allow dynamic handlers to be disabled before being removed
1727 hash<string, bool> disable_map;
1728
1729public:
1730
1732 setHandler(string name, hash<HttpHandlerConfigInfo> info);
1733
1734
1736
1738 removeHandler(string name, *bool force_immediate);
1739
1740
1742
1744 disableHandler(string name);
1745
1746
1747 *DynamicHandlerInfo findHandler(hash<auto> hdr, reference<int> score, reference<DynamicHandlerHelper> dhh, *reference<string> root_path);
1748
1749};
1750
1751class HttpServerCallbackAppender : public LoggerAppenderWithLayout {
1752
1753public:
1754protected:
1755 code logfunc;
1756
1757public:
1758
1759 constructor(code logfunc) ;
1760
1761
1762 processEventImpl(int type, auto params);
1763
1764};
1765};
*hash< HttpResponseInfo > authenticateRequest(HttpListenerInterface listener, hash< auto > hdr, reference< hash< auto > > cx)
hash< HttpResponseInfo > handleRequest(hash< auto > cx, hash< auto > hdr, *data body)
restoreThreadLocalData(*hash< auto > data)
static data decodeBody(string content_encoding, binary body, *string string_encoding)
hash< HttpResponseInfo > handleExpectHeader(hash< auto > cx, hash< auto > hdr)
this class implements the listeners for the HttpServer class
Definition: HttpServer.qm.dox.h:1378
removeHandler(HttpServer::AbstractHttpRequestHandler handler)
const SslVerifyMap
map for converting ssl verify flags to strings
Definition: HttpServer.qm.dox.h:1448
constructor(HttpServer server, string name, int id, Sequence ss, *hash< HttpListenerOptionInfo > opts)
creates the object with the given parameters
static bool stopIfNoHandlers(HttpServer::HttpListener listener)
stop listeners if there are no handlers
removeHandler(string handler_name)
addHandlers(hash< string, hash< HttpHandlerConfigInfo > > handler_info)
add handlers to the listener
bool get_remote_certs
get remote certificates
Definition: HttpServer.qm.dox.h:1429
int ssl_verify_flags
SSL verify flags.
Definition: HttpServer.qm.dox.h:1432
bool ssl_accept_all_certs
accept all certificates
Definition: HttpServer.qm.dox.h:1435
*Logger logger
listener-specific logger; if not present, then the server's logger will be used
Definition: HttpServer.qm.dox.h:1409
reloadCertificate()
Reloads the HTTPS certificate from the original location.
constructor()
Creates the server with default options and an empty logger.
stopListenerID(softint id)
stops a single listener based on its listener ID; does not return until all connections on the listen...
const ReadTimeout
default read timeout in ms
Definition: HttpServer.qm.dox.h:362
hash< string, hash< auto > > getListeners()
returns a hash of listener information
static nothing setReplyHeaders(Socket s, hash< auto > cx, reference< hash< auto > > rv)
helper method to set HTTP response headers
disableDynamicHandler(string name)
Disable dynamic handler.
final list< hash< auto > > addListeners(string bind, hash< HttpListenerOptionInfo > info, *reference< hash< string, string > > errmap)
adds one or more dedicated listeners to the server with the given bind address
Definition: HttpServer.qm.dox.h:580
setMaskCode(code maskfunc)
sets the closure or call reference that will be used to mask sensitive data in log messages
addHandlerToListener(softstring bind, string name, hash< HttpHandlerConfigInfo > info)
adds a request handler to a listener given the listener's name or bind address
static string getHttpServerVersionString()
returns the HTTP server version string
setDefaultHandler(string name, HttpServer::AbstractHttpRequestHandler obj)
sets the default request handler when no other handler can be matched
removeDynamicHandler(string name, *bool force_immediate)
remove dynamic handler
int getListenerCount()
returns the number of running HTTP listeners
destructor()
calls stop() and destroys the object
hash< auto > addListener(hash< HttpListenerOptionInfo > opts)
adds a single global listener to the server
setDynamicHandler(string name, HttpServer::AbstractUrlHandler obj)
sets a dynamic request handler according to the arguments given
string maskData(string msg)
masks log messages by removing sensitive data
listenerStarted(int id, hash< auto > sinfo)
called from listener when the actual listener thread is running
bool removeHandlerFromListenerID(softstring id, HttpServer::AbstractHttpRequestHandler handler)
remove request handler from a listener given the listener's id
waitStop()
waits for all listeners to be stopped; call after calling HttpServer::stopNoWait()
reloadListenerCertificate(int id)
Reloads an HTTPS certificate from the original location for the given listener from the listener ID.
hash< auto > getListenerInfoName(string name)
returns a hash of information about the listener given the listener name or bind ID
stopListener(softstring bind)
stops a single listener based on its name or bind address; does not return until all connections on t...
static string getURLFromBind(softstring bind, *string host)
returns a complete URL from a bind address
reloadListenerCertificateName(string name)
Reloads an HTTPS certificate from the original location for the given listener from the listener name...
copy()
throws an exception; these objects do not support copying
bool removeHandlerFromListenerID(softstring id, string handler_name)
remove request handler from a listener given the listener's id
logError(string fmt)
called to log error information to the registered error log code
setHandler(string name, string path, *softlist< softstring > content_types, HttpServer::AbstractHttpRequestHandler obj, *softlist< softstring > special_headers, bool isregex=True)
sets a request handler according to the arguments given
addHandlerToListenerID(softint id, string name, hash< HttpHandlerConfigInfo > info)
adds a request handler to a listener given the listener's id
constructor(hash< HttpServerOptionInfo > opts)
Creates the server with the given options.
deprecated constructor(*code logfunc, *code errlogfunc, bool dbg=False, string name=HttpServer::HttpServerString, hash< auto > hdr={"X-Powered-By":"Qore/"+Qore::VersionString})
creates the HttpServer
Definition: HttpServer.qm.dox.h:513
addHttpMethod(string m)
add a new supported HTTP method
final list< hash< auto > > addListeners(hash< HttpListenerOptionInfo > info, *reference< hash< string, string > > errmap)
adds one or more dedicated listeners to the server with the given bind address
int getListenerTID(softint id)
gets the TID of a listener based on its listener ID
callListenerStopCallback(code stopc, string name, hash< auto > socket_info)
To call a listener's stop callback.
bool getDebug()
returns the current status of the debug flag
addHandlerToListenerID(softint id, string name, HttpServer::AbstractUrlHandler obj)
adds a request handler to a listener given the listener's id
setListenerLogOptions(softstring bind, softint code)
turns on or off header and body logging options for receive operations for the given listener
addHandlerToListener(softstring bind, string name, HttpServer::AbstractUrlHandler obj)
adds a request handler to a listener given the listener's name or bind address
setDebug(bool dbg=True)
turns on or off debugging; when debugging is enabled more verbose error messages are reported
const HttpCodes
map of HTTP result codes and text messages
Definition: HttpServer.qm.dox.h:387
hash< auto > addListener(int port)
adds a single global listener to the server
setHandler(string name, hash< HttpHandlerConfigInfo > info)
sets a request handler according to the arguments given
setListenerLogOptionsID(softint id, softint code)
turns on or off header and body logging options for receive operations for the given listener
sendHttpError(HttpListener listener, hash< auto > cx, Socket s, int code, *data msg, *InputStream chunked_msg, *hash< auto > extra_hdrs, *string encoding, bool head)
sends an HTTP error message on the socket
log(string fmt)
called to log information to the registered log code
addHandlerToListenerID(softint id, string name, string path, *softlist content_type, HttpServer::AbstractHttpRequestHandler obj, *softlist special_headers, bool isregex=True)
adds a request handler to a listener given the listener's id
Definition: HttpServer.qm.dox.h:842
setDynamicHandler(string name, string path, *softlist< softstring > content_types, HttpServer::AbstractHttpRequestHandler obj, *softlist< softstring > special_headers, bool isregex=True)
sets a dynamic request handler according to the arguments given
Definition: HttpServer.qm.dox.h:798
int getListenerLogOptionsUnlocked(softstring id)
returns header and body logging options for the given listener as a binary-or'ed value of listener lo...
int getListenerLogOptions(softstring bind)
returns a binary-or'ed product of listener log options corresponding to enabled log options for the g...
stopNoWait()
stops all listeners; does not wait for all connections on the listeners to close
stopListenerIDNoWait(softint id)
starts the shutdown process for a listener based on its listener ID; returns immediately
int getListenerLogOptionsID(softint id)
returns a binary-or'ed product of listener log options corresponding to enabled log options for the g...
setHandler(string name, HttpServer::AbstractUrlHandler obj)
sets a request handler according to the arguments given
setListenerLogOptionsUnlocked(softstring id, int code)
turns on or off header and body logging options for the given listener according to the option code
stop()
stops all listeners; only returns when all connections are closed on all listeners
hash< auto > getListenerInfo(softint id)
returns a hash of information about the listener given the listener ID
int getListenerIdFromBindUnlocked(string bind)
returns the listener ID from the bind name or throws an exception if not valid
setDynamicHandler(string name, hash< HttpHandlerConfigInfo > info)
sets a dynamic request handler according to the arguments given
const LP_LEVELMASK
const LP_LOGPARAMS
const True
binary bzip2(binary bin, softint level=BZ2_DEFAULT_COMPRESSION)
binary compress(string str, int level=Z_DEFAULT_COMPRESSION)
binary gzip(string str, int level=Z_DEFAULT_COMPRESSION)
string get_ex_pos(hash< auto > ex)
string sprintf(string fmt,...)
string force_encoding(string str, string encoding)
const VersionString
bool boolean(any arg)
hash< auto > hash(object obj)
the main namespace for the HttpServer module
const HttpCodes
const HttpServerVersion
const ReadTimeout
nothing http_set_reply_headers(Socket s, hash< auto > cx, reference< auto > rv, *string server_string)
string get_exception_string(hash< auto > ex)
returns a multi-line string from the exception hash argument suitable for logging or output on the co...
const HttpServerString
Private namespace.
Definition: HttpServer.qm.dox.h:1614
Hash for HttpServer options.
Definition: HttpServer.qm.dox.h:339
*Logger logger
HTTP server logger.
Definition: HttpServer.qm.dox.h:352
bool debug
Debug flag.
Definition: HttpServer.qm.dox.h:344
string name
HTTP server name.
Definition: HttpServer.qm.dox.h:341
hash< auto > hdr
Headers to add in responses.
Definition: HttpServer.qm.dox.h:347