]> git.ipfire.org Git - thirdparty/squid.git/blame - src/clients/Client.cc
Renamed http_hdr_type to Http::HdrType, fixed some HdrType-int implicit conversions
[thirdparty/squid.git] / src / clients / Client.cc
CommitLineData
cd304fc2 1/*
bde978a6 2 * Copyright (C) 1996-2015 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
fccd4a86 40Client::Client(FwdState *theFwdState): AsyncJob("Client"),
f53969cc
SM
41 completed(false),
42 currentOffset(0),
43 responseBodyBuffer(NULL),
44 fwd(theFwdState),
45 requestSender(NULL),
a83c6ed6 46#if USE_ADAPTATION
f53969cc
SM
47 adaptedHeadSource(NULL),
48 adaptationAccessCheckPending(false),
49 startedAdaptation(false),
77089558 50#endif
f53969cc
SM
51 receivedWholeRequestBody(false),
52 theVirginReply(NULL),
53 theFinalReply(NULL)
cd304fc2 54{
cd304fc2 55 entry = fwd->entry;
fccd4a86 56 entry->lock("Client");
34266cde 57
b248c2a3
AJ
58 request = fwd->request;
59 HTTPMSGLOCK(request);
cd304fc2 60}
61
fccd4a86 62Client::~Client()
cd304fc2 63{
49918309
AR
64 // paranoid: check that swanSong has been called
65 assert(!requestBodySource);
66#if USE_ADAPTATION
67 assert(!virginBodyDestination);
68 assert(!adaptedBodySource);
69#endif
70
fccd4a86 71 entry->unlock("Client");
cd304fc2 72
6dd9f4bd 73 HTTPMSGUNLOCK(request);
585ab260 74 HTTPMSGUNLOCK(theVirginReply);
75 HTTPMSGUNLOCK(theFinalReply);
cd304fc2 76
77 fwd = NULL; // refcounted
78
49918309 79 if (responseBodyBuffer != NULL) {
9e008dda
AJ
80 delete responseBodyBuffer;
81 responseBodyBuffer = NULL;
49918309
AR
82 }
83}
84
85void
fccd4a86 86Client::swanSong()
49918309
AR
87{
88 // get rid of our piping obligations
5f8252d2 89 if (requestBodySource != NULL)
279152e7 90 stopConsumingFrom(requestBodySource);
5f8252d2 91
a83c6ed6
AR
92#if USE_ADAPTATION
93 cleanAdaptation();
5f8252d2 94#endif
7dc79973 95
49918309
AR
96 BodyConsumer::swanSong();
97#if USE_ADAPTATION
98 Initiator::swanSong();
99 BodyProducer::swanSong();
100#endif
b2c251cc
AJ
101
102 // paranoid: check that swanSong has been called
103 // extra paranoid: yeah, I really mean it. they MUST pass here.
104 assert(!requestBodySource);
105#if USE_ADAPTATION
106 assert(!virginBodyDestination);
107 assert(!adaptedBodySource);
108#endif
5f8252d2 109}
110
585ab260 111HttpReply *
fccd4a86 112Client::virginReply()
9e008dda 113{
585ab260 114 assert(theVirginReply);
115 return theVirginReply;
116}
117
118const HttpReply *
fccd4a86 119Client::virginReply() const
9e008dda 120{
585ab260 121 assert(theVirginReply);
122 return theVirginReply;
123}
124
125HttpReply *
fccd4a86 126Client::setVirginReply(HttpReply *rep)
9e008dda 127{
585ab260 128 debugs(11,5, HERE << this << " setting virgin reply to " << rep);
129 assert(!theVirginReply);
130 assert(rep);
b248c2a3
AJ
131 theVirginReply = rep;
132 HTTPMSGLOCK(theVirginReply);
9e008dda 133 return theVirginReply;
585ab260 134}
135
136HttpReply *
fccd4a86 137Client::finalReply()
9e008dda 138{
585ab260 139 assert(theFinalReply);
140 return theFinalReply;
141}
142
143HttpReply *
fccd4a86 144Client::setFinalReply(HttpReply *rep)
9e008dda 145{
585ab260 146 debugs(11,5, HERE << this << " setting final reply to " << rep);
147
148 assert(!theFinalReply);
149 assert(rep);
b248c2a3
AJ
150 theFinalReply = rep;
151 HTTPMSGLOCK(theFinalReply);
585ab260 152
3756e5c0
AR
153 // give entry the reply because haveParsedReplyHeaders() expects it there
154 entry->replaceHttpReply(theFinalReply, false); // but do not write yet
155 haveParsedReplyHeaders(); // update the entry/reply (e.g., set timestamps)
b481492b 156 if (!EBIT_TEST(entry->flags, RELEASE_REQUEST) && blockCaching())
70706149 157 entry->release();
3756e5c0 158 entry->startWriting(); // write the updated entry to store
585ab260 159
160 return theFinalReply;
161}
162
5f8252d2 163// called when no more server communication is expected; may quit
164void
fccd4a86 165Client::serverComplete()
5f8252d2 166{
167 debugs(11,5,HERE << "serverComplete " << this);
168
169 if (!doneWithServer()) {
170 closeServer();
171 assert(doneWithServer());
172 }
173
7dc79973 174 completed = true;
175
3ff65596 176 HttpRequest *r = originalRequest();
16b70e2a 177 r->hier.stopPeerClock(true);
e1381638 178
5f8252d2 179 if (requestBodySource != NULL)
180 stopConsumingFrom(requestBodySource);
181
7dc79973 182 if (responseBodyBuffer != NULL)
9e008dda 183 return;
7dc79973 184
185 serverComplete2();
186}
187
188void
fccd4a86 189Client::serverComplete2()
7dc79973 190{
191 debugs(11,5,HERE << "serverComplete2 " << this);
192
a83c6ed6 193#if USE_ADAPTATION
5f8252d2 194 if (virginBodyDestination != NULL)
195 stopProducingFor(virginBodyDestination, true);
196
a83c6ed6 197 if (!doneWithAdaptation())
5f8252d2 198 return;
199#endif
200
201 completeForwarding();
5f8252d2 202}
203
fccd4a86 204bool Client::doneAll() const
9e008dda 205{
79628299 206 return doneWithServer() &&
a83c6ed6 207#if USE_ADAPTATION
b692311b
A
208 doneWithAdaptation() &&
209 Adaptation::Initiator::doneAll() &&
210 BodyProducer::doneAll() &&
5f8252d2 211#endif
b692311b 212 BodyConsumer::doneAll();
5f8252d2 213}
214
215// FTP side overloads this to work around multiple calls to fwd->complete
216void
fccd4a86 217Client::completeForwarding()
9e008dda 218{
5f8252d2 219 debugs(11,5, HERE << "completing forwarding for " << fwd);
220 assert(fwd != NULL);
221 fwd->complete();
222}
223
123ec4de 224// Register to receive request body
fccd4a86 225bool Client::startRequestBodyFlow()
123ec4de 226{
227 HttpRequest *r = originalRequest();
228 assert(r->body_pipe != NULL);
229 requestBodySource = r->body_pipe;
230 if (requestBodySource->setConsumerIfNotLate(this)) {
231 debugs(11,3, HERE << "expecting request body from " <<
9e008dda 232 requestBodySource->status());
123ec4de 233 return true;
234 }
235
236 debugs(11,3, HERE << "aborting on partially consumed request body: " <<
9e008dda 237 requestBodySource->status());
123ec4de 238 requestBodySource = NULL;
239 return false;
240}
241
5f8252d2 242// Entry-dependent callbacks use this check to quit if the entry went bad
243bool
fccd4a86 244Client::abortOnBadEntry(const char *abortReason)
5f8252d2 245{
246 if (entry->isAccepting())
247 return false;
248
249 debugs(11,5, HERE << "entry is not Accepting!");
250 abortTransaction(abortReason);
251 return true;
252}
253
254// more request or adapted response body is available
255void
fccd4a86 256Client::noteMoreBodyDataAvailable(BodyPipe::Pointer bp)
5f8252d2 257{
a83c6ed6 258#if USE_ADAPTATION
dc56a9b1 259 if (adaptedBodySource == bp) {
5f8252d2 260 handleMoreAdaptedBodyAvailable();
261 return;
262 }
263#endif
0ad2b63b
CT
264 if (requestBodySource == bp)
265 handleMoreRequestBodyAvailable();
5f8252d2 266}
267
268// the entire request or adapted response body was provided, successfully
269void
fccd4a86 270Client::noteBodyProductionEnded(BodyPipe::Pointer bp)
5f8252d2 271{
a83c6ed6 272#if USE_ADAPTATION
dc56a9b1 273 if (adaptedBodySource == bp) {
5f8252d2 274 handleAdaptedBodyProductionEnded();
275 return;
c99de607 276 }
cd304fc2 277#endif
0ad2b63b
CT
278 if (requestBodySource == bp)
279 handleRequestBodyProductionEnded();
5f8252d2 280}
281
282// premature end of the request or adapted response body production
283void
fccd4a86 284Client::noteBodyProducerAborted(BodyPipe::Pointer bp)
5f8252d2 285{
a83c6ed6 286#if USE_ADAPTATION
dc56a9b1 287 if (adaptedBodySource == bp) {
5f8252d2 288 handleAdaptedBodyProducerAborted();
289 return;
290 }
291#endif
0ad2b63b
CT
292 if (requestBodySource == bp)
293 handleRequestBodyProducerAborted();
5f8252d2 294}
295
5f8252d2 296// more origin request body data is available
297void
fccd4a86 298Client::handleMoreRequestBodyAvailable()
5f8252d2 299{
300 if (!requestSender)
301 sendMoreRequestBody();
302 else
303 debugs(9,3, HERE << "waiting for request body write to complete");
304}
305
306// there will be no more handleMoreRequestBodyAvailable calls
307void
fccd4a86 308Client::handleRequestBodyProductionEnded()
5f8252d2 309{
39cb8c41 310 receivedWholeRequestBody = true;
5f8252d2 311 if (!requestSender)
312 doneSendingRequestBody();
313 else
314 debugs(9,3, HERE << "waiting for request body write to complete");
315}
316
317// called when we are done sending request body; kids extend this
318void
fccd4a86 319Client::doneSendingRequestBody()
9e008dda 320{
5f8252d2 321 debugs(9,3, HERE << "done sending request body");
322 assert(requestBodySource != NULL);
323 stopConsumingFrom(requestBodySource);
324
325 // kids extend this
326}
327
328// called when body producers aborts; kids extend this
329void
fccd4a86 330Client::handleRequestBodyProducerAborted()
5f8252d2 331{
332 if (requestSender != NULL)
333 debugs(9,3, HERE << "fyi: request body aborted while we were sending");
334
0919c51e 335 fwd->dontRetry(true); // the problem is not with the server
5f8252d2 336 stopConsumingFrom(requestBodySource); // requestSender, if any, will notice
337
338 // kids extend this
339}
340
5f8252d2 341// called when we wrote request headers(!) or a part of the body
342void
fccd4a86 343Client::sentRequestBody(const CommIoCbParams &io)
5f8252d2 344{
dc56a9b1 345 debugs(11, 5, "sentRequestBody: FD " << io.fd << ": size " << io.size << ": errflag " << io.flag << ".");
5f8252d2 346 debugs(32,3,HERE << "sentRequestBody called");
347
348 requestSender = NULL;
349
dc56a9b1 350 if (io.size > 0) {
351 fd_bytes(io.fd, io.size, FD_WRITE);
e4f1fdae 352 kb_incr(&(statCounter.server.all.kbytes_out), io.size);
5f8252d2 353 // kids should increment their counters
354 }
355
c8407295 356 if (io.flag == Comm::ERR_CLOSING)
5f8252d2 357 return;
358
359 if (!requestBodySource) {
360 debugs(9,3, HERE << "detected while-we-were-sending abort");
361 return; // do nothing;
362 }
363
dc56a9b1 364 if (io.flag) {
e0236918 365 debugs(11, DBG_IMPORTANT, "sentRequestBody error: FD " << io.fd << ": " << xstrerr(io.xerrno));
5f8252d2 366 ErrorState *err;
955394ce 367 err = new ErrorState(ERR_WRITE_ERROR, Http::scBadGateway, fwd->request);
f5f9e44c 368 err->xerrno = io.xerrno;
5f8252d2 369 fwd->fail(err);
370 abortTransaction("I/O error while sending request body");
371 return;
372 }
373
374 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
375 abortTransaction("store entry aborted while sending request body");
376 return;
377 }
378
39cb8c41
AR
379 if (!requestBodySource->exhausted())
380 sendMoreRequestBody();
de48b288 381 else if (receivedWholeRequestBody)
5f8252d2 382 doneSendingRequestBody();
383 else
39cb8c41 384 debugs(9,3, HERE << "waiting for body production end or abort");
cd304fc2 385}
386
5f8252d2 387void
fccd4a86 388Client::sendMoreRequestBody()
5f8252d2 389{
390 assert(requestBodySource != NULL);
391 assert(!requestSender);
a0297974 392
e83cc785 393 const Comm::ConnectionPointer conn = dataConnection();
a0297974 394
6b679a01 395 if (!Comm::IsConnOpen(conn)) {
e0d28505 396 debugs(9,3, HERE << "cannot send request body to closing " << conn);
a0297974
AR
397 return; // wait for the kid's close handler; TODO: assert(closer);
398 }
399
5f8252d2 400 MemBuf buf;
39cb8c41 401 if (getMoreRequestBody(buf) && buf.contentSize() > 0) {
5f8252d2 402 debugs(9,3, HERE << "will write " << buf.contentSize() << " request body bytes");
fccd4a86
AJ
403 typedef CommCbMemFunT<Client, CommIoCbParams> Dialer;
404 requestSender = JobCallback(93,3, Dialer, this, Client::sentRequestBody);
b0388924 405 Comm::Write(conn, &buf, requestSender);
5f8252d2 406 } else {
407 debugs(9,3, HERE << "will wait for more request body bytes or eof");
408 requestSender = NULL;
409 }
410}
411
39cb8c41
AR
412/// either fill buf with available [encoded] request body bytes or return false
413bool
fccd4a86 414Client::getMoreRequestBody(MemBuf &buf)
39cb8c41
AR
415{
416 // default implementation does not encode request body content
417 Must(requestBodySource != NULL);
418 return requestBodySource->getMoreData(buf);
419}
420
c1520b67
AJ
421// Compares hosts in urls, returns false if different, no sheme, or no host.
422static bool
423sameUrlHosts(const char *url1, const char *url2)
424{
425 // XXX: Want urlHostname() here, but it uses static storage and copying
426 const char *host1 = strchr(url1, ':');
427 const char *host2 = strchr(url2, ':');
428
429 if (host1 && host2) {
430 // skip scheme slashes
431 do {
432 ++host1;
433 ++host2;
434 } while (*host1 == '/' && *host2 == '/');
435
436 if (!*host1)
437 return false; // no host
438
439 // increment while the same until we reach the end of the URL/host
440 while (*host1 && *host1 != '/' && *host1 == *host2) {
441 ++host1;
442 ++host2;
443 }
444 return *host1 == *host2;
445 }
446
447 return false; // no URL scheme
448}
449
450// purges entries that match the value of a given HTTP [response] header
451static void
789217a2 452purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, HttpMsg *rep, Http::HdrType hdr)
c1520b67 453{
bf956b0a 454 const char *hdrUrl, *absUrl;
9e008dda 455
71051277
BR
456 absUrl = NULL;
457 hdrUrl = rep->header.getStr(hdr);
458 if (hdrUrl == NULL) {
459 return;
460 }
9e008dda 461
71051277
BR
462 /*
463 * If the URL is relative, make it absolute so we can find it.
464 * If it's absolute, make sure the host parts match to avoid DOS attacks
465 * as per RFC 2616 13.10.
466 */
467 if (urlIsRelative(hdrUrl)) {
468 absUrl = urlMakeAbsolute(req, hdrUrl);
3cbbd242 469 if (absUrl != NULL) {
71051277 470 hdrUrl = absUrl;
3cbbd242 471 }
71051277
BR
472 } else if (!sameUrlHosts(reqUrl, hdrUrl)) {
473 return;
474 }
9e008dda 475
8dceeee3 476 purgeEntriesByUrl(req, hdrUrl);
9e008dda 477
71051277
BR
478 if (absUrl != NULL) {
479 safe_free(absUrl);
3cbbd242 480 }
c1520b67
AJ
481}
482
483// some HTTP methods should purge matching cache entries
484void
fccd4a86 485Client::maybePurgeOthers()
c1520b67 486{
9e008dda
AJ
487 // only some HTTP methods should purge matching cache entries
488 if (!request->method.purgesOthers())
489 return;
c1520b67 490
9e008dda 491 // and probably only if the response was successful
9b769c67 492 if (theFinalReply->sline.status() >= 400)
9e008dda 493 return;
c1520b67 494
9e008dda 495 // XXX: should we use originalRequest() here?
851feda6
AJ
496 SBuf tmp(request->effectiveRequestUri());
497 const char *reqUrl = tmp.c_str();
498 debugs(88, 5, "maybe purging due to " << request->method << ' ' << tmp);
9e008dda 499 purgeEntriesByUrl(request, reqUrl);
789217a2
FC
500 purgeEntriesByHeader(request, reqUrl, theFinalReply, Http::HdrType::LOCATION);
501 purgeEntriesByHeader(request, reqUrl, theFinalReply, Http::HdrType::CONTENT_LOCATION);
c1520b67
AJ
502}
503
fad1a21e 504/// called when we have final (possibly adapted) reply headers; kids extend
5f8252d2 505void
fccd4a86 506Client::haveParsedReplyHeaders()
5f8252d2 507{
9e008dda
AJ
508 Must(theFinalReply);
509 maybePurgeOthers();
5120a38b
AR
510
511 // adaptation may overwrite old offset computed using the virgin response
512 const bool partial = theFinalReply->content_range &&
513 theFinalReply->sline.status() == Http::scPartialContent;
514 currentOffset = partial ? theFinalReply->content_range->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.
524 ACLFilledChecklist ch(acl, originalRequest(), NULL);
525 ch.reply = const_cast<HttpReply*>(entry->getReply()); // ACLFilledChecklist API bug
526 HTTPMSGLOCK(ch.reply);
527 if (ch.fastCheck() != ACCESS_ALLOWED) { // when in doubt, block
528 debugs(20, 3, "store_miss prohibits caching");
529 return true;
530 }
531 }
532 return false;
5f8252d2 533}
534
7dc79973 535HttpRequest *
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:
b248c2a3 669 handleAdaptedHeader(const_cast<HttpMsg*>(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
fccd4a86 683Client::handleAdaptedHeader(HttpMsg *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{
801 stopConsumingFrom(adaptedBodySource);
a83c6ed6 802 handleAdaptationAborted();
5f8252d2 803}
804
a83c6ed6 805// common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
5f8252d2 806void
fccd4a86 807Client::handleAdaptationCompleted()
5f8252d2 808{
a83c6ed6
AR
809 debugs(11,5, HERE << "handleAdaptationCompleted");
810 cleanAdaptation();
bc81cb2b 811
719c885c 812 // We stop reading origin response because we have no place to put it(*) and
bc81cb2b 813 // cannot use it. If some origin servers do not like that or if we want to
814 // reuse more pconns, we can add code to discard unneeded origin responses.
719c885c
AR
815 // (*) TODO: Is it possible that the adaptation xaction is still running?
816 if (mayReadVirginReplyBody()) {
bc81cb2b 817 debugs(11,3, HERE << "closing origin conn due to ICAP completion");
818 closeServer();
819 }
820
5f8252d2 821 completeForwarding();
5f8252d2 822}
823
a83c6ed6 824// common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
5f8252d2 825void
fccd4a86 826Client::handleAdaptationAborted(bool bypassable)
5f8252d2 827{
a83c6ed6 828 debugs(11,5, HERE << "handleAdaptationAborted; bypassable: " << bypassable <<
9e008dda 829 ", entry empty: " << entry->isEmpty());
5f8252d2 830
831 if (abortOnBadEntry("entry went bad while ICAP aborted"))
832 return;
833
0f283edf 834 // TODO: bypass if possible
835
5f8252d2 836 if (entry->isEmpty()) {
837 debugs(11,9, HERE << "creating ICAP error entry after ICAP failure");
955394ce 838 ErrorState *err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, request);
129fe2a1 839 err->detailError(ERR_DETAIL_ICAP_RESPMOD_EARLY);
5f8252d2 840 fwd->fail(err);
841 fwd->dontRetry(true);
b3c9f64a 842 } else if (request) { // update logged info directly
64b66b76
CT
843 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_ICAP_RESPMOD_LATE);
844 }
5f8252d2 845
0f283edf 846 abortTransaction("ICAP failure");
5f8252d2 847}
848
3af10ac0
AR
849// adaptation service wants us to deny HTTP client access to this response
850void
fccd4a86 851Client::handleAdaptationBlocked(const Adaptation::Answer &answer)
3af10ac0 852{
6666da11 853 debugs(11,5, HERE << answer.ruleId);
3af10ac0
AR
854
855 if (abortOnBadEntry("entry went bad while ICAP aborted"))
856 return;
857
858 if (!entry->isEmpty()) { // too late to block (should not really happen)
ec4d1a1d
A
859 if (request)
860 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_RESPMOD_BLOCK_LATE);
861 abortTransaction("late adaptation block");
862 return;
3af10ac0 863 }
ec4d1a1d 864
3af10ac0
AR
865 debugs(11,7, HERE << "creating adaptation block response");
866
867 err_type page_id =
868 aclGetDenyInfoPage(&Config.denyInfoList, answer.ruleId.termedBuf(), 1);
869 if (page_id == ERR_NONE)
870 page_id = ERR_ACCESS_DENIED;
871
955394ce 872 ErrorState *err = new ErrorState(page_id, Http::scForbidden, request);
129fe2a1 873 err->detailError(ERR_DETAIL_RESPMOD_BLOCK_EARLY);
3af10ac0
AR
874 fwd->fail(err);
875 fwd->dontRetry(true);
876
877 abortTransaction("timely adaptation block");
878}
879
7c4e4e7f 880void
fccd4a86 881Client::noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer group)
7c4e4e7f 882{
a83c6ed6 883 adaptationAccessCheckPending = false;
7c4e4e7f 884
885 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
886 return;
887
d5430dc8
AJ
888 // TODO: Should non-ICAP and ICAP REPMOD pre-cache paths check this?
889 // That check now only happens on REQMOD pre-cache and REPMOD post-cache, in processReplyAccess().
47416555 890 if (virginReply()->expectedBodyTooLarge(*request)) {
891 sendBodyIsTooLargeError();
892 return;
893 }
d5430dc8 894 // TODO: Should we check receivedBodyTooLarge as well?
47416555 895
a22e6cd3
AR
896 if (!group) {
897 debugs(11,3, HERE << "no adapation needed");
585ab260 898 setFinalReply(virginReply());
7c4e4e7f 899 processReplyBody();
7c4e4e7f 900 return;
901 }
902
a22e6cd3 903 startAdaptation(group, originalRequest());
7c4e4e7f 904 processReplyBody();
905}
0c25e715 906#endif
7dc79973 907
47416555 908void
fccd4a86 909Client::sendBodyIsTooLargeError()
47416555 910{
955394ce 911 ErrorState *err = new ErrorState(ERR_TOO_BIG, Http::scForbidden, request);
47416555 912 fwd->fail(err);
913 fwd->dontRetry(true);
914 abortTransaction("Virgin body too large.");
915}
916
9e008dda 917// TODO: when HttpStateData sends all errors to ICAP,
585ab260 918// we should be able to move this at the end of setVirginReply().
7dc79973 919void
fccd4a86 920Client::adaptOrFinalizeReply()
7dc79973 921{
a83c6ed6 922#if USE_ADAPTATION
abd4b611
AR
923 // TODO: merge with client side and return void to hide the on/off logic?
924 // The callback can be called with a NULL service if adaptation is off.
925 adaptationAccessCheckPending = Adaptation::AccessCheck::Start(
9e008dda 926 Adaptation::methodRespmod, Adaptation::pointPreCache,
af0ded40 927 originalRequest(), virginReply(), fwd->al, this);
c30ac6ea 928 debugs(11,5, HERE << "adaptationAccessCheckPending=" << adaptationAccessCheckPending);
abd4b611 929 if (adaptationAccessCheckPending)
7dc79973 930 return;
7dc79973 931#endif
932
585ab260 933 setFinalReply(virginReply());
7dc79973 934}
935
bae917ac
CT
936/// initializes bodyBytesRead stats if needed and applies delta
937void
fccd4a86 938Client::adjustBodyBytesRead(const int64_t delta)
bae917ac
CT
939{
940 int64_t &bodyBytesRead = originalRequest()->hier.bodyBytesRead;
941
942 // if we got here, do not log a dash even if we got nothing from the server
943 if (bodyBytesRead < 0)
944 bodyBytesRead = 0;
945
946 bodyBytesRead += delta; // supports negative and zero deltas
947
948 // check for overflows ("infinite" response?) and undeflows (a bug)
949 Must(bodyBytesRead >= 0);
950}
951
7dc79973 952void
fccd4a86 953Client::addVirginReplyBody(const char *data, ssize_t len)
7dc79973 954{
bae917ac
CT
955 adjustBodyBytesRead(len);
956
a83c6ed6
AR
957#if USE_ADAPTATION
958 assert(!adaptationAccessCheckPending); // or would need to buffer while waiting
959 if (startedAdaptation) {
bc81cb2b 960 adaptVirginReplyBody(data, len);
7dc79973 961 return;
962 }
7dc79973 963#endif
bc81cb2b 964 storeReplyBody(data, len);
965}
7dc79973 966
bc81cb2b 967// writes virgin or adapted reply body to store
968void
fccd4a86 969Client::storeReplyBody(const char *data, ssize_t len)
bc81cb2b 970{
2d1a172f 971 // write even if len is zero to push headers towards the client side
7dc79973 972 entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
973
974 currentOffset += len;
975}
976
f2554f0f 977size_t
5867ac79 978Client::calcBufferSpaceToReserve(size_t space, const size_t wantSpace) const
f2554f0f 979{
5867ac79 980 if (space < wantSpace) {
f2554f0f 981 const size_t maxSpace = SBuf::maxSize; // absolute best
5867ac79 982 space = min(wantSpace, maxSpace); // do not promise more than asked
f2554f0f
AJ
983 }
984
985#if USE_ADAPTATION
986 if (responseBodyBuffer) {
1810a0cb 987 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
f2554f0f
AJ
988 }
989
990 if (virginBodyDestination != NULL) {
991 /*
992 * BodyPipe buffer has a finite size limit. We
993 * should not read more data from the network than will fit
994 * into the pipe buffer or we _lose_ what did not fit if
995 * the response ends sooner that BodyPipe frees up space:
996 * There is no code to keep pumping data into the pipe once
997 * response ends and serverComplete() is called.
f2554f0f 998 */
5867ac79 999 const size_t adaptor_space = virginBodyDestination->buf().potentialSpaceSize();
f2554f0f
AJ
1000
1001 debugs(11,9, "Client may read up to min(" <<
5867ac79 1002 adaptor_space << ", " << space << ") bytes");
f2554f0f 1003
5867ac79
AJ
1004 if (adaptor_space < space)
1005 space = adaptor_space;
f2554f0f
AJ
1006 }
1007#endif
1008
1009 return space;
1010}
1011
1012size_t
1013Client::replyBodySpace(const MemBuf &readBuf, const size_t minSpace) const
7dc79973 1014{
52edecde
AJ
1015 size_t space = readBuf.spaceSize(); // available space w/o heroic measures
1016 if (space < minSpace) {
1017 const size_t maxSpace = readBuf.potentialSpaceSize(); // absolute best
1018 space = min(minSpace, maxSpace); // do not promise more than asked
1019 }
1020
a83c6ed6 1021#if USE_ADAPTATION
7dc79973 1022 if (responseBodyBuffer) {
f53969cc 1023 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
7dc79973 1024 }
1025
1026 if (virginBodyDestination != NULL) {
1027 /*
1028 * BodyPipe buffer has a finite size limit. We
1029 * should not read more data from the network than will fit
1030 * into the pipe buffer or we _lose_ what did not fit if
1031 * the response ends sooner that BodyPipe frees up space:
1032 * There is no code to keep pumping data into the pipe once
1033 * response ends and serverComplete() is called.
1034 *
1035 * If the pipe is totally full, don't register the read handler.
1036 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
1037 * when it has free space again.
1038 */
a83c6ed6
AR
1039 size_t adaptation_space =
1040 virginBodyDestination->buf().potentialSpaceSize();
7dc79973 1041
fccd4a86 1042 debugs(11,9, "Client may read up to min(" <<
9e008dda 1043 adaptation_space << ", " << space << ") bytes");
7dc79973 1044
a83c6ed6
AR
1045 if (adaptation_space < space)
1046 space = adaptation_space;
7dc79973 1047 }
1048#endif
1049
1050 return space;
1051}
f53969cc 1052