Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
AG_WEB(3)		    Library Functions Manual		     AG_WEB(3)

NAME
       AG_Web -- Agar HTTP/1.1 application server

SYNOPSIS
       #include	<agar/core.h>
       #include	<agar/net/web.h>

DESCRIPTION
       The  AG_Web interface provides the components needed to create a	multi-
       process,	privilege-separated HTTP/1.1 application server	in C.	AG_Web
       is included in the Agar-Core library if compiled	with `--enable-web'.

       An  HTTP	 application server using AG_Web might be deployed as a	set of
       instances running behind	an external facing HTTP	server (such as	Apache
       httpd with mod_proxy_balancer).	AG_Web may also	be integrated into  an
       existing	application in need of an HTTP interface.

       AG_Web  spawns  (and expires) one worker	process	per authenticated ses-
       sion.  Queries can be processed (and responses compressed) in parallel.

       AG_Web handles:
          Authentication and session management
          Content and language	negotiation
          Parsing of HTTP requests
          Forwarding of requests to the appropriate worker
          Chunking and	zlib(3)	compression of worker responses
          Multiplexing	Push events into text/event-stream

       The AG_Web API also facilitates web development with a  basic  template
       engine  and  methods  for  input	parsing	and validation of URLs / query
       strings,	JSON and FORMDATA.  Forms encoded as  multipart/form-data  may
       include binary BLOBs.

       The  GET	argument "op" specifies	the method that	a client wishes	to in-
       voke.  Methods execute in worker	processes, and are organized  in  mod-
       ules (see WEB_RegisterModule() below).

INITIALIZATION
       void WEB_Init(WEB_Application *appData, int clusterID, int eventSource)

       void WEB_RegisterModule(WEB_Module *mod)

       void WEB_CheckSignals(void)

       void WEB_Exit(int exitCode, const char *fmt, ...)

       void WEB_SetLanguageFn(int (*fn)(const char *langCode, void *arg), void
       *arg)

       void  WEB_SetMenuFn(void	 (*WEB_MenuFn)(WEB_Query  *q, WEB_Variable *V,
       void *arg), void	*arg)

       void WEB_SetProcTitle(const char	*title,	...)

       void  WEB_QueryLoop(const  char	*hostname,  const  char	 *port,	 const
       WEB_SessionOps *sessOps)

       The  WEB_Init() routine initializes the AG_Web library.	clusterID is a
       number which should identify this instance uniquely in the  cluster  of
       servers.	  If  the  eventSource flag is non-zero, this instance will be
       able to serve requests for text/event-stream, multiplexing Push	events
       to the client until the connection is closed.  The app structure	should
       be partially initialized:

       typedef struct web_application {
	       const char *name;		       /* Description */
	       const char *copyright;		       /* Copyright notice */
	       const char *availLangs[WEB_LANGS_MAX];  /* Available languages */
	       const char *homeOp;		       /* Default operation */
	       Uint flags;			       /* Option flags */
	       void (*destroyFn)(void);
	       void (*logFn)(enum web_loglvl, const char *s);
	       /* ... */
       } WEB_Application;

       name  is	an arbitrary string identifier for the application.  copyright
       is an optional copyright	notice.	 availLangs is a NULL-terminated array
       of ISO-639 language codes which are valid for this application.	homeOp
       specifies the default AG_Web operation to invoke	after a	successful lo-
       gin.  flags should  be  set  to	zero  (none  are  currently  defined).
       destroyFn  is  an  optional  cleanup function to	be run when the	server
       terminates.  logFn is an	optional callback to receive log messages pro-
       duced by	WEB_Log*() (see	`LOGGING' section).

       For example:

       WEB_Application myExampleApp = {
	       "ExampleApp",
	       "Copyright (c) my name",
	       { "en", "es", "fr", NULL	},  /* English,	Spanish, French	*/
	       "main_welcome",
	       0,	       /* flags	*/
	       NULL,	       /* destroy */
	       NULL	       /* log */
       };

       The WEB_RegisterModule()	function registers a new module	 (and  invokes
       the  module's  init()  method).	The mod	argument must point to an ini-
       tialized	WEB_Module structure:

       typedef struct web_module {
	       char *name;		       /* Short	name */
	       char *icon;		       /* Icon (HTML) */
	       char *lname;		       /* Long name (HTML) */
	       char *desc;		       /* Description (HTML) */
	       int  (*init)(void *sess);       /* App initialization */
	       void (*destroy)(void);	       /* App cleanup */
	       int  (*sessOpen)(void *sess);   /* Session opened */
	       void (*sessClose)(void *sess);  /* Session closed */
	       int (*indexFn)(WEB_Query	*q);   /* Default op */
	       void (*menu)(WEB_Query *q,      /* Menu override	*/
			    WEB_Variable *V);
	       WEB_MenuSection *menuSections;  /* Menu sections	or NULL	*/
	       WEB_Command *commands;	       /* Command map */
       } WEB_Module;

       The name	string is a short identifier and  operation  prefix  for  this
       module.	 It should not exceed WEB_OPNAME_MAX bytes in length.  icon is
       an optional icon	for the	module.	 lname is the full title of the	module
       to display to the user.	desc is	a description of the  module's	opera-
       tion.  icon, lname and desc may contain HTML code.

       All function pointers below are optional	and may	be set to NULL.

       The  init()  function  is  invoked after	the module has been registered
       (typically when the application server is first started).  destroy() is
       invoked to clean	up the module's	resources (typically when the applica-
       tion server is shutting down).

       sessOpen() is called when a new user session is created,	where sess  is
       a  pointer to newly created WEB_Session.	 It is a good place for	a mod-
       ule to initialize its session variables (see WEB_SetSV()).  On success,
       this function should return 0.  If it returns -1, session  creation  is
       aborted (and the	user will be unable to log in).

       The sessClose() routine is called when a	user closes a session.

       indexFn() points	to the default method to invoke	when the "op" argument
       contains	the module name	but does not map onto a	specific method.

       If set, menu() will be called to	render the menu	entry for this module,
       allowing	 dynamically  generated	 contents.  This method	is expected to
       write HTML code into V.

       The commands table maps method names to a module's functions:

       typedef struct web_command {
	       char *name;			   /* Method name */
	       int (*fn)(void *mod, WEB_Query *q); /* Function */
	       const char *type;		   /* MIME type	(or NULL) */
       } WEB_Command;

       name is the full	method name (the matching "op"	argument).   fn	 is  a
       pointer	to the function	implementing the method.  If type is not NULL,
       it indicates the	Content-Type of	the data returned by the method.   For
       example:

       static WEB_Command mymodCommands[] = {
	       { "mymod_hello",	       mymod_hello,    "text/html",	"Pi" },
	       { "mymod_image",	       mymod_image,    "image/png",	"" },
	       { "mymod_json",	       mymod_json,     "[json]",	"" },
	       { "mymod_status",       mymod_status,   "[json-status]",	"" },
	       { "mymod_customtype",   mymod_custtype, NULL,		"" },
	       { NULL,		       NULL,	       NULL,		"" }
       };

       For  a  method  that does not output anything other than	a return value
       and error code, the special type	"[json-status]"	can be used.  On  suc-
       cess,  the JSON code {"code":0} will be emitted.	 If the	function fails
       and return -1, the following will be emitted:

       { "code": -1,
	 "error": "<text from AG_GetError()>",
	 "backend_version": "<agar version>" }

       The special type	"[json]" may be	used if	the function emits  JSON  con-
       tent of its own.	 Then the following will be emitted:

       { "lang": <language code>,
	 <extra	JSON emitted by	function>,
	 "code": <return code from function>,
	 "error": "<text from AG_GetError() on failure>",
	 "backend_version": "<agar version on failure>"	}

       If  the type field of a method is NULL, the function is invoked without
       any additional processing, and will be expected to set at  least	 "Con-
       tent-Type" using	WEB_SetHeader()	or WEB_SetHeaderS().

       The  flags  string  defines per-method options.	It may contain charac-
       ters:

       `P'   Public method.  Make accessible to	both authenticated and non-au-
	     thenticated clients (in the latter	case, q->sess will be NULL).

       `i'   Index method.  Invoke by default when no specific "op" given.

       WEB_CheckSignals() handles a previous  SIGTERM,	SIGPIPE	 and  SIGCHLD.
       The SIGCHLD handler issues a control command to notify server processes
       that  a	particular  worker process has terminated.  Internally,	AG_Web
       invokes WEB_CheckSignals() whenever system calls	 in  the  main	server
       process	are  interrupted.  Ideally, the	same should be done at the ap-
       plication level when an interruptible system  call  fails  with	EINTR.
       This  important for code	executing under	the main server	process	(e.g.,
       authentication module methods).	This is	not needed  for	 code  running
       inside worker processes (e.g., module methods).

       The  WEB_Exit()	routine	immediately cleans up resources	and terminates
       the running process returning the specified exit	code and optional mes-
       sage string.

       WEB_SetLanguageFn() sets	a callback routine (and	optional user pointer)
       for switching between different locales based on	language  preferences.
       The langCode argument is	an ISO-639 language code.

       WEB_SetMenuFn() sets a callback routine (and optional user pointer) for
       constructing the	menu.  It is expected to return	the dynamically-gener-
       ated menu HTML into V.

       WEB_SetProcTitle()  set	the  process  title (as	shown by ps(1))	of the
       current worker process.	If setproctitle(3) is not available, the func-
       tion is a no-op.

       WEB_QueryLoop() is the standard event loop for the application  server.
       It  listens  on	one or more sockets under hostname and port as well as
       the control socket.  WEB_QueryLoop() loops  reading  HTTP  queries  and
       forwarding  requests  to	 worker	 processes,  spawning new workers when
       needed.	 sessOps  defines  the	authentication	module	to  use	  (see
       `AUTHENTICATION'	section	for details).

HTTP RESPONSE HEADERS
       void WEB_SetCode(WEB_Query *q, const char *code)

       void WEB_SetCompression(WEB_Query *q, int enable, int level)

       void  WEB_SetHeader(WEB_Query  *q, const	char *name, const char *value,
       ...)

       void WEB_SetHeaderS(WEB_Query *q, const char *name, const char *value)

       void  WEB_AppendHeader(WEB_Query	 *q,  const  char  *name,  const  char
       *value, ...)

       void  WEB_AppendHeaderS(WEB_Query  *q,  const  char  *name,  const char
       *value)

       WEB_Cookie * WEB_SetCookie(WEB_Query *q,	const char *name,  const  char
       *value, ...)

       WEB_Cookie  * WEB_SetCookieS(WEB_Query *q, const	char *name, const char
       *value)

       WEB_Cookie * WEB_GetCookie(WEB_Query *q,	const char *name)

       void WEB_DelCookie(WEB_Query *q,	const char *name)

       WEB_SetCode() sets the HTTP response code of the	output.	 For  example,
       "404  Not Found"	or "500	Internal Server	Error".	 When a	method is suc-
       cessful,	the default is "200 OK".

       WEB_SetCompression() sets compression parameters	for the	response.  The
       enable flag enables or disables compression, and	level sets the zlib(3)
       compression level from 1	to 9 (1	= Best speed, 9	= Best compression).

       WEB_SetHeader() sets the	value of the HTTP output header	name to	a  new
       value.  If the header already exists, its value is updated.  Otherwise,
       a  new  header is created.  WEB_AppendHeader() appends the given	header
       unconditionally (without	checking for duplicates).

       WEB_SetCookie() sets the	HTTP cookie identified by name	to  the	 given
       value.  If an error (such as overflow) occurs, it returns NULL.	If the
       operation  is  successful, it returns a pointer to the following	struc-
       ture which can be used to change	cookie attributes:

       typedef struct web_cookie {
	       char name[WEB_COOKIE_NAME_MAX];	    /* Name (RO) */
	       char value[WEB_COOKIE_VALUE_MAX];    /* Value */
	       char expires[WEB_COOKIE_EXPIRE_MAX]; /* Expiration date */
	       char domain[WEB_COOKIE_DOMAIN_MAX];  /* Domain match */
	       char path[WEB_COOKIE_PATH_MAX];	    /* Path attribute */
	       Uint flags;
       #define WEB_COOKIE_SECURE 0x01		    /* Set Secure attribute */
       } WEB_Cookie;

       The caller can modify any member	except name.

       WEB_GetCookie() returns a pointer to the	value of cookie	name  or  NULL
       if no such cookie exists.

       WEB_DelCookie() deletes the cookie identified by	name.

HTTP ARGUMENT PARSING
       const char * WEB_Get(WEB_Query *q, const	char *key, AG_Size maxLength)

       const  char  *  WEB_GetTrim(WEB_Query  *q,  const  char	*key,  AG_Size
       maxLength)

       void WEB_Set(WEB_Query *q, const	char *key, const char *value, ...)

       void WEB_SetS(WEB_Query *q, const char *key, const char *value)

       const char * WEB_GetSV(WEB_Session *sess, const char *key)

       void WEB_SetSV(WEB_Query	*q, const char *key, const char	*value,	...)

       void WEB_SetSV_S(WEB_Query *q, const char *key, const char *value)

       void WEB_SetSV_ALL(const	WEB_SessionOps	*sessOps,  const  char	*user,
       const char *key,	const char *value)

       void WEB_Unset(WEB_Query	*q, const char *key)

       int WEB_GetBool(WEB_Query *q, const char	*key)

       int WEB_GetInt(WEB_Query	*q, const char *key, int *dest)

       int  WEB_GetIntR(WEB_Query *q, const char *key, int *dest, int min, int
       max)

       int WEB_GetIntRange(WEB_Query *q, const char *key, int *minValue, const
       char *separator,	int *maxValue)

       int WEB_GetUint(WEB_Query *q, const char	*key, Uint *dest)

       int WEB_GetUintR(WEB_Query *q, const char *key, Uint *dest,  Uint  min,
       Uint max)

       int WEB_GetUint64(WEB_Query *q, const char *key,	Uint64 *dest)

       int WEB_GetSint64(WEB_Query *q, const char *key,	Sint64 *dest)

       int WEB_GetEnum(WEB_Query *q, const char	*key, Uint *dest, Uint last)

       int WEB_GetFloat(WEB_Query *q, const char *key, float *dest)

       int WEB_GetDouble(WEB_Query *q, const char *key,	double *dest)

       char * WEB_EscapeURL(WEB_Query *q, const	char *url)

       char * WEB_UnescapeURL(WEB_Query	*q, const char *url)

       WEB_Get() looks up the HTTP argument named key and returns a pointer to
       the  value  as a	NUL-terminated string.	If no such argument exists, it
       returns NULL (with a "Missing argument" error).

       The WEB_GetTrim() variant of WEB_Get() implicitely removes leading  and
       trailing	 spaces	 (characters  matching	isspace(3))  from the argument
       value.

       WEB_Set() modifies the in-memory	value associated  with	argument  key.
       If  no  such  argument exists then one is created.  WEB_Unset() deletes
       the specified argument from memory.

       Session variables are key-value pairs associated	with an	 authenticated
       user  session.	They  are saved	to disk	and preserved across processes
       handling	a same session.	 WEB_GetSV() returns the value	of  the	 given
       session	variable or NULL if no such variable exists.  WEB_SetSV() sets
       the session variable key	to value.  The WEB_SetSV_ALL() variant updates
       all session variables named key to value	for every  session  opened  by
       user.

       WEB_GetBool() returns 1 if argument key exists and its value is not the
       empty string (""), otherwise it returns 0.

       The  following WEB_Get*() functions convert arguments to	numerical val-
       ues, returning 0	on success.  If	no such	argument exists, if the	 input
       is  invalid  or	the  number is out of range, these functions return -1
       with an error message.

       WEB_GetInt() converts argument key to a signed integer,	returning  the
       result  in  dest.   The	number	must  lie  within the range INT_MIN to
       INT_MAX.	 The WEB_GetIntR() variant fails if the	number is  lower  than
       min or greater than max.

       The WEB_GetIntRange() function parses a range, specified	as a string of
       the  form  "<min><separator><max>", for example "1-10" (where separator
       would be	"-").  The first number	is returned into minValue  and	second
       number  into  maxValue.	The function returns 0 on success or -1	if the
       argument	is missing or does not describe	a valid	range.

       WEB_GetUint() converts argument key to an unsigned  integer,  returning
       the  result  in	dest.	The  number  must  lie	within	the range 0 to
       UINT_MAX.  The WEB_GetUintR() variant fails if the number is lower than
       min or greater than max.

       WEB_Get[SU]int64() converts argument key	to a signed or unsigned	64-bit
       integer,	returning the result in	dest.  The number must lie within  the
       range [SU]INT64_MIN to [SU]INT64_MAX.

       The WEB_GetEnum() function converts argument key	to an unsigned integer
       greater than 0 and less than or equal to	last.

       WEB_GetFloat()  and WEB_GetDouble() convert the argument	to a single or
       double-precision	floating point number and return the value in dest.

       The WEB_EscapeURL() function turns URL-unsafe characters	(per  RFC1738)
       from  url  into "%02x" format and returns a newly allocated string with
       the result.  WEB_UnescapeURL() transforms all instances of  "%02x"  es-
       caped  characters  in  url  back	 to the	original character (except NUL
       which would be returned as '_') and returns a  newly  allocated	string
       with the	result.

LOGGING
       void WEB_SetLogFile(const char *path)

       void WEB_Log(enum web_loglvl logLevel, const char *msg, ...)

       void WEB_LogS(enum web_loglvl logLevel, const char *msg)

       void WEB_LogErr(const char *msg,	...)

       void WEB_LogWarn(const char *msg, ...)

       void WEB_LogInfo(const char *msg, ...)

       void WEB_LogNotice(const	char *msg, ...)

       void WEB_LogDebug(const char *msg, ...)

       void WEB_LogWorker(const	char *msg, ...)

       void WEB_LogEvent(const char *msg, ...)

       The  WEB_SetLogFile()  function	sets an	alternate destination log file
       (by default the application name	+ ".log" in the	working	directory).

       The WEB_Log() and WEB_LogS() functions generate a log  entry  with  the
       given logLevel.	That the log file is unbuffered.  Log levels include:

       enum web_loglvl {
	       WEB_LOG_EMERG,	/* General panic condition */
	       WEB_LOG_ALERT,	/* Immediate attention required	*/
	       WEB_LOG_CRIT,	/* Critical conditions,	I/O errors */
	       WEB_LOG_ERR,	/* General errors */
	       WEB_LOG_WARNING,	/* Warning messages */
	       WEB_LOG_NOTICE,	/* Condition should be handled specially */
	       WEB_LOG_INFO,	/* Informational messages */
	       WEB_LOG_DEBUG,	/* Debugging information */
	       WEB_LOG_QUERY,	/* HTTP	query (e.g., GET, POST)	parsing	*/
	       WEB_LOG_WORKER,	/* Errors specific to worker processes */
	       WEB_LOG_EVENT	/* Errors related to Push events */
       };

       Alternatively,  the  WEB_Log<Level>() shorthand routines	can be used to
       generate	a log message under the	implied	log level.

HTTP RESPONSE OUTPUT
       void WEB_Write(WEB_Query	*q, const char *data, AG_Size len)

       void WEB_PutC(WEB_Query *q, char	c)

       void WEB_PutS(WEB_Query *q, const char *s)

       void WEB_Printf(WEB_Query *q, const char	*format, ...)

       void WEB_PutJSON(WEB_Query *q, const char *key, const char *data, ...)

       void WEB_PutJSON_S(WEB_Query *q,	const char *key, const char *data)

       void WEB_PutJSON_NoHTML_S(WEB_Query *q, const  char  *key,  const  char
       *data)

       void WEB_OutputHTML(WEB_Query *q, const char *template)

       void   WEB_PutJSON_HTML(WEB_Query  *q,  const  char  *key,  const  char
       *document)

       void WEB_OutputError(WEB_Query *q, const	char *msg)

       void WEB_SetError(const char *msg, ...)

       void WEB_SetErrorS(const	char *msg)

       void WEB_SetSuccess(const char *msg, ...)

       WEB_Variable * WEB_VAR_New(const	char *key)

       void WEB_VAR_Grow(WEB_Variable *v, AG_Size newLen)

       WEB_Variable * WEB_VAR_Set(const	char *key, const char *value, ...)

       WEB_Variable * WEB_VAR_SetS(const char *key, const char *value)

       WEB_Variable * WEB_VAR_SetS_NODUP(const char *key, char *value)

       WEB_Variable * WEB_VAR_SetGlobal(const char *key,  const	 char  *value,
       ...)

       WEB_Variable * WEB_VAR_SetGlobalS(const char *key, const	char *value)

       void WEB_VAR_Cat(WEB_Variable *v, const char *value, ...)

       void WEB_VAR_CatS(WEB_Variable *v, const	char *value)

       void WEB_VAR_CatS_NODUP(WEB_Variable *v,	char *value)

       void WEB_VAR_CatS_NoHTML(WEB_Variable *v, const char *value)

       void WEB_VAR_CatC(WEB_Variable *v, const	char c)

       void WEB_VAR_CatN(WEB_Variable *v, const	void *src, AG_Size len)

       void WEB_VAR_CatN_NoNUL(WEB_Variable *v,	const void *src, AG_Size len)

       void WEB_VAR_CatJS(WEB_Variable *v, const char *value)

       void WEB_VAR_CatJS_NODUP(WEB_Variable *v, char *value)

       void WEB_VAR_CatJS_NoHTML(WEB_Variable *v, const	char *value)

       void WEB_VAR_CatJS_NoHTML_NODUP(WEB_Variable *v,	char *value)

       char * WEB_VAR_Get(const	char *key)

       void WEB_VAR_Wipe(const char *key)

       void WEB_VAR_Unset(const	char *key)

       int WEB_VAR_Defined(const char *key)

       void WEB_VAR_Free(WEB_Variable *v)

       The  following routines produce HTTP response data.  Upon query comple-
       tion, this data will be compressed, chunked and	written	 back  to  the
       HTTP client.

       WEB_Write()  appends  len  bytes	from data to the HTTP response buffer.
       WEB_PutC() writes a single character c.	WEB_PutS() writes a NUL-termi-
       nated string s.	WEB_Printf() produces printf(3)	formatted text.

       WEB_PutJSON()  produces	a  JSON	 data  pair   from   key   and	 data.
       WEB_PutJSON()  escapes  data for	double quotes, backslashes, "\r", "\n"
       and "\t".  The WEB_PutJSON_NoHTML_S() variant additionally escapes  "<"
       to "&lt;" and ">" to "&gt;".

       The  WEB_OutputHTML()  function	invokes	the template engine to produce
       text/html output	from the contents of a template	file with  "$variable"
       references  substituted with the	current	set of WEB_Variable.  The tem-
       plate	    file	should	      be	 located	 under
       WEB_PATH_HTML/<template>.html.<lang>,  where  lang  is the ISO-639 lan-
       guage code for the current session.  If no such template	 file  exists,
       it fails	and returns -1.

       The  WEB_PutJSON_HTML() function	invokes	the template engine to produce
       JSON-encapsulated text/html output from template	and the	current	set of
       WEB_Variable.  If no such template file exists, it  fails  and  returns
       -1.

       WEB_OutputError()  outputs  a  complete	text/html document with	a body
       displaying error	message	msg.  WEB_SetError() sets the $_error variable
       to contain a dismissible	HTML error message.  WEB_SetSuccess() sets the
       $_error variable	to contain a dismissible HTML "success"	message.

       The WEB_Variable	structure represents a variable	holding	 a  C  string.
       In  template  files, variables are referenced as	"$variable".  Variable
       values are  typically  set  by  a  method  handler  prior  to  invoking
       WEB_OutputHTML().   Variables  are linked to the	current	WEB_Query, ex-
       cept for	globals	which remain persistent	across queries.

       typedef struct web_variable {
	       char	key[WEB_VAR_NAME_MAX]; /* Name ("\0" = anonymous) */
	       char    *value;		       /* Value	(C string) */
	       AG_Size	len;		       /* Content length (characters) */
	       AG_Size	bufSize;	       /* Buffer size */
	       int	global;		       /* Persistent across queries */
	       AG_TAILQ_ENTRY(web_variable) vars;
       } WEB_Variable;

       WEB_VAR_New() returns a pointer to a newly allocated WEB_Variable of an
       undefined value.	 If the	key argument is	NULL, it returns an  anonymous
       variable	which must be freed explicitely	by the caller after use.

       WEB_VAR_Grow() pre-allocates up to newLen bytes for the value of	v.

       WEB_VAR_Set()  sets  the	 value	of  variable key to value.  If no such
       variable	exists then a new one is created.

       The WEB_VAR_SetS_NODUP()	variant	accepts	a pointer to user memory which
       must remain accessible and valid	for as long as the variable is in use.

       The  scope  of  WEB_Variable  variables	is  limited  to	 the   current
       WEB_Query.   However,  global  variables	 which	will remain persistent
       across queries can be declared using WEB_VAR_SetGlobal().  Since	 glob-
       als are allocated once in the parent process, globals can be shared be-
       tween processes without extra memory usage.

       The WEB_VAR_Cat() and WEB_VAR_CatS() routines append a string to	an ex-
       isting  variable.   The	WEB_VAR_CatS_NODUP() variant frees value after
       appending its contents.	The WEB_VAR_CatS_NoHTML() variant escapes  "<"
       to "&lt;" and ">" to "&gt;".

       WEB_VAR_CatC() appends a	single character c to the value	of variable v.

       WEB_VAR_CatN()  grows  the  value of v by len bytes, performs memcpy(3)
       and  NUL-terminates  the	 result	 (the	WEB_VAR_CatN_NoNUL()   variant
       doesn't).

       WEB_VAR_CatJS() appends value to	JSON data in v,	escaping any backslash
       and  double  quote characters.  The WEB_VAR_CatJS_NoHTML() variant also
       escapes "<" to "&lt;" and ">" to	"&gt;".	 The WEB_VAR_CatJS_NODUP() and
       WEB_VAR_CatJS_NoHTML_NODUP() variants both free s after concatenation.

       WEB_VAR_Get() looks for a variable key and returns  a  pointer  to  its
       value.  If no such variable is defined, it returns NULL.

       WEB_VAR_Wipe()  trivially  overwrites  the in-memory value of the vari-
       able.

       WEB_VAR_Unset() deletes and frees the variable named key, if it exists.

       WEB_VAR_Defined() returns 1 if the given	variable exists, otherwise 0.

       WEB_VAR_Free() frees all	resources allocated by an  anonymous  variable
       v.   It	is used	internally by WEB_VAR_Unset() and called automatically
       on all variables	after the WEB_Query has	been processed.

       This example sets variables "username"  and  "password"	and  generates
       HTML  using  the	 "login_form"  template.  Instances of "$username" and
       "$password"  in	login_form  will  be  substituted  for	"nobody"   and
       "test1234".

       WEB_VAR_SetS("username",	"nobody");
       WEB_VAR_SetS("password",	"test1234");
       WEB_OutputHTML("login_form");
       WEB_VAR_Wipe("password");

       Anonymous variables must	be freed explicitely by	the caller:

       WEB_VAR *v;
       v = WEB_VAR_SetS(NULL, "Hello, ");      /* Anonymous variable */
       WEB_VAR_CatS(v, "world!");
       WEB_VAR_Free(v);

AUTHENTICATION
       The  sessOps  argument passed to	WEB_QueryLoop()	sets the effective au-
       thentication  module.   The  argument  must  point  to  an  initialized
       WEB_SessionOps structure:

       typedef struct web_session_ops {
	       const char *name;	     /*	Session	class name */
	       AG_Size size;		     /*	Structure size */
	       Uint flags;
       #define WEB_SESSION_PREFORK_AUTH	0x01 /*	Call auth() before fork() */
	       time_t sessTimeout;	     /*	Session	inactivity (s) */
	       time_t workerTimeout;	     /*	Worker inactivity (s) */

	       void (*init)(void *sess);
	       void (*destroy)(void *sess);
	       int  (*load)(void *sess,	AG_DataSource *);
	       void (*save)(void *sess,	AG_DataSource *);
	       int  (*auth)(void *sess,	const char *user, const	char *pass);
	       WEB_CommandPreAuth preAuthCmds[10];
	       int  (*sessOpen)(void *sess, const char *user);
	       void (*sessRestored)(void *sess,	const char *user);
	       void (*sessClose)(void *sess);
	       void (*sessExpired)(void	*sess);
	       void (*beginFrontQuery)(WEB_Query *q, const char	*op);
	       void (*loginPage)(WEB_Query *q);
	       void (*logout)(WEB_Query	*q);
	       void (*addSelectFDs)(void *sess,	fd_set *rd, fd_set *wr,	int *max);
	       void (*procSelectFDs)(void *sess, fd_set	*rd, fd_set *wr);
       } WEB_SessionOps;

       The  name  field	 is a string identifier	for the	authentication module.
       size is the size	in bytes of the	structure  describing  a  session  in-
       stance  (which  may be a	WEB_Session structure or a user-defined	struc-
       ture  derived  from  it).   Currently  the   only   flags   option   is
       WEB_SESSION_PREFORK_AUTH.   If  this is set, the	auth() method will run
       in the parent process.  Otherwise, a worker  process  will  be  spawned
       first  with  fork(2),  and  the	auth() code will execute in the	worker
       process.	 Pre-fork auth is best for fast, local file-based  authentica-
       tion methods, and auth()	running	in the worker process is best for net-
       work-based auth methods.

       sessTimeout   sets   the	  session   inactivity	 timeout  in  seconds.
       workerTimeout sess the worker process inactivity	timeout	in seconds.

       init() initializes a the	session	 instance  structure.	destroy()  re-
       leases  resources  allocated  by	a session instance.  load() and	save()
       serialize the session instance structure	to machine-independent format.

       The auth() operation verifies the  given	 username  and	password.   On
       failure	 it   should   return	-1  and	 set  an  error	 message  with
       AG_SetError(3).	It is  expected	 to  return  0	on  success.   If  the
       WEB_SESSION_PREFORK_AUTH	 option	 is set, the operation will run	in the
       parent server process (best for fast, local auth	methods).   Otherwise,
       it will run in a	worker process which will terminate should authentica-
       tion fail (best for network-bound auth methods).

       The  following  preAuthCmds[]  array  maps the URL-provided "op"	onto a
       method which will execute in the	parent server process (as  opposed  to
       running	inside	a worker process).  This is useful for pre-auth	opera-
       tions such as generating	CAPTCHAs, processing POSTDATA from a user-sub-
       mitted registration form, e-mail	or mobile verification	routines,  and
       handling	of password recovery requests.

       The sessOpen() method is	invoked	after successful authentication	(the s
       argument	will point to the newly	allocated session instance structure).
       sessRestored()  is invoked whenever a previously	expired	worker process
       restarts, after the saved session state has been	successfully recovered
       from disk.  sessClose() is called when  a  session  terminates  and  is
       about to	be deleted from	disk.  sessExpired() is	called whenever	a ses-
       sion expires due	to its inactivity timeout.

       The  beginFrontQuery()  routine	is invoked as a	prologue to any	of the
       preAuthCmds[]  methods.	 It  is	  useful   for	 initializing	common
       WEB_Variable  elements  such  as	$_user and $_theme (see	`HTTP RESPONSE
       OUTPUT' section).

       loginPage() returns text/html content  to  show	unauthenticated	 users
       (typically  a  login  form).   The logout() operation is	invoked	when a
       user logs out.  It is expected to clear the session ID from the	"sess"
       cookie.

       Authentication  modules which use a polling mechanism such as select(2)
       should implement	addSelectFDs().	 This method registers extra file  de-
       scriptors  to be	watched	for read/write conditions.  procSelectFDs() is
       invoked to process a read or write condition on a watched file descrip-
       tor.

       The code	below illustrates a basic authentication module.  It  declares
       a session structure derived from	WEB_Session.  It also provides a back-
       end for a registration form (regChallenge, regFinish and	regConfirm).

       /* Per session data */
       typedef struct mySiteSession {
	       struct web_session _inherit;  /*	WEB_Session->MySiteSession */
	       char username[32];	     /*	Authenticated username */
	       time_t lastLogin;	     /*	Last login */
	       /* ... */
       } MySiteSession;

       static void
       Init(void *pSess)
       {
	       MySiteSession *S	= pSess;
	       S->username[0] =	'\0';
	       S->lastLogin = 0;
       }
       static void
       Load(void *pSess, AG_DataSource *ds)
       {
	       MySiteSession *S	= pSess;
	       S->lastLogin = (time_t)AG_ReadUint64(ds);
       }
       static void
       Save(void *pSess, AG_DataSource *ds)
       {
	       MySiteSession *S	= pSess;
	       AG_WriteUint64(ds, S->lastLogin);
       }
       static int
       Auth(void *pSess, const char *user, const char *password)
       {
	       return AuthSuccessful(user, password) ? 0 : -1;
       }
       static void
       AuthRegChallenge(WEB_Query *q)
       {
	       GenerateCaptchaGif();
	       WEB_Write(q, captchaGif,	captchaGifSize);
       }
       static void
       AuthRegFinish(WEB_Query *q)
       {
	       /* Process POSTDATA from	registration form and create account */
       }
       static void
       AuthRegConfirm(WEB_Query	*q)
       {
	       /* Complete e-mail or mobile verification */
       }
       static int
       SessOpen(void *pSess, const char	*username)
       {
	       MySiteSession *S	= pSess;
	       time_t t;

	       Strlcpy(S->username, username, sizeof(S->username));
	       time(&t);
	       S->lastLogin = t;
	       WEB_LogInfo("User %s logged in",	username);
	       return (0);
       }
       static void
       SessRestored(void *pSess, const char *username)
       {
	       MySiteSession *S	= pSess;
	       WEB_Session *WS = pSess;

	       Strlcpy(S->username, username, sizeof(S->username));
	       WEB_LogInfo("User %s recovered session %s", username, WS->id);
       }
       static void
       SessClose(void *pSess)
       {
	       MySiteSession *S	= pSess;
	       WEB_LogInfo("User %s logged out", S->username);
       }
       static void
       BeginPreAuthCmd(WEB_Query *q, const char	*op)
       {
	       /* Set common variables for all preAuthCmds[] methods */
	       WEB_VAR_SetS("_admin", "webmaster@example.com");
	       WEB_VAR_SetS("_user", "");
	       WEB_VAR_SetS("_theme", "Default");
	       WEB_VAR_SetS("_modules",
		   "<li><a href='/'>Sign in</a></li>"
		   "<li><a href='/register.html'>Create	account</a></li>");
       }
       static void
       LoginPage(WEB_Query *q)
       {
	       const char *user	= WEB_Get(q, "username", 32);
	       const char *pass	= WEB_Get(q, "password", 32);
	       const char *op =	WEB_Get(q, "op", WEB_OPNAME_MAX);

	       WEB_VAR_SetS("login_username", (user) ? user : "");
	       WEB_VAR_SetS("op", (op) ? op : "main_index");

	       WEB_SetCode(q, "200 OK");
	       WEB_SetHeaderS(q, "Vary", "accept-language,Accept-Encoding,"
					 "User-Agent");
	       WEB_SetHeaderS(q, "Last-Modified", q->date);
	       WEB_SetHeaderS(q, "Content-Type", "text/html; charset=utf8");
	       WEB_SetHeaderS(q, "Content-Language", q->lang);
	       WEB_SetHeaderS(q, "Cache-Control", "no-cache, no-store, "
						  "must-revalidate");
	       WEB_SetHeaderS(q, "Expires", "0");
	       WEB_OutputHTML(q, "loginPage");
       }
       static void
       Logout(WEB_Query	*q)
       {
	       WEB_Cookie *ck;

	       /* Clear	the session ID cookie */
	       ck = WEB_SetCookieS(q, "sess", "");
	       ck->path[0] = '/';
	       ck->path[1] = '\0';

	       WEB_OutputHTML(q, "logoutPage");
       }

       WEB_SessionOps mySiteSessionOps = {
	       "My Site's Auth Module",
	       sizeof(MySiteSession),
	       WEB_SESSION_PREFORK_AUTH, /* Invoke auth() before fork */
	       7*24*60*60,		 /* Session inactivity timeout (s) */
	       1*60,			 /* Worker inactivity timeout (s) */
	       Init,
	       NULL,			 /* destroy */
	       Load,
	       Save,
	       Auth,
	       {
		       { "regChallenge", AuthRegChallenge, "image/gif" },
		       { "regFinish",	 AuthRegFinish,	   "text/html" },
		       { "regConfirm",	 AuthRegConfirm,   "text/html" },
		       /*
			* Declare more methods to handle password recovery
			* and other administrative functions.
			*/
		       { NULL,		 NULL,		   NULL	       }
	       },
	       SessOpen,
	       SessRestored,
	       SessClose,
	       NULL,		 /* sessExpired	*/
	       BeginPreAuthCmd,
	       LoginPage,
	       Logout,
	       NULL,		 /* addSelectFDs */
	       NULL		 /* procSelectFDs */
       };

PUSH EVENTS
       int  WEB_PostEvent(const	char *match, WEB_EventFilterFn filterFn, const
       void *filterFnArg, const	char *type, const char *data, ...)

       int WEB_PostEventS(const	char *match, WEB_EventFilterFn filterFn, const
       void *filterFnArg, const	char *type, const char *data)

       WEB_PostEvent() generates a Server-Sent (Push) Event.  The match	 argu-
       ment  indicates which of	the running event listeners should receive the
       event:

       *	   All active event sources.
       username	   All sessions	by the given user.
       L=lang	   All sessions	in specified language.
       S=id	   The session with the	given session ID.
       NULL	   Based on return value of filterFn.

       A custom	event filter can be used by passing NULL to match  and	having
       filterFn	point to a compare function of the form:

       typedef int (*WEB_EventFilterFn)(char *sessID, char *user,
					char *langCode,	const void *arg);

       The  compare function will be invoked for each running session.	If its
       returns 0, the event will be forwarded to  the  associated  event  lis-
       tener.	filterFnArg  is	an optional user pointer (passed to arg	of the
       compare function).  type	and data specify  the  contents	 of  the  Push
       event.	AG_Web	automatically  adds and	increments the "id" field.  It
       also generates "ping" events at regular intervals.

SEE ALSO
       AG_Intro(3)

HISTORY
       The AG_Web interface is based on	libpercgi, which was developed over at
       Csoft.net Hosting (https://csoft.net/)  in  2003	 and  originally  used
       CGI/FastCGI.  It	was rewritten to become	a standalone framework and fi-
       nally integrated	in Agar	1.5.1 as a component of	ag_core.  It was moved
       to a separate library ag_net in Agar 1.6.0.

Agar 1.7		       December	21, 2022		     AG_WEB(3)

Want to link to this manual page? Use this URL:
<https://man.freebsd.org/cgi/man.cgi?query=AG_Web&sektion=3&manpath=FreeBSD+Ports+14.3.quarterly>

home | help