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