]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/icap/Options.cc
e08120adce6b643bad20e0f278d52605f100a7d5
[thirdparty/squid.git] / src / adaptation / icap / Options.cc
1 /*
2 * Copyright (C) 1996-2016 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
9 #include "squid.h"
10 #include "adaptation/icap/Config.h"
11 #include "adaptation/icap/Options.h"
12 #include "base/TextException.h"
13 #include "HttpReply.h"
14 #include "SquidTime.h"
15 #include "StrList.h"
16 #include "wordlist.h"
17
18 Adaptation::Icap::Options::Options() :
19 error("unconfigured"),
20 max_connections(-1),
21 allow204(false),
22 allow206(false),
23 preview(-1),
24 theTTL(-1),
25 theTimestamp(0)
26 {
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 }
38
39 Adaptation::Icap::Options::~Options()
40 {
41 }
42
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.
46 Adaptation::Icap::Options::TransferKind
47 Adaptation::Icap::Options::transferKind(const SBuf &urlPath) const
48 {
49 if (theTransfers.preview.matches(urlPath))
50 return xferPreview;
51
52 if (theTransfers.complete.matches(urlPath))
53 return xferComplete;
54
55 if (theTransfers.ignore.matches(urlPath))
56 return xferIgnore;
57
58 debugs(93,7, "url " << urlPath << " matches no extensions; " <<
59 "using default: " << theTransfers.byDefault->name);
60 return theTransfers.byDefault->kind;
61 }
62
63 bool Adaptation::Icap::Options::valid() const
64 {
65 return !error;
66 }
67
68 bool Adaptation::Icap::Options::fresh() const
69 {
70 return squid_curtime <= expire();
71 }
72
73 int Adaptation::Icap::Options::ttl() const
74 {
75 Must(valid());
76 return theTTL >= 0 ? theTTL : TheConfig.default_options_ttl;
77 }
78
79 time_t Adaptation::Icap::Options::expire() const
80 {
81 Must(valid());
82 return theTimestamp + ttl();
83 }
84
85 void Adaptation::Icap::Options::configure(const HttpReply *reply)
86 {
87 error = NULL; // reset initial "unconfigured" value (or an old error?)
88
89 const HttpHeader *h = &reply->header;
90
91 if (reply->sline.status() != Http::scOkay)
92 error = "unsupported status code of OPTIONS response";
93
94 // Methods
95 if (h->hasByNameListMember("Methods", "REQMOD", ','))
96 cfgMethod(ICAP::methodReqmod);
97
98 if (h->hasByNameListMember("Methods", "RESPMOD", ','))
99 cfgMethod(ICAP::methodRespmod);
100
101 service = h->getByName("Service");
102
103 serviceId = h->getByName("ServiceId");
104
105 istag = h->getByName("ISTag");
106
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 " <<
110 "OPTIONS body; type: " << h->getByName("Opt-body-type"));
111 // Do not set error, assuming the response headers are valid.
112 }
113
114 cfgIntHeader(h, "Max-Connections", max_connections);
115 if (max_connections == 0)
116 debugs(93, DBG_IMPORTANT, "WARNING: Max-Connections is set to zero! ");
117
118 cfgIntHeader(h, "Options-TTL", theTTL);
119
120 theTimestamp = h->getTime(Http::HdrType::DATE);
121
122 if (theTimestamp < 0)
123 theTimestamp = squid_curtime;
124
125 if (h->hasListMember(Http::HdrType::ALLOW, "204", ','))
126 allow204 = true;
127
128 if (h->hasListMember(Http::HdrType::ALLOW, "206", ','))
129 allow206 = true;
130
131 cfgIntHeader(h, "Preview", preview);
132
133 cfgTransferList(h, theTransfers.preview);
134 cfgTransferList(h, theTransfers.ignore);
135 cfgTransferList(h, theTransfers.complete);
136 }
137
138 void Adaptation::Icap::Options::cfgMethod(ICAP::Method m)
139 {
140 Must(m != ICAP::methodNone);
141 methods.push_back(m);
142 }
143
144 // TODO: HttpHeader should provide a general method for this type of conversion
145 void Adaptation::Icap::Options::cfgIntHeader(const HttpHeader *h, const char *fname, int &value)
146 {
147 const String s = h->getByName(fname);
148
149 if (s.size() && xisdigit(*s.termedBuf()))
150 value = atoi(s.termedBuf());
151 else
152 value = -1;
153
154 debugs(93,5, HERE << "int header: " << fname << ": " << value);
155 }
156
157 void Adaptation::Icap::Options::cfgTransferList(const HttpHeader *h, TransferList &list)
158 {
159 const String buf = h->getByName(list.name);
160 bool foundStar = false;
161 list.parse(buf, foundStar);
162
163 if (foundStar) {
164 theTransfers.byDefault = &list;
165 debugs(93,5, HERE << "set default transfer to " << list.name);
166 }
167
168 list.report(5, "Adaptation::Icap::Options::cfgTransferList: ");
169 }
170
171 /* Adaptation::Icap::Options::TransferList */
172
173 Adaptation::Icap::Options::TransferList::TransferList(): extensions(NULL), name(NULL),
174 kind(xferNone)
175 {
176 };
177
178 Adaptation::Icap::Options::TransferList::~TransferList()
179 {
180 wordlistDestroy(&extensions);
181 };
182
183 void Adaptation::Icap::Options::TransferList::add(const char *extension)
184 {
185 wordlistAdd(&extensions, extension);
186 };
187
188 bool Adaptation::Icap::Options::TransferList::matches(const SBuf &urlPath) const
189 {
190 const SBuf::size_type urlLen = urlPath.length();
191 for (wordlist *e = extensions; e; e = e->next) {
192 // optimize: store extension lengths
193 const size_t eLen = strlen(e->key);
194
195 // assume URL contains at least '/' before the extension
196 if (eLen < urlLen) {
197 const size_t eOff = urlLen - eLen;
198 // RFC 3507 examples imply that extensions come without leading '.'
199 if (urlPath[eOff-1] == '.' && urlPath.substr(eOff).cmp(e->key, eLen) == 0) {
200 debugs(93,7, "url " << urlPath << " matches " << name << " extension " << e->key);
201 return true;
202 }
203 }
204 }
205 debugs(93,8, "url " << urlPath << " matches no " << name << " extensions");
206 return false;
207 }
208
209 void Adaptation::Icap::Options::TransferList::parse(const String &buf, bool &foundStar)
210 {
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;
219 else {
220 const char *tmp = xstrndup(item, ilen+1);
221 add(tmp);
222 xfree(tmp);
223 }
224 }
225 }
226
227 void Adaptation::Icap::Options::TransferList::report(int level, const char *prefix) const
228 {
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 }
235 }
236