REST

The default network listening framework allows for simple registration of REST-based services via HTTP service.

app.conf (snippet)
<app>
  <listener type="http_rest_api" address="127.0.0.1" port="80">
    <config>
      <acl>internal</acl>
      <document_root>/path/to/docroot</document_root>
    </config>
  </listener>
  <rest>
    <acl type="deny" listener_acl="^internal$">
      <rule type="allow" url="."/>
    </acl>
    <acl type="deny"></acl>
  </rest>
</app>

More information about listener configuration can be found in the listener configuration section.

The above config establishes a REST-capable listener listening on localhost port 80. The "ACL" config option is set to internal which can be used as a filter for ACL rules in the rest/acl config section. The document_root is set, which will enable serving of static files on URLs that do not otherwise match routing rules. The ACL rule for internal set that all urls are allowed followed by a blanket deny rule. Other listeners that might not specify an acl option would not see the first ACL allowing any URL, but still see the blanket deny rule.

Registering a REST handler.

myhandler.c (snippet)
#include <mtev_rest.h>

static int
my_rest_handler(mtev_http_rest_closure_t *restc,
                int npats, char **pats) {
  mtev_http_session_ctx *ctx = restc->http_ctx;
  mtev_http_response_ok(ctx, "text/plain");
  mtev_http_response_appendf(ctx, "myhandler: %s\n", pats[0]);
  mtev_http_response_end(ctx);
  return 0;
}

void child_main() {
 ...

  mtev_http_rest_register_auth(
    "GET", "/", "^myhandler/(.+)$", my_rest_handler,
           mtev_http_rest_client_cert_auth
  );
  mtev_http_rest_register_auth(
    "GET", "/", "^(.*)$", mtev_rest_simple_file_handler,
           mtev_http_rest_client_cert_auth
  );
  eventer_loop();
  return 0;
}

Handling asynchronous work.

In order to complete some complex action in response to an inbound REST request, it might be necessary to schedule some asynchronous work and complete the response later. This is possible, but requires a bit of juggling. The basic idea is:

  • from your handler:
    • copy and remove the connection's event from the eventer
    • set the rest's fastpath to a completion routine
    • schedule asynchronous work
    • return 0;
  • from the async job's completion:
    • trigger the connection's event
sleepy.c (snippet)
#include <mtev_rest.h>

static int handler_work(eventer_t e, int mask, void *closure,
                        struct timeval *now) {
  mtev_http_rest_closure_t *restc = closure;
  if(mask == EVENTER_ASYNCH_WORK) {
    sleep(5);
  }
  if(mask == EVENTER_ASYNCH) {
    mtev_http_session_resume_after_float(restc->http_ctx);
  }
  return 0;
}

static int handler_complete(mtev_http_rest_closure_t *restc,
                            int npats, char **pats) {
  mtev_http_session_ctx *ctx = restc->http_ctx;
  mtev_http_response_ok(ctx, "text/plain");
  mtev_http_response_append_str(ctx, "Hello world\n");
  mtev_http_response_end(ctx);
  return 0;
}

static int handler(mtev_http_rest_closure_t *restc,
                   int npats, char **pats) {
  eventer_t conne, worke;
  mtev_http_session_ctx *ctx = restc->http_ctx;

  /* remove the eventer */
  conne = mtev_http_connection_event_float(mtev_http_session_connection(ctx));
  if(conne) eventer_remove_fde(conne);

  /* set a completion routine */
  restc->fastpath = handler_complete;

  /* schedule our work */
  worke = eventer_alloc_asynch(handler_work, restc);
  eventer_add(worke);
  return 0;
}

Handling POST/PUT data

Reading data from the HTTP request is done by calling the mtev_http_session_req_consume function. This can be tedious, so unless you are doing something special it can be much easier to simply first invoke the mtev_rest_complete_upload convenience wrapper.

It must be called as the first action inside your REST callback handler. Any manipulation of the restc (closures in particular) will have undefined outcome.

static int handler(mtev_http_rest_closure_t *restc,
                   int npats, char **pats) {
  int mask;
  void *payload;
  int64_t payload_len;
  mtev_http_request *req = mtev_http_session_request(restc->http_ctx);

  if(!mtev_rest_complete_upload(restc, &mask)) return mask;
  payload = mtev_http_request_get_upload(req, &payload_len);

  ...
}

results matching ""

    No results matching ""