Revision 237 (by dpavlin, 2004/03/08 17:43:12) initial import of openisis 0.9.0 vendor drop
/*
	openisis - an open implementation of the CDS/ISIS database
	Version 0.8.x (patchlevel see file Version)
	Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	see README for more information
EOH */
#ifndef LSV_H

/*
	$Id: lsv.h,v 1.11 2003/06/03 17:09:20 kripke Exp $
	OpenIsis server
*/

#ifdef _REENTRANT /* it's really the wrong flag, but *sigh* */
#define LSVTHR
#endif

#ifdef LSVTHR
#include <pthread.h> /* else the work is done by the receiver */
#endif


#include "loi.h"
#include "lio.h"
#include "lses.h"


/** network service

RECV: loop in receiver thread
-	loop selecting on all connections
-	dequeue connections from recv queue, clear BUSY
- for every available connection:
-	if request is new (no input so far),
	prepare req with peer header, preset default flags
-	read input (which may clear the LIO_IN flag on EOF)
-	call proto to extract input
	proto calls check (if any) to preprocess each piece of input
-	if done, set BUSY, pass to dispatcher (via queue or in same thread)

DISP: the one and only session master (in separate thread, if multiple recv)
-	no input after this point, the req is considered read-only
	any data on connection is considered an error (i.e. no pipelining)
-	if request contains a SID header, find or create session.
	if session is busy, enqueue to session's wait queue
-	enqueue to work queue

WORK: (in worker thread)
-	once a worker thread dequeued a request, it calls process function
	this may add fields to response and/or write to output stream
-	before output buffer is flushed first time,
	it is set aside and proto is called to output the response
- after process is done, the request is marked in flush phase:
	the req is not accessed after this point,
	the output is flushed and the response freed
-	enqueue to recv queue


events in connection lifetime:
-	accept:
	if the socket hasn't been used so far, a new connection is created.
	an existing connection is reused.
	The connection object may still be in use by a worker.
	Since the socket file id is reused, the former socket with the
	same fid must have been closed, and the LIO_INOUT flags
	must have been cleared, so lio_write on that old lio file id will fail.
	The connection is marked as new and, if not in use by a worker,
	entered into the receive queue.
- dequeue from receive queue:
	if the connection is marked new,
	it is prepared and added to the select set.
	else, if it was closed, it is removed from the select set.
-	read events:
	if the connection is in use, that is, a previous request hasn't yet
	entered the flush phase, there is an attempt to pipeline.
	while this is considered an error, for robustness it's handled:
	the connection is marked pipeline and the socket is cleared
	from the select set, to be reenabled later.
	Once the connection is idle, input is read until the protocol
	decides on a complete request. Excess data (immediate pipelining)
	is considered an error.
*/

/* common header tags -- actually negative tags are used */
enum {
	/* nonstandard */
	LSV_TEXT,   /* +- 0: a small, inlined untyped or text/plain body */
	LSV_RID,    /* row id, a.k.a MFN */
	LSV_SID,    /* session id */
	LSV_PEER,   /* network peer ("remote host") */
	LSV_TIME,   /* GTF + millis */
	LSV_ATTACH, /* wrapped-up attachment, TBD */
	/* MIME basics */
	LSV_CTYPE,
	/* HTTP: CGI/servlet */
	LSV_URI,
	LSV_QUERY
};

enum {
#ifdef LSVTHR
	LSV_NUMWRK = 8,
#else
	LSV_NUMWRK = 1,
#endif
	LSV_BUFSIZ = 0x04000, /* 16384 a.k.a. 16K */
	LSV_BUFMSK = 0x03fff  /* mask buf lengths */
};

typedef struct Que {
	struct Con     *head;
	struct Con     *tail;
	int             len;
} Que;

/* queue with pool of workers waiting */
typedef struct Pool {
	Que          que;
#ifdef LSVTHR
	struct Wrk  *wait; /* stack */
#endif
} Pool;

typedef struct Wrk {
#ifdef LSVTHR
	pthread_t      thr;
#endif
	unsigned       id;
	struct Srv    *srv;
	struct Wrk    *nxt;
	Session        ses;
#ifdef LSVTHR
	pthread_cond_t todo; /* work condition waited for by workers */
#endif
	/* stats */
	struct Con    *cur; /* current job */
	int            jobs;
	int            waits;
} Wrk;

