]> git.ipfire.org Git - thirdparty/squid.git/blame - src/clients/Client.cc
Parser-NG: HTTP Response Parser upgrade
[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
90bd689c 452purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, HttpMsg *rep, http_hdr_type 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
AJ
495 // XXX: should we use originalRequest() here?
496 const char *reqUrl = urlCanonical(request);
7f06a3d8 497 debugs(88, 5, "maybe purging due to " << request->method << ' ' << reqUrl);
9e008dda
AJ
498 purgeEntriesByUrl(request, reqUrl);
499 purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_LOCATION);
500 purgeEntriesByHeader(request, reqUrl, theFinalReply, HDR_CONTENT_LOCATION);
c1520b67
AJ
501}
502
fad1a21e 503/// called when we have final (possibly adapted) reply headers; kids extend
5f8252d2 504void
fccd4a86 505Client::haveParsedReplyHeaders()
5f8252d2 506{
9e008dda
AJ
507 Must(theFinalReply);
508 maybePurgeOthers();
5120a38b
AR
509
510 // adaptation may overwrite old offset computed using the virgin response
511 const bool partial = theFinalReply->content_range &&
512 theFinalReply->sline.status() == Http::scPartialContent;
513 currentOffset = partial ? theFinalReply->content_range->spec.offset : 0;
5f8252d2 514}
515
70706149
AR
516/// whether to prevent caching of an otherwise cachable response
517bool
fccd4a86 518Client::blockCaching()
70706149
AR
519{
520 if (const Acl::Tree *acl = Config.accessList.storeMiss) {
521 // This relatively expensive check is not in StoreEntry::checkCachable:
522 // That method lacks HttpRequest and may be called too many times.
523 ACLFilledChecklist ch(acl, originalRequest(), NULL);
524 ch.reply = const_cast<HttpReply*>(entry->getReply()); // ACLFilledChecklist API bug
525 HTTPMSGLOCK(ch.reply);
526 if (ch.fastCheck() != ACCESS_ALLOWED) { // when in doubt, block
527 debugs(20, 3, "store_miss prohibits caching");
528 return true;
529 }
530 }
531 return false;
5f8252d2 532}
533
7dc79973 534HttpRequest *
fccd4a86 535Client::originalRequest()
7dc79973 536{
537 return request;
538}
5f8252d2 539
a83c6ed6 540#if USE_ADAPTATION
a22e6cd3
AR
541/// Initiate an asynchronous adaptation transaction which will call us back.
542void
fccd4a86 543Client::startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause)
cd304fc2 544{
fccd4a86 545 debugs(11, 5, "Client::startAdaptation() called");
5f8252d2 546 // check whether we should be sending a body as well
5f8252d2 547 // start body pipe to feed ICAP transaction if needed
585ab260 548 assert(!virginBodyDestination);
9e008dda 549 HttpReply *vrep = virginReply();
585ab260 550 assert(!vrep->body_pipe);
47f6e231 551 int64_t size = 0;
585ab260 552 if (vrep->expectingBody(cause->method, size) && size) {
5f8252d2 553 virginBodyDestination = new BodyPipe(this);
585ab260 554 vrep->body_pipe = virginBodyDestination;
9e008dda
AJ
555 debugs(93, 6, HERE << "will send virgin reply body to " <<
556 virginBodyDestination << "; size: " << size);
c2eef5bd 557 if (size > 0)
558 virginBodyDestination->setBodySize(size);
5f8252d2 559 }
560
a22e6cd3 561 adaptedHeadSource = initiateAdaptation(
af0ded40 562 new Adaptation::Iterator(vrep, cause, fwd->al, group));
4299f876 563 startedAdaptation = initiated(adaptedHeadSource);
a22e6cd3 564 Must(startedAdaptation);
cd304fc2 565}
0c25e715 566
5f8252d2 567// properly cleans up ICAP-related state
568// may be called multiple times
fccd4a86 569void Client::cleanAdaptation()
9e008dda 570{
a83c6ed6 571 debugs(11,5, HERE << "cleaning ICAP; ACL: " << adaptationAccessCheckPending);
5f8252d2 572
573 if (virginBodyDestination != NULL)
574 stopProducingFor(virginBodyDestination, false);
575
0f283edf 576 announceInitiatorAbort(adaptedHeadSource);
5f8252d2 577
578 if (adaptedBodySource != NULL)
579 stopConsumingFrom(adaptedBodySource);
580
a83c6ed6
AR
581 if (!adaptationAccessCheckPending) // we cannot cancel a pending callback
582 assert(doneWithAdaptation()); // make sure the two methods are in sync
5f8252d2 583}
584
585bool
fccd4a86 586Client::doneWithAdaptation() const
9e008dda 587{
a83c6ed6 588 return !adaptationAccessCheckPending &&
9e008dda 589 !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource;
5f8252d2 590}
591
bc81cb2b 592// sends virgin reply body to ICAP, buffering excesses if needed
593void
fccd4a86 594Client::adaptVirginReplyBody(const char *data, ssize_t len)
bc81cb2b 595{
a83c6ed6 596 assert(startedAdaptation);
bc81cb2b 597
598 if (!virginBodyDestination) {
599 debugs(11,3, HERE << "ICAP does not want more virgin body");
600 return;
601 }
602
603 // grow overflow area if already overflowed
604 if (responseBodyBuffer) {
605 responseBodyBuffer->append(data, len);
606 data = responseBodyBuffer->content();
607 len = responseBodyBuffer->contentSize();
608 }
609
610 const ssize_t putSize = virginBodyDestination->putMoreData(data, len);
611 data += putSize;
612 len -= putSize;
613
614 // if we had overflow area, shrink it as necessary
615 if (responseBodyBuffer) {
616 if (putSize == responseBodyBuffer->contentSize()) {
617 delete responseBodyBuffer;
618 responseBodyBuffer = NULL;
619 } else {
620 responseBodyBuffer->consume(putSize);
9e008dda 621 }
bc81cb2b 622 return;
623 }
624
625 // if we did not have an overflow area, create it as needed
626 if (len > 0) {
627 assert(!responseBodyBuffer);
628 responseBodyBuffer = new MemBuf;
629 responseBodyBuffer->init(4096, SQUID_TCP_SO_RCVBUF * 10);
630 responseBodyBuffer->append(data, len);
631 }
632}
633
5f8252d2 634// can supply more virgin response body data
635void
fccd4a86 636Client::noteMoreBodySpaceAvailable(BodyPipe::Pointer)
5f8252d2 637{
7dc79973 638 if (responseBodyBuffer) {
bc81cb2b 639 addVirginReplyBody(NULL, 0); // kick the buffered fragment alive again
640 if (completed && !responseBodyBuffer) {
641 serverComplete2();
642 return;
643 }
7dc79973 644 }
5f8252d2 645 maybeReadVirginBody();
646}
647
bc81cb2b 648// the consumer of our virgin response body aborted
5f8252d2 649void
fccd4a86 650Client::noteBodyConsumerAborted(BodyPipe::Pointer)
5f8252d2 651{
652 stopProducingFor(virginBodyDestination, false);
bc81cb2b 653
a83c6ed6 654 // do not force closeServer here in case we need to bypass AdaptationQueryAbort
bc81cb2b 655
a83c6ed6
AR
656 if (doneWithAdaptation()) // we may still be receiving adapted response
657 handleAdaptationCompleted();
5f8252d2 658}
659
660// received adapted response headers (body may follow)
661void
fccd4a86 662Client::noteAdaptationAnswer(const Adaptation::Answer &answer)
5f8252d2 663{
a83c6ed6 664 clearAdaptation(adaptedHeadSource); // we do not expect more messages
5f8252d2 665
3af10ac0
AR
666 switch (answer.kind) {
667 case Adaptation::Answer::akForward:
b248c2a3 668 handleAdaptedHeader(const_cast<HttpMsg*>(answer.message.getRaw()));
3af10ac0
AR
669 break;
670
671 case Adaptation::Answer::akBlock:
672 handleAdaptationBlocked(answer);
673 break;
674
675 case Adaptation::Answer::akError:
676 handleAdaptationAborted(!answer.final);
677 break;
678 }
679}
680
681void
fccd4a86 682Client::handleAdaptedHeader(HttpMsg *msg)
3af10ac0 683{
1733bbba
CT
684 if (abortOnBadEntry("entry went bad while waiting for adapted headers")) {
685 // If the adapted response has a body, the ICAP side needs to know
686 // that nobody will consume that body. We will be destroyed upon
687 // return. Tell the ICAP side that it is on its own.
688 HttpReply *rep = dynamic_cast<HttpReply*>(msg);
689 assert(rep);
690 if (rep->body_pipe != NULL)
691 rep->body_pipe->expectNoConsumption();
692
5f8252d2 693 return;
1733bbba 694 }
5f8252d2 695
585ab260 696 HttpReply *rep = dynamic_cast<HttpReply*>(msg);
5f8252d2 697 assert(rep);
585ab260 698 debugs(11,5, HERE << this << " setting adapted reply to " << rep);
699 setFinalReply(rep);
5f8252d2 700
701 assert(!adaptedBodySource);
585ab260 702 if (rep->body_pipe != NULL) {
5f8252d2 703 // subscribe to receive adapted body
585ab260 704 adaptedBodySource = rep->body_pipe;
5f8252d2 705 // assume that ICAP does not auto-consume on failures
dfb6dc9d
NH
706 const bool result = adaptedBodySource->setConsumerIfNotLate(this);
707 assert(result);
5f8252d2 708 } else {
709 // no body
a83c6ed6
AR
710 if (doneWithAdaptation()) // we may still be sending virgin response
711 handleAdaptationCompleted();
5f8252d2 712 }
5f8252d2 713}
714
5f8252d2 715void
fccd4a86 716Client::resumeBodyStorage()
5f8252d2 717{
0ad2b63b
CT
718 if (abortOnBadEntry("store entry aborted while kick producer callback"))
719 return;
5f8252d2 720
e83cdc25 721 if (!adaptedBodySource)
0ad2b63b
CT
722 return;
723
724 handleMoreAdaptedBodyAvailable();
725
726 if (adaptedBodySource != NULL && adaptedBodySource->exhausted())
727 endAdaptedBodyConsumption();
728}
5f8252d2 729
0ad2b63b
CT
730// more adapted response body is available
731void
fccd4a86 732Client::handleMoreAdaptedBodyAvailable()
0ad2b63b 733{
5f8252d2 734 if (abortOnBadEntry("entry refuses adapted body"))
735 return;
736
737 assert(entry);
0ad2b63b
CT
738
739 size_t contentSize = adaptedBodySource->buf().contentSize();
0ad2b63b
CT
740
741 if (!contentSize)
742 return; // XXX: bytesWanted asserts on zero-size ranges
743
384a7590 744 const size_t spaceAvailable = entry->bytesWanted(Range<size_t>(0, contentSize), true);
0ad2b63b 745
e83cdc25 746 if (spaceAvailable < contentSize ) {
0ad2b63b 747 // No or partial body data consuming
fccd4a86
AJ
748 typedef NullaryMemFunT<Client> Dialer;
749 AsyncCall::Pointer call = asyncCall(93, 5, "Client::resumeBodyStorage",
750 Dialer(this, &Client::resumeBodyStorage));
0ad2b63b
CT
751 entry->deferProducer(call);
752 }
753
4dc2b072 754 if (!spaceAvailable) {
0ad2b63b
CT
755 debugs(11, 5, HERE << "NOT storing " << contentSize << " bytes of adapted " <<
756 "response body at offset " << adaptedBodySource->consumedSize());
757 return;
758 }
e83cdc25 759
0ad2b63b
CT
760 if (spaceAvailable < contentSize ) {
761 debugs(11, 5, HERE << "postponing storage of " <<
762 (contentSize - spaceAvailable) << " body bytes");
763 contentSize = spaceAvailable;
0ad2b63b 764 }
e83cdc25 765
0ad2b63b
CT
766 debugs(11,5, HERE << "storing " << contentSize << " bytes of adapted " <<
767 "response body at offset " << adaptedBodySource->consumedSize());
e83cdc25 768
5f8252d2 769 BodyPipeCheckout bpc(*adaptedBodySource);
0ad2b63b
CT
770 const StoreIOBuffer ioBuf(&bpc.buf, currentOffset, contentSize);
771 currentOffset += ioBuf.length;
5f8252d2 772 entry->write(ioBuf);
773 bpc.buf.consume(contentSize);
774 bpc.checkIn();
775}
776
777// the entire adapted response body was produced, successfully
778void
fccd4a86 779Client::handleAdaptedBodyProductionEnded()
5f8252d2 780{
5f8252d2 781 if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
782 return;
e83cdc25
A
783
784 // end consumption if we consumed everything
0ad2b63b
CT
785 if (adaptedBodySource != NULL && adaptedBodySource->exhausted())
786 endAdaptedBodyConsumption();
787 // else resumeBodyStorage() will eventually consume the rest
788}
5f8252d2 789
0ad2b63b 790void
fccd4a86 791Client::endAdaptedBodyConsumption()
0ad2b63b
CT
792{
793 stopConsumingFrom(adaptedBodySource);
a83c6ed6 794 handleAdaptationCompleted();
5f8252d2 795}
796
797// premature end of the adapted response body
fccd4a86 798void Client::handleAdaptedBodyProducerAborted()
5f8252d2 799{
800 stopConsumingFrom(adaptedBodySource);
a83c6ed6 801 handleAdaptationAborted();
5f8252d2 802}
803
a83c6ed6 804// common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
5f8252d2 805void
fccd4a86 806Client::handleAdaptationCompleted()
5f8252d2 807{
a83c6ed6
AR
808 debugs(11,5, HERE << "handleAdaptationCompleted");
809 cleanAdaptation();
bc81cb2b 810
719c885c 811 // We stop reading origin response because we have no place to put it(*) and
bc81cb2b 812 // cannot use it. If some origin servers do not like that or if we want to
813 // reuse more pconns, we can add code to discard unneeded origin responses.
719c885c
AR
814 // (*) TODO: Is it possible that the adaptation xaction is still running?
815 if (mayReadVirginReplyBody()) {
bc81cb2b 816 debugs(11,3, HERE << "closing origin conn due to ICAP completion");
817 closeServer();
818 }
819
5f8252d2 820 completeForwarding();
5f8252d2 821}
822
a83c6ed6 823// common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
5f8252d2 824void
fccd4a86 825Client::handleAdaptationAborted(bool bypassable)
5f8252d2 826{
a83c6ed6 827 debugs(11,5, HERE << "handleAdaptationAborted; bypassable: " << bypassable <<
9e008dda 828 ", entry empty: " << entry->isEmpty());
5f8252d2 829
830 if (abortOnBadEntry("entry went bad while ICAP aborted"))
831 return;
832
0f283edf 833 // TODO: bypass if possible
834
5f8252d2 835 if (entry->isEmpty()) {
836 debugs(11,9, HERE << "creating ICAP error entry after ICAP failure");
955394ce 837 ErrorState *err = new ErrorState(ERR_ICAP_FAILURE, Http::scInternalServerError, request);
129fe2a1 838 err->detailError(ERR_DETAIL_ICAP_RESPMOD_EARLY);
5f8252d2 839 fwd->fail(err);
840 fwd->dontRetry(true);
b3c9f64a 841 } else if (request) { // update logged info directly
64b66b76
CT
842 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_ICAP_RESPMOD_LATE);
843 }
5f8252d2 844
0f283edf 845 abortTransaction("ICAP failure");
5f8252d2 846}
847
3af10ac0
AR
848// adaptation service wants us to deny HTTP client access to this response
849void
fccd4a86 850Client::handleAdaptationBlocked(const Adaptation::Answer &answer)
3af10ac0 851{
6666da11 852 debugs(11,5, HERE << answer.ruleId);
3af10ac0
AR
853
854 if (abortOnBadEntry("entry went bad while ICAP aborted"))
855 return;
856
857 if (!entry->isEmpty()) { // too late to block (should not really happen)
ec4d1a1d
A
858 if (request)
859 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_RESPMOD_BLOCK_LATE);
860 abortTransaction("late adaptation block");
861 return;
3af10ac0 862 }
ec4d1a1d 863
3af10ac0
AR
864 debugs(11,7, HERE << "creating adaptation block response");
865
866 err_type page_id =
867 aclGetDenyInfoPage(&Config.denyInfoList, answer.ruleId.termedBuf(), 1);
868 if (page_id == ERR_NONE)
869 page_id = ERR_ACCESS_DENIED;
870
955394ce 871 ErrorState *err = new ErrorState(page_id, Http::scForbidden, request);
129fe2a1 872 err->detailError(ERR_DETAIL_RESPMOD_BLOCK_EARLY);
3af10ac0
AR
873 fwd->fail(err);
874 fwd->dontRetry(true);
875
876 abortTransaction("timely adaptation block");
877}
878
7c4e4e7f 879void
fccd4a86 880Client::noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer group)
7c4e4e7f 881{
a83c6ed6 882 adaptationAccessCheckPending = false;
7c4e4e7f 883
884 if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
885 return;
886
d5430dc8
AJ
887 // TODO: Should non-ICAP and ICAP REPMOD pre-cache paths check this?
888 // That check now only happens on REQMOD pre-cache and REPMOD post-cache, in processReplyAccess().
47416555 889 if (virginReply()->expectedBodyTooLarge(*request)) {
890 sendBodyIsTooLargeError();
891 return;
892 }
d5430dc8 893 // TODO: Should we check receivedBodyTooLarge as well?
47416555 894
a22e6cd3
AR
895 if (!group) {
896 debugs(11,3, HERE << "no adapation needed");
585ab260 897 setFinalReply(virginReply());
7c4e4e7f 898 processReplyBody();
7c4e4e7f 899 return;
900 }
901
a22e6cd3 902 startAdaptation(group, originalRequest());
7c4e4e7f 903 processReplyBody();
904}
0c25e715 905#endif
7dc79973 906
47416555 907void
fccd4a86 908Client::sendBodyIsTooLargeError()
47416555 909{
955394ce 910 ErrorState *err = new ErrorState(ERR_TOO_BIG, Http::scForbidden, request);
47416555 911 fwd->fail(err);
912 fwd->dontRetry(true);
913 abortTransaction("Virgin body too large.");
914}
915
9e008dda 916// TODO: when HttpStateData sends all errors to ICAP,
585ab260 917// we should be able to move this at the end of setVirginReply().
7dc79973 918void
fccd4a86 919Client::adaptOrFinalizeReply()
7dc79973 920{
a83c6ed6 921#if USE_ADAPTATION
abd4b611
AR
922 // TODO: merge with client side and return void to hide the on/off logic?
923 // The callback can be called with a NULL service if adaptation is off.
924 adaptationAccessCheckPending = Adaptation::AccessCheck::Start(
9e008dda 925 Adaptation::methodRespmod, Adaptation::pointPreCache,
af0ded40 926 originalRequest(), virginReply(), fwd->al, this);
c30ac6ea 927 debugs(11,5, HERE << "adaptationAccessCheckPending=" << adaptationAccessCheckPending);
abd4b611 928 if (adaptationAccessCheckPending)
7dc79973 929 return;
7dc79973 930#endif
931
585ab260 932 setFinalReply(virginReply());
7dc79973 933}
934
bae917ac
CT
935/// initializes bodyBytesRead stats if needed and applies delta
936void
fccd4a86 937Client::adjustBodyBytesRead(const int64_t delta)
bae917ac
CT
938{
939 int64_t &bodyBytesRead = originalRequest()->hier.bodyBytesRead;
940
941 // if we got here, do not log a dash even if we got nothing from the server
942 if (bodyBytesRead < 0)
943 bodyBytesRead = 0;
944
945 bodyBytesRead += delta; // supports negative and zero deltas
946
947 // check for overflows ("infinite" response?) and undeflows (a bug)
948 Must(bodyBytesRead >= 0);
949}
950
7dc79973 951void
fccd4a86 952Client::addVirginReplyBody(const char *data, ssize_t len)
7dc79973 953{
bae917ac
CT
954 adjustBodyBytesRead(len);
955
a83c6ed6
AR
956#if USE_ADAPTATION
957 assert(!adaptationAccessCheckPending); // or would need to buffer while waiting
958 if (startedAdaptation) {
bc81cb2b 959 adaptVirginReplyBody(data, len);
7dc79973 960 return;
961 }
7dc79973 962#endif
bc81cb2b 963 storeReplyBody(data, len);
964}
7dc79973 965
bc81cb2b 966// writes virgin or adapted reply body to store
967void
fccd4a86 968Client::storeReplyBody(const char *data, ssize_t len)
bc81cb2b 969{
2d1a172f 970 // write even if len is zero to push headers towards the client side
7dc79973 971 entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
972
973 currentOffset += len;
974}
975
f2554f0f 976size_t
5867ac79 977Client::calcBufferSpaceToReserve(size_t space, const size_t wantSpace) const
f2554f0f 978{
5867ac79 979 if (space < wantSpace) {
f2554f0f 980 const size_t maxSpace = SBuf::maxSize; // absolute best
5867ac79 981 space = min(wantSpace, maxSpace); // do not promise more than asked
f2554f0f
AJ
982 }
983
984#if USE_ADAPTATION
985 if (responseBodyBuffer) {
986 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
987 }
988
989 if (virginBodyDestination != NULL) {
990 /*
991 * BodyPipe buffer has a finite size limit. We
992 * should not read more data from the network than will fit
993 * into the pipe buffer or we _lose_ what did not fit if
994 * the response ends sooner that BodyPipe frees up space:
995 * There is no code to keep pumping data into the pipe once
996 * response ends and serverComplete() is called.
f2554f0f 997 */
5867ac79 998 const size_t adaptor_space = virginBodyDestination->buf().potentialSpaceSize();
f2554f0f
AJ
999
1000 debugs(11,9, "Client may read up to min(" <<
5867ac79 1001 adaptor_space << ", " << space << ") bytes");
f2554f0f 1002
5867ac79
AJ
1003 if (adaptor_space < space)
1004 space = adaptor_space;
f2554f0f
AJ
1005 }
1006#endif
1007
1008 return space;
1009}
1010
1011size_t
1012Client::replyBodySpace(const MemBuf &readBuf, const size_t minSpace) const
7dc79973 1013{
52edecde
AJ
1014 size_t space = readBuf.spaceSize(); // available space w/o heroic measures
1015 if (space < minSpace) {
1016 const size_t maxSpace = readBuf.potentialSpaceSize(); // absolute best
1017 space = min(minSpace, maxSpace); // do not promise more than asked
1018 }
1019
a83c6ed6 1020#if USE_ADAPTATION
7dc79973 1021 if (responseBodyBuffer) {
f53969cc 1022 return 0; // Stop reading if already overflowed waiting for ICAP to catch up
7dc79973 1023 }
1024
1025 if (virginBodyDestination != NULL) {
1026 /*
1027 * BodyPipe buffer has a finite size limit. We
1028 * should not read more data from the network than will fit
1029 * into the pipe buffer or we _lose_ what did not fit if
1030 * the response ends sooner that BodyPipe frees up space:
1031 * There is no code to keep pumping data into the pipe once
1032 * response ends and serverComplete() is called.
1033 *
1034 * If the pipe is totally full, don't register the read handler.
1035 * The BodyPipe will call our noteMoreBodySpaceAvailable() method
1036 * when it has free space again.
1037 */
a83c6ed6
AR
1038 size_t adaptation_space =
1039 virginBodyDestination->buf().potentialSpaceSize();
7dc79973 1040
fccd4a86 1041 debugs(11,9, "Client may read up to min(" <<
9e008dda 1042 adaptation_space << ", " << space << ") bytes");
7dc79973 1043
a83c6ed6
AR
1044 if (adaptation_space < space)
1045 space = adaptation_space;
7dc79973 1046 }
1047#endif
1048
1049 return space;
1050}
f53969cc 1051