/*
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 */