Qore HttpServer Module Reference 1.1.1
Loading...
Searching...
No Matches
HttpServer.qm.dox.h
1// -*- mode: c++; indent-tabs-mode: nil -*-
3
4/* HttpServer.qm Copyright (C) 2012 - 2023 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
327
333namespace HttpServer {
335
341 string get_exception_string(hash<auto> ex);
342
343
345public hashdecl HttpServerOptionInfo {
348
350 bool debug = False;
351
353 hash<auto> hdr = {
354 "X-Powered-By": "Qore/" + Qore::VersionString,
355 };;
356
358 *Logger logger;
359};
360
363
364public:
368 const ReadTimeout = HttpServer::ReadTimeout; # recvs timeout after 30 seconds
370 const PollTimeout = 250ms;
371
372 // logging options
375
377 const AIFlags = AI_PASSIVE | AI_ADDRCONFIG;
378
380 const HttpMethods = {
381 "HEAD": True,
382 "POST": True,
383 "PUT": True,
384 "DELETE": True,
385 "GET": True,
386 "OPTIONS": True,
387 "PATCH": True,
388 //"TRACE": True,
389 #"CONNECT": True,
390 };
391
394
396 const ContentEncodings = {
397 "gzip": "gzip",
398 "deflate": "deflate",
399 "bzip2": "bzip2",
400 "x-gzip": "gzip",
401 "x-deflate": "deflate",
402 "x-bzip2": "bzip2",
403 };
404
406 const DefaultIdleThreads = 10;
407
409 const CompressionThreshold = 1024;
410
421 const LLO_RECV_HEADERS = (1 << 0);
422
424 const LLO_RECV_BODY = (1 << 1);
425
427 const LLO_SEND_HEADERS = (1 << 2);
428
430 const LLO_SEND_BODY = (1 << 3);
432
434protected:
435 // for masking HTTP request log msgs
436 *code maskfunc;
437
438 // quit server flag
439 bool exit = False;
440
441 // if True then verbose exception info will be logged
442 bool debug;
443
444 Sequence seqSessions();
445 Sequence seqListeners();
446
447 bool stopped = False;
448
449 // permanent handlers; these handlers are never removed
450 HttpHandlerList handlers();
451
452 // default handler
453 hash<auto> defaultHandler;
454
455 // hash of listeners keyed by listener ID
456 hash<string, HttpListener> listeners;
457
458 // map of bind addresses to listener IDs
459 hash<string, int> smap;
460
461 // map of listener names to listener IDs
462 hash<string, int> nmap;
463
464 // listener Gate
465 Gate lm();
466
467 // running listener counter
468 Counter c();
469
470 // dynamic handlers
471 DynamicHttpHandlerList dhandlers();
472
473 // connection thread pool
474 ThreadPool threadPool(-1, DefaultIdleThreads);
475
476 // other misc response headers
477 hash<auto> hdr;
478
480
482 *string override_encoding;
483
485 string http_server_string;
486
488 hash<string, bool> http_methods = HttpMethods;
489
491 *Logger logger;
492
493public:
495
497
519 deprecated constructor(*code logfunc, *code errlogfunc, bool dbg = False,
520 string name = HttpServer::HttpServerString,
521 hash<auto> hdr = {"X-Powered-By": "Qore/" + Qore::VersionString}) {
522 http_server_string = name;
523 debug = dbg;
524 hdr = {
525 "Server": name,
526 } + hdr;
527
528 logger = new Logger(http_server_string);
529 if (logfunc || errlogfunc);
530 else {
531 logger.setLevel(LoggerLevel::INFO);
532 }
533 }
534
536
538 constructor(hash<HttpServerOptionInfo> opts);
539
540
543
544
547
548
550
552 addHttpMethod(string m);
553
554
557
558 setDefaultTextEncoding(string enc);
559
560
561 string getDefaultTextEncoding();
562
563
565 callListenerStopCallback(code stopc, string name, hash<auto> socket_info);
566
567
569
586 final list<hash<auto>> addListeners(string bind, hash<HttpListenerOptionInfo> info,
587 *reference<hash<string, string>> errmap) {
588 if (bind ==1);
589
590
591 // add node and service
592 (info.node, info.service) = (bind =~ x/(.+):(\w+)/);
593 return addINETListenersIntern(info, \errmap);
594 }
595
597
610 final list<hash<auto>> addListeners(hash<HttpListenerOptionInfo> info, *reference<hash<string, string>> errmap);
611
612
614
621 hash<auto> addListener(int port);
622
623
625
635 hash<auto> addListener(hash<HttpListenerOptionInfo> opts);
636
637
640
641
643
645 hash<string, hash<auto>> getListeners();
646
647
649
666 hash<auto> getListenerInfo(softint id);
667
668
670
692 hash<auto> getListenerInfoName(string name);
693
694
696
707
708
710
721
722
725
726
728
731
732
735
736
738
743 listenerStarted(int id, hash<auto> sinfo);
744
745
746 // only called from the listeners - do not call externally
747 listenerStopped(HttpListener l);
748
749
751
754
755
757
762 stopListener(softstring bind);
763
764
766
771 stopListenerID(softint id);
772
773
775
781
782
784 int getListenerTID(softint id);
785
786
789
790
792 setHandler(string name, hash<HttpHandlerConfigInfo> info);
793
794
796 setHandler(string name, string path, *softlist<softstring> content_types, HttpServer::AbstractHttpRequestHandler obj, *softlist<softstring> special_headers, bool isregex = True);
797
798
800 setDynamicHandler(string name, hash<HttpHandlerConfigInfo> info);
801
802
804 setDynamicHandler(string name, string path, *softlist<softstring> content_types,
805 HttpServer::AbstractHttpRequestHandler obj, *softlist<softstring> special_headers, bool isregex = True) {
806 dhandlers.setHandler(name, <HttpHandlerConfigInfo>{
807 "path": path,
808 "isregex": isregex,
809 "content_types": content_types,
810 "handler": obj,
811 "headers": special_headers,
812 });
813 }
814
817
818
821
822
824 addHandlerToListener(softstring bind, string name, hash<HttpHandlerConfigInfo> info);
825
826
828
834 addHandlerToListenerID(softint id, string name, hash<HttpHandlerConfigInfo> info);
835
836
838
848 addHandlerToListenerID(softint id, string name, string path, *softlist content_type,
849 HttpServer::AbstractHttpRequestHandler obj, *softlist special_headers, bool isregex = True) {
850 addHandlerToListenerID(id, name, <HttpHandlerConfigInfo>{
851 "path": path,
852 "isregex": isregex,
853 "content_types": content_type,
854 "handler": obj,
855 "headers": special_headers,
856 });
857 }
858
860 addHandlerToListener(softstring bind, string name, HttpServer::AbstractUrlHandler obj);
861
862
864
871
872
874
880 bool removeHandlerFromListenerID(softstring id, string handler_name);
881
882
884
891
892
894
904 setListenerLogOptions(softstring bind, softint code);
905
906
908
920 setListenerLogOptionsID(softint id, softint code);
921
922
924
929 int getListenerLogOptions(softstring bind);
930
931
933
938 int getListenerLogOptionsID(softint id);
939
940
942
945
946
948
950 removeDynamicHandler(string name, *bool force_immediate);
951
952
954 log(string fmt);
955
956
958 logError(string fmt);
959
960
962 sendHttpError(HttpListener listener, hash<auto> cx, Socket s, int code, *data msg, *InputStream chunked_msg, *hash<auto> extra_hdrs, *string encoding, bool head);
963
964
966
970 string maskData(string msg);
971
972
974
985 setMaskCode(code maskfunc);
986
987
989
994 static string getURLFromBind(softstring bind, *string host);
995
997 setDebug(bool dbg = True);
998
999
1001 bool getDebug();
1002
1003
1004 startConnection(code c);
1005
1006
1008
1010protected:
1012public:
1013
1014
1016protected:
1017 setListenerLogOptionsUnlocked(softstring id, int code);
1018public:
1019
1020
1022protected:
1024public:
1025
1026
1028 static nothing setReplyHeaders(Socket s, hash<auto> cx, reference<hash<auto>> rv);
1029
1031 // don't reimplement this method; fix/enhance it in the module
1032 final private HttpListener addListenerIntern(*string node, *softstring service, *Qore::SSLCertificate cert,
1033 *Qore::SSLPrivateKey key, *hash<string, hash<HttpHandlerConfigInfo>> handler_info, *Logger logger,
1034 *code stopc, *string name, int family = AF_UNSPEC) {
1035 return addListenerIntern(<HttpListenerOptionInfo>{
1036 "node": node,
1037 "service": service,
1038 "cert": cert,
1039 "key": key,
1040 "handler_info": handler_info,
1041 "logger": logger,
1042 "stopc": stopc,
1043 "family": family,
1044 });
1045 }
1046
1047protected:
1048 final HttpListener addListenerIntern(hash<HttpListenerOptionInfo> info);
1049public:
1050
1051
1052 // don't reimplement this method; fix/enhance it in the module
1053 final private list<hash<auto>> addINETListenersIntern(hash<HttpListenerOptionInfo> info,
1054 *reference<hash<string, string>> errmap) {
1055 list<auto> al = getaddrinfo(info.node, info.service, info.family, AIFlags);
1056
1057 // sort ipv6 addresses first in list
1058 al = sort(al, int (hash<auto> l, hash<auto> r) {
1059 return l.family === AF_INET6 ? -1 : 0;
1060 });
1061
1062 list<hash<auto>> rv = ();
1063 foreach hash<auto> h in (al);
1064
1065
1066 return rv;
1067 }
1068
1069 // don't reimplement this method; fix/enhance it in the module
1070protected:
1071 final hash<HttpResponseInfo> noHandlerError(hash<auto> cx, hash<auto> hdr, auto body);
1072public:
1073
1074
1075 // handles an incoming request - do not call externally; this method is called by the listeners when a request is received
1076 // don't reimplement this method; fix/enhance it in the module
1077 final handleRequest(HttpListener listener, Socket s, reference<hash<auto>> cx, hash<auto> hdr, bool head = False,
1078 HttpPersistentHandlerInfo phi, hash<auto> info, reference<bool> dedicated) {
1079 if (!http_methods{hdr.method});
1080
1081
1082 // get first supported encoding for response
1083 foreach string enc in (cx.'header-info'.'accept-encoding');
1084
1085
1086 // convert "accept-encoding" to a hash
1087 cx."header-info"."accept-encoding" = map {$1: True}, cx."header-info"."accept-encoding";
1088
1089 // erase the encoding string on exit
1090 on_exit remove cx.encoding;
1091
1092 hash url = {
1093 "path": hdr.path,
1094 };
1095
1096 // add logging functions and url
1097 cx += {
1098 "logger": logger,
1099 "url": url,
1100 "debug": debug,
1101 };
1102
1104 // 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
1105 DynamicHandlerHelper dhh;
1106
1107 if (phi.handler);
1108 else {
1109 // find a handler for the request
1110 *HandlerInfo hi;
1111 int score = 0;
1112 string root_path;
1113 if (listener.hasHandlers()) {
1114 hi = listener.findHandler(hdr, \score, True, \root_path);
1115 if (!hi) {
1116 if (listener.defaultHandler)
1117 hi = listener.defaultHandler;
1118 else {
1119 sendHttpError(listener, cx, s, 404, "Not found", NOTHING, NOTHING,
1120 cx."response-encoding", head);
1121 return;
1122 }
1123 }
1124 } else {
1125 hi = handlers.findHandler(hdr, \score, False, \root_path);
1126 if (score < 3) {
1127 *HandlerInfo dhi = dhandlers.findHandler(hdr, \score, \dhh, \root_path);
1128 if (dhi)
1129 hi = dhi;
1130 }
1131 }
1132
1133 if (hi) {
1134 handler = hi.obj;
1135 cx.handler_name = hi.name;
1136 if (root_path)
1137 cx.root_path = root_path;
1138 }
1139 }
1140
1141 try {
1142 if (!handler && defaultHandler) {
1143 handler = defaultHandler.obj;
1144 cx.handler_name = defaultHandler.name;
1145 }
1146
1147 if (handler && hdr.expect) {
1148 if (hdr.expect == "100-continue") {
1149 hash<HttpResponseInfo> resp = handler.handleExpectHeader(cx, hdr);
1150 sendReply(listener, s, handler, resp, \cx, hdr, head, \dedicated);
1151 } else {
1152 string etxt = sprintf("unrecognized 'Expect' header: %y", hdr.expect);
1153 string str = sprintf("%s: received from %s via %s (header: %y)", etxt,
1154 cx."peer-info".address_desc, cx."socket-info".address_desc, hdr);
1155 listener.logError(str);
1156 cx.close = True;
1157 sendHttpError(listener, cx, s, 400, etxt, NOTHING, NOTHING, cx."response-encoding",
1158 hdr.method == "HEAD");
1159 return;
1160 }
1161 }
1162
1163 *data body;
1164 // if we need to get a body
1165 if (hdr."content-length") {
1166 try {
1167 if (hdr."content-encoding" || hdr."content-type" == MimeTypeOctetStream)
1168 body = s.recvBinary(hdr."content-length", HttpServer::ReadTimeout);
1169 else
1170 body = s.recv(hdr."content-length", HttpServer::ReadTimeout);
1171 #printf("HTTP DEBUG: %s\n", body);
1172 } catch (hash<ExceptionInfo> ex) {
1173 string etxt = sprintf("error reading body in %s (Content-Length: %d): %s: %s", hdr.method,
1174 hdr."content-length", ex.err, ex.desc);
1175 string str = sprintf("%s: received from %s via %s (header: %y)", etxt,
1176 cx."peer-info".address_desc, cx."socket-info".address_desc, hdr);
1177 listener.logError(str);
1178 cx.close = True;
1179 sendHttpError(listener, cx, s, 400, etxt, NOTHING, NOTHING, cx."response-encoding",
1180 hdr.method == "HEAD");
1181 return;
1182 }
1183 }
1184
1185 # override assumed "iso-8859-1" character encoding if necessary
1186 if (body.typeCode() == NT_STRING && override_encoding && hdr."_qore_orig_content_type" !~ /charset=/) {
1187 body = force_encoding(body, override_encoding);
1188 }
1189
1190 listener.doLogRecvBody(body, cx, hdr, info);
1191
1192 hash<HttpResponseInfo> rv;
1193
1194 #printf("HTTP DEBUG: B: handler: %y strict-bool-eval: %y\n", cx.handler_name, get_parse_options() & PO_STRICT_BOOLEAN_EVAL);
1195 #printf("HTTP DEBUG: B: handler: %y: context: %y, hdr: %y, body: %y\n", cx.handler_name, cx, hdr, body);
1196 #printf("HTTP DEBUG: BEFORE handler=%s", dbg_node_info(handlers.(cx.handler_name)));
1197
1198 #printf("HTTP DEBUG: A: cid: %d handler: %y auth: %y\n", cx.id, cx.handler_name, boolean(handler.auth));
1199
1200 if (!handler) {
1201 cx.handler_name = "error";
1202 rv = noHandlerError(cx, hdr, body);
1203 } else {
1204 # check for authentication info
1205 #printf("HTTP DEBUG: handler: %y (auth: %y) hdr: %y\n", cx.handler_name, exists handler.auth && handler.auth.requiresAuthentication(), hdr);
1206
1207 if (handler.auth) {
1208 try {
1209 *hash<auto> ah = handler.auth.authenticateRequest(listener, hdr, \cx);
1210 # return an error if authentication failed
1211 if (ah) {
1212 if (!ah.code)
1213 ah.code = 401;
1214 if (!ah.body && !ah.hasKeyValue("chunked_body"))
1215 ah.body = "Authentication is required to access this server";
1216 cx.close = True;
1217 sendHttpError(listener, cx, s, ah.code, ah.body,
1218 ah.hasKey("chunked_body") ? ah.chunked_body : NOTHING, ah.hdr, cx."response-encoding",
1219 head);
1220 return;
1221 }
1222 } catch (hash<ExceptionInfo> ex) {
1223 # log the error
1224 string desc = !debug
1225 ? sprintf("%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc)
1227 desc += sprintf(": received from %s", cx."peer-info".address_desc);
1228 listener.logError("%s", desc);
1229 cx.close = True;
1230 sendHttpError(listener, cx, s, 501, "Server Authentication Error", NOTHING, NOTHING, NOTHING,
1231 head);
1232 return;
1233 }
1234 #listener.log("authenticated with user %y", cx.user);
1235 }
1236
1237 *hash<auto> tld_save = handler.saveThreadLocalData();
1238 on_exit handler.restoreThreadLocalData(tld_save);
1239
1240 # decode body if applicable
1241 if (body && hdr."content-encoding" && handler.decompress)
1242 body = AbstractHttpRequestHandler::decodeBody(hdr."content-encoding", body,
1243 handler.decompress_to_string ? s.getEncoding() : NOTHING);
1244
1245 rv = cast<hash<HttpResponseInfo>>(handler.handleRequest(listener, s, cx, hdr, body));
1246
1247 # manage persistent handler info, if applicable
1248 if (!phi.handler) {
1249 if (handler.isPersistent())
1250 phi.assign(dhh, handler);
1251 } else if (!handler.isPersistent())
1252 phi.clear();
1253
1254 if (rv.reply_sent) {
1255 return;
1256 }
1257 }
1258
1259 sendReply(listener, s, handler, rv, \cx, hdr, head, \dedicated);
1260 } catch (hash<ExceptionInfo> ex) {
1261 string desc = !debug
1262 ? sprintf("%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc)
1264
1265 string str = sprintf("handler: %s: %s", cx.handler_name, desc);
1266
1267 if (ex.err == "ENCODING-CONVERSION-ERROR") {
1268 s.setEncoding("utf8");
1269 cx."response-encoding" = "utf8";
1270 }
1271
1272 cx.close = True;
1273
1274 # if there is a pending chunked body that has not yet been read on exit, then read it and discard before sending any response
1275 on_exit if (s.pendingHttpChunkedBody())
1276 s.readHTTPChunkedBodyBinary(HttpServer::ReadTimeout);
1277
1278 sendHttpError(listener, cx, s, 500, str, NOTHING, NOTHING, cx."response-encoding", head);
1279 logError(str);
1280 }
1281 }
1282
1283 // sends a reply to a request
1284 // don't reimplement this method; fix/enhance it in the module
1285 final sendReply(HttpListener listener, Socket s, *HttpServer::AbstractHttpRequestHandler handler, hash<auto> rv,
1286 reference<hash> cx, hash<auto> hdr, bool head, reference<bool> dedicated) {
1287 if (exists rv.close)
1288 cx.close = boolean(rv.close);
1289
1290 rv.close = cx.close;
1291
1292 // if there is a pending chunked body that has not yet been read on exit, then read it and discard before sending any response
1293 on_exit if (s.pendingHttpChunkedBody())
1294 s.readHTTPChunkedBodyBinary(HttpServer::ReadTimeout);
1295
1296 if (!HttpCodes.(rv.code));
1297
1298
1299 //printf("HTTP DEBUG: handler %s returned: %n\n", cx.handler_name, rv);
1300
1301 // currently enforced by the handlers:
1302 // RFC 2616 section 4.3: http://tools.ietf.org/html/rfc2616#section-4.3
1303 // All 1xx (informational), 204 (no content), and 304 (not modified) responses
1304 // MUST NOT include a message-body. All other responses do include a
1305 // message-body, although it MAY be of zero length.
1306
1307 rv.hdr = self.hdr + rv.hdr;
1308
1309 if ((exists rv.body || (bool chunked_body = rv.hasKeyValue("chunked_body")))
1310 && (head
1311 || (rv.code < 200
1312 || (rv.code == 204
1313 || rv.code == 304)))) {
1314 remove rv.body;
1315 if (chunked_body);
1316
1317 }
1318
1320
1321 if (rv.code >= 300 && rv.code < 400);
1322 else if (rv.code >= 400);
1323 else {
1324 //printf("**** RESPONSE: %d ct: %s (te: %y) encoding: %y: %N\n", rv.code, rv.hdr."Content-Type",
1325 # rv.hdr."Transfer-Encoding", cx.encoding, rv.body);
1326
1327 # compress body if eligible for compression
1328 if (rv.body && !rv.hdr."Content-Encoding" && rv.body.size() > CompressionThreshold) {
1329 if (cx.encoding == "deflate") {
1330 rv.hdr."Content-Encoding" = "deflate";
1331 rv.body = compress(rv.body);
1332 } else if (cx.encoding == "gzip") {
1333 rv.hdr."Content-Encoding" = "gzip";
1334 rv.body = gzip(rv.body);
1335 } else if (cx.encoding == "bzip2") {
1336 rv.hdr."Content-Encoding" = "bzip2";
1337 rv.body = bzip2(rv.body);
1338 }
1339 }
1340
1341 doResponse(listener, s, cx, rv);
1342 }
1343
1344 if (rv.log)
1345 listener.log("cid %d: %s (from %s): %s", cx.id, cx.handler_name, cx."peer-info".address_desc, rv.log);
1346 if (rv.errlog)
1347 listener.logError("cid %d: %s (from %s): %s", cx.id, cx.handler_name, cx."peer-info".address_desc,
1348 rv.errlog);
1349
1350 if (!rv.close && rv.user_state);
1351
1352
1353 if (rv.code == 101);
1354
1355 }
1356
1357 // rv is "hash" to strip types
1358private:
1359 doResponse(HttpListener listener, Socket s, hash<auto> cx, hash rv);
1360public:
1361
1363};
1364
1365class HttpPersistentHandlerInfo {
1366
1367public:
1368 *DynamicHandlerHelper dhh;
1370
1371 destructor();
1372
1373
1374 assign(*DynamicHandlerHelper n_dhh, HttpServer::AbstractHttpRequestHandler n_handler);
1375
1376
1377 clear();
1378
1379};
1380
1382
1385
1386public:
1387protected:
1388 HttpServer serv;
1389 Sequence ss;
1390
1391 *hash<HttpListenerOptionInfo> opts;
1392
1393 //*SSLCertificate cert;
1394 //*SSLPrivateKey key;
1395 bool ssl = False;
1396 auto socket;
1397 hash socket_info;
1398
1399 // connection counter for normal connection threads
1400 Counter cThreads();
1401 // connection counter for dedicated socket threads
1402 Counter dThreads();
1403
1404 bool exit = False;
1405 bool stopped = False;
1406 int id;
1407
1408 // socket handler hash
1409 hash<string, AbstractHttpSocketHandlerInterface> shh;
1410
1411 // mutex
1412 Mutex m();
1413
1415 *Logger logger;
1416
1417 // stop notification closure
1418 *code stopc;
1419
1420 string name;
1421
1422 // log recv headers flag
1423 bool log_recv_headers = False;
1424
1425 // log recv body flag
1426 bool log_recv_body = False;
1427
1428 // log send headers flag
1429 bool log_send_headers = False;
1430
1431 // log send body flag
1432 bool log_send_body = False;
1433
1435 bool get_remote_certs = False;
1436
1438 int ssl_verify_flags = SSL_VERIFY_NONE;
1439
1442
1443 const PollInterval = 250ms;
1444 const ListenQueue = 100;
1445 const BodyLogLimit = 40;
1446
1447 // handler ref count
1448 int handler_ref_cnt = 0;
1449
1450 // listener-specific handlers
1451 HttpHandlerList handlers();
1452
1455 SSL_VERIFY_PEER: "SSL_VERIFY_PEER",
1456 SSL_VERIFY_FAIL_IF_NO_PEER_CERT: "SSL_VERIFY_FAIL_IF_NO_PEER_CERT",
1457 SSL_VERIFY_CLIENT_ONCE: "SSL_VERIFY_CLIENT_ONCE",
1458 };
1459
1460public:
1461
1462 // TID of the background listener thread
1463 int tid;
1464
1465 // default handler info
1466 *HandlerInfo defaultHandler;
1467
1469
1476 constructor(HttpServer server, string name, int id, Sequence ss, *hash<HttpListenerOptionInfo> opts);
1477
1478
1480
1488
1489
1491 addHandlers(hash<string, hash<HttpHandlerConfigInfo>> handler_info);
1492
1493
1497 removeHandler(string handler_name);
1498
1499
1504
1505
1506 setDefaultHandler(string name);
1507
1508
1509 *HandlerInfo findHandler(hash<auto> hdr, reference<int> score, bool finalv = False, *reference<string> root_path);
1510
1511
1512 bool hasHandlers();
1513
1514
1516
1520
1521 hash getListenerSocketInfo();
1522
1523
1524 copy();
1525
1526
1527 destructor();
1528
1529
1530 logRecvHeaders(softbool flag = True);
1531
1532
1533 logRecvBody(softbool flag = True);
1534
1535
1536 logSendHeaders(softbool flag = True);
1537
1538
1539 logSendBody(softbool flag = True);
1540
1541
1542 doLogRecvBody(*data body, hash<auto> cx, hash<auto> hdr, hash<auto> info);
1543
1544
1545 hash<auto> getLogOptions();
1546
1547
1548 string getName();
1549
1550
1551 auto getAddress();
1552
1553
1554 int getID();
1555
1556
1557 bool isSecure();
1558
1559
1560 hash<auto> getInfo();
1561
1562
1563protected:
1564 setupSslIntern();
1565public:
1566
1567
1568protected:
1569 softlist<string> getSslVerifyModeList();
1570public:
1571
1572
1573 auto removeUserThreadContext(*string k);
1574
1575
1576 addUserThreadContext(hash<auto> uctx);
1577
1578
1579 stopNoWait();
1580
1581
1582 stop();
1583
1584
1585 logResponse(hash<auto> cx, int code, *data body, *hash<auto> hdr);
1586
1587
1588 logResponse(hash<auto> cx, int code, InputStream body, *hash<auto> hdr);
1589
1590
1591 logResponse(hash<auto> cx, hash<auto> rv);
1592
1593
1594 log(string fmt);
1595
1596
1597 logError(string fmt);
1598
1599
1600protected:
1601 mainThread();
1602public:
1603
1604
1605 // thread for handling communication per connection
1606protected:
1607 connectionThread(Socket s);
1608public:
1609
1610
1611 bool registerDedicatedSocket(softstring id, HttpServer::AbstractHttpSocketHandlerInterface h, reference<bool> dedicated);
1612
1613
1614 removeDedicatedSocket(softstring id, HttpServer::AbstractHttpSocketHandlerInterface h);
1615
1616};
1617}
1618
1620namespace Priv {
1621// class containing handler info
1622class HandlerInfo {
1623
1624public:
1625 string name;
1627 string path;
1628 bool isregex;
1629 // content type map
1630 *hash<string, bool> content_type_map;
1631 *list<string> special_header_list;
1632
1634
1636 constructor(string name, hash<HttpHandlerConfigInfo> info);
1637
1638
1639 bool matchContentType(string ct);
1640
1641
1643 int matchRequest(hash<auto> hdr, int score);
1644
1645
1646 stop();
1647
1648};
1649
1650// class to implement handler-handling (private)
1651class HttpHandlerList {
1652
1653public:
1654 hash<string, HandlerInfo> handlers;
1655 TreeMap treeMap();
1656
1657 /*
1658 private static checkSpecialHeaders(reference sh) {
1659 foreach auto h in (\sh) {
1660 if (h.typeCode() != NT_STRING)
1661 throw "SETHANDLER-ERROR", sprintf("entry %d in the special header list is not a string; type: %s", $#, h.type());
1662 # make sure header is in lower case for matching
1663 h = tolower(h);
1664 }
1665 }
1666 */
1667
1669 setHandler(string name, hash<HttpHandlerConfigInfo> info);
1670
1671
1673
1676 removeHandler(string handler_name);
1677
1678
1680
1683 removeHandler(HttpServer::AbstractHttpRequestHandler handler);
1684
1685
1686 // matches a handler to the request
1687 *HandlerInfo findHandler(hash<auto> hdr, reference<int> score, bool finalv = False, *reference<string> root_path);
1688
1689
1690 bool empty();
1691
1692
1693 int size();
1694
1695};
1696
1697// class containing dynamic handler info
1698class DynamicHandlerInfo : public HandlerInfo {
1699
1700public:
1701 Counter counter();
1702
1703 constructor(string name, hash<HttpHandlerConfigInfo> info) ;
1704
1705};
1706
1707// maintains the request count for dynamic handlers (private)
1708class DynamicHandlerHelper {
1709
1710public:
1711
1712
1713protected:
1714 Counter c;
1715
1716public:
1717
1718 constructor(Counter n_c);
1719
1720
1721 destructor();
1722
1723};
1724
1725// for dynamic handler-handling (private)
1726class DynamicHttpHandlerList : public HttpHandlerList {
1727
1728public:
1729protected:
1730 RWLock dhl();
1731
1732 // issue #4173: allow dynamic handlers to be disabled before being removed
1733 hash<string, bool> disable_map;
1734
1735public:
1736
1738 setHandler(string name, hash<HttpHandlerConfigInfo> info);
1739
1740
1742
1744 removeHandler(string name, *bool force_immediate);
1745
1746
1748
1750 disableHandler(string name);
1751
1752
1753 *DynamicHandlerInfo findHandler(hash<auto> hdr, reference<int> score, reference<DynamicHandlerHelper> dhh, *reference<string> root_path);
1754
1755};
1756
1757class HttpServerCallbackAppender : public LoggerAppenderWithLayout {
1758
1759public:
1760protected:
1761 code logfunc;
1762
1763public:
1764
1765 constructor(code logfunc) ;
1766
1767
1768 processEventImpl(int type, auto params);
1769
1770};
1771};
*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:1384
removeHandler(HttpServer::AbstractHttpRequestHandler handler)
const SslVerifyMap
map for converting ssl verify flags to strings
Definition: HttpServer.qm.dox.h:1454
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:1435
int ssl_verify_flags
SSL verify flags.
Definition: HttpServer.qm.dox.h:1438
bool ssl_accept_all_certs
accept all certificates
Definition: HttpServer.qm.dox.h:1441
*Logger logger
listener-specific logger; if not present, then the server's logger will be used
Definition: HttpServer.qm.dox.h:1415
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:368
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:586
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:519
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:393
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:848
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:804
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:1620
Hash for HttpServer options.
Definition: HttpServer.qm.dox.h:345
*Logger logger
HTTP server logger.
Definition: HttpServer.qm.dox.h:358
bool debug
Debug flag.
Definition: HttpServer.qm.dox.h:350
string name
HTTP server name.
Definition: HttpServer.qm.dox.h:347
hash< auto > hdr
Headers to add in responses.
Definition: HttpServer.qm.dox.h:353