]>
Commit | Line | Data |
---|---|---|
bbc27441 AJ |
1 | /* |
2 | * Copyright (C) 1996-2014 The Squid Software Foundation and contributors | |
3 | * | |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
582c2af2 | 9 | #include "squid.h" |
26cc52cb | 10 | #include "adaptation/icap/Config.h" |
3d93a84d AJ |
11 | #include "adaptation/icap/Options.h" |
12 | #include "base/TextException.h" | |
13 | #include "HttpReply.h" | |
985c86bc | 14 | #include "SquidTime.h" |
28204b3b | 15 | #include "StrList.h" |
3d93a84d | 16 | #include "wordlist.h" |
78e8cfc4 | 17 | |
26cc52cb | 18 | Adaptation::Icap::Options::Options(): error("unconfigured"), |
774c051c | 19 | max_connections(-1), allow204(false), |
83c51da9 | 20 | allow206(false), |
c99de607 | 21 | preview(-1), theTTL(-1) |
774c051c | 22 | { |
c99de607 | 23 | theTransfers.preview.name = "Transfer-Preview"; |
24 | theTransfers.preview.kind = xferPreview; | |
25 | theTransfers.ignore.name = "Transfer-Ignore"; | |
26 | theTransfers.ignore.kind = xferIgnore; | |
27 | theTransfers.complete.name = "Transfer-Complete"; | |
28 | theTransfers.complete.kind = xferComplete; | |
29 | ||
30 | // Section 4.10.2 of RFC 3507 says that default is no Preview | |
31 | // TODO: provide a squid.conf option to overwrite the default | |
32 | theTransfers.byDefault = &theTransfers.complete; | |
33 | } | |
774c051c | 34 | |
26cc52cb | 35 | Adaptation::Icap::Options::~Options() |
774c051c | 36 | { |
774c051c | 37 | } |
38 | ||
c99de607 | 39 | // future optimization note: this method is called by ICAP ACL code at least |
40 | // twice for each HTTP message to see if the message should be ignored. For any | |
41 | // non-ignored HTTP message, ICAP calls to check whether a preview is needed. | |
26cc52cb | 42 | Adaptation::Icap::Options::TransferKind Adaptation::Icap::Options::transferKind(const String &urlPath) const |
774c051c | 43 | { |
c99de607 | 44 | if (theTransfers.preview.matches(urlPath)) |
45 | return xferPreview; | |
774c051c | 46 | |
c99de607 | 47 | if (theTransfers.complete.matches(urlPath)) |
48 | return xferComplete; | |
774c051c | 49 | |
c99de607 | 50 | if (theTransfers.ignore.matches(urlPath)) |
51 | return xferIgnore; | |
774c051c | 52 | |
192378eb | 53 | debugs(93,7, HERE << "url " << urlPath << " matches no extensions; " << |
9e008dda | 54 | "using default: " << theTransfers.byDefault->name); |
c99de607 | 55 | return theTransfers.byDefault->kind; |
774c051c | 56 | } |
57 | ||
26cc52cb | 58 | bool Adaptation::Icap::Options::valid() const |
774c051c | 59 | { |
60 | return !error; | |
61 | } | |
62 | ||
26cc52cb | 63 | bool Adaptation::Icap::Options::fresh() const |
774c051c | 64 | { |
65 | return squid_curtime <= expire(); | |
66 | } | |
67 | ||
26cc52cb | 68 | int Adaptation::Icap::Options::ttl() const |
5f8252d2 | 69 | { |
70 | Must(valid()); | |
26cc52cb | 71 | return theTTL >= 0 ? theTTL : TheConfig.default_options_ttl; |
5f8252d2 | 72 | } |
73 | ||
26cc52cb | 74 | time_t Adaptation::Icap::Options::expire() const |
774c051c | 75 | { |
76 | Must(valid()); | |
5f8252d2 | 77 | return theTimestamp + ttl(); |
774c051c | 78 | } |
79 | ||
26cc52cb | 80 | void Adaptation::Icap::Options::configure(const HttpReply *reply) |
774c051c | 81 | { |
82 | error = NULL; // reset initial "unconfigured" value (or an old error?) | |
83 | ||
84 | const HttpHeader *h = &reply->header; | |
85 | ||
9b769c67 | 86 | if (reply->sline.status() != Http::scOkay) |
774c051c | 87 | error = "unsupported status code of OPTIONS response"; |
88 | ||
89 | // Methods | |
a9925b40 | 90 | if (h->hasByNameListMember("Methods", "REQMOD", ',')) |
774c051c | 91 | cfgMethod(ICAP::methodReqmod); |
92 | ||
a9925b40 | 93 | if (h->hasByNameListMember("Methods", "RESPMOD", ',')) |
774c051c | 94 | cfgMethod(ICAP::methodRespmod); |
95 | ||
a9925b40 | 96 | service = h->getByName("Service"); |
774c051c | 97 | |
a9925b40 | 98 | serviceId = h->getByName("ServiceId"); |
774c051c | 99 | |
a9925b40 | 100 | istag = h->getByName("ISTag"); |
774c051c | 101 | |
40bb9887 AR |
102 | if (h->getByName("Opt-body-type").size()) { |
103 | // TODO: add a class to rate-limit such warnings using FadingCounter | |
104 | debugs(93,DBG_IMPORTANT, "WARNING: Ignoring unsupported ICAP " << | |
ab745b44 | 105 | "OPTIONS body; type: " << h->getByName("Opt-body-type")); |
40bb9887 AR |
106 | // Do not set error, assuming the response headers are valid. |
107 | } | |
774c051c | 108 | |
109 | cfgIntHeader(h, "Max-Connections", max_connections); | |
2dba5b8e CT |
110 | if (max_connections == 0) |
111 | debugs(93, DBG_IMPORTANT, "WARNING: Max-Connections is set to zero! "); | |
774c051c | 112 | |
8eeb99bf | 113 | cfgIntHeader(h, "Options-TTL", theTTL); |
774c051c | 114 | |
a9925b40 | 115 | theTimestamp = h->getTime(HDR_DATE); |
774c051c | 116 | |
8eeb99bf | 117 | if (theTimestamp < 0) |
118 | theTimestamp = squid_curtime; | |
774c051c | 119 | |
a9925b40 | 120 | if (h->hasListMember(HDR_ALLOW, "204", ',')) |
774c051c | 121 | allow204 = true; |
122 | ||
83c51da9 CT |
123 | if (h->hasListMember(HDR_ALLOW, "206", ',')) |
124 | allow206 = true; | |
125 | ||
774c051c | 126 | cfgIntHeader(h, "Preview", preview); |
127 | ||
c99de607 | 128 | cfgTransferList(h, theTransfers.preview); |
129 | cfgTransferList(h, theTransfers.ignore); | |
130 | cfgTransferList(h, theTransfers.complete); | |
774c051c | 131 | } |
132 | ||
26cc52cb | 133 | void Adaptation::Icap::Options::cfgMethod(ICAP::Method m) |
774c051c | 134 | { |
135 | Must(m != ICAP::methodNone); | |
4c9eadc2 | 136 | methods.push_back(m); |
774c051c | 137 | } |
138 | ||
139 | // TODO: HttpHeader should provide a general method for this type of conversion | |
26cc52cb | 140 | void Adaptation::Icap::Options::cfgIntHeader(const HttpHeader *h, const char *fname, int &value) |
774c051c | 141 | { |
30abd221 | 142 | const String s = h->getByName(fname); |
774c051c | 143 | |
5b4117d8 FC |
144 | if (s.size() && xisdigit(*s.termedBuf())) |
145 | value = atoi(s.termedBuf()); | |
774c051c | 146 | else |
147 | value = -1; | |
c99de607 | 148 | |
192378eb | 149 | debugs(93,5, HERE << "int header: " << fname << ": " << value); |
c99de607 | 150 | } |
151 | ||
26cc52cb | 152 | void Adaptation::Icap::Options::cfgTransferList(const HttpHeader *h, TransferList &list) |
c99de607 | 153 | { |
30abd221 | 154 | const String buf = h->getByName(list.name); |
c99de607 | 155 | bool foundStar = false; |
156 | list.parse(buf, foundStar); | |
157 | ||
158 | if (foundStar) { | |
159 | theTransfers.byDefault = &list; | |
192378eb | 160 | debugs(93,5, HERE << "set default transfer to " << list.name); |
c99de607 | 161 | } |
162 | ||
26cc52cb | 163 | list.report(5, "Adaptation::Icap::Options::cfgTransferList: "); |
c99de607 | 164 | } |
165 | ||
26cc52cb | 166 | /* Adaptation::Icap::Options::TransferList */ |
c99de607 | 167 | |
26cc52cb | 168 | Adaptation::Icap::Options::TransferList::TransferList(): extensions(NULL), name(NULL), |
9e008dda AJ |
169 | kind(xferNone) |
170 | { | |
c99de607 | 171 | }; |
172 | ||
26cc52cb | 173 | Adaptation::Icap::Options::TransferList::~TransferList() |
9e008dda | 174 | { |
c99de607 | 175 | wordlistDestroy(&extensions); |
176 | }; | |
177 | ||
26cc52cb | 178 | void Adaptation::Icap::Options::TransferList::add(const char *extension) |
9e008dda | 179 | { |
c99de607 | 180 | wordlistAdd(&extensions, extension); |
181 | }; | |
182 | ||
26cc52cb | 183 | bool Adaptation::Icap::Options::TransferList::matches(const String &urlPath) const |
9e008dda | 184 | { |
c99de607 | 185 | const int urlLen = urlPath.size(); |
186 | for (wordlist *e = extensions; e; e = e->next) { | |
187 | // optimize: store extension lengths | |
188 | const int eLen = strlen(e->key); | |
189 | ||
190 | // assume URL contains at least '/' before the extension | |
191 | if (eLen < urlLen) { | |
192 | const int eOff = urlLen - eLen; | |
193 | // RFC 3507 examples imply that extensions come without leading '.' | |
5b4117d8 FC |
194 | if (urlPath[eOff-1] == '.' && |
195 | strcmp(urlPath.termedBuf() + eOff, e->key) == 0) { | |
192378eb | 196 | debugs(93,7, HERE << "url " << urlPath << " matches " << |
9e008dda | 197 | name << " extension " << e->key); |
c99de607 | 198 | return true; |
199 | } | |
200 | } | |
201 | } | |
192378eb | 202 | debugs(93,8, HERE << "url " << urlPath << " matches no " << name << " extensions"); |
c99de607 | 203 | return false; |
204 | } | |
205 | ||
26cc52cb | 206 | void Adaptation::Icap::Options::TransferList::parse(const String &buf, bool &foundStar) |
9e008dda | 207 | { |
c99de607 | 208 | foundStar = false; |
209 | ||
210 | const char *item; | |
211 | const char *pos = NULL; | |
212 | int ilen; | |
213 | while (strListGetItem(&buf, ',', &item, &ilen, &pos)) { | |
214 | if (ilen == 1 && *item == '*') | |
215 | foundStar = true; | |
5c6dae44 AJ |
216 | else { |
217 | const char *tmp = xstrndup(item, ilen+1); | |
218 | add(tmp); | |
219 | xfree(tmp); | |
220 | } | |
c99de607 | 221 | } |
222 | } | |
223 | ||
26cc52cb | 224 | void Adaptation::Icap::Options::TransferList::report(int level, const char *prefix) const |
9e008dda | 225 | { |
c99de607 | 226 | if (extensions) { |
227 | for (wordlist *e = extensions; e; e = e->next) | |
228 | debugs(93,level, prefix << name << ": " << e->key); | |
229 | } else { | |
230 | debugs(93,level, prefix << "no " << name << " extensions"); | |
231 | } | |
774c051c | 232 | } |