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