]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/icap/Options.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / adaptation / icap / Options.cc
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
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 Adaptation::Icap::Options::transferKind(const String &urlPath) const
47 {
48 if (theTransfers.preview.matches(urlPath))
49 return xferPreview;
50
51 if (theTransfers.complete.matches(urlPath))
52 return xferComplete;
53
54 if (theTransfers.ignore.matches(urlPath))
55 return xferIgnore;
56
57 debugs(93,7, HERE << "url " << urlPath << " matches no extensions; " <<
58 "using default: " << theTransfers.byDefault->name);
59 return theTransfers.byDefault->kind;
60 }
61
62 bool Adaptation::Icap::Options::valid() const
63 {
64 return !error;
65 }
66
67 bool Adaptation::Icap::Options::fresh() const
68 {
69 return squid_curtime <= expire();
70 }
71
72 int Adaptation::Icap::Options::ttl() const
73 {
74 Must(valid());
75 return theTTL >= 0 ? theTTL : TheConfig.default_options_ttl;
76 }
77
78 time_t Adaptation::Icap::Options::expire() const
79 {
80 Must(valid());
81 return theTimestamp + ttl();
82 }
83
84 void Adaptation::Icap::Options::configure(const HttpReply *reply)
85 {
86 error = NULL; // reset initial "unconfigured" value (or an old error?)
87
88 const HttpHeader *h = &reply->header;
89
90 if (reply->sline.status() != Http::scOkay)
91 error = "unsupported status code of OPTIONS response";
92
93 // Methods
94 if (h->hasByNameListMember("Methods", "REQMOD", ','))
95 cfgMethod(ICAP::methodReqmod);
96
97 if (h->hasByNameListMember("Methods", "RESPMOD", ','))
98 cfgMethod(ICAP::methodRespmod);
99
100 service = h->getByName("Service");
101
102 serviceId = h->getByName("ServiceId");
103
104 istag = h->getByName("ISTag");
105
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 " <<
109 "OPTIONS body; type: " << h->getByName("Opt-body-type"));
110 // Do not set error, assuming the response headers are valid.
111 }
112
113 cfgIntHeader(h, "Max-Connections", max_connections);
114 if (max_connections == 0)
115 debugs(93, DBG_IMPORTANT, "WARNING: Max-Connections is set to zero! ");
116
117 cfgIntHeader(h, "Options-TTL", theTTL);
118
119 theTimestamp = h->getTime(HDR_DATE);
120
121 if (theTimestamp < 0)
122 theTimestamp = squid_curtime;
123
124 if (h->hasListMember(HDR_ALLOW, "204", ','))
125 allow204 = true;
126
127 if (h->hasListMember(HDR_ALLOW, "206", ','))
128 allow206 = true;
129
130 cfgIntHeader(h, "Preview", preview);
131
132 cfgTransferList(h, theTransfers.preview);
133 cfgTransferList(h, theTransfers.ignore);
134 cfgTransferList(h, theTransfers.complete);
135 }
136
137 void Adaptation::Icap::Options::cfgMethod(ICAP::Method m)
138 {
139 Must(m != ICAP::methodNone);
140 methods.push_back(m);
141 }
142
143 // TODO: HttpHeader should provide a general method for this type of conversion
144 void Adaptation::Icap::Options::cfgIntHeader(const HttpHeader *h, const char *fname, int &value)
145 {
146 const String s = h->getByName(fname);
147
148 if (s.size() && xisdigit(*s.termedBuf()))
149 value = atoi(s.termedBuf());
150 else
151 value = -1;
152
153 debugs(93,5, HERE << "int header: " << fname << ": " << value);
154 }
155
156 void Adaptation::Icap::Options::cfgTransferList(const HttpHeader *h, TransferList &list)
157 {
158 const String buf = h->getByName(list.name);
159 bool foundStar = false;
160 list.parse(buf, foundStar);
161
162 if (foundStar) {
163 theTransfers.byDefault = &list;
164 debugs(93,5, HERE << "set default transfer to " << list.name);
165 }
166
167 list.report(5, "Adaptation::Icap::Options::cfgTransferList: ");
168 }
169
170 /* Adaptation::Icap::Options::TransferList */
171
172 Adaptation::Icap::Options::TransferList::TransferList(): extensions(NULL), name(NULL),
173 kind(xferNone)
174 {
175 };
176
177 Adaptation::Icap::Options::TransferList::~TransferList()
178 {
179 wordlistDestroy(&extensions);
180 };
181
182 void Adaptation::Icap::Options::TransferList::add(const char *extension)
183 {
184 wordlistAdd(&extensions, extension);
185 };
186
187 bool Adaptation::Icap::Options::TransferList::matches(const String &urlPath) const
188 {
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 '.'
198 if (urlPath[eOff-1] == '.' &&
199 strcmp(urlPath.termedBuf() + eOff, e->key) == 0) {
200 debugs(93,7, HERE << "url " << urlPath << " matches " <<
201 name << " extension " << e->key);
202 return true;
203 }
204 }
205 }
206 debugs(93,8, HERE << "url " << urlPath << " matches no " << name << " extensions");
207 return false;
208 }
209
210 void Adaptation::Icap::Options::TransferList::parse(const String &buf, bool &foundStar)
211 {
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;
220 else {
221 const char *tmp = xstrndup(item, ilen+1);
222 add(tmp);
223 xfree(tmp);
224 }
225 }
226 }
227
228 void Adaptation::Icap::Options::TransferList::report(int level, const char *prefix) const
229 {
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 }
236 }
237