620 hash<auto>
cx, hash<auto>
hdr,
auto body)
622 cx + (exists
body ? {
'deserialized': True} : NOTHING),
hdr,
body);
748 Socket s, *list<string> cl,
string mn, hash<auto> cx, *hash<auto> args) {
749 if (!args && cx.body.typeCode() == NT_HASH);
755 string astr = rh.
maskData(sprintf(
"%y", args));
756 rh.
logDebug(
"REST DBG: class %y: dispatching method %y args: %s",
name(), mn, astr);
761 private hash<HttpServer::HttpHandlerResponseInfo>
dispatchStream(HttpListenerInterface listener,
762 RestHandler rh, Socket s,
string mn, *hash<auto> ah, hash<auto> cx) {
763 string sn =
"stream" + mn[0].upr() + mn.substr(1);
767 rsh = call_object_method_args(self, sn, (cx, ah));
768 }
catch (hash<ExceptionInfo> ex) {
769 if (ex.err ==
"METHOD-DOES-NOT-EXIST" && ex.desc.find(sn) != -1) {
770 if (cx.hdr.
"transfer-encoding" ==
"chunked")
773 return cast<hash<HttpHandlerResponseInfo>>(
dispatch(rh, mn, ah, cx));
775 string desc = !cx.debug
776 ? sprintf(
"%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc)
777 : get_exception_string(ex);
778 rh.
logDebug(
"REST DBG: class %y: stream method %y: %s",
name(), sn, desc);
782 RestStreamRequest req(rsh, listener, rh, s, cx, cx.hdr, cx.hdr.
"content-length" ? cx.body : NOTHING);
784 }
catch (hash<ExceptionInfo> ex) {
787 # issue #4291: handle errors here correctly
790 }
catch (hash<ExceptionInfo> ex1) {
791 rh.
logError(
"cannot register error %s: %s with stream handler: %s: %s", ex.err, ex.desc,
795 string desc = !cx.debug
796 ? sprintf(
"%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc)
797 : get_exception_string(ex);
798 rh.
logDebug(
"REST DBG: class %y: stream method %y: %s",
name(), sn, desc);
828 return cast<hash<HttpHandlerResponseInfo>>(call_object_method_args(self, mn, (cx, ah)));
829 }
catch (hash<ExceptionInfo> ex) {
830 if (ex.err ==
"METHOD-DOES-NOT-EXIST") {
832 string astr = rh.
maskData(sprintf(
"%y", ah));
833 rh.
logError(
"DISPATCH-ERROR: cannot dispatch to unimplemented method %y: class %y, args: %s", mn,
835 # see if alternate methods would work
840 string nmn = mh.value + cx.rest_method;
841 if (self.hasCallableMethod(nmn)) {
842 if (mh.key == cx.hdr.method) {
852 "not implement method %y",
name(), self.className(),
853 cx.orig_method ? cx.orig_method : mn));
855 string ml = foldl $1 +
"," + $2, hl;
856 string desc = sprintf(
"HTTP method %s is unimplemented in REST class %y (%s)", cx.hdr.method,
857 name(), self.className());
859 desc += sprintf(
" REST method %y", cx.orig_method);
860 desc += sprintf(
", available methods: %s", ml);
861 return cast<hash<HttpHandlerResponseInfo>>(
866 string desc = !cx.debug
867 ? sprintf(
"%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc)
868 : get_exception_string(ex);
869 rh.
logDebug(
"REST DBG: class %y: method %y: %s",
name(), mn, desc);
889 hash<HttpServer::HttpHandlerResponseInfo>
unknownSubClassError(
string cls_name, hash<auto> cx, *hash<auto> ah);
927 const Err501 =
new hash<HttpResponseInfo>({
929 "body":
"not implemented",
938 "OPTIONS":
"options",
1039 hash<HttpResponseInfo>
handleRequest(HttpListenerInterface listener, Socket s, hash<auto> cx, hash<auto> hdr,
1047 string path = cx.url.path ??
"";
1049 removeRootPath(\path);
1051 hash<RestRequestServerInfo> req;
1054 string bstr = maskData(b.typeCode() == NT_STRING ? trim(b) : sprintf(
"%y", b));
1055 logDebug(
"REST DBG: body: %s", bstr);
1058 req = validator.
parseRequest(hdr.method, path, body, \hdr);
1059 }
catch (hash<ExceptionInfo> ex) {
1060 logDebug(
"REST DBG: %s", get_exception_string(ex));
1062 if (ex.err ==
"INVALID-ENCODING"
1063 || ex.err ==
"ENCODING-CONVERSION-ERROR"
1064 || ex.err ==
"DESERIALIZATION-ERROR"
1065 || ex.err ==
"SCHEMA-VALIDATION-ERROR")
1066 return AbstractHttpRequestHandler::makeResponse(400, sprintf(
"%s: %s", ex.err, ex.desc),
1067 errorResponseHeaders(cx));
1068 if (ex.err ==
"INVALID-METHOD") {
1069 string ml = (foldl $1 +
"," + $2, ex.arg) ??
"";
1070 return AbstractHttpRequestHandler::makeResponse(405, sprintf(
"HTTP method %y is not supported "
1071 "with URI path %y; supported methods: %s", hdr.method, path, ml ?*
"<none>"),
1072 errorResponseHeaders(cx) + {
"Allow": ml});
1078 hash<auto> ah = path ? (
1080 "params": req.query,
1094 if (body.typeCode() == NT_HASH)
1095 map args.$1 = body.$1, keys body, !exists args.$1;
1097 if (!Methods.(hdr.method))
1098 return AbstractHttpRequestHandler::make400(
"unsupported HTTP method %y given in REST call %y",
1104 else if (body.action);
1106 mn = Methods.(hdr.method);
1109 cx.rest_action_method = mn;
1113 splice ah.method, 0, 1;
1121 *
string astr = hdr.accept;
1126 # it is assumed that the client accepts all media types
1133 cx += {
"hdr": hdr,
"body": body,
"aih": aih};
1136 hash<HttpHandlerResponseInfo> rv;
1139 rv = dispatchRequest(listener, s, cl, mn, path, cx, args);
1140 }
catch (hash<ExceptionInfo> ex) {
1141 string desc = !cx.debug
1142 ? sprintf(
"%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc)
1143 : get_exception_string(ex);
1146 rv = returnRestException(ex);
1149 if (!rv.reply_sent);
1154 return cast<hash<HttpResponseInfo>>(rv);
1166 private hash<HttpHandlerResponseInfo>
dispatchRequest(HttpListenerInterface listener, Socket s,
1167 *list<string> class_list,
string method_name,
string path, hash<auto> cx, *hash<auto> args) {
1171 *
string cls = shift class_list;
1178 # ind an action method instead
1179 if (!class_list && !args.action) {
1180 string mname = sprintf(
"%s%s%s", method_name, cls[0].upr(), cls[1..]);
1181 if (self.hasCallableMethod(mname)) {
1183 method_name = mname;
1188 return unknownSubClassError(cls, cx, args);
1195 return rcls.
handleRequest(listener, self, s, class_list, method_name, cx, args);
1214 hash<auto>
get(hash<auto> cx, *hash<auto> ah);
1248 static hash<HttpHandlerResponseInfo>
make200(
string fmt);
1251 static hash<HttpHandlerResponseInfo>
make200(hash<auto> hdr,
string fmt);
1254 static hash<HttpHandlerResponseInfo>
make400(
string fmt);
1257 static hash<HttpHandlerResponseInfo>
make400(hash<auto> hdr,
string fmt);
1260 static hash<HttpHandlerResponseInfo>
makeResponse(
int code,
auto body, *hash<auto> hdr);
1263 static hash<HttpHandlerResponseInfo>
make500(
string fmt);
1266 static hash<HttpHandlerResponseInfo>
make500(hash<auto> hdr,
string fmt);
1269 static hash<HttpHandlerResponseInfo>
make501(
string fmt);
1272 static hash<HttpHandlerResponseInfo>
make501(hash<auto> hdr,
string fmt);
1279class DummyListenerInterface :
public HttpListenerInterface {
1282 addUserThreadContext(hash<auto> uctx);
1285 auto removeUserThreadContext(*
string k);
1291 logError(
string fmt);
static hash< HttpResponseInfo > makeResponse(hash< auto > hdr, int code, binary body)
*data getMessageBody(Socket s, hash< auto > hdr, *data body, bool decode=True)
string maskData(string msg)
constructor(HttpListenerInterface listener, AbstractHttpRequestHandler handler, Socket s, hash< auto > cx, hash< auto > hdr, auto body)
AbstractHttpRequestHandler handler
hash< HttpHandlerResponseInfo > handleRequest()
HttpListenerInterface listener
the base abstract class for REST handler classes
Definition: RestHandler.qm.dox.h:671
hash< HttpServer::HttpHandlerResponseInfo > unknownSubClassError(string cls_name, hash< auto > cx, *hash< auto > ah)
returns a 404 Not Found response when a request tries to access an unknown subclass
private hash< HttpServer::HttpHandlerResponseInfo > dispatch(RestHandler rh, string mn, *hash< auto > ah, hash< auto > cx)
this method is called to dispatch requests on the given object
Definition: RestHandler.qm.dox.h:825
abstract string name()
this provides the name of the REST class
private hash< HttpServer::HttpHandlerResponseInfo > dispatchStream(HttpListenerInterface listener, RestHandler rh, Socket s, string mn, *hash< auto > ah, hash< auto > cx)
this method is called to dispatch streamed requests on the given object
Definition: RestHandler.qm.dox.h:761
hash< HttpServer::HttpHandlerResponseInfo > handleRequest(HttpListenerInterface listener, RestHandler rh, Socket s, *list< string > cl, string mn, hash< auto > cx, *hash< auto > args)
this method is called by the RestHandler class to match the right object with incoming requests
Definition: RestHandler.qm.dox.h:747
addClass(AbstractRestClass cls)
adds a REST class to the handler
*AbstractRestClass subClassImpl(string name, hash< auto > cx, *hash< auto > args)
this method will be called to find a sub-class (ie with GET /invoices/1 - if this class represents "i...
*hash< string, bool > doGetPossibleSubClasses(hash< auto > cx, *hash< auto > ah)
returns a set of possible subclasses for a particular request
hash< string, AbstractRestClass > class_hash
class hash: name -> AbstractRestClass
Definition: RestHandler.qm.dox.h:675
*AbstractRestClass subClass(string name, hash< auto > cx, *hash< auto > args)
this method will be called to find a sub-class (ie with GET /invoices/1 - if this class represents "i...
const RestBasicMethodSet
set of REST class method names based on basic HTTP methods
Definition: RestHandler.qm.dox.h:678
the base abstract class for REST stream request handlers
Definition: RestHandler.qm.dox.h:480
hash< auto > rhdr
headers to add in the response
Definition: RestHandler.qm.dox.h:496
auto send()
this method provides the callback method for sending chunked data by calling sendImpl()
abstract auto sendImpl()
abstract callback method for sending chunked data
hash< auto > cx
call context hash
Definition: RestHandler.qm.dox.h:487
nothing recv(hash< auto > v)
this method provides the callback method for receiving chunked data by calling recvImpl()
constructor(hash< auto > n_cx, *hash< auto > n_ah)
creates the object with the given arguments
*int getTimeout()
returns the timeout in milliseconds or NOTHING if no timeout is set
*hash< auto > ah
call argument hash
Definition: RestHandler.qm.dox.h:493
hash< HttpServer::HttpHandlerResponseInfo > getResponseHeaderMessage()
this method returns the response message description hash by calling getResponseHeaderMessageImpl()
bool isPersistent()
returns True if the connection is persistent; this method in the base class returns False by default
abstract hash< auto > getResponseHeaderMessageImpl()
this method should return the response message description hash
*int timeout_ms
socket I/O timeout in milliseconds
Definition: RestHandler.qm.dox.h:499
setTimeout(timeout n_timeout_ms)
sets the internal socket I/O timeout value in ms
streamError(hash< auto > n_ex)
registers stream errors in the send operation with the stream handler if no error is already present
*hash< auto > ex
if an exception is raised in a callback then the exception hash is saved here
Definition: RestHandler.qm.dox.h:490
*code getPersistentClosedNotification()
returns a callable value in case a persistent connection is in progress; NOTHING if not; this method ...
abstract nothing recvImpl(hash< auto > v)
abstract callback method for receiving chunked data
auto handleExternalRequest(string method, string path, *hash< auto > body, hash< auto > cx={})
processes REST API calls outside the HTTP server
responseSerializationError(hash< auto > cx, *hash< auto > aih, hash< auto > rv)
default implementation is empty
static hash< HttpHandlerResponseInfo > make500(hash< auto > hdr, string fmt)
creates a hash for an HTTP 500 error response with the response message body as a string
const Methods
supported HTTP methods
Definition: RestHandler.qm.dox.h:933
RestSchemaValidator::AbstractRestSchemaValidator validator
REST schema validator.
Definition: RestHandler.qm.dox.h:947
removeRootPath(reference< string > path)
default implementation is empty
hash< HttpResponseInfo > handleRequest(HttpListenerInterface listener, Socket s, hash< auto > cx, hash< auto > hdr, *data b)
called by the HTTP server to handle incoming HTTP requests
Definition: RestHandler.qm.dox.h:1039
static hash< HttpHandlerResponseInfo > make501(hash< auto > hdr, string fmt)
creates a hash for an HTTP 501 error response with the response message body as a string
static hash< HttpHandlerResponseInfo > make200(hash< auto > hdr, string fmt)
creates a hash for an HTTP 200 OK error response with the response message body as a string
logInfo(string fmt)
This method is called with informational log messages.
checkExceptionSerializable(reference< hash< ExceptionInfo > > ex)
Recursively ensure that exception arguments are serializable.
static hash< HttpHandlerResponseInfo > make501(string fmt)
creates a hash for an HTTP 501 error response with the response message body as a string
private hash< HttpHandlerResponseInfo > dispatchRequest(HttpListenerInterface listener, Socket s, *list< string > class_list, string method_name, string path, hash< auto > cx, *hash< auto > args)
Dispatches the request and returns the response.
Definition: RestHandler.qm.dox.h:1166
static hash< HttpHandlerResponseInfo > makeResponse(int code, auto body, *hash< auto > hdr)
creates a hash for an HTTP response with the response code and a literal response message body
hash< HttpHandlerResponseInfo > returnRestException(hash< ExceptionInfo > ex)
method that determines how exceptions handling REST requests are returned
logError(string fmt)
This method is called with error log messages.
constructor(*HttpServer::AbstractAuthenticator auth, RestSchemaValidator::AbstractRestSchemaValidator validator=new NullRestSchemaValidator())
create the object optionally with the given HttpServer::AbstractAuthenticator
constructor(Logger::Logger logger, *HttpServer::AbstractAuthenticator auth, RestSchemaValidator::AbstractRestSchemaValidator validator=new NullRestSchemaValidator(logger))
create the object optionally with the given Logger and authenticator
static hash< HttpHandlerResponseInfo > make400(hash< auto > hdr, string fmt)
creates a hash for an HTTP 400 error response with the response message body as a string
logDebug(string fmt)
This method is called with debug log messages.
string name()
returns the name of the root REST class
*hash< auto > errorResponseHeaders(hash< auto > cx)
Retrieves headers for an error response.
setLogger(Logger::Logger logger)
Sets a new logger.
hash< auto > get(hash< auto > cx, *hash< auto > ah)
default get handler for the base handler class
static hash< HttpHandlerResponseInfo > make500(string fmt)
creates a hash for an HTTP 500 error response with the response message body as a string
Logger::Logger logger
logger
Definition: RestHandler.qm.dox.h:944
static hash< HttpHandlerResponseInfo > make200(string fmt)
creates a hash for an HTTP 200 OK error response with the response message body as a string
requestDeserializationError(hash< auto > hdr, hash< auto > cx, string body)
default implementation is empty
static hash< HttpHandlerResponseInfo > make400(string fmt)
creates a hash for an HTTP 400 error response with the response message body as a string
the base class for handling HTTP chunked requests and responses within the RestHandler infrastructure
Definition: RestHandler.qm.dox.h:608
nothing recvImpl(hash< auto > v)
callback method for receiving chunked data; this calls RestHandler::AbstractRestStreamRequestHandler:...
hash< HttpServer::HttpHandlerResponseInfo > getResponseHeaderMessageImpl()
constructor(RestHandler::AbstractRestStreamRequestHandler n_stream, HttpServer::HttpListenerInterface listener, HttpServer::AbstractHttpRequestHandler handler, Socket s, hash< auto > cx, hash< auto > hdr, auto body) destructor()
creates the object with the given attributes
auto sendImpl()
callback method for sending chunked data; this calls RestHandler::AbstractRestStreamRequestHandler::s...
hash< RestRequestServerInfo > parseRequest(string method, string path, *data http_body, reference< hash< auto > > headers)
the RestHandler namespace contains all the objects in the RestHandler module
Definition: RestHandler.qm.dox.h:471