mpx_session -- run a multiplexed network service on a given port
#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);
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:
This field is initially null and may be freely used to maintain a pointer to a custom session object.
This field depends on the global_handle parameter passed to run_mpx_service.
This is a pointer that points to the next input record. This sequence is not nullbyte-terminated.
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.
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); }
Andreas F. Borchert