]>
Commit | Line | Data |
---|---|---|
b510f3a1 AJ |
1 | /* |
2 | * DEBUG: section 93 eCAP Interface | |
3 | */ | |
582c2af2 | 4 | #include "squid.h" |
4d0854d4 AR |
5 | #include <libecap/common/area.h> |
6 | #include <libecap/common/delay.h> | |
22fff3bf AR |
7 | #include <libecap/common/named_values.h> |
8 | #include <libecap/common/names.h> | |
fdc96a39 | 9 | #include <libecap/adapter/xaction.h> |
1adcebc3 | 10 | #include "adaptation/Answer.h" |
22fff3bf | 11 | #include "adaptation/ecap/Config.h" |
602d9612 | 12 | #include "adaptation/ecap/XactionRep.h" |
3af10ac0 | 13 | #include "adaptation/Initiator.h" |
0a720258 | 14 | #include "base/AsyncJobCalls.h" |
3d93a84d | 15 | #include "base/TextException.h" |
af0ded40 | 16 | #include "format/Format.h" |
602d9612 A |
17 | #include "HttpReply.h" |
18 | #include "HttpRequest.h" | |
19 | #include "SquidTime.h" | |
fdc96a39 | 20 | |
574b508c | 21 | CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep); |
fdc96a39 | 22 | |
5038f9d8 AR |
23 | /// a libecap Visitor for converting adapter transaction options to HttpHeader |
24 | class OptionsExtractor: public libecap::NamedValueVisitor | |
25 | { | |
26 | public: | |
27 | typedef libecap::Name Name; | |
28 | typedef libecap::Area Area; | |
29 | ||
30 | OptionsExtractor(HttpHeader &aMeta): meta(aMeta) {} | |
31 | ||
32 | // libecap::NamedValueVisitor API | |
ec4d1a1d | 33 | virtual void visit(const Name &name, const Area &value) { |
5038f9d8 AR |
34 | meta.putExt(name.image().c_str(), value.toString().c_str()); |
35 | } | |
36 | ||
37 | HttpHeader &meta; ///< where to put extracted options | |
38 | }; | |
39 | ||
4299f876 | 40 | Adaptation::Ecap::XactionRep::XactionRep( |
af0ded40 | 41 | HttpMsg *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp, |
4cb2536f | 42 | const Adaptation::ServicePointer &aService): |
574b508c | 43 | AsyncJob("Adaptation::Ecap::XactionRep"), |
4299f876 | 44 | Adaptation::Initiate("Adaptation::Ecap::XactionRep"), |
a22e6cd3 | 45 | theService(aService), |
26ac0430 | 46 | theVirginRep(virginHeader), theCauseRep(NULL), |
e1e90d26 | 47 | makingVb(opUndecided), proxyingAb(opUndecided), |
3ff65596 | 48 | adaptHistoryId(-1), |
e1e90d26 | 49 | vbProductionFinished(false), |
af0ded40 CT |
50 | abProductionFinished(false), abProductionAtEnd(false), |
51 | al(alp) | |
fdc96a39 | 52 | { |
027320b4 | 53 | if (virginCause) |
4d0854d4 | 54 | theCauseRep = new MessageRep(virginCause); |
fdc96a39 AR |
55 | } |
56 | ||
574b508c | 57 | Adaptation::Ecap::XactionRep::~XactionRep() |
fdc96a39 AR |
58 | { |
59 | assert(!theMaster); | |
027320b4 | 60 | delete theCauseRep; |
4d0854d4 | 61 | theAnswerRep.reset(); |
fdc96a39 AR |
62 | } |
63 | ||
64 | void | |
574b508c | 65 | Adaptation::Ecap::XactionRep::master(const AdapterXaction &x) |
fdc96a39 AR |
66 | { |
67 | Must(!theMaster); | |
68 | Must(x != NULL); | |
69 | theMaster = x; | |
70 | } | |
71 | ||
a22e6cd3 AR |
72 | Adaptation::Service & |
73 | Adaptation::Ecap::XactionRep::service() | |
74 | { | |
75 | Must(theService != NULL); | |
76 | return *theService; | |
77 | } | |
78 | ||
22fff3bf AR |
79 | const libecap::Area |
80 | Adaptation::Ecap::XactionRep::option(const libecap::Name &name) const | |
81 | { | |
82 | if (name == libecap::metaClientIp) | |
83 | return clientIpValue(); | |
84 | if (name == libecap::metaUserName) | |
85 | return usernameValue(); | |
71be37e0 | 86 | if (Adaptation::Config::masterx_shared_name && name == Adaptation::Config::masterx_shared_name) |
5038f9d8 AR |
87 | return masterxSharedValue(name); |
88 | ||
89 | // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups | |
71be37e0 CT |
90 | |
91 | // If the name is unknown, metaValue returns an emtpy area | |
92 | return metaValue(name); | |
22fff3bf AR |
93 | } |
94 | ||
95 | void | |
96 | Adaptation::Ecap::XactionRep::visitEachOption(libecap::NamedValueVisitor &visitor) const | |
97 | { | |
98 | if (const libecap::Area value = clientIpValue()) | |
ec4d1a1d | 99 | visitor.visit(libecap::metaClientIp, value); |
22fff3bf | 100 | if (const libecap::Area value = usernameValue()) |
ec4d1a1d | 101 | visitor.visit(libecap::metaUserName, value); |
5038f9d8 AR |
102 | |
103 | if (Adaptation::Config::masterx_shared_name) { | |
ec4d1a1d A |
104 | const libecap::Name name(Adaptation::Config::masterx_shared_name); |
105 | if (const libecap::Area value = masterxSharedValue(name)) | |
106 | visitor.visit(name, value); | |
5038f9d8 | 107 | } |
71ee0835 | 108 | |
71be37e0 | 109 | visitEachMetaHeader(visitor); |
5038f9d8 AR |
110 | |
111 | // TODO: metaServerIp, metaAuthenticatedUser, and metaAuthenticatedGroups | |
22fff3bf AR |
112 | } |
113 | ||
114 | const libecap::Area | |
115 | Adaptation::Ecap::XactionRep::clientIpValue() const | |
116 | { | |
117 | const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ? | |
118 | theCauseRep->raw().header : theVirginRep.raw().header); | |
119 | Must(request); | |
120 | // TODO: move this logic into HttpRequest::clientIp(bool) and | |
121 | // HttpRequest::clientIpString(bool) and reuse everywhere | |
122 | if (TheConfig.send_client_ip && request) { | |
123 | Ip::Address client_addr; | |
124 | #if FOLLOW_X_FORWARDED_FOR | |
125 | if (TheConfig.use_indirect_client) { | |
126 | client_addr = request->indirect_client_addr; | |
ec4d1a1d | 127 | } else |
22fff3bf AR |
128 | #endif |
129 | client_addr = request->client_addr; | |
4dd643d5 | 130 | if (!client_addr.isAnyAddr() && !client_addr.isNoAddr()) { |
22fff3bf | 131 | char ntoabuf[MAX_IPSTRLEN] = ""; |
4dd643d5 | 132 | client_addr.toStr(ntoabuf,MAX_IPSTRLEN); |
22fff3bf AR |
133 | return libecap::Area::FromTempBuffer(ntoabuf, strlen(ntoabuf)); |
134 | } | |
ec4d1a1d | 135 | } |
22fff3bf AR |
136 | return libecap::Area(); |
137 | } | |
138 | ||
139 | const libecap::Area | |
140 | Adaptation::Ecap::XactionRep::usernameValue() const | |
141 | { | |
321f219c | 142 | #if USE_AUTH |
22fff3bf AR |
143 | const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ? |
144 | theCauseRep->raw().header : theVirginRep.raw().header); | |
145 | Must(request); | |
146 | if (request->auth_user_request != NULL) { | |
147 | if (char const *name = request->auth_user_request->username()) | |
148 | return libecap::Area::FromTempBuffer(name, strlen(name)); | |
b38b26cb | 149 | else if (request->extacl_user.size() > 0) |
8661a1d0 | 150 | return libecap::Area::FromTempBuffer(request->extacl_user.rawBuf(), |
cb72cd25 | 151 | request->extacl_user.size()); |
ec4d1a1d | 152 | } |
321f219c | 153 | #endif |
22fff3bf AR |
154 | return libecap::Area(); |
155 | } | |
156 | ||
5038f9d8 AR |
157 | const libecap::Area |
158 | Adaptation::Ecap::XactionRep::masterxSharedValue(const libecap::Name &name) const | |
159 | { | |
160 | const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ? | |
161 | theCauseRep->raw().header : theVirginRep.raw().header); | |
162 | Must(request); | |
163 | if (name.known()) { // must check to avoid empty names matching unset cfg | |
164 | Adaptation::History::Pointer ah = request->adaptHistory(false); | |
165 | if (ah != NULL) { | |
166 | String name, value; | |
167 | if (ah->getXxRecord(name, value)) | |
168 | return libecap::Area::FromTempBuffer(value.rawBuf(), value.size()); | |
169 | } | |
170 | } | |
171 | return libecap::Area(); | |
172 | } | |
173 | ||
71be37e0 CT |
174 | const libecap::Area |
175 | Adaptation::Ecap::XactionRep::metaValue(const libecap::Name &name) const | |
176 | { | |
177 | HttpRequest *request = dynamic_cast<HttpRequest*>(theCauseRep ? | |
71ee0835 | 178 | theCauseRep->raw().header : theVirginRep.raw().header); |
71be37e0 CT |
179 | Must(request); |
180 | HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header); | |
181 | ||
182 | if (name.known()) { // must check to avoid empty names matching unset cfg | |
d7f4a0b7 | 183 | typedef Notes::iterator ACAMLI; |
71ee0835 | 184 | for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) { |
d7f4a0b7 | 185 | if (name == (*i)->key.termedBuf()) { |
af0ded40 | 186 | if (const char *value = (*i)->match(request, reply, al)) |
71be37e0 CT |
187 | return libecap::Area::FromTempString(value); |
188 | else | |
189 | return libecap::Area(); | |
190 | } | |
191 | } | |
192 | } | |
193 | ||
194 | return libecap::Area(); | |
195 | } | |
196 | ||
71ee0835 | 197 | void |
71be37e0 CT |
198 | Adaptation::Ecap::XactionRep::visitEachMetaHeader(libecap::NamedValueVisitor &visitor) const |
199 | { | |
200 | HttpRequest *request = dynamic_cast<HttpRequest*>(theCauseRep ? | |
71ee0835 | 201 | theCauseRep->raw().header : theVirginRep.raw().header); |
71be37e0 CT |
202 | Must(request); |
203 | HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header); | |
71ee0835 | 204 | |
d7f4a0b7 | 205 | typedef Notes::iterator ACAMLI; |
71ee0835 | 206 | for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) { |
af0ded40 | 207 | const char *v = (*i)->match(request, reply, al); |
baa31c42 | 208 | if (v) { |
d7f4a0b7 | 209 | const libecap::Name name((*i)->key.termedBuf()); |
71be37e0 CT |
210 | const libecap::Area value = libecap::Area::FromTempString(v); |
211 | visitor.visit(name, value); | |
212 | } | |
213 | } | |
214 | } | |
215 | ||
fdc96a39 | 216 | void |
574b508c | 217 | Adaptation::Ecap::XactionRep::start() |
fdc96a39 AR |
218 | { |
219 | Must(theMaster); | |
4d0854d4 | 220 | |
e1e90d26 AR |
221 | if (!theVirginRep.raw().body_pipe) |
222 | makingVb = opNever; // there is nothing to deliver | |
4d0854d4 | 223 | |
d7f4a0b7 | 224 | HttpRequest *request = dynamic_cast<HttpRequest*> (theCauseRep ? |
ffb82151 | 225 | theCauseRep->raw().header : theVirginRep.raw().header); |
3ff65596 | 226 | Must(request); |
d7f4a0b7 CT |
227 | |
228 | HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header); | |
229 | ||
a22e6cd3 | 230 | Adaptation::History::Pointer ah = request->adaptLogHistory(); |
e1381638 | 231 | if (ah != NULL) { |
3ff65596 AR |
232 | // retrying=false because ecap never retries transactions |
233 | adaptHistoryId = ah->recordXactStart(service().cfg().key, current_time, false); | |
d7f4a0b7 CT |
234 | typedef Notes::iterator ACAMLI; |
235 | for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) { | |
af0ded40 | 236 | const char *v = (*i)->match(request, reply, al); |
cf9f0261 CT |
237 | if (v) { |
238 | if (ah->metaHeaders == NULL) | |
239 | ah->metaHeaders = new NotePairs(); | |
7e6ef752 | 240 | if (!ah->metaHeaders->hasPair((*i)->key.termedBuf(), v)) |
cf9f0261 | 241 | ah->metaHeaders->add((*i)->key.termedBuf(), v); |
d7f4a0b7 CT |
242 | } |
243 | } | |
3ff65596 AR |
244 | } |
245 | ||
fdc96a39 AR |
246 | theMaster->start(); |
247 | } | |
248 | ||
249 | void | |
574b508c | 250 | Adaptation::Ecap::XactionRep::swanSong() |
fdc96a39 | 251 | { |
506a0530 | 252 | // clear body_pipes, if any |
ea76d91e | 253 | // this code does not maintain proxying* and canAccessVb states; should it? |
506a0530 AR |
254 | |
255 | if (theAnswerRep != NULL) { | |
f1a768b2 AR |
256 | BodyPipe::Pointer body_pipe = answer().body_pipe; |
257 | if (body_pipe != NULL) { | |
258 | Must(body_pipe->stillProducing(this)); | |
259 | stopProducingFor(body_pipe, false); | |
260 | } | |
261 | } | |
506a0530 | 262 | |
e1e90d26 AR |
263 | BodyPipe::Pointer &body_pipe = theVirginRep.raw().body_pipe; |
264 | if (body_pipe != NULL && body_pipe->stillConsuming(this)) | |
265 | stopConsumingFrom(body_pipe); | |
506a0530 | 266 | |
fdc96a39 | 267 | terminateMaster(); |
3ff65596 AR |
268 | |
269 | const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ? | |
e1381638 | 270 | theCauseRep->raw().header : theVirginRep.raw().header); |
3ff65596 | 271 | Must(request); |
a22e6cd3 | 272 | Adaptation::History::Pointer ah = request->adaptLogHistory(); |
3ff65596 AR |
273 | if (ah != NULL && adaptHistoryId >= 0) |
274 | ah->recordXactFinish(adaptHistoryId); | |
275 | ||
fdc96a39 AR |
276 | Adaptation::Initiate::swanSong(); |
277 | } | |
278 | ||
0a720258 AR |
279 | void |
280 | Adaptation::Ecap::XactionRep::resume() | |
281 | { | |
282 | // go async to gain exception protection and done()-based job destruction | |
283 | typedef NullaryMemFunT<Adaptation::Ecap::XactionRep> Dialer; | |
284 | AsyncCall::Pointer call = asyncCall(93, 5, "Adaptation::Ecap::XactionRep::doResume", | |
285 | Dialer(this, &Adaptation::Ecap::XactionRep::doResume)); | |
286 | ScheduleCallHere(call); | |
287 | } | |
288 | ||
289 | /// the guts of libecap::host::Xaction::resume() API implementation | |
290 | /// which just goes async in Adaptation::Ecap::XactionRep::resume(). | |
291 | void | |
292 | Adaptation::Ecap::XactionRep::doResume() | |
293 | { | |
294 | Must(theMaster); | |
295 | theMaster->resume(); | |
296 | } | |
297 | ||
fdc96a39 | 298 | libecap::Message & |
574b508c | 299 | Adaptation::Ecap::XactionRep::virgin() |
fdc96a39 | 300 | { |
7b67e5b6 | 301 | return theVirginRep; |
fdc96a39 AR |
302 | } |
303 | ||
4d0854d4 | 304 | const libecap::Message & |
574b508c | 305 | Adaptation::Ecap::XactionRep::cause() |
fdc96a39 | 306 | { |
4d0854d4 AR |
307 | Must(theCauseRep != NULL); |
308 | return *theCauseRep; | |
fdc96a39 AR |
309 | } |
310 | ||
4d0854d4 | 311 | libecap::Message & |
574b508c | 312 | Adaptation::Ecap::XactionRep::adapted() |
fdc96a39 | 313 | { |
4d0854d4 AR |
314 | Must(theAnswerRep != NULL); |
315 | return *theAnswerRep; | |
316 | } | |
317 | ||
318 | Adaptation::Message & | |
574b508c | 319 | Adaptation::Ecap::XactionRep::answer() |
4d0854d4 | 320 | { |
f1a768b2 AR |
321 | MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get()); |
322 | Must(rep); | |
4d0854d4 AR |
323 | return rep->raw(); |
324 | } | |
325 | ||
ea76d91e | 326 | void |
574b508c | 327 | Adaptation::Ecap::XactionRep::terminateMaster() |
4d0854d4 AR |
328 | { |
329 | if (theMaster) { | |
ea76d91e AR |
330 | AdapterXaction x = theMaster; |
331 | theMaster.reset(); | |
332 | x->stop(); | |
f1a768b2 | 333 | } |
4d0854d4 AR |
334 | } |
335 | ||
4d0854d4 | 336 | bool |
574b508c | 337 | Adaptation::Ecap::XactionRep::doneAll() const |
4d0854d4 | 338 | { |
e1e90d26 | 339 | return makingVb >= opComplete && proxyingAb >= opComplete && |
26ac0430 | 340 | Adaptation::Initiate::doneAll(); |
4d0854d4 AR |
341 | } |
342 | ||
e1e90d26 | 343 | // stops receiving virgin and enables auto-consumption, dropping any vb bytes |
4d0854d4 | 344 | void |
e1e90d26 | 345 | Adaptation::Ecap::XactionRep::sinkVb(const char *reason) |
4d0854d4 | 346 | { |
e1e90d26 | 347 | debugs(93,4, HERE << "sink for " << reason << "; status:" << status()); |
4d0854d4 | 348 | |
e1e90d26 AR |
349 | // we reset raw().body_pipe when we are done, so use this one for checking |
350 | const BodyPipePointer &permPipe = theVirginRep.raw().header->body_pipe; | |
351 | if (permPipe != NULL) | |
352 | permPipe->enableAutoConsumption(); | |
3af10ac0 | 353 | |
e1e90d26 AR |
354 | forgetVb(reason); |
355 | } | |
356 | ||
357 | // stops receiving virgin but preserves it for others to use | |
358 | void | |
359 | Adaptation::Ecap::XactionRep::preserveVb(const char *reason) | |
360 | { | |
361 | debugs(93,4, HERE << "preserve for " << reason << "; status:" << status()); | |
362 | ||
363 | // we reset raw().body_pipe when we are done, so use this one for checking | |
364 | const BodyPipePointer &permPipe = theVirginRep.raw().header->body_pipe; | |
365 | if (permPipe != NULL) { | |
366 | // if libecap consumed, we cannot preserve | |
367 | Must(!permPipe->consumedSize()); | |
3af10ac0 AR |
368 | } |
369 | ||
e1e90d26 AR |
370 | forgetVb(reason); |
371 | } | |
372 | ||
373 | // disassociates us from vb; the last step of sinking or preserving vb | |
374 | void | |
375 | Adaptation::Ecap::XactionRep::forgetVb(const char *reason) | |
376 | { | |
377 | debugs(93,9, HERE << "forget vb " << reason << "; status:" << status()); | |
ea76d91e | 378 | |
e1e90d26 AR |
379 | BodyPipePointer &p = theVirginRep.raw().body_pipe; |
380 | if (p != NULL && p->stillConsuming(this)) | |
381 | stopConsumingFrom(p); | |
382 | ||
383 | if (makingVb == opUndecided) | |
384 | makingVb = opNever; | |
385 | else if (makingVb == opOn) | |
386 | makingVb = opComplete; | |
fdc96a39 AR |
387 | } |
388 | ||
26ac0430 | 389 | void |
574b508c | 390 | Adaptation::Ecap::XactionRep::useVirgin() |
fdc96a39 | 391 | { |
4d0854d4 | 392 | debugs(93,3, HERE << status()); |
ea76d91e AR |
393 | Must(proxyingAb == opUndecided); |
394 | proxyingAb = opNever; | |
4d0854d4 | 395 | |
e1e90d26 | 396 | preserveVb("useVirgin"); |
2874d9e3 AR |
397 | |
398 | HttpMsg *clone = theVirginRep.raw().header->clone(); | |
399 | // check that clone() copies the pipe so that we do not have to | |
e1e90d26 | 400 | Must(!theVirginRep.raw().header->body_pipe == !clone->body_pipe); |
ea76d91e | 401 | |
aaf0559d | 402 | updateHistory(clone); |
3af10ac0 | 403 | sendAnswer(Answer::Forward(clone)); |
4d0854d4 | 404 | Must(done()); |
fdc96a39 AR |
405 | } |
406 | ||
26ac0430 | 407 | void |
574b508c | 408 | Adaptation::Ecap::XactionRep::useAdapted(const libecap::shared_ptr<libecap::Message> &m) |
fdc96a39 | 409 | { |
4d0854d4 | 410 | debugs(93,3, HERE << status()); |
ea76d91e | 411 | Must(m); |
4d0854d4 | 412 | theAnswerRep = m; |
ea76d91e AR |
413 | Must(proxyingAb == opUndecided); |
414 | ||
f1a768b2 | 415 | HttpMsg *msg = answer().header; |
ea76d91e AR |
416 | if (!theAnswerRep->body()) { // final, bodyless answer |
417 | proxyingAb = opNever; | |
aaf0559d | 418 | updateHistory(msg); |
3af10ac0 | 419 | sendAnswer(Answer::Forward(msg)); |
f1a768b2 | 420 | } else { // got answer headers but need to handle body |
ea76d91e | 421 | proxyingAb = opOn; |
f1a768b2 | 422 | Must(!msg->body_pipe); // only host can set body pipes |
ea76d91e | 423 | MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get()); |
f1a768b2 AR |
424 | Must(rep); |
425 | rep->tieBody(this); // sets us as a producer | |
426 | Must(msg->body_pipe != NULL); // check tieBody | |
ea76d91e | 427 | |
aaf0559d | 428 | updateHistory(msg); |
3af10ac0 | 429 | sendAnswer(Answer::Forward(msg)); |
ea76d91e | 430 | |
4d0854d4 | 431 | debugs(93,4, HERE << "adapter will produce body" << status()); |
8679e6c2 | 432 | theMaster->abMake(); // libecap will produce |
4d0854d4 | 433 | } |
fdc96a39 AR |
434 | } |
435 | ||
3af10ac0 AR |
436 | void |
437 | Adaptation::Ecap::XactionRep::blockVirgin() | |
438 | { | |
439 | debugs(93,3, HERE << status()); | |
440 | Must(proxyingAb == opUndecided); | |
441 | proxyingAb = opNever; | |
442 | ||
e1e90d26 | 443 | sinkVb("blockVirgin"); |
3af10ac0 | 444 | |
aaf0559d | 445 | updateHistory(NULL); |
3af10ac0 AR |
446 | sendAnswer(Answer::Block(service().cfg().key)); |
447 | Must(done()); | |
448 | } | |
449 | ||
5038f9d8 AR |
450 | /// Called just before sendAnswer() to record adapter meta-information |
451 | /// which may affect answer processing and may be needed for logging. | |
452 | void | |
aaf0559d | 453 | Adaptation::Ecap::XactionRep::updateHistory(HttpMsg *adapted) |
5038f9d8 AR |
454 | { |
455 | if (!theMaster) // all updates rely on being able to query the adapter | |
456 | return; | |
457 | ||
458 | const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ? | |
459 | theCauseRep->raw().header : theVirginRep.raw().header); | |
460 | Must(request); | |
461 | ||
462 | // TODO: move common ICAP/eCAP logic to Adaptation::Xaction or similar | |
463 | // TODO: optimize Area-to-String conversion | |
464 | ||
465 | // update the cross-transactional database if needed | |
466 | if (const char *xxNameStr = Adaptation::Config::masterx_shared_name) { | |
467 | Adaptation::History::Pointer ah = request->adaptHistory(true); | |
468 | if (ah != NULL) { | |
469 | libecap::Name xxName(xxNameStr); // TODO: optimize? | |
470 | if (const libecap::Area val = theMaster->option(xxName)) | |
471 | ah->updateXxRecord(xxNameStr, val.toString().c_str()); | |
472 | } | |
473 | } | |
474 | ||
475 | // update the adaptation plan if needed | |
476 | if (service().cfg().routing) { | |
477 | String services; | |
478 | if (const libecap::Area services = theMaster->option(libecap::metaNextServices)) { | |
479 | Adaptation::History::Pointer ah = request->adaptHistory(true); | |
480 | if (ah != NULL) | |
481 | ah->updateNextServices(services.toString().c_str()); | |
482 | } | |
483 | } // TODO: else warn (occasionally!) if we got libecap::metaNextServices | |
484 | ||
485 | // Store received meta headers for adapt::<last_h logformat code use. | |
486 | // If we already have stored headers from a previous adaptation transaction | |
487 | // related to the same master transction, they will be replaced. | |
488 | Adaptation::History::Pointer ah = request->adaptLogHistory(); | |
489 | if (ah != NULL) { | |
490 | HttpHeader meta(hoReply); | |
491 | OptionsExtractor extractor(meta); | |
492 | theMaster->visitEachOption(extractor); | |
493 | ah->recordMeta(&meta); | |
494 | } | |
aaf0559d AR |
495 | |
496 | // Add just-created history to the adapted/cloned request that lacks it. | |
497 | if (HttpRequest *adaptedReq = dynamic_cast<HttpRequest*>(adapted)) | |
498 | adaptedReq->adaptHistoryImport(*request); | |
5038f9d8 AR |
499 | } |
500 | ||
4d0854d4 | 501 | void |
574b508c | 502 | Adaptation::Ecap::XactionRep::vbDiscard() |
fdc96a39 | 503 | { |
e1e90d26 | 504 | Must(makingVb == opUndecided); |
8679e6c2 | 505 | // if adapter does not need vb, we do not need to send it |
e1e90d26 AR |
506 | sinkVb("vbDiscard"); |
507 | Must(makingVb == opNever); | |
fdc96a39 AR |
508 | } |
509 | ||
4d0854d4 | 510 | void |
574b508c | 511 | Adaptation::Ecap::XactionRep::vbMake() |
fdc96a39 | 512 | { |
e1e90d26 | 513 | Must(makingVb == opUndecided); |
ea76d91e AR |
514 | BodyPipePointer &p = theVirginRep.raw().body_pipe; |
515 | Must(p != NULL); | |
e1e90d26 AR |
516 | Must(p->setConsumerIfNotLate(this)); // to deliver vb, we must receive vb |
517 | makingVb = opOn; | |
fdc96a39 AR |
518 | } |
519 | ||
4d0854d4 | 520 | void |
574b508c | 521 | Adaptation::Ecap::XactionRep::vbStopMaking() |
fdc96a39 | 522 | { |
e1e90d26 | 523 | Must(makingVb == opOn); |
ea76d91e | 524 | // if adapter does not need vb, we do not need to receive it |
e1e90d26 AR |
525 | sinkVb("vbStopMaking"); |
526 | Must(makingVb == opComplete); | |
4d0854d4 AR |
527 | } |
528 | ||
529 | void | |
574b508c | 530 | Adaptation::Ecap::XactionRep::vbMakeMore() |
4d0854d4 | 531 | { |
e1e90d26 | 532 | Must(makingVb == opOn); // cannot make more if done proxying |
ea76d91e | 533 | // we cannot guarantee more vb, but we can check that there is a chance |
e1e90d26 AR |
534 | const BodyPipePointer &p = theVirginRep.raw().body_pipe; |
535 | Must(p != NULL && p->stillConsuming(this)); // we are plugged in | |
536 | Must(!p->productionEnded() && p->mayNeedMoreData()); // and may get more | |
4d0854d4 AR |
537 | } |
538 | ||
8679e6c2 | 539 | libecap::Area |
574b508c | 540 | Adaptation::Ecap::XactionRep::vbContent(libecap::size_type o, libecap::size_type s) |
4d0854d4 | 541 | { |
e1e90d26 | 542 | // We may not be makingVb yet. It should be OK, but see vbContentShift(). |
ea76d91e | 543 | |
8679e6c2 | 544 | const BodyPipePointer &p = theVirginRep.raw().body_pipe; |
ea76d91e AR |
545 | Must(p != NULL); |
546 | ||
547 | // TODO: make MemBuf use size_t? | |
548 | const size_t haveSize = static_cast<size_t>(p->buf().contentSize()); | |
4d0854d4 | 549 | |
8679e6c2 AR |
550 | // convert to Squid types; XXX: check for overflow |
551 | const uint64_t offset = static_cast<uint64_t>(o); | |
552 | Must(offset <= haveSize); // equal iff at the end of content | |
553 | ||
554 | // nsize means no size limit: all content starting from offset | |
555 | const size_t size = s == libecap::nsize ? | |
26ac0430 | 556 | haveSize - offset : static_cast<size_t>(s); |
8679e6c2 | 557 | |
8679e6c2 AR |
558 | // XXX: optimize by making theBody a shared_ptr (see Area::FromTemp*() src) |
559 | return libecap::Area::FromTempBuffer(p->buf().content() + offset, | |
26ac0430 | 560 | min(static_cast<size_t>(haveSize - offset), size)); |
4d0854d4 AR |
561 | } |
562 | ||
563 | void | |
574b508c | 564 | Adaptation::Ecap::XactionRep::vbContentShift(libecap::size_type n) |
4d0854d4 | 565 | { |
e1e90d26 | 566 | // We may not be makingVb yet. It should be OK now, but if BodyPipe |
ea76d91e AR |
567 | // consume() requirements change, we would have to return empty vbContent |
568 | // until the adapter registers as a consumer | |
569 | ||
8679e6c2 AR |
570 | BodyPipePointer &p = theVirginRep.raw().body_pipe; |
571 | Must(p != NULL); | |
572 | const size_t size = static_cast<size_t>(n); // XXX: check for overflow | |
573 | const size_t haveSize = static_cast<size_t>(p->buf().contentSize()); // TODO: make MemBuf use size_t? | |
574 | p->consume(min(size, haveSize)); | |
4d0854d4 AR |
575 | } |
576 | ||
577 | void | |
574b508c | 578 | Adaptation::Ecap::XactionRep::noteAbContentDone(bool atEnd) |
4d0854d4 | 579 | { |
7477a343 AR |
580 | Must(proxyingAb == opOn && !abProductionFinished); |
581 | abProductionFinished = true; | |
582 | abProductionAtEnd = atEnd; // store until ready to stop producing ourselves | |
583 | debugs(93,5, HERE << "adapted body production ended"); | |
584 | moveAbContent(); | |
4d0854d4 AR |
585 | } |
586 | ||
8679e6c2 | 587 | void |
574b508c | 588 | Adaptation::Ecap::XactionRep::noteAbContentAvailable() |
4d0854d4 | 589 | { |
7477a343 | 590 | Must(proxyingAb == opOn && !abProductionFinished); |
8679e6c2 | 591 | moveAbContent(); |
4d0854d4 AR |
592 | } |
593 | ||
ea76d91e | 594 | #if 0 /* XXX: implement */ |
4d0854d4 | 595 | void |
574b508c | 596 | Adaptation::Ecap::XactionRep::setAdaptedBodySize(const libecap::BodySize &size) |
4d0854d4 AR |
597 | { |
598 | Must(answer().body_pipe != NULL); | |
8679e6c2 AR |
599 | if (size.known()) |
600 | answer().body_pipe->setBodySize(size.value()); | |
601 | // else the piped body size is unknown by default | |
4d0854d4 | 602 | } |
8679e6c2 | 603 | #endif |
4d0854d4 AR |
604 | |
605 | void | |
574b508c | 606 | Adaptation::Ecap::XactionRep::adaptationDelayed(const libecap::Delay &d) |
4d0854d4 AR |
607 | { |
608 | debugs(93,3, HERE << "adapter needs time: " << | |
26ac0430 | 609 | d.state << '/' << d.progress); |
4d0854d4 | 610 | // XXX: set timeout? |
fdc96a39 AR |
611 | } |
612 | ||
26ac0430 | 613 | void |
574b508c | 614 | Adaptation::Ecap::XactionRep::adaptationAborted() |
fdc96a39 | 615 | { |
fdc96a39 | 616 | tellQueryAborted(true); // should eCAP support retries? |
ea76d91e | 617 | mustStop("adaptationAborted"); |
fdc96a39 AR |
618 | } |
619 | ||
26ac0430 | 620 | void |
574b508c | 621 | Adaptation::Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp) |
fdc96a39 | 622 | { |
ea76d91e AR |
623 | Must(proxyingAb == opOn); |
624 | moveAbContent(); | |
fdc96a39 AR |
625 | } |
626 | ||
26ac0430 | 627 | void |
574b508c | 628 | Adaptation::Ecap::XactionRep::noteBodyConsumerAborted(RefCount<BodyPipe> bp) |
fdc96a39 | 629 | { |
ea76d91e AR |
630 | Must(proxyingAb == opOn); |
631 | stopProducingFor(answer().body_pipe, false); | |
632 | Must(theMaster); | |
633 | theMaster->abStopMaking(); | |
634 | proxyingAb = opComplete; | |
fdc96a39 AR |
635 | } |
636 | ||
637 | void | |
574b508c | 638 | Adaptation::Ecap::XactionRep::noteMoreBodyDataAvailable(RefCount<BodyPipe> bp) |
fdc96a39 | 639 | { |
e1e90d26 | 640 | Must(makingVb == opOn); // or we would not be registered as a consumer |
fdc96a39 | 641 | Must(theMaster); |
8679e6c2 | 642 | theMaster->noteVbContentAvailable(); |
fdc96a39 AR |
643 | } |
644 | ||
645 | void | |
574b508c | 646 | Adaptation::Ecap::XactionRep::noteBodyProductionEnded(RefCount<BodyPipe> bp) |
fdc96a39 | 647 | { |
e1e90d26 | 648 | Must(makingVb == opOn); // or we would not be registered as a consumer |
fdc96a39 | 649 | Must(theMaster); |
8679e6c2 | 650 | theMaster->noteVbContentDone(true); |
e1e90d26 | 651 | vbProductionFinished = true; |
fdc96a39 AR |
652 | } |
653 | ||
654 | void | |
574b508c | 655 | Adaptation::Ecap::XactionRep::noteBodyProducerAborted(RefCount<BodyPipe> bp) |
fdc96a39 | 656 | { |
e1e90d26 | 657 | Must(makingVb == opOn); // or we would not be registered as a consumer |
8679e6c2 AR |
658 | Must(theMaster); |
659 | theMaster->noteVbContentDone(false); | |
e1e90d26 | 660 | vbProductionFinished = true; |
fdc96a39 AR |
661 | } |
662 | ||
663 | void | |
574b508c | 664 | Adaptation::Ecap::XactionRep::noteInitiatorAborted() |
fdc96a39 AR |
665 | { |
666 | mustStop("initiator aborted"); | |
667 | } | |
668 | ||
8679e6c2 AR |
669 | // get content from the adapter and put it into the adapted pipe |
670 | void | |
574b508c | 671 | Adaptation::Ecap::XactionRep::moveAbContent() |
8679e6c2 | 672 | { |
ea76d91e | 673 | Must(proxyingAb == opOn); |
8679e6c2 | 674 | const libecap::Area c = theMaster->abContent(0, libecap::nsize); |
7477a343 AR |
675 | debugs(93,5, HERE << "up to " << c.size << " bytes"); |
676 | if (c.size == 0 && abProductionFinished) { // no ab now and in the future | |
677 | stopProducingFor(answer().body_pipe, abProductionAtEnd); | |
678 | proxyingAb = opComplete; | |
679 | debugs(93,5, HERE << "last adapted body data retrieved"); | |
e1381638 | 680 | } else if (c.size > 0) { |
7477a343 AR |
681 | if (const size_t used = answer().body_pipe->putMoreData(c.start, c.size)) |
682 | theMaster->abContentShift(used); | |
683 | } | |
8679e6c2 AR |
684 | } |
685 | ||
686 | const char * | |
574b508c | 687 | Adaptation::Ecap::XactionRep::status() const |
fdc96a39 | 688 | { |
4d0854d4 AR |
689 | static MemBuf buf; |
690 | buf.reset(); | |
691 | ||
692 | buf.append(" [", 2); | |
693 | ||
e1e90d26 AR |
694 | if (makingVb) |
695 | buf.Printf("M%d", static_cast<int>(makingVb)); | |
696 | ||
697 | const BodyPipePointer &vp = theVirginRep.raw().body_pipe; | |
698 | if (!vp) | |
699 | buf.append(" !V", 3); | |
ec4d1a1d | 700 | else if (vp->stillConsuming(const_cast<XactionRep*>(this))) |
e1e90d26 AR |
701 | buf.append(" Vc", 3); |
702 | else | |
703 | buf.append(" V?", 3); | |
704 | ||
705 | if (vbProductionFinished) | |
706 | buf.append(".", 1); | |
707 | ||
e1e90d26 | 708 | buf.Printf(" A%d", static_cast<int>(proxyingAb)); |
506a0530 | 709 | |
ea76d91e AR |
710 | if (proxyingAb == opOn) { |
711 | MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get()); | |
712 | Must(rep); | |
f1a768b2 | 713 | const BodyPipePointer &ap = rep->raw().body_pipe; |
e1e90d26 AR |
714 | if (!ap) |
715 | buf.append(" !A", 3); | |
716 | else if (ap->stillProducing(const_cast<XactionRep*>(this))) | |
717 | buf.append(" Ap", 3); | |
718 | else | |
719 | buf.append(" A?", 3); | |
f1a768b2 | 720 | } |
4d0854d4 | 721 | |
52ed047a | 722 | buf.Printf(" %s%u]", id.Prefix, id.value); |
4d0854d4 AR |
723 | |
724 | buf.terminate(); | |
725 | ||
726 | return buf.content(); | |
fdc96a39 | 727 | } |