Added support for large content requests spooled to a temp file. In these cases, the NS_CONN_FILECONTENT flag is set and the content can be accessed via the the new Ns_ConnContentFd routine or ns_conn contentchannel option (limited test, feedback appreciated).
/*
* 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.
*/
/*
* conn.c --
*
* Manage the Ns_Conn structure
*/
static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/conn.c,v 1.43 2005/03/25 00:34:10 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__;
#include "nsd.h"
static void SetFlag(Ns_Conn *conn, int bit, int flag);
static int GetChan(Tcl_Interp *interp, char *id, Tcl_Channel *chanPtr);
static int GetIndices(Tcl_Interp *interp, Conn *connPtr, Tcl_Obj **objv,
int *offPtr, int *lenPtr);
/*
*----------------------------------------------------------------------
*
* Ns_ConnHeaders --
*
* Get the headers
*
* Results:
* An Ns_Set containing HTTP headers from the client
*
* Side effects:
* None
*
*----------------------------------------------------------------------
*/
Ns_Set *
Ns_ConnHeaders(Ns_Conn *conn)
{
return conn->headers;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnOutputHeaders --
*
* Get the output headers
*
* Results:
* A writeable Ns_Set containing headers to send back to the client
*
* Side effects:
* None
*
*----------------------------------------------------------------------
*/
Ns_Set *
Ns_ConnOutputHeaders(Ns_Conn *conn)
{
return conn->outputheaders;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnAuthUser --
*
* Get the authenticated user
*
* Results:
* A pointer to a string with the username
*
* Side effects:
* None
*
*----------------------------------------------------------------------
*/
char *
Ns_ConnAuthUser(Ns_Conn *conn)
{
return conn->authUser;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnAuthPasswd --
*
* Get the authenticated user's password
*
* Results:
* A pointer to a string with the user's plaintext password
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
Ns_ConnAuthPasswd(Ns_Conn *conn)
{
return conn->authPasswd;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnContentLength --
*
* Get the content length from the client
*
* Results:
* An integer content length, or 0 if none sent
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_ConnContentLength(Ns_Conn *conn)
{
return conn->contentLength;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnContent --
*
* Return pointer to start of content.
*
* Results:
* Start of content or NULL on no memory content.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
Ns_ConnContent(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->content;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnContentFd --
*
* Return pointer to content fd.
*
* Results:
* Open file or -1 on no content file.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_ConnContentFd(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->tfd;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnServer --
*
* Get the server name
*
* Results:
* A string ptr to the server name
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
Ns_ConnServer(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->server;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnResponseStatus --
*
* Get the HTTP reponse code that will be sent
*
* Results:
* An integer response code (e.g., 200 for OK)
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_ConnResponseStatus(Ns_Conn *conn)
{
return Ns_ConnGetStatus(conn);
}
int
Ns_ConnGetStatus(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->status;
}
void
Ns_ConnSetStatus(Ns_Conn *conn, int status)
{
Conn *connPtr = (Conn *) conn;
connPtr->status = status;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnContentSent --
*
* Return the number of bytes sent to the browser after headers.
*
* Results:
* Bytes sent.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_ConnContentSent(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->nContentSent;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnResponseLength --
*
* Get the response length
*
* Results:
* integer, number of bytes to send
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_ConnResponseLength(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->responseLength;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnPeer --
*
* Get the peer's internet address
*
* Results:
* A string IP address
*
* Side effects:
* None
*
*----------------------------------------------------------------------
*/
char *
Ns_ConnPeer(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->peer;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnPeerPort --
*
* Get the port from which the peer is coming
*
* Results:
* An integer port #
*
* Side effects:
* None
*
*----------------------------------------------------------------------
*/
int
Ns_ConnPeerPort(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->port;
}
/*
*----------------------------------------------------------------------
* Ns_SetConnLocationProc --
*
* Set pointer to custom routine that acts like Ns_ConnLocation();
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
Ns_SetConnLocationProc(Ns_LocationProc *procPtr)
{
NsServer *servPtr = NsGetInitServer();
if (servPtr != NULL) {
servPtr->locationProc = procPtr;
}
}
/*
*----------------------------------------------------------------------
* Ns_SetLocationProc --
*
* Set pointer to custom routine that acts like Ns_ConnLocation();
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
Ns_SetLocationProc(char *server, Ns_LocationProc *procPtr)
{
NsServer *servPtr = NsGetServer(server);
if (servPtr != NULL) {
servPtr->locationProc = procPtr;
}
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnLocation --
*
* Get the location of this connection. It is of the form
* METHOD://HOSTNAME:PORT
*
* Results:
* a string URL, not including path
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
Ns_ConnLocation(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
NsServer *servPtr = connPtr->servPtr;
char *location;
if (servPtr->locationProc != NULL) {
location = (*servPtr->locationProc)(conn);
} else {
location = connPtr->location;
}
return location;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnHost --
*
* Get the address of the current connection
*
* Results:
* A string address
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
Ns_ConnHost(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->drvPtr->address;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnPort --
*
* What server port is this connection on?
*
* Results:
* Integer port number
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_ConnPort(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->drvPtr->port;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnSock --
*
* Return the underlying socket for a connection.
*
* Results:
* A driver name
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_ConnSock(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return (connPtr->sockPtr ? connPtr->sockPtr->sock : -1);
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnDriverName --
*
* Return the name of this driver
*
* Results:
* A driver name
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
Ns_ConnDriverName(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->drvPtr->name;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnDriverContext --
*
* Get the conn-wide context for this driver
*
* Results:
* The driver-supplied context
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void *
Ns_ConnDriverContext(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return (void *)(connPtr->sockPtr ? connPtr->sockPtr->arg : NULL);
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnStartTime --
*
* Return the Start Time
*
* Results:
* Ns_Time value
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Ns_Time *
Ns_ConnStartTime(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return &connPtr->times.queue;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnId --
*
* Return the connection id.
*
* Results:
* Integer id.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_ConnId(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->id;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnModifiedSince --
*
* Has the data the url points to changed since a given time?
*
* Results:
* NS_TRUE if data modified, NS_FALSE otherwise.
*
* Side effects:
* None
*
* NOTE: This doesn't do a strict time check. If the server flags aren't
* set to check modification, or if there wasn't an 'If-Modified-Since'
* header in the request, then this'll always return true
*
*----------------------------------------------------------------------
*/
int
Ns_ConnModifiedSince(Ns_Conn *conn, time_t since)
{
Conn *connPtr = (Conn *) conn;
char *hdr;
if (connPtr->servPtr->opts.flags & SERV_MODSINCE) {
hdr = Ns_SetIGet(conn->headers, "If-Modified-Since");
if (hdr != NULL && Ns_ParseHttpTime(hdr) >= since) {
return NS_FALSE;
}
}
return NS_TRUE;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnGetType, Ns_ConnSetType --
*
* Get (set) the response mime type.
*
* Results:
* Pointer to current type.
*
* Side effects:
* May update connection enconding.
*
*----------------------------------------------------------------------
*/
char *
Ns_ConnGetType(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->type;
}
void
Ns_ConnSetType(Ns_Conn *conn, char *type)
{
Conn *connPtr = (Conn *) conn;
NsServer *servPtr = connPtr->servPtr;
Tcl_Encoding encoding;
Ns_DString ds;
char *charset;
int len;
/*
* If the output type is text, set the output encoding based on
* the type charset. If the charset is missing, use the server
* default.
*/
Ns_DStringInit(&ds);
if (type != NULL && (strncmp(type, "text/", 5) == 0)) {
encoding = NULL;
charset = Ns_FindCharset(type, &len);
if (charset == NULL && (charset = servPtr->defcharset) != NULL) {
Ns_DStringVarAppend(&ds, type, "; charset=", charset, NULL);
type = ds.string;
len = ds.length;
}
if (charset != NULL) {
encoding = Ns_GetCharsetEncodingEx(charset, len);
}
Ns_ConnSetEncoding(conn, encoding);
}
ns_free(connPtr->type);
connPtr->type = ns_strcopy(type);
Ns_DStringFree(&ds);
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnGetEncoding, Ns_ConnSetEncoding --
*
* Get (set) the Tcl_Encoding for the connection which is used
* to convert from UTF to specified output character set.
*
* Results:
* Pointer to Tcl_Encoding (get) or NULL (set).
*
* Side effects:
* See Ns_ConnGetQuery().
*
*----------------------------------------------------------------------
*/
Tcl_Encoding
Ns_ConnGetEncoding(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->outputEncoding;
}
void
Ns_ConnSetEncoding(Ns_Conn *conn, Tcl_Encoding encoding)
{
Conn *connPtr = (Conn *) conn;
connPtr->outputEncoding = encoding;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnGetUrlEncoding, Ns_ConnSetUrlEncoding --
*
* Get (set) the Tcl_Encoding for the connection which is used
* to convert input forms to proper UTF.
*
* Results:
* Pointer to Tcl_Encoding (get) or NULL (set).
*
* Side effects:
* See Ns_ConnGetQuery().
*
*----------------------------------------------------------------------
*/
Tcl_Encoding
Ns_ConnGetUrlEncoding(Ns_Conn *conn)
{
Conn *connPtr = (Conn *) conn;
return connPtr->urlEncoding;
}
void
Ns_ConnSetUrlEncoding(Ns_Conn *conn, Tcl_Encoding encoding)
{
Conn *connPtr = (Conn *) conn;
connPtr->urlEncoding = encoding;
}
/*
*----------------------------------------------------------------------
*
* NsIsIdConn --
*
* Given an conn ID, could this be a conn ID?
*
* Results:
* Boolean.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
NsIsIdConn(char *connId)
{
if (connId == NULL || *connId != 'c') {
return NS_FALSE;
}
return NS_TRUE;
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnGetWriteEncodedFlag --
* Ns_ConnGetKeepAliveFlag --
*
* Is the given connection set for encoded writes/keepalive.
*
* Results:
* Boolean.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_ConnGetWriteEncodedFlag(Ns_Conn *conn)
{
return (conn->flags & NS_CONN_WRITE_ENCODED);
}
int
Ns_ConnGetKeepAliveFlag(Ns_Conn *conn)
{
return (conn->flags & NS_CONN_KEEPALIVE);
}
/*
*----------------------------------------------------------------------
*
* Ns_ConnSetWriteEncodedFlag --
* Ns_ConnSetKeepAliveFlag --
*
* Set the given connection encoded writes flag per parameter.
*
* Results:
* void.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
Ns_ConnSetWriteEncodedFlag(Ns_Conn *conn, int flag)
{
SetFlag(conn, NS_CONN_WRITE_ENCODED, flag);
}
void
Ns_ConnSetKeepAliveFlag(Ns_Conn *conn, int flag)
{
SetFlag(conn, NS_CONN_KEEPALIVE, flag);
}
static void
SetFlag(Ns_Conn *conn, int bit, int flag)
{
if (flag) {
conn->flags |= bit;
} else {
conn->flags &= ~bit;
}
}
/*
*----------------------------------------------------------------------
*
* NsTclConnObjCmd --
*
* Implements ns_conn as an obj command.
*
* Results:
* Standard Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclConnObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
NsInterp *itPtr = arg;
Ns_Conn *conn;
Conn *connPtr;
Ns_Set *form;
Ns_Request *request;
Tcl_Encoding encoding, *encodingPtr;
Tcl_Channel chan;
Tcl_Obj *result;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
FormFile *filePtr;
int idx, off, len, flag, fd;
static CONST char *opts[] = {
"authpassword", "authuser", "close", "content", "contentlength",
"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
}; enum ISubCmdIdx {
CAuthPasswordIdx, CAuthUserIdx, CCloseIdx, CContentIdx,
CContentLengthIdx, CContentChannelIdx, CCopyIdx, CDriverIdx,
CEncodingIdx, CFilesIdx, CFileOffIdx, CFileLenIdx,
CFileHdrIdx, CFlagsIdx, CFormIdx, CHeadersIdx, CHostIdx,
CIdIdx, CIsConnectedIdx, CLocationIdx, CMethodIdx,
COutputHeadersIdx, CPeerAddrIdx, CPeerPortIdx, CPortIdx,
CProtocolIdx, CQueryIdx, CRequestIdx, CServerIdx, CSockIdx,
CStartIdx, CStatusIdx, CUrlIdx, CUrlcIdx, CUrlEncodingIdx,
CUrlvIdx, CVersionIdx, CWriteEncodedIdx
} opt;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,
(int *) &opt) != TCL_OK) {
return TCL_ERROR;
}
result = Tcl_GetObjResult(interp);
connPtr = (Conn *) conn = itPtr->conn;
/*
* Only the "isconnected" option operates without a conn.
*/
if (opt == CIsConnectedIdx) {
Tcl_SetBooleanObj(result, connPtr ? 1 : 0);
return TCL_OK;
}
if (connPtr == NULL) {
Tcl_SetResult(interp, "no current connection", TCL_STATIC);
return TCL_ERROR;
}
request = connPtr->request;
switch (opt) {
case CIsConnectedIdx:
/* NB: Not reached - silence compiler warning. */
break;
case CUrlvIdx:
if (objc == 2 || (objc == 3 && NsIsIdConn(Tcl_GetString(objv[2])))) {
for (idx = 0; idx < request->urlc; idx++) {
Tcl_AppendElement(interp, request->urlv[idx]);
}
} else if (Tcl_GetIntFromObj(interp, objv[2], &idx) != TCL_OK) {
return TCL_ERROR;
} else if (idx >= 0 && idx < request->urlc) {
Tcl_SetResult(interp, request->urlv[idx], TCL_STATIC);
}
break;
case CAuthUserIdx:
Tcl_SetResult(interp, connPtr->authUser, TCL_STATIC);
break;
case CAuthPasswordIdx:
Tcl_SetResult(interp, connPtr->authPasswd, TCL_STATIC);
break;
case CContentChannelIdx:
fd = Ns_ConnContentFd(conn);
if (fd >= 0 && (fd = dup(fd)) >= 0) {
/* NB: Dup the fd so the channel can be safely closed later. */
chan = Tcl_MakeFileChannel((ClientData) fd, TCL_READABLE);
if (chan == NULL) {
close(fd);
} else {
Tcl_RegisterChannel(interp, chan);
Tcl_SetResult(interp, Tcl_GetChannelName(chan),
TCL_VOLATILE);
}
}
break;
case CContentIdx:
if (objc != 2 && objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "?off len?");
return TCL_ERROR;
}
if (objc == 2) {
Tcl_SetResult(interp, Ns_ConnContent(conn), TCL_STATIC);
} else {
if (GetIndices(interp, connPtr, objv+2, &off, &len) != TCL_OK) {
return TCL_ERROR;
}
result = Tcl_NewStringObj(Ns_ConnContent(conn)+off, len);
Tcl_SetObjResult(interp, result);
}
break;
case CContentLengthIdx:
Tcl_SetIntObj(result, conn->contentLength);
break;
case CEncodingIdx:
case CUrlEncodingIdx:
if (opt == CEncodingIdx) {
encodingPtr = &connPtr->outputEncoding;
} else {
encodingPtr = &connPtr->urlEncoding;
}
if (objc > 2) {
encoding = Ns_GetEncoding(Tcl_GetString(objv[2]));
if (encoding == NULL) {
Tcl_AppendResult(interp, "no such encoding: ",
Tcl_GetString(objv[2]), NULL);
return TCL_ERROR;
}
*encodingPtr = encoding;
}
encoding = *encodingPtr;
if (encoding != NULL) {
Tcl_SetResult(interp, (char *) Tcl_GetEncodingName(encoding),
TCL_STATIC);
}
break;
case CPeerAddrIdx:
Tcl_SetResult(interp, Ns_ConnPeer(conn), TCL_STATIC);
break;
case CPeerPortIdx:
Tcl_SetIntObj(result, Ns_ConnPeerPort(conn));
break;
case CHeadersIdx:
if (itPtr->nsconn.flags & CONN_TCLHDRS) {
Tcl_SetResult(interp, itPtr->nsconn.hdrs, TCL_STATIC);
} else {
Ns_TclEnterSet(interp, connPtr->headers, NS_TCL_SET_STATIC);
strcpy(itPtr->nsconn.hdrs, Tcl_GetStringResult(interp));
itPtr->nsconn.flags |= CONN_TCLHDRS;
}
break;
case COutputHeadersIdx:
if (itPtr->nsconn.flags & CONN_TCLOUTHDRS) {
Tcl_SetResult(interp, itPtr->nsconn.outhdrs, TCL_STATIC);
} else {
Ns_TclEnterSet(interp, connPtr->outputheaders, NS_TCL_SET_STATIC);
strcpy(itPtr->nsconn.outhdrs, Tcl_GetStringResult(interp));
itPtr->nsconn.flags |= CONN_TCLOUTHDRS;
}
break;
case CFormIdx:
/* NB: Ignore any cached form if query is no longer valid. */
if (!NsCheckQuery(conn)) {
itPtr->nsconn.flags &= ~CONN_TCLFORM;
}
if (itPtr->nsconn.flags & CONN_TCLFORM) {
Tcl_SetResult(interp, itPtr->nsconn.form, TCL_STATIC);
} else {
form = Ns_ConnGetQuery(conn);
if (form == NULL) {
itPtr->nsconn.form[0] = '\0';
} else {
Ns_TclEnterSet(interp, form, NS_TCL_SET_STATIC);
strcpy(itPtr->nsconn.form, Tcl_GetStringResult(interp));
}
itPtr->nsconn.flags |= CONN_TCLFORM;
}
break;
case CFilesIdx:
if (objc != 2) {
Tcl_WrongNumArgs(interp, 2, objv, NULL);
return TCL_ERROR;
}
hPtr = Tcl_FirstHashEntry(&connPtr->files, &search);
while (hPtr != NULL) {
Tcl_AppendElement(interp, Tcl_GetHashKey(&connPtr->files, hPtr));
hPtr = Tcl_NextHashEntry(&search);
}
break;
case CFileOffIdx:
case CFileLenIdx:
case CFileHdrIdx:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "file");
return TCL_ERROR;
}
hPtr = Tcl_FindHashEntry(&connPtr->files, Tcl_GetString(objv[2]));
if (hPtr == NULL) {
Tcl_AppendResult(interp, "no such file: ",
Tcl_GetString(objv[2]), NULL);
return TCL_ERROR;
}
filePtr = Tcl_GetHashValue(hPtr);
if (opt == CFileOffIdx) {
Tcl_SetLongObj(result, (long) filePtr->off);
} else if (opt == CFileLenIdx) {
Tcl_SetLongObj(result, (long) filePtr->len);
} else {
Ns_TclEnterSet(interp, filePtr->hdrs, NS_TCL_SET_STATIC);
}
break;
case CCopyIdx:
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "off len chan");
return TCL_ERROR;
}
if (GetIndices(interp, connPtr, objv+2, &off, &len) != TCL_OK ||
GetChan(interp, Tcl_GetString(objv[4]), &chan) != TCL_OK) {
return TCL_ERROR;
}
if (Tcl_Write(chan, connPtr->content + off, len) != len) {
Tcl_AppendResult(interp, "could not write ",
Tcl_GetString(objv[3]), " bytes to ",
Tcl_GetString(objv[4]), ": ",
Tcl_PosixError(interp), NULL);
return TCL_ERROR;
}
break;
case CWriteEncodedIdx:
if (objc > 2) {
if (Tcl_GetBooleanFromObj(interp, objv[2], &flag) != TCL_OK) {
return TCL_ERROR;
}
Ns_ConnSetWriteEncodedFlag(conn, flag);
}
Tcl_SetBooleanObj(result, Ns_ConnGetWriteEncodedFlag(conn));
break;
case CRequestIdx:
Tcl_SetResult(interp, request->line, TCL_STATIC);
break;
case CMethodIdx:
Tcl_SetResult(interp, request->method, TCL_STATIC);
break;
case CProtocolIdx:
Tcl_SetResult(interp, request->protocol, TCL_STATIC);
break;
case CHostIdx:
Tcl_SetResult(interp, request->host, TCL_STATIC);
break;
case CPortIdx:
Tcl_SetIntObj(result, request->port);
break;
case CUrlIdx:
Tcl_SetResult(interp, request->url, TCL_STATIC);
break;
case CQueryIdx:
Tcl_SetResult(interp, request->query, TCL_STATIC);
break;
case CUrlcIdx:
Tcl_SetIntObj(result, request->urlc);
break;
case CVersionIdx:
Tcl_SetDoubleObj(result, request->version);
break;
case CLocationIdx:
Tcl_SetResult(interp, Ns_ConnLocation(conn), TCL_STATIC);
break;
case CDriverIdx:
Tcl_SetResult(interp, Ns_ConnDriverName(conn), TCL_STATIC);
break;
case CServerIdx:
Tcl_SetResult(interp, Ns_ConnServer(conn), TCL_STATIC);
break;
case CStatusIdx:
Tcl_SetIntObj(result, Ns_ConnResponseStatus(conn));
break;
case CSockIdx:
Tcl_SetIntObj(result, Ns_ConnSock(conn));
break;
case CIdIdx:
Tcl_SetIntObj(result, Ns_ConnId(conn));
break;
case CFlagsIdx:
Tcl_SetIntObj(result, connPtr->flags);
break;
case CStartIdx:
Ns_TclSetTimeObj(result, &connPtr->times.queue);
break;
case CCloseIdx:
if (Ns_ConnClose(conn) != NS_OK) {
Tcl_SetResult(interp, "could not close connection", TCL_STATIC);
return TCL_ERROR;
}
break;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsTclWriteContentObjCmd --
*
* Implements ns_writecontent as obj command.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclWriteContentObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
NsInterp *itPtr = arg;
Tcl_Channel chan;
if (objc != 2 && objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "?connid? channel");
return TCL_ERROR;
}
if (objc == 3 && !NsIsIdConn(Tcl_GetString(objv[1]))) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "bad connid: \"",
Tcl_GetString(objv[1]), "\"", NULL);
return TCL_ERROR;
}
if (itPtr->conn == NULL) {
Tcl_SetResult(interp, "no connection", TCL_STATIC);
return TCL_ERROR;
}
if (GetChan(interp, Tcl_GetString(objv[objc-1]), &chan) != TCL_OK) {
return TCL_ERROR;
}
Tcl_Flush(chan);
if (Ns_ConnCopyToChannel(itPtr->conn, (size_t)itPtr->conn->contentLength,
chan) != NS_OK) {
Tcl_SetResult(interp, "could not copy content (likely client disconnect)",
TCL_STATIC);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsTclStartContentObjCmd --
*
* Set connPtr->sendState to "Content" and set the charset/encoding
* to use for further data.
*
* Results:
* NS_OK/NS_ERROR
*
* Side effects:
* connPtr->sendState and connPtr->encoding may be set.
*
*----------------------------------------------------------------------
*/
int
NsTclStartContentObjCmd(ClientData arg, Tcl_Interp *interp, int objc,
Tcl_Obj **objv)
{
NsInterp *itPtr = arg;
Tcl_Encoding encoding;
char *opt;
int status;
int i;
status = TCL_OK;
encoding = NULL;
for (i = 1; i < objc && status == TCL_OK; i++) {
opt = Tcl_GetString(objv[i]);
if (STREQ(opt, "-charset")) {
if (encoding != NULL) {
Tcl_AppendResult(interp, Tcl_GetString(objv[0]),
": charset may only be specified by one flag", NULL);
status = TCL_ERROR;
}
if (++i >= objc) {
Tcl_AppendResult(interp, Tcl_GetString(objv[0]),
": missing argument for -charset flag", NULL);
status = TCL_ERROR;
}
encoding = Ns_GetCharsetEncoding(Tcl_GetString(objv[i]));
if (encoding == NULL) {
Tcl_AppendResult(interp, Tcl_GetString(objv[0]),
": could not find an encoding for charset ",
Tcl_GetString(objv[i]), NULL);
status = TCL_ERROR;
}
}
else if (STREQ(opt, "-type")) {
if (encoding != NULL) {
Tcl_AppendResult(interp, Tcl_GetString(objv[0]),
": charset may only be specified by one flag", NULL);
status = TCL_ERROR;
}
if (++i >= objc) {
Tcl_AppendResult(interp, Tcl_GetString(objv[0]),
": missing argument for -type flag", NULL);
status = TCL_ERROR;
}
encoding = Ns_GetTypeEncoding(Tcl_GetString(objv[i]));
}
else {
Tcl_AppendResult(interp, "usage: ", Tcl_GetString(objv[0]),
" ?-charset charsetname? ?-type content-type?", NULL);
status = TCL_ERROR;
}
}
if (status != TCL_OK) {
return status;
}
Ns_ConnSetWriteEncodedFlag(itPtr->conn, NS_TRUE);
Ns_ConnSetEncoding(itPtr->conn, encoding);
return status;
}
/*
*----------------------------------------------------------------------
*
* GetChan --
*
* Return an open channel.
*
* Results:
* TCL_OK if given a valid channel id, TCL_ERROR otherwise.
*
* Side effects:
* Channel is set in given chanPtr or error message left in
* given interp.
*
*----------------------------------------------------------------------
*/
static int
GetChan(Tcl_Interp *interp, char *id, Tcl_Channel *chanPtr)
{
Tcl_Channel chan;
int mode;
chan = Tcl_GetChannel(interp, id, &mode);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
if ((mode & TCL_WRITABLE) == 0) {
Tcl_AppendResult(interp, "channel \"", id,
"\" wasn't opened for writing", (char *) NULL);
return TCL_ERROR;
}
*chanPtr = chan;
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GetIndices --
*
* Return offset and length from given Tcl_Obj's.
*
* Results:
* TCL_OK if objects are valid offsets, TCL_ERROR otherwise.
*
* Side effects:
* Given offPtr and lenPtr are updated with indices or error
* message is left in the interp.
*
*----------------------------------------------------------------------
*/
static int
GetIndices(Tcl_Interp *interp, Conn *connPtr, Tcl_Obj **objv, int *offPtr, int *lenPtr)
{
int off, len;
if (Tcl_GetIntFromObj(interp, objv[0], &off) != TCL_OK ||
Tcl_GetIntFromObj(interp, objv[1], &len) != TCL_OK) {
return TCL_ERROR;
}
if (off < 0 || off > connPtr->contentLength) {
Tcl_AppendResult(interp, "invalid offset: ", Tcl_GetString(objv[0]), NULL);
return TCL_ERROR;
}
if (len < 0 || len > (connPtr->contentLength - off)) {
Tcl_AppendResult(interp, "invalid length: ", Tcl_GetString(objv[1]), NULL);
return TCL_ERROR;
}
*offPtr = off;
*lenPtr = len;
return TCL_OK;
}
|
Back to SourceForge.net Powered by ViewCVS 1.0-dev |