]> git.ipfire.org Git - thirdparty/squid.git/blame - src/clients/Client.cc
Bug 1961 partial: cleanup urlParseFinish()
[thirdparty/squid.git] / src / clients / Client.cc
CommitLineData
cd304fc2 1/*
4ac4a490 2 * Copyright (C) 1996-2017 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
dc56a9b1 362 if (io.flag) {
e0236918 363 debugs(11, DBG_IMPORTANT, "sentRequestBody error: FD " << io.fd << ": " << xstrerr(io.xerrno));
5f8252d2 364 ErrorState *err;
955394ce 365 err = new ErrorState(ERR_WRITE_ERROR, Http::scBadGateway, fwd->request);
f5f9e44c 366 err->xerrno = io.xerrno;
5f8252d2 367 fwd->fail(err);
92cfc72f 368 abortOnData("I/O error while sending request body");
5f8252d2 369 return;
370 }
371
372 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
92cfc72f 373 abortOnData("store entry aborted while sending request body");
5f8252d2 374 return;
375 }
376
39cb8c41
AR
377 if (!requestBodySource->exhausted())
378 sendMoreRequestBody();
de48b288 379 else if (receivedWholeRequestBody)
5f8252d2 380 doneSendingRequestBody();
381 else
39cb8c41 382 debugs(9,3, HERE << "waiting for body production end or abort");
cd304fc2 383}
384
5f8252d2 385void
fccd4a86 386Client::sendMoreRequestBody()
5f8252d2 387{
388 assert(requestBodySource != NULL);
389 assert(!requestSender);
a0297974 390
e83cc785 391 const Comm::ConnectionPointer conn = dataConnection();
a0297974 392
6b679a01 393 if (!Comm::IsConnOpen(conn)) {
e0d28505 394 debugs(9,3, HERE << "cannot send request body to closing " << conn);
a0297974
AR
395 return; // wait for the kid's close handler; TODO: assert(closer);
396 }
397
5f8252d2 398 MemBuf buf;
39cb8c41 399 if (getMoreRequestBody(buf) && buf.contentSize() > 0) {
5f8252d2 400 debugs(9,3, HERE << "will write " << buf.contentSize() << " request body bytes");
fccd4a86
AJ
401 typedef CommCbMemFunT<Client, CommIoCbParams> Dialer;
402 requestSender = JobCallback(93,3, Dialer, this, Client::sentRequestBody);
b0388924 403 Comm::Write(conn, &buf, requestSender);
5f8252d2 404 } else {
405 debugs(9,3, HERE << "will wait for more request body bytes or eof");
406 requestSender = NULL;
407 }
408}
409
39cb8c41
AR
410/// either fill buf with available [encoded] request body bytes or return false
411bool
fccd4a86 412Client::getMoreRequestBody(MemBuf &buf)
39cb8c41
AR
413{
414 // default implementation does not encode request body content
415 Must(requestBodySource != NULL);
416 return requestBodySource->getMoreData(buf);
417}
418
c1520b67
AJ
419// Compares hosts in urls, returns false if different, no sheme, or no host.
420static bool
421sameUrlHosts(const char *url1, const char *url2)
422{
423 // XXX: Want urlHostname() here, but it uses static storage and copying
424 const char *host1 = strchr(url1, ':');
425 const char *host2 = strchr(url2, ':');
426
427 if (host1 && host2) {
428 // skip scheme slashes
429 do {
430 ++host1;
431 ++host2;
432 } while (*host1 == '/' && *host2 == '/');
433
434 if (!*host1)
435 return false; // no host
436
437 // increment while the same until we reach the end of the URL/host
438 while (*host1 && *host1 != '/' && *host1 == *host2) {
439 ++host1;
440 ++host2;
441 }
442 return *host1 == *host2;
443 }
444
445 return false; // no URL scheme
446}
447
448// purges entries that match the value of a given HTTP [response] header
449static void
63df1d28 450purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, Http::Message *rep, Http::HdrType hdr)
c1520b67 451{
bf956b0a 452 const char *hdrUrl, *absUrl;
9e008dda 453
71051277
BR
454 absUrl = NULL;
455 hdrUrl = rep->header.getStr(hdr);
456 if (hdrUrl == NULL) {
457 return;
458 }
9e008dda 459
71051277
BR
460 /*
461 * If the URL is relative, make it absolute so we can find it.
462 * If it's absolute, make sure the host parts match to avoid DOS attacks
463 * as per RFC 2616 13.10.
464 */
465 if (urlIsRelative(hdrUrl)) {
466 absUrl = urlMakeAbsolute(req, hdrUrl);
3cbbd242 467 if (absUrl != NULL) {
71051277 468 hdrUrl = absUrl;
3cbbd242 469 }
71051277
BR
470 } else if (!sameUrlHosts(reqUrl, hdrUrl)) {
471 return;
472 }
9e008dda 473
8dceeee3 474 purgeEntriesByUrl(req, hdrUrl);
9e008dda 475
71051277
BR
476 if (absUrl != NULL) {
477 safe_free(absUrl);
3cbbd242 478 }
c1520b67
AJ
479}
480
481// some HTTP methods should purge matching cache entries
482void
fccd4a86 483Client::maybePurgeOthers()
c1520b67 484{
9e008dda
AJ
485 // only some HTTP methods should purge matching cache entries
486 if (!request->method.purgesOthers())
487 return;
c1520b67 488
9e008dda 489 // and probably only if the response was successful
9b769c67 490 if (theFinalReply->sline.status() >= 400)
9e008dda 491 return;
c1520b67 492
9e008dda 493 // XXX: should we use originalRequest() here?
851feda6
AJ
494 SBuf tmp(request->effectiveRequestUri());
495 const char *reqUrl = tmp.c_str();
496 debugs(88, 5, "maybe purging due to " << request->method << ' ' << tmp);
d603e3c2
AJ
497 purgeEntriesByUrl(request.getRaw(), reqUrl);
498 purgeEntriesByHeader(request.getRaw(), reqUrl, theFinalReply, Http::HdrType::LOCATION);
499 purgeEntriesByHeader(request.getRaw(), reqUrl, theFinalReply, Http::HdrType::CONTENT_LOCATION);
c1520b67
AJ
500}
501
fad1a21e 502/// called when we have final (possibly adapted) reply headers; kids extend
5f8252d2 503void
fccd4a86 504Client::haveParsedReplyHeaders()
5f8252d2 505{
9e008dda
AJ
506 Must(theFinalReply);
507 maybePurgeOthers();
5120a38b
AR
508
509 // adaptation may overwrite old offset computed using the virgin response
510 const bool partial = theFinalReply->content_range &&
511 theFinalReply->sline.status() == Http::scPartialContent;
512 currentOffset = partial ? theFinalReply->content_range->spec.offset : 0;
5f8252d2 513}
514
70706149
AR
515/// whether to prevent caching of an otherwise cachable response
516bool
fccd4a86 517Client::blockCaching()
70706149
AR
518{
519 if (const Acl::Tree *acl = Config.accessList.storeMiss) {
520 // This relatively expensive check is not in StoreEntry::checkCachable:
521 // That method lacks HttpRequest and may be called too many times.
d603e3c2 522 ACLFilledChecklist ch(acl, originalRequest().getRaw());
70706149
AR
523 ch.reply = const_cast<HttpReply*>(entry->getReply()); // ACLFilledChecklist API bug
524 HTTPMSGLOCK(ch.reply);
525 if (ch.fastCheck() != ACCESS_ALLOWED) { // when in doubt, block
526 debugs(20, 3, "store_miss prohibits caching");
527 return true;
528 }
529 }
530 return false;
5f8252d2 531}
532
d603e3c2 533HttpRequestPointer
fccd4a86 534Client::originalRequest()
7dc79973 535{
536 return request;
537}
5f8252d2 538
a83c6ed6 539#if USE_ADAPTATION
a22e6cd3
AR
540/// Initiate an asynchronous adaptation transaction which will call us back.
541void
fccd4a86 542Client::startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause)
cd304fc2 543{
fccd4a86 544 debugs(11, 5, "Client::startAdaptation() called");
5f8252d2 545 // check whether we should be sending a body as well
5f8252d2 546 // start body pipe to feed ICAP transaction if needed
585ab260 547 assert(!virginBodyDestination);
9e008dda 548 HttpReply *vrep = virginReply();
585ab260 549 assert(!vrep->body_pipe);
47f6e231 550 int64_t size = 0;
585ab260 551 if (vrep->expectingBody(cause->method, size) && size) {
5f8252d2 552 virginBodyDestination = new BodyPipe(this);
585ab260 553 vrep->body_pipe = virginBodyDestination;
9e008dda
AJ
554 debugs(93, 6, HERE << "will send virgin reply body to " <<
555 virginBodyDestination << "; size: " << size);
c2eef5bd 556 if (size > 0)
557 virginBodyDestination->setBodySize(size);
5f8252d2 558 }
559
a22e6cd3 560 adaptedHeadSource = initiateAdaptation(
af0ded40 561 new Adaptation::Iterator(vrep, cause, fwd->al, group));
4299f876 562 startedAdaptation = initiated(adaptedHeadSource);
a22e6cd3 563 Must(startedAdaptation);
cd304fc2 564}
0c25e715 565
5f8252d2 566// properly cleans up ICAP-related state
567// may be called multiple times
fccd4a86 568void Client::cleanAdaptation()
9e008dda 569{
a83c6ed6 570 debugs(11,5, HERE << "cleaning ICAP; ACL: " << adaptationAccessCheckPending);
5f8252d2 571
572 if (virginBodyDestination != NULL)
573 stopProducingFor(virginBodyDestination, false);
574
0f283edf 575 announceInitiatorAbort(adaptedHeadSource);
5f8252d2 576
577 if (adaptedBodySource != NULL)
578 stopConsumingFrom(adaptedBodySource);
579
a83c6ed6
AR
580 if (!adaptationAccessCheckPending) // we cannot cancel a pending callback
581 assert(doneWithAdaptation()); // make sure the two methods are in sync
5f8252d2 582}
583
584bool
fccd4a86 585Client::doneWithAdaptation() const
9e008dda 586{
a83c6ed6 587 return !adaptationAccessCheckPending &&
9e008dda 588 !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource;
5f8252d2 589}
590
bc81cb2b 591// sends virgin reply body to ICAP, buffering excesses if needed
592void
fccd4a86 593Client::adaptVirginReplyBody(const char *data, ssize_t len)
bc81cb2b 594{
a83c6ed6 595 assert(startedAdaptation);
bc81cb2b 596
597 if (!virginBodyDestination) {
598 debugs(11,3, HERE << "ICAP does not want more virgin body");
599 return;
600 }
601
602 // grow overflow area if already overflowed
603 if (responseBodyBuffer) {
604 responseBodyBuffer->append(data, len);
605 data = responseBodyBuffer->content();
606 len = responseBodyBuffer->contentSize();
607 }
608
609 const ssize_t putSize = virginBodyDestination->putMoreData(data, len);
610 data += putSize;
611 len -= putSize;
612
613 // if we had overflow area, shrink it as necessary
614 if (responseBodyBuffer) {
615 if (putSize == responseBodyBuffer->contentSize()) {
616 delete responseBodyBuffer;
617 responseBodyBuffer = NULL;
618 } else {
619 responseBodyBuffer->consume(putSize);
9e008dda 620 }
bc81cb2b 621 return;
622 }
623
624 // if we did not have an overflow area, create it as needed
625 if (len > 0) {
626 assert(!responseBodyBuffer);
627 responseBodyBuffer = new MemBuf;
628 responseBodyBuffer->init(4096, SQUID_TCP_SO_RCVBUF * 10);
629 responseBodyBuffer->append(data, len);
630 }
631}
632
5f8252d2 633// can supply more virgin response body data
634void
fccd4a86 635Client::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
5f8252d2 636{
7dc79973 637 if (responseBodyBuffer) {
bc81cb2b 638 addVirginReplyBody(NULL, 0); // kick the buffered fragment alive again
639 if (completed && !responseBodyBuffer) {
640 serverComplete2();
641 return;
642 }
7dc79973 643 }
5f8252d2 644 maybeReadVirginBody();
645}
646
bc81cb2b 647// the consumer of our virgin response body aborted
5f8252d2 648void
fccd4a86 649Client::noteBodyConsumerAborted(BodyPipe::Pointer)
5f8252d2 650{
651 stopProducingFor(virginBodyDestination, false);
bc81cb2b 652
a83c6ed6 653 // do not force closeServer here in case we need to bypass AdaptationQueryAbort
bc81cb2b 654
a83c6ed6
AR
655 if (doneWithAdaptation()) // we may still be receiving adapted response
656 handleAdaptationCompleted();
5f8252d2 657}
658
659// received adapted response headers (body may follow)
660void
fccd4a86 661Client::noteAdaptationAnswer(const Adaptation::Answer &answer)
5f8252d2 662{
a83c6ed6 663 clearAdaptation(adaptedHeadSource); // we do not expect more messages
5f8252d2 664
3af10ac0
AR
665 switch (answer.kind) {
666 case Adaptation::Answer::akForward:
63df1d28 667 handleAdaptedHeader(const_cast<Http::Message*>(answer.message.getRaw()));
3af10ac0
AR
668 break;
669
670 case Adaptation::Answer::akBlock:
671 handleAdaptationBlocked(answer);
672 break;
673
674 case Adaptation::Answer::akError:
675 handleAdaptationAborted(!answer.final);
676 break;
677 }
678}
679
680void
63df1d28 681Client::handleAdaptedHeader(Http::Message *msg)
3af10ac0 682{
1733bbba
CT
683 if (abortOnBadEntry("entry went bad while waiting for adapted headers")) {
684 // If the adapted response has a body, the ICAP side needs to know
685 // that nobody will consume that body. We will be destroyed upon
686 // return. Tell the ICAP side that it is on its own.
687 HttpReply *rep = dynamic_cast<HttpReply*>(msg);
688 assert(rep);
689 if (rep->body_pipe != NULL)
690 rep->body_pipe->expectNoConsumption();
691
5f8252d2 692 return;
1733bbba 693 }
5f8252d2 694
585ab260 695 HttpReply *rep = dynamic_cast<HttpReply*>(msg);
5f8252d2 696 assert(rep);
585ab260 697 debugs(11,5, HERE << this << " setting adapted reply to " << rep);
698 setFinalReply(rep);
5f8252d2 699
700 assert(!adaptedBodySource);
585ab260 701 if (rep->body_pipe != NULL) {
5f8252d2 702 // subscribe to receive adapted body
585ab260 703 adaptedBodySource = rep->body_pipe;
5f8252d2 704 // assume that ICAP does not auto-consume on failures
dfb6dc9d
NH
705 const bool result = adaptedBodySource->setConsumerIfNotLate(this);
706 assert(result);
5f8252d2 707 } else {
708 // no body
a83c6ed6
AR
709 if (doneWithAdaptation()) // we may still be sending virgin response
710 handleAdaptationCompleted();
5f8252d2 711 }
5f8252d2 712}
713
5f8252d2 714void
fccd4a86 715Client::resumeBodyStorage()
5f8252d2 716{
0ad2b63b
CT
717 if (abortOnBadEntry("store entry aborted while kick producer callback"))
718 return;
5f8252d2 719
e83cdc25 720 if (!adaptedBodySource)
0ad2b63b
CT
721 return;
722
723 handleMoreAdaptedBodyAvailable();
724
725 if (adaptedBodySource != NULL && adaptedBodySource->exhausted())
726 endAdaptedBodyConsumption();
727}
5f8252d2 728
0ad2b63b
CT
729// more adapted response body is available
730void
fccd4a86 731Client::handleMoreAdaptedBodyAvailable()
0ad2b63b 732{
5f8252d2 733 if (abortOnBadEntry("entry refuses adapted body"))
734 return;
735
736 assert(entry);
0ad2b63b
CT
737
738 size_t contentSize = adaptedBodySource->buf().contentSize();
0ad2b63b
CT
739
740 if (!contentSize)
741 return; // XXX: bytesWanted asserts on zero-size ranges
742
384a7590 743 const size_t spaceAvailable = entry->bytesWanted(Range<size_t>(0, contentSize), true);
0ad2b63b 744
e83cdc25 745 if (spaceAvailable < contentSize ) {
0ad2b63b 746 // No or partial body data consuming
fccd4a86
AJ
747 typedef NullaryMemFunT<Client> Dialer;
748 AsyncCall::Pointer call = asyncCall(93, 5, "Client::resumeBodyStorage",
749 Dialer(this, &Client::resumeBodyStorage));
0ad2b63b
CT
750 entry->deferProducer(call);
751 }
752
4dc2b072 753 if (!spaceAvailable) {
0ad2b63b
CT
754 debugs(11, 5, HERE << "NOT storing " << contentSize << " bytes of adapted " <<
755 "response body at offset " << adaptedBodySource->consumedSize());
756 return;
757 }
e83cdc25 758
0ad2b63b
CT
759 if (spaceAvailable < contentSize ) {
760 debugs(11, 5, HERE << "postponing storage of " <<
761 (contentSize - spaceAvailable) << " body bytes");
762 contentSize = spaceAvailable;
0ad2b63b 763 }
e83cdc25 764
0ad2b63b
CT
765 debugs(11,5, HERE << "storing " << contentSize << " bytes of adapted " <<
766 "response body at offset " << adaptedBodySource->consumedSize());
e83cdc25 767
5f8252d2 768 BodyPipeCheckout bpc(*adaptedBodySource);
0ad2b63b
CT
769 const StoreIOBuffer ioBuf(&bpc.buf, currentOffset, contentSize);
770 currentOffset += ioBuf.length;
5f8252d2 771 entry->write(ioBuf);
772 bpc.buf.consume(contentSize);
773 bpc.checkIn();
774}
775
776// the entire adapted response body was produced, successfully
777void
fccd4a86 778Client::handleAdaptedBodyProductionEnded()
5f8252d2 779{
5f8252d2 780 if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
781 return;
e83cdc25
A
782
783 // end consumption if we consumed everything
0ad2b63b
CT
784 if (adaptedBodySource != NULL && adaptedBodySource->exhausted())
785 endAdaptedBodyConsumption();
786 // else resumeBodyStorage() will eventually consume the rest
787}
5f8252d2 788
0ad2b63b 789void
fccd4a86 790Client::endAdaptedBodyConsumption()
0ad2b63b
CT
791{
792 stopConsumingFrom(adaptedBodySource);
a83c6ed6 793 handleAdaptationCompleted();
5f8252d2 794}
795
796// premature end of the adapted response body
fccd4a86 797void Client::handleAdaptedBodyProducerAborted()
5f8252d2 798{
7224ca5a
AR
799 if (abortOnBadEntry("entry went bad while waiting for the now-aborted adapted body"))
800 return;
801
802 Must(adaptedBodySource != nullptr);
803 if (!adaptedBodySource->exhausted()) {
804 debugs(11,5, "waiting to consume the remainder of the aborted adapted body");
805 return; // resumeBodyStorage() should eventually consume the rest
806 }
807
5f8252d2 808 stopConsumingFrom(adaptedBodySource);
7224ca5a
AR
809
810 if (handledEarlyAdaptationAbort())
811 return;
812
813 entry->lengthWentBad("body adaptation aborted");
814 handleAdaptationCompleted(); // the user should get a truncated response
5f8252d2 815}
816
a83c6ed6 817// common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
5f8252d2 818void
fccd4a86 819Client::handleAdaptationCompleted()
5f8252d2 820{
a83c6ed6
AR
821 debugs(11,5, HERE << "handleAdaptationCompleted");
822 cleanAdaptation();
bc81cb2b 823
719c885c 824 // We stop reading origin response because we have no place to put it(*) and
bc81cb2b 825 // cannot use it. If some origin servers do not like that or if we want to
826 // reuse more pconns, we can add code to discard unneeded origin responses.
719c885c
AR
827 // (*) TODO: Is it possible that the adaptation xaction is still running?
828 if (mayReadVirginReplyBody()) {
bc81cb2b 829 debugs(11,3, HERE << "closing origin conn due to ICAP completion");
830 closeServer();
831 }
832
5f8252d2 833 completeForwarding();
5f8252d2 834}
835
a83c6ed6 836// common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
5f8252d2 837void
fccd4a86 838Client::handleAdaptationAborted(bool bypassable)
5f8252d2 839{
a83c6ed6 840 debugs(11,5, HERE << "handleAdaptationAborted; bypassable: " << bypassable <<
9e008dda 841 ", entry empty: " << entry->isEmpty());
5f8252d2 842
843 if (abortOnBadEntry("entry went bad while ICAP aborted"))
844 return;
845
0f283edf 846 // TODO: bypass if possible
7224ca5a 847 if (!handledEarlyAdaptationAbort())
92cfc72f 848 abortAll("adaptation failure with a filled entry");
7224ca5a 849}
0f283edf 850
7224ca5a
AR
851/// If the store entry is still empty, fully handles adaptation abort, returning
852/// true. Otherwise just updates the request error detail and returns false.
853bool
854Client::handledEarlyAdaptationAbort()
855{
5f8252d2 856 if (entry->isEmpty()) {
7224ca5a 857 debugs(11,8, "adaptation failure with an empty entry: " << *entry);
d603e3c2 858 ErrorState *err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, request.getRaw());
129fe2a1 859 err->detailError(ERR_DETAIL_ICAP_RESPMOD_EARLY);
5f8252d2 860 fwd->fail(err);
861 fwd->dontRetry(true);
92cfc72f 862 abortAll("adaptation failure with an empty entry");
7224ca5a 863 return true; // handled
64b66b76 864 }
5f8252d2 865
7224ca5a
AR
866 if (request) // update logged info directly
867 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_ICAP_RESPMOD_LATE);
868
869 return false; // the caller must handle
5f8252d2 870}
871
3af10ac0
AR
872// adaptation service wants us to deny HTTP client access to this response
873void
fccd4a86 874Client::handleAdaptationBlocked(const Adaptation::Answer &answer)
3af10ac0 875{
6666da11 876 debugs(11,5, HERE << answer.ruleId);
3af10ac0
AR
877
878 if (abortOnBadEntry("entry went bad while ICAP aborted"))
879 return;
880
881 if (!entry->isEmpty()) { // too late to block (should not really happen)
ec4d1a1d
A
882 if (request)
883 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_RESPMOD_BLOCK_LATE);
92cfc72f 884 abortAll("late adaptation block");
ec4d1a1d 885 return;
3af10ac0 886 }
ec4d1a1d 887
3af10ac0
AR
888 debugs(11,7, HERE << "creating adaptation block response");
889
890 err_type page_id =
891 aclGetDenyInfoPage(&Config.denyInfoList, answer.ruleId.termedBuf(), 1);
892 if (page_id == ERR_NONE)
893 page_id = ERR_ACCESS_DENIED;
894
d603e3c2 895 ErrorState *err = new ErrorState(page_id, Http::scForbidden, request.getRaw());
129fe2a1 896 err->detailError(ERR_DETAIL_RESPMOD_BLOCK_EARLY);
3af10ac0
AR
897 fwd->fail(err);
898 fwd->dontRetry(true);
899
92cfc72f 900 abortOnData("timely adaptation block");
3af10ac0
AR
901}
902
7c4e4e7f 903void
fccd4a86 904Client::noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer group)
7c4e4e7f 905{
a83c6ed6 906 adaptationAccessCheckPending = false;
7c4e4e7f 907
908 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
909 return;
910
d5430dc8
AJ
911 // TODO: Should non-ICAP and ICAP REPMOD pre-cache paths check this?
912 // That check now only happens on REQMOD pre-cache and REPMOD post-cache, in processReplyAccess().
47416555 913 if (virginReply()->expectedBodyTooLarge(*request)) {
914 sendBodyIsTooLargeError();
915 return;
916 }
d5430dc8 917 // TODO: Should we check receivedBodyTooLarge as well?
47416555 918
a22e6cd3
AR
919 if (!group) {
920 debugs(11,3, HERE << "no adapation needed");
585ab260 921 setFinalReply(virginReply());
7c4e4e7f 922 processReplyBody();
7c4e4e7f 923 return;
924 }
925
d603e3c2 926 startAdaptation(group, originalRequest().getRaw());
7c4e4e7f 927 processReplyBody();
928}
0c25e715 929#endif
7dc79973 930
47416555 931void
fccd4a86 932Client::sendBodyIsTooLargeError()
47416555 933{
d603e3c2 934 ErrorState *err = new ErrorState(ERR_TOO_BIG, Http::scForbidden, request.getRaw());
47416555 935 fwd->fail(err);
936 fwd->dontRetry(true);
92cfc72f 937 abortOnData("Virgin body too large.");
47416555 938}
939
9e008dda 940// TODO: when HttpStateData sends all errors to ICAP,
585ab260 941// we should be able to move this at the end of setVirginReply().
7dc79973 942void
fccd4a86 943Client::adaptOrFinalizeReply()
7dc79973 944{
a83c6ed6 945#if USE_ADAPTATION
abd4b611
AR
946 // TODO: merge with client side and return void to hide the on/off logic?
947 // The callback can be called with a NULL service if adaptation is off.
948 adaptationAccessCheckPending = Adaptation::AccessCheck::Start(
9e008dda 949 Adaptation::methodRespmod, Adaptation::pointPreCache,
d603e3c2 950 originalRequest().getRaw(), virginReply(), fwd->al, this);
c30ac6ea 951 debugs(11,5, HERE << "adaptationAccessCheckPending=" << adaptationAccessCheckPending);
abd4b611 952 if (adaptationAccessCheckPending)
7dc79973 953 return;
7dc79973 954#endif
955
585ab260 956 setFinalReply(virginReply());
7dc79973 957}
958
bae917ac
CT
959/// initializes bodyBytesRead stats if needed and applies delta
960void
fccd4a86 961Client::adjustBodyBytesRead(const int64_t delta)
bae917ac
CT
962{
963 int64_t &bodyBytesRead = originalRequest()->hier.bodyBytesRead;
964
965 // if we got here, do not log a dash even if we got nothing from the server
966 if (bodyBytesRead < 0)
967 bodyBytesRead = 0;
968
969 bodyBytesRead += delta; // supports negative and zero deltas
970
971 // check for overflows ("infinite" response?) and undeflows (a bug)
972 Must(bodyBytesRead >= 0);
973}
974
7dc79973 975void
fccd4a86 976Client::addVirginReplyBody(const char *data, ssize_t len)
7dc79973 977{
bae917ac
CT
978 adjustBodyBytesRead(len);
979
a83c6ed6
AR
980#if USE_ADAPTATION
981 assert(!adaptationAccessCheckPending); // or would need to buffer while waiting
982 if (startedAdaptation) {
bc81cb2b 983 adaptVirginReplyBody(data, len);
7dc79973 984 return;
985 }
7dc79973 986#endif
bc81cb2b 987 storeReplyBody(data, len);
988}
7dc79973 989
bc81cb2b 990// writes virgin or adapted reply body to store
991void
fccd4a86 992Client::storeReplyBody(const char *data, ssize_t len)
bc81cb2b 993{
2d1a172f 994 // write even if len is zero to push headers towards the client side
7dc79973 995 entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
996
997 currentOffset += len;
998}
999
f2554f0f 1000size_t
5867ac79 1001Client::calcBufferSpaceToReserve(size_t space, const size_t wantSpace) const
f2554f0f 1002{
5867ac79 1003 if (space < wantSpace) {
f2554f0f 1004 const size_t maxSpace = SBuf::maxSize; // absolute best
5867ac79 1005 space = min(wantSpace, maxSpace); // do not promise more than asked
f2554f0f
AJ
1006 }
1007
1008#if USE_ADAPTATION
1009 if (responseBodyBuffer) {
1810a0cb 1010 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
f2554f0f
AJ
1011 }
1012
1013 if (virginBodyDestination != NULL) {
1014 /*
1015 * BodyPipe buffer has a finite size limit. We
1016 * should not read more data from the network than will fit
1017 * into the pipe buffer or we _lose_ what did not fit if
1018 * the response ends sooner that BodyPipe frees up space:
1019 * There is no code to keep pumping data into the pipe once
1020 * response ends and serverComplete() is called.
f2554f0f 1021 */
5867ac79 1022 const size_t adaptor_space = virginBodyDestination->buf().potentialSpaceSize();
f2554f0f
AJ
1023
1024 debugs(11,9, "Client may read up to min(" <<
5867ac79 1025 adaptor_space << ", " << space << ") bytes");
f2554f0f 1026
5867ac79
AJ
1027 if (adaptor_space < space)
1028 space = adaptor_space;
f2554f0f
AJ
1029 }
1030#endif
1031
1032 return space;
1033}
1034
1035size_t
1036Client::replyBodySpace(const MemBuf &readBuf, const size_t minSpace) const
7dc79973 1037{
52edecde
AJ
1038 size_t space = readBuf.spaceSize(); // available space w/o heroic measures
1039 if (space < minSpace) {
1040 const size_t maxSpace = readBuf.potentialSpaceSize(); // absolute best
1041 space = min(minSpace, maxSpace); // do not promise more than asked
1042 }
1043
a83c6ed6 1044#if USE_ADAPTATION
7dc79973 1045 if (responseBodyBuffer) {
f53969cc 1046 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
7dc79973 1047 }
1048
1049 if (virginBodyDestination != NULL) {
1050 /*
1051 * BodyPipe buffer has a finite size limit. We
1052 * should not read more data from the network than will fit
1053 * into the pipe buffer or we _lose_ what did not fit if
1054 * the response ends sooner that BodyPipe frees up space:
1055 * There is no code to keep pumping data into the pipe once
1056 * response ends and serverComplete() is called.
1057 *
1058 * If the pipe is totally full, don't register the read handler.
1059 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
1060 * when it has free space again.
1061 */
a83c6ed6
AR
1062 size_t adaptation_space =
1063 virginBodyDestination->buf().potentialSpaceSize();
7dc79973 1064
fccd4a86 1065 debugs(11,9, "Client may read up to min(" <<
9e008dda 1066 adaptation_space << ", " << space << ") bytes");
7dc79973 1067
a83c6ed6
AR
1068 if (adaptation_space < space)
1069 space = adaptation_space;
7dc79973 1070 }
1071#endif
1072
1073 return space;
1074}
f53969cc 1075