client_side_reply.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 /* DEBUG: section 88 Client-side Reply Routines */
10 
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "acl/Gadgets.h"
14 #include "anyp/PortCfg.h"
15 #include "client_side_reply.h"
16 #include "clientStream.h"
17 #include "errorpage.h"
18 #include "ETag.h"
19 #include "fd.h"
20 #include "fde.h"
21 #include "format/Token.h"
22 #include "FwdState.h"
23 #include "globals.h"
24 #include "HeaderMangling.h"
25 #include "http/Stream.h"
26 #include "HttpHeaderTools.h"
27 #include "HttpReply.h"
28 #include "HttpRequest.h"
29 #include "ip/QosConfig.h"
30 #include "ipcache.h"
31 #include "log/access_log.h"
32 #include "MemObject.h"
33 #include "mime_header.h"
34 #include "neighbors.h"
35 #include "refresh.h"
36 #include "RequestFlags.h"
37 #include "SquidConfig.h"
38 #include "SquidMath.h"
39 #include "Store.h"
40 #include "StrList.h"
41 #include "tools.h"
42 #if USE_AUTH
43 #include "auth/UserRequest.h"
44 #endif
45 #if USE_DELAY_POOLS
46 #include "DelayPools.h"
47 #endif
48 
49 #include <memory>
50 
52 
53 /* Local functions */
56 
57 /* privates */
58 
60 {
61  deleting = true;
62  /* This may trigger a callback back into SendMoreData as the cbdata
63  * is still valid
64  */
66  /* old_entry might still be set if we didn't yet get the reply
67  * code in HandleIMSReply() */
71 }
72 
74  purgeStatus(Http::scNone),
75  http(cbdataReference(clientContext)),
76  sc(nullptr),
77  ourNode(nullptr),
78  reply(nullptr),
79  old_entry(nullptr),
80  old_sc(nullptr),
81  old_lastmod(-1),
82  deleting(false),
83  collapsedRevalidation(crNone)
84 {
85  *tempbuf = 0;
86 }
87 
93 void
95  err_type err, Http::StatusCode status, char const *uri,
96  const ConnStateData *conn, HttpRequest *failedrequest, const char *unparsedrequest,
97 #if USE_AUTH
98  Auth::UserRequest::Pointer auth_user_request
99 #else
100  void*
101 #endif
102 )
103 {
104  auto errstate = clientBuildError(err, status, uri, conn, failedrequest, http->al);
105 
106  if (unparsedrequest)
107  errstate->request_hdrs = xstrdup(unparsedrequest);
108 
109 #if USE_AUTH
110  errstate->auth_user_request = auth_user_request;
111 #endif
112  setReplyToError(failedrequest ? failedrequest->method : HttpRequestMethod(Http::METHOD_NONE), errstate);
113 }
114 
116 {
117  if (errstate->httpStatus == Http::scNotImplemented && http->request)
118  /* prevent confusion over whether we default to persistent or not */
119  http->request->flags.proxyKeepalive = false;
120 
121  http->al->http.code = errstate->httpStatus;
122 
123  if (http->request)
124  http->request->ignoreRange("responding with a Squid-generated error");
125 
126  createStoreEntry(method, RequestFlags());
127  assert(errstate->callback_data == nullptr);
128  errorAppendEntry(http->storeEntry(), errstate);
129  /* Now the caller reads to get this */
130 }
131 
132 void
134 {
135  Must(futureReply);
136  http->al->http.code = futureReply->sline.status();
137 
138  HttpRequestMethod method;
139  if (http->request) { // nil on responses to unparsable requests
140  http->request->ignoreRange("responding with a Squid-generated reply");
141  method = http->request->method;
142  }
143 
144  createStoreEntry(method, RequestFlags());
145 
146  http->storeEntry()->storeErrorResponse(futureReply);
147  /* Now the caller reads to get futureReply */
148 }
149 
150 // Assumes that the entry contains an error response without Content-Range.
151 // To use with regular entries, make HTTP Range header removal conditional.
152 void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry, const char *reason)
153 {
154  entry->lock("clientReplyContext::setReplyToStoreEntry"); // removeClientStoreReference() unlocks
155  sc = storeClientListAdd(entry, this);
156 #if USE_DELAY_POOLS
158 #endif
159  if (http->request)
160  http->request->ignoreRange(reason);
162  http->storeEntry(entry);
163 }
164 
165 void
167  StoreEntry ** ep)
168 {
169  StoreEntry *e;
170  store_client *sc_tmp = *scp;
171 
172  if ((e = *ep) != nullptr) {
173  *ep = nullptr;
174  storeUnregister(sc_tmp, e, this);
175  *scp = nullptr;
176  e->unlock("clientReplyContext::removeStoreReference");
177  }
178 }
179 
180 void
182 {
183  StoreEntry *reference = aHttpRequest->storeEntry();
184  removeStoreReference(scp, &reference);
185  aHttpRequest->storeEntry(reference);
186 }
187 
188 void
190 {
191  assert(old_sc == nullptr);
192  debugs(88, 3, "clientReplyContext::saveState: saving store context");
194  old_sc = sc;
197  /* Prevent accessing the now saved entries */
198  http->storeEntry(nullptr);
199  sc = nullptr;
200 }
201 
202 void
204 {
205  assert(old_sc != nullptr);
206  debugs(88, 3, "clientReplyContext::restoreState: Restoring store context");
209  sc = old_sc;
212  /* Prevent accessed the old saved entries */
213  old_entry = nullptr;
214  old_sc = nullptr;
215  old_lastmod = -1;
216  old_etag.clean();
217 }
218 
219 void
221 {
225 }
226 
229 {
230  return (clientStreamNode *)ourNode->node.next->data;
231 }
232 
236 void
238 {
239  Assure(recipient != HandleIMSReply);
240  lastStreamBufferedBytes = StoreIOBuffer(); // storeClientCopy(next()->readBuffer) invalidates
241  StoreIOBuffer localTempBuffer (next()->readBuffer.length, 0, next()->readBuffer.data);
242  ::storeClientCopy(sc, http->storeEntry(), localTempBuffer, recipient, this);
243 }
244 
249 void
251 {
252  lastStreamBufferedBytes = StoreIOBuffer(); // storeClientCopy(next()->readBuffer) invalidates
253  ::storeClientCopy(sc, http->storeEntry(), next()->readBuffer, SendMoreData, this);
254 }
255 
256 /* there is an expired entry in the store.
257  * setup a temporary buffer area and perform an IMS to the origin
258  */
259 void
261 {
262  const char *url = storeId();
263  debugs(88, 3, "clientReplyContext::processExpired: '" << http->uri << "'");
264  const time_t lastmod = http->storeEntry()->lastModified();
265  assert(lastmod >= 0);
266  /*
267  * check if we are allowed to contact other servers
268  * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return
269  * a stale entry *if* it matches client requirements
270  */
271 
272  if (http->onlyIfCached()) {
274  return;
275  }
276 
278  http->request->flags.refresh = true;
279 #if STORE_CLIENT_LIST_DEBUG
280  /* Prevent a race with the store client memory free routines
281  */
283 #endif
284  /* Prepare to make a new temporary request */
285  saveState();
286 
287  // TODO: Consider also allowing regular (non-collapsed) revalidation hits.
288  // TODO: support collapsed revalidation for Vary-controlled entries
289  bool collapsingAllowed = Config.onoff.collapsed_forwarding &&
292 
293  StoreEntry *entry = nullptr;
294  if (collapsingAllowed) {
295  if (const auto e = storeGetPublicByRequest(http->request, ksRevalidation)) {
296  if (e->hittingRequiresCollapsing() && startCollapsingOn(*e, true)) {
297  entry = e;
298  entry->lock("clientReplyContext::processExpired#alreadyRevalidating");
299  } else {
300  e->abandon(__func__);
301  // assume mayInitiateCollapsing() would fail too
302  collapsingAllowed = false;
303  }
304  }
305  }
306 
307  if (entry) {
308  entry->ensureMemObject(url, http->log_uri, http->request->method);
309  debugs(88, 5, "collapsed on existing revalidation entry: " << *entry);
311  } else {
312  entry = storeCreateEntry(url,
314  /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
315 
316  if (collapsingAllowed && mayInitiateCollapsing() &&
317  Store::Root().allowCollapsing(entry, http->request->flags, http->request->method)) {
318  debugs(88, 5, "allow other revalidation requests to collapse on " << *entry);
320  } else {
322  }
323  }
324 
325  sc = storeClientListAdd(entry, this);
326 #if USE_DELAY_POOLS
327  /* delay_id is already set on original store client */
329 #endif
330 
331  http->request->lastmod = lastmod;
332 
334  ETag etag = {nullptr, -1}; // TODO: make that a default ETag constructor
335  if (old_entry->hasEtag(etag) && !etag.weak)
336  http->request->etag = etag.str;
337  }
338 
339  debugs(88, 5, "lastmod " << entry->lastModified());
340  http->storeEntry(entry);
341  assert(http->out.offset == 0);
343 
345  /*
346  * A refcounted pointer so that FwdState stays around as long as
347  * this clientReplyContext does
348  */
349  Comm::ConnectionPointer conn = http->getConn() != nullptr ? http->getConn()->clientConnection : nullptr;
351  }
352  /* Register with storage manager to receive updates when data comes in. */
353 
354  if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
355  debugs(88, DBG_CRITICAL, "clientReplyContext::processExpired: Found ENTRY_ABORTED object");
356 
357  {
358  /* start counting the length from 0 */
359  StoreIOBuffer localTempBuffer(HTTP_REQBUF_SZ, 0, tempbuf);
360  // keep lastStreamBufferedBytes: tempbuf is not a Client Stream buffer
361  ::storeClientCopy(sc, entry, localTempBuffer, HandleIMSReply, this);
362  }
363 }
364 
365 void
367 {
369 
372 
373  /* here the data to send is the data we just received */
375  sendMoreData(upstreamResponse);
376 }
377 
378 void
380 {
381  clientReplyContext *context = (clientReplyContext *)data;
382  context->handleIMSReply(result);
383 }
384 
385 void
387 {
388  /* Get the old request back */
389  restoreState();
390 
392  debugs(88, 3, "stale entry aborted while we revalidated: " << *http->storeEntry());
394  processMiss();
395  return;
396  }
397 
398  /* here the data to send is in the next nodes buffers already */
402 }
403 
404 /* This is the workhorse of the HandleIMSReply callback.
405  *
406  * It is called when we've got data back from the origin following our
407  * IMS request to revalidate a stale entry.
408  */
409 void
411 {
412  if (deleting)
413  return;
414 
415  if (http->storeEntry() == nullptr)
416  return;
417 
418  debugs(88, 3, http->storeEntry()->url() << " got " << result);
419 
420  if (result.flags.error && !EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
421  return;
422 
424  debugs(88, 3, "CF slave hit private non-shareable " << *http->storeEntry() << ". MISS");
425  // restore context to meet processMiss() expectations
426  restoreState();
428  processMiss();
429  return;
430  }
431 
432  // request to origin was aborted
434  debugs(88, 3, "request to origin aborted '" << http->storeEntry()->url() << "', sending old entry to client");
437  return;
438  }
439 
440  const auto oldStatus = old_entry->mem().freshestReply().sline.status();
441  const auto &new_rep = http->storeEntry()->mem().freshestReply();
442  const auto status = new_rep.sline.status();
443 
444  // XXX: Disregard stale incomplete (i.e. still being written) borrowed (i.e.
445  // not caused by our request) IMS responses. That new_rep may be very old!
446 
447  // origin replied 304
448  if (status == Http::scNotModified) {
449  // TODO: The update may not be instantaneous. Should we wait for its
450  // completion to avoid spawning too much client-disassociated work?
451  if (!Store::Root().updateOnNotModified(old_entry, *http->storeEntry())) {
452  old_entry->release(true);
453  restoreState();
455  processMiss();
456  return;
457  }
458 
460  http->request->flags.staleIfHit = false; // old_entry is no longer stale
461 
462  // if client sent IMS
464  // forward the 304 from origin
465  debugs(88, 3, "origin replied 304, revalidated existing entry and forwarding 304 to client");
467  return;
468  }
469 
470  // send existing entry, it's still valid
471  debugs(88, 3, "origin replied 304, revalidated existing entry and sending " << oldStatus << " to client");
473  return;
474  }
475 
476  // origin replied with a non-error code
477  if (status > Http::scNone && status < Http::scInternalServerError) {
478  // RFC 9111 section 4:
479  // "When more than one suitable response is stored,
480  // a cache MUST use the most recent one
481  // (as determined by the Date header field)."
482  if (new_rep.olderThan(&old_entry->mem().freshestReply())) {
483  http->al->cache.code.err.ignored = true;
484  debugs(88, 3, "origin replied " << status << " but with an older date header, sending old entry (" << oldStatus << ") to client");
486  return;
487  }
488 
490  debugs(88, 3, "origin replied " << status << ", forwarding to client");
492  return;
493  }
494 
495  // origin replied with an error
498  debugs(88, 3, "origin replied with error " << status << ", forwarding to client due to fail_on_validation_err");
500  return;
501  }
502 
503  // ignore and let client have old entry
505  debugs(88, 3, "origin replied with error " << status << ", sending old entry (" << oldStatus << ") to client");
507 }
508 
511 
513 void
515 {
516  clientReplyContext *context = (clientReplyContext *)data;
517  context->cacheHit(result);
518 }
519 
523 void
525 {
527  if (deleting) {
528  debugs(88, 3, "HIT object being deleted. Ignore the HIT.");
529  return;
530  }
531 
532  StoreEntry *e = http->storeEntry();
533 
534  HttpRequest *r = http->request;
535 
536  debugs(88, 3, http->uri << " got " << result);
537 
538  if (http->storeEntry() == nullptr) {
539  debugs(88, 3, "clientCacheHit: request aborted");
540  return;
541  } else if (result.flags.error) {
542  /* swap in failure */
543  debugs(88, 3, "clientCacheHit: swapin failure for " << http->uri);
546  processMiss();
547  return;
548  }
549 
550  // The previously identified hit suddenly became unshareable!
551  // This is common for collapsed forwarding slaves but might also
552  // happen to regular hits because we are called asynchronously.
553  if (!e->mayStartHitting()) {
554  debugs(88, 3, "unshareable " << *e << ". MISS");
556  processMiss();
557  return;
558  }
559 
560  if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
561  debugs(88, 3, "refusing aborted " << *e);
563  processMiss();
564  return;
565  }
566 
567  /*
568  * Got the headers, now grok them
569  */
571 
572  if (http->request->storeId().cmp(e->mem_obj->storeId()) != 0) {
573  debugs(33, DBG_IMPORTANT, "clientProcessHit: URL mismatch, '" << e->mem_obj->storeId() << "' != '" << http->request->storeId() << "'");
574  http->updateLoggingTags(LOG_TCP_MISS); // we lack a more precise LOG_*_MISS code
575  processMiss();
576  return;
577  }
578 
579  noteStreamBufferredBytes(result);
580 
581  switch (varyEvaluateMatch(e, r)) {
582 
583  case VARY_NONE:
584  /* No variance detected. Continue as normal */
585  break;
586 
587  case VARY_MATCH:
588  /* This is the correct entity for this request. Continue */
589  debugs(88, 2, "clientProcessHit: Vary MATCH!");
590  break;
591 
592  case VARY_OTHER:
593  /* This is not the correct entity for this request. We need
594  * to requery the cache.
595  */
597  e = nullptr;
598  /* Note: varyEvalyateMatch updates the request with vary information
599  * so we only get here once. (it also takes care of cancelling loops)
600  */
601  debugs(88, 2, "clientProcessHit: Vary detected!");
603  return;
604 
605  case VARY_CANCEL:
606  /* varyEvaluateMatch found a object loop. Process as miss */
607  debugs(88, DBG_IMPORTANT, "clientProcessHit: Vary object loop!");
608  http->updateLoggingTags(LOG_TCP_MISS); // we lack a more precise LOG_*_MISS code
609  processMiss();
610  return;
611  }
612 
613  if (r->method == Http::METHOD_PURGE) {
614  debugs(88, 5, "PURGE gets a HIT");
616  e = nullptr;
617  purgeRequest();
618  return;
619  }
620 
621  if (e->checkNegativeHit() && !r->flags.noCacheHack()) {
622  debugs(88, 5, "negative-HIT");
624  sendMoreData(result);
625  return;
626  } else if (blockedHit()) {
627  debugs(88, 5, "send_hit forces a MISS");
629  processMiss();
630  return;
631  } else if (!r->flags.internal && !didCollapse && refreshCheckHTTP(e, r)) {
632  debugs(88, 5, "clientCacheHit: in refreshCheck() block");
633  /*
634  * We hold a stale copy; it needs to be validated
635  */
636  /*
637  * The 'needValidation' flag is used to prevent forwarding
638  * loops between siblings. If our copy of the object is stale,
639  * then we should probably only use parents for the validation
640  * request. Otherwise two siblings could generate a loop if
641  * both have a stale version of the object.
642  */
643  r->flags.needValidation = true;
644 
645  if (e->lastModified() < 0) {
646  debugs(88, 3, "validate HIT object? NO. Can't calculate entry modification time. Do MISS.");
647  /*
648  * We cannot revalidate entries without knowing their
649  * modification time.
650  * XXX: BUG 1890 objects without Date do not get one added.
651  */
653  processMiss();
654  } else if (r->flags.noCache) {
655  debugs(88, 3, "validate HIT object? NO. Client sent CC:no-cache. Do CLIENT_REFRESH_MISS");
656  /*
657  * This did not match a refresh pattern that overrides no-cache
658  * we should honour the client no-cache header.
659  */
661  processMiss();
662  } else if (r->url.getScheme() == AnyP::PROTO_HTTP || r->url.getScheme() == AnyP::PROTO_HTTPS) {
663  debugs(88, 3, "validate HIT object? YES.");
664  /*
665  * Object needs to be revalidated
666  * XXX This could apply to FTP as well, if Last-Modified is known.
667  */
668  processExpired();
669  } else {
670  debugs(88, 3, "validate HIT object? NO. Client protocol non-HTTP. Do MISS.");
671  /*
672  * We don't know how to re-validate other protocols. Handle
673  * them as if the object has expired.
674  */
676  processMiss();
677  }
678  return;
679  } else if (r->conditional()) {
680  debugs(88, 5, "conditional HIT");
681  if (processConditional())
682  return;
683  }
684 
685  /*
686  * plain ol' cache hit
687  */
688  debugs(88, 5, "plain old HIT");
689 
690 #if USE_DELAY_POOLS
691  if (e->store_status != STORE_OK)
693  else
694 #endif
695  if (e->mem_status == IN_MEMORY)
697  else if (Config.onoff.offline)
699 
700  sendMoreData(result);
701 }
702 
706 void
708 {
709  char *url = http->uri;
710  HttpRequest *r = http->request;
711  ErrorState *err = nullptr;
712  debugs(88, 4, r->method << ' ' << url);
713 
718  if (http->storeEntry()) {
720  debugs(88, DBG_CRITICAL, "clientProcessMiss: miss on a special object (" << url << ").");
721  debugs(88, DBG_CRITICAL, "\tlog_type = " << http->loggingTags().c_str());
722  http->storeEntry()->dump(1);
723  }
724 
726  }
727 
729  if (r->method == Http::METHOD_PURGE) {
730  purgeRequest();
731  return;
732  }
733 
735  if (r->method == Http::METHOD_OTHER) {
736  purgeAllCached();
737  }
738 
740  if (http->onlyIfCached()) {
742  return;
743  }
744 
746  if (r->flags.loopDetected) {
747  http->al->http.code = Http::scForbidden;
752  return;
753  } else {
754  assert(http->out.offset == 0);
755  createStoreEntry(r->method, r->flags);
757 
758  if (http->redirect.status) {
759  const HttpReplyPointer rep(new HttpReply);
764  http->storeEntry()->complete();
765  return;
766  }
767 
769 
770  Comm::ConnectionPointer conn = http->getConn() != nullptr ? http->getConn()->clientConnection : nullptr;
772  FwdState::Start(conn, http->storeEntry(), r, http->al);
773  }
774 }
775 
782 void
784 {
785  debugs(88, 4, http->request->method << ' ' << http->uri);
786  http->al->http.code = Http::scGatewayTimeout;
788  http->getConn(), http->request, http->al);
790  startError(err);
791 }
792 
794 bool
796 {
797  StoreEntry *const e = http->storeEntry();
798 
799  const auto replyStatusCode = e->mem().baseReply().sline.status();
800  if (replyStatusCode != Http::scOkay) {
801  debugs(88, 4, "miss because " << replyStatusCode << " != 200");
803  processMiss();
804  return true;
805  }
806 
807  HttpRequest &r = *http->request;
808 
810  // RFC 2616: reply with 412 Precondition Failed if If-Match did not match
812  return true;
813  }
814 
816  // RFC 7232: If-None-Match recipient MUST ignore IMS
817  r.flags.ims = false;
818  r.ims = -1;
819  r.imslen = 0;
821 
822  if (e->hasIfNoneMatchEtag(r)) {
824  return true;
825  }
826 
827  // None-Match is true (no ETag matched); treat as an unconditional hit
828  return false;
829  }
830 
831  if (r.flags.ims) {
832  // handle If-Modified-Since requests from the client
833  if (e->modifiedSince(r.ims, r.imslen)) {
834  // Modified-Since is true; treat as an unconditional hit
835  return false;
836 
837  } else {
838  // otherwise reply with 304 Not Modified
839  sendNotModified();
840  }
841  return true;
842  }
843 
844  return false;
845 }
846 
848 bool
850 {
852  return false; // hits are not blocked by default
853 
854  if (http->request->flags.internal)
855  return false; // internal content "hits" cannot be blocked
856 
857  {
861  return !chl.fastCheck().allowed(); // when in doubt, block
862  }
863 }
864 
865 // Purges all entries with a given url
866 // TODO: move to SideAgent parent, when we have one
867 /*
868  * We probably cannot purge Vary-affected responses because their MD5
869  * keys depend on vary headers.
870  */
871 void
872 purgeEntriesByUrl(HttpRequest * req, const char *url)
873 {
875  if (m.respMaybeCacheable()) {
876  const cache_key *key = storeKeyPublic(url, m);
877  debugs(88, 5, m << ' ' << url << ' ' << storeKeyText(key));
878 #if USE_HTCP
879  neighborsHtcpClear(nullptr, req, m, HTCP_CLR_INVALIDATION);
880 #else
881  (void)req;
882 #endif
883  Store::Root().evictIfFound(key);
884  }
885  }
886 }
887 
888 void
890 {
891  // XXX: performance regression, c_str() reallocates
894 }
895 
896 LogTags *
898 {
899  // XXX: clientReplyContext code assumes that http cbdata is always valid.
900  // TODO: Either add cbdataReferenceValid(http) checks in all the relevant
901  // places, like this one, or remove cbdata protection of the http member.
902  return &http->al->cache.code;
903 }
904 
905 void
907 {
908  debugs(88, 3, "Config2.onoff.enable_purge = " <<
910 
911  if (!Config2.onoff.enable_purge) {
914  http->getConn(), http->request, http->al);
915  startError(err);
916  return;
917  }
918 
919  /* Release both IP cache */
921 
922  // TODO: can we use purgeAllCached() here instead?
923  purgeDoPurge();
924 }
925 
926 void
928 {
929  auto firstFound = false;
930  if (const auto entry = storeGetPublicByRequestMethod(http->request, Http::METHOD_GET)) {
931  // special entries are only METHOD_GET entries without variance
932  if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
934  const auto err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, nullptr,
935  http->getConn(), http->request, http->al);
936  startError(err);
937  entry->abandon(__func__);
938  return;
939  }
940  firstFound = true;
941  if (!purgeEntry(*entry, Http::METHOD_GET))
942  return;
943  }
944 
946 
947  if (const auto entry = storeGetPublicByRequestMethod(http->request, Http::METHOD_HEAD)) {
948  if (!purgeEntry(*entry, Http::METHOD_HEAD))
949  return;
950  }
951 
952  /* And for Vary, release the base URI if none of the headers was included in the request */
954  && http->request->vary_headers.find('=') != SBuf::npos) {
955  // XXX: performance regression, c_str() reallocates
957 
958  if (const auto entry = storeGetPublic(tmp.c_str(), Http::METHOD_GET)) {
959  if (!purgeEntry(*entry, Http::METHOD_GET, "Vary "))
960  return;
961  }
962 
963  if (const auto entry = storeGetPublic(tmp.c_str(), Http::METHOD_HEAD)) {
964  if (!purgeEntry(*entry, Http::METHOD_HEAD, "Vary "))
965  return;
966  }
967  }
968 
969  if (purgeStatus == Http::scNone)
971 
972  /*
973  * Make a new entry to hold the reply to be written
974  * to the client.
975  */
976  /* TODO: This doesn't need to go through the store. Simply
977  * push down the client chain
978  */
980 
982 
983  const HttpReplyPointer rep(new HttpReply);
984  rep->setHeaders(purgeStatus, nullptr, nullptr, 0, 0, -1);
986  http->storeEntry()->complete();
987 }
988 
989 bool
990 clientReplyContext::purgeEntry(StoreEntry &entry, const Http::MethodType methodType, const char *descriptionPrefix)
991 {
992  debugs(88, 4, descriptionPrefix << Http::MethodStr(methodType) << " '" << entry.url() << "'" );
993 #if USE_HTCP
995 #endif
996  entry.release(true);
998  return true;
999 }
1000 
1001 void
1003 {
1007  http->storeEntry()->buffer();
1008  const HttpReplyPointer rep(new HttpReply);
1009  rep->setHeaders(Http::scOkay, nullptr, "text/plain", http->request->prefixLen(), 0, squid_curtime);
1010  http->storeEntry()->replaceHttpReply(rep);
1012  http->storeEntry()->complete();
1013 }
1014 
1015 #define SENDING_BODY 0
1016 #define SENDING_HDRSONLY 1
1017 int
1019 {
1020  StoreEntry *entry = http->storeEntry();
1021 
1022  if (entry == nullptr)
1023  return 0;
1024 
1025  /*
1026  * For now, 'done_copying' is used for special cases like
1027  * Range and HEAD requests.
1028  */
1029  if (http->flags.done_copying)
1030  return 1;
1031 
1033  // last-chunk was not sent
1034  return 0;
1035  }
1036 
1037  /*
1038  * Handle STORE_OK objects.
1039  * objectLen(entry) will be set properly.
1040  * RC: Does objectLen(entry) include the Headers?
1041  * RC: Yes.
1042  */
1043  if (entry->store_status == STORE_OK) {
1044  return storeOKTransferDone();
1045  } else {
1046  return storeNotOKTransferDone();
1047  }
1048 }
1049 
1050 int
1052 {
1053  assert(http->storeEntry()->objectLen() >= 0);
1054  const auto headers_sz = http->storeEntry()->mem().baseReply().hdr_sz;
1055  assert(http->storeEntry()->objectLen() >= headers_sz);
1056  const auto done = http->out.offset >= http->storeEntry()->objectLen() - headers_sz;
1057  const auto debugLevel = done ? 3 : 5;
1058  debugs(88, debugLevel, done <<
1059  " out.offset=" << http->out.offset <<
1060  " objectLen()=" << http->storeEntry()->objectLen() <<
1061  " headers_sz=" << headers_sz);
1062  return done ? 1 : 0;
1063 }
1064 
1065 int
1067 {
1068  /*
1069  * Now, handle STORE_PENDING objects
1070  */
1071  MemObject *mem = http->storeEntry()->mem_obj;
1072  assert(mem != nullptr);
1073  assert(http->request != nullptr);
1074 
1076  /* haven't found end of headers yet */
1077  return 0;
1078 
1079  // TODO: Use MemObject::expectedReplySize(method) after resolving XXX below.
1080  const auto expectedBodySize = mem->baseReply().content_length;
1081 
1082  // XXX: The code below talks about sending data, and checks stats about
1083  // bytes written to the client connection, but this method must determine
1084  // whether we are done _receiving_ data from Store. This code should work OK
1085  // when expectedBodySize is unknown or matches written data, but it may
1086  // malfunction when we are writing ranges while receiving a full response.
1087 
1088  /*
1089  * Figure out how much data we are supposed to send.
1090  * If we are sending a body and we don't have a content-length,
1091  * then we must wait for the object to become STORE_OK.
1092  */
1093  if (expectedBodySize < 0)
1094  return 0;
1095 
1096  const auto done = http->out.offset >= expectedBodySize;
1097  const auto debugLevel = done ? 3 : 5;
1098  debugs(88, debugLevel, done <<
1099  " out.offset=" << http->out.offset <<
1100  " expectedBodySize=" << expectedBodySize);
1101  return done ? 1 : 0;
1102 }
1103 
1104 /* Preconditions:
1105  * *http is a valid structure.
1106  * fd is either -1, or an open fd.
1107  *
1108  * TODO: enumify this
1109  *
1110  * This function is used by any http request sink, to determine the status
1111  * of the object.
1112  */
1115 {
1116  clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1117  assert (context);
1118  assert (context->http == http);
1119  return context->replyStatus();
1120 }
1121 
1124 {
1125  int done;
1126  /* Here because lower nodes don't need it */
1127 
1128  if (http->storeEntry() == nullptr) {
1129  debugs(88, 5, "clientReplyStatus: no storeEntry");
1130  return STREAM_FAILED; /* yuck, but what can we do? */
1131  }
1132 
1134  /* TODO: Could upstream read errors (result.flags.error) be
1135  * lost, and result in undersize requests being considered
1136  * complete. Should we tcp reset such connections ?
1137  */
1138  debugs(88, 5, "clientReplyStatus: aborted storeEntry");
1139  return STREAM_FAILED;
1140  }
1141 
1142  if ((done = checkTransferDone()) != 0 || flags.complete) {
1143  debugs(88, 5, "clientReplyStatus: transfer is DONE: " << done << flags.complete);
1144  /* Ok we're finished, but how? */
1145 
1147  debugs(88, 5, "clientReplyStatus: truncated response body");
1149  }
1150 
1151  if (!done) {
1152  debugs(88, 5, "clientReplyStatus: closing, !done, but read 0 bytes");
1153  return STREAM_FAILED;
1154  }
1155 
1156  // TODO: See also (and unify with) storeNotOKTransferDone() checks.
1157  const int64_t expectedBodySize =
1159  if (expectedBodySize >= 0 && !http->gotEnough()) {
1160  debugs(88, 5, "clientReplyStatus: client didn't get all it expected");
1162  }
1163 
1164  debugs(88, 5, "clientReplyStatus: stream complete; keepalive=" <<
1166  return STREAM_COMPLETE;
1167  }
1168 
1169  // XXX: Should this be checked earlier? We could return above w/o checking.
1171  debugs(88, 5, "clientReplyStatus: client reply body is too large");
1172  return STREAM_FAILED;
1173  }
1174 
1175  return STREAM_NONE;
1176 }
1177 
1178 /* Responses with no body will not have a content-type header,
1179  * which breaks the rep_mime_type acl, which
1180  * coincidentally, is the most common acl for reply access lists.
1181  * A better long term fix for this is to allow acl matches on the various
1182  * status codes, and then supply a default ruleset that puts these
1183  * codes before any user defines access entries. That way the user
1184  * can choose to block these responses where appropriate, but won't get
1185  * mysterious breakages.
1186  */
1187 bool
1189 {
1190  bool result;
1191 
1192  switch (sline) {
1193 
1194  case Http::scContinue:
1195 
1197 
1198  case Http::scProcessing:
1199 
1200  case Http::scNoContent:
1201 
1202  case Http::scNotModified:
1203  result = true;
1204  break;
1205 
1206  default:
1207  result = false;
1208  }
1209 
1210  return result;
1211 }
1212 
1220 void
1222 {
1223  HttpHeader *hdr = &reply->header;
1224  const bool is_hit = http->loggingTags().isTcpHit();
1225  HttpRequest *request = http->request;
1226 
1227  if (is_hit || collapsedRevalidation == crSlave)
1229  // TODO: RFC 2965 : Must honour Cache-Control: no-cache="set-cookie2" and remove header.
1230 
1231  // if there is not configured a peer proxy with login=PASS or login=PASSTHRU option enabled
1232  // remove the Proxy-Authenticate header
1233  if ( !request->peer_login || (strcmp(request->peer_login,"PASS") != 0 && strcmp(request->peer_login,"PASSTHRU") != 0)) {
1234 #if USE_ADAPTATION
1235  // but allow adaptation services to authenticate clients
1236  // via request satisfaction
1237  if (!http->requestSatisfactionMode())
1238 #endif
1240  }
1241 
1243  // paranoid: ContentLengthInterpreter has cleaned non-generated replies
1245 
1246  // if (request->range)
1247  // clientBuildRangeHeader(http, reply);
1248 
1249  /*
1250  * Add a estimated Age header on cache hits.
1251  */
1252  if (is_hit) {
1253  /*
1254  * Remove any existing Age header sent by upstream caches
1255  * (note that the existing header is passed along unmodified
1256  * on cache misses)
1257  */
1259  /*
1260  * This adds the calculated object age. Note that the details of the
1261  * age calculation is performed by adjusting the timestamp in
1262  * StoreEntry::timestampsSet(), not here.
1263  */
1267  } else if (http->getConn() && http->getConn()->port->actAsOrigin) {
1268  // Swap the Date: header to current time if we are simulating an origin
1270  if (h)
1271  hdr->putExt("X-Origin-Date", h->value.termedBuf());
1275  if (h && http->storeEntry()->expires >= 0) {
1276  hdr->putExt("X-Origin-Expires", h->value.termedBuf());
1279  }
1280  if (http->storeEntry()->timestamp <= squid_curtime) {
1281  // put X-Cache-Age: instead of Age:
1282  char age[64];
1283  snprintf(age, sizeof(age), "%" PRId64, static_cast<int64_t>(squid_curtime - http->storeEntry()->timestamp));
1284  hdr->putExt("X-Cache-Age", age);
1285  }
1286  } else if (http->storeEntry()->timestamp <= squid_curtime) {
1289  }
1290  }
1291 
1292  /* RFC 2616: Section 14.18
1293  *
1294  * Add a Date: header if missing.
1295  * We have access to a clock therefore are required to amend any shortcoming in servers.
1296  *
1297  * NP: done after Age: to prevent ENTRY_SPECIAL double-handling this header.
1298  */
1299  if ( !hdr->has(Http::HdrType::DATE) ) {
1300  if (!http->storeEntry())
1302  else if (http->storeEntry()->timestamp > 0)
1304  else {
1305  debugs(88, DBG_IMPORTANT, "ERROR: Squid BUG #3279: HTTP reply without Date:");
1306  /* dump something useful about the problem */
1308  }
1309  }
1310 
1311  /* Filter unproxyable authentication types */
1312  if (http->loggingTags().oldType != LOG_TCP_DENIED &&
1315  HttpHeaderEntry *e;
1316 
1317  int connection_auth_blocked = 0;
1318  while ((e = hdr->getEntry(&pos))) {
1319  if (e->id == Http::HdrType::WWW_AUTHENTICATE) {
1320  const char *value = e->value.rawBuf();
1321 
1322  if ((strncasecmp(value, "NTLM", 4) == 0 &&
1323  (value[4] == '\0' || value[4] == ' '))
1324  ||
1325  (strncasecmp(value, "Negotiate", 9) == 0 &&
1326  (value[9] == '\0' || value[9] == ' '))
1327  ||
1328  (strncasecmp(value, "Kerberos", 8) == 0 &&
1329  (value[8] == '\0' || value[8] == ' '))) {
1330  if (request->flags.connectionAuthDisabled) {
1331  hdr->delAt(pos, connection_auth_blocked);
1332  continue;
1333  }
1334  request->flags.mustKeepalive = true;
1335  if (!request->flags.accelerated && !request->flags.intercepted) {
1336  httpHeaderPutStrf(hdr, Http::HdrType::PROXY_SUPPORT, "Session-Based-Authentication");
1337  /*
1338  We send "Connection: Proxy-Support" header to mark
1339  Proxy-Support as a hop-by-hop header for intermediaries that do not
1340  understand the semantics of this header. The RFC should have included
1341  this recommendation.
1342  */
1343  httpHeaderPutStrf(hdr, Http::HdrType::CONNECTION, "Proxy-support");
1344  }
1345  break;
1346  }
1347  }
1348  }
1349 
1350  if (connection_auth_blocked)
1351  hdr->refreshMask();
1352  }
1353 
1354 #if USE_AUTH
1355  /* Handle authentication headers */
1356  if (http->loggingTags().oldType == LOG_TCP_DENIED &&
1359  ) {
1360  /* Add authentication header */
1361  /* TODO: alter errorstate to be accel on|off aware. The 0 on the next line
1362  * depends on authenticate behaviour: all schemes to date send no extra
1363  * data on 407/401 responses, and do not check the accel state on 401/407
1364  * responses
1365  */
1367  } else if (request->auth_user_request != nullptr)
1369 #endif
1370 
1371  SBuf cacheStatus(uniqueHostname());
1372  if (const auto hitOrFwd = http->loggingTags().cacheStatusSource())
1373  cacheStatus.append(hitOrFwd);
1374  if (firstStoreLookup_) {
1375  cacheStatus.append(";detail=");
1376  cacheStatus.append(firstStoreLookup_);
1377  }
1378  // TODO: Remove c_str() after converting HttpHeaderEntry::value to SBuf
1379  hdr->putStr(Http::HdrType::CACHE_STATUS, cacheStatus.c_str());
1380 
1381  const bool maySendChunkedReply = !request->multipartRangeRequest() &&
1382  reply->sline.version.protocol == AnyP::PROTO_HTTP && // response is HTTP
1383  (request->http_ver >= Http::ProtocolVersion(1,1));
1384 
1385  /* Check whether we should send keep-alive */
1386  if (!Config.onoff.error_pconns && reply->sline.status() >= 400 && !request->flags.mustKeepalive) {
1387  debugs(33, 3, "clientBuildReplyHeader: Error, don't keep-alive");
1388  request->flags.proxyKeepalive = false;
1389  } else if (!Config.onoff.client_pconns && !request->flags.mustKeepalive) {
1390  debugs(33, 2, "clientBuildReplyHeader: Connection Keep-Alive not requested by admin or client");
1391  request->flags.proxyKeepalive = false;
1392  } else if (request->flags.proxyKeepalive && shutting_down) {
1393  debugs(88, 3, "clientBuildReplyHeader: Shutting down, don't keep-alive.");
1394  request->flags.proxyKeepalive = false;
1395  } else if (request->flags.connectionAuth && !reply->keep_alive) {
1396  debugs(33, 2, "clientBuildReplyHeader: Connection oriented auth but server side non-persistent");
1397  request->flags.proxyKeepalive = false;
1398  } else if (reply->bodySize(request->method) < 0 && !maySendChunkedReply) {
1399  debugs(88, 3, "clientBuildReplyHeader: can't keep-alive, unknown body size" );
1400  request->flags.proxyKeepalive = false;
1401  } else if (fdUsageHigh()&& !request->flags.mustKeepalive) {
1402  debugs(88, 3, "clientBuildReplyHeader: Not many unused FDs, can't keep-alive");
1403  request->flags.proxyKeepalive = false;
1404  } else if (request->flags.sslBumped && !reply->persistent()) {
1405  // We do not really have to close, but we pretend we are a tunnel.
1406  debugs(88, 3, "clientBuildReplyHeader: bumped reply forces close");
1407  request->flags.proxyKeepalive = false;
1408  } else if (request->pinnedConnection() && !reply->persistent()) {
1409  // The peer wants to close the pinned connection
1410  debugs(88, 3, "pinned reply forces close");
1411  request->flags.proxyKeepalive = false;
1412  } else if (http->getConn()) {
1413  ConnStateData * conn = http->getConn();
1414  if (!Comm::IsConnOpen(conn->port->listenConn)) {
1415  // The listening port closed because of a reconfigure
1416  debugs(88, 3, "listening port closed");
1417  request->flags.proxyKeepalive = false;
1418  }
1419  }
1420 
1421  // Decide if we send chunked reply
1422  if (maySendChunkedReply && reply->bodySize(request->method) < 0) {
1423  debugs(88, 3, "clientBuildReplyHeader: chunked reply");
1424  request->flags.chunkedReply = true;
1425  hdr->putStr(Http::HdrType::TRANSFER_ENCODING, "chunked");
1426  }
1427 
1428  hdr->addVia(reply->sline.version);
1429 
1430  /* Signal keep-alive or close explicitly */
1431  hdr->putStr(Http::HdrType::CONNECTION, request->flags.proxyKeepalive ? "keep-alive" : "close");
1432 
1433  /* Surrogate-Control requires Surrogate-Capability from upstream to pass on */
1434  if ( hdr->has(Http::HdrType::SURROGATE_CONTROL) ) {
1437  }
1438  /* TODO: else case: drop any controls intended specifically for our surrogate ID */
1439  }
1440 
1441  httpHdrMangleList(hdr, request, http->al, ROR_REPLY);
1442 }
1443 
1444 void
1446 {
1447  assert(reply == nullptr);
1448 
1450  HTTPMSGLOCK(reply);
1451 
1452  http->al->reply = reply;
1453 
1455  /* RFC 2616 requires us to advertise our version (but only on real HTTP traffic) */
1457  }
1458 
1459  /* do header conversions */
1460  buildReplyHeader();
1461 }
1462 
1466 void
1468 {
1469  StoreEntry *e = http->storeEntry();
1470  assert(e); // or we are not dealing with a hit
1471  // We probably have not locked the entry earlier, unfortunately. We lock it
1472  // now so that we can unlock two lines later (and trigger cleanup).
1473  // Ideally, ClientHttpRequest::storeEntry() should lock/unlock, but it is
1474  // used so inconsistently that simply adding locking there leads to bugs.
1475  e->lock("clientReplyContext::forgetHit");
1476  http->storeEntry(nullptr);
1477  e->unlock("clientReplyContext::forgetHit"); // may delete e
1478 }
1479 
1480 void
1482 {
1483  HttpRequest *r = http->request;
1484 
1485  // client sent CC:no-cache or some other condition has been
1486  // encountered which prevents delivering a public/cached object.
1487  // XXX: The above text does not match the condition below. It might describe
1488  // the opposite condition, but the condition itself should be adjusted
1489  // (e.g., to honor flags.noCache in cache manager requests).
1490  if (!r->flags.noCache || r->flags.internal) {
1491  const auto e = storeGetPublicByRequest(r);
1493  } else {
1494  // "external" no-cache requests skip Store lookups
1495  identifyFoundObject(nullptr, "no-cache");
1496  }
1497 }
1498 
1503 void
1505 {
1506  detailStoreLookup(detail);
1507 
1508  HttpRequest *r = http->request;
1509  http->storeEntry(newEntry);
1510  const auto e = http->storeEntry();
1511 
1512  /* Release IP-cache entries on reload */
1516  if (r->flags.noCache || r->flags.noCacheHack())
1518 
1519  if (!e) {
1521  debugs(85, 3, "StoreEntry is NULL - MISS");
1523  doGetMoreData();
1524  return;
1525  }
1526 
1527  if (Config.onoff.offline) {
1529  debugs(85, 3, "offline HIT " << *e);
1531  doGetMoreData();
1532  return;
1533  }
1534 
1535  if (http->redirect.status) {
1537  debugs(85, 3, "REDIRECT status forced StoreEntry to NULL (no body on 3XX responses) " << *e);
1538  forgetHit();
1540  doGetMoreData();
1541  return;
1542  }
1543 
1544  if (!e->validToSend()) {
1545  debugs(85, 3, "!storeEntryValidToSend MISS " << *e);
1546  forgetHit();
1548  doGetMoreData();
1549  return;
1550  }
1551 
1552  if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1553  /* \li Special entries are always hits, no matter what the client says */
1554  debugs(85, 3, "ENTRY_SPECIAL HIT " << *e);
1556  doGetMoreData();
1557  return;
1558  }
1559 
1560  if (r->flags.noCache) {
1561  debugs(85, 3, "no-cache REFRESH MISS " << *e);
1562  forgetHit();
1564  doGetMoreData();
1565  return;
1566  }
1567 
1568  if (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false)) {
1569  debugs(85, 3, "prohibited CF MISS " << *e);
1570  forgetHit();
1572  doGetMoreData();
1573  return;
1574  }
1575 
1576  debugs(85, 3, "default HIT " << *e);
1578  doGetMoreData();
1579 }
1580 
1582 void
1584 {
1585  if (!firstStoreLookup_) {
1586  debugs(85, 7, detail);
1587  firstStoreLookup_ = detail;
1588  } else {
1589  debugs(85, 7, "ignores " << detail << " after " << firstStoreLookup_);
1590  }
1591 }
1592 
1602 void
1604 {
1605  /* Test preconditions */
1606  assert(aNode != nullptr);
1607  assert(cbdataReferenceValid(aNode));
1608  assert(aNode->node.prev == nullptr);
1609  assert(aNode->node.next != nullptr);
1610  clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1611  assert (context);
1612  assert(context->http == http);
1613 
1614  if (!context->ourNode)
1615  context->ourNode = aNode;
1616 
1617  /* no cbdatareference, this is only used once, and safely */
1618  if (context->flags.storelogiccomplete) {
1619  context->requestMoreBodyFromStore();
1620  return;
1621  }
1622 
1623  if (context->http->request->method == Http::METHOD_PURGE) {
1624  context->purgeRequest();
1625  return;
1626  }
1627 
1628  // OPTIONS with Max-Forwards:0 handled in clientProcessRequest()
1629 
1630  if (context->http->request->method == Http::METHOD_TRACE) {
1631  if (context->http->request->header.getInt64(Http::HdrType::MAX_FORWARDS) == 0) {
1632  context->traceReply();
1633  return;
1634  }
1635 
1636  /* continue forwarding, not finished yet. */
1638 
1639  context->doGetMoreData();
1640  } else
1641  context->identifyStoreObject();
1642 }
1643 
1644 void
1646 {
1647  /* We still have to do store logic processing - vary, cache hit etc */
1648  if (http->storeEntry() != nullptr) {
1649  /* someone found the object in the cache for us */
1650  StoreIOBuffer localTempBuffer;
1651 
1652  http->storeEntry()->lock("clientReplyContext::doGetMoreData");
1653 
1655 
1656  sc = storeClientListAdd(http->storeEntry(), this);
1657 #if USE_DELAY_POOLS
1659 #endif
1660 
1662  /* guarantee nothing has been sent yet! */
1663  assert(http->out.size == 0);
1664  assert(http->out.offset == 0);
1665 
1666  if (ConnStateData *conn = http->getConn()) {
1667  if (Ip::Qos::TheConfig.isHitTosActive()) {
1668  Ip::Qos::doTosLocalHit(conn->clientConnection);
1669  }
1670 
1671  if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
1672  Ip::Qos::doNfmarkLocalHit(conn->clientConnection);
1673  }
1674  }
1675 
1677  } else {
1678  /* MISS CASE, http->loggingTags() are already set! */
1679  processMiss();
1680  }
1681 }
1682 
1684 void
1686 {
1688  clientStreamDetach(node, http);
1689 }
1690 
1695 void
1697 {
1698  clientReplyContext *context = static_cast<clientReplyContext *>(data);
1699  context->sendMoreData (result);
1700 }
1701 
1702 void
1704 {
1705  /* At least, I think that's what this does */
1708 }
1709 
1710 bool
1712 {
1713  return /* aborted request */
1715  /* Upstream read error */ (result.flags.error);
1716 }
1717 
1718 void
1720 {
1726  debugs(88, 5, "A stream error has occurred, marking as complete and sending no data.");
1727  StoreIOBuffer localTempBuffer;
1728  flags.complete = 1;
1729  http->request->flags.streamError = true;
1730  localTempBuffer.flags.error = result.flags.error;
1732  localTempBuffer);
1733 }
1734 
1735 void
1737 {
1738  if (result.length == 0) {
1739  debugs(88, 5, "clientReplyContext::pushStreamData: marking request as complete due to 0 length store result");
1740  flags.complete = 1;
1741  }
1742 
1743  assert(!result.length || result.offset == next()->readBuffer.offset);
1745  result);
1746 }
1747 
1750 {
1752  return getNextNode();
1753 }
1754 
1755 void
1757 {
1760  http->getConn(), http->request, http->al);
1763  startError(err);
1764 
1765 }
1766 
1768 void
1770 {
1772  ErrorState *const err =
1774  nullptr, http->getConn(), http->request, http->al);
1777  startError(err);
1778 }
1779 
1781 void
1783 {
1784  StoreEntry *e = http->storeEntry();
1785  const time_t timestamp = e->timestamp;
1786  const auto temprep = e->mem().freshestReply().make304();
1787  // log as TCP_INM_HIT if code 304 generated for
1788  // If-None-Match request
1789  if (!http->request->flags.ims)
1791  else
1795  e = http->storeEntry();
1796  // Copy timestamp from the original entry so the 304
1797  // reply has a meaningful Age: header.
1798  e->timestampsSet();
1799  e->timestamp = timestamp;
1800  e->replaceHttpReply(temprep);
1801  e->complete();
1802  /*
1803  * TODO: why put this in the store and then serialise it and
1804  * then parse it again. Simply mark the request complete in
1805  * our context and write the reply struct to the client side.
1806  */
1808 }
1809 
1812 void
1814 {
1815  if (http->request->method == Http::METHOD_GET ||
1817  sendNotModified();
1818  else
1820 }
1821 
1822 void
1824 {
1825  /* NP: this should probably soft-fail to a zero-sized-reply error ?? */
1826  assert(reply);
1827 
1829  if (http->loggingTags().oldType == LOG_TCP_DENIED ||
1833  return;
1834  }
1835 
1839  return;
1840  }
1841 
1843  if (!Config.accessList.reply) {
1845  return;
1846  }
1847 
1849  auto replyChecklist =
1851  replyChecklist->updateReply(reply);
1852  ACLFilledChecklist::NonBlockingCheck(std::move(replyChecklist), ProcessReplyAccessResult, this);
1853 }
1854 
1855 void
1857 {
1858  clientReplyContext *me = static_cast<clientReplyContext *>(voidMe);
1859  me->processReplyAccessResult(rv);
1860 }
1861 
1862 void
1864 {
1865  debugs(88, 2, "The reply for " << http->request->method
1866  << ' ' << http->uri << " is " << accessAllowed << ", because it matched "
1867  << accessAllowed.lastCheckDescription());
1868 
1869  if (!accessAllowed.allowed()) {
1870  ErrorState *err;
1871  auto page_id = FindDenyInfoPage(accessAllowed, true);
1872 
1874 
1875  if (page_id == ERR_NONE)
1876  page_id = ERR_ACCESS_DENIED;
1877 
1878  err = clientBuildError(page_id, Http::scForbidden, nullptr,
1879  http->getConn(), http->request, http->al);
1880 
1882 
1884 
1885  startError(err);
1886 
1887  return;
1888  }
1889 
1890  /* Ok, the reply is allowed, */
1892 
1895  auto body_size = lastStreamBufferedBytes.length; // may be zero
1896 
1897  debugs(88, 3, "clientReplyContext::sendMoreData: Appending " <<
1898  (int) body_size << " bytes after " << reply->hdr_sz <<
1899  " bytes of headers");
1900 
1901  if (http->request->method == Http::METHOD_HEAD) {
1902  /* do not forward body for HEAD replies */
1903  body_size = 0;
1904  http->flags.done_copying = true;
1905  flags.complete = 1;
1906  }
1907 
1909  flags.headersSent = true;
1910 
1911  // next()->readBuffer.offset may be positive for Range requests, but our
1912  // localTempBuffer initialization code assumes that next()->readBuffer.data
1913  // points to the response body at offset 0 because the first
1914  // storeClientCopy() request always has offset 0 (i.e. our first Store
1915  // request ignores next()->readBuffer.offset).
1916  //
1917  // XXX: We cannot fully check that assumption: readBuffer.offset field is
1918  // often out of sync with the buffer content, and if some buggy code updates
1919  // the buffer while we were waiting for the processReplyAccessResult()
1920  // callback, we may not notice.
1921 
1922  StoreIOBuffer localTempBuffer;
1923  const auto body_buf = next()->readBuffer.data;
1924 
1925  //Server side may disable ranges under some circumstances.
1926 
1927  if ((!http->request->range))
1928  next()->readBuffer.offset = 0;
1929 
1930  if (next()->readBuffer.offset > 0) {
1931  if (Less(body_size, next()->readBuffer.offset)) {
1932  /* Can't use any of the body we received. send nothing */
1933  localTempBuffer.length = 0;
1934  localTempBuffer.data = nullptr;
1935  } else {
1936  localTempBuffer.length = body_size - next()->readBuffer.offset;
1937  localTempBuffer.data = body_buf + next()->readBuffer.offset;
1938  }
1939  } else {
1940  localTempBuffer.length = body_size;
1941  localTempBuffer.data = body_buf;
1942  }
1943 
1945  http, reply, localTempBuffer);
1946 
1947  return;
1948 }
1949 
1950 void
1952 {
1953  if (deleting)
1954  return;
1955 
1956  debugs(88, 5, http->uri << " got " << result);
1957 
1958  StoreEntry *entry = http->storeEntry();
1959 
1960  if (ConnStateData * conn = http->getConn()) {
1961  if (!conn->isOpen()) {
1962  debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
1963  return;
1964  }
1965  if (conn->pinning.zeroReply) {
1966  debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
1967  return;
1968  }
1969 
1970  if (!flags.headersSent && !http->loggingTags().isTcpHit()) {
1971  // We get here twice if processReplyAccessResult() calls startError().
1972  // TODO: Revise when we check/change QoS markings to reduce syscalls.
1973  if (Ip::Qos::TheConfig.isHitTosActive()) {
1974  Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
1975  }
1976  if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
1977  Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
1978  }
1979  }
1980 
1981  debugs(88, 5, conn->clientConnection <<
1982  " '" << entry->url() << "'" <<
1983  " out.offset=" << http->out.offset);
1984  }
1985 
1986  /* We've got the final data to start pushing... */
1988 
1989  assert(http->request != nullptr);
1990 
1991  /* ESI TODO: remove this assert once everything is stable */
1994 
1995  makeThisHead();
1996 
1997  if (errorInStream(result)) {
1998  sendStreamError(result);
1999  return;
2000  }
2001 
2002  if (!matchesStreamBodyBuffer(result)) {
2003  // Subsequent processing expects response body bytes to be at the start
2004  // of our Client Stream buffer. When given something else (e.g., bytes
2005  // in our tempbuf), we copy and adjust to meet those expectations.
2006  const auto &ourClientStreamsBuffer = next()->readBuffer;
2007  assert(result.length <= ourClientStreamsBuffer.length);
2008  memcpy(ourClientStreamsBuffer.data, result.data, result.length);
2009  result.data = ourClientStreamsBuffer.data;
2010  }
2011 
2012  noteStreamBufferredBytes(result);
2013 
2014  if (flags.headersSent) {
2015  pushStreamData(result);
2016  return;
2017  }
2018 
2019  cloneReply();
2020 
2021 #if USE_DELAY_POOLS
2022  if (sc)
2024 #endif
2025 
2027  return;
2028 }
2029 
2032 bool
2034 {
2035  // the answer is undefined for errors; they are not really "body buffers"
2036  Assure(!their.flags.error);
2037 
2038  if (!their.length)
2039  return true; // an empty body area always matches our body area
2040 
2041  if (their.data != next()->readBuffer.data) {
2042  debugs(88, 7, "no: " << their << " vs. " << next()->readBuffer);
2043  return false;
2044  }
2045 
2046  return true;
2047 }
2048 
2049 void
2051 {
2053  lastStreamBufferedBytes = result; // may be unchanged and/or zero-length
2054 }
2055 
2056 void
2058 {
2059  clientAclChecklistFill(checklist, http);
2060 }
2061 
2062 /* Using this breaks the client layering just a little!
2063  */
2064 void
2066 {
2067  assert(http != nullptr);
2068  /*
2069  * For erroneous requests, we might not have a h->request,
2070  * so make a fake one.
2071  */
2072 
2073  if (http->request == nullptr) {
2074  const auto connManager = http->getConn();
2075  const auto mx = MasterXaction::MakePortful(connManager ? connManager->port : nullptr);
2076  // XXX: These fake URI parameters shadow the real (or error:...) URI.
2077  // TODO: Either always set the request earlier and assert here OR use
2078  // http->uri (converted to Anyp::Uri) to create this catch-all request.
2079  const_cast<HttpRequest *&>(http->request) = new HttpRequest(m, AnyP::PROTO_NONE, "http", null_string, mx);
2081  }
2082 
2083  StoreEntry *e = storeCreateEntry(storeId(), http->log_uri, reqFlags, m);
2084 
2085  // Make entry collapsible ASAP, to increase collapsing chances for others,
2086  // TODO: every must-revalidate and similar request MUST reach the origin,
2087  // but do we have to prohibit others from collapsing on that request?
2088  if (reqFlags.cachable &&
2089  !reqFlags.needValidation &&
2090  (m == Http::METHOD_GET || m == Http::METHOD_HEAD) &&
2092  // make the entry available for future requests now
2093  (void)Store::Root().allowCollapsing(e, reqFlags, m);
2094  }
2095 
2096  sc = storeClientListAdd(e, this);
2097 
2098 #if USE_DELAY_POOLS
2100 #endif
2101 
2102  /* The next line is illegal because we don't know if the client stream
2103  * buffers have been set up
2104  */
2105  // storeClientCopy(http->sc, e, 0, HTTP_REQBUF_SZ, http->reqbuf,
2106  // SendMoreData, this);
2107  /* So, we mark the store logic as complete */
2109 
2110  /* and get the caller to request a read, from wherever they are */
2111  /* NOTE: after ANY data flows down the pipe, even one step,
2112  * this function CAN NOT be used to manage errors
2113  */
2114  http->storeEntry(e);
2115 }
2116 
2117 ErrorState *
2118 clientBuildError(err_type page_id, Http::StatusCode status, char const *url,
2119  const ConnStateData *conn, HttpRequest *request, const AccessLogEntry::Pointer &al)
2120 {
2121  const auto err = new ErrorState(page_id, status, request, al);
2122  err->src_addr = conn && conn->clientConnection ? conn->clientConnection->remote : Ip::Address::NoAddr();
2123 
2124  if (url)
2125  err->url = xstrdup(url);
2126 
2127  return err;
2128 }
2129 
int hdr_sz
Definition: Message.h:81
HttpReplyPointer make304() const
Definition: HttpReply.cc:129
Definition: parse.c:104
@ LOG_TCP_IMS_HIT
Definition: LogTags.h:50
void refreshMask()
Definition: HttpHeader.cc:855
int storeClientIsThisAClient(store_client *sc, void *someClient)
static DelayId DelayClient(ClientHttpRequest *, HttpReply *reply=nullptr)
Definition: DelayId.cc:64
@ SURROGATE_CONTROL
@ METHOD_OTHER
Definition: MethodType.h:93
void httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep)
size_type find(char c, size_type startPos=0) const
Definition: SBuf.cc:584
void removeClientStoreReference(store_client **scp, ClientHttpRequest *http)
bool expectedBodyTooLarge(HttpRequest &request)
Definition: HttpReply.cc:565
time_t timestamp
Definition: Store.h:223
static void AddReplyAuthHeader(HttpReply *rep, UserRequest::Pointer auth_user_request, HttpRequest *request, int accelerated, int internal)
Add the appropriate [Proxy-]Authenticate header to the given reply.
Definition: UserRequest.cc:492
AnyP::ProtocolVersion http_ver
Definition: Message.h:72
void ignoreRange(const char *reason)
forgets about the cached Range header (for a reason)
Definition: HttpRequest.cc:618
@ scUnauthorized
Definition: StatusCode.h:46
@ METHOD_HEAD
Definition: MethodType.h:28
#define DBG_CRITICAL
Definition: Stream.h:37
const char * rawBuf() const
Definition: SquidString.h:87
dlink_node node
Definition: clientStream.h:87
const char * uniqueHostname(void)
Definition: tools.cc:548
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
Definition: ETag.h:17
@ scProcessing
Definition: StatusCode.h:24
ClientStreamData data
Definition: clientStream.h:93
ClientHttpRequest * http
void ensureMemObject(const char *storeId, const char *logUri, const HttpRequestMethod &aMethod)
initialize mem_obj (if needed) and set URIs/method (if missing)
Definition: store.cc:1589
const char * storeId() const
Definition: MemObject.cc:53
CSS clientReplyStatus
void releaseRequest(const bool shareable=false)
Definition: store.cc:458
@ scNone
Definition: StatusCode.h:21
bool needValidation
Definition: RequestFlags.h:50
struct ClientHttpRequest::Redirect redirect
unsigned char cache_key
Store key.
Definition: forward.h:29
SupportOrVeto cachable
whether the response may be stored in the cache
Definition: RequestFlags.h:35
@ METHOD_ENUM_END
Definition: MethodType.h:94
void removeHopByHopEntries()
Definition: HttpHeader.cc:1843
HttpHeader header
Definition: Message.h:74
~clientReplyContext() override
CSR clientGetMoreData
void ipcacheInvalidate(const char *name)
Definition: ipcache.cc:865
void errorAppendEntry(StoreEntry *entry, ErrorState *err)
Definition: errorpage.cc:738
bool isEmpty() const
Definition: SBuf.h:435
void purgeDoPurge()
releases both cached GET and HEAD entries
MemObject * mem_obj
Definition: Store.h:220
RequestFlags flags
Definition: HttpRequest.h:141
void sendNotModifiedOrPreconditionFailedError()
int varyEvaluateMatch(StoreEntry *entry, HttpRequest *request)
int fdUsageHigh(void)
Definition: fd.cc:268
ssize_t HttpHeaderPos
Definition: HttpHeader.h:45
const cache_key * storeKeyPublic(const char *url, const HttpRequestMethod &method, const KeyScope keyScope)
const char * cacheStatusSource() const
Definition: LogTags.cc:124
void clearPublicKeyScope()
Definition: store.cc:609
bool isTcpHit() const
determine if the log tag code indicates a cache HIT
Definition: LogTags.cc:110
const char * url() const
Definition: store.cc:1566
ConnStateData * getConn() const
@ PROTO_NONE
Definition: ProtocolType.h:24
void createStoreEntry(const HttpRequestMethod &m, RequestFlags flags)
@ STREAM_NONE
Definition: enums.h:121
@ ksRevalidation
Definition: store_key_md5.h:20
static bool SmpAware()
whether there are any SMP-aware storages
Definition: Controller.cc:912
void addVia(const AnyP::ProtocolVersion &ver, const HttpHeader *from=nullptr)
Definition: HttpHeader.cc:1078
MemObject & mem()
Definition: Store.h:47
#define HttpHeaderInitPos
Definition: HttpHeader.h:48
@ ENTRY_ABORTED
Definition: enums.h:110
void lock(const char *context)
Definition: store.cc:445
const SBuf & lastCheckDescription() const
describes the ACL that was evaluated last while obtaining this answer (for debugging)
Definition: Acl.cc:123
@ VARY_CANCEL
Definition: enums.h:191
void ipcacheInvalidateNegative(const char *name)
Definition: ipcache.cc:882
bool loopDetected
Definition: RequestFlags.h:40
int doTosLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
Definition: QosConfig.cc:231
bool connectionAuth
Definition: RequestFlags.h:85
int64_t bodySize(const HttpRequestMethod &) const
Definition: HttpReply.cc:377
HttpReply * clone() const override
Definition: HttpReply.cc:611
int64_t offset
Definition: StoreIOBuffer.h:58
Definition: SBuf.h:93
char tempbuf[HTTP_REQBUF_SZ]
@ VARY_MATCH
Definition: enums.h:189
void fillChecklist(ACLFilledChecklist &) const override
configure the given checklist (to reflect the current transaction state)
const char * str
quoted-string
Definition: ETag.h:20
#define xstrdup
void updateLoggingTags(const LogTags_ot code)
update the code in the transaction processing tags
void sendStreamError(StoreIOBuffer const &result)
void removeStoreReference(store_client **scp, StoreEntry **ep)
struct ClientHttpRequest::Flags flags
@ LOG_TCP_CLIENT_REFRESH_MISS
Definition: LogTags.h:49
clientStream_status_t CSS(clientStreamNode *, ClientHttpRequest *)
Auth::UserRequest::Pointer auth_user_request
Definition: HttpRequest.h:127
struct ClientHttpRequest::Out out
C * getRaw() const
Definition: RefCount.h:89
uint16_t flags
Definition: Store.h:231
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:270
ACLFilledChecklist::MakingPointer clientAclChecklistCreate(const acl_access *acl, ClientHttpRequest *http)
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
Http::StatusLine sline
Definition: HttpReply.h:56
time_t expires
Definition: Store.h:225
void HTTPMSGUNLOCK(M *&a)
Definition: Message.h:150
int64_t objectLen() const
Definition: Store.h:253
@ ERR_NONE
Definition: forward.h:15
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1705
@ STREAM_COMPLETE
Definition: enums.h:122
StatusCode
Definition: StatusCode.h:20
CSD clientReplyDetach
err_type
Definition: forward.h:14
Definition: forward.h:17
bool multipartRangeRequest() const
Definition: HttpRequest.cc:431
#define cbdataReference(var)
Definition: cbdata.h:348
bool errorInStream(const StoreIOBuffer &result) const
Http::StatusCode purgeStatus
static void Start(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp)
Initiates request forwarding to a peer or origin server.
Definition: FwdState.cc:338
bool connectionAuthDisabled
Definition: RequestFlags.h:87
@ LOG_TCP_HIT
Definition: LogTags.h:42
@ IF_MODIFIED_SINCE
bool conditional() const
has at least one recognized If-* header
Definition: HttpRequest.cc:569
void httpHeaderPutStrf(HttpHeader *hdr, Http::HdrType id, const char *fmt,...)
StoreEntry * storeGetPublicByRequest(HttpRequest *req, const KeyScope keyScope)
Definition: store.cc:516
ProtocolType protocol
which protocol this version is for
@ LOG_TCP_MISS
Definition: LogTags.h:43
int refreshCheckHTTP(const StoreEntry *entry, HttpRequest *request)
Definition: refresh.cc:582
bool allowCollapsing(StoreEntry *, const RequestFlags &, const HttpRequestMethod &)
tries to make the entry available for collapsing future requests
Definition: Controller.cc:727
CollapsedRevalidation collapsedRevalidation
StoreEntry * storeGetPublicByRequestMethod(HttpRequest *req, const HttpRequestMethod &method, const KeyScope keyScope)
Definition: store.cc:510
@ STREAM_FAILED
Definition: enums.h:132
void sendPreconditionFailedError()
send 412 (Precondition Failed) to client
@ LOG_TCP_REFRESH_FAIL_OLD
Definition: LogTags.h:45
const char * c_str() const
compute the status access.log field
Definition: LogTags.cc:75
bool noCacheHack() const
Definition: RequestFlags.h:133
void updateReply(const HttpReply::Pointer &)
void clientStreamDetach(clientStreamNode *thisObject, ClientHttpRequest *http)
err_type FindDenyInfoPage(const Acl::Answer &answer, const bool redirect_allowed)
Definition: Gadgets.cc:34
void identifyFoundObject(StoreEntry *entry, const char *detail)
@ ERR_ACCESS_DENIED
Definition: forward.h:18
@ ENTRY_SPECIAL
Definition: enums.h:79
@ scGatewayTimeout
Definition: StatusCode.h:77
@ WWW_AUTHENTICATE
class SquidConfig2 Config2
Definition: SquidConfig.cc:14
bool persistent() const
Definition: Message.cc:236
time_t lastmod
Definition: HttpRequest.h:165
void removeIrrelevantContentLength()
Some response status codes prohibit sending Content-Length (RFC 7230 section 3.3.2).
Definition: HttpReply.cc:646
@ HTCP_CLR_INVALIDATION
Definition: enums.h:236
@ LOG_TCP_DENIED_REPLY
Definition: LogTags.h:56
String etag
A strong etag of the cached entry. Used for refreshing that entry.
Definition: HttpRequest.h:189
acl_access * sendHit
Definition: SquidConfig.h:367
@ crSlave
we collapsed on the existing revalidation request
@ ROR_REPLY
bool mayStartHitting() const
Definition: Store.h:284
bool requestSatisfactionMode() const
void startError(ErrorState *err)
@ scSwitchingProtocols
Definition: StatusCode.h:23
void CSD(clientStreamNode *, ClientHttpRequest *)
client stream detach
@ LOG_TCP_REFRESH_FAIL_ERR
Definition: LogTags.h:46
bool hasIfNoneMatchEtag(const HttpRequest &request) const
has ETag matching at least one of the If-None-Match etags
Definition: store.cc:1880
void CSR(clientStreamNode *, ClientHttpRequest *)
client stream read
@ scForbidden
Definition: StatusCode.h:48
@ IN_MEMORY
Definition: enums.h:31
void cacheHit(StoreIOBuffer result)
void sendMoreData(StoreIOBuffer result)
@ scPreconditionFailed
Definition: StatusCode.h:57
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
@ scNotImplemented
Definition: StatusCode.h:74
const HttpReply & baseReply() const
Definition: MemObject.h:60
@ LOG_TCP_REFRESH_MODIFIED
Definition: LogTags.h:47
@ HTCP_CLR_PURGE
Definition: enums.h:235
bool mayInitiateCollapsing() const
whether Squid configuration allows us to become a CF initiator
Definition: StoreClient.h:59
char * peer_login
Definition: HttpRequest.h:163
const Acl::Answer & fastCheck()
Definition: Checklist.cc:298
void setReplyToReply(HttpReply *reply)
creates a store entry for the reply and appends error reply to it
int doNfmarkLocalHit(const Comm::ConnectionPointer &conn)
Definition: QosConfig.cc:280
ConnStateData * pinnedConnection()
Definition: HttpRequest.cc:722
@ ENTRY_BAD_LENGTH
Definition: enums.h:109
unsigned error
Definition: StoreIOBuffer.h:55
void setReplyToStoreEntry(StoreEntry *e, const char *reason)
replaces current response store entry with the given one
@ STREAM_UNPLANNED_COMPLETE
Definition: enums.h:127
void detailStoreLookup(const char *detail)
remembers the very first Store lookup classification, ignoring the rest
#define EBIT_TEST(flag, bit)
Definition: defines.h:67
int collapsed_forwarding
Definition: SquidConfig.h:323
bool failOnValidationError
Definition: RequestFlags.h:52
int error_pconns
Definition: SquidConfig.h:306
static decltype(::storeClientCopy) storeClientCopy
@ VARY_OTHER
Definition: enums.h:190
void handleIMSReply(StoreIOBuffer result)
void evictIfFound(const cache_key *) override
Definition: Controller.cc:492
clientStream_status_t replyStatus()
static void NonBlockingCheck(MakingPointer &&p, ACLCB *cb, void *data)
ErrorState * clientBuildError(err_type, Http::StatusCode, char const *, const ConnStateData *, HttpRequest *, const AccessLogEntry::Pointer &)
int delById(Http::HdrType id)
Definition: HttpHeader.cc:799
Http::HdrType id
Definition: HttpHeader.h:66
struct clientReplyContext::Flags flags
static const Address & NoAddr()
Definition: Address.h:321
void putExt(const char *name, const char *value)
Definition: HttpHeader.cc:1208
int unlock(const char *context)
Definition: store.cc:469
void purgeEntriesByUrl(HttpRequest *req, const char *url)
int64_t getInt64(Http::HdrType id) const
Definition: HttpHeader.cc:1266
@ LOG_TCP_REFRESH_UNMODIFIED
Definition: LogTags.h:44
Ip::Address remote
Definition: Connection.h:152
store_status_t store_status
Definition: Store.h:243
int weak
true if it is a weak validator
Definition: ETag.h:21
void putTime(Http::HdrType id, time_t htime)
Definition: HttpHeader.cc:1119
#define assert(EX)
Definition: assert.h:17
@ scContinue
Definition: StatusCode.h:22
bool intercepted
Definition: RequestFlags.h:66
clientReplyContext(ClientHttpRequest *)
int storeOKTransferDone() const
HierarchyLogEntry hier
Definition: HttpRequest.h:157
int checkNegativeHit() const
Definition: store.cc:1300
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:161
void buffer() override
Definition: store.cc:1601
void neighborsHtcpClear(StoreEntry *e, HttpRequest *req, const HttpRequestMethod &method, htcp_clr_reason reason)
Definition: neighbors.cc:1675
@ TRANSFER_ENCODING
SBuf vary_headers
The variant second-stage cache key. Generated from Vary header pattern for this request.
Definition: HttpRequest.h:168
const AnyP::UriScheme & getScheme() const
Definition: Uri.h:58
@ METHOD_TRACE
Definition: MethodType.h:30
#define cbdataReferenceDone(var)
Definition: cbdata.h:357
void redirect(Http::StatusCode, const char *)
Definition: HttpReply.cc:205
#define Assure(condition)
Definition: Assure.h:35
@ scInternalServerError
Definition: StatusCode.h:73
HttpHdrRange * range
Definition: HttpRequest.h:143
mem_status_t mem_status
Definition: Store.h:239
const char * null_string
const char * c_str()
Definition: SBuf.cc:516
@ LOG_TCP_REFRESH
Definition: LogTags.h:48
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:325
int prefixLen() const
Definition: HttpRequest.cc:366
void dump(int debug_lvl) const
Definition: store.cc:1499
@ STORE_OK
Definition: enums.h:45
time_t squid_curtime
Definition: stub_libtime.cc:20
Comm::ConnectionPointer clientConnection
Definition: Server.h:100
@ crInitiator
we initiated collapsed revalidation request
uint64_t size
Response header and body bytes written to the client connection.
@ LOG_TCP_INM_HIT
Definition: LogTags.h:51
StoreEntry * storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod &method)
Definition: store.cc:759
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
int64_t content_length
Definition: Message.h:83
friend CSR clientGetMoreData
int client_pconns
Definition: SquidConfig.h:304
bool receivedBodyTooLarge(HttpRequest &, int64_t receivedBodySize)
Definition: HttpReply.cc:557
bool matchesStreamBodyBuffer(const StoreIOBuffer &) const
struct SquidConfig2::@102 onoff
@ scNotModified
Definition: StatusCode.h:41
void clientAclChecklistFill(ACLFilledChecklist &checklist, ClientHttpRequest *http)
bool chunkedReply
Definition: RequestFlags.h:99
static const size_type npos
Definition: SBuf.h:100
clientStreamNode * getNextNode() const
const LogTags & loggingTags() const
the processing tags associated with this request transaction.
enum Http::_method_t MethodType
bool mustKeepalive
Definition: RequestFlags.h:83
@ METHOD_PURGE
Definition: MethodType.h:92
bool proxyKeepalive
Definition: RequestFlags.h:42
bool startCollapsingOn(const StoreEntry &, const bool doingRevalidation) const
Definition: store_client.cc:66
LogTags * loggingTags() const override
void complete()
Definition: store.cc:1031
bool didCollapse
whether startCollapsingOn() was called and returned true
Definition: StoreClient.h:64
@ PROTO_HTTPS
Definition: ProtocolType.h:27
void putInt(Http::HdrType id, int number)
Definition: HttpHeader.cc:1101
bool accelerated
Definition: RequestFlags.h:62
HttpRequestMethod method
Definition: HttpRequest.h:114
Config TheConfig
Globally available instance of Qos::Config.
Definition: QosConfig.cc:288
@ scNotFound
Definition: StatusCode.h:49
acl_access * reply
Definition: SquidConfig.h:379
HttpHeaderEntry * findEntry(Http::HdrType id) const
Definition: HttpHeader.cc:735
bool blockedHit() const
whether squid.conf send_hit prevents us from serving this hit
@ PROTO_HTTP
Definition: ProtocolType.h:25
const char * termedBuf() const
Definition: SquidString.h:93
@ LOG_TCP_DENIED
Definition: LogTags.h:55
int doNfmarkLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
Definition: QosConfig.cc:252
bool allowed() const
Definition: Acl.h:82
const char * storeId() const
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition: SBuf.h:279
AnyP::ProtocolVersion version
breakdown of protocol version label: (HTTP/ICY) and (0.9/1.0/1.1)
Definition: StatusLine.h:65
@ ERR_ONLY_IF_CACHED_MISS
Definition: forward.h:39
bool timestampsSet()
Definition: store.cc:1387
@ LOG_TCP_OFFLINE_HIT
Definition: LogTags.h:57
@ scNoContent
Definition: StatusCode.h:31
@ PROXY_AUTHENTICATE
@ ERR_TOO_BIG
Definition: forward.h:40
bool processConditional()
process conditional request from client
int has(Http::HdrType id) const
Definition: HttpHeader.cc:1070
const SBuf storeId()
Definition: HttpRequest.cc:730
int storeNotOKTransferDone() const
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1128
StoreIOBuffer lastStreamBufferedBytes
HTTP response body bytes stored in our Client Stream buffer (if any)
struct SquidConfig::@91 accessList
@ METHOD_NONE
Definition: MethodType.h:22
@ crNone
collapsed revalidation is not allowed for this context
clientStream_status_t
Definition: enums.h:120
void setReplyToError(err_type, Http::StatusCode, char const *, const ConnStateData *, HttpRequest *, const char *, Auth::UserRequest::Pointer)
builds error using clientBuildError() and calls setReplyToError() below
#define Must(condition)
Definition: TextException.h:75
bool alwaysAllowResponse(Http::StatusCode sline) const
@ SURROGATE_CAPABILITY
@ ACCESS_ALLOWED
Definition: Acl.h:42
const char * storeLookupString(bool found) const
#define DBG_IMPORTANT
Definition: Stream.h:38
@ scProxyAuthenticationRequired
Definition: StatusCode.h:52
const char * storeKeyText(const cache_key *key)
AnyP::Port port
destination port of the request that caused serverConnection
Definition: client_side.h:145
LogTags_ot oldType
a set of client protocol, cache use, and other transaction outcome tags
Definition: LogTags.h:96
time_t ims
Definition: HttpRequest.h:145
StoreEntry * storeGetPublic(const char *uri, const HttpRequestMethod &method)
Definition: store.cc:504
const AccessLogEntry::Pointer al
access.log entry
void release(const bool shareable=false)
Definition: store.cc:1146
#define PRId64
Definition: types.h:104
bool purgeEntry(StoreEntry &, const Http::MethodType, const char *descriptionPrefix="")
const HttpReply & freshestReply() const
Definition: MemObject.h:68
#define HTTP_REQBUF_SZ
Definition: forward.h:14
int shutting_down
const char * firstStoreLookup_
@ LOG_TCP_REDIRECT
Definition: LogTags.h:58
store_client * old_sc
int doTosLocalHit(const Comm::ConnectionPointer &conn)
Definition: QosConfig.cc:273
clientStreamNode * next() const
void lastModified(const time_t when)
Definition: Store.h:175
void clientStreamCallback(clientStreamNode *thisObject, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer replyBuffer)
StoreIOBuffer readBuffer
Definition: clientStream.h:94
void delAt(HttpHeaderPos pos, int &headers_deleted)
Definition: HttpHeader.cc:827
static ACLCB ProcessReplyAccessResult
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:170
void sendNotModified()
send 304 (Not Modified) to client
StoreEntry * storeEntry() const
void processReplyAccessResult(const Acl::Answer &accessAllowed)
StoreEntry * loggingEntry() const
@ scOkay
Definition: StatusCode.h:27
clientStreamNode * ourNode
bool hasParsedReplyHeader() const
whether this entry has access to [deserialized] [HTTP] response headers
Definition: store.cc:231
void(void *, StoreIOBuffer) STCB
Definition: StoreClient.h:32
void swapOut(StoreEntry *e)
Definition: HttpRequest.cc:333
HttpHeaderEntry * getEntry(HttpHeaderPos *pos) const
Definition: HttpHeader.cc:716
@ METHOD_GET
Definition: MethodType.h:25
void triggerInitialStoreRead(STCB=SendMoreData)
void setDelayId(DelayId delay_id)
void pushStreamData(const StoreIOBuffer &)
const SBuf & MethodStr(const MethodType m)
Definition: MethodType.h:100
struct SquidConfig::@90 onoff
void * callback_data
Definition: errorpage.h:186
bool modifiedSince(const time_t ims, const int imslen=-1) const
Definition: store.cc:1836
constexpr bool Less(const A a, const B b)
whether integer a is less than integer b, with correct overflow handling
Definition: SquidMath.h:48
static STCB SendMoreData
@ LOG_TCP_MEM_HIT
Definition: LogTags.h:54
int storeUnregister(store_client *sc, StoreEntry *e, void *data)
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:741
unsigned complete
we have read all we can from upstream
bool hasIfMatchEtag(const HttpRequest &request) const
has ETag matching at least one of the If-Match etags
Definition: store.cc:1873
bool hasEtag(ETag &etag) const
whether this entry has an ETag; if yes, puts ETag value into parameter
Definition: store.cc:1862
void host(const char *src)
Definition: Uri.cc:142
void noteStreamBufferredBytes(const StoreIOBuffer &)
struct StoreIOBuffer::@123 flags
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
static STCB HandleIMSReply
short int keep_alive
Definition: HttpReply.h:53
@ VARY_NONE
Definition: enums.h:188
void sendClientUpstreamResponse(const StoreIOBuffer &upstreamResponse)
@ LOG_TCP_SWAPFAIL_MISS
Definition: LogTags.h:52
Http::StatusCode httpStatus
Definition: errorpage.h:173
void storeErrorResponse(HttpReply *reply)
Store a prepared error response. MemObject locks the reply object.
Definition: store.cc:1688
@ ERR_PRECONDITION_FAILED
Definition: forward.h:47
@ LOG_TCP_NEGATIVE_HIT
Definition: LogTags.h:53
CbcPointer< ConnStateData > clientConnectionManager
Definition: HttpRequest.h:230
class SquidConfig Config
Definition: SquidConfig.cc:12
store_client * storeClientListAdd(StoreEntry *e, void *data)
HttpRequest *const request
void clean()
Definition: String.cc:104
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
static Pointer MakePortful(const AnyP::PortCfgPointer &aPort)
Definition: MasterXaction.h:54
Controller & Root()
safely access controller singleton
Definition: Controller.cc:926

 

Introduction

Documentation

Support

Miscellaneous