]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/AccessCheck.cc
Added an adaptation service group API to support groups of services. Current
[thirdparty/squid.git] / src / adaptation / AccessCheck.cc
CommitLineData
62c7f90e
AR
1#include "squid.h"
2#include "structs.h"
3
4#include "ConfigParser.h"
5#include "ACL.h"
6#include "HttpRequest.h"
7#include "HttpReply.h"
8#include "ACLChecklist.h"
9#include "adaptation/Service.h"
10#include "adaptation/ServiceGroups.h"
11#include "adaptation/AccessRule.h"
12#include "adaptation/AccessCheck.h"
13
14
15cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN;
16
17Adaptation::AccessCheck::AccessCheck(Method aMethod,
18 VectPoint aPoint,
19 HttpRequest *aReq,
20 HttpReply *aRep,
21 AccessCheckCallback *aCallback,
22 void *aCallbackData): AsyncJob("AccessCheck"), done(FALSE)
23{
24 // TODO: assign these at creation time
25
26 method = aMethod;
27 point = aPoint;
28
29 req = HTTPMSGLOCK(aReq);
30 rep = aRep ? HTTPMSGLOCK(aRep) : NULL;
31
32 callback = aCallback;
33
34 callback_data = cbdataReference(aCallbackData);
35
36 acl_checklist = NULL;
37
38 debugs(93, 5, "AccessCheck constructed for " << methodStr(method) << " " << vectPointStr(point));
39}
40
41Adaptation::AccessCheck::~AccessCheck()
42{
43 HTTPMSGUNLOCK(req);
44 HTTPMSGUNLOCK(rep);
45}
46
47/*
48 * Walk the access rules list and find all classes that have at least
49 * one service with matching method and vectoring point.
50 */
51void
52Adaptation::AccessCheck::check()
53{
54 debugs(93, 4, "Adaptation::AccessCheck::check");
55
56 typedef AccessRules::iterator ARI;
57 for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) {
58
59 /*
60 * We only find the first matching service because we only need
61 * one matching service to justify ACL-checking a class. We might
62 * use other services belonging to the class if the first service
63 * turns out to be unusable for some reason.
64 */
65 AccessRule *r = *i;
66 ServicePointer service = findBestService(*r, false);
67 if (service != NULL) {
68 debugs(93, 5, "Adaptation::AccessCheck::check: rule '" << r->id << "' has candidate service '" << service->cfg().key << "'");
69 candidates += r->id;
70 }
71 }
72
73 checkCandidates();
74}
75
76// XXX: Here and everywhere we call FindRule(topCandidate()):
77// Once we identified the candidate, we should not just ignore it
78// if reconfigure changes rules. We should either lock the rule to
79// prevent reconfigure from stealing it or restart the check with
80// new rules. Throwing an exception may also be appropriate.
81void
82Adaptation::AccessCheck::checkCandidates()
83{
84 debugs(93, 3, "Adaptation::AccessCheck checks " << candidates.size());
85
86 while (!candidates.empty()) {
87 if (AccessRule *r = FindRule(topCandidate())) {
88 // XXX: we do not have access to conn->rfc931 here.
89 acl_checklist = aclChecklistCreate(r->acl, req, dash_str);
90 acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this);
91 return;
92 }
93
94 candidates.shift(); // the rule apparently went away (reconfigure)
95 }
96
97 // when there are no canidates, fake answer 1
98 debugs(93, 3, "Adaptation::AccessCheck::check: NO candidates left");
99 noteAnswer(1);
100}
101
102void
103Adaptation::AccessCheck::AccessCheckCallbackWrapper(int answer, void *data)
104{
105 debugs(93, 8, "AccessCheckCallbackWrapper: answer=" << answer);
106 AccessCheck *ac = (AccessCheck*)data;
107 ac->noteAnswer(answer);
108}
109
110void
111Adaptation::AccessCheck::noteAnswer(int answer)
112{
113 debugs(93, 5, HERE << "AccessCheck::noteAnswer " << answer);
114 if (candidates.size())
115 debugs(93, 5, HERE << "was checking " << topCandidate());
116
117 if (!answer) {
118 checkCandidates();
119 return;
120 }
121
122 /*
123 * We use an event here to break deep function call sequences
124 */
125 // XXX: use AsyncCall for callback and remove
126 CallJobHere(93, 5, this, Adaptation::AccessCheck::do_callback);
127}
128
129void
130Adaptation::AccessCheck::do_callback()
131{
132 debugs(93, 3, "Adaptation::AccessCheck::do_callback");
133
134 if (candidates.size())
135 debugs(93, 3, HERE << "was checking rule" << topCandidate());
136
137 void *validated_cbdata;
138 if (!cbdataReferenceValidDone(callback_data, &validated_cbdata)) {
139 debugs(93,3,HERE << "do_callback: callback_data became invalid, skipping");
140 return;
141 }
142
143 ServicePointer service = NULL;
144 if (candidates.size()) {
145 if (AccessRule *r = FindRule(topCandidate())) {
146 service = findBestService(*r, true);
147 if (service != NULL)
148 debugs(93,3,HERE << "do_callback: with service " << service->cfg().uri);
149 else
150 debugs(93,3,HERE << "do_callback: no service for rule" << r->id);
151 } else {
152 debugs(93,3,HERE << "do_callback: no rule" << topCandidate());
153 }
154 candidates.shift(); // done with topCandidate()
155 } else {
156 debugs(93,3,HERE << "do_callback: no candidate rules");
157 }
158
159 callback(service, validated_cbdata);
160 done = TRUE;
161}
162
163Adaptation::ServicePointer
164Adaptation::AccessCheck::findBestService(AccessRule &r, bool preferUp) {
165
166 const char *what = preferUp ? "up " : "";
167 debugs(93,7,HERE << "looking for the first matching " <<
168 what << "service in group " << r.groupId);
169
170 ServicePointer secondBest;
171
172 ServiceGroup *g = FindGroup(r.groupId);
173
174 if (!g) {
175 debugs(93,5,HERE << "lost " << r.groupId << " group in rule" << r.id);
176 return ServicePointer();
177 }
178
179 ServiceGroup::Loop loop(g->initialServices());
180 typedef ServiceGroup::iterator SGI;
181 for (SGI i = loop.begin; i != loop.end; ++i) {
182
183 ServicePointer service = FindService(*i);
184
185 if (!service)
186 continue;
187
188 if (method != service->cfg().method)
189 continue;
190
191 if (point != service->cfg().point)
192 continue;
193
194 // sending a message to a broken service is likely to cause errors
195 if (service->cfg().bypass && service->broken())
196 continue;
197
198 if (service->up()) {
199 // sending a message to a service that does not want it is useless
200 // note that we cannot check wantsUrl for service that is not "up"
201 // note that even essential services are skipped on unwanted URLs!
202 if (!service->wantsUrl(req->urlpath))
203 continue;
204 } else {
205 if (!secondBest)
206 secondBest = service;
207 if (preferUp) {
208 // the caller asked for an "up" service and we can bypass this one
209 if (service->cfg().bypass)
210 continue;
211 debugs(93,5,HERE << "cannot skip an essential down service");
212 what = "down-but-essential ";
213 }
214 }
215
216 debugs(93,5,HERE << "found first matching " <<
217 what << "service for " << r.groupId << " group in rule" << r.id <<
218 ": " << service->cfg().key);
219
220 return service;
221 }
222
223 if (secondBest != NULL) {
224 what = "down ";
225 debugs(93,5,HERE << "found first matching " <<
226 what << "service for " << r.groupId << " group in rule" << r.id <<
227 ": " << secondBest->cfg().key);
228 return secondBest;
229 }
230
231 debugs(93,5,HERE << "found no matching " <<
232 what << "services for " << r.groupId << " group in rule" << r.id);
233 return ServicePointer();
234}