typedef struct Srv {
	/*
		the protocol stream method
	-	with LIO_SPUSH, input is pushed to request (len 0 indicates EOF)
		return 0=more, >0=done, <0=error
	-	on LIO_SFLUSH, prt must convert output buffer to flt
	- on LIO_SCLOSE, prt must flush and close answer
	*/
	int           (*prt) ( Ios*, int op );
	/*
		the application
	-	with task APPARG (called by prt for each field):
		check (and typically modify) last field extracted by prt
		return 0=more, >0=done, <0=error
	-	with task APPGOT (after request is complete):
		check request, add -LSV_SID field, if needed
		(e.g. to extract from an URL or cookie)
	-	with task APPRUN (0):
		process request
	*/
	int           (*app) ( struct Con*, int task );
	Fdt            *fdt; /* field def */
	int             flg; /* flags */
	unsigned        sto; /* config: session timeout (seconds) */
	unsigned        nwr; /* config: #workers (<= LSV_NUMWRK) */
	Session         ses; /* usually the default session */
	Tm              tim; /* tim of last turn */
	char            gtm[20]; /* generalized time (actually 18 digits) */
	Wrk             wrk[LSV_NUMWRK];
	/*
		single receiver model
	*/
	int             lsn; /* listening socket */
	Que             recv; /* enq by worker, deq by receiver */
	Pool            main; /* enq by dispatcher, deq by workers */
#ifdef LSVTHR
	Pool            pool; /* enq by dispatcher, deq by workers */
	pthread_t       rcv; /* the receiver thread */
	pthread_mutex_t mut; /* single mutex held by recv/disp while not in select */
	int             pip[2]; /* pipe for pending events */
#endif
	/* stats */
	int             plen; /* length of pool */
	int             jobs; /* total #jobs */
	int             conn; /* total #conn */
	int             turn; /* total #turn */
	float           wlen; /* cumulated length of work queue (on each turn) */
	float           busy; /* cumulated # of active workers (on each turn) */
} Srv;

enum { /* server flags */
	LSV_CONSES = 0x01, /* session bound to connection */
	LSV_WRKSES = 0x02, /* session bound to worker */
	LSV_SHUTDN = 0x100 /* server is shutting down */
};
enum {
	/* called in worker thread: */
	LSV_APPRUN, /* app called to process request */
	LSV_APPINI, /* app called to initialise non-main thread (no con) */
	LSV_APPFIN, /* app called to finalize non-main thread (no con) */
	/* called in receiver thread: */
	LSV_APPARG, /* app called to check parameter */
	LSV_APPGOT, /* app called after all input */
	LSV_APPSES  /* app called on new session */
};
enum { /* connection pending state */
	LSV_PDGNON, /* new connection pending */
	LSV_PDGNEW, /* new connection pending */
	LSV_PDGRED, /* ready to read pending */
	LSV_PDGEOF  /* EOF pending */
	/* when adding, keep array of names in sync */
};
enum { /* connection processing stage */
	LSV_STGNEW, /* connection needs to be setup */
	LSV_STGCON, /* connected, no request on the way */
	LSV_STGINP, /* reading input */
	LSV_STGSES, /* waiting for session */
	LSV_STGWRK, /* queued for work */
	/* set by worker: */
	LSV_STGRUN, /* dequeued */
	LSV_STGCOM, /* commited (output has started) */
	LSV_STGDON, /* done processing, flushing output */
	LSV_STGRET  /* returning (queued to recv) */
	/* when adding, keep array of names in sync */
};

typedef struct Con {
	Ios            ios; /* raw output */
	Buf            flt; /* protocol filtered output */
	const  Srv    *srv;
	struct Con    *nxt;
	unsigned char  stg; /* processing stage (guarded by mutex) */
	unsigned char  pdg; /* pending flags set by receiver only */
	unsigned char  grp; /* thread group (0=main) set by receiver app calls */
	unsigned char  bin; /* client requested binary mode */
	int            prt; /* for free use by prt, 0 init */
	int            app; /* for free use by app, 0 init */
	int            host;
	char           nam[64]; /* 123.123.123.123:123456 for IPv4 */
	Session        ses;
	Rec           *req; /* request -- owned by srv->ses */
	Tm             tim; /* time when the request was scheduled */
	/* Rec           *res; response -- owned by ses */
	int            con; /* connection counter */
} Con;

extern Wrk *svCur ();
extern int svRun ( Srv *srv, const char *addr );
/* the plain protocol */
extern int svPlain ( Ios *s, int op );
extern int svEcho ( Con *c, int task );

#define LSV_H
#endif /* LSV_H */