NAME

mpx_session -- run a multiplexed network service on a given port


SYNOPSIS

   #include <afblib/mpx_session.h>
   typedef struct session {
      void* handle;
      void* global_handle;
      const char* request;
      size_t request_len;
   } session;
   typedef void (*mpx_handler)(session* s);
   void run_mpx_service(hostport* hp, const char* regexp,
      mpx_handler ohandler, mpx_handler rhandler, mpx_handler hhandler,
      void* global_handle);
   int mpx_session_scan(session* s, ...);
   int mpx_session_printf(session* s, const char* restrict format, ...);
   int mpx_session_vprintf(session* s, const char* restrict format, va_list ap);
   void close_session(session* s);


DESCRIPTION

run_mpx_service creates a socket that listens on the given hostport, accepts all incoming connections, invokes ohandler, if non-null, for each incoming connection, rhandler for every input record that matches regexp (see pcrepattern), and hhandler, if non-null, whenever a network connection terminates.

Request handlers get a session reference with following fields:

handle

This field is initially null and may be freely used to maintain a pointer to a custom session object.

global_handle

This field depends on the global_handle parameter passed to run_mpx_service.

request

This is a pointer that points to the next input record. This sequence is not nullbyte-terminated.

request_len

Gives the length of the next input record in bytes.

If capturing subpatterns have been given in the regexp parameter to run_mpx_service, mpx_session_scan can be used within the rhandler (and only there) to extract these captures into stralloc objects. The number of capturing subpatterns must match the number of additional stralloc* paramters.

mpx_session_printf works like printf but sends the output non-blocking to the network connection associated with s. mpx_session_vprintf provides an alternative interface that supports variadic argument lists that are already present as va_list reference.

close_session may be called to initiate the shutdown of a session, i.e. no further input packets will be processed, just the pending response packets will be taken care of.

run_mpx_service runs normally infinitely and returns in error cases only.


EXAMPLE

Following example implements a network service that allows to increment whole numbers. Usually, every session has its own number that gets incremented. But, by adding the keyword global to an increment, a global counter that is shared over all sessions gets incremented.

   #include <afblib/hostport.h>
   #include <afblib/mpx_session.h>
   #include <assert.h>
   #include <stdio.h>
   #include <stdlib.h>
   #include <string.h>
   struct global_status {
      int counter; // global counter
   };
   struct session_status {
      int counter; // per-session counter;
      stralloc cmd; // request command
      stralloc param; // request parameter
   };
   void ohandler(session* s) {
      struct session_status* sp = calloc(1, sizeof(struct session_status));
      if (!sp) {
         close_session(s); return;
      }
      s->handle = sp;
   }
   void rhandler(session* s) {
      struct global_status* gs = s->global_handle;
      struct session_status* sp = s->handle;
      if (mpx_session_scan(s, &sp->cmd, &sp->param) != 2) {
         close_session(s); return;
      }
      int* counterp;
      if (stralloc_diffs(&sp->cmd, "global") == 0) {
         counterp = &gs->counter; // global counter
      } else {
         counterp = &sp->counter; // session counter
      }
      stralloc_0(&sp->param); int incr_value = atoi(sp->param.s);
      *counterp += incr_value;
      mpx_session_printf(s, "%d\r\n", *counterp);
   }
   void hhandler(session* s) {
      struct session_status* sp = s->handle;
      stralloc_free(&sp->cmd); stralloc_free(&sp->param); free(sp);
   }
   int main(int argc, char** argv) {
      char* cmdname = *argv++; --argc;
      if (argc != 1) {
         fprintf(stderr, "Usage: %s hostport\n", cmdname); exit(1);
      }
      char* hostport_string = *argv++; --argc;
      hostport hp;
      if (!parse_hostport(hostport_string, &hp, 33013)) {
         fprintf(stderr, "%s: hostport expected\n", cmdname); exit(1);
      }
      struct global_status gs = {0};
      run_mpx_service(&hp, "(?:(global) )?(-?\\d+)\r\n",
         ohandler, rhandler, hhandler, &gs);
   }


AUTHOR

Andreas F. Borchert