/* * 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. */ /* * op.c -- * * Routines to register, unregister, and run connection request * routines (previously known as "op procs"). */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/op.c,v 1.15 2005/10/07 00:48:23 dossy Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" #define MAX_RECURSION 3 /* Max return direct recursion limit. */ /* * The following structure defines a request procedure including user * routine and client data. */ typedef struct { int refcnt; Ns_OpProc *proc; Ns_Callback *delete; void *arg; unsigned int flags; } Req; /* * Static functions defined in this file. */ static void FreeReq(void *arg); /* * Static variables defined in this file. */ static Ns_Mutex ulock; static int uid; /* *---------------------------------------------------------------------- * * NsInitRequests -- * * Initialize the request API. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void NsInitRequests(void) { uid = Ns_UrlSpecificAlloc(); Ns_MutexInit(&ulock); Ns_MutexSetName(&ulock, "nsd:requests"); } /* *---------------------------------------------------------------------- * * Ns_RegisterRequest -- * * Register a new procedure to be called to service matching * given method and url pattern. * * Results: * None. * * Side effects: * Delete procedure of previously registered request, if any, * will be called unless NS_OP_NODELETE flag is set. * *---------------------------------------------------------------------- */ void Ns_RegisterRequest(char *server, char *method, char *url, Ns_OpProc *proc, Ns_Callback *delete, void *arg, int flags) { Req *reqPtr; reqPtr = ns_malloc(sizeof(Req)); reqPtr->proc = proc; reqPtr->delete = delete; reqPtr->arg = arg; reqPtr->flags = flags; reqPtr->refcnt = 1; Ns_MutexLock(&ulock); Ns_UrlSpecificSet(server, method, url, uid, reqPtr, flags, FreeReq); Ns_MutexUnlock(&ulock); } /* *---------------------------------------------------------------------- * * Ns_GetRequest -- * * Return the procedures and context for a given method and url * pattern. * * Results: * None. * * Side effects: * * *---------------------------------------------------------------------- */ void Ns_GetRequest(char *server, char *method, char *url, Ns_OpProc **procPtr, Ns_Callback **deletePtr, void **argPtr, int *flagsPtr) { Req *reqPtr; Ns_MutexLock(&ulock); reqPtr = Ns_UrlSpecificGet(server, method, url, uid); if (reqPtr != NULL) { *procPtr = reqPtr->proc; *deletePtr = reqPtr->delete; *argPtr = reqPtr->arg; *flagsPtr = reqPtr->flags; } else { *procPtr = NULL; *deletePtr = NULL; *argPtr = NULL; *flagsPtr = 0; } Ns_MutexUnlock(&ulock); } /* *---------------------------------------------------------------------- * * Ns_UnRegisterRequest -- * * Remove the procedure which would run for the given method and * url pattern. * * Results: * None. * * Side effects: * Requests deleteProc may run. * *---------------------------------------------------------------------- */ void Ns_UnRegisterRequest(char *server, char *method, char *url, int inherit) { Ns_MutexLock(&ulock); Ns_UrlSpecificDestroy(server, method, url, uid, inherit ? 0 : NS_OP_NOINHERIT); Ns_MutexUnlock(&ulock); } /* *---------------------------------------------------------------------- * * Ns_ConnRunRequest -- * * Locate and execute the procedure for the given method and * url pattern. * * Results: * Standard request procedure result, normally NS_OK. * * Side effects: * Depends on request procedure. * *---------------------------------------------------------------------- */ int Ns_ConnRunRequest(Ns_Conn *conn) { Req *reqPtr; Conn *connPtr = (Conn *) conn; int status; char *server = Ns_ConnServer(conn); /* * Return a quick unavailable error on overflow. */ if (connPtr->flags & NS_CONN_OVERFLOW) { return Ns_ConnReturnServiceUnavailable(conn); } /* * Prevent infinite internal redirect loops. */ if (connPtr->recursionCount > MAX_RECURSION) { Ns_Log(Error, "return: failed to redirect '%s %s': " "exceeded recursion limit of %d", conn->request->method, conn->request->url, MAX_RECURSION); return Ns_ConnReturnInternalError(conn); } Ns_MutexLock(&ulock); reqPtr = Ns_UrlSpecificGet(server, conn->request->method, conn->request->url, uid); if (reqPtr == NULL) { Ns_MutexUnlock(&ulock); return Ns_ConnReturnNotFound(conn); } ++reqPtr->refcnt; Ns_MutexUnlock(&ulock); status = (*reqPtr->proc) (reqPtr->arg, conn); Ns_MutexLock(&ulock); FreeReq(reqPtr); Ns_MutexUnlock(&ulock); return status; } /* *---------------------------------------------------------------------- * * Ns_ConnRedirect -- * * Perform an internal redirect by updating the connection's * request URL and re-authorizing and running the request. This * Routine is used in FastPath to redirect to directory files * (e.g., index.html) and in return.c to redirect by HTTP result * code (e.g., custom not-found handler). * * Results: * Standard request procedure result, normally NS_OK. * * Side effects: * Depends on request procedure. * *---------------------------------------------------------------------- */ int Ns_ConnRedirect(Ns_Conn *conn, char *url) { Conn *connPtr = (Conn *) conn; int status; ++connPtr->recursionCount; /* * Update the request URL. */ Ns_SetRequestUrl(conn->request, url); /* * Re-authorize and run the request. */ status = Ns_AuthorizeRequest(Ns_ConnServer(conn), conn->request->method, conn->request->url, conn->authUser, conn->authPasswd, Ns_ConnPeer(conn)); switch (status) { case NS_OK: status = Ns_ConnRunRequest(conn); break; case NS_FORBIDDEN: status = Ns_ConnReturnForbidden(conn); break; case NS_UNAUTHORIZED: status = Ns_ConnReturnUnauthorized(conn); break; case NS_ERROR: default: status = Ns_ConnReturnInternalError(conn); break; } return status; } /* *---------------------------------------------------------------------- * * Ns_RegisterProxyRequest -- * * Register a new procedure to be called to proxy matching * given method and protocol pattern. * * Results: * None. * * Side effects: * Delete procedure of previously registered request, if any. * *---------------------------------------------------------------------- */ void Ns_RegisterProxyRequest(char *server, char *method, char *protocol, Ns_OpProc *proc, Ns_Callback *delete, void *arg) { NsServer *servPtr; Req *reqPtr; Ns_DString ds; int new; Tcl_HashEntry *hPtr; servPtr = NsGetServer(server); if (servPtr == NULL) { Ns_Log(Error, "Ns_RegisterProxyRequest: no such server: %s", server); return; } Ns_DStringInit(&ds); Ns_DStringVarAppend(&ds, method, protocol, NULL); reqPtr = ns_malloc(sizeof(Req)); reqPtr->refcnt = 1; reqPtr->proc = proc; reqPtr->delete = delete; reqPtr->arg = arg; reqPtr->flags = 0; Ns_MutexLock(&servPtr->request.plock); hPtr = Tcl_CreateHashEntry(&servPtr->request.proxy, ds.string, &new); if (!new) { FreeReq(Tcl_GetHashValue(hPtr)); } Tcl_SetHashValue(hPtr, reqPtr); Ns_MutexUnlock(&servPtr->request.plock); Ns_DStringFree(&ds); } /* *---------------------------------------------------------------------- * * Ns_UnRegisterProxyRequest -- * * Remove the procedure which would run for the given method and * protocol. * * Results: * None. * * Side effects: * Request's deleteProc may run. * *---------------------------------------------------------------------- */ void Ns_UnRegisterProxyRequest(char *server, char *method, char *protocol) { NsServer *servPtr; Ns_DString ds; Tcl_HashEntry *hPtr; servPtr = NsGetServer(server); if (servPtr != NULL) { Ns_DStringInit(&ds); Ns_DStringVarAppend(&ds, method, protocol, NULL); Ns_MutexLock(&servPtr->request.plock); hPtr = Tcl_FindHashEntry(&servPtr->request.proxy, ds.string); if (hPtr != NULL) { FreeReq(Tcl_GetHashValue(hPtr)); Tcl_DeleteHashEntry(hPtr); } Ns_MutexUnlock(&servPtr->request.plock); Ns_DStringFree(&ds); } } /* *---------------------------------------------------------------------- * * NsConnRunProxyRequest -- * * Locate and execute the procedure for the given method and * protocol pattern. * * Results: * Standard request procedure result, normally NS_OK. * * Side effects: * Depends on request procedure. * *---------------------------------------------------------------------- */ int NsConnRunProxyRequest(Ns_Conn *conn) { Conn *connPtr = (Conn *) conn; NsServer *servPtr = connPtr->servPtr; Ns_Request *request = conn->request; Req *reqPtr = NULL; int status; Ns_DString ds; Tcl_HashEntry *hPtr; Ns_DStringInit(&ds); Ns_DStringVarAppend(&ds, request->method, request->protocol, NULL); Ns_MutexLock(&servPtr->request.plock); hPtr = Tcl_FindHashEntry(&servPtr->request.proxy, ds.string); if (hPtr != NULL) { reqPtr = Tcl_GetHashValue(hPtr); ++reqPtr->refcnt; } Ns_MutexUnlock(&servPtr->request.plock); if (reqPtr == NULL) { status = Ns_ConnReturnNotFound(conn); } else { status = (*reqPtr->proc) (reqPtr->arg, conn); Ns_MutexLock(&servPtr->request.plock); FreeReq(reqPtr); Ns_MutexUnlock(&servPtr->request.plock); } Ns_DStringFree(&ds); return status; } /* *---------------------------------------------------------------------- * * FreeReq -- * * URL space callback to delete a request structure. * * Results: * None. * * Side effects: * Depends on request delete procedure. * *---------------------------------------------------------------------- */ static void FreeReq(void *arg) { Req *reqPtr = (Req *) arg; if (--reqPtr->refcnt == 0) { if (reqPtr->delete != NULL) { (*reqPtr->delete) (reqPtr->arg); } ns_free(reqPtr); } }