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