From 253caccb08f5a5a23ec0c9b89272145213c09ec0 Mon Sep 17 00:00:00 2001 From: wessels <> Date: Thu, 26 Jan 2006 00:41:23 +0000 Subject: [PATCH] The purpose of this change is to add ICAP RESPMOD support for FTP responses. I created a "ServerStateData" class which has common elements of both HttpStateData and FtpStateData. It becomes a base class for both of them. ICAP now uses the ServerStateData methods. --- src/ICAP/ICAPClientRespmodPrecache.cc | 38 +- src/ICAP/ICAPClientRespmodPrecache.h | 12 +- src/Makefile.am | 4 +- src/ftp.cc | 522 ++++++++++++++++++++------ src/http.cc | 49 +-- src/http.h | 18 +- 6 files changed, 438 insertions(+), 205 deletions(-) diff --git a/src/ICAP/ICAPClientRespmodPrecache.cc b/src/ICAP/ICAPClientRespmodPrecache.cc index ed9bc50fa0..fcc6672c69 100644 --- a/src/ICAP/ICAPClientRespmodPrecache.cc +++ b/src/ICAP/ICAPClientRespmodPrecache.cc @@ -12,7 +12,7 @@ CBDATA_CLASS_INIT(ICAPClientRespmodPrecache); -ICAPClientRespmodPrecache::ICAPClientRespmodPrecache(ICAPServiceRep::Pointer aService): service(aService), httpState(NULL), virgin(NULL), adapted(NULL) +ICAPClientRespmodPrecache::ICAPClientRespmodPrecache(ICAPServiceRep::Pointer aService): service(aService), serverState(NULL), virgin(NULL), adapted(NULL) { debug(93,5)("ICAPClientRespmodPrecache constructed, this=%p\n", this); } @@ -20,7 +20,7 @@ ICAPClientRespmodPrecache::ICAPClientRespmodPrecache(ICAPServiceRep::Pointer aSe ICAPClientRespmodPrecache::~ICAPClientRespmodPrecache() { stop(notifyNone); - cbdataReferenceDone(httpState); + cbdataReferenceDone(serverState); debug(93,5)("ICAPClientRespmodPrecache destructed, this=%p\n", this); if (virgin != NULL) @@ -32,9 +32,9 @@ ICAPClientRespmodPrecache::~ICAPClientRespmodPrecache() service = NULL; } -void ICAPClientRespmodPrecache::startRespMod(HttpStateData *anHttpState, HttpRequest *request, HttpReply *reply) +void ICAPClientRespmodPrecache::startRespMod(ServerStateData *anServerState, HttpRequest *request, HttpReply *reply) { - httpState = cbdataReference(anHttpState); + serverState = cbdataReference(anServerState); virgin = new MsgPipe("virgin"); // this is the place to create a refcount ptr virgin->source = this; @@ -62,6 +62,8 @@ void ICAPClientRespmodPrecache::startRespMod(HttpStateData *anHttpState, HttpReq void ICAPClientRespmodPrecache::sendMoreData(StoreIOBuffer buf) { debug(93,5)("ICAPClientRespmodPrecache::sendMoreData() called\n"); + //debugs(93,0,HERE << "appending " << buf.length << " bytes"); + //debugs(93,0,HERE << "body.contentSize = " << virgin->data->body->contentSize()); //buf.dump(); /* * The caller is responsible for not giving us more data @@ -81,7 +83,7 @@ ICAPClientRespmodPrecache::potentialSpaceSize() return (int) virgin->data->body->potentialSpaceSize(); } -// HttpStateData says we have the entire HTTP message +// ServerStateData says we have the entire HTTP message void ICAPClientRespmodPrecache::doneSending() { debug(93,5)("ICAPClientRespmodPrecache::doneSending() called\n"); @@ -98,7 +100,7 @@ void ICAPClientRespmodPrecache::doneSending() #endif } -// HttpStateData tells us to abort +// ServerStateData tells us to abort void ICAPClientRespmodPrecache::ownerAbort() { debug(93,5)("ICAPClientRespmodPrecache::ownerAbort() called\n"); @@ -111,7 +113,7 @@ void ICAPClientRespmodPrecache::noteSinkNeed(MsgPipe *p) debug(93,5)("ICAPClientRespmodPrecache::noteSinkNeed() called\n"); if (virgin->data->body->potentialSpaceSize()) - httpState->icapSpaceAvailable(); + serverState->icapSpaceAvailable(); } // ICAP client aborting @@ -144,7 +146,7 @@ void ICAPClientRespmodPrecache::noteSourceStart(MsgPipe *p) ssize_t dummy; bool expect_body = reply->expectingBody(virgin->data->cause->method, dummy); - httpState->takeAdaptedHeaders(reply); + serverState->takeAdaptedHeaders(reply); if (expect_body) noteSourceProgress(p); @@ -156,10 +158,12 @@ void ICAPClientRespmodPrecache::noteSourceStart(MsgPipe *p) void ICAPClientRespmodPrecache::noteSourceProgress(MsgPipe *p) { debug(93,5)("ICAPClientRespmodPrecache::noteSourceProgress() called\n"); - //tell HttpStateData to store a fresh portion of the adapted response + //tell ServerStateData to store a fresh portion of the adapted response + + assert(serverState); if (p->data->body->hasContent()) { - httpState->takeAdaptedBody(p->data->body); + serverState->takeAdaptedBody(p->data->body); } } @@ -167,8 +171,8 @@ void ICAPClientRespmodPrecache::noteSourceProgress(MsgPipe *p) void ICAPClientRespmodPrecache::noteSourceFinish(MsgPipe *p) { debug(93,5)("ICAPClientRespmodPrecache::noteSourceFinish() called\n"); - //tell HttpStateData that we expect no more response data - httpState->doneAdapting(); + //tell ServerStateData that we expect no more response data + serverState->doneAdapting(); stop(notifyNone); } @@ -200,14 +204,14 @@ void ICAPClientRespmodPrecache::stop(Notify notify) freeAdapted(); } - if (httpState) { + if (serverState) { if (notify == notifyOwner) - // tell HttpStateData that we are aborting prematurely - httpState->abortAdapting(); + // tell ServerStateData that we are aborting prematurely + serverState->abortAdapting(); - cbdataReferenceDone(httpState); + cbdataReferenceDone(serverState); - // httpState is now NULL, will not call it any more + // serverState is now NULL, will not call it any more } } diff --git a/src/ICAP/ICAPClientRespmodPrecache.h b/src/ICAP/ICAPClientRespmodPrecache.h index f8e165a1d7..e13324ddb0 100644 --- a/src/ICAP/ICAPClientRespmodPrecache.h +++ b/src/ICAP/ICAPClientRespmodPrecache.h @@ -1,6 +1,6 @@ /* - * $Id: ICAPClientRespmodPrecache.h,v 1.2 2005/12/22 22:26:31 wessels Exp $ + * $Id: ICAPClientRespmodPrecache.h,v 1.3 2006/01/25 17:41:23 wessels Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -40,10 +40,10 @@ #include "ICAPServiceRep.h" /* The ICAP Anchor implements message pipe sink and source interfaces. It - * helps HttpStateData to marshall the incoming/virgin HTTP message (being + * helps ServerStateData to marshall the incoming/virgin HTTP message (being * recieved from the HTTP server) to Squid's ICAP client module, using the * MsgPipe interface. The same interface is used to get the adapted HTTP - * message back from the ICAP client. HttpStateData is the "owner" of the + * message back from the ICAP client. ServerStateData is the "owner" of the * ICAPClientRespmodPrecache. */ @@ -58,8 +58,8 @@ public: ICAPClientRespmodPrecache(ICAPServiceRep::Pointer); virtual ~ICAPClientRespmodPrecache(); - // synchronous calls called by HttpStateData - void startRespMod(HttpStateData *anHttpState, HttpRequest *request, HttpReply *reply); + // synchronous calls called by ServerStateData + void startRespMod(ServerStateData *anServerState, HttpRequest *request, HttpReply *reply); void sendMoreData(StoreIOBuffer buf); void doneSending(); void ownerAbort(); @@ -77,7 +77,7 @@ public: public: ICAPServiceRep::Pointer service; - HttpStateData *httpState; + ServerStateData *serverState; MsgPipe::Pointer virgin; MsgPipe::Pointer adapted; diff --git a/src/Makefile.am b/src/Makefile.am index 78453d7d7f..504f812e70 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ # # Makefile for the Squid Object Cache server # -# $Id: Makefile.am,v 1.126 2006/01/14 00:06:19 wessels Exp $ +# $Id: Makefile.am,v 1.127 2006/01/25 17:41:23 wessels Exp $ # # Uncomment and customize the following to suit your needs: # @@ -523,6 +523,8 @@ squid_SOURCES = \ StoreSearch.h \ StoreSwapLogData.cc \ StoreSwapLogData.h \ + Server.h \ + Server.cc \ structs.h \ SwapDir.cc \ SwapDir.h \ diff --git a/src/ftp.cc b/src/ftp.cc index a27b9fbde0..8a14da8712 100644 --- a/src/ftp.cc +++ b/src/ftp.cc @@ -1,6 +1,6 @@ /* - * $Id: ftp.cc,v 1.381 2006/01/24 17:40:11 wessels Exp $ + * $Id: ftp.cc,v 1.382 2006/01/25 17:41:23 wessels Exp $ * * DEBUG: section 9 File Transfer Protocol (FTP) * AUTHOR: Harvest Derived @@ -48,6 +48,15 @@ #endif #include "ConnectionDetail.h" #include "forward.h" +#include "Server.h" +#include "MemBuf.h" + +#if ICAP_CLIENT +#include "ICAP/ICAPClientRespmodPrecache.h" +#include "ICAP/ICAPConfig.h" +extern ICAPConfig TheICAPConfig; +static void icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data); +#endif static const char *const crlf = "\r\n"; static char cbuf[1024]; @@ -93,12 +102,13 @@ struct _ftp_flags bool put; bool put_mkdir; bool listformat_unknown; + bool listing_started; }; class FtpStateData; typedef void (FTPSM) (FtpStateData *); -class FtpStateData +class FtpStateData : public ServerStateData { public: @@ -106,8 +116,6 @@ public: void operator delete (void *); FtpStateData(FwdState *); ~FtpStateData(); - StoreEntry *entry; - HttpRequest *request; char user[MAX_URL]; char password[MAX_URL]; int password_url; @@ -152,22 +160,21 @@ public: struct { int fd; - char *buf; - size_t size; - off_t offset; + MemBuf *readBuf; char *host; u_short port; + bool read_pending; } data; struct _ftp_flags flags; - FwdState::Pointer fwd; private: CBDATA_CLASS(FtpStateData); public: + // these should all be private void start(); void loginParser(const char *, int escaped); int restartable(); @@ -187,6 +194,11 @@ public: int checkAuth(const HttpHeader * req_hdr); void checkUrlpath(); void buildTitleUrl(); + void writeReplyBody(const char *, int len); + void printfReplyBody(const char *fmt, ...); + void maybeReadData(); + void transactionComplete(); + void processReplyBody(); static PF ftpSocketClosed; static CNCB ftpPasvCallback; @@ -199,6 +211,19 @@ public: static HttpReply *ftpAuthRequired(HttpRequest * request, const char *realm); static void ftpRequestBody(char *buf, ssize_t size, void *data); static wordlist *ftpParseControlReply(char *, size_t, int *, int *); + +#if ICAP_CLIENT + +public: + void icapAclCheckDone(ICAPServiceRep::Pointer); + void takeAdaptedHeaders(HttpReply *); + void takeAdaptedBody(MemBuf *); + void doneAdapting(); + void abortAdapting(); + void icapSpaceAvailable(); + bool icapAccessCheckPending; +#endif + }; CBDATA_CLASS_INIT(FtpStateData); @@ -336,18 +361,12 @@ FtpStateData::ftpSocketClosed(int fdnotused, void *data) delete ftpState; } -FtpStateData::FtpStateData(FwdState *theFwdState) +FtpStateData::FtpStateData(FwdState *theFwdState) : ServerStateData(theFwdState) { - request = theFwdState->request; - entry = theFwdState->entry; - storeLockObject(entry); - fwd = theFwdState; const char *url = storeUrl(entry); - debug(9, 3) ("ftpStart: '%s'\n", url); statCounter.server.all.requests++; statCounter.server.ftp.requests++; - request = requestLink(request); ctrl.fd = theFwdState->server_fd; data.fd = -1; size = -1; @@ -370,15 +389,11 @@ FtpStateData::~FtpStateData() storeUnregisterAbort(entry); - storeUnlockObject(entry); - if (reply_hdr) { memFree(reply_hdr, MEM_8K_BUF); reply_hdr = NULL; } - requestUnlink(request); - if (data.fd > -1) { int fd = data.fd; data.fd = -1; @@ -390,10 +405,10 @@ FtpStateData::~FtpStateData() ctrl.buf = NULL; } - if (data.buf) { - memFreeBuf(data.size, data.buf); - data.buf = NULL; - } + if (!data.readBuf->isNull()) + data.readBuf->clean(); + + delete data.readBuf; if (pathcomps) wordlistDestroy(&pathcomps); @@ -474,108 +489,111 @@ FtpStateData::ftpTimeout(int fd, void *data) void FtpStateData::listingStart() { + debugs(9,3,HERE << "listingStart()"); wordlist *w; char *dirup; int i, j, k; const char *title = title_url.buf(); - storeAppendPrintf(entry, "\n"); - storeAppendPrintf(entry, "\n", - version_string); - storeAppendPrintf(entry, "\n", mkrfc1123(squid_curtime)); - storeAppendPrintf(entry, "\n"); + flags.listing_started = true; + printfReplyBody("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"); + printfReplyBody("<!-- HTML listing generated by Squid %s -->\n", + version_string); + printfReplyBody("<!-- %s -->\n", mkrfc1123(squid_curtime)); + printfReplyBody("<HTML><HEAD><TITLE>\n"); { char *t = xstrdup(title); rfc1738_unescape(t); - storeAppendPrintf(entry, "FTP Directory: %s\n", html_quote(t)); + printfReplyBody("FTP Directory: %s\n", html_quote(t)); xfree(t); } - storeAppendPrintf(entry, "\n"); - storeAppendPrintf(entry, "\n"); + printfReplyBody("\n"); + printfReplyBody("\n"); if (flags.need_base_href) - storeAppendPrintf(entry, "\n", - html_quote(base_href.buf())); + printfReplyBody("\n", + html_quote(base_href.buf())); - storeAppendPrintf(entry, "\n"); + printfReplyBody("\n"); if (cwd_message) { - storeAppendPrintf(entry, "
\n");
+        printfReplyBody("
\n");
 
         for (w = cwd_message; w; w = w->next)
-            storeAppendPrintf(entry, "%s\n", html_quote(w->key));
+            printfReplyBody("%s\n", html_quote(w->key));
 
-        storeAppendPrintf(entry, "
\n"); + printfReplyBody("
\n"); - storeAppendPrintf(entry, "
\n"); + printfReplyBody("
\n"); wordlistDestroy(&cwd_message); } - storeAppendPrintf(entry, "

\n"); - storeAppendPrintf(entry, "FTP Directory: "); + printfReplyBody("

\n"); + printfReplyBody("FTP Directory: "); /* "ftp://" == 6 characters */ assert(title_url.size() >= 6); k = 6 + strcspn(&title[6], "/"); for (i = 6, j = 0; title[i]; j = i) { - storeAppendPrintf(entry, " j) { char *url = xstrdup(title); url[i] = '\0'; - storeAppendPrintf(entry, "%s", html_quote(url + k)); - storeAppendPrintf(entry, "/"); - storeAppendPrintf(entry, "\">"); + printfReplyBody("%s", html_quote(url + k)); + printfReplyBody("/"); + printfReplyBody("\">"); rfc1738_unescape(url + j); - storeAppendPrintf(entry, "%s", html_quote(url + j)); + printfReplyBody("%s", html_quote(url + j)); safe_free(url); - storeAppendPrintf(entry, ""); + printfReplyBody(""); } - storeAppendPrintf(entry, "/"); + printfReplyBody("/"); if (title[i] == '/') i++; if (i == j) { /* Error guard, or "assert" */ - storeAppendPrintf(entry, "ERROR: Failed to parse URL: %s\n", - html_quote(title)); + printfReplyBody("ERROR: Failed to parse URL: %s\n", + html_quote(title)); debug(9, 0) ("Failed to parse URL: %s\n", title); break; } } - storeAppendPrintf(entry, "

\n"); - storeAppendPrintf(entry, "
\n");
+    printfReplyBody("\n");
+    printfReplyBody("
\n");
     dirup = htmlifyListEntry("");
-    storeAppend(entry, dirup, strlen(dirup));
+    writeReplyBody(dirup, strlen(dirup));
     flags.html_header_sent = 1;
 }
 
 void
 FtpStateData::listingFinish()
 {
+    debugs(9,3,HERE << "listingFinish()");
     storeBuffer(entry);
-    storeAppendPrintf(entry, "
\n"); + printfReplyBody("
\n"); if (flags.listformat_unknown && !flags.tried_nlst) { - storeAppendPrintf(entry, "[As plain directory]\n", - flags.dir_slash ? rfc1738_escape_part(old_filepath) : "."); + printfReplyBody("[As plain directory]\n", + flags.dir_slash ? rfc1738_escape_part(old_filepath) : "."); } else if (typecode == 'D') { const char *path = flags.dir_slash ? filepath : "."; - storeAppendPrintf(entry, "[As extended directory]\n", html_quote(path)); + printfReplyBody("[As extended directory]\n", html_quote(path)); } - storeAppendPrintf(entry, "
\n"); - storeAppendPrintf(entry, "
\n"); - storeAppendPrintf(entry, "Generated %s by %s (%s)\n", - mkrfc1123(squid_curtime), - getMyHostname(), - visible_appname_string); - storeAppendPrintf(entry, "
\n"); + printfReplyBody("
\n"); + printfReplyBody("
\n"); + printfReplyBody("Generated %s by %s (%s)\n", + mkrfc1123(squid_curtime), + getMyHostname(), + visible_appname_string); + printfReplyBody("
\n"); } static const char *Month[] = @@ -1061,7 +1079,7 @@ FtpStateData::htmlifyListEntry(const char *line) void FtpStateData::parseListing() { - char *buf = data.buf; + char *buf = data.readBuf->content(); char *sbuf; /* NULL-terminated copy of buf */ char *end; char *line; @@ -1070,7 +1088,7 @@ FtpStateData::parseListing() size_t linelen; size_t usable; StoreEntry *e = entry; - size_t len = data.offset; + size_t len = data.readBuf->contentSize(); /* * We need a NULL-terminated buffer for scanning, ick */ @@ -1119,25 +1137,22 @@ FtpStateData::parseListing() assert(t != NULL); - storeAppend(e, t, strlen(t)); - } - - assert(usable <= len); - - if (usable < len) { - /* must copy partial line to beginning of buf */ - linelen = len - usable; - - if (linelen > 4096) - linelen = 4096; +#if ICAP_CLIENT - xstrncpy(line, end, linelen); + if (icap) { + if ((int)strlen(t) > icap->potentialSpaceSize()) { + debugs(0,0,HERE << "WARNING avoid overwhelming ICAP with data!"); + usable = s - sbuf; + break; + } + } - xstrncpy(data.buf, line, data.size); +#endif - data.offset = strlen(data.buf); + writeReplyBody(t, strlen(t)); } + data.readBuf->consume(usable); memFree(line, MEM_4K_BUF); xfree(sbuf); } @@ -1165,17 +1180,51 @@ void FtpStateData::dataReadWrapper(int fd, char *buf, size_t len, comm_err_t errflag, int xerrno, void *data) { FtpStateData *ftpState = (FtpStateData *)data; + ftpState->data.read_pending = false; ftpState->dataRead(fd, buf, len, errflag, xerrno); } +void +FtpStateData::maybeReadData() +{ + if (data.fd < 0) + return; + + if (data.read_pending) + return; + + int read_sz = data.readBuf->spaceSize(); + +#if ICAP_CLIENT + + if (icap) { + int icap_space = icap->potentialSpaceSize(); + + if (icap_space < read_sz) + read_sz = icap_space; + } + +#endif + + debugs(11,9, HERE << "FTP may read up to " << read_sz << " bytes"); + + if (read_sz < 2) // see http.cc + return; + + data.read_pending = true; + + debugs(9,5,HERE << "queueing read on FD " << data.fd); + + entry->delayAwareRead(data.fd, data.readBuf->space(), read_sz, dataReadWrapper, this); +} + void FtpStateData::dataRead(int fd, char *buf, size_t len, comm_err_t errflag, int xerrno) { int j; int bin; - size_t read_sz; - debug(9, 5) ("ftpDataRead: FD %d, Read %d bytes\n", fd, (unsigned int)len); + debugs(9, 3, HERE << "ftpDataRead: FD " << fd << " Read " << len << " bytes"); if (len > 0) { kb_incr(&statCounter.server.all.kbytes_in, len); @@ -1203,11 +1252,18 @@ FtpStateData::dataRead(int fd, char *buf, size_t len, comm_err_t errflag, int xe delayId.bytesIn(len); #endif - data.offset += len; } if (errflag == COMM_OK && len > 0) { + debugs(9,5,HERE << "appended " << len << " bytes to readBuf"); + data.readBuf->appended(len); +#if DELAY_POOLS + + DelayId delayId = entry->mem_obj->mostBytesAllowed(); + delayId.bytesIn(len); +#endif + IOStats.Ftp.reads++; for (j = len - 1, bin = 0; j; bin++) @@ -1216,25 +1272,12 @@ FtpStateData::dataRead(int fd, char *buf, size_t len, comm_err_t errflag, int xe IOStats.Ftp.read_hist[bin]++; } - if (!flags.http_header_sent && len >= 0) { - appendSuccessHeader(); - - if (flags.isdir) - listingStart(); - } - if (errflag != COMM_OK || len < 0) { debug(50, ignoreErrno(xerrno) ? 3 : 1) ("ftpDataRead: read error: %s\n", xstrerr(xerrno)); if (ignoreErrno(xerrno)) { /* XXX what about Config.Timeout.read? */ - read_sz = data.size - data.offset; -#if DELAY_POOLS - - read_sz = delayId.bytesWanted(1, read_sz); -#endif - - comm_read(fd, data.buf + data.offset, read_sz, dataReadWrapper, this); + maybeReadData(); } else { if (!flags.http_header_sent && !fwd->ftpPasvFailed() && flags.pasv_supported) { fwd->dontRetry(false); /* this is a retryable error */ @@ -1247,27 +1290,41 @@ FtpStateData::dataRead(int fd, char *buf, size_t len, comm_err_t errflag, int xe } } else if (len == 0) { dataComplete(); - } else { - if (flags.isdir) { - parseListing(); - } else { - storeAppend(entry, data.buf, len); - data.offset = 0; - } + } - storeBufferFlush(entry); + processReplyBody(); +} - /* XXX what about Config.Timeout.read? */ - read_sz = data.size - data.offset; +void +FtpStateData::processReplyBody() +{ + if (!flags.http_header_sent && data.readBuf->contentSize() >= 0) + appendSuccessHeader(); -#if DELAY_POOLS +#if ICAP_CLIENT - read_sz = delayId.bytesWanted(1, read_sz); + if (icapAccessCheckPending) { + debugs(9,3,HERE << "returning from dataRead due to icapAccessCheckPending"); + return; + } #endif - comm_read(fd, data.buf + data.offset, read_sz, dataReadWrapper, this); + if (flags.isdir && !flags.listing_started) + listingStart(); + + if (flags.isdir) { + parseListing(); + } else { + writeReplyBody(data.readBuf->content(), data.readBuf->contentSize()); + debugs(9,5,HERE << "consuming " << data.readBuf->contentSize() << " bytes of readBuf"); + data.readBuf->consume(data.readBuf->contentSize()); } + + storeBufferFlush(entry); + + /* XXX what about Config.Timeout.read? */ + maybeReadData(); } /* @@ -1429,7 +1486,8 @@ FtpStateData::start() ctrl.last_command = xstrdup("Connect to server"); ctrl.buf = (char *)memAllocBuf(4096, &ctrl.size); ctrl.offset = 0; - data.buf = (char *)memAllocBuf(SQUID_TCP_SO_RCVBUF, &data.size); + data.readBuf = new MemBuf; + data.readBuf->init(4096, SQUID_TCP_SO_RCVBUF); scheduleReadControlReply(0); } @@ -2611,8 +2669,7 @@ ftpReadList(FtpStateData * ftpState) if (code == 125 || (code == 150 && ftpState->data.host)) { /* Begin data transfer */ /* XXX what about Config.Timeout.read? */ - assert(ftpState->data.offset == 0); - ftpState->entry->delayAwareRead(ftpState->data.fd, ftpState->data.buf, ftpState->data.size, FtpStateData::dataReadWrapper, ftpState); + ftpState->maybeReadData(); ftpState->state = READING_DATA; /* * Cancel the timeout on the Control socket and establish one @@ -2658,10 +2715,7 @@ ftpReadRetr(FtpStateData * ftpState) /* Begin data transfer */ debug(9, 3) ("ftpReadRetr: reading data channel\n"); /* XXX what about Config.Timeout.read? */ - size_t read_sz = ftpState->data.size - ftpState->data.offset; - - ftpState->entry->delayAwareRead(ftpState->data.fd, ftpState->data.buf + ftpState->data.offset, read_sz, FtpStateData::dataReadWrapper, ftpState); - + ftpState->maybeReadData(); ftpState->state = READING_DATA; /* * Cancel the timeout on the Control socket and establish one @@ -2704,11 +2758,7 @@ ftpReadTransferDone(FtpStateData * ftpState) if (ftpState->flags.html_header_sent) ftpState->listingFinish(); - ftpState->fwd->unregister(ftpState->ctrl.fd); - - ftpState->fwd->complete(); - - ftpSendQuit(ftpState); + ftpState->transactionComplete(); } else { /* != 226 */ debug(9, 1) ("ftpReadTransferDone: Got code %d after reading data\n", code); @@ -2724,7 +2774,6 @@ FtpStateData::ftpRequestBody(char *buf, ssize_t size, void *data) { FtpStateData *ftpState = (FtpStateData *) data; debug(9, 3) ("ftpRequestBody: buf=%p size=%d ftpState=%p\n", buf, (int) size, data); - ftpState->data.offset = size; if (size > 0) { /* DataWrite */ @@ -2750,7 +2799,11 @@ FtpStateData::ftpDataWriteCallback(int fd, char *buf, size_t size, comm_err_t er if (!err) { /* Shedule the rest of the request */ - clientReadBody(ftpState->request, ftpState->data.buf, ftpState->data.size, ftpRequestBody, ftpState); + clientReadBody(ftpState->request, + ftpState->data.readBuf->content(), + ftpState->data.readBuf->contentSize(), + ftpRequestBody, + ftpState); } else { debug(9, 1) ("ftpDataWriteCallback: write error: %s\n", xstrerr(xerrno)); ftpState->failed(ERR_WRITE_ERROR, xerrno); @@ -2763,7 +2816,11 @@ FtpStateData::ftpDataWrite(int ftp, void *data) FtpStateData *ftpState = (FtpStateData *) data; debug(9, 3) ("ftpDataWrite\n"); /* This starts the body transfer */ - clientReadBody(ftpState->request, ftpState->data.buf, ftpState->data.size, ftpRequestBody, ftpState); + clientReadBody(ftpState->request, + ftpState->data.readBuf->content(), + ftpState->data.readBuf->contentSize(), + ftpRequestBody, + ftpState); } static void @@ -3032,7 +3089,11 @@ FtpStateData::appendSuccessHeader() const char *filename = NULL; const char *t = NULL; StoreEntry *e = entry; - HttpReply *reply = new HttpReply; + HttpReply *newrep = new HttpReply; + + reply = newrep->lock() + + ; if (flags.http_header_sent) return; @@ -3090,6 +3151,23 @@ FtpStateData::appendSuccessHeader() if (mime_enc) httpHeaderPutStr(&reply->header, HDR_CONTENT_ENCODING, mime_enc); +#if ICAP_CLIENT + + if (TheICAPConfig.onoff) { + ICAPAccessCheck *icap_access_check = new ICAPAccessCheck(ICAP::methodRespmod, + ICAP::pointPreCache, + request, + reply, + icapAclCheckDoneWrapper, + this); + + icapAccessCheckPending = true; + icap_access_check->check(); // will eventually delete self + return; + } + +#endif + storeEntryReplaceObject(e, reply); storeTimestampsSet(e); @@ -3157,3 +3235,197 @@ ftpUrlWith2f(const HttpRequest * request) return buf; } + +void +FtpStateData::printfReplyBody(const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + static char buf[4096]; + buf[0] = '\0'; + vsnprintf(buf, 4096, fmt, args); + writeReplyBody(buf, strlen(buf)); +} + +/* + * Call this when there is data from the origin server + * which should be sent to either StoreEntry, or to ICAP... + */ +void +FtpStateData::writeReplyBody(const char *data, int len) +{ +#if ICAP_CLIENT + + if (icap) { + debugs(9,5,HERE << "writing " << len << " bytes to ICAP"); + icap->sendMoreData (StoreIOBuffer(len, 0, (char*)data)); + return; + } + +#endif + + debugs(9,5,HERE << "writing " << len << " bytes to StoreEntry"); + + storeAppend(entry, data, len); +} + + +void +FtpStateData::transactionComplete() +{ + debugs(9,5,HERE << "transactionComplete FD " << ctrl.fd << " this " << this); + + fwd->unregister(ctrl.fd); + + ftpSendQuit(this); + +#if ICAP_CLIENT + + if (icap) { + icap->doneSending(); + return; + } + +#endif + + fwd->complete(); +} + +#if ICAP_CLIENT + +static void +icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data) +{ + FtpStateData *ftpState = (FtpStateData *)data; + ftpState->icapAclCheckDone(service); +} + +void +FtpStateData::icapAclCheckDone(ICAPServiceRep::Pointer service) +{ + icapAccessCheckPending = false; + + if (service == NULL) { + // handle case where no service is selected; + debugs(0,0,HERE << "write me"); + processReplyBody(); + return; + } + + if (doIcap(service) < 0) { + /* + * XXX Maybe instead of an error page we should + * handle the reply normally (without ICAP). + */ + ErrorState *err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); + err->xerrno = errno; + err->request = requestLink(request); + errorAppendEntry(entry, err); + comm_close(ctrl.fd); + return; + } + + icap->startRespMod(this, request, reply); + processReplyBody(); +} + +/* + * Called by ICAPClientRespmodPrecache when it has space available for us. + */ +void +FtpStateData::icapSpaceAvailable() +{ + debug(11,5)("FtpStateData::icapSpaceAvailable() called\n"); + maybeReadData(); +} + +void +FtpStateData::takeAdaptedHeaders(HttpReply *rep) +{ + debug(11,5)("FtpStateData::takeAdaptedHeaders() called\n"); + + if (!entry->isAccepting()) { + debug(11,5)("\toops, entry is not Accepting!\n"); + icap->ownerAbort(); + return; + } + + assert (rep); + storeEntryReplaceObject(entry, rep); + reply->unlock(); + + reply = rep->lock() + + ; + + debug(11,5)("FtpStateData::takeAdaptedHeaders() finished\n"); +} + +void +FtpStateData::takeAdaptedBody(MemBuf *buf) +{ + debug(11,5)("FtpStateData::takeAdaptedBody() called\n"); + debug(11,5)("\t%d bytes\n", (int) buf->contentSize()); + + if (!entry->isAccepting()) { + debug(11,5)("\toops, entry is not Accepting!\n"); + icap->ownerAbort(); + return; + } + + storeAppend(entry, buf->content(), buf->contentSize()); + buf->consume(buf->contentSize()); // consume everything written +} + +void +FtpStateData::doneAdapting() +{ + debug(11,5)("FtpStateData::doneAdapting() called\n"); + + if (!entry->isAccepting()) { + debug(11,5)("\toops, entry is not Accepting!\n"); + icap->ownerAbort(); + } else { + fwd->complete(); + } + + /* + * ICAP is done, so we don't need this any more. + */ + delete icap; + + cbdataReferenceDone(icap); + + if (ctrl.fd >= 0) + comm_close(ctrl.fd); + else + delete this; +} + +void +FtpStateData::abortAdapting() +{ + debug(11,5)("FtpStateData::abortAdapting() called\n"); + + /* + * ICAP has given up, we're done with it too + */ + delete icap; + cbdataReferenceDone(icap); + + if (entry->isEmpty()) { + ErrorState *err; + err = errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); + err->request = requestLink((HttpRequest *) request); + err->xerrno = errno; + fwd->fail(err); + fwd->dontRetry(true); + } + + if (ctrl.fd >= 0) + comm_close(ctrl.fd); + else + delete this; +} + +#endif diff --git a/src/http.cc b/src/http.cc index 9cc390508a..7d1769241a 100644 --- a/src/http.cc +++ b/src/http.cc @@ -1,6 +1,6 @@ /* - * $Id: http.cc,v 1.483 2006/01/23 20:04:24 wessels Exp $ + * $Id: http.cc,v 1.484 2006/01/25 17:41:23 wessels Exp $ * * DEBUG: section 11 Hypertext Transfer Protocol (HTTP) * AUTHOR: Harvest Derived @@ -74,14 +74,11 @@ static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeader static void icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data); #endif -HttpStateData::HttpStateData(FwdState *theFwdState) +HttpStateData::HttpStateData(FwdState *theFwdState) : ServerStateData(theFwdState) { debugs(11,5,HERE << "HttpStateData " << this << " created"); ignoreCacheControl = false; surrogateNoStore = false; - fwd = theFwdState; - entry = fwd->entry; - storeLockObject(entry); fd = fwd->server_fd; readBuf = new MemBuf; readBuf->init(4096, SQUID_TCP_SO_RCVBUF); @@ -111,6 +108,8 @@ HttpStateData::HttpStateData(FwdState *theFwdState) proxy_req->flags.proxying = 1; + requestUnlink(request); + request = requestLink(proxy_req); /* @@ -127,8 +126,6 @@ HttpStateData::HttpStateData(FwdState *theFwdState) #endif - } else { - request = requestLink(orig_request); } /* @@ -139,6 +136,10 @@ HttpStateData::HttpStateData(FwdState *theFwdState) HttpStateData::~HttpStateData() { + /* + * don't forget about ~ServerStateData() + */ + if (request_body_buf) { if (orig_request->body_connection.getRaw()) { clientAbortBody(orig_request); @@ -150,34 +151,15 @@ HttpStateData::~HttpStateData() } } - storeUnlockObject(entry); - if (!readBuf->isNull()) readBuf->clean(); delete readBuf; - requestUnlink(request); - requestUnlink(orig_request); - request = NULL; - orig_request = NULL; - fwd = NULL; // refcounted - - if (reply) - reply->unlock(); - -#if ICAP_CLIENT - - if (icap) { - delete icap; - cbdataReferenceDone(icap); - } - -#endif debugs(11,5,HERE << "HttpStateData " << this << " destroyed"); } @@ -2042,21 +2024,6 @@ HttpStateData::icapAclCheckDone(ICAPServiceRep::Pointer service) processReplyBody(); } -/* - * Initiate an ICAP transaction. Return 0 if all is well, or -1 upon error. - * Caller will handle error condition by generating a Squid error message - * or take other action. - */ -int -HttpStateData::doIcap(ICAPServiceRep::Pointer service) -{ - debug(11,5)("HttpStateData::doIcap() called\n"); - assert(NULL == icap); - icap = new ICAPClientRespmodPrecache(service); - (void) cbdataReference(icap); - return 0; -} - /* * Called by ICAPClientRespmodPrecache when it has space available for us. */ diff --git a/src/http.h b/src/http.h index cd9dc822b4..7868944eb8 100644 --- a/src/http.h +++ b/src/http.h @@ -1,6 +1,6 @@ /* - * $Id: http.h,v 1.19 2006/01/23 20:04:24 wessels Exp $ + * $Id: http.h,v 1.20 2006/01/25 17:41:23 wessels Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -37,6 +37,7 @@ #include "StoreIOBuffer.h" #include "comm.h" #include "forward.h" +#include "Server.h" #if ICAP_CLIENT #include "ICAP/ICAPServiceRep.h" @@ -46,7 +47,7 @@ class ICAPClientRespmodPrecache; class ICAPAccessCheck; #endif -class HttpStateData +class HttpStateData : public ServerStateData { public: @@ -79,14 +80,11 @@ public: void icapSpaceAvailable(); #endif - StoreEntry *entry; - HttpRequest *request; peer *_peer; /* peer request made to */ int eof; /* reached end-of-object? */ HttpRequest *orig_request; int fd; http_state_flags flags; - FwdState::Pointer fwd; char *request_body_buf; off_t currentOffset; size_t read_sz; @@ -115,12 +113,6 @@ const HttpReply * getReply() const { return reply ? reply : entry->getReply(); } #endif private: - /* - * HttpReply is now shared (locked) among multiple classes, - * including HttpStateData, StoreEntry, and ICAP. - */ - HttpReply *reply; - enum ConnectionStatus { INCOMPLETE_MSG, COMPLETE_PERSISTENT_MSG, @@ -143,10 +135,6 @@ private: MemBuf * mb, http_state_flags flags); static bool decideIfWeDoRanges (HttpRequest * orig_request); -#if ICAP_CLIENT - - int doIcap(ICAPServiceRep::Pointer); -#endif private: CBDATA_CLASS2(HttpStateData); -- 2.39.5