Client.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3  *
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.
7  */
8 
9 #include "squid.h"
10 #include "acl/FilledChecklist.h"
11 #include "acl/Gadgets.h"
12 #include "base/TextException.h"
13 #include "clients/Client.h"
14 #include "comm/Connection.h"
15 #include "comm/forward.h"
16 #include "comm/Write.h"
17 #include "error/Detail.h"
18 #include "errorpage.h"
19 #include "fd.h"
20 #include "HttpHdrContRange.h"
21 #include "HttpReply.h"
22 #include "HttpRequest.h"
23 #include "SquidConfig.h"
24 #include "StatCounters.h"
25 #include "Store.h"
26 #include "tools.h"
27 
28 #if USE_ADAPTATION
29 #include "adaptation/AccessCheck.h"
30 #include "adaptation/Answer.h"
31 #include "adaptation/Iterator.h"
32 #include "base/AsyncCall.h"
33 #endif
34 
35 // implemented in client_side_reply.cc until sides have a common parent
36 void purgeEntriesByUrl(HttpRequest * req, const char *url);
37 
38 Client::Client(FwdState *theFwdState) :
39  AsyncJob("Client"),
40  fwd(theFwdState),
41  request(fwd->request)
42 {
43  entry = fwd->entry;
44  entry->lock("Client");
45 }
46 
48 {
49  // paranoid: check that swanSong has been called
51 #if USE_ADAPTATION
54 #endif
55 
56  entry->unlock("Client");
57 
60 
61  if (responseBodyBuffer != nullptr) {
62  delete responseBodyBuffer;
63  responseBodyBuffer = nullptr;
64  }
65 }
66 
67 void
69 {
70  // get rid of our piping obligations
71  if (requestBodySource != nullptr)
73 
74 #if USE_ADAPTATION
76 #endif
77 
78  if (!doneWithServer())
79  closeServer();
80 
81  if (!doneWithFwd) {
82  doneWithFwd = "swanSong()";
84  }
85 
87 #if USE_ADAPTATION
88  Initiator::swanSong();
90 #endif
91 
92  // paranoid: check that swanSong has been called
93  // extra paranoid: yeah, I really mean it. they MUST pass here.
95 #if USE_ADAPTATION
98 #endif
99 }
100 
101 HttpReply *
103 {
105  return theVirginReply;
106 }
107 
108 const HttpReply *
109 Client::virginReply() const
110 {
112  return theVirginReply;
113 }
114 
115 HttpReply *
117 {
118  debugs(11,5, this << " setting virgin reply to " << rep);
120  assert(rep);
121  theVirginReply = rep;
123  if (fwd->al)
125  return theVirginReply;
126 }
127 
128 HttpReply *
130 {
132  return theFinalReply;
133 }
134 
135 HttpReply *
137 {
138  debugs(11,5, this << " setting final reply to " << rep);
139 
141  assert(rep);
142  theFinalReply = rep;
144  if (fwd->al)
146 
147  // give entry the reply because haveParsedReplyHeaders() expects it there
148  entry->replaceHttpReply(theFinalReply, false); // but do not write yet
149  haveParsedReplyHeaders(); // update the entry/reply (e.g., set timestamps)
151  entry->release();
152  entry->startWriting(); // write the updated entry to store
153 
154  return theFinalReply;
155 }
156 
157 void
158 Client::markParsedVirginReplyAsWhole(const char *reasonWeAreSure)
159 {
160  assert(reasonWeAreSure);
161  debugs(11, 3, reasonWeAreSure);
162  markedParsedVirginReplyAsWhole = reasonWeAreSure;
163 }
164 
165 // called when no more server communication is expected; may quit
166 void
168 {
169  debugs(11,5, "serverComplete " << this);
170 
171  if (!doneWithServer()) {
172  closeServer();
174  }
175 
176  completed = true;
177 
178  if (requestBodySource != nullptr)
180 
181  if (responseBodyBuffer != nullptr)
182  return;
183 
184  serverComplete2();
185 }
186 
187 void
189 {
190  debugs(11,5, "serverComplete2 " << this);
191 
192 #if USE_ADAPTATION
193  if (virginBodyDestination != nullptr)
195 
196  if (!doneWithAdaptation())
197  return;
198 #endif
199 
201 }
202 
203 bool Client::doneAll() const
204 {
205  return doneWithServer() &&
206 #if USE_ADAPTATION
207  doneWithAdaptation() &&
210 #endif
212 }
213 
214 // FTP side overloads this to work around multiple calls to fwd->complete
215 void
217 {
218  debugs(11,5, "completing forwarding for " << fwd);
219  assert(fwd != nullptr);
220 
221  auto storedWholeReply = markedParsedVirginReplyAsWhole;
222 #if USE_ADAPTATION
223  // This precondition is necessary for its two implications:
224  // * We cannot be waiting to decide whether to adapt this response. Thus,
225  // the startedAdaptation check below correctly detects all adaptation
226  // cases (i.e. it does not miss adaptationAccessCheckPending ones).
227  // * We cannot be waiting to consume/store received adapted response bytes.
228  // Thus, receivedWholeAdaptedReply implies that we stored everything.
230 
231  if (startedAdaptation)
232  storedWholeReply = receivedWholeAdaptedReply ? "receivedWholeAdaptedReply" : nullptr;
233 #endif
234 
235  if (storedWholeReply)
236  fwd->markStoredReplyAsWhole(storedWholeReply);
237 
238  doneWithFwd = "completeForwarding()";
239  fwd->complete();
240 }
241 
242 // Register to receive request body
244 {
246  assert(r->body_pipe != nullptr);
249  debugs(11,3, "expecting request body from " <<
251  return true;
252  }
253 
254  debugs(11,3, "aborting on partially consumed request body: " <<
256  requestBodySource = nullptr;
257  return false;
258 }
259 
260 // Entry-dependent callbacks use this check to quit if the entry went bad
261 bool
262 Client::abortOnBadEntry(const char *abortReason)
263 {
264  if (entry->isAccepting())
265  return false;
266 
267  debugs(11,5, "entry is not Accepting!");
268  abortOnData(abortReason);
269  return true;
270 }
271 
272 // more request or adapted response body is available
273 void
275 {
276 #if USE_ADAPTATION
277  if (adaptedBodySource == bp) {
279  return;
280  }
281 #endif
282  if (requestBodySource == bp)
284 }
285 
286 // the entire request or adapted response body was provided, successfully
287 void
289 {
290 #if USE_ADAPTATION
291  if (adaptedBodySource == bp) {
293  return;
294  }
295 #endif
296  if (requestBodySource == bp)
298 }
299 
300 // premature end of the request or adapted response body production
301 void
303 {
304 #if USE_ADAPTATION
305  if (adaptedBodySource == bp) {
307  return;
308  }
309 #endif
310  if (requestBodySource == bp)
312 }
313 
314 bool
315 Client::abortOnData(const char *reason)
316 {
317  abortAll(reason);
318  return true;
319 }
320 
321 // more origin request body data is available
322 void
324 {
325  if (!requestSender)
327  else
328  debugs(9,3, "waiting for request body write to complete");
329 }
330 
331 // there will be no more handleMoreRequestBodyAvailable calls
332 void
334 {
336  if (!requestSender)
338  else
339  debugs(9,3, "waiting for request body write to complete");
340 }
341 
342 // called when we are done sending request body; kids extend this
343 void
345 {
346  debugs(9,3, "done sending request body");
347  assert(requestBodySource != nullptr);
349 
350  // kids extend this
351 }
352 
353 // called when body producers aborts; kids extend this
354 void
356 {
357  if (requestSender != nullptr)
358  debugs(9,3, "fyi: request body aborted while we were sending");
359 
360  fwd->dontRetry(true); // the problem is not with the server
361  stopConsumingFrom(requestBodySource); // requestSender, if any, will notice
362 
363  // kids extend this
364 }
365 
366 // called when we wrote request headers(!) or a part of the body
367 void
369 {
370  debugs(11, 5, "sentRequestBody: FD " << io.fd << ": size " << io.size << ": errflag " << io.flag << ".");
371  debugs(32,3, "sentRequestBody called");
372 
373  requestSender = nullptr;
374 
375  if (io.size > 0) {
377  statCounter.server.all.kbytes_out += io.size;
378  // kids should increment their counters
379  }
380 
381  if (io.flag == Comm::ERR_CLOSING)
382  return;
383 
384  if (!requestBodySource) {
385  debugs(9,3, "detected while-we-were-sending abort");
386  return; // do nothing;
387  }
388 
389  // both successful and failed writes affect response times
391 
392  if (io.flag) {
393  debugs(11, DBG_IMPORTANT, "ERROR: sentRequestBody failure: FD " << io.fd << ": " << xstrerr(io.xerrno));
394  ErrorState *err;
396  err->xerrno = io.xerrno;
397  fwd->fail(err);
398  abortOnData("I/O error while sending request body");
399  return;
400  }
401 
403  abortOnData("store entry aborted while sending request body");
404  return;
405  }
406 
409  else if (receivedWholeRequestBody)
411  else
412  debugs(9,3, "waiting for body production end or abort");
413 }
414 
415 void
417 {
418  assert(requestBodySource != nullptr);
420 
422 
423  if (!Comm::IsConnOpen(conn)) {
424  debugs(9,3, "cannot send request body to closing " << conn);
425  return; // wait for the kid's close handler; TODO: assert(closer);
426  }
427 
428  MemBuf buf;
429  if (getMoreRequestBody(buf) && buf.contentSize() > 0) {
430  debugs(9,3, "will write " << buf.contentSize() << " request body bytes");
432  requestSender = JobCallback(93,3, Dialer, this, Client::sentRequestBody);
433  Comm::Write(conn, &buf, requestSender);
434  } else {
435  debugs(9,3, "will wait for more request body bytes or eof");
436  requestSender = nullptr;
437  }
438 }
439 
441 bool
443 {
444  // default implementation does not encode request body content
445  Must(requestBodySource != nullptr);
446  return requestBodySource->getMoreData(buf);
447 }
448 
449 // Compares hosts in urls, returns false if different, no sheme, or no host.
450 static bool
451 sameUrlHosts(const char *url1, const char *url2)
452 {
453  // XXX: Want AnyP::Uri::parse() here, but it uses static storage and copying
454  const char *host1 = strchr(url1, ':');
455  const char *host2 = strchr(url2, ':');
456 
457  if (host1 && host2) {
458  // skip scheme slashes
459  do {
460  ++host1;
461  ++host2;
462  } while (*host1 == '/' && *host2 == '/');
463 
464  if (!*host1)
465  return false; // no host
466 
467  // increment while the same until we reach the end of the URL/host
468  while (*host1 && *host1 != '/' && *host1 == *host2) {
469  ++host1;
470  ++host2;
471  }
472  return *host1 == *host2;
473  }
474 
475  return false; // no URL scheme
476 }
477 
478 // purges entries that match the value of a given HTTP [response] header
479 static void
480 purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, Http::Message *rep, Http::HdrType hdr)
481 {
482  const auto hdrUrl = rep->header.getStr(hdr);
483  if (!hdrUrl)
484  return;
485 
486  /*
487  * If the URL is relative, make it absolute so we can find it.
488  * If it's absolute, make sure the host parts match to avoid DOS attacks
489  * as per RFC 2616 13.10.
490  */
491  SBuf absUrlMaker;
492  const char *absUrl = nullptr;
493  if (urlIsRelative(hdrUrl)) {
494  if (req->method.id() == Http::METHOD_CONNECT)
495  absUrl = hdrUrl; // TODO: merge authority-uri and hdrUrl
496  else if (req->url.getScheme() == AnyP::PROTO_URN)
497  absUrl = req->url.absolute().c_str();
498  else {
499  AnyP::Uri tmpUrl = req->url;
500  if (*hdrUrl == '/') {
501  // RFC 3986 section 4.2: absolute-path reference
502  // for this logic replace the entire request-target URI path
503  tmpUrl.path(hdrUrl);
504  } else {
505  tmpUrl.addRelativePath(reqUrl);
506  }
507  absUrlMaker = tmpUrl.absolute();
508  absUrl = absUrlMaker.c_str();
509  }
510  } else if (!sameUrlHosts(reqUrl, hdrUrl)) {
511  return;
512  } else
513  absUrl = hdrUrl;
514 
515  purgeEntriesByUrl(req, absUrl);
516 }
517 
518 // some HTTP methods should purge matching cache entries
519 void
521 {
522  // only some HTTP methods should purge matching cache entries
523  if (!request->method.purgesOthers())
524  return;
525 
526  // and probably only if the response was successful
527  if (theFinalReply->sline.status() >= 400)
528  return;
529 
530  // XXX: should we use originalRequest() here?
532  const char *reqUrl = tmp.c_str();
533  debugs(88, 5, "maybe purging due to " << request->method << ' ' << tmp);
534  purgeEntriesByUrl(request.getRaw(), reqUrl);
537 }
538 
540 void
542 {
545 
546  // adaptation may overwrite old offset computed using the virgin response
547  currentOffset = 0;
548  if (const auto cr = theFinalReply->contentRange()) {
549  if (cr->spec.offset != HttpHdrRangeSpec::UnknownPosition)
550  currentOffset = cr->spec.offset;
551  }
552 }
553 
555 bool
557 {
558  if (const auto acl = Config.accessList.storeMiss) {
559  // This relatively expensive check is not in StoreEntry::checkCachable:
560  // That method lacks HttpRequest and may be called too many times.
561  ACLFilledChecklist ch(acl, originalRequest().getRaw());
562  ch.updateAle(fwd->al);
564  if (!ch.fastCheck().allowed()) { // when in doubt, block
565  debugs(20, 3, "store_miss prohibits caching");
566  return true;
567  }
568  }
569  return false;
570 }
571 
574 {
575  return request;
576 }
577 
578 #if USE_ADAPTATION
579 void
582 {
583  debugs(11, 5, "Client::startAdaptation() called");
584  // check whether we should be sending a body as well
585  // start body pipe to feed ICAP transaction if needed
587  HttpReply *vrep = virginReply();
588  assert(!vrep->body_pipe);
589  int64_t size = 0;
590  if (vrep->expectingBody(cause->method, size) && size) {
591  virginBodyDestination = new BodyPipe(this);
593  debugs(93, 6, "will send virgin reply body to " <<
594  virginBodyDestination << "; size: " << size);
595  if (size > 0)
597  }
598 
600  new Adaptation::Iterator(vrep, cause, fwd->al, group));
603 }
604 
605 // properly cleans up ICAP-related state
606 // may be called multiple times
608 {
609  debugs(11,5, "cleaning ICAP; ACL: " << adaptationAccessCheckPending);
610 
611  if (virginBodyDestination != nullptr)
613 
615 
616  if (adaptedBodySource != nullptr)
618 
619  if (!adaptationAccessCheckPending) // we cannot cancel a pending callback
620  assert(doneWithAdaptation()); // make sure the two methods are in sync
621 }
622 
623 bool
625 {
628 }
629 
630 // sends virgin reply body to ICAP, buffering excesses if needed
631 void
632 Client::adaptVirginReplyBody(const char *data, ssize_t len)
633 {
635 
636  if (!virginBodyDestination) {
637  debugs(11,3, "ICAP does not want more virgin body");
638  return;
639  }
640 
641  // grow overflow area if already overflowed
642  if (responseBodyBuffer) {
643  responseBodyBuffer->append(data, len);
644  data = responseBodyBuffer->content();
646  }
647 
648  const ssize_t putSize = virginBodyDestination->putMoreData(data, len);
649  data += putSize;
650  len -= putSize;
651 
652  // if we had overflow area, shrink it as necessary
653  if (responseBodyBuffer) {
654  if (putSize == responseBodyBuffer->contentSize()) {
655  delete responseBodyBuffer;
656  responseBodyBuffer = nullptr;
657  } else {
658  responseBodyBuffer->consume(putSize);
659  }
660  return;
661  }
662 
663  // if we did not have an overflow area, create it as needed
664  if (len > 0) {
667  responseBodyBuffer->init(4096, SQUID_TCP_SO_RCVBUF * 10);
668  responseBodyBuffer->append(data, len);
669  }
670 }
671 
672 // can supply more virgin response body data
673 void
675 {
676  if (responseBodyBuffer) {
677  addVirginReplyBody(nullptr, 0); // kick the buffered fragment alive again
678  if (completed && !responseBodyBuffer) {
679  serverComplete2();
680  return;
681  }
682  }
684 }
685 
686 // the consumer of our virgin response body aborted
687 void
689 {
691 
692  // do not force closeServer here in case we need to bypass AdaptationQueryAbort
693 
694  if (doneWithAdaptation()) // we may still be receiving adapted response
696 }
697 
698 // received adapted response headers (body may follow)
699 void
701 {
702  clearAdaptation(adaptedHeadSource); // we do not expect more messages
703 
704  switch (answer.kind) {
706  handleAdaptedHeader(const_cast<Http::Message*>(answer.message.getRaw()));
707  break;
708 
710  handleAdaptationBlocked(answer);
711  break;
712 
715  break;
716  }
717 }
718 
719 void
721 {
722  if (abortOnBadEntry("entry went bad while waiting for adapted headers")) {
723  // If the adapted response has a body, the ICAP side needs to know
724  // that nobody will consume that body. We will be destroyed upon
725  // return. Tell the ICAP side that it is on its own.
726  HttpReply *rep = dynamic_cast<HttpReply*>(msg);
727  assert(rep);
728  if (rep->body_pipe != nullptr)
730 
731  return;
732  }
733 
734  HttpReply *rep = dynamic_cast<HttpReply*>(msg);
735  assert(rep);
736  debugs(11,5, this << " setting adapted reply to " << rep);
737  setFinalReply(rep);
738 
740  if (rep->body_pipe != nullptr) {
741  // subscribe to receive adapted body
743  // assume that ICAP does not auto-consume on failures
744  const bool result = adaptedBodySource->setConsumerIfNotLate(this);
745  assert(result);
747  } else {
748  // no body
751  if (doneWithAdaptation()) // we may still be sending virgin response
753  }
754 }
755 
756 void
758 {
759  if (abortOnBadEntry("store entry aborted while kick producer callback"))
760  return;
761 
762  if (!adaptedBodySource)
763  return;
764 
766 
768 }
769 
770 // more adapted response body is available
771 void
773 {
774  if (abortOnBadEntry("entry refuses adapted body"))
775  return;
776 
777  assert(entry);
778 
779  size_t contentSize = adaptedBodySource->buf().contentSize();
780 
781  if (!contentSize)
782  return; // XXX: bytesWanted asserts on zero-size ranges
783 
784  const size_t spaceAvailable = entry->bytesWanted(Range<size_t>(0, contentSize), true);
785 
786  if (spaceAvailable < contentSize ) {
787  // No or partial body data consuming
788  typedef NullaryMemFunT<Client> Dialer;
789  AsyncCall::Pointer call = asyncCall(93, 5, "Client::resumeBodyStorage",
790  Dialer(this, &Client::resumeBodyStorage));
791  entry->deferProducer(call);
792  }
793 
794  if (!spaceAvailable) {
795  debugs(11, 5, "NOT storing " << contentSize << " bytes of adapted " <<
796  "response body at offset " << adaptedBodySource->consumedSize());
797  return;
798  }
799 
800  if (spaceAvailable < contentSize ) {
801  debugs(11, 5, "postponing storage of " <<
802  (contentSize - spaceAvailable) << " body bytes");
803  contentSize = spaceAvailable;
804  }
805 
806  debugs(11,5, "storing " << contentSize << " bytes of adapted " <<
807  "response body at offset " << adaptedBodySource->consumedSize());
808 
810  const StoreIOBuffer ioBuf(&bpc.buf, currentOffset, contentSize);
811  currentOffset += ioBuf.length;
812  entry->write(ioBuf);
813  bpc.buf.consume(contentSize);
814  bpc.checkIn();
815 }
816 
817 // the entire adapted response body was produced, successfully
818 void
820 {
821  if (abortOnBadEntry("entry went bad while waiting for adapted body eof"))
822  return;
823 
826 
828 }
829 
830 void
832 {
833  if (!adaptedBodySource) {
834  debugs(11, 7, "not consuming; " << startedAdaptation);
835  return;
836  }
837 
839  // wait for noteBodyProductionEnded() or noteBodyProducerAborted()
840  // because completeForwarding() needs to know whether we receivedWholeAdaptedReply
841  debugs(11, 7, "waiting for adapted body production ending");
842  return;
843  }
844 
845  if (!adaptedBodySource->exhausted()) {
846  debugs(11, 5, "waiting to consume the remainder of the adapted body from " << adaptedBodySource->status());
847  return; // resumeBodyStorage() should eventually consume the rest
848  }
849 
851 
852  if (doneWithAdaptation()) // we may still be sending virgin response
854 }
855 
856 // premature end of the adapted response body
858 {
859  if (abortOnBadEntry("entry went bad while waiting for the now-aborted adapted body"))
860  return;
861 
863  adaptedReplyAborted = true;
864  Must(adaptedBodySource != nullptr);
865  if (!adaptedBodySource->exhausted()) {
866  debugs(11,5, "waiting to consume the remainder of the aborted adapted body");
867  return; // resumeBodyStorage() should eventually consume the rest
868  }
869 
871  return;
872 
873  checkAdaptationWithBodyCompletion(); // the user should get a truncated response
874 }
875 
876 // common part of noteAdaptationAnswer and handleAdaptedBodyProductionEnded
877 void
879 {
880  debugs(11,5, "handleAdaptationCompleted");
881  cleanAdaptation();
882 
883  // We stop reading origin response because we have no place to put it(*) and
884  // cannot use it. If some origin servers do not like that or if we want to
885  // reuse more pconns, we can add code to discard unneeded origin responses.
886  // (*) TODO: Is it possible that the adaptation xaction is still running?
887  if (mayReadVirginReplyBody()) {
888  debugs(11,3, "closing origin conn due to ICAP completion");
889  closeServer();
890  }
891 
893 }
894 
895 // common part of noteAdaptation*Aborted and noteBodyConsumerAborted methods
896 void
898 {
899  debugs(11,5, "handleAdaptationAborted; bypassable: " << bypassable <<
900  ", entry empty: " << entry->isEmpty());
901 
902  if (abortOnBadEntry("entry went bad while ICAP aborted"))
903  return;
904 
905  // TODO: bypass if possible
907  abortAll("adaptation failure with a filled entry");
908 }
909 
912 bool
914 {
915  if (entry->isEmpty()) {
916  debugs(11,8, "adaptation failure with an empty entry: " << *entry);
918  static const auto d = MakeNamedErrorDetail("ICAP_RESPMOD_EARLY");
919  err->detailError(d);
920  fwd->fail(err);
921  fwd->dontRetry(true);
922  abortAll("adaptation failure with an empty entry");
923  return true; // handled
924  }
925 
926  if (request) { // update logged info directly
927  static const auto d = MakeNamedErrorDetail("ICAP_RESPMOD_LATE");
929  }
930 
931  return false; // the caller must handle
932 }
933 
934 // adaptation service wants us to deny HTTP client access to this response
935 void
937 {
938  const auto blockedAnswer = answer.blockedToChecklistAnswer();
939 
940  debugs(11,5, blockedAnswer.lastCheckDescription());
941 
942  if (abortOnBadEntry("entry went bad while ICAP aborted"))
943  return;
944 
945  if (!entry->isEmpty()) { // too late to block (should not really happen)
946  if (request) {
947  static const auto d = MakeNamedErrorDetail("RESPMOD_BLOCK_LATE");
949  }
950  abortAll("late adaptation block");
951  return;
952  }
953 
954  debugs(11,7, "creating adaptation block response");
955 
956  auto page_id = FindDenyInfoPage(blockedAnswer, true);
957  if (page_id == ERR_NONE)
958  page_id = ERR_ACCESS_DENIED;
959 
960  const auto err = new ErrorState(page_id, Http::scForbidden, request.getRaw(), fwd->al);
961  static const auto d = MakeNamedErrorDetail("RESPMOD_BLOCK_EARLY");
962  err->detailError(d);
963  fwd->fail(err);
964  fwd->dontRetry(true);
965 
966  abortOnData("timely adaptation block");
967 }
968 
969 void
971 {
973 
974  if (abortOnBadEntry("entry went bad while waiting for ICAP ACL check"))
975  return;
976 
977  // TODO: Should non-ICAP and ICAP REPMOD pre-cache paths check this?
978  // That check now only happens on REQMOD pre-cache and REPMOD post-cache, in processReplyAccess().
979  if (virginReply()->expectedBodyTooLarge(*request)) {
981  return;
982  }
983  // TODO: Should we check receivedBodyTooLarge as well?
984 
985  if (!group) {
986  debugs(11,3, "no adapation needed");
989  return;
990  }
991 
992  startAdaptation(group, originalRequest().getRaw());
994 }
995 #endif
996 
997 void
999 {
1000  const auto err = new ErrorState(ERR_TOO_BIG, Http::scForbidden, request.getRaw(), fwd->al);
1001  fwd->fail(err);
1002  fwd->dontRetry(true);
1003  abortOnData("Virgin body too large.");
1004 }
1005 
1006 // TODO: when HttpStateData sends all errors to ICAP,
1007 // we should be able to move this at the end of setVirginReply().
1008 void
1010 {
1011 #if USE_ADAPTATION
1012  // TODO: merge with client side and return void to hide the on/off logic?
1013  // The callback can be called with a NULL service if adaptation is off.
1016  originalRequest().getRaw(), virginReply(), fwd->al, this);
1017  debugs(11,5, "adaptationAccessCheckPending=" << adaptationAccessCheckPending);
1019  return;
1020 #endif
1021 
1023 }
1024 
1026 void
1027 Client::adjustBodyBytesRead(const int64_t delta)
1028 {
1029  int64_t &bodyBytesRead = originalRequest()->hier.bodyBytesRead;
1030 
1031  // if we got here, do not log a dash even if we got nothing from the server
1032  if (bodyBytesRead < 0)
1033  bodyBytesRead = 0;
1034 
1035  bodyBytesRead += delta; // supports negative and zero deltas
1036 
1037  // check for overflows ("infinite" response?) and underflows (a bug)
1038  Must(bodyBytesRead >= 0);
1039 }
1040 
1041 void
1043 {
1046 
1047  using DeferredReadDialer = NullaryMemFunT<Client>;
1048  AsyncCall::Pointer call = asyncCall(11, 5, "Client::noteDelayAwareReadChance",
1049  DeferredReadDialer(this, &Client::noteDelayAwareReadChance));
1050  entry->mem().delayRead(call);
1051 }
1052 
1053 void
1054 Client::addVirginReplyBody(const char *data, ssize_t len)
1055 {
1056  adjustBodyBytesRead(len);
1057 
1058 #if USE_ADAPTATION
1059  assert(!adaptationAccessCheckPending); // or would need to buffer while waiting
1060  if (startedAdaptation) {
1061  adaptVirginReplyBody(data, len);
1062  return;
1063  }
1064 #endif
1065  storeReplyBody(data, len);
1066 }
1067 
1068 // writes virgin or adapted reply body to store
1069 void
1070 Client::storeReplyBody(const char *data, ssize_t len)
1071 {
1072  // write even if len is zero to push headers towards the client side
1073  entry->write (StoreIOBuffer(len, currentOffset, (char*)data));
1074 
1075  currentOffset += len;
1076 }
1077 
1078 size_t
1079 Client::calcBufferSpaceToReserve(size_t space, const size_t wantSpace) const
1080 {
1081  if (space < wantSpace) {
1082  const size_t maxSpace = SBuf::maxSize; // absolute best
1083  space = min(wantSpace, maxSpace); // do not promise more than asked
1084  }
1085 
1086 #if USE_ADAPTATION
1087  if (responseBodyBuffer) {
1088  return 0; // Stop reading if already overflowed waiting for ICAP to catch up
1089  }
1090 
1091  if (virginBodyDestination != nullptr) {
1092  /*
1093  * BodyPipe buffer has a finite size limit. We
1094  * should not read more data from the network than will fit
1095  * into the pipe buffer or we _lose_ what did not fit if
1096  * the response ends sooner that BodyPipe frees up space:
1097  * There is no code to keep pumping data into the pipe once
1098  * response ends and serverComplete() is called.
1099  */
1100  const size_t adaptor_space = virginBodyDestination->buf().potentialSpaceSize();
1101 
1102  debugs(11,9, "Client may read up to min(" <<
1103  adaptor_space << ", " << space << ") bytes");
1104 
1105  if (adaptor_space < space)
1106  space = adaptor_space;
1107  }
1108 #endif
1109 
1110  return space;
1111 }
1112 
1113 size_t
1114 Client::replyBodySpace(const MemBuf &readBuf, const size_t minSpace) const
1115 {
1116  size_t space = readBuf.spaceSize(); // available space w/o heroic measures
1117  if (space < minSpace) {
1118  const size_t maxSpace = readBuf.potentialSpaceSize(); // absolute best
1119  space = min(minSpace, maxSpace); // do not promise more than asked
1120  }
1121 
1122 #if USE_ADAPTATION
1123  if (responseBodyBuffer) {
1124  return 0; // Stop reading if already overflowed waiting for ICAP to catch up
1125  }
1126 
1127  if (virginBodyDestination != nullptr) {
1128  /*
1129  * BodyPipe buffer has a finite size limit. We
1130  * should not read more data from the network than will fit
1131  * into the pipe buffer or we _lose_ what did not fit if
1132  * the response ends sooner that BodyPipe frees up space:
1133  * There is no code to keep pumping data into the pipe once
1134  * response ends and serverComplete() is called.
1135  *
1136  * If the pipe is totally full, don't register the read handler.
1137  * The BodyPipe will call our noteMoreBodySpaceAvailable() method
1138  * when it has free space again.
1139  */
1140  size_t adaptation_space =
1142 
1143  debugs(11,9, "Client may read up to min(" <<
1144  adaptation_space << ", " << space << ") bytes");
1145 
1146  if (adaptation_space < space)
1147  space = adaptation_space;
1148  }
1149 #endif
1150 
1151  return space;
1152 }
1153 
const MemBuf & buf() const
Definition: BodyPipe.h:137
const char * xstrerr(int error)
Definition: xstrerror.cc:83
void handleMoreRequestBodyAvailable()
Definition: Client.cc:323
static bool sameUrlHosts(const char *url1, const char *url2)
Definition: Client.cc:451
void storeReplyBody(const char *buf, ssize_t len)
Definition: Client.cc:1070
HttpReply * theVirginReply
Definition: Client.h:219
virtual bool getMoreRequestBody(MemBuf &buf)
either fill buf with available [encoded] request body bytes or return false
Definition: Client.cc:442
void noteAdaptationAnswer(const Adaptation::Answer &answer) override
Definition: Client.cc:700
bool expectingBody(const HttpRequestMethod &, int64_t &) const override
Definition: HttpReply.cc:528
StoreEntry * entry
Definition: Client.h:179
void noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer group) override
Definition: Client.cc:970
bool initiated(const CbcPointer< AsyncJob > &job) const
Must(initiated(initiate)) instead of Must(initiate.set()), for clarity.
Definition: Initiator.h:52
void delayRead()
Definition: Client.cc:1042
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
common parts of HttpRequest and HttpReply
Definition: Message.h:25
bool doneAll() const override
whether positive goal has been reached
Definition: Client.cc:203
void fd_bytes(const int fd, const int len, const IoDirection direction)
Definition: fd.cc:226
BodyPipe::Pointer body_pipe
optional pipeline to receive message body
Definition: Message.h:97
Definition: Uri.h:31
void adaptVirginReplyBody(const char *buf, ssize_t len)
Definition: Client.cc:632
bool waitingForDelayAwareReadChance
whether we are waiting for MemObject::delayRead() to call us back
Definition: Client.h:205
struct SquidConfig::@98 accessList
bool completed
Definition: Client.h:90
const char * getStr(Http::HdrType id) const
Definition: HttpHeader.cc:1163
HttpHeader header
Definition: Message.h:74
AsyncCall::Pointer requestSender
Definition: Client.h:185
void noteMoreBodyDataAvailable(BodyPipe::Pointer) override
Definition: Client.cc:274
virtual void swanSong()
Definition: AsyncJob.h:61
void checkAdaptationWithBodyCompletion()
Definition: Client.cc:831
bool final
whether the error, if any, cannot be bypassed
Definition: Answer.h:46
Client(FwdState *)
Definition: Client.cc:38
void setBodySize(uint64_t aSize)
Definition: BodyPipe.cc:147
MemObject & mem()
Definition: Store.h:47
void handleAdaptationCompleted()
Definition: Client.cc:878
void noteBodyConsumerAborted(BodyPipe::Pointer) override
Definition: Client.cc:688
struct StatCounters::@112::@122 all
void lock(const char *context)
Definition: store.cc:445
virtual void handleRequestBodyProducerAborted()=0
Definition: Client.cc:355
@ ENTRY_ABORTED
Definition: enums.h:110
AccessLogEntryPointer al
info for the future access.log entry
Definition: FwdState.h:204
HttpReplyPointer reply
virtual void abortAll(const char *reason)=0
abnormal transaction termination; reason is for debugging only
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
Definition: SBuf.h:93
void maybePurgeOthers()
Definition: Client.cc:520
void serverComplete2()
Definition: Client.cc:188
bool purgesOthers() const
BodyPipe::Pointer requestBodySource
Definition: Client.h:184
static void purgeEntriesByHeader(HttpRequest *req, const char *reqUrl, Http::Message *rep, Http::HdrType hdr)
Definition: Client.cc:480
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:85
struct StatCounters::@112 server
C * getRaw() const
Definition: RefCount.h:89
uint16_t flags
Definition: Store.h:231
virtual const Comm::ConnectionPointer & dataConnection() const =0
void cleanAdaptation()
Definition: Client.cc:607
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
void adjustBodyBytesRead(const int64_t delta)
initializes bodyBytesRead stats if needed and applies delta
Definition: Client.cc:1027
bool isAccepting() const
Definition: store.cc:1988
Http::StatusLine sline
Definition: HttpReply.h:56
void HTTPMSGUNLOCK(M *&a)
Definition: Message.h:150
@ ERR_NONE
Definition: forward.h:15
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1705
void adaptOrFinalizeReply()
Definition: Client.cc:1009
MemBuf * responseBodyBuffer
Definition: Client.h:176
void fail(ErrorState *err)
Definition: FwdState.cc:458
BodyPipe::Pointer virginBodyDestination
Definition: Client.h:188
bool startRequestBodyFlow()
Definition: Client.cc:243
@ akForward
forward the supplied adapted HTTP message
Definition: Answer.h:29
void startWriting()
Definition: store.cc:1721
void detailError(const err_type c, const ErrorDetail::Pointer &d)
sets error detail if no earlier detail was available
Definition: HttpRequest.h:101
@ ERR_CLOSING
Definition: Flag.h:24
CbcPointer< Initiate > initiateAdaptation(Initiate *x)
< starts freshly created initiate and returns a safe pointer to it
Definition: Initiator.cc:23
virtual void haveParsedReplyHeaders()
called when we have final (possibly adapted) reply headers; kids extend
Definition: Client.cc:541
int64_t currentOffset
Definition: Client.h:175
bool setConsumerIfNotLate(const Consumer::Pointer &aConsumer)
Definition: BodyPipe.cc:228
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:83
static const size_type maxSize
Maximum size of a SBuf. By design it MUST be < MAX(size_type)/2. Currently 256Mb.
Definition: SBuf.h:103
RefCount< AsyncCallT< Dialer > > asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:156
virtual bool mayReadVirginReplyBody() const =0
whether we may receive more virgin response body bytes
size_t getMoreData(MemBuf &buf)
Definition: BodyPipe.cc:294
bool exhausted() const
Definition: BodyPipe.cc:174
void serverComplete()
Definition: Client.cc:167
void updateReply(const HttpReply::Pointer &)
virtual void completeForwarding()
Definition: Client.cc:216
err_type FindDenyInfoPage(const Acl::Answer &answer, const bool redirect_allowed)
Definition: Gadgets.cc:34
Definition: Range.h:18
@ ERR_ACCESS_DENIED
Definition: forward.h:18
@ PROTO_URN
Definition: ProtocolType.h:35
void handleMoreAdaptedBodyAvailable()
Definition: Client.cc:772
MemBuf & buf
Definition: BodyPipe.h:74
void handleAdaptedHeader(Http::Message *msg)
Definition: Client.cc:720
@ ERR_ICAP_FAILURE
Definition: forward.h:64
Http::MethodType id() const
Definition: RequestMethod.h:70
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
int size
Definition: ModDevPoll.cc:69
void append(const char *c, int sz) override
Definition: MemBuf.cc:209
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:112
void noteBodyProductionEnded(BodyPipe::Pointer) override
Definition: Client.cc:288
virtual void doneSendingRequestBody()=0
Definition: Client.cc:344
@ scForbidden
Definition: StatusCode.h:48
bool dontRetry()
Definition: FwdState.h:130
void resumeBodyStorage()
called by StoreEntry when it has more buffer space available
Definition: Client.cc:757
const HttpReply * virginReply() const
Definition: Client.cc:102
void stopConsumingFrom(RefCount< BodyPipe > &)
Definition: BodyPipe.cc:118
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
iterates services in ServiceGroup, starting adaptation launchers
Definition: Iterator.h:31
HttpReply * theFinalReply
Definition: Client.h:220
bool blockCaching()
whether to prevent caching of an otherwise cachable response
Definition: Client.cc:556
@ RELEASE_REQUEST
prohibits making the key public
Definition: enums.h:93
virtual void processReplyBody()=0
const Acl::Answer & fastCheck()
Definition: Checklist.cc:298
void clearAdaptation(CbcPointer< Initiate > &x)
clears the pointer (does not call announceInitiatorAbort)
Definition: Initiator.cc:32
@ scBadGateway
Definition: StatusCode.h:75
Definition: MemBuf.h:23
@ CONTENT_LOCATION
#define EBIT_TEST(flag, bit)
Definition: defines.h:67
void swanSong() override
Definition: Client.cc:68
Kind kind
the type of the answer
Definition: Answer.h:47
@ METHOD_CONNECT
Definition: MethodType.h:29
void handleAdaptedBodyProducerAborted()
Definition: Client.cc:857
int unlock(const char *context)
Definition: store.cc:469
#define assert(EX)
Definition: assert.h:17
void sendMoreRequestBody()
Definition: Client.cc:416
FwdState::Pointer fwd
Definition: Client.h:180
HierarchyLogEntry hier
Definition: HttpRequest.h:157
void purgeEntriesByUrl(HttpRequest *req, const char *url)
bool handledEarlyAdaptationAbort()
Definition: Client.cc:913
HttpReply * setFinalReply(HttpReply *r)
Definition: Client.cc:136
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:161
virtual HttpRequestPointer originalRequest()
a hack to reach HttpStateData::orignal_request
Definition: Client.cc:573
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:82
const AnyP::UriScheme & getScheme() const
Definition: Uri.h:58
bool adaptationAccessCheckPending
Definition: Client.h:192
void write(StoreIOBuffer)
Definition: store.cc:780
#define Assure(condition)
Definition: Assure.h:35
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:70
@ scInternalServerError
Definition: StatusCode.h:73
HttpReply * setVirginReply(HttpReply *r)
Definition: Client.cc:116
static const int64_t UnknownPosition
const char * c_str()
Definition: SBuf.cc:516
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:33
void delayRead(const AsyncCallPointer &)
Definition: MemObject.cc:445
bool receivedWholeRequestBody
handleRequestBodyProductionEnded called
Definition: Client.h:202
size_t bytesWanted(Range< size_t > const aRange, bool ignoreDelayPool=false) const
Definition: store.cc:213
void addRelativePath(const char *relUrl)
Definition: Uri.cc:836
HttpReply * finalReply()
Definition: Client.cc:129
int xerrno
Definition: errorpage.h:179
virtual void noteDelayAwareReadChance()=0
bool abortOnBadEntry(const char *abortReason)
Entry-dependent callbacks use this check to quit if the entry went bad.
Definition: Client.cc:262
summarizes adaptation service answer for the noteAdaptationAnswer() API
Definition: Answer.h:24
void markParsedVirginReplyAsWhole(const char *reasonWeAreSure)
Definition: Client.cc:158
BodyPipe::Pointer adaptedBodySource
Definition: Client.h:190
virtual bool doneWithAdaptation() const
Definition: Client.cc:624
void sendBodyIsTooLargeError()
Definition: Client.cc:998
int64_t bodyBytesRead
number of body bytes received from the next hop or -1
SBuf & absolute() const
Definition: Uri.cc:711
size_t calcBufferSpaceToReserve(const size_t space, const size_t wantSpace) const
determine how much space the buffer needs to reserve
Definition: Client.cc:1079
void startAdaptation(const Adaptation::ServiceGroupPointer &group, HttpRequest *cause)
Initiate an asynchronous adaptation transaction which will call us back.
Definition: Client.cc:581
@ pointPreCache
Definition: Elements.h:18
void handleAdaptationAborted(bool bypassable=false)
Definition: Client.cc:897
HttpRequestMethod method
Definition: HttpRequest.h:114
void path(const char *p)
Definition: Uri.h:96
Http::MessagePointer message
HTTP request or response to forward.
Definition: Answer.h:44
@ methodRespmod
Definition: Elements.h:17
bool allowed() const
Definition: Acl.h:82
size_t putMoreData(const char *buf, size_t size)
Definition: BodyPipe.cc:213
void addVirginReplyBody(const char *buf, ssize_t len)
Definition: Client.cc:1054
const char * status() const
Definition: BodyPipe.cc:446
const char * doneWithFwd
Definition: Client.h:213
void complete()
Definition: FwdState.cc:526
virtual void sentRequestBody(const CommIoCbParams &io)=0
Definition: Client.cc:368
mb_size_t potentialSpaceSize() const
Definition: MemBuf.cc:161
Acl::Answer blockedToChecklistAnswer() const
creates an Acl::Answer from akBlock answer
Definition: Answer.cc:44
@ akError
no adapted message will come; see bypassable
Definition: Answer.h:31
@ ERR_TOO_BIG
Definition: forward.h:40
const HttpHdrContRange * contentRange() const
Definition: HttpReply.cc:345
char * content()
start of the added data
Definition: MemBuf.h:41
mb_size_t spaceSize() const
Definition: MemBuf.cc:155
void expectNoConsumption()
there will be no more setConsumer() calls
Definition: BodyPipe.cc:267
@ ERR_WRITE_ERROR
Definition: forward.h:29
void announceInitiatorAbort(CbcPointer< Initiate > &x)
inform the transaction about abnormal termination and clear the pointer
Definition: Initiator.cc:38
bool isEmpty() const
Definition: Store.h:65
void noteBodyProducerAborted(BodyPipe::Pointer) override
Definition: Client.cc:302
#define Must(condition)
Definition: TextException.h:75
acl_access * storeMiss
Definition: SquidConfig.h:368
void handleRequestBodyProductionEnded()
Definition: Client.cc:333
void stopProducingFor(RefCount< BodyPipe > &, bool atEof)
Definition: BodyPipe.cc:107
#define DBG_IMPORTANT
Definition: Stream.h:38
const char * markedParsedVirginReplyAsWhole
Definition: Client.h:209
virtual void maybeReadVirginBody()=0
read response data from the network
StoreEntry * entry
Definition: FwdState.h:202
bool urlIsRelative(const char *url)
Definition: Uri.cc:803
~Client() override
Definition: Client.cc:47
void release(const bool shareable=false)
Definition: store.cc:1146
virtual void closeServer()=0
virtual bool abortOnData(const char *reason)
Definition: Client.cc:315
const HttpReply & freshestReply() const
Definition: MemObject.h:68
void handleAdaptedBodyProductionEnded()
Definition: Client.cc:819
bool startedAdaptation
Definition: Client.h:193
bool receivedWholeAdaptedReply
Definition: Client.h:198
void deferProducer(const AsyncCall::Pointer &producer)
call back producer when more buffer space is available
Definition: store.cc:366
void markStoredReplyAsWhole(const char *whyWeAreSure)
Definition: FwdState.cc:575
@ akBlock
block or deny the master xaction; see authority
Definition: Answer.h:30
void consume(mb_size_t sz)
removes sz bytes and "packs" by moving content left
Definition: MemBuf.cc:168
CbcPointer< Adaptation::Initiate > adaptedHeadSource
Definition: Client.h:189
size_t replyBodySpace(const MemBuf &readBuf, const size_t minSpace) const
Definition: Client.cc:1114
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:743
void handleAdaptationBlocked(const Adaptation::Answer &answer)
Definition: Client.cc:936
HttpRequest * request
Definition: FwdState.h:203
HttpRequestPointer request
Definition: Client.h:181
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
const A & min(A const &lhs, A const &rhs)
void updateAle(const AccessLogEntry::Pointer &)
uint64_t consumedSize() const
Definition: BodyPipe.h:111
void noteMoreBodySpaceAvailable(BodyPipe::Pointer) override
Definition: Client.cc:674
static bool Start(Method method, VectPoint vp, HttpRequest *req, HttpReply *, const AccessLogEntryPointer &, Adaptation::Initiator *)
Definition: AccessCheck.cc:30
void handleUnregisteredServerEnd()
Definition: FwdState.cc:804
class SquidConfig Config
Definition: SquidConfig.cc:12
ErrorDetail::Pointer MakeNamedErrorDetail(const char *name)
Definition: Detail.cc:54
StatCounters statCounter
Definition: StatCounters.cc:12
virtual bool doneWithServer() const =0
bool adaptedReplyAborted
handleAdaptedBodyProducerAborted() has been called
Definition: Client.h:200

 

Introduction

Documentation

Support

Miscellaneous