]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/ecap/XactionRep.cc
libecap v0.2.0 options support: supply client IP and user name to eCAP.
[thirdparty/squid.git] / src / adaptation / ecap / XactionRep.cc
CommitLineData
b510f3a1
AJ
1/*
2 * DEBUG: section 93 eCAP Interface
3 */
fdc96a39 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>
fdc96a39
AR
10#include "HttpRequest.h"
11#include "HttpReply.h"
3ff65596 12#include "SquidTime.h"
1f3c65fc 13#include "adaptation/ecap/XactionRep.h"
22fff3bf 14#include "adaptation/ecap/Config.h"
3af10ac0 15#include "adaptation/Initiator.h"
3d93a84d 16#include "base/TextException.h"
fdc96a39 17
574b508c 18CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep);
fdc96a39
AR
19
20
4299f876 21Adaptation::Ecap::XactionRep::XactionRep(
4cb2536f
A
22 HttpMsg *virginHeader, HttpRequest *virginCause,
23 const Adaptation::ServicePointer &aService):
574b508c 24 AsyncJob("Adaptation::Ecap::XactionRep"),
4299f876 25 Adaptation::Initiate("Adaptation::Ecap::XactionRep"),
a22e6cd3 26 theService(aService),
26ac0430 27 theVirginRep(virginHeader), theCauseRep(NULL),
e1e90d26 28 makingVb(opUndecided), proxyingAb(opUndecided),
3ff65596 29 adaptHistoryId(-1),
e1e90d26 30 vbProductionFinished(false),
7477a343 31 abProductionFinished(false), abProductionAtEnd(false)
fdc96a39 32{
027320b4 33 if (virginCause)
4d0854d4 34 theCauseRep = new MessageRep(virginCause);
fdc96a39
AR
35}
36
574b508c 37Adaptation::Ecap::XactionRep::~XactionRep()
fdc96a39
AR
38{
39 assert(!theMaster);
027320b4 40 delete theCauseRep;
4d0854d4 41 theAnswerRep.reset();
fdc96a39
AR
42}
43
44void
574b508c 45Adaptation::Ecap::XactionRep::master(const AdapterXaction &x)
fdc96a39
AR
46{
47 Must(!theMaster);
48 Must(x != NULL);
49 theMaster = x;
50}
51
a22e6cd3
AR
52Adaptation::Service &
53Adaptation::Ecap::XactionRep::service()
54{
55 Must(theService != NULL);
56 return *theService;
57}
58
22fff3bf
AR
59const libecap::Area
60Adaptation::Ecap::XactionRep::option(const libecap::Name &name) const
61{
62 if (name == libecap::metaClientIp)
63 return clientIpValue();
64 if (name == libecap::metaUserName)
65 return usernameValue();
66 // TODO: metaServerIp, metaAuthenticatedUser, metaAuthenticatedGroups, and
67 // Adaptation::Config::masterx_shared_name
68 return libecap::Area();
69}
70
71void
72Adaptation::Ecap::XactionRep::visitEachOption(libecap::NamedValueVisitor &visitor) const
73{
74 if (const libecap::Area value = clientIpValue())
75 visitor.visit(libecap::metaClientIp, value);
76 if (const libecap::Area value = usernameValue())
77 visitor.visit(libecap::metaUserName, value);
78 // TODO: metaServerIp, metaAuthenticatedUser, metaAuthenticatedGroups, and
79 // Adaptation::Config::masterx_shared_name
80}
81
82const libecap::Area
83Adaptation::Ecap::XactionRep::clientIpValue() const
84{
85 const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ?
86 theCauseRep->raw().header : theVirginRep.raw().header);
87 Must(request);
88 // TODO: move this logic into HttpRequest::clientIp(bool) and
89 // HttpRequest::clientIpString(bool) and reuse everywhere
90 if (TheConfig.send_client_ip && request) {
91 Ip::Address client_addr;
92#if FOLLOW_X_FORWARDED_FOR
93 if (TheConfig.use_indirect_client) {
94 client_addr = request->indirect_client_addr;
95 } else
96#endif
97 client_addr = request->client_addr;
98 if (!client_addr.IsAnyAddr() && !client_addr.IsNoAddr()) {
99 char ntoabuf[MAX_IPSTRLEN] = "";
100 client_addr.NtoA(ntoabuf,MAX_IPSTRLEN);
101 return libecap::Area::FromTempBuffer(ntoabuf, strlen(ntoabuf));
102 }
103 }
104 return libecap::Area();
105}
106
107const libecap::Area
108Adaptation::Ecap::XactionRep::usernameValue() const
109{
110 const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ?
111 theCauseRep->raw().header : theVirginRep.raw().header);
112 Must(request);
113 if (request->auth_user_request != NULL) {
114 if (char const *name = request->auth_user_request->username())
115 return libecap::Area::FromTempBuffer(name, strlen(name));
116 }
117 return libecap::Area();
118}
119
fdc96a39 120void
574b508c 121Adaptation::Ecap::XactionRep::start()
fdc96a39
AR
122{
123 Must(theMaster);
4d0854d4 124
e1e90d26
AR
125 if (!theVirginRep.raw().body_pipe)
126 makingVb = opNever; // there is nothing to deliver
4d0854d4 127
3ff65596 128 const HttpRequest *request = dynamic_cast<const HttpRequest*> (theCauseRep ?
e1381638 129 theCauseRep->raw().header : theVirginRep.raw().header);
3ff65596 130 Must(request);
a22e6cd3 131 Adaptation::History::Pointer ah = request->adaptLogHistory();
e1381638 132 if (ah != NULL) {
3ff65596
AR
133 // retrying=false because ecap never retries transactions
134 adaptHistoryId = ah->recordXactStart(service().cfg().key, current_time, false);
135 }
136
fdc96a39
AR
137 theMaster->start();
138}
139
140void
574b508c 141Adaptation::Ecap::XactionRep::swanSong()
fdc96a39 142{
506a0530 143 // clear body_pipes, if any
ea76d91e 144 // this code does not maintain proxying* and canAccessVb states; should it?
506a0530
AR
145
146 if (theAnswerRep != NULL) {
f1a768b2
AR
147 BodyPipe::Pointer body_pipe = answer().body_pipe;
148 if (body_pipe != NULL) {
149 Must(body_pipe->stillProducing(this));
150 stopProducingFor(body_pipe, false);
151 }
152 }
506a0530 153
e1e90d26
AR
154 BodyPipe::Pointer &body_pipe = theVirginRep.raw().body_pipe;
155 if (body_pipe != NULL && body_pipe->stillConsuming(this))
156 stopConsumingFrom(body_pipe);
506a0530 157
fdc96a39 158 terminateMaster();
3ff65596
AR
159
160 const HttpRequest *request = dynamic_cast<const HttpRequest*>(theCauseRep ?
e1381638 161 theCauseRep->raw().header : theVirginRep.raw().header);
3ff65596 162 Must(request);
a22e6cd3 163 Adaptation::History::Pointer ah = request->adaptLogHistory();
3ff65596
AR
164 if (ah != NULL && adaptHistoryId >= 0)
165 ah->recordXactFinish(adaptHistoryId);
166
fdc96a39
AR
167 Adaptation::Initiate::swanSong();
168}
169
fdc96a39 170libecap::Message &
574b508c 171Adaptation::Ecap::XactionRep::virgin()
fdc96a39 172{
7b67e5b6 173 return theVirginRep;
fdc96a39
AR
174}
175
4d0854d4 176const libecap::Message &
574b508c 177Adaptation::Ecap::XactionRep::cause()
fdc96a39 178{
4d0854d4
AR
179 Must(theCauseRep != NULL);
180 return *theCauseRep;
fdc96a39
AR
181}
182
4d0854d4 183libecap::Message &
574b508c 184Adaptation::Ecap::XactionRep::adapted()
fdc96a39 185{
4d0854d4
AR
186 Must(theAnswerRep != NULL);
187 return *theAnswerRep;
188}
189
190Adaptation::Message &
574b508c 191Adaptation::Ecap::XactionRep::answer()
4d0854d4 192{
f1a768b2
AR
193 MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get());
194 Must(rep);
4d0854d4
AR
195 return rep->raw();
196}
197
ea76d91e 198void
574b508c 199Adaptation::Ecap::XactionRep::terminateMaster()
4d0854d4
AR
200{
201 if (theMaster) {
ea76d91e
AR
202 AdapterXaction x = theMaster;
203 theMaster.reset();
204 x->stop();
f1a768b2 205 }
4d0854d4
AR
206}
207
4d0854d4 208bool
574b508c 209Adaptation::Ecap::XactionRep::doneAll() const
4d0854d4 210{
e1e90d26 211 return makingVb >= opComplete && proxyingAb >= opComplete &&
26ac0430 212 Adaptation::Initiate::doneAll();
4d0854d4
AR
213}
214
e1e90d26 215// stops receiving virgin and enables auto-consumption, dropping any vb bytes
4d0854d4 216void
e1e90d26 217Adaptation::Ecap::XactionRep::sinkVb(const char *reason)
4d0854d4 218{
e1e90d26 219 debugs(93,4, HERE << "sink for " << reason << "; status:" << status());
4d0854d4 220
e1e90d26
AR
221 // we reset raw().body_pipe when we are done, so use this one for checking
222 const BodyPipePointer &permPipe = theVirginRep.raw().header->body_pipe;
223 if (permPipe != NULL)
224 permPipe->enableAutoConsumption();
3af10ac0 225
e1e90d26
AR
226 forgetVb(reason);
227}
228
229// stops receiving virgin but preserves it for others to use
230void
231Adaptation::Ecap::XactionRep::preserveVb(const char *reason)
232{
233 debugs(93,4, HERE << "preserve for " << reason << "; status:" << status());
234
235 // we reset raw().body_pipe when we are done, so use this one for checking
236 const BodyPipePointer &permPipe = theVirginRep.raw().header->body_pipe;
237 if (permPipe != NULL) {
238 // if libecap consumed, we cannot preserve
239 Must(!permPipe->consumedSize());
3af10ac0
AR
240 }
241
e1e90d26
AR
242 forgetVb(reason);
243}
244
245// disassociates us from vb; the last step of sinking or preserving vb
246void
247Adaptation::Ecap::XactionRep::forgetVb(const char *reason)
248{
249 debugs(93,9, HERE << "forget vb " << reason << "; status:" << status());
ea76d91e 250
e1e90d26
AR
251 BodyPipePointer &p = theVirginRep.raw().body_pipe;
252 if (p != NULL && p->stillConsuming(this))
253 stopConsumingFrom(p);
254
255 if (makingVb == opUndecided)
256 makingVb = opNever;
257 else if (makingVb == opOn)
258 makingVb = opComplete;
fdc96a39
AR
259}
260
26ac0430 261void
574b508c 262Adaptation::Ecap::XactionRep::useVirgin()
fdc96a39 263{
4d0854d4 264 debugs(93,3, HERE << status());
ea76d91e
AR
265 Must(proxyingAb == opUndecided);
266 proxyingAb = opNever;
4d0854d4 267
e1e90d26 268 preserveVb("useVirgin");
2874d9e3
AR
269
270 HttpMsg *clone = theVirginRep.raw().header->clone();
271 // check that clone() copies the pipe so that we do not have to
e1e90d26 272 Must(!theVirginRep.raw().header->body_pipe == !clone->body_pipe);
ea76d91e 273
3af10ac0 274 sendAnswer(Answer::Forward(clone));
4d0854d4 275 Must(done());
fdc96a39
AR
276}
277
26ac0430 278void
574b508c 279Adaptation::Ecap::XactionRep::useAdapted(const libecap::shared_ptr<libecap::Message> &m)
fdc96a39 280{
4d0854d4 281 debugs(93,3, HERE << status());
ea76d91e 282 Must(m);
4d0854d4 283 theAnswerRep = m;
ea76d91e
AR
284 Must(proxyingAb == opUndecided);
285
f1a768b2 286 HttpMsg *msg = answer().header;
ea76d91e
AR
287 if (!theAnswerRep->body()) { // final, bodyless answer
288 proxyingAb = opNever;
3af10ac0 289 sendAnswer(Answer::Forward(msg));
f1a768b2 290 } else { // got answer headers but need to handle body
ea76d91e 291 proxyingAb = opOn;
f1a768b2 292 Must(!msg->body_pipe); // only host can set body pipes
ea76d91e 293 MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get());
f1a768b2
AR
294 Must(rep);
295 rep->tieBody(this); // sets us as a producer
296 Must(msg->body_pipe != NULL); // check tieBody
ea76d91e 297
3af10ac0 298 sendAnswer(Answer::Forward(msg));
ea76d91e 299
4d0854d4 300 debugs(93,4, HERE << "adapter will produce body" << status());
8679e6c2 301 theMaster->abMake(); // libecap will produce
4d0854d4 302 }
fdc96a39
AR
303}
304
3af10ac0
AR
305void
306Adaptation::Ecap::XactionRep::blockVirgin()
307{
308 debugs(93,3, HERE << status());
309 Must(proxyingAb == opUndecided);
310 proxyingAb = opNever;
311
e1e90d26 312 sinkVb("blockVirgin");
3af10ac0
AR
313
314 sendAnswer(Answer::Block(service().cfg().key));
315 Must(done());
316}
317
4d0854d4 318void
574b508c 319Adaptation::Ecap::XactionRep::vbDiscard()
fdc96a39 320{
e1e90d26 321 Must(makingVb == opUndecided);
8679e6c2 322 // if adapter does not need vb, we do not need to send it
e1e90d26
AR
323 sinkVb("vbDiscard");
324 Must(makingVb == opNever);
fdc96a39
AR
325}
326
4d0854d4 327void
574b508c 328Adaptation::Ecap::XactionRep::vbMake()
fdc96a39 329{
e1e90d26 330 Must(makingVb == opUndecided);
ea76d91e
AR
331 BodyPipePointer &p = theVirginRep.raw().body_pipe;
332 Must(p != NULL);
e1e90d26
AR
333 Must(p->setConsumerIfNotLate(this)); // to deliver vb, we must receive vb
334 makingVb = opOn;
fdc96a39
AR
335}
336
4d0854d4 337void
574b508c 338Adaptation::Ecap::XactionRep::vbStopMaking()
fdc96a39 339{
e1e90d26 340 Must(makingVb == opOn);
ea76d91e 341 // if adapter does not need vb, we do not need to receive it
e1e90d26
AR
342 sinkVb("vbStopMaking");
343 Must(makingVb == opComplete);
4d0854d4
AR
344}
345
346void
574b508c 347Adaptation::Ecap::XactionRep::vbMakeMore()
4d0854d4 348{
e1e90d26 349 Must(makingVb == opOn); // cannot make more if done proxying
ea76d91e 350 // we cannot guarantee more vb, but we can check that there is a chance
e1e90d26
AR
351 const BodyPipePointer &p = theVirginRep.raw().body_pipe;
352 Must(p != NULL && p->stillConsuming(this)); // we are plugged in
353 Must(!p->productionEnded() && p->mayNeedMoreData()); // and may get more
4d0854d4
AR
354}
355
8679e6c2 356libecap::Area
574b508c 357Adaptation::Ecap::XactionRep::vbContent(libecap::size_type o, libecap::size_type s)
4d0854d4 358{
e1e90d26 359 // We may not be makingVb yet. It should be OK, but see vbContentShift().
ea76d91e 360
8679e6c2 361 const BodyPipePointer &p = theVirginRep.raw().body_pipe;
ea76d91e
AR
362 Must(p != NULL);
363
364 // TODO: make MemBuf use size_t?
365 const size_t haveSize = static_cast<size_t>(p->buf().contentSize());
4d0854d4 366
8679e6c2
AR
367 // convert to Squid types; XXX: check for overflow
368 const uint64_t offset = static_cast<uint64_t>(o);
369 Must(offset <= haveSize); // equal iff at the end of content
370
371 // nsize means no size limit: all content starting from offset
372 const size_t size = s == libecap::nsize ?
26ac0430 373 haveSize - offset : static_cast<size_t>(s);
8679e6c2 374
8679e6c2
AR
375 // XXX: optimize by making theBody a shared_ptr (see Area::FromTemp*() src)
376 return libecap::Area::FromTempBuffer(p->buf().content() + offset,
26ac0430 377 min(static_cast<size_t>(haveSize - offset), size));
4d0854d4
AR
378}
379
380void
574b508c 381Adaptation::Ecap::XactionRep::vbContentShift(libecap::size_type n)
4d0854d4 382{
e1e90d26 383 // We may not be makingVb yet. It should be OK now, but if BodyPipe
ea76d91e
AR
384 // consume() requirements change, we would have to return empty vbContent
385 // until the adapter registers as a consumer
386
8679e6c2
AR
387 BodyPipePointer &p = theVirginRep.raw().body_pipe;
388 Must(p != NULL);
389 const size_t size = static_cast<size_t>(n); // XXX: check for overflow
390 const size_t haveSize = static_cast<size_t>(p->buf().contentSize()); // TODO: make MemBuf use size_t?
391 p->consume(min(size, haveSize));
4d0854d4
AR
392}
393
394void
574b508c 395Adaptation::Ecap::XactionRep::noteAbContentDone(bool atEnd)
4d0854d4 396{
7477a343
AR
397 Must(proxyingAb == opOn && !abProductionFinished);
398 abProductionFinished = true;
399 abProductionAtEnd = atEnd; // store until ready to stop producing ourselves
400 debugs(93,5, HERE << "adapted body production ended");
401 moveAbContent();
4d0854d4
AR
402}
403
8679e6c2 404void
574b508c 405Adaptation::Ecap::XactionRep::noteAbContentAvailable()
4d0854d4 406{
7477a343 407 Must(proxyingAb == opOn && !abProductionFinished);
8679e6c2 408 moveAbContent();
4d0854d4
AR
409}
410
ea76d91e 411#if 0 /* XXX: implement */
4d0854d4 412void
574b508c 413Adaptation::Ecap::XactionRep::setAdaptedBodySize(const libecap::BodySize &size)
4d0854d4
AR
414{
415 Must(answer().body_pipe != NULL);
8679e6c2
AR
416 if (size.known())
417 answer().body_pipe->setBodySize(size.value());
418 // else the piped body size is unknown by default
4d0854d4 419}
8679e6c2 420#endif
4d0854d4
AR
421
422void
574b508c 423Adaptation::Ecap::XactionRep::adaptationDelayed(const libecap::Delay &d)
4d0854d4
AR
424{
425 debugs(93,3, HERE << "adapter needs time: " <<
26ac0430 426 d.state << '/' << d.progress);
4d0854d4 427 // XXX: set timeout?
fdc96a39
AR
428}
429
26ac0430 430void
574b508c 431Adaptation::Ecap::XactionRep::adaptationAborted()
fdc96a39 432{
fdc96a39 433 tellQueryAborted(true); // should eCAP support retries?
ea76d91e 434 mustStop("adaptationAborted");
fdc96a39
AR
435}
436
8679e6c2 437bool
574b508c 438Adaptation::Ecap::XactionRep::callable() const
8679e6c2
AR
439{
440 return !done();
441}
442
26ac0430 443void
574b508c 444Adaptation::Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount<BodyPipe> bp)
fdc96a39 445{
ea76d91e
AR
446 Must(proxyingAb == opOn);
447 moveAbContent();
fdc96a39
AR
448}
449
26ac0430 450void
574b508c 451Adaptation::Ecap::XactionRep::noteBodyConsumerAborted(RefCount<BodyPipe> bp)
fdc96a39 452{
ea76d91e
AR
453 Must(proxyingAb == opOn);
454 stopProducingFor(answer().body_pipe, false);
455 Must(theMaster);
456 theMaster->abStopMaking();
457 proxyingAb = opComplete;
fdc96a39
AR
458}
459
460void
574b508c 461Adaptation::Ecap::XactionRep::noteMoreBodyDataAvailable(RefCount<BodyPipe> bp)
fdc96a39 462{
e1e90d26 463 Must(makingVb == opOn); // or we would not be registered as a consumer
fdc96a39 464 Must(theMaster);
8679e6c2 465 theMaster->noteVbContentAvailable();
fdc96a39
AR
466}
467
468void
574b508c 469Adaptation::Ecap::XactionRep::noteBodyProductionEnded(RefCount<BodyPipe> bp)
fdc96a39 470{
e1e90d26 471 Must(makingVb == opOn); // or we would not be registered as a consumer
fdc96a39 472 Must(theMaster);
8679e6c2 473 theMaster->noteVbContentDone(true);
e1e90d26 474 vbProductionFinished = true;
fdc96a39
AR
475}
476
477void
574b508c 478Adaptation::Ecap::XactionRep::noteBodyProducerAborted(RefCount<BodyPipe> bp)
fdc96a39 479{
e1e90d26 480 Must(makingVb == opOn); // or we would not be registered as a consumer
8679e6c2
AR
481 Must(theMaster);
482 theMaster->noteVbContentDone(false);
e1e90d26 483 vbProductionFinished = true;
fdc96a39
AR
484}
485
486void
574b508c 487Adaptation::Ecap::XactionRep::noteInitiatorAborted()
fdc96a39
AR
488{
489 mustStop("initiator aborted");
490}
491
8679e6c2
AR
492// get content from the adapter and put it into the adapted pipe
493void
574b508c 494Adaptation::Ecap::XactionRep::moveAbContent()
8679e6c2 495{
ea76d91e 496 Must(proxyingAb == opOn);
8679e6c2 497 const libecap::Area c = theMaster->abContent(0, libecap::nsize);
7477a343
AR
498 debugs(93,5, HERE << "up to " << c.size << " bytes");
499 if (c.size == 0 && abProductionFinished) { // no ab now and in the future
500 stopProducingFor(answer().body_pipe, abProductionAtEnd);
501 proxyingAb = opComplete;
502 debugs(93,5, HERE << "last adapted body data retrieved");
e1381638 503 } else if (c.size > 0) {
7477a343
AR
504 if (const size_t used = answer().body_pipe->putMoreData(c.start, c.size))
505 theMaster->abContentShift(used);
506 }
8679e6c2
AR
507}
508
509const char *
574b508c 510Adaptation::Ecap::XactionRep::status() const
fdc96a39 511{
4d0854d4
AR
512 static MemBuf buf;
513 buf.reset();
514
515 buf.append(" [", 2);
516
e1e90d26
AR
517 if (makingVb)
518 buf.Printf("M%d", static_cast<int>(makingVb));
519
520 const BodyPipePointer &vp = theVirginRep.raw().body_pipe;
521 if (!vp)
522 buf.append(" !V", 3);
523 else
524 if (vp->stillConsuming(const_cast<XactionRep*>(this)))
525 buf.append(" Vc", 3);
526 else
527 buf.append(" V?", 3);
528
529 if (vbProductionFinished)
530 buf.append(".", 1);
531
532
533 buf.Printf(" A%d", static_cast<int>(proxyingAb));
506a0530 534
ea76d91e
AR
535 if (proxyingAb == opOn) {
536 MessageRep *rep = dynamic_cast<MessageRep*>(theAnswerRep.get());
537 Must(rep);
f1a768b2 538 const BodyPipePointer &ap = rep->raw().body_pipe;
e1e90d26
AR
539 if (!ap)
540 buf.append(" !A", 3);
541 else if (ap->stillProducing(const_cast<XactionRep*>(this)))
542 buf.append(" Ap", 3);
543 else
544 buf.append(" A?", 3);
f1a768b2 545 }
4d0854d4 546
52ed047a 547 buf.Printf(" %s%u]", id.Prefix, id.value);
4d0854d4
AR
548
549 buf.terminate();
550
551 return buf.content();
fdc96a39 552}