]> git.ipfire.org Git - thirdparty/squid.git/blame - src/clients/Client.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / clients / Client.cc
CommitLineData
cd304fc2 1/*
bf95c10a 2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
cd304fc2 3 *
bbc27441
AJ
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.
cd304fc2 7 */
8
582c2af2 9#include "squid.h"
70706149 10#include "acl/FilledChecklist.h"
3af10ac0 11#include "acl/Gadgets.h"
3d93a84d 12#include "base/TextException.h"
fccd4a86 13#include "clients/Client.h"
6b679a01
AJ
14#include "comm/Connection.h"
15#include "comm/forward.h"
ec41b64c 16#include "comm/Write.h"
83b053a0 17#include "error/Detail.h"
582c2af2 18#include "errorpage.h"
602d9612 19#include "fd.h"
5120a38b 20#include "HttpHdrContRange.h"
582c2af2
FC
21#include "HttpReply.h"
22#include "HttpRequest.h"
400760b3 23#include "SquidConfig.h"
582c2af2
FC
24#include "StatCounters.h"
25#include "Store.h"
4e540555 26#include "tools.h"
5f8252d2 27
a83c6ed6 28#if USE_ADAPTATION
62c7f90e 29#include "adaptation/AccessCheck.h"
1adcebc3 30#include "adaptation/Answer.h"
a22e6cd3 31#include "adaptation/Iterator.h"
0ad2b63b 32#include "base/AsyncCall.h"
0f283edf 33#endif
cd304fc2 34
c1520b67 35// implemented in client_side_reply.cc until sides have a common parent
82afb125 36void purgeEntriesByUrl(HttpRequest * req, const char *url);
c1520b67 37
f2272542
AJ
38Client::Client(FwdState *theFwdState) :
39 AsyncJob("Client"),
f53969cc 40 fwd(theFwdState),
f2272542 41 request(fwd->request)
cd304fc2 42{
cd304fc2 43 entry = fwd->entry;
fccd4a86 44 entry->lock("Client");
cd304fc2 45}
46
fccd4a86 47Client::~Client()
cd304fc2 48{
49918309
AR
49 // paranoid: check that swanSong has been called
50 assert(!requestBodySource);
51#if USE_ADAPTATION
52 assert(!virginBodyDestination);
53 assert(!adaptedBodySource);
54#endif
55
fccd4a86 56 entry->unlock("Client");
cd304fc2 57
585ab260 58 HTTPMSGUNLOCK(theVirginReply);
59 HTTPMSGUNLOCK(theFinalReply);
cd304fc2 60
aee3523a 61 if (responseBodyBuffer != nullptr) {
9e008dda 62 delete responseBodyBuffer;
aee3523a 63 responseBodyBuffer = nullptr;
49918309
AR
64 }
65}
66
67void
fccd4a86 68Client::swanSong()
49918309
AR
69{
70 // get rid of our piping obligations
aee3523a 71 if (requestBodySource != nullptr)
279152e7 72 stopConsumingFrom(requestBodySource);
5f8252d2 73
a83c6ed6
AR
74#if USE_ADAPTATION
75 cleanAdaptation();
5f8252d2 76#endif
7dc79973 77
70df76e3
AR
78 if (!doneWithServer())
79 closeServer();
80
81 if (!doneWithFwd) {
82 doneWithFwd = "swanSong()";
83 fwd->handleUnregisteredServerEnd();
84 }
85
49918309
AR
86 BodyConsumer::swanSong();
87#if USE_ADAPTATION
88 Initiator::swanSong();
89 BodyProducer::swanSong();
90#endif
b2c251cc
AJ
91
92 // paranoid: check that swanSong has been called
93 // extra paranoid: yeah, I really mean it. they MUST pass here.
94 assert(!requestBodySource);
95#if USE_ADAPTATION
96 assert(!virginBodyDestination);
97 assert(!adaptedBodySource);
98#endif
5f8252d2 99}
100
585ab260 101HttpReply *
fccd4a86 102Client::virginReply()
9e008dda 103{
585ab260 104 assert(theVirginReply);
105 return theVirginReply;
106}
107
108const HttpReply *
fccd4a86 109Client::virginReply() const
9e008dda 110{
585ab260 111 assert(theVirginReply);
112 return theVirginReply;
113}
114
115HttpReply *
fccd4a86 116Client::setVirginReply(HttpReply *rep)
9e008dda 117{
bf95c10a 118 debugs(11,5, this << " setting virgin reply to " << rep);
585ab260 119 assert(!theVirginReply);
120 assert(rep);
b248c2a3
AJ
121 theVirginReply = rep;
122 HTTPMSGLOCK(theVirginReply);
49f57088
EB
123 if (fwd->al)
124 fwd->al->reply = theVirginReply;
9e008dda 125 return theVirginReply;
585ab260 126}
127
128HttpReply *
fccd4a86 129Client::finalReply()
9e008dda 130{
585ab260 131 assert(theFinalReply);
132 return theFinalReply;
133}
134
135HttpReply *
fccd4a86 136Client::setFinalReply(HttpReply *rep)
9e008dda 137{
bf95c10a 138 debugs(11,5, this << " setting final reply to " << rep);
585ab260 139
140 assert(!theFinalReply);
141 assert(rep);
b248c2a3
AJ
142 theFinalReply = rep;
143 HTTPMSGLOCK(theFinalReply);
49f57088
EB
144 if (fwd->al)
145 fwd->al->reply = theFinalReply;
585ab260 146
3756e5c0
AR
147 // give entry the reply because haveParsedReplyHeaders() expects it there
148 entry->replaceHttpReply(theFinalReply, false); // but do not write yet
149 haveParsedReplyHeaders(); // update the entry/reply (e.g., set timestamps)
b481492b 150 if (!EBIT_TEST(entry->flags, RELEASE_REQUEST) && blockCaching())
70706149 151 entry->release();
3756e5c0 152 entry->startWriting(); // write the updated entry to store
585ab260 153
154 return theFinalReply;
155}
156
ba3fe8d9
EB
157void
158Client::markParsedVirginReplyAsWhole(const char *reasonWeAreSure)
159{
160 assert(reasonWeAreSure);
161 debugs(11, 3, reasonWeAreSure);
162
163 // The code storing adapted reply takes care of markStoredReplyAsWhole().
164 // We need to take care of the remaining regular network-to-store case.
165#if USE_ADAPTATION
166 if (startedAdaptation) {
167 debugs(11, 5, "adaptation handles markStoredReplyAsWhole()");
168 return;
169 }
170#endif
171
172 // Convert the "parsed whole virgin reply" event into the "stored..." event
173 // because, without adaptation, we store everything we parse: There is no
174 // buffer for parsed content; addVirginReplyBody() stores every parsed byte.
175 fwd->markStoredReplyAsWhole(reasonWeAreSure);
176}
177
5f8252d2 178// called when no more server communication is expected; may quit
179void
fccd4a86 180Client::serverComplete()
5f8252d2 181{
bf95c10a 182 debugs(11,5, "serverComplete " << this);
5f8252d2 183
184 if (!doneWithServer()) {
185 closeServer();
186 assert(doneWithServer());
187 }
188
7dc79973 189 completed = true;
d603e3c2 190 originalRequest()->hier.stopPeerClock(true);
e1381638 191
aee3523a 192 if (requestBodySource != nullptr)
5f8252d2 193 stopConsumingFrom(requestBodySource);
194
aee3523a 195 if (responseBodyBuffer != nullptr)
9e008dda 196 return;
7dc79973 197
198 serverComplete2();
199}
200
201void
fccd4a86 202Client::serverComplete2()
7dc79973 203{
bf95c10a 204 debugs(11,5, "serverComplete2 " << this);
7dc79973 205
a83c6ed6 206#if USE_ADAPTATION
aee3523a 207 if (virginBodyDestination != nullptr)
5f8252d2 208 stopProducingFor(virginBodyDestination, true);
209
a83c6ed6 210 if (!doneWithAdaptation())
5f8252d2 211 return;
212#endif
213
214 completeForwarding();
5f8252d2 215}
216
fccd4a86 217bool Client::doneAll() const
9e008dda 218{
79628299 219 return doneWithServer() &&
a83c6ed6 220#if USE_ADAPTATION
b692311b
A
221 doneWithAdaptation() &&
222 Adaptation::Initiator::doneAll() &&
223 BodyProducer::doneAll() &&
5f8252d2 224#endif
b692311b 225 BodyConsumer::doneAll();
5f8252d2 226}
227
228// FTP side overloads this to work around multiple calls to fwd->complete
229void
fccd4a86 230Client::completeForwarding()
9e008dda 231{
bf95c10a 232 debugs(11,5, "completing forwarding for " << fwd);
aee3523a 233 assert(fwd != nullptr);
70df76e3 234 doneWithFwd = "completeForwarding()";
5f8252d2 235 fwd->complete();
236}
237
123ec4de 238// Register to receive request body
fccd4a86 239bool Client::startRequestBodyFlow()
123ec4de 240{
d603e3c2 241 HttpRequestPointer r(originalRequest());
aee3523a 242 assert(r->body_pipe != nullptr);
123ec4de 243 requestBodySource = r->body_pipe;
244 if (requestBodySource->setConsumerIfNotLate(this)) {
bf95c10a 245 debugs(11,3, "expecting request body from " <<
9e008dda 246 requestBodySource->status());
123ec4de 247 return true;
248 }
249
bf95c10a 250 debugs(11,3, "aborting on partially consumed request body: " <<
9e008dda 251 requestBodySource->status());
aee3523a 252 requestBodySource = nullptr;
123ec4de 253 return false;
254}
255
5f8252d2 256// Entry-dependent callbacks use this check to quit if the entry went bad
257bool
fccd4a86 258Client::abortOnBadEntry(const char *abortReason)
5f8252d2 259{
260 if (entry->isAccepting())
261 return false;
262
bf95c10a 263 debugs(11,5, "entry is not Accepting!");
92cfc72f 264 abortOnData(abortReason);
5f8252d2 265 return true;
266}
267
268// more request or adapted response body is available
269void
fccd4a86 270Client::noteMoreBodyDataAvailable(BodyPipe::Pointer bp)
5f8252d2 271{
a83c6ed6 272#if USE_ADAPTATION
dc56a9b1 273 if (adaptedBodySource == bp) {
5f8252d2 274 handleMoreAdaptedBodyAvailable();
275 return;
276 }
277#endif
0ad2b63b
CT
278 if (requestBodySource == bp)
279 handleMoreRequestBodyAvailable();
5f8252d2 280}
281
282// the entire request or adapted response body was provided, successfully
283void
fccd4a86 284Client::noteBodyProductionEnded(BodyPipe::Pointer bp)
5f8252d2 285{
a83c6ed6 286#if USE_ADAPTATION
dc56a9b1 287 if (adaptedBodySource == bp) {
5f8252d2 288 handleAdaptedBodyProductionEnded();
289 return;
c99de607 290 }
cd304fc2 291#endif
0ad2b63b
CT
292 if (requestBodySource == bp)
293 handleRequestBodyProductionEnded();
5f8252d2 294}
295
296// premature end of the request or adapted response body production
297void
fccd4a86 298Client::noteBodyProducerAborted(BodyPipe::Pointer bp)
5f8252d2 299{
a83c6ed6 300#if USE_ADAPTATION
dc56a9b1 301 if (adaptedBodySource == bp) {
5f8252d2 302 handleAdaptedBodyProducerAborted();
303 return;
304 }
305#endif
0ad2b63b
CT
306 if (requestBodySource == bp)
307 handleRequestBodyProducerAborted();
5f8252d2 308}
309
92cfc72f
CT
310bool
311Client::abortOnData(const char *reason)
312{
313 abortAll(reason);
314 return true;
315}
316
5f8252d2 317// more origin request body data is available
318void
fccd4a86 319Client::handleMoreRequestBodyAvailable()
5f8252d2 320{
321 if (!requestSender)
322 sendMoreRequestBody();
323 else
bf95c10a 324 debugs(9,3, "waiting for request body write to complete");
5f8252d2 325}
326
327// there will be no more handleMoreRequestBodyAvailable calls
328void
fccd4a86 329Client::handleRequestBodyProductionEnded()
5f8252d2 330{
39cb8c41 331 receivedWholeRequestBody = true;
5f8252d2 332 if (!requestSender)
333 doneSendingRequestBody();
334 else
bf95c10a 335 debugs(9,3, "waiting for request body write to complete");
5f8252d2 336}
337
338// called when we are done sending request body; kids extend this
339void
fccd4a86 340Client::doneSendingRequestBody()
9e008dda 341{
bf95c10a 342 debugs(9,3, "done sending request body");
aee3523a 343 assert(requestBodySource != nullptr);
5f8252d2 344 stopConsumingFrom(requestBodySource);
345
346 // kids extend this
347}
348
349// called when body producers aborts; kids extend this
350void
fccd4a86 351Client::handleRequestBodyProducerAborted()
5f8252d2 352{
aee3523a 353 if (requestSender != nullptr)
bf95c10a 354 debugs(9,3, "fyi: request body aborted while we were sending");
5f8252d2 355
0919c51e 356 fwd->dontRetry(true); // the problem is not with the server
5f8252d2 357 stopConsumingFrom(requestBodySource); // requestSender, if any, will notice
358
359 // kids extend this
360}
361
5f8252d2 362// called when we wrote request headers(!) or a part of the body
363void
fccd4a86 364Client::sentRequestBody(const CommIoCbParams &io)
5f8252d2 365{
dc56a9b1 366 debugs(11, 5, "sentRequestBody: FD " << io.fd << ": size " << io.size << ": errflag " << io.flag << ".");
bf95c10a 367 debugs(32,3, "sentRequestBody called");
5f8252d2 368
aee3523a 369 requestSender = nullptr;
5f8252d2 370
dc56a9b1 371 if (io.size > 0) {
372 fd_bytes(io.fd, io.size, FD_WRITE);
a0864754 373 statCounter.server.all.kbytes_out += io.size;
5f8252d2 374 // kids should increment their counters
375 }
376
c8407295 377 if (io.flag == Comm::ERR_CLOSING)
5f8252d2 378 return;
379
380 if (!requestBodySource) {
bf95c10a 381 debugs(9,3, "detected while-we-were-sending abort");
5f8252d2 382 return; // do nothing;
383 }
384
d8165775
AR
385 // both successful and failed writes affect response times
386 request->hier.notePeerWrite();
387
dc56a9b1 388 if (io.flag) {
d816f28d 389 debugs(11, DBG_IMPORTANT, "ERROR: sentRequestBody failure: FD " << io.fd << ": " << xstrerr(io.xerrno));
5f8252d2 390 ErrorState *err;
7e6eabbc 391 err = new ErrorState(ERR_WRITE_ERROR, Http::scBadGateway, fwd->request, fwd->al);
f5f9e44c 392 err->xerrno = io.xerrno;
5f8252d2 393 fwd->fail(err);
92cfc72f 394 abortOnData("I/O error while sending request body");
5f8252d2 395 return;
396 }
397
398 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
92cfc72f 399 abortOnData("store entry aborted while sending request body");
5f8252d2 400 return;
401 }
402
39cb8c41
AR
403 if (!requestBodySource->exhausted())
404 sendMoreRequestBody();
de48b288 405 else if (receivedWholeRequestBody)
5f8252d2 406 doneSendingRequestBody();
407 else
bf95c10a 408 debugs(9,3, "waiting for body production end or abort");
cd304fc2 409}
410
5f8252d2 411void
fccd4a86 412Client::sendMoreRequestBody()
5f8252d2 413{
aee3523a 414 assert(requestBodySource != nullptr);
5f8252d2 415 assert(!requestSender);
a0297974 416
e83cc785 417 const Comm::ConnectionPointer conn = dataConnection();
a0297974 418
6b679a01 419 if (!Comm::IsConnOpen(conn)) {
bf95c10a 420 debugs(9,3, "cannot send request body to closing " << conn);
a0297974
AR
421 return; // wait for the kid's close handler; TODO: assert(closer);
422 }
423
5f8252d2 424 MemBuf buf;
39cb8c41 425 if (getMoreRequestBody(buf) && buf.contentSize() > 0) {
bf95c10a 426 debugs(9,3, "will write " << buf.contentSize() << " request body bytes");
fccd4a86
AJ
427 typedef CommCbMemFunT<Client, CommIoCbParams> Dialer;
428 requestSender = JobCallback(93,3, Dialer, this, Client::sentRequestBody);
b0388924 429 Comm::Write(conn, &buf, requestSender);
5f8252d2 430 } else {
bf95c10a 431 debugs(9,3, "will wait for more request body bytes or eof");
aee3523a 432 requestSender = nullptr;
5f8252d2 433 }
434}
435
39cb8c41
AR
436/// either fill buf with available [encoded] request body bytes or return false
437bool
fccd4a86 438Client::getMoreRequestBody(MemBuf &buf)
39cb8c41
AR
439{
440 // default implementation does not encode request body content
aee3523a 441 Must(requestBodySource != nullptr);
39cb8c41
AR
442 return requestBodySource->getMoreData(buf);
443}
444
c1520b67
AJ
445// Compares hosts in urls, returns false if different, no sheme, or no host.
446static bool
447sameUrlHosts(const char *url1, const char *url2)
448{
1ac1d4d3 449 // XXX: Want AnyP::Uri::parse() here, but it uses static storage and copying
c1520b67
AJ
450 const char *host1 = strchr(url1, ':');
451 const char *host2 = strchr(url2, ':');
452
453 if (host1 && host2) {
454 // skip scheme slashes
455 do {
456 ++host1;
457 ++host2;
458 } while (*host1 == '/' && *host2 == '/');
459
460 if (!*host1)
461 return false; // no host
462
463 // increment while the same until we reach the end of the URL/host
464 while (*host1 && *host1 != '/' && *host1 == *host2) {
465 ++host1;
466 ++host2;
467 }
468 return *host1 == *host2;
469 }
470
471 return false; // no URL scheme
472}
473
474// purges entries that match the value of a given HTTP [response] header
475static void
63df1d28 476purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, Http::Message *rep, Http::HdrType hdr)
c1520b67 477{
614bd511
AJ
478 const auto hdrUrl = rep->header.getStr(hdr);
479 if (!hdrUrl)
71051277 480 return;
9e008dda 481
71051277
BR
482 /*
483 * If the URL is relative, make it absolute so we can find it.
484 * If it's absolute, make sure the host parts match to avoid DOS attacks
485 * as per RFC 2616 13.10.
486 */
614bd511
AJ
487 SBuf absUrlMaker;
488 const char *absUrl = nullptr;
71051277 489 if (urlIsRelative(hdrUrl)) {
614bd511
AJ
490 if (req->method.id() == Http::METHOD_CONNECT)
491 absUrl = hdrUrl; // TODO: merge authority-uri and hdrUrl
492 else if (req->url.getScheme() == AnyP::PROTO_URN)
493 absUrl = req->url.absolute().c_str();
494 else {
495 AnyP::Uri tmpUrl = req->url;
496 if (*hdrUrl == '/') {
497 // RFC 3986 section 4.2: absolute-path reference
498 // for this logic replace the entire request-target URI path
499 tmpUrl.path(hdrUrl);
500 } else {
501 tmpUrl.addRelativePath(reqUrl);
502 }
503 absUrlMaker = tmpUrl.absolute();
504 absUrl = absUrlMaker.c_str();
3cbbd242 505 }
71051277
BR
506 } else if (!sameUrlHosts(reqUrl, hdrUrl)) {
507 return;
614bd511
AJ
508 } else
509 absUrl = hdrUrl;
9e008dda 510
614bd511 511 purgeEntriesByUrl(req, absUrl);
c1520b67
AJ
512}
513
514// some HTTP methods should purge matching cache entries
515void
fccd4a86 516Client::maybePurgeOthers()
c1520b67 517{
9e008dda
AJ
518 // only some HTTP methods should purge matching cache entries
519 if (!request->method.purgesOthers())
520 return;
c1520b67 521
9e008dda 522 // and probably only if the response was successful
9b769c67 523 if (theFinalReply->sline.status() >= 400)
9e008dda 524 return;
c1520b67 525
9e008dda 526 // XXX: should we use originalRequest() here?
851feda6
AJ
527 SBuf tmp(request->effectiveRequestUri());
528 const char *reqUrl = tmp.c_str();
529 debugs(88, 5, "maybe purging due to " << request->method << ' ' << tmp);
d603e3c2
AJ
530 purgeEntriesByUrl(request.getRaw(), reqUrl);
531 purgeEntriesByHeader(request.getRaw(), reqUrl, theFinalReply, Http::HdrType::LOCATION);
532 purgeEntriesByHeader(request.getRaw(), reqUrl, theFinalReply, Http::HdrType::CONTENT_LOCATION);
c1520b67
AJ
533}
534
fad1a21e 535/// called when we have final (possibly adapted) reply headers; kids extend
5f8252d2 536void
fccd4a86 537Client::haveParsedReplyHeaders()
5f8252d2 538{
9e008dda
AJ
539 Must(theFinalReply);
540 maybePurgeOthers();
5120a38b
AR
541
542 // adaptation may overwrite old offset computed using the virgin response
6c9c44d0
AR
543 currentOffset = 0;
544 if (const auto cr = theFinalReply->contentRange()) {
545 if (cr->spec.offset != HttpHdrRangeSpec::UnknownPosition)
546 currentOffset = cr->spec.offset;
547 }
5f8252d2 548}
549
70706149
AR
550/// whether to prevent caching of an otherwise cachable response
551bool
fccd4a86 552Client::blockCaching()
70706149
AR
553{
554 if (const Acl::Tree *acl = Config.accessList.storeMiss) {
555 // This relatively expensive check is not in StoreEntry::checkCachable:
556 // That method lacks HttpRequest and may be called too many times.
d603e3c2 557 ACLFilledChecklist ch(acl, originalRequest().getRaw());
66d51f4f 558 ch.reply = const_cast<HttpReply*>(&entry->mem().freshestReply()); // ACLFilledChecklist API bug
70706149 559 HTTPMSGLOCK(ch.reply);
49f57088 560 ch.al = fwd->al;
06bf5384 561 if (!ch.fastCheck().allowed()) { // when in doubt, block
70706149
AR
562 debugs(20, 3, "store_miss prohibits caching");
563 return true;
564 }
565 }
566 return false;
5f8252d2 567}
568
d603e3c2 569HttpRequestPointer
fccd4a86 570Client::originalRequest()
7dc79973 571{
572 return request;
573}
5f8252d2 574
a83c6ed6 575#if USE_ADAPTATION
a22e6cd3
AR
576/// Initiate an asynchronous adaptation transaction which will call us back.
577void
fccd4a86 578Client::startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause)
cd304fc2 579{
fccd4a86 580 debugs(11, 5, "Client::startAdaptation() called");
5f8252d2 581 // check whether we should be sending a body as well
5f8252d2 582 // start body pipe to feed ICAP transaction if needed
585ab260 583 assert(!virginBodyDestination);
9e008dda 584 HttpReply *vrep = virginReply();
585ab260 585 assert(!vrep->body_pipe);
47f6e231 586 int64_t size = 0;
585ab260 587 if (vrep->expectingBody(cause->method, size) && size) {
5f8252d2 588 virginBodyDestination = new BodyPipe(this);
585ab260 589 vrep->body_pipe = virginBodyDestination;
bf95c10a 590 debugs(93, 6, "will send virgin reply body to " <<
9e008dda 591 virginBodyDestination << "; size: " << size);
c2eef5bd 592 if (size > 0)
593 virginBodyDestination->setBodySize(size);
5f8252d2 594 }
595
a22e6cd3 596 adaptedHeadSource = initiateAdaptation(
af0ded40 597 new Adaptation::Iterator(vrep, cause, fwd->al, group));
4299f876 598 startedAdaptation = initiated(adaptedHeadSource);
a22e6cd3 599 Must(startedAdaptation);
cd304fc2 600}
0c25e715 601
5f8252d2 602// properly cleans up ICAP-related state
603// may be called multiple times
fccd4a86 604void Client::cleanAdaptation()
9e008dda 605{
bf95c10a 606 debugs(11,5, "cleaning ICAP; ACL: " << adaptationAccessCheckPending);
5f8252d2 607
aee3523a 608 if (virginBodyDestination != nullptr)
5f8252d2 609 stopProducingFor(virginBodyDestination, false);
610
0f283edf 611 announceInitiatorAbort(adaptedHeadSource);
5f8252d2 612
aee3523a 613 if (adaptedBodySource != nullptr)
5f8252d2 614 stopConsumingFrom(adaptedBodySource);
615
a83c6ed6
AR
616 if (!adaptationAccessCheckPending) // we cannot cancel a pending callback
617 assert(doneWithAdaptation()); // make sure the two methods are in sync
5f8252d2 618}
619
620bool
fccd4a86 621Client::doneWithAdaptation() const
9e008dda 622{
a83c6ed6 623 return !adaptationAccessCheckPending &&
9e008dda 624 !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource;
5f8252d2 625}
626
bc81cb2b 627// sends virgin reply body to ICAP, buffering excesses if needed
628void
fccd4a86 629Client::adaptVirginReplyBody(const char *data, ssize_t len)
bc81cb2b 630{
a83c6ed6 631 assert(startedAdaptation);
bc81cb2b 632
633 if (!virginBodyDestination) {
bf95c10a 634 debugs(11,3, "ICAP does not want more virgin body");
bc81cb2b 635 return;
636 }
637
638 // grow overflow area if already overflowed
639 if (responseBodyBuffer) {
640 responseBodyBuffer->append(data, len);
641 data = responseBodyBuffer->content();
642 len = responseBodyBuffer->contentSize();
643 }
644
645 const ssize_t putSize = virginBodyDestination->putMoreData(data, len);
646 data += putSize;
647 len -= putSize;
648
649 // if we had overflow area, shrink it as necessary
650 if (responseBodyBuffer) {
651 if (putSize == responseBodyBuffer->contentSize()) {
652 delete responseBodyBuffer;
aee3523a 653 responseBodyBuffer = nullptr;
bc81cb2b 654 } else {
655 responseBodyBuffer->consume(putSize);
9e008dda 656 }
bc81cb2b 657 return;
658 }
659
660 // if we did not have an overflow area, create it as needed
661 if (len > 0) {
662 assert(!responseBodyBuffer);
663 responseBodyBuffer = new MemBuf;
664 responseBodyBuffer->init(4096, SQUID_TCP_SO_RCVBUF * 10);
665 responseBodyBuffer->append(data, len);
666 }
667}
668
5f8252d2 669// can supply more virgin response body data
670void
fccd4a86 671Client::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
5f8252d2 672{
7dc79973 673 if (responseBodyBuffer) {
aee3523a 674 addVirginReplyBody(nullptr, 0); // kick the buffered fragment alive again
bc81cb2b 675 if (completed && !responseBodyBuffer) {
676 serverComplete2();
677 return;
678 }
7dc79973 679 }
5f8252d2 680 maybeReadVirginBody();
681}
682
bc81cb2b 683// the consumer of our virgin response body aborted
5f8252d2 684void
fccd4a86 685Client::noteBodyConsumerAborted(BodyPipe::Pointer)
5f8252d2 686{
687 stopProducingFor(virginBodyDestination, false);
bc81cb2b 688
a83c6ed6 689 // do not force closeServer here in case we need to bypass AdaptationQueryAbort
bc81cb2b 690
a83c6ed6
AR
691 if (doneWithAdaptation()) // we may still be receiving adapted response
692 handleAdaptationCompleted();
5f8252d2 693}
694
695// received adapted response headers (body may follow)
696void
fccd4a86 697Client::noteAdaptationAnswer(const Adaptation::Answer &answer)
5f8252d2 698{
a83c6ed6 699 clearAdaptation(adaptedHeadSource); // we do not expect more messages
5f8252d2 700
3af10ac0
AR
701 switch (answer.kind) {
702 case Adaptation::Answer::akForward:
63df1d28 703 handleAdaptedHeader(const_cast<Http::Message*>(answer.message.getRaw()));
3af10ac0
AR
704 break;
705
706 case Adaptation::Answer::akBlock:
707 handleAdaptationBlocked(answer);
708 break;
709
710 case Adaptation::Answer::akError:
711 handleAdaptationAborted(!answer.final);
712 break;
713 }
714}
715
716void
63df1d28 717Client::handleAdaptedHeader(Http::Message *msg)
3af10ac0 718{
1733bbba
CT
719 if (abortOnBadEntry("entry went bad while waiting for adapted headers")) {
720 // If the adapted response has a body, the ICAP side needs to know
721 // that nobody will consume that body. We will be destroyed upon
722 // return. Tell the ICAP side that it is on its own.
723 HttpReply *rep = dynamic_cast<HttpReply*>(msg);
724 assert(rep);
aee3523a 725 if (rep->body_pipe != nullptr)
1733bbba
CT
726 rep->body_pipe->expectNoConsumption();
727
5f8252d2 728 return;
1733bbba 729 }
5f8252d2 730
585ab260 731 HttpReply *rep = dynamic_cast<HttpReply*>(msg);
5f8252d2 732 assert(rep);
bf95c10a 733 debugs(11,5, this << " setting adapted reply to " << rep);
585ab260 734 setFinalReply(rep);
5f8252d2 735
736 assert(!adaptedBodySource);
aee3523a 737 if (rep->body_pipe != nullptr) {
5f8252d2 738 // subscribe to receive adapted body
585ab260 739 adaptedBodySource = rep->body_pipe;
5f8252d2 740 // assume that ICAP does not auto-consume on failures
dfb6dc9d
NH
741 const bool result = adaptedBodySource->setConsumerIfNotLate(this);
742 assert(result);
5f8252d2 743 } else {
744 // no body
ba3fe8d9 745 fwd->markStoredReplyAsWhole("setFinalReply() stored header-only adapted reply");
a83c6ed6
AR
746 if (doneWithAdaptation()) // we may still be sending virgin response
747 handleAdaptationCompleted();
5f8252d2 748 }
5f8252d2 749}
750
5f8252d2 751void
fccd4a86 752Client::resumeBodyStorage()
5f8252d2 753{
0ad2b63b
CT
754 if (abortOnBadEntry("store entry aborted while kick producer callback"))
755 return;
5f8252d2 756
e83cdc25 757 if (!adaptedBodySource)
0ad2b63b
CT
758 return;
759
760 handleMoreAdaptedBodyAvailable();
761
aee3523a 762 if (adaptedBodySource != nullptr && adaptedBodySource->exhausted())
0ad2b63b
CT
763 endAdaptedBodyConsumption();
764}
5f8252d2 765
0ad2b63b
CT
766// more adapted response body is available
767void
fccd4a86 768Client::handleMoreAdaptedBodyAvailable()
0ad2b63b 769{
5f8252d2 770 if (abortOnBadEntry("entry refuses adapted body"))
771 return;
772
773 assert(entry);
0ad2b63b
CT
774
775 size_t contentSize = adaptedBodySource->buf().contentSize();
0ad2b63b
CT
776
777 if (!contentSize)
778 return; // XXX: bytesWanted asserts on zero-size ranges
779
384a7590 780 const size_t spaceAvailable = entry->bytesWanted(Range<size_t>(0, contentSize), true);
0ad2b63b 781
e83cdc25 782 if (spaceAvailable < contentSize ) {
0ad2b63b 783 // No or partial body data consuming
fccd4a86
AJ
784 typedef NullaryMemFunT<Client> Dialer;
785 AsyncCall::Pointer call = asyncCall(93, 5, "Client::resumeBodyStorage",
786 Dialer(this, &Client::resumeBodyStorage));
0ad2b63b
CT
787 entry->deferProducer(call);
788 }
789
4dc2b072 790 if (!spaceAvailable) {
bf95c10a 791 debugs(11, 5, "NOT storing " << contentSize << " bytes of adapted " <<
0ad2b63b
CT
792 "response body at offset " << adaptedBodySource->consumedSize());
793 return;
794 }
e83cdc25 795
0ad2b63b 796 if (spaceAvailable < contentSize ) {
bf95c10a 797 debugs(11, 5, "postponing storage of " <<
0ad2b63b
CT
798 (contentSize - spaceAvailable) << " body bytes");
799 contentSize = spaceAvailable;
0ad2b63b 800 }
e83cdc25 801
bf95c10a 802 debugs(11,5, "storing " << contentSize << " bytes of adapted " <<
0ad2b63b 803 "response body at offset " << adaptedBodySource->consumedSize());
e83cdc25 804
5f8252d2 805 BodyPipeCheckout bpc(*adaptedBodySource);
0ad2b63b
CT
806 const StoreIOBuffer ioBuf(&bpc.buf, currentOffset, contentSize);
807 currentOffset += ioBuf.length;
5f8252d2 808 entry->write(ioBuf);
809 bpc.buf.consume(contentSize);
810 bpc.checkIn();
811}
812
813// the entire adapted response body was produced, successfully
814void
fccd4a86 815Client::handleAdaptedBodyProductionEnded()
5f8252d2 816{
5f8252d2 817 if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
818 return;
e83cdc25 819
ba3fe8d9
EB
820 // distinguish this code path from handleAdaptedBodyProducerAborted()
821 receivedWholeAdaptedReply = true;
822
e83cdc25 823 // end consumption if we consumed everything
aee3523a 824 if (adaptedBodySource != nullptr && adaptedBodySource->exhausted())
0ad2b63b
CT
825 endAdaptedBodyConsumption();
826 // else resumeBodyStorage() will eventually consume the rest
827}
5f8252d2 828
0ad2b63b 829void
fccd4a86 830Client::endAdaptedBodyConsumption()
0ad2b63b
CT
831{
832 stopConsumingFrom(adaptedBodySource);
ba3fe8d9
EB
833
834 if (receivedWholeAdaptedReply) {
835 // We received the entire adapted reply per receivedWholeAdaptedReply.
836 // We are called when we consumed everything received (per our callers).
837 // We consume only what we store per handleMoreAdaptedBodyAvailable().
838 fwd->markStoredReplyAsWhole("received,consumed=>stored the entire RESPMOD reply");
839 }
840
a83c6ed6 841 handleAdaptationCompleted();
5f8252d2 842}
843
844// premature end of the adapted response body
fccd4a86 845void Client::handleAdaptedBodyProducerAborted()
5f8252d2 846{
7224ca5a
AR
847 if (abortOnBadEntry("entry went bad while waiting for the now-aborted adapted body"))
848 return;
849
850 Must(adaptedBodySource != nullptr);
851 if (!adaptedBodySource->exhausted()) {
852 debugs(11,5, "waiting to consume the remainder of the aborted adapted body");
853 return; // resumeBodyStorage() should eventually consume the rest
854 }
855
5f8252d2 856 stopConsumingFrom(adaptedBodySource);
7224ca5a
AR
857
858 if (handledEarlyAdaptationAbort())
859 return;
860
7224ca5a 861 handleAdaptationCompleted(); // the user should get a truncated response
5f8252d2 862}
863
a83c6ed6 864// common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
5f8252d2 865void
fccd4a86 866Client::handleAdaptationCompleted()
5f8252d2 867{
bf95c10a 868 debugs(11,5, "handleAdaptationCompleted");
a83c6ed6 869 cleanAdaptation();
bc81cb2b 870
719c885c 871 // We stop reading origin response because we have no place to put it(*) and
bc81cb2b 872 // cannot use it. If some origin servers do not like that or if we want to
873 // reuse more pconns, we can add code to discard unneeded origin responses.
719c885c
AR
874 // (*) TODO: Is it possible that the adaptation xaction is still running?
875 if (mayReadVirginReplyBody()) {
bf95c10a 876 debugs(11,3, "closing origin conn due to ICAP completion");
bc81cb2b 877 closeServer();
878 }
879
5f8252d2 880 completeForwarding();
5f8252d2 881}
882
a83c6ed6 883// common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
5f8252d2 884void
fccd4a86 885Client::handleAdaptationAborted(bool bypassable)
5f8252d2 886{
bf95c10a 887 debugs(11,5, "handleAdaptationAborted; bypassable: " << bypassable <<
9e008dda 888 ", entry empty: " << entry->isEmpty());
5f8252d2 889
890 if (abortOnBadEntry("entry went bad while ICAP aborted"))
891 return;
892
0f283edf 893 // TODO: bypass if possible
7224ca5a 894 if (!handledEarlyAdaptationAbort())
92cfc72f 895 abortAll("adaptation failure with a filled entry");
7224ca5a 896}
0f283edf 897
7224ca5a
AR
898/// If the store entry is still empty, fully handles adaptation abort, returning
899/// true. Otherwise just updates the request error detail and returns false.
900bool
901Client::handledEarlyAdaptationAbort()
902{
5f8252d2 903 if (entry->isEmpty()) {
7224ca5a 904 debugs(11,8, "adaptation failure with an empty entry: " << *entry);
7e6eabbc 905 const auto err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, request.getRaw(), fwd->al);
83b053a0
CT
906 static const auto d = MakeNamedErrorDetail("ICAP_RESPMOD_EARLY");
907 err->detailError(d);
5f8252d2 908 fwd->fail(err);
909 fwd->dontRetry(true);
92cfc72f 910 abortAll("adaptation failure with an empty entry");
7224ca5a 911 return true; // handled
64b66b76 912 }
5f8252d2 913
83b053a0
CT
914 if (request) { // update logged info directly
915 static const auto d = MakeNamedErrorDetail("ICAP_RESPMOD_LATE");
916 request->detailError(ERR_ICAP_FAILURE, d);
917 }
7224ca5a
AR
918
919 return false; // the caller must handle
5f8252d2 920}
921
3af10ac0
AR
922// adaptation service wants us to deny HTTP client access to this response
923void
fccd4a86 924Client::handleAdaptationBlocked(const Adaptation::Answer &answer)
3af10ac0 925{
bf95c10a 926 debugs(11,5, answer.ruleId);
3af10ac0
AR
927
928 if (abortOnBadEntry("entry went bad while ICAP aborted"))
929 return;
930
931 if (!entry->isEmpty()) { // too late to block (should not really happen)
83b053a0
CT
932 if (request) {
933 static const auto d = MakeNamedErrorDetail("RESPMOD_BLOCK_LATE");
934 request->detailError(ERR_ICAP_FAILURE, d);
935 }
92cfc72f 936 abortAll("late adaptation block");
ec4d1a1d 937 return;
3af10ac0 938 }
ec4d1a1d 939
bf95c10a 940 debugs(11,7, "creating adaptation block response");
3af10ac0
AR
941
942 err_type page_id =
943 aclGetDenyInfoPage(&Config.denyInfoList, answer.ruleId.termedBuf(), 1);
944 if (page_id == ERR_NONE)
945 page_id = ERR_ACCESS_DENIED;
946
7e6eabbc 947 const auto err = new ErrorState(page_id, Http::scForbidden, request.getRaw(), fwd->al);
83b053a0
CT
948 static const auto d = MakeNamedErrorDetail("RESPMOD_BLOCK_EARLY");
949 err->detailError(d);
3af10ac0
AR
950 fwd->fail(err);
951 fwd->dontRetry(true);
952
92cfc72f 953 abortOnData("timely adaptation block");
3af10ac0
AR
954}
955
7c4e4e7f 956void
fccd4a86 957Client::noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer group)
7c4e4e7f 958{
a83c6ed6 959 adaptationAccessCheckPending = false;
7c4e4e7f 960
961 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
962 return;
963
d5430dc8
AJ
964 // TODO: Should non-ICAP and ICAP REPMOD pre-cache paths check this?
965 // That check now only happens on REQMOD pre-cache and REPMOD post-cache, in processReplyAccess().
47416555 966 if (virginReply()->expectedBodyTooLarge(*request)) {
967 sendBodyIsTooLargeError();
968 return;
969 }
d5430dc8 970 // TODO: Should we check receivedBodyTooLarge as well?
47416555 971
a22e6cd3 972 if (!group) {
bf95c10a 973 debugs(11,3, "no adapation needed");
585ab260 974 setFinalReply(virginReply());
7c4e4e7f 975 processReplyBody();
7c4e4e7f 976 return;
977 }
978
d603e3c2 979 startAdaptation(group, originalRequest().getRaw());
7c4e4e7f 980 processReplyBody();
981}
0c25e715 982#endif
7dc79973 983
47416555 984void
fccd4a86 985Client::sendBodyIsTooLargeError()
47416555 986{
7e6eabbc 987 const auto err = new ErrorState(ERR_TOO_BIG, Http::scForbidden, request.getRaw(), fwd->al);
47416555 988 fwd->fail(err);
989 fwd->dontRetry(true);
92cfc72f 990 abortOnData("Virgin body too large.");
47416555 991}
992
9e008dda 993// TODO: when HttpStateData sends all errors to ICAP,
585ab260 994// we should be able to move this at the end of setVirginReply().
7dc79973 995void
fccd4a86 996Client::adaptOrFinalizeReply()
7dc79973 997{
a83c6ed6 998#if USE_ADAPTATION
abd4b611
AR
999 // TODO: merge with client side and return void to hide the on/off logic?
1000 // The callback can be called with a NULL service if adaptation is off.
1001 adaptationAccessCheckPending = Adaptation::AccessCheck::Start(
9e008dda 1002 Adaptation::methodRespmod, Adaptation::pointPreCache,
d603e3c2 1003 originalRequest().getRaw(), virginReply(), fwd->al, this);
bf95c10a 1004 debugs(11,5, "adaptationAccessCheckPending=" << adaptationAccessCheckPending);
abd4b611 1005 if (adaptationAccessCheckPending)
7dc79973 1006 return;
7dc79973 1007#endif
1008
585ab260 1009 setFinalReply(virginReply());
7dc79973 1010}
1011
bae917ac
CT
1012/// initializes bodyBytesRead stats if needed and applies delta
1013void
fccd4a86 1014Client::adjustBodyBytesRead(const int64_t delta)
bae917ac
CT
1015{
1016 int64_t &bodyBytesRead = originalRequest()->hier.bodyBytesRead;
1017
1018 // if we got here, do not log a dash even if we got nothing from the server
1019 if (bodyBytesRead < 0)
1020 bodyBytesRead = 0;
1021
1022 bodyBytesRead += delta; // supports negative and zero deltas
1023
2f8abb64 1024 // check for overflows ("infinite" response?) and underflows (a bug)
bae917ac
CT
1025 Must(bodyBytesRead >= 0);
1026}
1027
a928fdfd
EB
1028void
1029Client::delayRead()
1030{
1031 using DeferredReadDialer = NullaryMemFunT<Client>;
1032 AsyncCall::Pointer call = asyncCall(11, 5, "Client::noteDelayAwareReadChance",
1033 DeferredReadDialer(this, &Client::noteDelayAwareReadChance));
1034 entry->mem().delayRead(call);
1035}
1036
7dc79973 1037void
fccd4a86 1038Client::addVirginReplyBody(const char *data, ssize_t len)
7dc79973 1039{
bae917ac
CT
1040 adjustBodyBytesRead(len);
1041
a83c6ed6
AR
1042#if USE_ADAPTATION
1043 assert(!adaptationAccessCheckPending); // or would need to buffer while waiting
1044 if (startedAdaptation) {
bc81cb2b 1045 adaptVirginReplyBody(data, len);
7dc79973 1046 return;
1047 }
7dc79973 1048#endif
bc81cb2b 1049 storeReplyBody(data, len);
1050}
7dc79973 1051
bc81cb2b 1052// writes virgin or adapted reply body to store
1053void
fccd4a86 1054Client::storeReplyBody(const char *data, ssize_t len)
bc81cb2b 1055{
2d1a172f 1056 // write even if len is zero to push headers towards the client side
7dc79973 1057 entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
1058
1059 currentOffset += len;
1060}
1061
f2554f0f 1062size_t
5867ac79 1063Client::calcBufferSpaceToReserve(size_t space, const size_t wantSpace) const
f2554f0f 1064{
5867ac79 1065 if (space < wantSpace) {
f2554f0f 1066 const size_t maxSpace = SBuf::maxSize; // absolute best
5867ac79 1067 space = min(wantSpace, maxSpace); // do not promise more than asked
f2554f0f
AJ
1068 }
1069
1070#if USE_ADAPTATION
1071 if (responseBodyBuffer) {
1810a0cb 1072 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
f2554f0f
AJ
1073 }
1074
aee3523a 1075 if (virginBodyDestination != nullptr) {
f2554f0f
AJ
1076 /*
1077 * BodyPipe buffer has a finite size limit. We
1078 * should not read more data from the network than will fit
1079 * into the pipe buffer or we _lose_ what did not fit if
1080 * the response ends sooner that BodyPipe frees up space:
1081 * There is no code to keep pumping data into the pipe once
1082 * response ends and serverComplete() is called.
f2554f0f 1083 */
5867ac79 1084 const size_t adaptor_space = virginBodyDestination->buf().potentialSpaceSize();
f2554f0f
AJ
1085
1086 debugs(11,9, "Client may read up to min(" <<
5867ac79 1087 adaptor_space << ", " << space << ") bytes");
f2554f0f 1088
5867ac79
AJ
1089 if (adaptor_space < space)
1090 space = adaptor_space;
f2554f0f
AJ
1091 }
1092#endif
1093
1094 return space;
1095}
1096
1097size_t
1098Client::replyBodySpace(const MemBuf &readBuf, const size_t minSpace) const
7dc79973 1099{
52edecde
AJ
1100 size_t space = readBuf.spaceSize(); // available space w/o heroic measures
1101 if (space < minSpace) {
1102 const size_t maxSpace = readBuf.potentialSpaceSize(); // absolute best
1103 space = min(minSpace, maxSpace); // do not promise more than asked
1104 }
1105
a83c6ed6 1106#if USE_ADAPTATION
7dc79973 1107 if (responseBodyBuffer) {
f53969cc 1108 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
7dc79973 1109 }
1110
aee3523a 1111 if (virginBodyDestination != nullptr) {
7dc79973 1112 /*
1113 * BodyPipe buffer has a finite size limit. We
1114 * should not read more data from the network than will fit
1115 * into the pipe buffer or we _lose_ what did not fit if
1116 * the response ends sooner that BodyPipe frees up space:
1117 * There is no code to keep pumping data into the pipe once
1118 * response ends and serverComplete() is called.
1119 *
1120 * If the pipe is totally full, don't register the read handler.
1121 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
1122 * when it has free space again.
1123 */
a83c6ed6
AR
1124 size_t adaptation_space =
1125 virginBodyDestination->buf().potentialSpaceSize();
7dc79973 1126
fccd4a86 1127 debugs(11,9, "Client may read up to min(" <<
9e008dda 1128 adaptation_space << ", " << space << ") bytes");
7dc79973 1129
a83c6ed6
AR
1130 if (adaptation_space < space)
1131 space = adaptation_space;
7dc79973 1132 }
1133#endif
1134
1135 return space;
1136}
f53969cc 1137