--------------------- PatchSet 3874 Date: 2006/10/25 04:55:33 Author: rousskov Branch: squid3-icap Tag: (none) Log: - Rewrote parsing, storage, and querying of "transfer lists". Each list corresponds to one Transfer-* ICAP OPTIONS response header. The "byDefault" list pointer points to the list that should be used by default (we are only interested in the transfer kind and name of the default list). A transfer list is using wordlist to store URL extensions. In the future, this should be optimized to faster match a URL with a list of extensions. Members: src/ICAP/ICAPOptions.cc:1.1.2.2->1.1.2.3 src/ICAP/ICAPOptions.h:1.1.2.2->1.1.2.3 Index: squid3/src/ICAP/ICAPOptions.cc =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/ICAP/ICAPOptions.cc,v retrieving revision 1.1.2.2 retrieving revision 1.1.2.3 diff -u -r1.1.2.2 -r1.1.2.3 --- squid3/src/ICAP/ICAPOptions.cc 29 Sep 2006 23:27:15 -0000 1.1.2.2 +++ squid3/src/ICAP/ICAPOptions.cc 25 Oct 2006 04:55:33 -0000 1.1.2.3 @@ -1,4 +1,5 @@ #include "squid.h" +#include "wordlist.h" #include "HttpReply.h" #include "ICAPOptions.h" #include "TextException.h" @@ -9,104 +10,41 @@ ICAPOptions::ICAPOptions(): error("unconfigured"), max_connections(-1), allow204(false), - preview(-1), theTTL(-1), transfer_ext(NULL) + preview(-1), theTTL(-1) { - transfers.preview = transfers.ignore = transfers.complete = NULL; - transfers.other = TRANSFER_NONE; -}; - -ICAPOptions::~ICAPOptions() -{ - delete transfers.preview; - delete transfers.ignore; - delete transfers.complete; - delete transfer_ext; -}; - -ICAPOptions::transfer_type ICAPOptions::getTransferExt(const char *s) -{ - - if (transfer_ext) { - List *data = transfer_ext; - - while (data) { - if (*(data->element.ext) == *s) { - return data->element.type; - } - - data = data->next; - } - } - - return TRANSFER_NONE; + theTransfers.preview.name = "Transfer-Preview"; + theTransfers.preview.kind = xferPreview; + theTransfers.ignore.name = "Transfer-Ignore"; + theTransfers.ignore.kind = xferIgnore; + theTransfers.complete.name = "Transfer-Complete"; + theTransfers.complete.kind = xferComplete; + + // Section 4.10.2 of RFC 3507 says that default is no Preview + // TODO: provide a squid.conf option to overwrite the default + theTransfers.byDefault = &theTransfers.complete; } -void ICAPOptions::insertTransferExt(const char *t, transfer_type t_type) -{ - List **Tail; - TransferPair t_ext; - - if (t == "*") { - transfers.other = t_type; - return; - } - - for (Tail = &transfer_ext; *Tail; Tail = &((*Tail)->next)) { - if (*(*Tail)->element.ext == *t) { - (*Tail)->element.type = t_type; - return; - } - } - - t_ext.ext = xstrdup(t); - t_ext.type = t_type; - List *q = new List(t_ext); - *(Tail) = q; - -}; - -void ICAPOptions::cfgTransferListHeader(const HttpHeader *h, const char *fname, transfer_type t_type) +ICAPOptions::~ICAPOptions() { - const String s = h->getByName(fname); - - if (!s.size()) - return; - - if (t_type == TRANSFER_PREVIEW) - transfers.preview = parseExtFileList(s.buf(), s.buf() + s.size(), t_type); - else if (t_type == TRANSFER_IGNORE) - transfers.ignore = parseExtFileList(s.buf(), s.buf() + s.size(), t_type); - else if (t_type == TRANSFER_COMPLETE) - transfers.complete = parseExtFileList(s.buf(), s.buf() + s.size(), t_type); - else - fatalf("Unexpected transfer_type at %s:%d", __FILE__,__LINE__); } -List *ICAPOptions::parseExtFileList(const char *start, const char *end, transfer_type t_type) -{ - const String s = xstrndup(start, end - start + 1); - const char *item; - const char *pos = NULL; - char *fext = NULL; - int ilen; - String t = NULL; - - List **Tail = NULL; - List *H = NULL; - - for (Tail = &H; *Tail; Tail = &((*Tail)->next)) - - ; - while (strListGetItem(&s, ',', &item, &ilen, &pos)) { - fext = xstrndup(item, ilen + 1); - t = fext; - List *q = new List (t); - *(Tail) = q; - Tail = &q->next; - insertTransferExt(fext, t_type); - } - - return H; +// future optimization note: this method is called by ICAP ACL code at least +// twice for each HTTP message to see if the message should be ignored. For any +// non-ignored HTTP message, ICAP calls to check whether a preview is needed. +ICAPOptions::TransferKind ICAPOptions::transferKind(const String &urlPath) const +{ + if (theTransfers.preview.matches(urlPath)) + return xferPreview; + + if (theTransfers.complete.matches(urlPath)) + return xferComplete; + + if (theTransfers.ignore.matches(urlPath)) + return xferIgnore; + + debugs(93,7, "ICAPOptions url " << urlPath << " matches no extensions; " << + "using default: " << theTransfers.byDefault->name); + return theTransfers.byDefault->kind; } bool ICAPOptions::valid() const @@ -167,11 +105,9 @@ cfgIntHeader(h, "Preview", preview); - cfgTransferListHeader(h, "Transfer-Preview", TRANSFER_PREVIEW); - - cfgTransferListHeader(h, "Transfer-Ignore", TRANSFER_IGNORE); - - cfgTransferListHeader(h, "Transfer-Complete", TRANSFER_COMPLETE); + cfgTransferList(h, theTransfers.preview); + cfgTransferList(h, theTransfers.ignore); + cfgTransferList(h, theTransfers.complete); } void ICAPOptions::cfgMethod(ICAP::Method m) @@ -189,4 +125,81 @@ value = atoi(s.buf()); else value = -1; + + debugs(93,5, "ICAPOptions::cfgIntHeader " << fname << ": " << value); +} + +void ICAPOptions::cfgTransferList(const HttpHeader *h, TransferList &list) +{ + const String buf = h->getByName(list.name); + bool foundStar = false; + list.parse(buf, foundStar); + + if (foundStar) { + theTransfers.byDefault = &list; + debugs(93,5, "ICAPOptions::cfgTransferList: " << + "set default transfer to " << list.name); + } + + list.report(5, "ICAPOptions::cfgTransferList: "); +} + + +/* ICAPOptions::TransferList */ + +ICAPOptions::TransferList::TransferList(): extensions(NULL), name(NULL), + kind(xferNone) { +}; + +ICAPOptions::TransferList::~TransferList() { + wordlistDestroy(&extensions); +}; + +void ICAPOptions::TransferList::add(const char *extension) { + wordlistAdd(&extensions, extension); +}; + +bool ICAPOptions::TransferList::matches(const String &urlPath) const { + const int urlLen = urlPath.size(); + for (wordlist *e = extensions; e; e = e->next) { + // optimize: store extension lengths + const int eLen = strlen(e->key); + + // assume URL contains at least '/' before the extension + if (eLen < urlLen) { + const int eOff = urlLen - eLen; + // RFC 3507 examples imply that extensions come without leading '.' + if (urlPath.buf()[eOff-1] == '.' && + strcmp(urlPath.buf() + eOff, e->key) == 0) { + debugs(93,7, "ICAPOptions url " << urlPath << " matches " << + name << " extension " << e->key); + return true; + } + } + } + debugs(93,8, "ICAPOptions url " << urlPath << " matches no " << name << " extensions"); + return false; +} + +void ICAPOptions::TransferList::parse(const String &buf, bool &foundStar) { + foundStar = false; + + const char *item; + const char *pos = NULL; + int ilen; + while (strListGetItem(&buf, ',', &item, &ilen, &pos)) { + if (ilen == 1 && *item == '*') + foundStar = true; + else + add(xstrndup(item, ilen+1)); + } +} + +void ICAPOptions::TransferList::report(int level, const char *prefix) const { + if (extensions) { + for (wordlist *e = extensions; e; e = e->next) + debugs(93,level, prefix << name << ": " << e->key); + } else { + debugs(93,level, prefix << "no " << name << " extensions"); + } } Index: squid3/src/ICAP/ICAPOptions.h =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/ICAP/ICAPOptions.h,v retrieving revision 1.1.2.2 retrieving revision 1.1.2.3 diff -u -r1.1.2.2 -r1.1.2.3 --- squid3/src/ICAP/ICAPOptions.h 29 Sep 2006 23:27:15 -0000 1.1.2.2 +++ squid3/src/ICAP/ICAPOptions.h 25 Oct 2006 04:55:33 -0000 1.1.2.3 @@ -1,6 +1,6 @@ /* - * $Id: ICAPOptions.h,v 1.1.2.2 2006/09/29 23:27:15 dwsquid Exp $ + * $Id: ICAPOptions.h,v 1.1.2.3 2006/10/25 04:55:33 rousskov Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -35,9 +35,10 @@ #define SQUID_ICAPOPTIONS_H #include "squid.h" -#include "List.h" #include "ICAPClient.h" +class wordlist; + /* Maintains options supported by a given ICAP service. * See RFC 3507, Section "4.10.2 OPTIONS Response". */ @@ -61,8 +62,8 @@ int ttl() const { return theTTL; }; - typedef enum { TRANSFER_NONE, TRANSFER_PREVIEW, TRANSFER_IGNORE, TRANSFER_COMPLETE } transfer_type; - transfer_type getTransferExt(const char *); + typedef enum { xferNone, xferPreview, xferIgnore, xferComplete } TransferKind; + TransferKind transferKind(const String &urlPath) const; public: const char *error; // human-readable information; set iff !valid() @@ -78,38 +79,42 @@ bool allow204; int preview; - // varios Transfer-* lists +protected: + // Transfer-* extension list representation + // maintains wordlist and does parsing/matching + class TransferList { + public: + TransferList(); + ~TransferList(); + + bool matches(const String &urlPath) const; + + void parse(const String &buf, bool &foundStar); + void add(const char *extension); + void report(int level, const char *prefix) const; + + public: + wordlist *extensions; // TODO: optimize with a hash of some sort + const char *name; // header name, mostly for debugging + TransferKind kind; // to simplify caller's life + }; + // varios Transfer-* lists struct Transfers { - List *preview; - List *ignore; - List *complete; - transfer_type other; // default X from Transfer-X: * - } - - transfers; + TransferList preview; + TransferList ignore; + TransferList complete; + TransferList *byDefault; // Transfer-X that has '*' + } theTransfers; -protected: int theTTL; time_t theTimestamp; - // The list of pairs "file extension <-> transfer type" - - struct TransferPair - { - char *ext; - transfer_type type; - }; - - List *transfer_ext; - private: void cfgMethod(ICAP::Method m); void cfgIntHeader(const HttpHeader *h, const char *fname, int &value); - void insertTransferExt(const char *t, transfer_type t_type); - void cfgTransferListHeader(const HttpHeader *h, const char *fname, transfer_type type); - List *parseExtFileList(const char *start, const char *end, transfer_type t_type); + void cfgTransferList(const HttpHeader *h, TransferList &l); };