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