]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/ecap/XactionRep.cc
%tS logformat code
[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"
af0ded40 16#include "format/Format.h"
602d9612
A
17#include "HttpReply.h"
18#include "HttpRequest.h"
19#include "SquidTime.h"
fdc96a39 20
574b508c 21CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep);
fdc96a39 22
5038f9d8
AR
23/// a libecap Visitor for converting adapter transaction options to HttpHeader
24class OptionsExtractor: public libecap::NamedValueVisitor
25{
26public:
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 40Adaptation::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 57Adaptation::Ecap::XactionRep::~XactionRep()
fdc96a39
AR
58{
59 assert(!theMaster);
027320b4 60 delete theCauseRep;
4d0854d4 61 theAnswerRep.reset();
fdc96a39
AR
62}
63
64void
574b508c 65Adaptation::Ecap::XactionRep::master(const AdapterXaction &x)
fdc96a39
AR
66{
67 Must(!theMaster);
68 Must(x != NULL);
69 theMaster = x;
70}
71
a22e6cd3
AR
72Adaptation::Service &
73Adaptation::Ecap::XactionRep::service()
74{
75 Must(theService != NULL);
76 return *theService;
77}
78
22fff3bf
AR
79const libecap::Area
80Adaptation::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
95void
96Adaptation::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
114const libecap::Area
115Adaptation::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
139const libecap::Area
140Adaptation::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
157const libecap::Area
158Adaptation::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
174const libecap::Area
175Adaptation::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 197void
71be37e0
CT
198Adaptation::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 216void
574b508c 217Adaptation::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
249void
574b508c 250Adaptation::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
279void
280Adaptation::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().
291void
292Adaptation::Ecap::XactionRep::doResume()
293{
294 Must(theMaster);
295 theMaster->resume();
296}
297
fdc96a39 298libecap::Message &
574b508c 299Adaptation::Ecap::XactionRep::virgin()
fdc96a39 300{
7b67e5b6 301 return theVirginRep;
fdc96a39
AR
302}
303
4d0854d4 304const libecap::Message &
574b508c 305Adaptation::Ecap::XactionRep::cause()
fdc96a39 306{
4d0854d4
AR
307 Must(theCauseRep != NULL);
308 return *theCauseRep;
fdc96a39
AR
309}
310
4d0854d4 311libecap::Message &
574b508c 312Adaptation::Ecap::XactionRep::adapted()
fdc96a39 313{
4d0854d4
AR
314 Must(theAnswerRep != NULL);
315 return *theAnswerRep;
316}
317
318Adaptation::Message &
574b508c 319Adaptation::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 326void
574b508c 327Adaptation::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 336bool
574b508c 337Adaptation::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 344void
e1e90d26 345Adaptation::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
358void
359Adaptation::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
374void
375Adaptation::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 389void
574b508c 390Adaptation::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 407void
574b508c 408Adaptation::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
436void
437Adaptation::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.
452void
aaf0559d 453Adaptation::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 501void
574b508c 502Adaptation::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 510void
574b508c 511Adaptation::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 520void
574b508c 521Adaptation::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
529void
574b508c 530Adaptation::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 539libecap::Area
574b508c 540Adaptation::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
563void
574b508c 564Adaptation::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
577void
574b508c 578Adaptation::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 587void
574b508c 588Adaptation::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 595void
574b508c 596Adaptation::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
605void
574b508c 606Adaptation::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 613void
574b508c 614Adaptation::Ecap::XactionRep::adaptationAborted()
fdc96a39 615{
fdc96a39 616 tellQueryAborted(true); // should eCAP support retries?
ea76d91e 617 mustStop("adaptationAborted");
fdc96a39
AR
618}
619
26ac0430 620void
574b508c 621Adaptation::Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp)
fdc96a39 622{
ea76d91e
AR
623 Must(proxyingAb == opOn);
624 moveAbContent();
fdc96a39
AR
625}
626
26ac0430 627void
574b508c 628Adaptation::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
637void
574b508c 638Adaptation::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
645void
574b508c 646Adaptation::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
654void
574b508c 655Adaptation::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
663void
574b508c 664Adaptation::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
670void
574b508c 671Adaptation::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
686const char *
574b508c 687Adaptation::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}