+void afb_xreq_reply_unknown_api(struct afb_xreq *xreq)
+{
+ afb_xreq_reply_f(xreq, NULL, "unknown-api", "api %s not found (for verb %s)", xreq->request.called_api, xreq->request.called_verb);
+}
+
+void afb_xreq_reply_unknown_verb(struct afb_xreq *xreq)
+{
+ afb_xreq_reply_f(xreq, NULL, "unknown-verb", "verb %s unknown within api %s", xreq->request.called_verb, xreq->request.called_api);
+}
+
+static void init_hooking(struct afb_xreq *xreq)
+{
+ afb_hook_init_xreq(xreq);
+ if (xreq->hookflags) {
+ xreq->request.itf = &xreq_hooked_itf; /* unhook the interface */
+ afb_hook_xreq_begin(xreq);
+ }
+}
+
+/**
+ * job callback for asynchronous and secured processing of the request.
+ */
+static void process_async(int signum, void *arg)
+{
+ struct afb_xreq *xreq = arg;
+ const struct afb_api_item *api;
+
+ if (signum != 0) {
+ /* emit the error (assumes that hooking is initialised) */
+ afb_xreq_reply_f(xreq, NULL, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
+ } else {
+ /* init hooking */
+ init_hooking(xreq);
+ /* invoke api call method to process the request */
+ api = (const struct afb_api_item*)xreq->context.api_key;
+ api->itf->call(api->closure, xreq);
+ }
+ /* release the request */
+ afb_xreq_unhooked_unref(xreq);
+}
+
+/**
+ * Early request failure of the request 'xreq' with, as usual, 'status' and 'info'
+ * The early failure occurs only in function 'afb_xreq_process' where normally,
+ * the hooking is not initialised. So this "early" failure takes care to initialise
+ * the hooking in first.
+ */
+static void early_failure(struct afb_xreq *xreq, const char *status, const char *info, ...)
+{
+ va_list args;
+
+ /* init hooking */
+ init_hooking(xreq);
+
+ /* send error */
+ va_start(args, info);
+ afb_req_x2_reply_v(xreq_to_req_x2(xreq), NULL, status, info, args);
+ va_end(args);
+}
+
+/**
+ * Enqueue a job for processing the request 'xreq' using the given 'apiset'.
+ * Errors are reported as request failures.
+ */
+void afb_xreq_process(struct afb_xreq *xreq, struct afb_apiset *apiset)
+{
+ const struct afb_api_item *api;
+ struct afb_xreq *caller;
+
+ /* lookup at the api */
+ xreq->apiset = apiset;
+ api = afb_apiset_lookup_started(apiset, xreq->request.called_api, 1);
+ if (!api) {
+ if (errno == ENOENT)
+ early_failure(xreq, "unknown-api", "api %s not found (for verb %s)", xreq->request.called_api, xreq->request.called_verb);
+ else
+ early_failure(xreq, "bad-api-state", "api %s not started correctly: %m", xreq->request.called_api);
+ goto end;
+ }
+ xreq->context.api_key = api;
+
+ /* check self locking */
+ if (api->group) {
+ caller = xreq->caller;
+ while (caller) {
+ if (((const struct afb_api_item*)caller->context.api_key)->group == api->group) {
+ /* noconcurrency lock detected */
+ ERROR("self-lock detected in call stack for API %s", xreq->request.called_api);
+ early_failure(xreq, "self-locked", "recursive self lock, API %s", xreq->request.called_api);
+ goto end;
+ }
+ caller = caller->caller;
+ }
+ }
+
+ /* queue the request job */
+ afb_xreq_unhooked_addref(xreq);
+ if (jobs_queue(api->group, afb_apiset_timeout_get(apiset), process_async, xreq) < 0) {
+ /* TODO: allows or not to proccess it directly as when no threading? (see above) */
+ ERROR("can't process job with threads: %m");
+ early_failure(xreq, "cancelled", "not able to create a job for the task");
+ afb_xreq_unhooked_unref(xreq);
+ }
+end:
+ afb_xreq_unhooked_unref(xreq);
+}
+
+const char *xreq_on_behalf_cred_export(struct afb_xreq *xreq)
+{
+ return xreq->caller ? afb_cred_export(xreq->cred) : NULL;
+}
+