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