]>
Commit | Line | Data |
---|---|---|
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 | 28 | CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Ecap::XactionRep, XactionRep); |
fdc96a39 | 29 | |
5038f9d8 AR |
30 | /// a libecap Visitor for converting adapter transaction options to HttpHeader |
31 | class OptionsExtractor: public libecap::NamedValueVisitor | |
32 | { | |
33 | public: | |
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 | 47 | Adaptation::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 | 64 | Adaptation::Ecap::XactionRep::~XactionRep() |
fdc96a39 AR |
65 | { |
66 | assert(!theMaster); | |
027320b4 | 67 | delete theCauseRep; |
4d0854d4 | 68 | theAnswerRep.reset(); |
fdc96a39 AR |
69 | } |
70 | ||
71 | void | |
574b508c | 72 | Adaptation::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 |
79 | Adaptation::Service & |
80 | Adaptation::Ecap::XactionRep::service() | |
81 | { | |
aee3523a | 82 | Must(theService != nullptr); |
a22e6cd3 AR |
83 | return *theService; |
84 | } | |
85 | ||
22fff3bf AR |
86 | const libecap::Area |
87 | Adaptation::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 | ||
102 | void | |
103 | Adaptation::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 | ||
121 | const libecap::Area | |
122 | Adaptation::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 | ||
146 | const libecap::Area | |
147 | Adaptation::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 | 164 | const libecap::Area |
df0bc0f4 | 165 | Adaptation::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 |
181 | const libecap::Area |
182 | Adaptation::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 | 204 | void |
71be37e0 CT |
205 | Adaptation::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 | 222 | void |
574b508c | 223 | Adaptation::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 | ||
254 | void | |
574b508c | 255 | Adaptation::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 |
284 | void |
285 | Adaptation::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(). | |
296 | void | |
297 | Adaptation::Ecap::XactionRep::doResume() | |
298 | { | |
299 | Must(theMaster); | |
300 | theMaster->resume(); | |
301 | } | |
302 | ||
fdc96a39 | 303 | libecap::Message & |
574b508c | 304 | Adaptation::Ecap::XactionRep::virgin() |
fdc96a39 | 305 | { |
7b67e5b6 | 306 | return theVirginRep; |
fdc96a39 AR |
307 | } |
308 | ||
4d0854d4 | 309 | const libecap::Message & |
574b508c | 310 | Adaptation::Ecap::XactionRep::cause() |
fdc96a39 | 311 | { |
aee3523a | 312 | Must(theCauseRep != nullptr); |
4d0854d4 | 313 | return *theCauseRep; |
fdc96a39 AR |
314 | } |
315 | ||
4d0854d4 | 316 | libecap::Message & |
574b508c | 317 | Adaptation::Ecap::XactionRep::adapted() |
fdc96a39 | 318 | { |
0c276d50 | 319 | Must(theAnswerRep); |
4d0854d4 AR |
320 | return *theAnswerRep; |
321 | } | |
322 | ||
323 | Adaptation::Message & | |
574b508c | 324 | Adaptation::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 | 331 | void |
574b508c | 332 | Adaptation::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 | 341 | bool |
574b508c | 342 | Adaptation::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 | 349 | void |
e1e90d26 | 350 | Adaptation::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 | |
363 | void | |
364 | Adaptation::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 | |
379 | void | |
380 | Adaptation::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 | 394 | void |
574b508c | 395 | Adaptation::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 | 412 | void |
574b508c | 413 | Adaptation::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 |
442 | void |
443 | Adaptation::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. | |
458 | void | |
63df1d28 | 459 | Adaptation::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 | 506 | void |
574b508c | 507 | Adaptation::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 | 515 | void |
574b508c | 516 | Adaptation::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 | 525 | void |
574b508c | 526 | Adaptation::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 | ||
534 | void | |
574b508c | 535 | Adaptation::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 | 544 | libecap::Area |
574b508c | 545 | Adaptation::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 | ||
568 | void | |
574b508c | 569 | Adaptation::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 | ||
582 | void | |
574b508c | 583 | Adaptation::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 | 592 | void |
574b508c | 593 | Adaptation::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 | 600 | void |
574b508c | 601 | Adaptation::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 | |
610 | void | |
574b508c | 611 | Adaptation::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 | 618 | void |
574b508c | 619 | Adaptation::Ecap::XactionRep::adaptationAborted() |
fdc96a39 | 620 | { |
fdc96a39 | 621 | tellQueryAborted(true); // should eCAP support retries? |
ea76d91e | 622 | mustStop("adaptationAborted"); |
fdc96a39 AR |
623 | } |
624 | ||
26ac0430 | 625 | void |
16d20c5d | 626 | Adaptation::Ecap::XactionRep::noteMoreBodySpaceAvailable(RefCount<BodyPipe>) |
fdc96a39 | 627 | { |
ea76d91e AR |
628 | Must(proxyingAb == opOn); |
629 | moveAbContent(); | |
fdc96a39 AR |
630 | } |
631 | ||
26ac0430 | 632 | void |
16d20c5d | 633 | Adaptation::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 | ||
642 | void | |
16d20c5d | 643 | Adaptation::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 | ||
650 | void | |
16d20c5d | 651 | Adaptation::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 | ||
659 | void | |
16d20c5d | 660 | Adaptation::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 | ||
668 | void | |
574b508c | 669 | Adaptation::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 |
675 | void | |
574b508c | 676 | Adaptation::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 | ||
691 | const char * | |
574b508c | 692 | Adaptation::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 | 734 | void |
63df1d28 | 735 | Adaptation::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 |