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