364 const PollTimeout = 250ms;
371 const AIFlags = AI_PASSIVE | AI_ADDRCONFIG;
374 const HttpMethods = {
390 const ContentEncodings = {
392 "deflate":
"deflate",
395 "x-deflate":
"deflate",
400 const DefaultIdleThreads = 10;
403 const CompressionThreshold = 1024;
415 const LLO_RECV_HEADERS = (1 << 0);
418 const LLO_RECV_BODY = (1 << 1);
421 const LLO_SEND_HEADERS = (1 << 2);
424 const LLO_SEND_BODY = (1 << 3);
438 Sequence seqSessions();
439 Sequence seqListeners();
441 bool stopped = False;
444 HttpHandlerList handlers();
447 hash<auto> defaultHandler;
450 hash<string, HttpListener> listeners;
453 hash<string, int> smap;
456 hash<string, int> nmap;
465 DynamicHttpHandlerList dhandlers();
468 ThreadPool threadPool(-1, DefaultIdleThreads);
476 *
string override_encoding;
479 string http_server_string;
482 hash<string, bool> http_methods = HttpMethods;
513 deprecated
constructor(*code logfunc, *code errlogfunc,
bool dbg = False,
516 http_server_string = name;
522 logger =
new Logger(http_server_string);
523 if (logfunc || errlogfunc);
525 logger.setLevel(LoggerLevel::INFO);
552 setDefaultTextEncoding(
string enc);
555 string getDefaultTextEncoding();
580 final list<hash<auto>>
addListeners(
string bind, hash<HttpListenerOptionInfo> info,
581 *reference<hash<string, string>> errmap) {
586 (info.node, info.service) = (bind =~ x/(.+):(\w+)/);
587 return addINETListenersIntern(info, \errmap);
604 final list<hash<auto>>
addListeners(hash<HttpListenerOptionInfo> info, *reference<hash<string, string>> errmap);
803 "content_types": content_types,
805 "headers": special_headers,
847 "content_types": content_type,
849 "headers": special_headers,
956 sendHttpError(
HttpListener listener, hash<auto> cx, Socket s,
int code, *data msg, *InputStream chunked_msg, *hash<auto> extra_hdrs, *
string encoding,
bool head);
998 startConnection(code c);
1027 *
Qore::SSLPrivateKey key, *hash<
string, hash<HttpHandlerConfigInfo>> handler_info, *Logger logger,
1028 *code stopc, *
string name,
int family = AF_UNSPEC) {
1034 "handler_info": handler_info,
1042 final HttpListener addListenerIntern(hash<HttpListenerOptionInfo> info);
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);
1052 al = sort(al,
int (hash<auto> l, hash<auto> r) {
1053 return l.family === AF_INET6 ? -1 : 0;
1056 list<hash<auto>> rv = ();
1057 foreach hash<auto> h in (al);
1065 final hash<HttpResponseInfo> noHandlerError(hash<auto> cx, hash<auto> hdr,
auto body);
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});
1077 foreach string enc in (cx.
'header-info'.
'accept-encoding');
1081 cx.
"header-info".
"accept-encoding" = map {$1:
True}, cx.
"header-info".
"accept-encoding";
1084 on_exit remove cx.encoding;
1099 DynamicHandlerHelper dhh;
1107 if (listener.hasHandlers()) {
1108 hi = listener.findHandler(hdr, \score, True, \root_path);
1110 if (listener.defaultHandler)
1111 hi = listener.defaultHandler;
1113 sendHttpError(listener, cx, s, 404,
"Not found", NOTHING, NOTHING,
1114 cx.
"response-encoding", head);
1119 hi = handlers.findHandler(hdr, \score, False, \root_path);
1121 *HandlerInfo dhi = dhandlers.findHandler(hdr, \score, \dhh, \root_path);
1129 cx.handler_name = hi.name;
1131 cx.root_path = root_path;
1136 if (!handler && defaultHandler) {
1137 handler = defaultHandler.obj;
1138 cx.handler_name = defaultHandler.name;
1141 if (handler && hdr.expect) {
1142 if (hdr.expect ==
"100-continue") {
1144 sendReply(listener, s, handler, resp, \cx, hdr, head, \dedicated);
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);
1151 sendHttpError(listener, cx, s, 400, etxt, NOTHING, NOTHING, cx.
"response-encoding",
1152 hdr.method ==
"HEAD");
1159 if (hdr.
"content-length") {
1161 if (hdr.
"content-encoding" || hdr.
"content-type" == MimeTypeOctetStream)
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);
1173 sendHttpError(listener, cx, s, 400, etxt, NOTHING, NOTHING, cx.
"response-encoding",
1174 hdr.method ==
"HEAD");
1179 # override assumed "iso-8859-1" character encoding if necessary
1180 if (body.typeCode() == NT_STRING && override_encoding && hdr.
"_qore_orig_content_type" !~ /charset=/) {
1184 listener.doLogRecvBody(body, cx, hdr, info);
1186 hash<HttpResponseInfo> rv;
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)));
1192 #printf("HTTP DEBUG: A: cid: %d handler: %y auth: %y\n", cx.id, cx.handler_name, boolean(handler.auth));
1195 cx.handler_name =
"error";
1196 rv = noHandlerError(cx, hdr, body);
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);
1204 # return an error if authentication failed
1208 if (!ah.body && !ah.hasKeyValue(
"chunked_body"))
1209 ah.body =
"Authentication is required to access this server";
1211 sendHttpError(listener, cx, s, ah.code, ah.body,
1212 ah.hasKey(
"chunked_body") ? ah.chunked_body : NOTHING, ah.hdr, cx.
"response-encoding",
1216 }
catch (hash<ExceptionInfo> ex) {
1218 string desc = !debug
1221 desc +=
sprintf(
": received from %s", cx.
"peer-info".address_desc);
1222 listener.logError(
"%s", desc);
1224 sendHttpError(listener, cx, s, 501,
"Server Authentication Error", NOTHING, NOTHING, NOTHING,
1228 #listener.log("authenticated with user %y", cx.user);
1234 # decode body if applicable
1235 if (body && hdr.
"content-encoding" && handler.
decompress)
1239 rv = cast<hash<HttpResponseInfo>>(handler.
handleRequest(listener, s, cx, hdr, body));
1241 # manage persistent handler info, if applicable
1244 phi.assign(dhh, handler);
1248 if (rv.reply_sent) {
1253 sendReply(listener, s, handler, rv, \cx, hdr, head, \dedicated);
1254 }
catch (hash<ExceptionInfo> ex) {
1255 string desc = !debug
1259 string str =
sprintf(
"handler: %s: %s", cx.handler_name, desc);
1261 if (ex.err ==
"ENCODING-CONVERSION-ERROR") {
1262 s.setEncoding(
"utf8");
1263 cx.
"response-encoding" =
"utf8";
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())
1272 sendHttpError(listener, cx, s, 500, str, NOTHING, NOTHING, cx.
"response-encoding", head);
1280 reference<hash> cx, hash<auto> hdr,
bool head, reference<bool> dedicated) {
1281 if (exists rv.close)
1284 rv.close = cx.close;
1287 on_exit
if (s.pendingHttpChunkedBody())
1301 rv.hdr = self.hdr + rv.hdr;
1303 if ((exists rv.body || (
bool chunked_body = rv.hasKeyValue(
"chunked_body")))
1307 || rv.code == 304)))) {
1315 if (rv.code >= 300 && rv.code < 400);
1316 else if (rv.code >= 400);
1319 # rv.hdr."Transfer-Encoding", cx.encoding, rv.body);
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";
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);
1335 doResponse(listener, s, cx, rv);
1339 listener.log(
"cid %d: %s (from %s): %s", cx.id, cx.handler_name, cx.
"peer-info".address_desc, rv.log);
1341 listener.logError(
"cid %d: %s (from %s): %s", cx.id, cx.handler_name, cx.
"peer-info".address_desc,
1344 if (!rv.close && rv.user_state);
1347 if (rv.code == 101);
1353 doResponse(HttpListener listener, Socket s, hash<auto> cx, hash rv);
1359class HttpPersistentHandlerInfo {
1362 *DynamicHandlerHelper dhh;
1385 *hash<HttpListenerOptionInfo> opts;
1399 bool stopped = False;
1403 hash<string, AbstractHttpSocketHandlerInterface> shh;
1417 bool log_recv_headers = False;
1420 bool log_recv_body = False;
1423 bool log_send_headers = False;
1426 bool log_send_body = False;
1437 const PollInterval = 250ms;
1438 const ListenQueue = 100;
1439 const BodyLogLimit = 40;
1442 int handler_ref_cnt = 0;
1445 HttpHandlerList handlers();
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",
1460 *HandlerInfo defaultHandler;
1500 setDefaultHandler(
string name);
1503 *HandlerInfo findHandler(hash<auto> hdr, reference<int> score,
bool finalv = False, *reference<string> root_path);
1515 hash getListenerSocketInfo();
1524 logRecvHeaders(softbool flag = True);
1527 logRecvBody(softbool flag = True);
1530 logSendHeaders(softbool flag = True);
1533 logSendBody(softbool flag = True);
1536 doLogRecvBody(*data body, hash<auto> cx, hash<auto> hdr, hash<auto> info);
1539 hash<auto> getLogOptions();
1554 hash<auto> getInfo();
1563 softlist<string> getSslVerifyModeList();
1567 auto removeUserThreadContext(*
string k);
1570 addUserThreadContext(hash<auto> uctx);
1579 logResponse(hash<auto> cx,
int code, *data body, *hash<auto> hdr);
1582 logResponse(hash<auto> cx,
int code, InputStream body, *hash<auto> hdr);
1585 logResponse(hash<auto> cx, hash<auto> rv);
1591 logError(
string fmt);
1601 connectionThread(Socket s);
1624 *hash<string, bool> content_type_map;
1625 *list<string> special_header_list;
1630 constructor(
string name, hash<HttpHandlerConfigInfo> info);
1633 bool matchContentType(
string ct);
1637 int matchRequest(hash<auto> hdr,
int score);
1645class HttpHandlerList {
1648 hash<string, HandlerInfo> handlers;
1663 setHandler(
string name, hash<HttpHandlerConfigInfo> info);
1670 removeHandler(
string handler_name);
1681 *HandlerInfo findHandler(hash<auto> hdr, reference<int> score,
bool finalv = False, *reference<string> root_path);
1692class DynamicHandlerInfo :
public HandlerInfo {
1697 constructor(
string name, hash<HttpHandlerConfigInfo> info) ;
1702class DynamicHandlerHelper {
1712 constructor(Counter n_c);
1720class DynamicHttpHandlerList :
public HttpHandlerList {
1727 hash<string, bool> disable_map;
1732 setHandler(
string name, hash<HttpHandlerConfigInfo> info);
1738 removeHandler(
string name, *
bool force_immediate);
1744 disableHandler(
string name);
1747 *DynamicHandlerInfo findHandler(hash<auto> hdr, reference<int> score, reference<DynamicHandlerHelper> dhh, *reference<string> root_path);
1751class HttpServerCallbackAppender :
public LoggerAppenderWithLayout {
1759 constructor(code logfunc) ;
1762 processEventImpl(
int type,
auto params);
*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)
bool decompress_to_string
static data decodeBody(string content_encoding, binary body, *string string_encoding)
*AbstractAuthenticator auth
*hash< auto > saveThreadLocalData()
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
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)
hash< auto > hash(object obj)
the main namespace for the HttpServer module
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...
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