diff --git a/nsd/Makefile b/nsd/Makefile index 5193f6d..b82a0e9 100644 --- a/nsd/Makefile +++ b/nsd/Makefile @@ -27,7 +27,7 @@ # version of this file under either the License or the GPL. # # -# $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/Makefile,v 1.52 2006/06/27 16:35:55 jgdavidson Exp $ +# $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/Makefile,v 1.53 2009/12/08 04:12:19 jgdavidson Exp $ # INSTALL = install-init @@ -47,7 +47,7 @@ OBJS = adpcmds.o adpeval.o adpparse.o adprequest.o auth.o binder.o \ task.o tclcache.o tclcmds.o tclconf.o tclenv.o tclfile.o \ tclhttp.o tclimg.o tclinit.o tcljob.o tclloop.o tclmisc.o \ tclobj.o tclrequest.o tclresp.o tclsched.o tclset.o tclshare.o \ - tclsock.o tclthread.o tclvar.o tclxkeylist.o url.o \ + tclsock.o tclstore.o tclthread.o tclvar.o tclxkeylist.o url.o \ urlencode.o urlopen.o urlspace.o uuencode.o stamp.o UNIXOBJS= unix.o diff --git a/nsd/conn.c b/nsd/conn.c index 2a101f0..3074636 100644 --- a/nsd/conn.c +++ b/nsd/conn.c @@ -34,7 +34,7 @@ * Manage the Ns_Conn structure */ -static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/conn.c,v 1.49 2007/09/29 20:08:52 gneumann Exp $, compiled: " __DATE__ " " __TIME__; +static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/conn.c,v 1.50 2009/12/08 04:12:19 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" @@ -143,7 +143,9 @@ Ns_ConnAuthPasswd(Ns_Conn *conn) * * Ns_ConnContentLength -- * - * Get the content length from the client + * Get the content length to be sent from the client. + * Note the data may not have been all received if + * called in a "read" filter callback. * * Results: * An integer content length, or 0 if none sent @@ -160,6 +162,33 @@ Ns_ConnContentLength(Ns_Conn *conn) return conn->contentLength; } + +/* + *---------------------------------------------------------------------- + * + * Ns_ConnContentAvail -- + * + * Get the content currently available from the client. + * This will generally be all content unless it's called + * during a read filter callback during upload. + * + * Results: + * An integer content length, or 0 if none sent + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +Ns_ConnContentAvail(Ns_Conn *conn) +{ + Conn *connPtr = (Conn *) conn; + + return connPtr->avail; +} + /* *---------------------------------------------------------------------- @@ -1006,18 +1035,18 @@ NsTclConnObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv) char *content; static CONST char *opts[] = { - "authpassword", "authuser", "channel", "close", "content", "contentlength", - "contentsentlength", + "authpassword", "authuser", "channel", "close", + "contentavail", "content", "contentlength", "contentsentlength", "contentchannel", "copy", "driver", "encoding", "files", "fileoffset", "filelength", "fileheaders", "flags", "form", "headers", "host", "id", "isconnected", "location", "method", "outputheaders", "peeraddr", "peerport", "port", "protocol", "query", "request", "server", "sock", "start", "status", "url", "urlc", "urlencoding", "urlv", "version", - "write_encoded", NULL + "write_encoded", "interp", NULL }; enum { - CAuthPasswordIdx, CAuthUserIdx, CChannelIdx, CCloseIdx, CContentIdx, + CAuthPasswordIdx, CAuthUserIdx, CChannelIdx, CCloseIdx, CAvailIdx, CContentIdx, CContentLengthIdx, CContentSentLenIdx, CContentChannelIdx, CCopyIdx, CDriverIdx, CEncodingIdx, CFilesIdx, CFileOffIdx, CFileLenIdx, CFileHdrIdx, CFlagsIdx, CFormIdx, CHeadersIdx, CHostIdx, @@ -1025,7 +1054,7 @@ NsTclConnObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv) COutputHeadersIdx, CPeerAddrIdx, CPeerPortIdx, CPortIdx, CProtocolIdx, CQueryIdx, CRequestIdx, CServerIdx, CSockIdx, CStartIdx, CStatusIdx, CUrlIdx, CUrlcIdx, CUrlEncodingIdx, - CUrlvIdx, CVersionIdx, CWriteEncodedIdx + CUrlvIdx, CVersionIdx, CWriteEncodedIdx, CInterpIdx } opt; if (objc < 2) { @@ -1077,6 +1106,10 @@ NsTclConnObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv) Tcl_SetResult(interp, connPtr->authPasswd, TCL_STATIC); break; + case CAvailIdx: + Tcl_SetIntObj(result, connPtr->avail); + break; + case CContentChannelIdx: fd = Ns_ConnContentFd(conn); if (fd >= 0 && (fd = dup(fd)) >= 0) { @@ -1356,6 +1389,10 @@ NsTclConnObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv) return TCL_ERROR; } break; + + case CInterpIdx: + Tcl_SetLongObj(result, (long) interp); + break; } return TCL_OK; diff --git a/nsd/connio.c b/nsd/connio.c index f98bdf9..3c1158c 100644 --- a/nsd/connio.c +++ b/nsd/connio.c @@ -34,7 +34,7 @@ * Handle connection I/O. */ -static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/connio.c,v 1.27 2006/04/19 17:48:43 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; +static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/connio.c,v 1.28 2009/12/08 04:12:19 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" #define IOBUFSZ 2048 @@ -374,6 +374,10 @@ Ns_ConnSend(Ns_Conn *conn, struct iovec *bufs, int nbufs) nwrote = n; } + if (nwrote >= 0 + && NsRunFilters((Ns_Conn *) connPtr, NS_FILTER_WRITE) != NS_OK) { + nwrote = -1; + } return nwrote; } diff --git a/nsd/driver.c b/nsd/driver.c index a8f81e3..b29e744 100644 --- a/nsd/driver.c +++ b/nsd/driver.c @@ -34,7 +34,7 @@ * */ -static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/driver.c,v 1.58 2009/01/29 20:54:24 asteets Exp $, compiled: " __DATE__ " " __TIME__; +static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/driver.c,v 1.59 2009/12/08 04:12:19 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" @@ -43,12 +43,15 @@ static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd */ enum { + SOCK_ACCEPT, /* Socket has just been accepted. */ SOCK_READWAIT, /* Waiting for request or content from client. */ SOCK_PREQUE, /* Ready to invoke pre-queue filters. */ SOCK_QUEWAIT, /* Running pre-queue filters and event callbacks. */ SOCK_RUNWAIT, /* Ready to run when allowed by connection limits. */ SOCK_RUNNING, /* Running in a connection queue. */ + SOCK_CLOSEREQ, /* Close request in a read or pre-queue filter. */ SOCK_CLOSEWAIT, /* Graceful close wait draining remaining bytes .*/ + SOCK_CLOSED, /* Socket has been closed. */ SOCK_DROPPED, /* Client dropped connection. */ SOCK_TIMEOUT, /* Request timeout waiting to be queued. */ SOCK_OVERFLOW, /* Request was denied by connection limits. */ @@ -56,12 +59,15 @@ enum { }; char *states[] = { + "accept", "readwait", "preque", "quewait", "runwait", "running", + "closereq", "closewait", + "closed", "dropped", "timeout", "overflow", @@ -88,6 +94,8 @@ typedef enum { E_LRANGE, E_RRANGE, E_CRANGE, + E_FILTER, + E_QUEWAIT, } ReadErr; /* @@ -154,12 +162,14 @@ static int Poll(PollData *pdataPtr, SOCKET sock, int events, Ns_Time *timeoutPtr static Conn *AllocConn(Driver *drvPtr, Ns_Time *nowPtr, Sock *sockPtr); static void FreeConn(Conn *connPtr); static int RunQueWaits(PollData *pdataPtr, Ns_Time *nowPtr, Sock *sockPtr); +static int RunFilters(Conn *connPtr, int why); static void ThreadName(Driver *drvPtr, char *name); +static void SockState(Sock *sockPtr, int state); static void SockWait(Sock *sockPtr, Ns_Time *nowPtr, int timeout, Sock **listPtrPtr); static void AppendConn(Driver *drvPtr, Conn *connPtr); #define SockPush(s, sp) ((s)->nextPtr = *(sp), *(sp) = (s)) -static void LogReadError(Sock *sockPtr, ReadErr err); +static void LogReadError(Conn *connPtr, ReadErr err); /* * Static variables defined in this file. @@ -170,6 +180,7 @@ static Conn *firstConnPtr; /* Conn free list. */ static Ns_Mutex connlock; /* Lock around Conn free list. */ static Tcl_HashTable hosts; /* Host header to server table. */ static ServerMap *defMapPtr;/* Default server when not found in table. */ +static Ns_Tls drvtls; /* @@ -193,6 +204,7 @@ NsInitDrivers(void) { Ns_MutexSetName(&connlock, "ns:conns"); Tcl_InitHashTable(&hosts, TCL_STRING_KEYS); + Ns_TlsAlloc(&drvtls, NULL); } @@ -538,7 +550,7 @@ Ns_GetDriverContext(Ns_Driver drv) * socket. * * Results: - * None. + * NS_OK if registered, NS_ERROR otherwise. * * Side effects: * Proc will be called with given arg and sock as arguements @@ -549,13 +561,19 @@ Ns_GetDriverContext(Ns_Driver drv) *---------------------------------------------------------------------- */ -void +int Ns_QueueWait(Ns_Conn *conn, SOCKET sock, Ns_QueueWaitProc *proc, void *arg, int when, Ns_Time *timePtr) { Conn *connPtr = (Conn *) conn; QueWait *queWaitPtr; + Driver *drvPtr; + drvPtr = Ns_TlsGet(&drvtls); + if (connPtr->sockPtr == NULL || connPtr->sockPtr->drvPtr != Ns_TlsGet(&drvtls)) { + LogReadError(connPtr, E_QUEWAIT); + return NS_ERROR; + } queWaitPtr = ns_malloc(sizeof(QueWait)); queWaitPtr->proc = proc; queWaitPtr->arg = arg; @@ -570,6 +588,7 @@ Ns_QueueWait(Ns_Conn *conn, SOCKET sock, Ns_QueueWaitProc *proc, queWaitPtr->nextPtr = connPtr->queWaitPtr; connPtr->queWaitPtr = queWaitPtr; queWaitPtr->timeout = *timePtr; + return NS_OK; } @@ -828,6 +847,16 @@ NsSockClose(Sock *sockPtr, int keep) Driver *drvPtr = sockPtr->drvPtr; Ns_Sock *sock = (Ns_Sock *) sockPtr; + /* + * Defer the close if not requested from a connection thread + * which could happen in a read or pre-queue callback. + */ + + if (sockPtr->state != SOCK_RUNNING) { + SockState(sockPtr, SOCK_CLOSEREQ); + return; + } + /* * If keepalive is requested and enabled, set the read wait * state. Otherwise, set close wait which simply drains any @@ -836,9 +865,9 @@ NsSockClose(Sock *sockPtr, int keep) if (keep && drvPtr->keepwait > 0 && (*drvPtr->proc)(DriverKeep, sock, NULL, 0) == 0) { - sockPtr->state = SOCK_READWAIT; + SockState(sockPtr, SOCK_READWAIT); } else { - sockPtr->state = SOCK_CLOSEWAIT; + SockState(sockPtr, SOCK_CLOSEWAIT); } Ns_MutexLock(&drvPtr->lock); sockPtr->nextPtr = drvPtr->closeSockPtr; @@ -920,6 +949,7 @@ DriverThread(void *arg) Sock *preqSockPtr = NULL; /* Sock's ready for pre-queue callbacks. */ Sock *queSockPtr = NULL; /* Sock's ready to queue. */ + Ns_TlsSet(&drvtls, drvPtr); ThreadName(drvPtr, "driver"); /* @@ -1045,7 +1075,7 @@ DriverThread(void *arg) if ((sockPtr = connPtr->sockPtr) != NULL) { connPtr->sockPtr = NULL; - sockPtr->state = SOCK_CLOSEWAIT; + SockState(sockPtr, SOCK_CLOSEWAIT); SockPush(sockPtr, &closePtr); } FreeConn(connPtr); @@ -1184,35 +1214,51 @@ DriverThread(void *arg) } /* - * Process sockets ready to run, starting with pre-queue callbacks. + * Process sockets ready to run. */ while ((sockPtr = preqSockPtr) != NULL) { preqSockPtr = sockPtr->nextPtr; sockPtr->connPtr->times.ready = now; - if (sockPtr->state != SOCK_PREQUE || - NsRunFilters((Ns_Conn *) sockPtr->connPtr, - NS_FILTER_PRE_QUEUE) != NS_OK) { + /* + * Invoke any pre-queue filters + */ + + if (sockPtr->state == SOCK_PREQUE + && !RunFilters(sockPtr->connPtr, NS_FILTER_PRE_QUEUE)) { + if (sockPtr->state != SOCK_CLOSEREQ) { + SockState(sockPtr, SOCK_ERROR); + } + } + if (sockPtr->state != SOCK_PREQUE) { + /* NB: Should be one of SOCK_ERROR or SOCK_CLOSEREQ. */ SockClose(sockPtr); } else if (sockPtr->connPtr->queWaitPtr != NULL) { - /* NB: Sock timeout ignored during que wait. */ - sockPtr->state = SOCK_QUEWAIT; - SockPush(sockPtr, &waitPtr); + /* NB: Sock timeout ignored during que wait. */ + SockState(sockPtr, SOCK_QUEWAIT); + SockPush(sockPtr, &waitPtr); } else { - SockPush(sockPtr, &queSockPtr); + SockPush(sockPtr, &queSockPtr); } } /* - * Add Sock's now ready to the queue. + * Add Sock's now ready to queue, freeing any connection + * interp which may have been allocated for que-wait callbacks + * and/or pre-queue filters. */ while ((sockPtr = queSockPtr) != NULL) { queSockPtr = sockPtr->nextPtr; - connPtr = sockPtr->connPtr; - sockPtr->timeout = connPtr->times.queue = now; - Ns_IncrTime(&sockPtr->timeout, connPtr->limitsPtr->timeout, 0); - AppendConn(drvPtr, connPtr); + connPtr = sockPtr->connPtr; + NsFreeConnInterp(connPtr); + if (sockPtr->state == SOCK_ERROR) { + SockClose(sockPtr); + } else { + sockPtr->timeout = connPtr->times.queue = now; + Ns_IncrTime(&sockPtr->timeout, connPtr->limitsPtr->timeout, 0); + AppendConn(drvPtr, connPtr); + } } /* @@ -1230,24 +1276,24 @@ DriverThread(void *arg) --limitsPtr->nwaiting; if (PollIn(&pdata, sockPtr->pidx)) { ++drvPtr->stats.dropped; - sockPtr->state = SOCK_DROPPED; + SockState(sockPtr, SOCK_DROPPED); goto dropped; } } if (limitsPtr->nrunning < limitsPtr->maxrun) { ++limitsPtr->nrunning; - sockPtr->state = SOCK_RUNNING; + SockState(sockPtr, SOCK_RUNNING); } else if (Ns_DiffTime(&sockPtr->timeout, &now, NULL) <= 0) { ++limitsPtr->ntimeout; ++drvPtr->stats.timeout; - sockPtr->state = SOCK_TIMEOUT; + SockState(sockPtr, SOCK_TIMEOUT); } else if (limitsPtr->nwaiting < limitsPtr->maxwait) { ++limitsPtr->nwaiting; - sockPtr->state = SOCK_RUNWAIT; + SockState(sockPtr, SOCK_RUNWAIT); } else { ++limitsPtr->noverflow; ++drvPtr->stats.overflow; - sockPtr->state = SOCK_OVERFLOW; + SockState(sockPtr, SOCK_OVERFLOW); } dropped: Ns_MutexUnlock(&limitsPtr->lock); @@ -1415,6 +1461,33 @@ Poll(PollData *pdataPtr, SOCKET sock, int events, Ns_Time *timeoutPtr) return idx; } + +/* + *---------------------------------------------------------------------- + * + * SockState -- + * + * Set the socket state. + * + * Results: + * None. + * + * Side effects: + * Updates sockPtr->state. + * + *---------------------------------------------------------------------- + */ + +static void +SockState(Sock *sockPtr, int state) +{ + if (sockPtr->drvPtr->flags & DRIVER_DEBUG) { + Ns_Log(Notice, "%s[%d]: %s -> %s\n", sockPtr->drvPtr->name, + sockPtr->sock, states[sockPtr->state], states[state]); + } + sockPtr->state = state; +} + /* *---------------------------------------------------------------------- @@ -1504,7 +1577,8 @@ SockAccept(SOCKET lsock, Driver *drvPtr) } sockPtr->id = drvPtr->nextid++; sockPtr->drvPtr = drvPtr; - sockPtr->state = SOCK_READWAIT; + sockPtr->state = SOCK_ACCEPT; + SockState(sockPtr, SOCK_READWAIT); sockPtr->arg = NULL; sockPtr->connPtr = NULL; @@ -1562,12 +1636,14 @@ SockClose(Sock *sockPtr) */ if (sockPtr->connPtr != NULL) { + NsFreeConnInterp(sockPtr->connPtr); FreeConn(sockPtr->connPtr); sockPtr->connPtr = NULL; } (void) (*drvPtr->proc)(DriverClose, (Ns_Sock *) sockPtr, NULL, 0); ns_sockclose(sockPtr->sock); + SockState(sockPtr, SOCK_CLOSED); sockPtr->sock = INVALID_SOCKET; drvPtr->stats.reads += sockPtr->nreads; drvPtr->stats.writes += sockPtr->nwrites; @@ -1632,7 +1708,10 @@ SockRead(Sock *sockPtr) Ns_Sock *sock = (Ns_Sock *) sockPtr; ReadErr err; - //errno = 0; + /* + * Read any waiting request+headers and/or content. + */ + ++sockPtr->nreads; if ((connPtr->flags & NS_CONN_READHDRS)) { err = SockReadContent(drvPtr, sock, connPtr); @@ -1641,28 +1720,36 @@ SockRead(Sock *sockPtr) } /* - * Mark the connection ready if all input received, truncating any - * extra \r\n and rewinding the input. + * If the request+headers have been received, check that all content + * has been received (truncating any extra \r\n and rewinding + * file-based content) and/or invoke read filter callbacks. */ - if (!err - && (connPtr->flags & NS_CONN_READHDRS) - && connPtr->avail >= (size_t) connPtr->contentLength) { - connPtr->avail = connPtr->contentLength; - if (!(connPtr->flags & NS_CONN_FILECONTENT)) { - connPtr->content[connPtr->avail] = '\0'; - } else if (ftruncate(connPtr->tfd, connPtr->avail) != 0) { - err = E_FDTRUNC; - } else if (lseek(connPtr->tfd, (off_t) 0, SEEK_SET) != 0) { - err = E_FDSEEK; - } - if (!err) { - sockPtr->state = SOCK_PREQUE; + if (!err && (connPtr->flags & NS_CONN_READHDRS)) { + if (!RunFilters(connPtr, NS_FILTER_READ)) { + err = E_FILTER; + } else if (connPtr->avail >= (size_t) connPtr->contentLength) { + connPtr->avail = connPtr->contentLength; + if (!(connPtr->flags & NS_CONN_FILECONTENT)) { + connPtr->content[connPtr->avail] = '\0'; + } else { + if (ftruncate(connPtr->tfd, connPtr->avail) != 0) { + err = E_FDTRUNC; + } else if (lseek(connPtr->tfd, (off_t) 0, SEEK_SET) != 0) { + err = E_FDSEEK; + } + } + if (!err) { + SockState(sockPtr, SOCK_PREQUE); + } } } + if (err) { - LogReadError(sockPtr, err); - sockPtr->state = SOCK_ERROR; + if (sockPtr->state != SOCK_CLOSEREQ) { + SockState(sockPtr, SOCK_ERROR); + } + LogReadError(connPtr, err); } } @@ -2131,8 +2218,8 @@ RunQueWaits(PollData *pdataPtr, Ns_Time *nowPtr, Sock *sockPtr) static Conn * AllocConn(Driver *drvPtr, Ns_Time *nowPtr, Sock *sockPtr) { - Conn *connPtr; static unsigned int nextid = 0; + Conn *connPtr; int id; Ns_MutexLock(&connlock); @@ -2155,6 +2242,7 @@ AllocConn(Driver *drvPtr, Ns_Time *nowPtr, Sock *sockPtr) connPtr->tfd = -1; connPtr->headers = Ns_SetCreate(NULL); + connPtr->outputheaders = Ns_SetCreate(NULL); Tcl_InitHashTable(&connPtr->files, TCL_STRING_KEYS); connPtr->drvPtr = drvPtr; connPtr->times.accept = *nowPtr; @@ -2164,6 +2252,7 @@ AllocConn(Driver *drvPtr, Ns_Time *nowPtr, Sock *sockPtr) strcpy(connPtr->peer, ns_inet_ntoa(sockPtr->sa.sin_addr)); connPtr->times.accept = sockPtr->acceptTime; connPtr->sockPtr = sockPtr; + return connPtr; } @@ -2200,6 +2289,12 @@ FreeConn(Conn *connPtr) * Free resources which may have been allocated during the request. */ + if (connPtr->type != NULL) { + Ns_ConnSetType(conn, NULL); + } + if (connPtr->query != NULL) { + Ns_ConnClearQuery(conn); + } if (conn->request != NULL) { Ns_FreeRequest(conn->request); } @@ -2213,6 +2308,7 @@ FreeConn(Conn *connPtr) ns_free(connPtr->authUser); } Ns_SetFree(connPtr->headers); + Ns_SetFree(connPtr->outputheaders); /* * Truncate the I/O buffers, zero remaining elements of the Conn, @@ -2220,6 +2316,7 @@ FreeConn(Conn *connPtr) * */ + Ns_DStringTrunc(&connPtr->obuf, 0); Ns_DStringTrunc(&connPtr->ibuf, 0); zlen = (size_t) ((char *) &connPtr->ibuf - (char *) connPtr); memset(connPtr, 0, zlen); @@ -2230,6 +2327,32 @@ FreeConn(Conn *connPtr) Ns_MutexUnlock(&connlock); } + +/* + *---------------------------------------------------------------------- + * + * RunPreQueues -- + * + * Execute any pre-queue callbacks, freeing any allocated interp. + * + * Results: + * 1 if ok, 0 otherwise. + * + * Side effects: + * Depends on callbacks, if any. + * + *---------------------------------------------------------------------- + */ + +static int +RunFilters(Conn *connPtr, int why) +{ + int status; + + status = NsRunFilters((Ns_Conn *) connPtr, why); + return (status == NS_OK ? 1 : 0); +} + /* *---------------------------------------------------------------------- @@ -2280,9 +2403,12 @@ ReaderThread(void *arg) } while (sockPtr->state == SOCK_READWAIT); /* - * Return the connection to the driver thread. + * Return the connection to the driver thread, freeing + * any connection interp which may have been allocated + * by read filter callbacks. */ + NsFreeConnInterp(sockPtr->connPtr); Ns_MutexLock(&drvPtr->lock); sockPtr->nextPtr = drvPtr->runSockPtr; drvPtr->runSockPtr = sockPtr; @@ -2340,8 +2466,9 @@ ThreadName(Driver *drvPtr, char *name) */ static void -LogReadError(Sock *sockPtr, ReadErr err) +LogReadError(Conn *connPtr, ReadErr err) { + Sock *sockPtr = connPtr->sockPtr; char *msg, *fmt; if (!(sockPtr->drvPtr->flags & DRIVER_DEBUG)) { @@ -2393,6 +2520,12 @@ LogReadError(Sock *sockPtr, ReadErr err) case E_CRANGE: msg = "max content exceeded"; break; + case E_FILTER: + msg = "filter error or abort result"; + break; + case E_QUEWAIT: + msg = "attempt to register quewait outside driver thread"; + break; default: msg = "unknown error"; } @@ -2407,5 +2540,5 @@ LogReadError(Sock *sockPtr, ReadErr err) fmt = "conn[%d]: %s"; break; } - Ns_Log(Error, fmt, sockPtr->connPtr->id, msg, strerror(errno)); + Ns_Log(Error, fmt, connPtr->id, msg, strerror(errno)); } diff --git a/nsd/queue.c b/nsd/queue.c index ce93e3a..5c9ac5b 100644 --- a/nsd/queue.c +++ b/nsd/queue.c @@ -34,9 +34,10 @@ * and service threads. */ -static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/queue.c,v 1.46 2008/12/27 00:36:38 gneumann Exp $, compiled: " __DATE__ " " __TIME__; +static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/queue.c,v 1.47 2009/12/08 04:12:19 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" +#include /* * The following structure is allocated for each new thread. The @@ -557,16 +558,15 @@ NsConnThread(void *arg) static void ConnRun(Conn *connPtr) { + Tcl_Encoding encoding = NULL; Ns_Conn *conn = (Ns_Conn *) connPtr; NsServer *servPtr = connPtr->servPtr; - int i, status; - Tcl_Encoding encoding = NULL; + int i, status; /* - * Initialize the connection encodings and headers. + * Initialize the connection encodings. */ - connPtr->outputheaders = Ns_SetCreate(NULL); encoding = NsGetInputEncoding(connPtr); if (encoding == NULL) { encoding = NsGetOutputEncoding(connPtr); @@ -574,10 +574,10 @@ ConnRun(Conn *connPtr) encoding = connPtr->servPtr->urlEncoding; } } - Ns_ConnSetUrlEncoding(conn, encoding); - if (servPtr->opts.hdrcase != Preserve) { + Ns_ConnSetUrlEncoding((Ns_Conn *) connPtr, encoding); + if (connPtr->servPtr->opts.hdrcase != Preserve) { for (i = 0; i < Ns_SetSize(connPtr->headers); ++i) { - if (servPtr->opts.hdrcase == ToLower) { + if (connPtr->servPtr->opts.hdrcase == ToLower) { Ns_StrToLower(Ns_SetKey(connPtr->headers, i)); } else { Ns_StrToUpper(Ns_SetKey(connPtr->headers, i)); @@ -644,17 +644,6 @@ ConnRun(Conn *connPtr) NsRunCleanups(conn); NsFreeConnInterp(connPtr); - if (connPtr->type != NULL) { - Ns_ConnSetType(conn, NULL); - } - if (connPtr->query != NULL) { - Ns_ConnClearQuery(conn); - } - if (connPtr->outputheaders != NULL) { - Ns_SetFree(connPtr->outputheaders); - connPtr->outputheaders = NULL; - } - Ns_DStringTrunc(&connPtr->obuf, 0); } diff --git a/nsd/tclcmds.c b/nsd/tclcmds.c index 5cf71e7..9cc9cde 100644 --- a/nsd/tclcmds.c +++ b/nsd/tclcmds.c @@ -33,7 +33,7 @@ * Connect Tcl command names to the functions that implement them */ -static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/tclcmds.c,v 1.56 2006/06/26 00:28:02 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; +static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/tclcmds.c,v 1.57 2009/12/08 04:12:19 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" @@ -73,6 +73,7 @@ extern Tcl_ObjCmdProc NsTclCacheObjCmd, NsTclChanObjCmd, NsTclChmodObjCmd, + NsTclClsObjCmd, NsTclCondObjCmd, NsTclConnObjCmd, NsTclConnSendFpObjCmd, @@ -126,6 +127,7 @@ extern Tcl_ObjCmdProc NsTclParseQueryObjCmd, NsTclPoolsObjCmd, NsTclPurgeFilesObjCmd, + NsTclQueWaitObjCmd, NsTclRWLockObjCmd, NsTclRandObjCmd, NsTclRegisterAdpObjCmd, @@ -170,6 +172,7 @@ extern Tcl_ObjCmdProc NsTclSymlinkObjCmd, NsTclThreadObjCmd, NsTclTimeObjCmd, + NsTclTlsObjCmd, NsTclTmpNamObjCmd, NsTclTruncateObjCmd, NsTclUnRegisterObjCmd, @@ -293,6 +296,7 @@ static Cmd cmds[] = { {"ns_charsets", NsTclCharsetsCmd, NULL}, {"ns_checkurl", NULL, NsTclRequestAuthorizeObjCmd}, {"ns_chmod", NULL, NsTclChmodObjCmd}, + {"ns_cls", NULL, NsTclClsObjCmd}, {"ns_cond", NULL, NsTclCondObjCmd}, {"ns_config", NsTclConfigCmd, NULL}, {"ns_configsection", NsTclConfigSectionCmd, NULL}, @@ -349,6 +353,7 @@ static Cmd cmds[] = { {"ns_pools", NULL, NsTclPoolsObjCmd}, {"ns_purgefiles", NULL, NsTclPurgeFilesObjCmd}, {"ns_puts", NULL, NsTclAdpPutsObjCmd}, + {"ns_quewait", NULL, NsTclQueWaitObjCmd}, {"ns_quotehtml", NsTclQuoteHtmlCmd, NULL}, {"ns_rand", NULL, NsTclRandObjCmd}, {"ns_register_adp", NULL, NsTclRegisterAdpObjCmd}, @@ -400,6 +405,7 @@ static Cmd cmds[] = { {"ns_symlink", NULL, NsTclSymlinkObjCmd}, {"ns_thread", NULL, NsTclThreadObjCmd}, {"ns_time", NULL, NsTclTimeObjCmd}, + {"ns_tls", NULL, NsTclTlsObjCmd}, {"ns_tmpnam", NULL, NsTclTmpNamObjCmd}, {"ns_truncate", NULL, NsTclTruncateObjCmd}, {"ns_unlink", NULL, NsTclUnlinkObjCmd}, diff --git a/nsd/tclrequest.c b/nsd/tclrequest.c index 4b2a410..82bb98b 100644 --- a/nsd/tclrequest.c +++ b/nsd/tclrequest.c @@ -33,7 +33,7 @@ * Routines for Tcl proc and ADP registered requests. */ -static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/tclrequest.c,v 1.13 2005/08/23 22:05:04 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; +static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/tclrequest.c,v 1.14 2009/12/08 04:12:19 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" @@ -257,10 +257,10 @@ NsTclRegisterFilterObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj Tcl_Obj **lobjv; int when, i; static CONST char *wopt[] = { - "prequeue", "preauth", "postauth", "trace", NULL + "read", "write", "prequeue", "preauth", "postauth", "trace", NULL }; enum { - PQIdx, PRIdx, POIdx, TRIdx, + ReadIdx, WriteIdx, PreQueueIdx, PreAuthIdx, PostAuthIdx, TraceIdx, } widx; if (objc < 2) { @@ -281,16 +281,22 @@ NsTclRegisterFilterObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj return TCL_ERROR; } switch (widx) { - case PQIdx: + case ReadIdx: + when |= NS_FILTER_READ; + break; + case WriteIdx: + when |= NS_FILTER_WRITE; + break; + case PreQueueIdx: when |= NS_FILTER_PRE_QUEUE; break; - case PRIdx: + case PreAuthIdx: when |= NS_FILTER_PRE_AUTH; break; - case POIdx: + case PostAuthIdx: when |= NS_FILTER_POST_AUTH; break; - case TRIdx: + case TraceIdx: when |= NS_FILTER_TRACE; break; } @@ -504,6 +510,15 @@ ProcFilter(void *arg, Ns_Conn *conn, int why) Tcl_DStringAppendElement(&script, procPtr->args ? procPtr->args : ""); } switch (why) { + case NS_FILTER_READ: + Tcl_DStringAppendElement(&script, "read"); + break; + case NS_FILTER_WRITE: + Tcl_DStringAppendElement(&script, "write"); + break; + case NS_FILTER_PRE_QUEUE: + Tcl_DStringAppendElement(&script, "prequeue"); + break; case NS_FILTER_PRE_AUTH: Tcl_DStringAppendElement(&script, "preauth"); break; diff --git a/nsd/tclsock.c b/nsd/tclsock.c index 3809387..325b975 100644 --- a/nsd/tclsock.c +++ b/nsd/tclsock.c @@ -27,14 +27,13 @@ * version of this file under either the License or the GPL. */ - /* * tclsock.c -- * * Tcl commands that let you do TCP sockets. */ -static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/tclsock.c,v 1.24 2005/08/02 22:11:58 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; +static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/tclsock.c,v 1.25 2009/12/08 04:12:19 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" @@ -72,6 +71,7 @@ static int EnterDupedSocks(Tcl_Interp *interp, SOCKET sock); static int SockSetBlockingObj(char *value, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); static Ns_SockProc SockListenCallback; +static Ns_QueueWaitProc WaitCallback; void NsTclSockArgProc(Tcl_DString *dsPtr, void *arg) @@ -81,6 +81,81 @@ NsTclSockArgProc(Tcl_DString *dsPtr, void *arg) Tcl_DStringAppendElement(dsPtr, cbPtr->script); } + +/* + *---------------------------------------------------------------------- + * + * NsTclQueWaitObjCmd -- + * + * Implement the ns_quewait command to register a Tcl script + * Ns_QueueWait callback. Unlike the general ns_sockcallback, + * the script will execute later in the same, connection-bound + * interp which calls ns_quewait. Thus the interface is + * closer to Tcl's standard "fileevent" command. + * + * Results: + * Tcl result. + * + * Side effects: + * Depends on callbacks. + * + *---------------------------------------------------------------------- + */ + +int +NsTclQueWaitObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + TclSockCallback *cbPtr; + Ns_Conn *conn; + Tcl_Channel chan; + SOCKET sock; + int when; + Ns_Time timeout; + static CONST char *opt[] = { + "readable", "writable", NULL + }; + enum { + ReadIdx, WriteIdx + } idx; + + if (objc != 5) { + Tcl_WrongNumArgs(interp, 1, objv, "sockId event timeout script"); + return TCL_ERROR; + } + if (NsTclGetConn((NsInterp *) arg, &conn) != TCL_OK) { + return TCL_ERROR; + } + chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL); + if (chan == NULL) { + return TCL_ERROR; + } + if (Tcl_GetIndexFromObj(interp, objv[2], opt, "event", 0, + (int *) &idx) != TCL_OK) { + return TCL_ERROR; + } + switch (idx) { + case ReadIdx: + when = NS_SOCK_READ; + break; + case WriteIdx: + when = NS_SOCK_WRITE; + break; + } + if (Ns_TclGetOpenFd(interp, Tcl_GetString(objv[1]), + (idx == WriteIdx) ? 1 :0, (int *) &sock) != TCL_OK) { + return TCL_ERROR; + } + if (Ns_TclGetTimeFromObj(interp, objv[3], &timeout) != TCL_OK) { + return TCL_ERROR; + } + if (Ns_QueueWait(conn, sock, WaitCallback, objv[4], when, &timeout) != NS_OK) { + Tcl_SetResult(interp, "could not register sock wait", TCL_STATIC); + return TCL_ERROR; + } + Tcl_IncrRefCount(objv[4]); + return TCL_OK; +} + /* *---------------------------------------------------------------------- @@ -1130,6 +1205,61 @@ NsTclSockProc(SOCKET sock, void *arg, int why) return NS_TRUE; } + +/* + *---------------------------------------------------------------------- + * + * WaitCallback -- + * + * This is the C wrapper Ns_QueueWait callback registered in + * ns_quewait. + * + * Results: + * None. + * + * Side effects: + * Will run Tcl script. + * + *---------------------------------------------------------------------- + */ + +static void +WaitCallback(Ns_Conn *conn, SOCKET sock, void *arg, int why) +{ + Tcl_Interp *interp = Ns_GetConnInterp(conn); + Tcl_Obj *objPtr = arg; + Tcl_DString ds; + char *s; + int len; + + Tcl_DStringInit(&ds); + s = Tcl_GetStringFromObj(objPtr, &len); + Tcl_DStringAppend(&ds, s, len); + + /* + * NB: While the C interface allows for a single callback + * to be registered (NS_SOCK_READ | NS_SOCK_WRITE), the + * ns_quewait command enforces only readable or writable + * at a time. + */ + + if (why == 0) { + s = "timeout"; + } else if (why & NS_SOCK_READ) { + s = "readable"; + } else if (why & NS_SOCK_WRITE) { + s = "writable"; + } else if (why & NS_SOCK_DROP) { + s = "dropped"; + } + Tcl_DStringAppendElement(&ds, s); + if (Tcl_EvalEx(interp, ds.string, ds.length, 0) != TCL_OK) { + Ns_TclLogError(interp); + } + Tcl_DStringFree(&ds); + Tcl_DecrRefCount(objPtr); +} + /* *---------------------------------------------------------------------- diff --git a/nsd/tclstore.c b/nsd/tclstore.c new file mode 100644 index 0000000..534ee9a --- /dev/null +++ b/nsd/tclstore.c @@ -0,0 +1,146 @@ +/* + * The contents of this file are subject to the AOLserver Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://aolserver.com/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is AOLserver Code and related documentation + * distributed by AOL. + * + * The Initial Developer of the Original Code is America Online, + * Inc. Portions created by AOL are Copyright (C) 1999 America Online, + * Inc. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License (the "GPL"), in which case the + * provisions of GPL are applicable instead of those above. If you wish + * to allow use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under the + * License, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the License or the GPL. + */ + +/* + * tclstore.c -- + * + * Thread and connection local storage Tcl commands. + */ + +static const char *RCSID = "@(#) $Header: /Users/dossy/Desktop/cvs/aolserver/nsd/tclstore.c,v 1.1 2009/12/08 04:12:20 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; + +#include "nsd.h" + +/* + * Static functions defined in this file. + */ + +static int StoreObjCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj **objv, int tls); + + +/* + *---------------------------------------------------------------------- + * + * NsTclTlsObjCmd, NsTclClsObjCmd -- + * + * Implements ns_tls and ns_cls as obj commands. + * + * Results: + * Tcl result. + * + * Side effects: + * See docs. + * + *---------------------------------------------------------------------- + */ + +int +NsTclTlsObjCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj **objv, Ns_Conn *conn) +{ + return StoreObjCmd(data, interp, objc, objv, 1); +} + +int +NsTclClsObjCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj **objv, Ns_Conn *conn) +{ + return StoreObjCmd(data, interp, objc, objv, 0); +} + +static int +StoreObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv, int tls) +{ + static CONST char *opts[] = { + "alloc", "get", "set", NULL + }; + enum { + AllocIdx, GetIdx, SetIdx + } _nsmayalias opt; + Ns_Conn *conn; + char *val; + int id; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0, + (int *) &opt) != TCL_OK) { + return TCL_ERROR; + } + if (opt == AllocIdx) { + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; + } + if (tls) { + Ns_TlsAlloc((Ns_Tls *) &id, ns_free); + } else { + Ns_ClsAlloc((Ns_Cls *) &id, ns_free); + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(id)); + } else { + if (!tls && NsTclGetConn((NsInterp *) arg, &conn) != TCL_OK) { + return TCL_ERROR; + } + if (objc < 3) { + Tcl_WrongNumArgs(interp, 2, objv, "index"); + return TCL_ERROR; + } + if (Tcl_GetIntFromObj(interp, objv[2], &id) != TCL_OK) { + return TCL_ERROR; + } + if (id < 1 || id >= (tls ? NS_THREAD_MAXTLS : NS_CONN_MAXCLS)) { + Tcl_AppendResult(interp, "invalid id: ", Tcl_GetString(objv[2]), NULL); + return TCL_ERROR; + } + if (tls) { + val = Ns_TlsGet((Ns_Tls *) &id); + } else { + val = Ns_ClsGet((Ns_Cls *) &id, conn); + } + if (opt == GetIdx) { + Tcl_SetResult(interp, val, TCL_VOLATILE); + } else { + if (objc != 4) { + Tcl_WrongNumArgs(interp, 2, objv, "index value"); + return TCL_ERROR; + } + if (val) { + ns_free(val); + } + val = ns_strdup(Tcl_GetString(objv[3])); + if (tls) { + Ns_TlsSet((Ns_Tls *) &id, (void **) val); + } else { + Ns_ClsSet((Ns_Cls *) &id, conn, (void **) val); + } + } + } + return TCL_OK; +}