client_side_reply.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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 "errorpage.h"
17 #include "ETag.h"
18 #include "fd.h"
19 #include "fde.h"
20 #include "format/Token.h"
21 #include "FwdState.h"
22 #include "globals.h"
23 #include "http/Stream.h"
24 #include "HttpHeaderTools.h"
25 #include "HttpReply.h"
26 #include "HttpRequest.h"
27 #include "ip/QosConfig.h"
28 #include "ipcache.h"
29 #include "log/access_log.h"
30 #include "MemObject.h"
31 #include "mime_header.h"
32 #include "neighbors.h"
33 #include "refresh.h"
34 #include "RequestFlags.h"
35 #include "SquidConfig.h"
36 #include "Store.h"
37 #include "StrList.h"
38 #include "tools.h"
39 #if USE_AUTH
40 #include "auth/UserRequest.h"
41 #endif
42 #if USE_DELAY_POOLS
43 #include "DelayPools.h"
44 #endif
45 #if USE_SQUID_ESI
46 #include "esi/Esi.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() */
72 }
73 
75  purgeStatus(Http::scNone),
76  http(cbdataReference(clientContext)),
77  headers_sz(0),
78  sc(NULL),
79  old_reqsize(0),
80  reqsize(0),
81  reqofs(0),
82  ourNode(NULL),
83  reply(NULL),
84  old_entry(NULL),
85  old_sc(NULL),
86  old_lastmod(-1),
87  deleting(false),
88  collapsedRevalidation(crNone)
89 {
90  *tempbuf = 0;
91 }
92 
98 void
100  err_type err, Http::StatusCode status, char const *uri,
101  const ConnStateData *conn, HttpRequest *failedrequest, const char *unparsedrequest,
102 #if USE_AUTH
103  Auth::UserRequest::Pointer auth_user_request
104 #else
105  void*
106 #endif
107 )
108 {
109  auto errstate = clientBuildError(err, status, uri, conn, failedrequest, http->al);
110 
111  if (unparsedrequest)
112  errstate->request_hdrs = xstrdup(unparsedrequest);
113 
114 #if USE_AUTH
115  errstate->auth_user_request = auth_user_request;
116 #endif
117  setReplyToError(failedrequest ? failedrequest->method : HttpRequestMethod(Http::METHOD_NONE), errstate);
118 }
119 
121 {
122  if (errstate->httpStatus == Http::scNotImplemented && http->request)
123  /* prevent confusion over whether we default to persistent or not */
124  http->request->flags.proxyKeepalive = false;
125 
126  http->al->http.code = errstate->httpStatus;
127 
128  if (http->request)
129  http->request->ignoreRange("responding with a Squid-generated error");
130 
131  createStoreEntry(method, RequestFlags());
132  assert(errstate->callback_data == NULL);
133  errorAppendEntry(http->storeEntry(), errstate);
134  /* Now the caller reads to get this */
135 }
136 
137 void
139 {
140  Must(futureReply);
141  http->al->http.code = futureReply->sline.status();
142 
143  HttpRequestMethod method;
144  if (http->request) { // nil on responses to unparsable requests
145  http->request->ignoreRange("responding with a Squid-generated reply");
146  method = http->request->method;
147  }
148 
149  createStoreEntry(method, RequestFlags());
150 
151  http->storeEntry()->storeErrorResponse(futureReply);
152  /* Now the caller reads to get futureReply */
153 }
154 
155 // Assumes that the entry contains an error response without Content-Range.
156 // To use with regular entries, make HTTP Range header removal conditional.
157 void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry, const char *reason)
158 {
159  entry->lock("clientReplyContext::setReplyToStoreEntry"); // removeClientStoreReference() unlocks
160  sc = storeClientListAdd(entry, this);
161 #if USE_DELAY_POOLS
163 #endif
164  reqofs = 0;
165  reqsize = 0;
166  if (http->request)
167  http->request->ignoreRange(reason);
169  http->storeEntry(entry);
170 }
171 
172 void
174  StoreEntry ** ep)
175 {
176  StoreEntry *e;
177  store_client *sc_tmp = *scp;
178 
179  if ((e = *ep) != NULL) {
180  *ep = NULL;
181  storeUnregister(sc_tmp, e, this);
182  *scp = NULL;
183  e->unlock("clientReplyContext::removeStoreReference");
184  }
185 }
186 
187 void
189 {
190  StoreEntry *reference = aHttpRequest->storeEntry();
191  removeStoreReference(scp, &reference);
192  aHttpRequest->storeEntry(reference);
193 }
194 
195 void
197 {
198  assert(old_sc == NULL);
199  debugs(88, 3, "clientReplyContext::saveState: saving store context");
201  old_sc = sc;
206  /* Prevent accessing the now saved entries */
207  http->storeEntry(NULL);
208  sc = NULL;
209  reqsize = 0;
210  reqofs = 0;
211 }
212 
213 void
215 {
216  assert(old_sc != NULL);
217  debugs(88, 3, "clientReplyContext::restoreState: Restoring store context");
220  sc = old_sc;
225  /* Prevent accessed the old saved entries */
226  old_entry = NULL;
227  old_sc = NULL;
228  old_lastmod = -1;
229  old_etag.clean();
230  old_reqsize = 0;
231  tempBuffer.offset = 0;
232 }
233 
234 void
236 {
240 }
241 
244 {
245  return (clientStreamNode *)ourNode->node.next->data;
246 }
247 
248 /* This function is wrong - the client parameters don't include the
249  * header offset
250  */
251 void
253 {
254  /* when confident, 0 becomes reqofs, and then this factors into
255  * startSendProcess
256  */
257  assert(reqofs == 0);
258  StoreIOBuffer localTempBuffer (next()->readBuffer.length, 0, next()->readBuffer.data);
259  storeClientCopy(sc, http->storeEntry(), localTempBuffer, SendMoreData, this);
260 }
261 
262 /* there is an expired entry in the store.
263  * setup a temporary buffer area and perform an IMS to the origin
264  */
265 void
267 {
268  const char *url = storeId();
269  debugs(88, 3, "clientReplyContext::processExpired: '" << http->uri << "'");
270  const time_t lastmod = http->storeEntry()->lastModified();
271  assert(lastmod >= 0);
272  /*
273  * check if we are allowed to contact other servers
274  * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return
275  * a stale entry *if* it matches client requirements
276  */
277 
278  if (http->onlyIfCached()) {
280  return;
281  }
282 
284  http->request->flags.refresh = true;
285 #if STORE_CLIENT_LIST_DEBUG
286  /* Prevent a race with the store client memory free routines
287  */
289 #endif
290  /* Prepare to make a new temporary request */
291  saveState();
292 
293  // TODO: Consider also allowing regular (non-collapsed) revalidation hits.
294  // TODO: support collapsed revalidation for Vary-controlled entries
295  bool collapsingAllowed = Config.onoff.collapsed_forwarding &&
298 
299  StoreEntry *entry = nullptr;
300  if (collapsingAllowed) {
301  if (const auto e = storeGetPublicByRequest(http->request, ksRevalidation)) {
302  if (e->hittingRequiresCollapsing() && startCollapsingOn(*e, true)) {
303  entry = e;
304  entry->lock("clientReplyContext::processExpired#alreadyRevalidating");
305  } else {
306  e->abandon(__FUNCTION__);
307  // assume mayInitiateCollapsing() would fail too
308  collapsingAllowed = false;
309  }
310  }
311  }
312 
313  if (entry) {
314  entry->ensureMemObject(url, http->log_uri, http->request->method);
315  debugs(88, 5, "collapsed on existing revalidation entry: " << *entry);
317  } else {
318  entry = storeCreateEntry(url,
320  /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
321 
322  if (collapsingAllowed && mayInitiateCollapsing() &&
323  Store::Root().allowCollapsing(entry, http->request->flags, http->request->method)) {
324  debugs(88, 5, "allow other revalidation requests to collapse on " << *entry);
326  } else {
328  }
329  }
330 
331  sc = storeClientListAdd(entry, this);
332 #if USE_DELAY_POOLS
333  /* delay_id is already set on original store client */
335 #endif
336 
337  http->request->lastmod = lastmod;
338 
340  ETag etag = {NULL, -1}; // TODO: make that a default ETag constructor
341  if (old_entry->hasEtag(etag) && !etag.weak)
342  http->request->etag = etag.str;
343  }
344 
345  debugs(88, 5, "lastmod " << entry->lastModified());
346  http->storeEntry(entry);
347  assert(http->out.offset == 0);
349 
351  /*
352  * A refcounted pointer so that FwdState stays around as long as
353  * this clientReplyContext does
354  */
357  }
358  /* Register with storage manager to receive updates when data comes in. */
359 
360  if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
361  debugs(88, DBG_CRITICAL, "clientReplyContext::processExpired: Found ENTRY_ABORTED object");
362 
363  {
364  /* start counting the length from 0 */
365  StoreIOBuffer localTempBuffer(HTTP_REQBUF_SZ, 0, tempbuf);
366  storeClientCopy(sc, entry, localTempBuffer, HandleIMSReply, this);
367  }
368 }
369 
370 void
372 {
373  StoreIOBuffer tempresult;
375 
378 
379  /* here the data to send is the data we just received */
380  tempBuffer.offset = 0;
381  old_reqsize = 0;
382  /* sendMoreData tracks the offset as well.
383  * Force it back to zero */
384  reqofs = 0;
386  /* TODO: provide sendMoreData with the ready parsed reply */
387  tempresult.length = reqsize;
388  tempresult.data = tempbuf;
389  sendMoreData(tempresult);
390 }
391 
392 void
394 {
395  clientReplyContext *context = (clientReplyContext *)data;
396  context->handleIMSReply(result);
397 }
398 
399 void
401 {
402  /* Get the old request back */
403  restoreState();
404  /* here the data to send is in the next nodes buffers already */
406  /* sendMoreData tracks the offset as well.
407  * Force it back to zero */
408  reqofs = 0;
409  StoreIOBuffer tempresult (reqsize, reqofs, next()->readBuffer.data);
410  sendMoreData(tempresult);
411 }
412 
413 /* This is the workhorse of the HandleIMSReply callback.
414  *
415  * It is called when we've got data back from the origin following our
416  * IMS request to revalidate a stale entry.
417  */
418 void
420 {
421  if (deleting)
422  return;
423 
424  debugs(88, 3, http->storeEntry()->url() << ", " << (long unsigned) result.length << " bytes");
425 
426  if (http->storeEntry() == NULL)
427  return;
428 
429  if (result.flags.error && !EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
430  return;
431 
433  debugs(88, 3, "CF slave hit private non-shareable " << *http->storeEntry() << ". MISS");
434  // restore context to meet processMiss() expectations
435  restoreState();
437  processMiss();
438  return;
439  }
440 
441  /* update size of the request */
442  reqsize = result.length + reqofs;
443 
444  // request to origin was aborted
446  debugs(88, 3, "request to origin aborted '" << http->storeEntry()->url() << "', sending old entry to client");
449  return;
450  }
451 
452  const auto oldStatus = old_entry->mem().freshestReply().sline.status();
453  const auto &new_rep = http->storeEntry()->mem().freshestReply();
454  const auto status = new_rep.sline.status();
455 
456  // XXX: Disregard stale incomplete (i.e. still being written) borrowed (i.e.
457  // not caused by our request) IMS responses. That new_rep may be very old!
458 
459  // origin replied 304
460  if (status == Http::scNotModified) {
462  http->request->flags.staleIfHit = false; // old_entry is no longer stale
463 
464  // TODO: The update may not be instantaneous. Should we wait for its
465  // completion to avoid spawning too much client-disassociated work?
467 
468  // if client sent IMS
470  // forward the 304 from origin
471  debugs(88, 3, "origin replied 304, revalidated existing entry and forwarding 304 to client");
473  return;
474  }
475 
476  // send existing entry, it's still valid
477  debugs(88, 3, "origin replied 304, revalidated existing entry and sending " << oldStatus << " to client");
479  return;
480  }
481 
482  // origin replied with a non-error code
483  if (status > Http::scNone && status < Http::scInternalServerError) {
484  // RFC 7234 section 4: a cache MUST use the most recent response
485  // (as determined by the Date header field)
486  if (new_rep.olderThan(&old_entry->mem().freshestReply())) {
487  http->al->cache.code.err.ignored = true;
488  debugs(88, 3, "origin replied " << status << " but with an older date header, sending old entry (" << oldStatus << ") to client");
490  return;
491  }
492 
494  debugs(88, 3, "origin replied " << status << ", forwarding to client");
496  return;
497  }
498 
499  // origin replied with an error
502  debugs(88, 3, "origin replied with error " << status << ", forwarding to client due to fail_on_validation_err");
504  return;
505  }
506 
507  // ignore and let client have old entry
509  debugs(88, 3, "origin replied with error " << status << ", sending old entry (" << oldStatus << ") to client");
511 }
512 
515 
523 void
525 {
526  clientReplyContext *context = (clientReplyContext *)data;
527  context->cacheHit(result);
528 }
529 
533 void
535 {
537  if (deleting) {
538  debugs(88, 3, "HIT object being deleted. Ignore the HIT.");
539  return;
540  }
541 
542  StoreEntry *e = http->storeEntry();
543 
544  HttpRequest *r = http->request;
545 
546  debugs(88, 3, "clientCacheHit: " << http->uri << ", " << result.length << " bytes");
547 
548  if (http->storeEntry() == NULL) {
549  debugs(88, 3, "clientCacheHit: request aborted");
550  return;
551  } else if (result.flags.error) {
552  /* swap in failure */
553  debugs(88, 3, "clientCacheHit: swapin failure for " << http->uri);
556  processMiss();
557  return;
558  }
559 
560  // The previously identified hit suddenly became unshareable!
561  // This is common for collapsed forwarding slaves but might also
562  // happen to regular hits because we are called asynchronously.
563  if (!e->mayStartHitting()) {
564  debugs(88, 3, "unshareable " << *e << ". MISS");
566  processMiss();
567  return;
568  }
569 
570  if (result.length == 0) {
571  debugs(88, 5, "store IO buffer has no content. MISS");
572  /* the store couldn't get enough data from the file for us to id the
573  * object
574  */
575  /* treat as a miss */
577  processMiss();
578  return;
579  }
580 
582  /* update size of the request */
583  reqsize = result.length + reqofs;
584 
585  /*
586  * Got the headers, now grok them
587  */
589 
590  if (http->request->storeId().cmp(e->mem_obj->storeId()) != 0) {
591  debugs(33, DBG_IMPORTANT, "clientProcessHit: URL mismatch, '" << e->mem_obj->storeId() << "' != '" << http->request->storeId() << "'");
592  http->updateLoggingTags(LOG_TCP_MISS); // we lack a more precise LOG_*_MISS code
593  processMiss();
594  return;
595  }
596 
597  switch (varyEvaluateMatch(e, r)) {
598 
599  case VARY_NONE:
600  /* No variance detected. Continue as normal */
601  break;
602 
603  case VARY_MATCH:
604  /* This is the correct entity for this request. Continue */
605  debugs(88, 2, "clientProcessHit: Vary MATCH!");
606  break;
607 
608  case VARY_OTHER:
609  /* This is not the correct entity for this request. We need
610  * to requery the cache.
611  */
613  e = NULL;
614  /* Note: varyEvalyateMatch updates the request with vary information
615  * so we only get here once. (it also takes care of cancelling loops)
616  */
617  debugs(88, 2, "clientProcessHit: Vary detected!");
619  return;
620 
621  case VARY_CANCEL:
622  /* varyEvaluateMatch found a object loop. Process as miss */
623  debugs(88, DBG_IMPORTANT, "clientProcessHit: Vary object loop!");
624  http->updateLoggingTags(LOG_TCP_MISS); // we lack a more precise LOG_*_MISS code
625  processMiss();
626  return;
627  }
628 
629  if (r->method == Http::METHOD_PURGE) {
630  debugs(88, 5, "PURGE gets a HIT");
632  e = NULL;
633  purgeRequest();
634  return;
635  }
636 
637  if (e->checkNegativeHit() && !r->flags.noCacheHack()) {
638  debugs(88, 5, "negative-HIT");
640  sendMoreData(result);
641  return;
642  } else if (blockedHit()) {
643  debugs(88, 5, "send_hit forces a MISS");
645  processMiss();
646  return;
647  } else if (!http->flags.internal && refreshCheckHTTP(e, r)) {
648  debugs(88, 5, "clientCacheHit: in refreshCheck() block");
649  /*
650  * We hold a stale copy; it needs to be validated
651  */
652  /*
653  * The 'needValidation' flag is used to prevent forwarding
654  * loops between siblings. If our copy of the object is stale,
655  * then we should probably only use parents for the validation
656  * request. Otherwise two siblings could generate a loop if
657  * both have a stale version of the object.
658  */
659  r->flags.needValidation = true;
660 
661  if (e->lastModified() < 0) {
662  debugs(88, 3, "validate HIT object? NO. Can't calculate entry modification time. Do MISS.");
663  /*
664  * We cannot revalidate entries without knowing their
665  * modification time.
666  * XXX: BUG 1890 objects without Date do not get one added.
667  */
669  processMiss();
670  } else if (r->flags.noCache) {
671  debugs(88, 3, "validate HIT object? NO. Client sent CC:no-cache. Do CLIENT_REFRESH_MISS");
672  /*
673  * This did not match a refresh pattern that overrides no-cache
674  * we should honour the client no-cache header.
675  */
677  processMiss();
678  } else if (r->url.getScheme() == AnyP::PROTO_HTTP || r->url.getScheme() == AnyP::PROTO_HTTPS) {
679  debugs(88, 3, "validate HIT object? YES.");
680  /*
681  * Object needs to be revalidated
682  * XXX This could apply to FTP as well, if Last-Modified is known.
683  */
684  processExpired();
685  } else {
686  debugs(88, 3, "validate HIT object? NO. Client protocol non-HTTP. Do MISS.");
687  /*
688  * We don't know how to re-validate other protocols. Handle
689  * them as if the object has expired.
690  */
692  processMiss();
693  }
694  return;
695  } else if (r->conditional()) {
696  debugs(88, 5, "conditional HIT");
697  if (processConditional())
698  return;
699  }
700 
701  /*
702  * plain ol' cache hit
703  */
704  debugs(88, 5, "plain old HIT");
705 
706 #if USE_DELAY_POOLS
707  if (e->store_status != STORE_OK)
709  else
710 #endif
711  if (e->mem_status == IN_MEMORY)
713  else if (Config.onoff.offline)
715 
716  sendMoreData(result);
717 }
718 
722 void
724 {
725  char *url = http->uri;
726  HttpRequest *r = http->request;
727  ErrorState *err = NULL;
728  debugs(88, 4, r->method << ' ' << url);
729 
734  if (http->storeEntry()) {
736  debugs(88, DBG_CRITICAL, "clientProcessMiss: miss on a special object (" << url << ").");
737  debugs(88, DBG_CRITICAL, "\tlog_type = " << http->loggingTags().c_str());
738  http->storeEntry()->dump(1);
739  }
740 
742  }
743 
745  if (r->method == Http::METHOD_PURGE) {
746  purgeRequest();
747  return;
748  }
749 
751  if (r->method == Http::METHOD_OTHER) {
752  purgeAllCached();
753  }
754 
756  if (http->onlyIfCached()) {
758  return;
759  }
760 
762  if (r->flags.loopDetected) {
763  http->al->http.code = Http::scForbidden;
768  return;
769  } else {
770  assert(http->out.offset == 0);
771  createStoreEntry(r->method, r->flags);
773 
774  if (http->redirect.status) {
775  const HttpReplyPointer rep(new HttpReply);
780  http->storeEntry()->complete();
781  return;
782  }
783 
785 
786  Comm::ConnectionPointer conn = http->getConn() != nullptr ? http->getConn()->clientConnection : nullptr;
789  }
790 }
791 
798 void
800 {
801  debugs(88, 4, http->request->method << ' ' << http->uri);
802  http->al->http.code = Http::scGatewayTimeout;
804  http->getConn(), http->request, http->al);
806  startError(err);
807 }
808 
810 bool
812 {
813  StoreEntry *const e = http->storeEntry();
814 
815  const auto replyStatusCode = e->mem().baseReply().sline.status();
816  if (replyStatusCode != Http::scOkay) {
817  debugs(88, 4, "miss because " << replyStatusCode << " != 200");
819  processMiss();
820  return true;
821  }
822 
823  HttpRequest &r = *http->request;
824 
826  // RFC 2616: reply with 412 Precondition Failed if If-Match did not match
828  return true;
829  }
830 
832  // RFC 7232: If-None-Match recipient MUST ignore IMS
833  r.flags.ims = false;
834  r.ims = -1;
835  r.imslen = 0;
837 
838  if (e->hasIfNoneMatchEtag(r)) {
840  return true;
841  }
842 
843  // None-Match is true (no ETag matched); treat as an unconditional hit
844  return false;
845  }
846 
847  if (r.flags.ims) {
848  // handle If-Modified-Since requests from the client
849  if (e->modifiedSince(r.ims, r.imslen)) {
850  // Modified-Since is true; treat as an unconditional hit
851  return false;
852 
853  } else {
854  // otherwise reply with 304 Not Modified
855  sendNotModified();
856  }
857  return true;
858  }
859 
860  return false;
861 }
862 
864 bool
866 {
868  return false; // hits are not blocked by default
869 
870  if (http->flags.internal)
871  return false; // internal content "hits" cannot be blocked
872 
873  const auto &rep = http->storeEntry()->mem().freshestReply();
874  {
875  std::unique_ptr<ACLFilledChecklist> chl(clientAclChecklistCreate(Config.accessList.sendHit, http));
876  chl->reply = const_cast<HttpReply*>(&rep); // ACLChecklist API bug
877  HTTPMSGLOCK(chl->reply);
878  return !chl->fastCheck().allowed(); // when in doubt, block
879  }
880 }
881 
882 // Purges all entries with a given url
883 // TODO: move to SideAgent parent, when we have one
884 /*
885  * We probably cannot purge Vary-affected responses because their MD5
886  * keys depend on vary headers.
887  */
888 void
889 purgeEntriesByUrl(HttpRequest * req, const char *url)
890 {
892  if (m.respMaybeCacheable()) {
893  const cache_key *key = storeKeyPublic(url, m);
894  debugs(88, 5, m << ' ' << url << ' ' << storeKeyText(key));
895 #if USE_HTCP
896  neighborsHtcpClear(nullptr, req, m, HTCP_CLR_INVALIDATION);
897 #else
898  (void)req;
899 #endif
900  Store::Root().evictIfFound(key);
901  }
902  }
903 }
904 
905 void
907 {
908  // XXX: performance regression, c_str() reallocates
911 }
912 
913 LogTags *
915 {
916  // XXX: clientReplyContext code assumes that http cbdata is always valid.
917  // TODO: Either add cbdataReferenceValid(http) checks in all the relevant
918  // places, like this one, or remove cbdata protection of the http member.
919  return &http->al->cache.code;
920 }
921 
922 void
924 {
925  debugs(88, 3, "Config2.onoff.enable_purge = " <<
927 
928  if (!Config2.onoff.enable_purge) {
931  http->getConn(), http->request, http->al);
932  startError(err);
933  return;
934  }
935 
936  /* Release both IP cache */
938 
939  // TODO: can we use purgeAllCached() here instead?
940  purgeDoPurge();
941 }
942 
943 void
945 {
946  auto firstFound = false;
947  if (const auto entry = storeGetPublicByRequestMethod(http->request, Http::METHOD_GET)) {
948  // special entries are only METHOD_GET entries without variance
949  if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
951  const auto err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, nullptr,
952  http->getConn(), http->request, http->al);
953  startError(err);
954  entry->abandon(__FUNCTION__);
955  return;
956  }
957  firstFound = true;
958  if (!purgeEntry(*entry, Http::METHOD_GET))
959  return;
960  }
961 
963 
964  if (const auto entry = storeGetPublicByRequestMethod(http->request, Http::METHOD_HEAD)) {
965  if (!purgeEntry(*entry, Http::METHOD_HEAD))
966  return;
967  }
968 
969  /* And for Vary, release the base URI if none of the headers was included in the request */
971  && http->request->vary_headers.find('=') != SBuf::npos) {
972  // XXX: performance regression, c_str() reallocates
974 
975  if (const auto entry = storeGetPublic(tmp.c_str(), Http::METHOD_GET)) {
976  if (!purgeEntry(*entry, Http::METHOD_GET, "Vary "))
977  return;
978  }
979 
980  if (const auto entry = storeGetPublic(tmp.c_str(), Http::METHOD_HEAD)) {
981  if (!purgeEntry(*entry, Http::METHOD_HEAD, "Vary "))
982  return;
983  }
984  }
985 
986  if (purgeStatus == Http::scNone)
988 
989  /*
990  * Make a new entry to hold the reply to be written
991  * to the client.
992  */
993  /* TODO: This doesn't need to go through the store. Simply
994  * push down the client chain
995  */
997 
999 
1000  const HttpReplyPointer rep(new HttpReply);
1001  rep->setHeaders(purgeStatus, NULL, NULL, 0, 0, -1);
1002  http->storeEntry()->replaceHttpReply(rep);
1003  http->storeEntry()->complete();
1004 }
1005 
1006 bool
1007 clientReplyContext::purgeEntry(StoreEntry &entry, const Http::MethodType methodType, const char *descriptionPrefix)
1008 {
1009  debugs(88, 4, descriptionPrefix << Http::MethodStr(methodType) << " '" << entry.url() << "'" );
1010 #if USE_HTCP
1012 #endif
1013  entry.release(true);
1015  return true;
1016 }
1017 
1018 void
1020 {
1021  clientStreamNode *nextNode = (clientStreamNode *)node->node.next->data;
1022  StoreIOBuffer localTempBuffer;
1024  localTempBuffer.offset = nextNode->readBuffer.offset + headers_sz;
1025  localTempBuffer.length = nextNode->readBuffer.length;
1026  localTempBuffer.data = nextNode->readBuffer.data;
1028  localTempBuffer, SendMoreData, this);
1030  http->storeEntry()->buffer();
1031  const HttpReplyPointer rep(new HttpReply);
1032  rep->setHeaders(Http::scOkay, NULL, "text/plain", http->request->prefixLen(), 0, squid_curtime);
1033  http->storeEntry()->replaceHttpReply(rep);
1035  http->storeEntry()->complete();
1036 }
1037 
1038 #define SENDING_BODY 0
1039 #define SENDING_HDRSONLY 1
1040 int
1042 {
1043  StoreEntry *entry = http->storeEntry();
1044 
1045  if (entry == NULL)
1046  return 0;
1047 
1048  /*
1049  * For now, 'done_copying' is used for special cases like
1050  * Range and HEAD requests.
1051  */
1052  if (http->flags.done_copying)
1053  return 1;
1054 
1056  // last-chunk was not sent
1057  return 0;
1058  }
1059 
1060  /*
1061  * Handle STORE_OK objects.
1062  * objectLen(entry) will be set proprely.
1063  * RC: Does objectLen(entry) include the Headers?
1064  * RC: Yes.
1065  */
1066  if (entry->store_status == STORE_OK) {
1067  return storeOKTransferDone();
1068  } else {
1069  return storeNotOKTransferDone();
1070  }
1071 }
1072 
1073 int
1075 {
1076  assert(http->storeEntry()->objectLen() >= 0);
1078  if (http->out.offset >= http->storeEntry()->objectLen() - headers_sz) {
1079  debugs(88,3, "storeOKTransferDone " <<
1080  " out.offset=" << http->out.offset <<
1081  " objectLen()=" << http->storeEntry()->objectLen() <<
1082  " headers_sz=" << headers_sz);
1083  return 1;
1084  }
1085 
1086  return 0;
1087 }
1088 
1089 int
1091 {
1092  /*
1093  * Now, handle STORE_PENDING objects
1094  */
1095  MemObject *mem = http->storeEntry()->mem_obj;
1096  assert(mem != NULL);
1097  assert(http->request != NULL);
1098 
1099  /* mem->reply was wrong because it uses the UPSTREAM header length!!! */
1100  if (headers_sz == 0)
1101  /* haven't found end of headers yet */
1102  return 0;
1103 
1104  // TODO: Use MemObject::expectedReplySize(method) after resolving XXX below.
1105  const auto expectedBodySize = mem->baseReply().content_length;
1106 
1107  // XXX: The code below talks about sending data, and checks stats about
1108  // bytes written to the client connection, but this method must determine
1109  // whether we are done _receiving_ data from Store. This code should work OK
1110  // when expectedBodySize is unknown or matches written data, but it may
1111  // malfunction when we are writing ranges while receiving a full response.
1112 
1113  /*
1114  * Figure out how much data we are supposed to send.
1115  * If we are sending a body and we don't have a content-length,
1116  * then we must wait for the object to become STORE_OK.
1117  */
1118  if (expectedBodySize < 0)
1119  return 0;
1120 
1121  const uint64_t expectedLength = expectedBodySize + http->out.headers_sz;
1122 
1123  if (http->out.size < expectedLength)
1124  return 0;
1125  else {
1126  debugs(88,3, "storeNotOKTransferDone " <<
1127  " out.size=" << http->out.size <<
1128  " expectedLength=" << expectedLength);
1129  return 1;
1130  }
1131 }
1132 
1133 /* Preconditions:
1134  * *http is a valid structure.
1135  * fd is either -1, or an open fd.
1136  *
1137  * TODO: enumify this
1138  *
1139  * This function is used by any http request sink, to determine the status
1140  * of the object.
1141  */
1144 {
1145  clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1146  assert (context);
1147  assert (context->http == http);
1148  return context->replyStatus();
1149 }
1150 
1153 {
1154  int done;
1155  /* Here because lower nodes don't need it */
1156 
1157  if (http->storeEntry() == NULL) {
1158  debugs(88, 5, "clientReplyStatus: no storeEntry");
1159  return STREAM_FAILED; /* yuck, but what can we do? */
1160  }
1161 
1163  /* TODO: Could upstream read errors (result.flags.error) be
1164  * lost, and result in undersize requests being considered
1165  * complete. Should we tcp reset such connections ?
1166  */
1167  debugs(88, 5, "clientReplyStatus: aborted storeEntry");
1168  return STREAM_FAILED;
1169  }
1170 
1171  if ((done = checkTransferDone()) != 0 || flags.complete) {
1172  debugs(88, 5, "clientReplyStatus: transfer is DONE: " << done << flags.complete);
1173  /* Ok we're finished, but how? */
1174 
1176  debugs(88, 5, "clientReplyStatus: truncated response body");
1178  }
1179 
1180  if (!done) {
1181  debugs(88, 5, "clientReplyStatus: closing, !done, but read 0 bytes");
1182  return STREAM_FAILED;
1183  }
1184 
1185  // TODO: See also (and unify with) storeNotOKTransferDone() checks.
1186  const int64_t expectedBodySize =
1188  if (expectedBodySize >= 0 && !http->gotEnough()) {
1189  debugs(88, 5, "clientReplyStatus: client didn't get all it expected");
1191  }
1192 
1193  debugs(88, 5, "clientReplyStatus: stream complete; keepalive=" <<
1195  return STREAM_COMPLETE;
1196  }
1197 
1198  // XXX: Should this be checked earlier? We could return above w/o checking.
1199  if (reply->receivedBodyTooLarge(*http->request, http->out.offset - 4096)) {
1200  /* 4096 is a margin for the HTTP headers included in out.offset */
1201  debugs(88, 5, "clientReplyStatus: client reply body is too large");
1202  return STREAM_FAILED;
1203  }
1204 
1205  return STREAM_NONE;
1206 }
1207 
1208 /* Responses with no body will not have a content-type header,
1209  * which breaks the rep_mime_type acl, which
1210  * coincidentally, is the most common acl for reply access lists.
1211  * A better long term fix for this is to allow acl matches on the various
1212  * status codes, and then supply a default ruleset that puts these
1213  * codes before any user defines access entries. That way the user
1214  * can choose to block these responses where appropriate, but won't get
1215  * mysterious breakages.
1216  */
1217 bool
1219 {
1220  bool result;
1221 
1222  switch (sline) {
1223 
1224  case Http::scContinue:
1225 
1227 
1228  case Http::scProcessing:
1229 
1230  case Http::scNoContent:
1231 
1232  case Http::scNotModified:
1233  result = true;
1234  break;
1235 
1236  default:
1237  result = false;
1238  }
1239 
1240  return result;
1241 }
1242 
1250 void
1252 {
1253  HttpHeader *hdr = &reply->header;
1254  const bool is_hit = http->loggingTags().isTcpHit();
1256 
1257  if (is_hit || collapsedRevalidation == crSlave)
1259  // TODO: RFC 2965 : Must honour Cache-Control: no-cache="set-cookie2" and remove header.
1260 
1261  // if there is not configured a peer proxy with login=PASS or login=PASSTHRU option enabled
1262  // remove the Proxy-Authenticate header
1263  if ( !request->peer_login || (strcmp(request->peer_login,"PASS") != 0 && strcmp(request->peer_login,"PASSTHRU") != 0)) {
1264 #if USE_ADAPTATION
1265  // but allow adaptation services to authenticate clients
1266  // via request satisfaction
1267  if (!http->requestSatisfactionMode())
1268 #endif
1270  }
1271 
1273  // paranoid: ContentLengthInterpreter has cleaned non-generated replies
1275 
1276  // if (request->range)
1277  // clientBuildRangeHeader(http, reply);
1278 
1279  /*
1280  * Add a estimated Age header on cache hits.
1281  */
1282  if (is_hit) {
1283  /*
1284  * Remove any existing Age header sent by upstream caches
1285  * (note that the existing header is passed along unmodified
1286  * on cache misses)
1287  */
1289  /*
1290  * This adds the calculated object age. Note that the details of the
1291  * age calculation is performed by adjusting the timestamp in
1292  * StoreEntry::timestampsSet(), not here.
1293  */
1297  } else if (http->getConn() && http->getConn()->port->actAsOrigin) {
1298  // Swap the Date: header to current time if we are simulating an origin
1300  if (h)
1301  hdr->putExt("X-Origin-Date", h->value.termedBuf());
1305  if (h && http->storeEntry()->expires >= 0) {
1306  hdr->putExt("X-Origin-Expires", h->value.termedBuf());
1309  }
1310  if (http->storeEntry()->timestamp <= squid_curtime) {
1311  // put X-Cache-Age: instead of Age:
1312  char age[64];
1313  snprintf(age, sizeof(age), "%" PRId64, static_cast<int64_t>(squid_curtime - http->storeEntry()->timestamp));
1314  hdr->putExt("X-Cache-Age", age);
1315  }
1316  } else if (http->storeEntry()->timestamp <= squid_curtime) {
1319  /* Signal old objects. NB: rfc 2616 is not clear,
1320  * by implication, on whether we should do this to all
1321  * responses, or only cache hits.
1322  * 14.46 states it ONLY applies for heuristically calculated
1323  * freshness values, 13.2.4 doesn't specify the same limitation.
1324  * We interpret RFC 2616 under the combination.
1325  */
1326  /* TODO: if maxage or s-maxage is present, don't do this */
1327 
1328  if (squid_curtime - http->storeEntry()->timestamp >= 86400)
1329  hdr->putWarning(113, "This cache hit is still fresh and more than 1 day old");
1330  }
1331  }
1332 
1333  /* RFC 2616: Section 14.18
1334  *
1335  * Add a Date: header if missing.
1336  * We have access to a clock therefore are required to amend any shortcoming in servers.
1337  *
1338  * NP: done after Age: to prevent ENTRY_SPECIAL double-handling this header.
1339  */
1340  if ( !hdr->has(Http::HdrType::DATE) ) {
1341  if (!http->storeEntry())
1343  else if (http->storeEntry()->timestamp > 0)
1345  else {
1346  debugs(88, DBG_IMPORTANT, "ERROR: Squid BUG #3279: HTTP reply without Date:");
1347  /* dump something useful about the problem */
1349  }
1350  }
1351 
1352  // add Warnings required by RFC 2616 if serving a stale hit
1354  hdr->putWarning(110, "Response is stale");
1356  hdr->putWarning(111, "Revalidation failed");
1357  }
1358 
1359  /* Filter unproxyable authentication types */
1360  if (http->loggingTags().oldType != LOG_TCP_DENIED &&
1363  HttpHeaderEntry *e;
1364 
1365  int connection_auth_blocked = 0;
1366  while ((e = hdr->getEntry(&pos))) {
1367  if (e->id == Http::HdrType::WWW_AUTHENTICATE) {
1368  const char *value = e->value.rawBuf();
1369 
1370  if ((strncasecmp(value, "NTLM", 4) == 0 &&
1371  (value[4] == '\0' || value[4] == ' '))
1372  ||
1373  (strncasecmp(value, "Negotiate", 9) == 0 &&
1374  (value[9] == '\0' || value[9] == ' '))
1375  ||
1376  (strncasecmp(value, "Kerberos", 8) == 0 &&
1377  (value[8] == '\0' || value[8] == ' '))) {
1378  if (request->flags.connectionAuthDisabled) {
1379  hdr->delAt(pos, connection_auth_blocked);
1380  continue;
1381  }
1382  request->flags.mustKeepalive = true;
1383  if (!request->flags.accelerated && !request->flags.intercepted) {
1384  httpHeaderPutStrf(hdr, Http::HdrType::PROXY_SUPPORT, "Session-Based-Authentication");
1385  /*
1386  We send "Connection: Proxy-Support" header to mark
1387  Proxy-Support as a hop-by-hop header for intermediaries that do not
1388  understand the semantics of this header. The RFC should have included
1389  this recommendation.
1390  */
1391  httpHeaderPutStrf(hdr, Http::HdrType::CONNECTION, "Proxy-support");
1392  }
1393  break;
1394  }
1395  }
1396  }
1397 
1398  if (connection_auth_blocked)
1399  hdr->refreshMask();
1400  }
1401 
1402 #if USE_AUTH
1403  /* Handle authentication headers */
1404  if (http->loggingTags().oldType == LOG_TCP_DENIED &&
1407  ) {
1408  /* Add authentication header */
1409  /* TODO: alter errorstate to be accel on|off aware. The 0 on the next line
1410  * depends on authenticate behaviour: all schemes to date send no extra
1411  * data on 407/401 responses, and do not check the accel state on 401/407
1412  * responses
1413  */
1414  Auth::UserRequest::AddReplyAuthHeader(reply, request->auth_user_request, request, 0, 1);
1415  } else if (request->auth_user_request != NULL)
1417 #endif
1418 
1419  SBuf cacheStatus(uniqueHostname());
1420  if (const auto hitOrFwd = http->loggingTags().cacheStatusSource())
1421  cacheStatus.append(hitOrFwd);
1422  if (firstStoreLookup_) {
1423  cacheStatus.append(";detail=");
1424  cacheStatus.append(firstStoreLookup_);
1425  }
1426  // TODO: Remove c_str() after converting HttpHeaderEntry::value to SBuf
1427  hdr->putStr(Http::HdrType::CACHE_STATUS, cacheStatus.c_str());
1428 
1429  const bool maySendChunkedReply = !request->multipartRangeRequest() &&
1430  reply->sline.version.protocol == AnyP::PROTO_HTTP && // response is HTTP
1431  (request->http_ver >= Http::ProtocolVersion(1,1));
1432 
1433  /* Check whether we should send keep-alive */
1434  if (!Config.onoff.error_pconns && reply->sline.status() >= 400 && !request->flags.mustKeepalive) {
1435  debugs(33, 3, "clientBuildReplyHeader: Error, don't keep-alive");
1436  request->flags.proxyKeepalive = false;
1437  } else if (!Config.onoff.client_pconns && !request->flags.mustKeepalive) {
1438  debugs(33, 2, "clientBuildReplyHeader: Connection Keep-Alive not requested by admin or client");
1439  request->flags.proxyKeepalive = false;
1440  } else if (request->flags.proxyKeepalive && shutting_down) {
1441  debugs(88, 3, "clientBuildReplyHeader: Shutting down, don't keep-alive.");
1442  request->flags.proxyKeepalive = false;
1443  } else if (request->flags.connectionAuth && !reply->keep_alive) {
1444  debugs(33, 2, "clientBuildReplyHeader: Connection oriented auth but server side non-persistent");
1445  request->flags.proxyKeepalive = false;
1446  } else if (reply->bodySize(request->method) < 0 && !maySendChunkedReply) {
1447  debugs(88, 3, "clientBuildReplyHeader: can't keep-alive, unknown body size" );
1448  request->flags.proxyKeepalive = false;
1449  } else if (fdUsageHigh()&& !request->flags.mustKeepalive) {
1450  debugs(88, 3, "clientBuildReplyHeader: Not many unused FDs, can't keep-alive");
1451  request->flags.proxyKeepalive = false;
1452  } else if (request->flags.sslBumped && !reply->persistent()) {
1453  // We do not really have to close, but we pretend we are a tunnel.
1454  debugs(88, 3, "clientBuildReplyHeader: bumped reply forces close");
1455  request->flags.proxyKeepalive = false;
1456  } else if (request->pinnedConnection() && !reply->persistent()) {
1457  // The peer wants to close the pinned connection
1458  debugs(88, 3, "pinned reply forces close");
1459  request->flags.proxyKeepalive = false;
1460  } else if (http->getConn()) {
1461  ConnStateData * conn = http->getConn();
1462  if (!Comm::IsConnOpen(conn->port->listenConn)) {
1463  // The listening port closed because of a reconfigure
1464  debugs(88, 3, "listening port closed");
1465  request->flags.proxyKeepalive = false;
1466  }
1467  }
1468 
1469  // Decide if we send chunked reply
1470  if (maySendChunkedReply && reply->bodySize(request->method) < 0) {
1471  debugs(88, 3, "clientBuildReplyHeader: chunked reply");
1472  request->flags.chunkedReply = true;
1473  hdr->putStr(Http::HdrType::TRANSFER_ENCODING, "chunked");
1474  }
1475 
1476  hdr->addVia(reply->sline.version);
1477 
1478  /* Signal keep-alive or close explicitly */
1479  hdr->putStr(Http::HdrType::CONNECTION, request->flags.proxyKeepalive ? "keep-alive" : "close");
1480 
1481 #if ADD_X_REQUEST_URI
1482  /*
1483  * Knowing the URI of the request is useful when debugging persistent
1484  * connections in a client; we cannot guarantee the order of http headers,
1485  * but X-Request-URI is likely to be the very last header to ease use from a
1486  * debugger [hdr->entries.count-1].
1487  */
1489  http->memOjbect()->url ? http->memObject()->url : http->uri);
1490 
1491 #endif
1492 
1493  /* Surrogate-Control requires Surrogate-Capability from upstream to pass on */
1494  if ( hdr->has(Http::HdrType::SURROGATE_CONTROL) ) {
1495  if (!request->header.has(Http::HdrType::SURROGATE_CAPABILITY)) {
1497  }
1498  /* TODO: else case: drop any controls intended specifically for our surrogate ID */
1499  }
1500 
1502 }
1503 
1504 void
1506 {
1507  assert(reply == NULL);
1508 
1510  HTTPMSGLOCK(reply);
1511 
1512  http->al->reply = reply;
1513 
1515  /* RFC 2616 requires us to advertise our version (but only on real HTTP traffic) */
1517  }
1518 
1519  /* do header conversions */
1520  buildReplyHeader();
1521 }
1522 
1526 void
1528 {
1529  StoreEntry *e = http->storeEntry();
1530  assert(e); // or we are not dealing with a hit
1531  // We probably have not locked the entry earlier, unfortunately. We lock it
1532  // now so that we can unlock two lines later (and trigger cleanup).
1533  // Ideally, ClientHttpRequest::storeEntry() should lock/unlock, but it is
1534  // used so inconsistently that simply adding locking there leads to bugs.
1535  e->lock("clientReplyContext::forgetHit");
1536  http->storeEntry(NULL);
1537  e->unlock("clientReplyContext::forgetHit"); // may delete e
1538 }
1539 
1540 void
1542 {
1543  HttpRequest *r = http->request;
1544 
1545  // client sent CC:no-cache or some other condition has been
1546  // encountered which prevents delivering a public/cached object.
1547  if (!r->flags.noCache || r->flags.internal) {
1548  const auto e = storeGetPublicByRequest(r);
1550  } else {
1551  // "external" no-cache requests skip Store lookups
1552  identifyFoundObject(nullptr, "no-cache");
1553  }
1554 }
1555 
1560 void
1562 {
1563  detailStoreLookup(detail);
1564 
1565  HttpRequest *r = http->request;
1566  http->storeEntry(newEntry);
1567  const auto e = http->storeEntry();
1568 
1569  /* Release IP-cache entries on reload */
1573  if (r->flags.noCache || r->flags.noCacheHack())
1575 
1576  if (!e) {
1578  debugs(85, 3, "StoreEntry is NULL - MISS");
1580  doGetMoreData();
1581  return;
1582  }
1583 
1584  if (Config.onoff.offline) {
1586  debugs(85, 3, "offline HIT " << *e);
1588  doGetMoreData();
1589  return;
1590  }
1591 
1592  if (http->redirect.status) {
1594  debugs(85, 3, "REDIRECT status forced StoreEntry to NULL (no body on 3XX responses) " << *e);
1595  forgetHit();
1597  doGetMoreData();
1598  return;
1599  }
1600 
1601  if (!e->validToSend()) {
1602  debugs(85, 3, "!storeEntryValidToSend MISS " << *e);
1603  forgetHit();
1605  doGetMoreData();
1606  return;
1607  }
1608 
1609  if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1610  /* \li Special entries are always hits, no matter what the client says */
1611  debugs(85, 3, "ENTRY_SPECIAL HIT " << *e);
1613  doGetMoreData();
1614  return;
1615  }
1616 
1617  if (r->flags.noCache) {
1618  debugs(85, 3, "no-cache REFRESH MISS " << *e);
1619  forgetHit();
1621  doGetMoreData();
1622  return;
1623  }
1624 
1625  if (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false)) {
1626  debugs(85, 3, "prohibited CF MISS " << *e);
1627  forgetHit();
1629  doGetMoreData();
1630  return;
1631  }
1632 
1633  debugs(85, 3, "default HIT " << *e);
1635  doGetMoreData();
1636 }
1637 
1639 void
1641 {
1642  if (!firstStoreLookup_) {
1643  debugs(85, 7, detail);
1644  firstStoreLookup_ = detail;
1645  } else {
1646  debugs(85, 7, "ignores " << detail << " after " << firstStoreLookup_);
1647  }
1648 }
1649 
1659 void
1661 {
1662  /* Test preconditions */
1663  assert(aNode != NULL);
1664  assert(cbdataReferenceValid(aNode));
1665  assert(aNode->node.prev == NULL);
1666  assert(aNode->node.next != NULL);
1667  clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1668  assert (context);
1669  assert(context->http == http);
1670 
1671  clientStreamNode *next = ( clientStreamNode *)aNode->node.next->data;
1672 
1673  if (!context->ourNode)
1674  context->ourNode = aNode;
1675 
1676  /* no cbdatareference, this is only used once, and safely */
1677  if (context->flags.storelogiccomplete) {
1678  StoreIOBuffer tempBuffer;
1679  tempBuffer.offset = next->readBuffer.offset + context->headers_sz;
1680  tempBuffer.length = next->readBuffer.length;
1681  tempBuffer.data = next->readBuffer.data;
1682 
1683  storeClientCopy(context->sc, http->storeEntry(),
1684  tempBuffer, clientReplyContext::SendMoreData, context);
1685  return;
1686  }
1687 
1688  if (context->http->request->method == Http::METHOD_PURGE) {
1689  context->purgeRequest();
1690  return;
1691  }
1692 
1693  // OPTIONS with Max-Forwards:0 handled in clientProcessRequest()
1694 
1695  if (context->http->request->method == Http::METHOD_TRACE) {
1696  if (context->http->request->header.getInt64(Http::HdrType::MAX_FORWARDS) == 0) {
1697  context->traceReply(aNode);
1698  return;
1699  }
1700 
1701  /* continue forwarding, not finished yet. */
1703 
1704  context->doGetMoreData();
1705  } else
1706  context->identifyStoreObject();
1707 }
1708 
1709 void
1711 {
1712  /* We still have to do store logic processing - vary, cache hit etc */
1713  if (http->storeEntry() != NULL) {
1714  /* someone found the object in the cache for us */
1715  StoreIOBuffer localTempBuffer;
1716 
1717  http->storeEntry()->lock("clientReplyContext::doGetMoreData");
1718 
1720 
1721  sc = storeClientListAdd(http->storeEntry(), this);
1722 #if USE_DELAY_POOLS
1724 #endif
1725 
1727  reqofs = 0;
1728  /* guarantee nothing has been sent yet! */
1729  assert(http->out.size == 0);
1730  assert(http->out.offset == 0);
1731 
1732  if (ConnStateData *conn = http->getConn()) {
1733  if (Ip::Qos::TheConfig.isHitTosActive()) {
1734  Ip::Qos::doTosLocalHit(conn->clientConnection);
1735  }
1736 
1737  if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
1738  Ip::Qos::doNfmarkLocalHit(conn->clientConnection);
1739  }
1740  }
1741 
1742  localTempBuffer.offset = reqofs;
1743  localTempBuffer.length = getNextNode()->readBuffer.length;
1744  localTempBuffer.data = getNextNode()->readBuffer.data;
1745  storeClientCopy(sc, http->storeEntry(), localTempBuffer, CacheHit, this);
1746  } else {
1747  /* MISS CASE, http->loggingTags() are already set! */
1748  processMiss();
1749  }
1750 }
1751 
1753 void
1755 {
1757  clientStreamDetach(node, http);
1758 }
1759 
1764 void
1766 {
1767  clientReplyContext *context = static_cast<clientReplyContext *>(data);
1768  context->sendMoreData (result);
1769 }
1770 
1771 void
1773 {
1774  /* At least, I think that's what this does */
1777 }
1778 
1779 bool
1780 clientReplyContext::errorInStream(StoreIOBuffer const &result, size_t const &sizeToProcess)const
1781 {
1782  return /* aborted request */
1784  /* Upstream read error */ (result.flags.error) ||
1785  /* Upstream EOF */ (sizeToProcess == 0);
1786 }
1787 
1788 void
1790 {
1796  debugs(88, 5, "A stream error has occurred, marking as complete and sending no data.");
1797  StoreIOBuffer localTempBuffer;
1798  flags.complete = 1;
1799  http->request->flags.streamError = true;
1800  localTempBuffer.flags.error = result.flags.error;
1802  localTempBuffer);
1803 }
1804 
1805 void
1807 {
1808  StoreIOBuffer localTempBuffer;
1809 
1810  if (result.length == 0) {
1811  debugs(88, 5, "clientReplyContext::pushStreamData: marking request as complete due to 0 length store result");
1812  flags.complete = 1;
1813  }
1814 
1815  assert(result.offset - headers_sz == next()->readBuffer.offset);
1816  localTempBuffer.offset = result.offset - headers_sz;
1817  localTempBuffer.length = result.length;
1818 
1819  if (localTempBuffer.length)
1820  localTempBuffer.data = source;
1821 
1823  localTempBuffer);
1824 }
1825 
1828 {
1830  return getNextNode();
1831 }
1832 
1833 void
1835 {
1838  http->getConn(), http->request, http->al);
1841  startError(err);
1842 
1843 }
1844 
1846 void
1848 {
1850  ErrorState *const err =
1852  nullptr, http->getConn(), http->request, http->al);
1855  startError(err);
1856 }
1857 
1859 void
1861 {
1862  StoreEntry *e = http->storeEntry();
1863  const time_t timestamp = e->timestamp;
1864  const auto temprep = e->mem().freshestReply().make304();
1865  // log as TCP_INM_HIT if code 304 generated for
1866  // If-None-Match request
1867  if (!http->request->flags.ims)
1869  else
1873  e = http->storeEntry();
1874  // Copy timestamp from the original entry so the 304
1875  // reply has a meaningful Age: header.
1876  e->timestampsSet();
1877  e->timestamp = timestamp;
1878  e->replaceHttpReply(temprep);
1879  e->complete();
1880  /*
1881  * TODO: why put this in the store and then serialise it and
1882  * then parse it again. Simply mark the request complete in
1883  * our context and write the reply struct to the client side.
1884  */
1886 }
1887 
1890 void
1892 {
1893  if (http->request->method == Http::METHOD_GET ||
1895  sendNotModified();
1896  else
1898 }
1899 
1900 void
1902 {
1903  /* NP: this should probably soft-fail to a zero-sized-reply error ?? */
1904  assert(reply);
1905 
1907  if (http->loggingTags().oldType == LOG_TCP_DENIED ||
1910  headers_sz = reply->hdr_sz;
1912  return;
1913  }
1914 
1918  return;
1919  }
1920 
1921  headers_sz = reply->hdr_sz;
1922 
1924  if (!Config.accessList.reply) {
1926  return;
1927  }
1928 
1930  ACLFilledChecklist *replyChecklist =
1932  replyChecklist->reply = reply;
1933  HTTPMSGLOCK(replyChecklist->reply);
1934  replyChecklist->nonBlockingCheck(ProcessReplyAccessResult, this);
1935 }
1936 
1937 void
1939 {
1940  clientReplyContext *me = static_cast<clientReplyContext *>(voidMe);
1941  me->processReplyAccessResult(rv);
1942 }
1943 
1944 void
1946 {
1947  debugs(88, 2, "The reply for " << http->request->method
1948  << ' ' << http->uri << " is " << accessAllowed << ", because it matched "
1949  << (AclMatchedName ? AclMatchedName : "NO ACL's"));
1950 
1951  if (!accessAllowed.allowed()) {
1952  ErrorState *err;
1953  err_type page_id;
1955 
1957 
1958  if (page_id == ERR_NONE)
1959  page_id = ERR_ACCESS_DENIED;
1960 
1961  err = clientBuildError(page_id, Http::scForbidden, NULL,
1962  http->getConn(), http->request, http->al);
1963 
1965 
1967 
1968  startError(err);
1969 
1970  return;
1971  }
1972 
1973  /* Ok, the reply is allowed, */
1975 
1976  ssize_t body_size = reqofs - reply->hdr_sz;
1977  if (body_size < 0) {
1978  reqofs = reply->hdr_sz;
1979  body_size = 0;
1980  }
1981 
1982  debugs(88, 3, "clientReplyContext::sendMoreData: Appending " <<
1983  (int) body_size << " bytes after " << reply->hdr_sz <<
1984  " bytes of headers");
1985 
1986 #if USE_SQUID_ESI
1987 
1991  debugs(88, 2, "Enabling ESI processing for " << http->uri);
1994  }
1995 
1996 #endif
1997 
1998  if (http->request->method == Http::METHOD_HEAD) {
1999  /* do not forward body for HEAD replies */
2000  body_size = 0;
2001  http->flags.done_copying = true;
2002  flags.complete = 1;
2003  }
2004 
2006  flags.headersSent = true;
2007 
2008  StoreIOBuffer localTempBuffer;
2009  char *buf = next()->readBuffer.data;
2010  char *body_buf = buf + reply->hdr_sz;
2011 
2012  //Server side may disable ranges under some circumstances.
2013 
2014  if ((!http->request->range))
2015  next()->readBuffer.offset = 0;
2016 
2017  body_buf -= next()->readBuffer.offset;
2018 
2019  if (next()->readBuffer.offset != 0) {
2020  if (next()->readBuffer.offset > body_size) {
2021  /* Can't use any of the body we received. send nothing */
2022  localTempBuffer.length = 0;
2023  localTempBuffer.data = NULL;
2024  } else {
2025  localTempBuffer.length = body_size - next()->readBuffer.offset;
2026  localTempBuffer.data = body_buf + next()->readBuffer.offset;
2027  }
2028  } else {
2029  localTempBuffer.length = body_size;
2030  localTempBuffer.data = body_buf;
2031  }
2032 
2033  /* TODO??: move the data in the buffer back by the request header size */
2035  http, reply, localTempBuffer);
2036 
2037  return;
2038 }
2039 
2040 void
2042 {
2043  if (deleting)
2044  return;
2045 
2046  StoreEntry *entry = http->storeEntry();
2047 
2048  if (ConnStateData * conn = http->getConn()) {
2049  if (!conn->isOpen()) {
2050  debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
2051  return;
2052  }
2053  if (conn->pinning.zeroReply) {
2054  debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
2055  return;
2056  }
2057 
2058  if (reqofs==0 && !http->loggingTags().isTcpHit()) {
2059  if (Ip::Qos::TheConfig.isHitTosActive()) {
2060  Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
2061  }
2062  if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
2063  Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
2064  }
2065  }
2066 
2067  debugs(88, 5, conn->clientConnection <<
2068  " '" << entry->url() << "'" <<
2069  " out.offset=" << http->out.offset);
2070  }
2071 
2072  char *buf = next()->readBuffer.data;
2073 
2074  if (buf != result.data) {
2075  /* we've got to copy some data */
2076  assert(result.length <= next()->readBuffer.length);
2077  memcpy(buf, result.data, result.length);
2078  }
2079 
2080  /* We've got the final data to start pushing... */
2082 
2083  reqofs += result.length;
2084 
2086 
2087  assert(http->request != NULL);
2088 
2089  /* ESI TODO: remove this assert once everything is stable */
2092 
2093  makeThisHead();
2094 
2095  debugs(88, 5, "clientReplyContext::sendMoreData: " << http->uri << ", " <<
2096  reqofs << " bytes (" << result.length <<
2097  " new bytes)");
2098 
2099  /* update size of the request */
2100  reqsize = reqofs;
2101 
2102  if (errorInStream(result, reqofs)) {
2103  sendStreamError(result);
2104  return;
2105  }
2106 
2107  if (flags.headersSent) {
2108  pushStreamData (result, buf);
2109  return;
2110  }
2111 
2112  cloneReply();
2113 
2114 #if USE_DELAY_POOLS
2115  if (sc)
2117 #endif
2118 
2119  holdingBuffer = result;
2121  return;
2122 }
2123 
2124 void
2126 {
2127  clientAclChecklistFill(checklist, http);
2128 }
2129 
2130 /* Using this breaks the client layering just a little!
2131  */
2132 void
2134 {
2135  assert(http != NULL);
2136  /*
2137  * For erroneous requests, we might not have a h->request,
2138  * so make a fake one.
2139  */
2140 
2141  if (http->request == NULL) {
2142  const auto connManager = http->getConn();
2143  const auto mx = MasterXaction::MakePortful(connManager ? connManager->port : nullptr);
2144  // XXX: These fake URI parameters shadow the real (or error:...) URI.
2145  // TODO: Either always set the request earlier and assert here OR use
2146  // http->uri (converted to Anyp::Uri) to create this catch-all request.
2147  const_cast<HttpRequest *&>(http->request) = new HttpRequest(m, AnyP::PROTO_NONE, "http", null_string, mx);
2149  }
2150 
2151  StoreEntry *e = storeCreateEntry(storeId(), http->log_uri, reqFlags, m);
2152 
2153  // Make entry collapsible ASAP, to increase collapsing chances for others,
2154  // TODO: every must-revalidate and similar request MUST reach the origin,
2155  // but do we have to prohibit others from collapsing on that request?
2156  if (reqFlags.cachable &&
2157  !reqFlags.needValidation &&
2158  (m == Http::METHOD_GET || m == Http::METHOD_HEAD) &&
2160  // make the entry available for future requests now
2161  (void)Store::Root().allowCollapsing(e, reqFlags, m);
2162  }
2163 
2164  sc = storeClientListAdd(e, this);
2165 
2166 #if USE_DELAY_POOLS
2168 #endif
2169 
2170  reqofs = 0;
2171 
2172  reqsize = 0;
2173 
2174  /* I don't think this is actually needed! -- adrian */
2175  /* http->reqbuf = http->norm_reqbuf; */
2176  // assert(http->reqbuf == http->norm_reqbuf);
2177  /* The next line is illegal because we don't know if the client stream
2178  * buffers have been set up
2179  */
2180  // storeClientCopy(http->sc, e, 0, HTTP_REQBUF_SZ, http->reqbuf,
2181  // SendMoreData, this);
2182  /* So, we mark the store logic as complete */
2184 
2185  /* and get the caller to request a read, from wherever they are */
2186  /* NOTE: after ANY data flows down the pipe, even one step,
2187  * this function CAN NOT be used to manage errors
2188  */
2189  http->storeEntry(e);
2190 }
2191 
2192 ErrorState *
2193 clientBuildError(err_type page_id, Http::StatusCode status, char const *url,
2195 {
2196  const auto err = new ErrorState(page_id, status, request, al);
2197  err->src_addr = conn && conn->clientConnection ? conn->clientConnection->remote : Ip::Address::NoAddr();
2198 
2199  if (url)
2200  err->url = xstrdup(url);
2201 
2202  return err;
2203 }
2204 
int hdr_sz
Definition: Message.h:82
HttpReplyPointer make304() const
Definition: HttpReply.cc:127
Definition: parse.c:104
@ LOG_TCP_IMS_HIT
Definition: LogTags.h:47
char method[16]
Definition: tcp-banger2.c:115
void refreshMask()
Definition: HttpHeader.cc:751
int storeClientIsThisAClient(store_client *sc, void *someClient)
static DelayId DelayClient(ClientHttpRequest *, HttpReply *reply=nullptr)
Definition: DelayId.cc:67
@ SURROGATE_CONTROL
@ METHOD_OTHER
Definition: MethodType.h:93
virtual void buffer()
Definition: store.cc:1579
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:524
time_t timestamp
Definition: Store.h:222
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:479
void ignoreRange(const char *reason)
forgets about the cached Range header (for a reason)
Definition: HttpRequest.cc:629
@ scUnauthorized
Definition: StatusCode.h:45
@ METHOD_HEAD
Definition: MethodType.h:28
StoreIOBuffer tempBuffer
#define DBG_CRITICAL
Definition: Stream.h:40
dlink_node node
Definition: clientStream.h:87
const char * uniqueHostname(void)
Definition: tools.cc:544
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
Definition: ETag.h:18
@ scProcessing
Definition: StatusCode.h:24
@ VARY_NONE
Definition: enums.h:193
ClientStreamData data
Definition: clientStream.h:93
int esiEnableProcessing(HttpReply *rep)
Definition: Esi.cc:2239
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:1567
const char * storeId() const
Definition: MemObject.cc:53
CSS clientReplyStatus
void releaseRequest(const bool shareable=false)
Definition: store.cc:433
void traceReply(clientStreamNode *node)
ACLFilledChecklist * clientAclChecklistCreate(const acl_access *acl, ClientHttpRequest *http)
@ scNone
Definition: StatusCode.h:21
bool needValidation
Definition: RequestFlags.h:46
struct ClientHttpRequest::Redirect redirect
unsigned char cache_key
Store key.
Definition: forward.h:29
@ METHOD_ENUM_END
Definition: MethodType.h:94
void removeHopByHopEntries()
Definition: HttpHeader.cc:1739
HttpHeader header
Definition: Message.h:75
void ipcacheInvalidate(const char *name)
Definition: ipcache.cc:855
void errorAppendEntry(StoreEntry *entry, ErrorState *err)
Definition: errorpage.cc:719
bool isEmpty() const
Definition: SBuf.h:431
void purgeDoPurge()
releases both cached GET and HEAD entries
MemObject * mem_obj
Definition: Store.h:219
RequestFlags flags
Definition: HttpRequest.h:141
void sendNotModifiedOrPreconditionFailedError()
int varyEvaluateMatch(StoreEntry *entry, HttpRequest *request)
int fdUsageHigh(void)
Definition: fd.cc:271
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:115
void clearPublicKeyScope()
Definition: store.cc:584
bool isTcpHit() const
determine if the log tag code indicates a cache HIT
Definition: LogTags.cc:101
struct node * next
Definition: parse.c:105
const char * url() const
Definition: store.cc:1544
ConnStateData * getConn() const
@ PROTO_NONE
Definition: ProtocolType.h:24
void createStoreEntry(const HttpRequestMethod &m, RequestFlags flags)
@ STREAM_NONE
Definition: enums.h:126
virtual void fillChecklist(ACLFilledChecklist &) const
configure the given checklist (to reflect the current transaction state)
@ ksRevalidation
Definition: store_key_md5.h:20
static bool SmpAware()
whether there are any SMP-aware storages
Definition: Controller.cc:916
MemObject & mem()
Definition: Store.h:51
#define HttpHeaderInitPos
Definition: HttpHeader.h:48
void lock(const char *context)
Definition: store.cc:420
void ipcacheInvalidateNegative(const char *name)
Definition: ipcache.cc:872
bool loopDetected
Definition: RequestFlags.h:36
int doTosLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
Definition: QosConfig.cc:226
void httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep)
int64_t bodySize(const HttpRequestMethod &) const
Definition: HttpReply.cc:374
int64_t offset
Definition: StoreIOBuffer.h:55
Definition: SBuf.h:94
struct SquidConfig2::@125 onoff
char tempbuf[HTTP_REQBUF_SZ]
a temporary buffer if we need working storage
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:46
clientStream_status_t CSS(clientStreamNode *, ClientHttpRequest *)
struct ClientHttpRequest::Out out
C * getRaw() const
Definition: RefCount.h:80
uint16_t flags
Definition: Store.h:230
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:398
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
Http::StatusLine sline
Definition: HttpReply.h:56
time_t expires
Definition: Store.h:224
void HTTPMSGUNLOCK(M *&a)
Definition: Message.h:150
int64_t objectLen() const
Definition: Store.h:255
@ ERR_NONE
Definition: forward.h:15
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1683
@ STREAM_COMPLETE
Definition: enums.h:127
StatusCode
Definition: StatusCode.h:20
err_type
Definition: forward.h:14
Definition: forward.h:22
CSS esiStreamStatus
#define cbdataReference(var)
Definition: cbdata.h:341
Http::StatusCode purgeStatus
CSD esiStreamDetach
static void Start(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp)
Initiates request forwarding to a peer or origin server.
Definition: FwdState.cc:361
@ VARY_CANCEL
Definition: enums.h:196
@ LOG_TCP_HIT
Definition: LogTags.h:39
@ IF_MODIFIED_SINCE
bool conditional() const
has at least one recognized If-* header
Definition: HttpRequest.cc:580
void httpHeaderPutStrf(HttpHeader *hdr, Http::HdrType id, const char *fmt,...)
StoreEntry * storeGetPublicByRequest(HttpRequest *req, const KeyScope keyScope)
Definition: store.cc:491
AclDenyInfoList * denyInfoList
Definition: SquidConfig.h:414
ProtocolType protocol
which protocol this version is for
@ LOG_TCP_MISS
Definition: LogTags.h:40
int refreshCheckHTTP(const StoreEntry *entry, HttpRequest *request)
Definition: refresh.cc:575
StoreIOBuffer holdingBuffer
bool allowCollapsing(StoreEntry *, const RequestFlags &, const HttpRequestMethod &)
tries to make the entry available for collapsing future requests
Definition: Controller.cc:754
CollapsedRevalidation collapsedRevalidation
StoreEntry * storeGetPublicByRequestMethod(HttpRequest *req, const HttpRequestMethod &method, const KeyScope keyScope)
Definition: store.cc:485
@ STREAM_FAILED
Definition: enums.h:137
void sendPreconditionFailedError()
send 412 (Precondition Failed) to client
@ LOG_TCP_REFRESH_FAIL_OLD
Definition: LogTags.h:42
AnyP::UriScheme const & getScheme() const
Definition: Uri.h:67
const char * c_str() const
compute the status access.log field
Definition: LogTags.cc:66
struct StoreIOBuffer::@146 flags
bool noCacheHack() const
Definition: RequestFlags.h:124
void clientStreamDetach(clientStreamNode *thisObject, ClientHttpRequest *http)
void identifyFoundObject(StoreEntry *entry, const char *detail)
HttpReply * clone() const
Definition: HttpReply.cc:572
@ ENTRY_ABORTED
Definition: enums.h:115
@ ERR_ACCESS_DENIED
Definition: forward.h:18
@ scGatewayTimeout
Definition: StatusCode.h:75
@ WWW_AUTHENTICATE
class SquidConfig2 Config2
Definition: SquidConfig.cc:14
@ ENTRY_BAD_LENGTH
Definition: enums.h:114
bool persistent() const
Definition: Message.cc:247
time_t lastmod
Definition: HttpRequest.h:167
void removeIrrelevantContentLength()
Some response status codes prohibit sending Content-Length (RFC 7230 section 3.3.2).
Definition: HttpReply.cc:671
bool streamError
Definition: RequestFlags.h:92
@ HTCP_CLR_INVALIDATION
Definition: enums.h:243
const char * AclMatchedName
Definition: Acl.cc:29
@ LOG_TCP_DENIED_REPLY
Definition: LogTags.h:53
String etag
A strong etag of the cached entry. Used for refreshing that entry.
Definition: HttpRequest.h:191
@ VARY_MATCH
Definition: enums.h:194
acl_access * sendHit
Definition: SquidConfig.h:373
@ crSlave
we collapsed on the existing revalidation request
#define NULL
Definition: types.h:166
bool mayStartHitting() const
Definition: Store.h:286
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:43
bool hasIfNoneMatchEtag(const HttpRequest &request) const
has ETag matching at least one of the If-None-Match etags
Definition: store.cc:1842
void CSR(clientStreamNode *, ClientHttpRequest *)
client stream read
@ scForbidden
Definition: StatusCode.h:47
err_type aclGetDenyInfoPage(AclDenyInfoList **head, const char *name, int redirect_allowed)
Definition: Gadgets.cc:42
bool errorInStream(StoreIOBuffer const &result, size_t const &sizeToProcess) const
@ IN_MEMORY
Definition: enums.h:36
void cacheHit(StoreIOBuffer result)
void sendMoreData(StoreIOBuffer result)
@ scPreconditionFailed
Definition: StatusCode.h:56
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
@ scNotImplemented
Definition: StatusCode.h:72
const HttpReply & baseReply() const
Definition: MemObject.h:59
@ LOG_TCP_REFRESH_MODIFIED
Definition: LogTags.h:44
@ HTCP_CLR_PURGE
Definition: enums.h:242
bool mayInitiateCollapsing() const
whether Squid configuration allows us to become a CF initiator
Definition: StoreClient.h:45
void pushStreamData(StoreIOBuffer const &result, char *source)
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:275
#define SQUIDCEXTERN
Definition: squid.h:21
void putWarning(const int code, const char *const text)
add a Warning header
Definition: HttpHeader.cc:1132
unsigned error
Definition: StoreIOBuffer.h:52
void setReplyToStoreEntry(StoreEntry *e, const char *reason)
replaces current response store entry with the given one
@ STREAM_UNPLANNED_COMPLETE
Definition: enums.h:132
void detailStoreLookup(const char *detail)
remembers the very first Store lookup classification, ignoring the rest
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
int collapsed_forwarding
Definition: SquidConfig.h:329
bool failOnValidationError
Definition: RequestFlags.h:48
int error_pconns
Definition: SquidConfig.h:312
void handleIMSReply(StoreIOBuffer result)
virtual void evictIfFound(const cache_key *) override
Definition: Controller.cc:506
clientStream_status_t replyStatus()
ErrorState * clientBuildError(err_type, Http::StatusCode, char const *, const ConnStateData *, HttpRequest *, const AccessLogEntry::Pointer &)
int delById(Http::HdrType id)
Definition: HttpHeader.cc:695
Http::HdrType id
Definition: HttpHeader.h:63
struct clientReplyContext::Flags flags
static const Address & NoAddr()
Definition: Address.h:302
void putExt(const char *name, const char *value)
Definition: HttpHeader.cc:1141
int unlock(const char *context)
Definition: store.cc:444
void purgeEntriesByUrl(HttpRequest *req, const char *url)
int64_t getInt64(Http::HdrType id) const
Definition: HttpHeader.cc:1162
#define safe_free(x)
Definition: xalloc.h:73
@ LOG_TCP_REFRESH_UNMODIFIED
Definition: LogTags.h:41
store_status_t store_status
Definition: Store.h:242
int weak
true if it is a weak validator
Definition: ETag.h:21
void putTime(Http::HdrType id, time_t htime)
Definition: HttpHeader.cc:1043
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:19
char const * termedBuf() const
Definition: SquidString.h:92
CSR esiStreamRead
@ scContinue
Definition: StatusCode.h:22
clientReplyContext(ClientHttpRequest *)
int storeOKTransferDone() const
HierarchyLogEntry hier
Definition: HttpRequest.h:157
int checkNegativeHit() const
Definition: store.cc:1274
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:161
void neighborsHtcpClear(StoreEntry *e, HttpRequest *req, const HttpRequestMethod &method, htcp_clr_reason reason)
Definition: neighbors.cc:1815
@ TRANSFER_ENCODING
SBuf vary_headers
The variant second-stage cache key. Generated from Vary header pattern for this request.
Definition: HttpRequest.h:170
@ METHOD_TRACE
Definition: MethodType.h:30
#define cbdataReferenceDone(var)
Definition: cbdata.h:350
CSCB esiProcessStream
void redirect(Http::StatusCode, const char *)
Definition: HttpReply.cc:202
@ ENTRY_SPECIAL
Definition: enums.h:84
@ scInternalServerError
Definition: StatusCode.h:71
HttpHdrRange * range
Definition: HttpRequest.h:143
SQUIDCEXTERN CSD clientReplyDetach
mem_status_t mem_status
Definition: Store.h:238
static int sc[16]
Definition: smbdes.c:121
const char * null_string
const char * c_str()
Definition: SBuf.cc:516
@ LOG_TCP_REFRESH
Definition: LogTags.h:45
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:318
int prefixLen() const
Definition: HttpRequest.cc:370
void dump(int debug_lvl) const
Definition: store.cc:1477
@ STORE_OK
Definition: enums.h:50
time_t squid_curtime
Definition: stub_libtime.cc:20
Comm::ConnectionPointer clientConnection
Definition: Server.h:98
@ 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:48
StoreEntry * storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod &method)
Definition: store.cc:734
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
int64_t content_length
Definition: Message.h:84
int client_pconns
Definition: SquidConfig.h:310
bool receivedBodyTooLarge(HttpRequest &, int64_t receivedBodySize)
Definition: HttpReply.cc:516
@ scNotModified
Definition: StatusCode.h:40
void clientAclChecklistFill(ACLFilledChecklist &checklist, ClientHttpRequest *http)
bool chunkedReply
Definition: RequestFlags.h:90
static const size_type npos
Definition: SBuf.h:99
@ VARY_OTHER
Definition: enums.h:195
clientStreamNode * getNextNode() const
const LogTags & loggingTags() const
the processing tags associated with this request transaction.
enum Http::_method_t MethodType
void clientStreamInsertHead(dlink_list *list, CSR *func, CSCB *callback, CSD *detach, CSS *status, ClientStreamData data)
@ METHOD_PURGE
Definition: MethodType.h:92
bool proxyKeepalive
Definition: RequestFlags.h:38
bool startCollapsingOn(const StoreEntry &, const bool doingRevalidation) const
Definition: store_client.cc:65
void complete()
Definition: store.cc:1006
@ PROTO_HTTPS
Definition: ProtocolType.h:27
void putInt(Http::HdrType id, int number)
Definition: HttpHeader.cc:1025
HttpRequestMethod method
Definition: HttpRequest.h:114
Config TheConfig
Globally available instance of Qos::Config.
Definition: QosConfig.cc:283
struct SquidConfig::@111 accessList
@ scNotFound
Definition: StatusCode.h:48
acl_access * reply
Definition: SquidConfig.h:385
HttpHeaderEntry * findEntry(Http::HdrType id) const
Definition: HttpHeader.cc:631
bool blockedHit() const
whether squid.conf send_hit prevents us from serving this hit
@ PROTO_HTTP
Definition: ProtocolType.h:25
@ LOG_TCP_DENIED
Definition: LogTags.h:52
int doNfmarkLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
Definition: QosConfig.cc:247
bool allowed() const
Definition: Acl.h:149
const char * storeId() const
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition: SBuf.h:275
AnyP::ProtocolVersion version
breakdown of protocol version label: (HTTP/ICY) and (0.9/1.0/1.1)
Definition: StatusLine.h:62
@ ERR_ONLY_IF_CACHED_MISS
Definition: forward.h:39
void updateOnNotModified(StoreEntry *old, StoreEntry &e304)
using a 304 response, update the old entry (metadata and reply headers)
Definition: Controller.cc:721
struct SquidConfig::@110 onoff
bool timestampsSet()
Definition: store.cc:1371
@ LOG_TCP_OFFLINE_HIT
Definition: LogTags.h:54
@ scNoContent
Definition: StatusCode.h:30
@ 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:991
const SBuf storeId()
Definition: HttpRequest.cc:741
virtual LogTags * loggingTags() const
int storeNotOKTransferDone() const
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1052
@ METHOD_NONE
Definition: MethodType.h:22
@ crNone
collapsed revalidation is not allowed for this context
clientStream_status_t
Definition: enums.h:125
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:71
bool alwaysAllowResponse(Http::StatusCode sline) const
@ SURROGATE_CAPABILITY
@ ACCESS_ALLOWED
Definition: Acl.h:110
const char * storeLookupString(bool found) const
#define DBG_IMPORTANT
Definition: Stream.h:41
@ scProxyAuthenticationRequired
Definition: StatusCode.h:51
const char * storeKeyText(const cache_key *key)
LogTags_ot oldType
a set of client protocol, cache use, and other transaction outcome tags
Definition: LogTags.h:93
time_t ims
Definition: HttpRequest.h:145
StoreEntry * storeGetPublic(const char *uri, const HttpRequestMethod &method)
Definition: store.cc:479
const AccessLogEntry::Pointer al
access.log entry
void release(const bool shareable=false)
Definition: store.cc:1122
#define PRId64
Definition: types.h:110
bool purgeEntry(StoreEntry &, const Http::MethodType, const char *descriptionPrefix="")
const HttpReply & freshestReply() const
Definition: MemObject.h:67
#define HTTP_REQBUF_SZ
Definition: forward.h:14
int shutting_down
const char * firstStoreLookup_
@ LOG_TCP_REDIRECT
Definition: LogTags.h:55
store_client * old_sc
int doTosLocalHit(const Comm::ConnectionPointer &conn)
Definition: QosConfig.cc:268
clientStreamNode * next() const
void lastModified(const time_t when)
Definition: Store.h:174
void clientStreamCallback(clientStreamNode *thisObject, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer replyBuffer)
int port
port of pinned connection
Definition: client_side.h:146
StoreIOBuffer readBuffer
Definition: clientStream.h:94
void delAt(HttpHeaderPos pos, int &headers_deleted)
Definition: HttpHeader.cc:723
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:167
void sendNotModified()
send 304 (Not Modified) to client
StoreEntry * storeEntry() const
void processReplyAccessResult(const Acl::Answer &accessAllowed)
StoreEntry * loggingEntry() const
@ scOkay
Definition: StatusCode.h:26
clientStreamNode * ourNode
void swapOut(StoreEntry *e)
Definition: HttpRequest.cc:335
HttpHeaderEntry * getEntry(HttpHeaderPos *pos) const
Definition: HttpHeader.cc:612
@ METHOD_GET
Definition: MethodType.h:25
void setDelayId(DelayId delay_id)
const SBuf & MethodStr(const MethodType m)
Definition: MethodType.h:100
void * callback_data
Definition: errorpage.h:185
bool modifiedSince(const time_t ims, const int imslen=-1) const
Definition: store.cc:1798
MemObject * memObject() const
static STCB SendMoreData
@ LOG_TCP_MEM_HIT
Definition: LogTags.h:51
int storeUnregister(store_client *sc, StoreEntry *e, void *data)
#define false
Definition: GnuRegex.c:233
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:752
unsigned complete
we have read all we can from upstream
void addVia(const AnyP::ProtocolVersion &ver, const HttpHeader *from=0)
Definition: HttpHeader.cc:999
bool hasIfMatchEtag(const HttpRequest &request) const
has ETag matching at least one of the If-Match etags
Definition: store.cc:1835
void storeClientCopy(store_client *sc, StoreEntry *e, StoreIOBuffer copyInto, STCB *callback, void *data)
bool hasEtag(ETag &etag) const
whether this entry has an ETag; if yes, puts ETag value into parameter
Definition: store.cc:1824
void host(const char *src)
Definition: Uri.cc:99
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
static STCB HandleIMSReply
short int keep_alive
Definition: HttpReply.h:53
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition: Checklist.cc:237
char const * rawBuf() const
Definition: SquidString.h:86
@ LOG_TCP_SWAPFAIL_MISS
Definition: LogTags.h:49
Http::StatusCode httpStatus
Definition: errorpage.h:171
void storeErrorResponse(HttpReply *reply)
Store a prepared error response. MemObject locks the reply object.
Definition: store.cc:1666
@ ERR_PRECONDITION_FAILED
Definition: forward.h:47
@ LOG_TCP_NEGATIVE_HIT
Definition: LogTags.h:50
CbcPointer< ConnStateData > clientConnectionManager
Definition: HttpRequest.h:232
SQUIDCEXTERN CSR clientGetMoreData
@ ROR_REPLY
class SquidConfig Config
Definition: SquidConfig.cc:12
store_client * storeClientListAdd(StoreEntry *e, void *data)
HttpRequest *const request
void clean()
Definition: String.cc:118
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:934

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors