]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_reply.cc
Do not use HERE in new or changed debugs() statements.
[thirdparty/squid.git] / src / client_side_reply.cc
CommitLineData
edce4d98 1/*
edce4d98 2 * DEBUG: section 88 Client-side Reply Routines
3 * AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c)
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
26ac0430 21 *
edce4d98 22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26ac0430 26 *
edce4d98 27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
f7f3304a 32#include "squid.h"
b7ac5457
AJ
33#include "acl/FilledChecklist.h"
34#include "acl/Gadgets.h"
65d448bc 35#include "anyp/PortCfg.h"
0655fa4d 36#include "client_side_reply.h"
b7ac5457 37#include "errorpage.h"
46017fdd 38#include "ETag.h"
c4ad1349 39#include "fd.h"
7172612f 40#include "fde.h"
31971e6a 41#include "format/Token.h"
eb13c21e 42#include "FwdState.h"
582c2af2
FC
43#include "globals.h"
44#include "globals.h"
a5bac1d2 45#include "HttpHeaderTools.h"
b7ac5457
AJ
46#include "HttpReply.h"
47#include "HttpRequest.h"
48#include "ip/QosConfig.h"
714e68b7 49#include "ipcache.h"
1c7ae5ff 50#include "log/access_log.h"
b7ac5457 51#include "MemObject.h"
b6149797 52#include "mime_header.h"
f0ba2534 53#include "neighbors.h"
c6f15d40 54#include "refresh.h"
f206b652 55#include "RequestFlags.h"
4d5904f7 56#include "SquidConfig.h"
985c86bc 57#include "SquidTime.h"
b7ac5457 58#include "Store.h"
28204b3b 59#include "StrList.h"
4e540555 60#include "tools.h"
b1bd952a 61#include "URL.h"
582c2af2
FC
62#if USE_AUTH
63#include "auth/UserRequest.h"
64#endif
65#if USE_DELAY_POOLS
66#include "DelayPools.h"
67#endif
68#if USE_SQUID_ESI
69#include "esi/Esi.h"
70#endif
e6ccf245 71
0655fa4d 72CBDATA_CLASS_INIT(clientReplyContext);
edce4d98 73
edce4d98 74/* Local functions */
e6ccf245 75extern "C" CSS clientReplyStatus;
955394ce 76ErrorState *clientBuildError(err_type, Http::StatusCode, char const *, Ip::Address &, HttpRequest *);
edce4d98 77
edce4d98 78/* privates */
edce4d98 79
0655fa4d 80clientReplyContext::~clientReplyContext()
edce4d98 81{
50c09fc4 82 deleting = true;
83 /* This may trigger a callback back into SendMoreData as the cbdata
84 * is still valid
85 */
86a2f789 86 removeClientStoreReference(&sc, http);
edce4d98 87 /* old_entry might still be set if we didn't yet get the reply
0655fa4d 88 * code in HandleIMSReply() */
89 removeStoreReference(&old_sc, &old_entry);
90 safe_free(tempBuffer.data);
91 cbdataReferenceDone(http);
2b7d324b 92 HTTPMSGUNLOCK(reply);
edce4d98 93}
94
59a1efb2 95clientReplyContext::clientReplyContext(ClientHttpRequest *clientContext) : http (cbdataReference(clientContext)), old_entry (NULL), old_sc(NULL), deleting(false)
0655fa4d 96{}
edce4d98 97
a5c8d64d
AJ
98/** Create an error in the store awaiting the client side to read it.
99 *
100 * This may be better placed in the clientStream logic, but it has not been
0655fa4d 101 * relocated there yet
102 */
edce4d98 103void
0655fa4d 104clientReplyContext::setReplyToError(
955394ce 105 err_type err, Http::StatusCode status, const HttpRequestMethod& method, char const *uri,
b7ac5457 106 Ip::Address &addr, HttpRequest * failedrequest, const char *unparsedrequest,
2f1431ea 107#if USE_AUTH
c7baff40 108 Auth::UserRequest::Pointer auth_user_request
2f1431ea 109#else
9d2b8284 110 void*
2f1431ea 111#endif
74174c03 112)
edce4d98 113{
a33a428a 114 ErrorState *errstate = clientBuildError(err, status, uri, addr, failedrequest);
62e76326 115
edce4d98 116 if (unparsedrequest)
62e76326 117 errstate->request_hdrs = xstrdup(unparsedrequest);
edce4d98 118
8eb0a7ee
CT
119#if USE_AUTH
120 errstate->auth_user_request = auth_user_request;
121#endif
122 setReplyToError(method, errstate);
123}
124
125void clientReplyContext::setReplyToError(const HttpRequestMethod& method, ErrorState *errstate)
126{
955394ce 127 if (errstate->httpStatus == Http::scNotImplemented && http->request)
62e76326 128 /* prevent confusion over whether we default to persistent or not */
e857372a 129 http->request->flags.proxyKeepalive = false;
62e76326 130
41ebd397 131 http->al->http.code = errstate->httpStatus;
edce4d98 132
f206b652 133 createStoreEntry(method, RequestFlags());
edce4d98 134 assert(errstate->callback_data == NULL);
86a2f789 135 errorAppendEntry(http->storeEntry(), errstate);
edce4d98 136 /* Now the caller reads to get this */
137}
138
061bbdec
CT
139void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry)
140{
1bfe9ade 141 entry->lock("clientReplyContext::setReplyToStoreEntry"); // removeClientStoreReference() unlocks
061bbdec
CT
142 sc = storeClientListAdd(entry, this);
143#if USE_DELAY_POOLS
144 sc->setDelayId(DelayId::DelayClient(http));
145#endif
146 reqofs = 0;
147 reqsize = 0;
148 flags.storelogiccomplete = 1;
149 http->storeEntry(entry);
150}
151
edce4d98 152void
0655fa4d 153clientReplyContext::removeStoreReference(store_client ** scp,
154 StoreEntry ** ep)
edce4d98 155{
156 StoreEntry *e;
e4ae841b 157 store_client *sc_tmp = *scp;
62e76326 158
edce4d98 159 if ((e = *ep) != NULL) {
62e76326 160 *ep = NULL;
e4ae841b 161 storeUnregister(sc_tmp, e, this);
62e76326 162 *scp = NULL;
1bfe9ade 163 e->unlock("clientReplyContext::removeStoreReference");
edce4d98 164 }
165}
166
86a2f789 167void
8bcf08e0 168clientReplyContext::removeClientStoreReference(store_client **scp, ClientHttpRequest *aHttpRequest)
86a2f789 169{
8bcf08e0 170 StoreEntry *reference = aHttpRequest->storeEntry();
86a2f789 171 removeStoreReference(scp, &reference);
8bcf08e0 172 aHttpRequest->storeEntry(reference);
86a2f789 173}
174
edce4d98 175void
0655fa4d 176clientReplyContext::saveState()
edce4d98 177{
e6ccf245 178 assert(old_sc == NULL);
bf8fe701 179 debugs(88, 3, "clientReplyContext::saveState: saving store context");
86a2f789 180 old_entry = http->storeEntry();
e6ccf245 181 old_sc = sc;
182 old_reqsize = reqsize;
183 tempBuffer.offset = reqofs;
edce4d98 184 /* Prevent accessing the now saved entries */
86a2f789 185 http->storeEntry(NULL);
e6ccf245 186 sc = NULL;
187 reqsize = 0;
188 reqofs = 0;
edce4d98 189}
190
191void
0655fa4d 192clientReplyContext::restoreState()
edce4d98 193{
e6ccf245 194 assert(old_sc != NULL);
bf8fe701 195 debugs(88, 3, "clientReplyContext::restoreState: Restoring store context");
86a2f789 196 removeClientStoreReference(&sc, http);
197 http->storeEntry(old_entry);
e6ccf245 198 sc = old_sc;
199 reqsize = old_reqsize;
200 reqofs = tempBuffer.offset;
edce4d98 201 /* Prevent accessed the old saved entries */
0655fa4d 202 old_entry = NULL;
e6ccf245 203 old_sc = NULL;
204 old_reqsize = 0;
205 tempBuffer.offset = 0;
c8be6d7b 206}
207
208void
0655fa4d 209clientReplyContext::startError(ErrorState * err)
c8be6d7b 210{
f206b652 211 createStoreEntry(http->request->method, RequestFlags());
0655fa4d 212 triggerInitialStoreRead();
86a2f789 213 errorAppendEntry(http->storeEntry(), err);
edce4d98 214}
215
e6ccf245 216clientStreamNode *
528b2c61 217clientReplyContext::getNextNode() const
e6ccf245 218{
62e76326 219 return (clientStreamNode *)ourNode->node.next->data;
e6ccf245 220}
221
62e76326 222/* This function is wrong - the client parameters don't include the
528b2c61 223 * header offset
224 */
c8be6d7b 225void
0655fa4d 226clientReplyContext::triggerInitialStoreRead()
c8be6d7b 227{
0655fa4d 228 /* when confident, 0 becomes reqofs, and then this factors into
26ac0430 229 * startSendProcess
528b2c61 230 */
0655fa4d 231 assert(reqofs == 0);
8bcf08e0
FC
232 StoreIOBuffer localTempBuffer (next()->readBuffer.length, 0, next()->readBuffer.data);
233 storeClientCopy(sc, http->storeEntry(), localTempBuffer, SendMoreData, this);
c8be6d7b 234}
edce4d98 235
236/* there is an expired entry in the store.
237 * setup a temporary buffer area and perform an IMS to the origin
238 */
0655fa4d 239void
240clientReplyContext::processExpired()
edce4d98 241{
a8a0b1c2 242 const char *url = storeId();
edce4d98 243 StoreEntry *entry = NULL;
bf8fe701 244 debugs(88, 3, "clientReplyContext::processExpired: '" << http->uri << "'");
86a2f789 245 assert(http->storeEntry()->lastmod >= 0);
edce4d98 246 /*
247 * check if we are allowed to contact other servers
26ac0430 248 * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return
edce4d98 249 * a stale entry *if* it matches client requirements
250 */
62e76326 251
0655fa4d 252 if (http->onlyIfCached()) {
253 processOnlyIfCachedMiss();
62e76326 254 return;
edce4d98 255 }
62e76326 256
e857372a 257 http->request->flags.refresh = true;
edce4d98 258#if STORE_CLIENT_LIST_DEBUG
c8be6d7b 259 /* Prevent a race with the store client memory free routines
edce4d98 260 */
0655fa4d 261 assert(storeClientIsThisAClient(sc, this));
edce4d98 262#endif
263 /* Prepare to make a new temporary request */
0655fa4d 264 saveState();
edce4d98 265 entry = storeCreateEntry(url,
62e76326 266 http->log_uri, http->request->flags, http->request->method);
34266cde 267 /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
0655fa4d 268 sc = storeClientListAdd(entry, this);
9a0a18de 269#if USE_DELAY_POOLS
edce4d98 270 /* delay_id is already set on original store client */
0655fa4d 271 sc->setDelayId(DelayId::DelayClient(http));
edce4d98 272#endif
62e76326 273
0655fa4d 274 http->request->lastmod = old_entry->lastmod;
46017fdd
CT
275
276 if (!http->request->header.has(HDR_IF_NONE_MATCH)) {
277 ETag etag = {NULL, -1}; // TODO: make that a default ETag constructor
278 if (old_entry->hasEtag(etag) && !etag.weak)
279 http->request->etag = etag.str;
280 }
281
4a7a3d56 282 debugs(88, 5, "clientReplyContext::processExpired : lastmod " << entry->lastmod );
86a2f789 283 http->storeEntry(entry);
c8be6d7b 284 assert(http->out.offset == 0);
9174ba3d 285 assert(http->request->clientConnectionManager == http->getConn());
655daa06 286
b2d22df6 287 /*
288 * A refcounted pointer so that FwdState stays around as long as
289 * this clientReplyContext does
290 */
73c36fd9 291 Comm::ConnectionPointer conn = http->getConn() != NULL ? http->getConn()->clientConnection : NULL;
4bf68cfa 292 FwdState::Start(conn, http->storeEntry(), http->request, http->al);
62e76326 293
edce4d98 294 /* Register with storage manager to receive updates when data comes in. */
62e76326 295
edce4d98 296 if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
fa84c01d 297 debugs(88, DBG_CRITICAL, "clientReplyContext::processExpired: Found ENTRY_ABORTED object");
62e76326 298
c8be6d7b 299 {
62e76326 300 /* start counting the length from 0 */
8bcf08e0
FC
301 StoreIOBuffer localTempBuffer(HTTP_REQBUF_SZ, 0, tempbuf);
302 storeClientCopy(sc, entry, localTempBuffer, HandleIMSReply, this);
edce4d98 303 }
304}
305
528b2c61 306void
307clientReplyContext::sendClientUpstreamResponse()
308{
309 StoreIOBuffer tempresult;
0655fa4d 310 removeStoreReference(&old_sc, &old_entry);
2324cda2 311 /* here the data to send is the data we just received */
528b2c61 312 tempBuffer.offset = 0;
313 old_reqsize = 0;
0655fa4d 314 /* sendMoreData tracks the offset as well.
528b2c61 315 * Force it back to zero */
316 reqofs = 0;
86a2f789 317 assert(!EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED));
0655fa4d 318 /* TODO: provide sendMoreData with the ready parsed reply */
528b2c61 319 tempresult.length = reqsize;
320 tempresult.data = tempbuf;
0655fa4d 321 sendMoreData(tempresult);
528b2c61 322}
323
edce4d98 324void
0655fa4d 325clientReplyContext::HandleIMSReply(void *data, StoreIOBuffer result)
edce4d98 326{
e6ccf245 327 clientReplyContext *context = (clientReplyContext *)data;
0655fa4d 328 context->handleIMSReply(result);
329}
62e76326 330
0655fa4d 331void
332clientReplyContext::sendClientOldEntry()
333{
334 /* Get the old request back */
335 restoreState();
336 /* here the data to send is in the next nodes buffers already */
86a2f789 337 assert(!EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED));
0655fa4d 338 /* sendMoreData tracks the offset as well.
339 * Force it back to zero */
340 reqofs = 0;
341 StoreIOBuffer tempresult (reqsize, reqofs, next()->readBuffer.data);
342 sendMoreData(tempresult);
343}
62e76326 344
1d7ab0f4 345/* This is the workhorse of the HandleIMSReply callback.
346 *
347 * It is called when we've got data back from the origin following our
348 * IMS request to revalidate a stale entry.
349 */
0655fa4d 350void
1d7ab0f4 351clientReplyContext::handleIMSReply(StoreIOBuffer result)
0655fa4d 352{
1d7ab0f4 353 if (deleting)
354 return;
62e76326 355
bf8fe701 356 debugs(88, 3, "handleIMSReply: " << http->storeEntry()->url() << ", " << (long unsigned) result.length << " bytes" );
62e76326 357
1d7ab0f4 358 if (http->storeEntry() == NULL)
359 return;
07947ad8 360
1d7ab0f4 361 if (result.flags.error && !EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
362 return;
62e76326 363
1d7ab0f4 364 /* update size of the request */
365 reqsize = result.length + reqofs;
62e76326 366
9b769c67 367 const Http::StatusCode status = http->storeEntry()->getReply()->sline.status();
62e76326 368
1d7ab0f4 369 // request to origin was aborted
370 if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) {
bf8fe701 371 debugs(88, 3, "handleIMSReply: request to origin aborted '" << http->storeEntry()->url() << "', sending old entry to client" );
7948b784 372 http->logType = LOG_TCP_REFRESH_FAIL_OLD;
1d7ab0f4 373 sendClientOldEntry();
0655fa4d 374 }
62e76326 375
b297bcd0 376 HttpReply *old_rep = (HttpReply *) old_entry->getReply();
62e76326 377
b297bcd0 378 // origin replied 304
955394ce 379 if (status == Http::scNotModified) {
26ac0430 380 http->logType = LOG_TCP_REFRESH_UNMODIFIED;
e857372a 381 http->request->flags.staleIfHit = false; // old_entry is no longer stale
045a76ab 382
26ac0430 383 // update headers on existing entry
4802f60d 384 old_rep->updateOnNotModified(http->storeEntry()->getReply());
26ac0430 385 old_entry->timestampsSet();
62e76326 386
26ac0430 387 // if client sent IMS
62e76326 388
45e5102d 389 if (http->request->flags.ims && !old_entry->modifiedSince(http->request)) {
26ac0430
AJ
390 // forward the 304 from origin
391 debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and forwarding 304 to client");
392 sendClientUpstreamResponse();
393 } else {
394 // send existing entry, it's still valid
395 debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and sending " <<
9b769c67 396 old_rep->sline.status() << " to client");
26ac0430
AJ
397 sendClientOldEntry();
398 }
b297bcd0 399 }
62e76326 400
b297bcd0 401 // origin replied with a non-error code
955394ce 402 else if (status > Http::scNone && status < Http::scInternalServerError) {
26ac0430
AJ
403 // forward response from origin
404 http->logType = LOG_TCP_REFRESH_MODIFIED;
405 debugs(88, 3, "handleIMSReply: origin replied " << status << ", replacing existing entry and forwarding to client");
406 sendClientUpstreamResponse();
b297bcd0 407 }
62e76326 408
b297bcd0 409 // origin replied with an error
450fe1cb 410 else if (http->request->flags.failOnValidationError) {
7948b784
AR
411 http->logType = LOG_TCP_REFRESH_FAIL_ERR;
412 debugs(88, 3, "handleIMSReply: origin replied with error " << status <<
413 ", forwarding to client due to fail_on_validation_err");
414 sendClientUpstreamResponse();
415 } else {
26ac0430 416 // ignore and let client have old entry
7948b784 417 http->logType = LOG_TCP_REFRESH_FAIL_OLD;
26ac0430 418 debugs(88, 3, "handleIMSReply: origin replied with error " <<
9b769c67 419 status << ", sending old entry (" << old_rep->sline.status() << ") to client");
26ac0430 420 sendClientOldEntry();
1d7ab0f4 421 }
edce4d98 422}
423
82afb125
FC
424SQUIDCEXTERN CSR clientGetMoreData;
425SQUIDCEXTERN CSD clientReplyDetach;
edce4d98 426
7d5f62a4
AJ
427/**
428 * clientReplyContext::cacheHit Should only be called until the HTTP reply headers
edce4d98 429 * have been parsed. Normally this should be a single call, but
430 * it might take more than one. As soon as we have the headers,
0655fa4d 431 * we hand off to clientSendMoreData, processExpired, or
432 * processMiss.
edce4d98 433 */
434void
0655fa4d 435clientReplyContext::CacheHit(void *data, StoreIOBuffer result)
edce4d98 436{
e6ccf245 437 clientReplyContext *context = (clientReplyContext *)data;
7d5f62a4 438 context->cacheHit(result);
0655fa4d 439}
440
7d5f62a4
AJ
441/**
442 * Process a possible cache HIT.
443 */
0655fa4d 444void
445clientReplyContext::cacheHit(StoreIOBuffer result)
446{
7d5f62a4 447 /** Ignore if the HIT object is being deleted. */
045a76ab 448 if (deleting)
449 return;
450
86a2f789 451 StoreEntry *e = http->storeEntry();
045a76ab 452
190154cf 453 HttpRequest *r = http->request;
045a76ab 454
4a7a3d56 455 debugs(88, 3, "clientCacheHit: " << http->uri << ", " << result.length << " bytes");
62e76326 456
86a2f789 457 if (http->storeEntry() == NULL) {
bf8fe701 458 debugs(88, 3, "clientCacheHit: request aborted");
62e76326 459 return;
c8be6d7b 460 } else if (result.flags.error) {
62e76326 461 /* swap in failure */
bf8fe701 462 debugs(88, 3, "clientCacheHit: swapin failure for " << http->uri);
62e76326 463 http->logType = LOG_TCP_SWAPFAIL_MISS;
86a2f789 464 removeClientStoreReference(&sc, http);
0655fa4d 465 processMiss();
62e76326 466 return;
edce4d98 467 }
62e76326 468
d3b3ab85 469 if (result.length == 0) {
62e76326 470 /* the store couldn't get enough data from the file for us to id the
471 * object
472 */
473 /* treat as a miss */
474 http->logType = LOG_TCP_MISS;
0655fa4d 475 processMiss();
62e76326 476 return;
d3b3ab85 477 }
62e76326 478
edce4d98 479 assert(!EBIT_TEST(e->flags, ENTRY_ABORTED));
480 /* update size of the request */
0655fa4d 481 reqsize = result.length + reqofs;
62e76326 482
edce4d98 483 /*
484 * Got the headers, now grok them
485 */
29b8d8d6 486 assert(http->logType == LOG_TCP_HIT);
62e76326 487
c877c0bc
AR
488 if (strcmp(e->mem_obj->storeId(), http->request->storeId()) != 0) {
489 debugs(33, DBG_IMPORTANT, "clientProcessHit: URL mismatch, '" << e->mem_obj->storeId() << "' != '" << http->request->storeId() << "'");
b8a899c0 490 http->logType = LOG_TCP_MISS; // we lack a more precise LOG_*_MISS code
cdd4e1c1 491 processMiss();
492 return;
493 }
494
edce4d98 495 switch (varyEvaluateMatch(e, r)) {
62e76326 496
edce4d98 497 case VARY_NONE:
62e76326 498 /* No variance detected. Continue as normal */
499 break;
500
edce4d98 501 case VARY_MATCH:
62e76326 502 /* This is the correct entity for this request. Continue */
bf8fe701 503 debugs(88, 2, "clientProcessHit: Vary MATCH!");
62e76326 504 break;
505
edce4d98 506 case VARY_OTHER:
62e76326 507 /* This is not the correct entity for this request. We need
508 * to requery the cache.
509 */
86a2f789 510 removeClientStoreReference(&sc, http);
62e76326 511 e = NULL;
512 /* Note: varyEvalyateMatch updates the request with vary information
513 * so we only get here once. (it also takes care of cancelling loops)
514 */
26ac0430 515 debugs(88, 2, "clientProcessHit: Vary detected!");
0655fa4d 516 clientGetMoreData(ourNode, http);
62e76326 517 return;
518
edce4d98 519 case VARY_CANCEL:
62e76326 520 /* varyEvaluateMatch found a object loop. Process as miss */
e0236918 521 debugs(88, DBG_IMPORTANT, "clientProcessHit: Vary object loop!");
b8a899c0 522 http->logType = LOG_TCP_MISS; // we lack a more precise LOG_*_MISS code
0655fa4d 523 processMiss();
62e76326 524 return;
edce4d98 525 }
62e76326 526
c2a7cefd 527 if (r->method == Http::METHOD_PURGE) {
86a2f789 528 removeClientStoreReference(&sc, http);
62e76326 529 e = NULL;
0655fa4d 530 purgeRequest();
62e76326 531 return;
edce4d98 532 }
62e76326 533
45e5102d 534 if (e->checkNegativeHit()
450fe1cb 535 && !r->flags.noCacheHack()
36e6655b 536 ) {
62e76326 537 http->logType = LOG_TCP_NEGATIVE_HIT;
0655fa4d 538 sendMoreData(result);
bcfba8bd 539 } else if (!http->flags.internal && refreshCheckHTTP(e, r)) {
bf8fe701 540 debugs(88, 5, "clientCacheHit: in refreshCheck() block");
62e76326 541 /*
542 * We hold a stale copy; it needs to be validated
543 */
544 /*
450fe1cb 545 * The 'needValidation' flag is used to prevent forwarding
62e76326 546 * loops between siblings. If our copy of the object is stale,
547 * then we should probably only use parents for the validation
548 * request. Otherwise two siblings could generate a loop if
549 * both have a stale version of the object.
550 */
e857372a 551 r->flags.needValidation = true;
62e76326 552
553 if (e->lastmod < 0) {
86310427 554 debugs(88, 3, "validate HIT object? NO. Missing Last-Modified header. Do MISS.");
62e76326 555 /*
556 * Previous reply didn't have a Last-Modified header,
557 * we cannot revalidate it.
558 */
559 http->logType = LOG_TCP_MISS;
0655fa4d 560 processMiss();
450fe1cb 561 } else if (r->flags.noCache) {
86310427 562 debugs(88, 3, "validate HIT object? NO. Client sent CC:no-cache. Do CLIENT_REFRESH_MISS");
62e76326 563 /*
564 * This did not match a refresh pattern that overrides no-cache
565 * we should honour the client no-cache header.
566 */
567 http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
0655fa4d 568 processMiss();
0c3d3f65 569 } else if (r->protocol == AnyP::PROTO_HTTP) {
86310427 570 debugs(88, 3, "validate HIT object? YES.");
62e76326 571 /*
572 * Object needs to be revalidated
573 * XXX This could apply to FTP as well, if Last-Modified is known.
574 */
0655fa4d 575 processExpired();
62e76326 576 } else {
86310427 577 debugs(88, 3, "validate HIT object? NO. Client protocol non-HTTP. Do MISS.");
62e76326 578 /*
579 * We don't know how to re-validate other protocols. Handle
580 * them as if the object has expired.
581 */
582 http->logType = LOG_TCP_MISS;
0655fa4d 583 processMiss();
62e76326 584 }
79c8035e
AR
585 } else if (r->conditional())
586 processConditional(result);
587 else {
62e76326 588 /*
589 * plain ol' cache hit
590 */
591
9a0a18de 592#if USE_DELAY_POOLS
ffdf45f2 593 if (e->store_status != STORE_OK)
849b826a 594 http->logType = LOG_TCP_MISS;
5bf4d170 595 else
596#endif
26ac0430
AJ
597 if (e->mem_status == IN_MEMORY)
598 http->logType = LOG_TCP_MEM_HIT;
599 else if (Config.onoff.offline)
600 http->logType = LOG_TCP_OFFLINE_HIT;
62e76326 601
0655fa4d 602 sendMoreData(result);
edce4d98 603 }
604}
605
7d5f62a4 606/**
edce4d98 607 * Prepare to fetch the object as it's a cache miss of some kind.
608 */
609void
0655fa4d 610clientReplyContext::processMiss()
edce4d98 611{
edce4d98 612 char *url = http->uri;
190154cf 613 HttpRequest *r = http->request;
edce4d98 614 ErrorState *err = NULL;
60745f24 615 debugs(88, 4, "clientProcessMiss: '" << RequestMethodStr(r->method) << " " << url << "'");
7d5f62a4
AJ
616
617 /**
edce4d98 618 * We might have a left-over StoreEntry from a failed cache hit
619 * or IMS request.
620 */
86a2f789 621 if (http->storeEntry()) {
622 if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
fa84c01d 623 debugs(88, DBG_CRITICAL, "clientProcessMiss: miss on a special object (" << url << ").");
02c8dde5 624 debugs(88, DBG_CRITICAL, "\tlog_type = " << LogTags_str[http->logType]);
3900307b 625 http->storeEntry()->dump(1);
62e76326 626 }
627
86a2f789 628 removeClientStoreReference(&sc, http);
edce4d98 629 }
62e76326 630
7d5f62a4 631 /** Check if its a PURGE request to be actioned. */
c2a7cefd 632 if (r->method == Http::METHOD_PURGE) {
0655fa4d 633 purgeRequest();
62e76326 634 return;
edce4d98 635 }
914b89a2 636
7d5f62a4 637 /** Check if its an 'OTHER' request. Purge all cached entries if so and continue. */
c2a7cefd 638 if (r->method == Http::METHOD_OTHER) {
26ac0430 639 purgeAllCached();
60745f24 640 }
62e76326 641
7d5f62a4 642 /** Check if 'only-if-cached' flag is set. Action if so. */
0655fa4d 643 if (http->onlyIfCached()) {
644 processOnlyIfCachedMiss();
62e76326 645 return;
edce4d98 646 }
62e76326 647
42829656 648 /// Deny loops
450fe1cb 649 if (r->flags.loopDetected) {
955394ce
AJ
650 http->al->http.code = Http::scForbidden;
651 err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL, http->getConn()->clientConnection->remote, http->request);
f206b652 652 createStoreEntry(r->method, RequestFlags());
86a2f789 653 errorAppendEntry(http->storeEntry(), err);
0655fa4d 654 triggerInitialStoreRead();
62e76326 655 return;
edce4d98 656 } else {
26ac0430 657 assert(http->out.offset == 0);
0655fa4d 658 createStoreEntry(r->method, r->flags);
659 triggerInitialStoreRead();
62e76326 660
661 if (http->redirect.status) {
06a5ae20 662 HttpReply *rep = new HttpReply;
62e76326 663 http->logType = LOG_TCP_REDIRECT;
d88e3c49 664 http->storeEntry()->releaseRequest();
06a5ae20 665 rep->redirect(http->redirect.status, http->redirect.location);
db237875 666 http->storeEntry()->replaceHttpReply(rep);
86a2f789 667 http->storeEntry()->complete();
62e76326 668 return;
669 }
670
7d5f62a4 671 /** Check for internal requests. Update Protocol info if so. */
62e76326 672 if (http->flags.internal)
0c3d3f65 673 r->protocol = AnyP::PROTO_INTERNAL;
62e76326 674
9174ba3d 675 assert(r->clientConnectionManager == http->getConn());
655daa06 676
7d5f62a4 677 /** Start forwarding to get the new object from network */
73c36fd9 678 Comm::ConnectionPointer conn = http->getConn() != NULL ? http->getConn()->clientConnection : NULL;
4bf68cfa 679 FwdState::Start(conn, http->storeEntry(), r, http->al);
edce4d98 680 }
681}
682
7d5f62a4 683/**
edce4d98 684 * client issued a request with an only-if-cached cache-control directive;
685 * we did not find a cached object that can be returned without
686 * contacting other servers;
687 * respond with a 504 (Gateway Timeout) as suggested in [RFC 2068]
688 */
0655fa4d 689void
690clientReplyContext::processOnlyIfCachedMiss()
edce4d98 691{
bf8fe701 692 debugs(88, 4, "clientProcessOnlyIfCachedMiss: '" <<
60745f24 693 RequestMethodStr(http->request->method) << " " << http->uri << "'");
955394ce
AJ
694 http->al->http.code = Http::scGateway_Timeout;
695 ErrorState *err = clientBuildError(ERR_ONLY_IF_CACHED_MISS, Http::scGateway_Timeout, NULL,
73c36fd9 696 http->getConn()->clientConnection->remote, http->request);
86a2f789 697 removeClientStoreReference(&sc, http);
0655fa4d 698 startError(err);
edce4d98 699}
700
79c8035e
AR
701/// process conditional request from client
702void
703clientReplyContext::processConditional(StoreIOBuffer &result)
704{
a2bd457d 705 StoreEntry *const e = http->storeEntry();
79c8035e 706
9b769c67 707 if (e->getReply()->sline.status() != Http::scOkay) {
79c8035e 708 debugs(88, 4, "clientReplyContext::processConditional: Reply code " <<
9b769c67 709 e->getReply()->sline.status() << " != 200");
79c8035e
AR
710 http->logType = LOG_TCP_MISS;
711 processMiss();
712 return;
713 }
714
715 HttpRequest &r = *http->request;
716
717 if (r.header.has(HDR_IF_MATCH) && !e->hasIfMatchEtag(r)) {
718 // RFC 2616: reply with 412 Precondition Failed if If-Match did not match
719 sendPreconditionFailedError();
720 return;
721 }
722
723 bool matchedIfNoneMatch = false;
724 if (r.header.has(HDR_IF_NONE_MATCH)) {
725 if (!e->hasIfNoneMatchEtag(r)) {
726 // RFC 2616: ignore IMS if If-None-Match did not match
e857372a 727 r.flags.ims = false;
79c8035e
AR
728 r.ims = -1;
729 r.imslen = 0;
730 r.header.delById(HDR_IF_MODIFIED_SINCE);
731 http->logType = LOG_TCP_MISS;
732 sendMoreData(result);
733 return;
734 }
735
45e5102d 736 if (!r.flags.ims) {
79c8035e 737 // RFC 2616: if If-None-Match matched and there is no IMS,
a2bd457d
AJ
738 // reply with 304 Not Modified or 412 Precondition Failed
739 sendNotModifiedOrPreconditionFailedError();
79c8035e
AR
740 return;
741 }
742
a2bd457d 743 // otherwise check IMS below to decide if we reply with 304 or 412
79c8035e
AR
744 matchedIfNoneMatch = true;
745 }
746
45e5102d 747 if (r.flags.ims) {
79c8035e
AR
748 // handle If-Modified-Since requests from the client
749 if (e->modifiedSince(&r)) {
750 http->logType = LOG_TCP_IMS_HIT;
751 sendMoreData(result);
752 return;
753 }
754
755 if (matchedIfNoneMatch) {
a2bd457d
AJ
756 // If-None-Match matched, reply with 304 Not Modified or
757 // 412 Precondition Failed
758 sendNotModifiedOrPreconditionFailedError();
79c8035e
AR
759 return;
760 }
761
762 // otherwise reply with 304 Not Modified
a2bd457d 763 sendNotModified();
79c8035e
AR
764 }
765}
766
edce4d98 767void
e6ccf245 768clientReplyContext::purgeRequestFindObjectToPurge()
26ac0430 769{
e6ccf245 770 /* Try to find a base entry */
be4d35dc 771 http->flags.purging = true;
e6ccf245 772 lookingforstore = 1;
26ac0430
AJ
773
774 // TODO: can we use purgeAllCached() here instead of doing the
775 // getPublicByRequestMethod() dance?
c2a7cefd 776 StoreEntry::getPublicByRequestMethod(this, http->request, Http::METHOD_GET);
e6ccf245 777}
778
c1520b67
AJ
779// Purges all entries with a given url
780// TODO: move to SideAgent parent, when we have one
60745f24 781/*
782 * We probably cannot purge Vary-affected responses because their MD5
783 * keys depend on vary headers.
784 */
c1520b67 785void
90bd689c 786purgeEntriesByUrl(HttpRequest * req, const char *url)
c1520b67 787{
8dceeee3
BR
788#if USE_HTCP
789 bool get_or_head_sent = false;
790#endif
26ac0430 791
c2a7cefd
AJ
792 for (HttpRequestMethod m(Http::METHOD_NONE); m != Http::METHOD_ENUM_END; ++m) {
793 if (m.respMaybeCacheable()) {
c1520b67 794 if (StoreEntry *entry = storeGetPublic(url, m)) {
1bfe9ade 795 debugs(88, 5, "purging " << *entry << ' ' << RequestMethodStr(m) << ' ' << url);
90bd689c 796#if USE_HTCP
8dceeee3 797 neighborsHtcpClear(entry, url, req, m, HTCP_CLR_INVALIDATION);
c2a7cefd 798 if (m == Http::METHOD_GET || m == Http::METHOD_HEAD) {
8dceeee3
BR
799 get_or_head_sent = true;
800 }
90bd689c 801#endif
8dceeee3 802 entry->release();
c1520b67
AJ
803 }
804 }
805 }
8dceeee3
BR
806
807#if USE_HTCP
808 if (!get_or_head_sent) {
c2a7cefd 809 neighborsHtcpClear(NULL, url, req, HttpRequestMethod(Http::METHOD_GET), HTCP_CLR_INVALIDATION);
8dceeee3
BR
810 }
811#endif
c1520b67
AJ
812}
813
26ac0430 814void
60745f24 815clientReplyContext::purgeAllCached()
816{
26ac0430 817 const char *url = urlCanonical(http->request);
90bd689c 818 purgeEntriesByUrl(http->request, url);
35ececa5 819}
60745f24 820
e6ccf245 821void
3b13a8fd 822clientReplyContext::created(StoreEntry *newEntry)
e6ccf245 823{
824 if (lookingforstore == 1)
62e76326 825 purgeFoundGet(newEntry);
e6ccf245 826 else if (lookingforstore == 2)
62e76326 827 purgeFoundHead(newEntry);
e6ccf245 828 else if (lookingforstore == 3)
62e76326 829 purgeDoPurgeGet(newEntry);
e6ccf245 830 else if (lookingforstore == 4)
62e76326 831 purgeDoPurgeHead(newEntry);
e6ccf245 832 else if (lookingforstore == 5)
62e76326 833 identifyFoundObject(newEntry);
e6ccf245 834}
835
836void
3b13a8fd 837clientReplyContext::purgeFoundGet(StoreEntry *newEntry)
e6ccf245 838{
839 if (newEntry->isNull()) {
62e76326 840 lookingforstore = 2;
c2a7cefd 841 StoreEntry::getPublicByRequestMethod(this, http->request, Http::METHOD_HEAD);
e6ccf245 842 } else
62e76326 843 purgeFoundObject (newEntry);
e6ccf245 844}
845
846void
3b13a8fd 847clientReplyContext::purgeFoundHead(StoreEntry *newEntry)
e6ccf245 848{
849 if (newEntry->isNull())
62e76326 850 purgeDoMissPurge();
e6ccf245 851 else
62e76326 852 purgeFoundObject (newEntry);
e6ccf245 853}
62e76326 854
e6ccf245 855void
3b13a8fd 856clientReplyContext::purgeFoundObject(StoreEntry *entry)
e6ccf245 857{
858 assert (entry && !entry->isNull());
e4b9a2f0 859
860 if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
861 http->logType = LOG_TCP_DENIED;
955394ce 862 ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL,
73c36fd9 863 http->getConn()->clientConnection->remote, http->request);
e4b9a2f0 864 startError(err);
c877c0bc 865 return; // XXX: leaking unused entry if some store does not keep it
e4b9a2f0 866 }
867
8bcf08e0 868 StoreIOBuffer localTempBuffer;
62e76326 869 /* Swap in the metadata */
86a2f789 870 http->storeEntry(entry);
34266cde 871
1bfe9ade 872 http->storeEntry()->lock("clientReplyContext::purgeFoundObject");
c877c0bc
AR
873 http->storeEntry()->createMemObject(storeId(), http->log_uri,
874 http->request->method);
34266cde 875
86a2f789 876 sc = storeClientListAdd(http->storeEntry(), this);
34266cde 877
62e76326 878 http->logType = LOG_TCP_HIT;
34266cde 879
62e76326 880 reqofs = 0;
34266cde 881
8bcf08e0 882 localTempBuffer.offset = http->out.offset;
34266cde 883
8bcf08e0 884 localTempBuffer.length = next()->readBuffer.length;
34266cde 885
8bcf08e0 886 localTempBuffer.data = next()->readBuffer.data;
34266cde 887
86a2f789 888 storeClientCopy(sc, http->storeEntry(),
8bcf08e0 889 localTempBuffer, CacheHit, this);
e6ccf245 890}
891
892void
893clientReplyContext::purgeRequest()
edce4d98 894{
bf8fe701 895 debugs(88, 3, "Config2.onoff.enable_purge = " <<
896 Config2.onoff.enable_purge);
62e76326 897
edce4d98 898 if (!Config2.onoff.enable_purge) {
62e76326 899 http->logType = LOG_TCP_DENIED;
955394ce 900 ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL, http->getConn()->clientConnection->remote, http->request);
0655fa4d 901 startError(err);
62e76326 902 return;
edce4d98 903 }
62e76326 904
edce4d98 905 /* Release both IP cache */
cc192b50 906 ipcacheInvalidate(http->request->GetHost());
edce4d98 907
e6ccf245 908 if (!http->flags.purging)
62e76326 909 purgeRequestFindObjectToPurge();
e6ccf245 910 else
62e76326 911 purgeDoMissPurge();
e6ccf245 912}
913
914void
915clientReplyContext::purgeDoMissPurge()
916{
29b8d8d6 917 http->logType = LOG_TCP_MISS;
e6ccf245 918 lookingforstore = 3;
c2a7cefd 919 StoreEntry::getPublicByRequestMethod(this,http->request, Http::METHOD_GET);
e6ccf245 920}
921
922void
3b13a8fd 923clientReplyContext::purgeDoPurgeGet(StoreEntry *newEntry)
e6ccf245 924{
925 assert (newEntry);
926 /* Move to new() when that is created */
955394ce 927 purgeStatus = Http::scNotFound;
e6ccf245 928
929 if (!newEntry->isNull()) {
62e76326 930 /* Release the cached URI */
bf8fe701 931 debugs(88, 4, "clientPurgeRequest: GET '" << newEntry->url() << "'" );
8dceeee3 932#if USE_HTCP
c2a7cefd 933 neighborsHtcpClear(newEntry, NULL, http->request, HttpRequestMethod(Http::METHOD_GET), HTCP_CLR_PURGE);
8dceeee3 934#endif
5f33b71d 935 newEntry->release();
955394ce 936 purgeStatus = Http::scOkay;
edce4d98 937 }
62e76326 938
e6ccf245 939 lookingforstore = 4;
c2a7cefd 940 StoreEntry::getPublicByRequestMethod(this, http->request, Http::METHOD_HEAD);
e6ccf245 941}
942
943void
3b13a8fd 944clientReplyContext::purgeDoPurgeHead(StoreEntry *newEntry)
e6ccf245 945{
4fc0ac76 946 if (newEntry && !newEntry->isNull()) {
bf8fe701 947 debugs(88, 4, "clientPurgeRequest: HEAD '" << newEntry->url() << "'" );
8dceeee3 948#if USE_HTCP
c2a7cefd 949 neighborsHtcpClear(newEntry, NULL, http->request, HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_PURGE);
8dceeee3 950#endif
5f33b71d 951 newEntry->release();
955394ce 952 purgeStatus = Http::scOkay;
edce4d98 953 }
62e76326 954
edce4d98 955 /* And for Vary, release the base URI if none of the headers was included in the request */
62e76326 956
edce4d98 957 if (http->request->vary_headers
62e76326 958 && !strstr(http->request->vary_headers, "=")) {
c2a7cefd 959 StoreEntry *entry = storeGetPublic(urlCanonical(http->request), Http::METHOD_GET);
62e76326 960
961 if (entry) {
bf8fe701 962 debugs(88, 4, "clientPurgeRequest: Vary GET '" << entry->url() << "'" );
8dceeee3 963#if USE_HTCP
c2a7cefd 964 neighborsHtcpClear(entry, NULL, http->request, HttpRequestMethod(Http::METHOD_GET), HTCP_CLR_PURGE);
8dceeee3 965#endif
5f33b71d 966 entry->release();
955394ce 967 purgeStatus = Http::scOkay;
62e76326 968 }
969
c2a7cefd 970 entry = storeGetPublic(urlCanonical(http->request), Http::METHOD_HEAD);
62e76326 971
972 if (entry) {
bf8fe701 973 debugs(88, 4, "clientPurgeRequest: Vary HEAD '" << entry->url() << "'" );
8dceeee3 974#if USE_HTCP
c2a7cefd 975 neighborsHtcpClear(entry, NULL, http->request, HttpRequestMethod(Http::METHOD_HEAD), HTCP_CLR_PURGE);
8dceeee3 976#endif
5f33b71d 977 entry->release();
955394ce 978 purgeStatus = Http::scOkay;
62e76326 979 }
edce4d98 980 }
62e76326 981
edce4d98 982 /*
983 * Make a new entry to hold the reply to be written
984 * to the client.
985 */
528b2c61 986 /* FIXME: This doesn't need to go through the store. Simply
987 * push down the client chain
988 */
f206b652 989 createStoreEntry(http->request->method, RequestFlags());
62e76326 990
0655fa4d 991 triggerInitialStoreRead();
62e76326 992
7dc5f514 993 HttpReply *rep = new HttpReply;
11992b6f 994 rep->setHeaders(purgeStatus, NULL, NULL, 0, 0, -1);
7dc5f514 995 http->storeEntry()->replaceHttpReply(rep);
86a2f789 996 http->storeEntry()->complete();
edce4d98 997}
998
999void
0655fa4d 1000clientReplyContext::traceReply(clientStreamNode * node)
edce4d98 1001{
076df709 1002 clientStreamNode *nextNode = (clientStreamNode *)node->node.next->data;
8bcf08e0 1003 StoreIOBuffer localTempBuffer;
f206b652 1004 createStoreEntry(http->request->method, RequestFlags());
076df709
FC
1005 localTempBuffer.offset = nextNode->readBuffer.offset + headers_sz;
1006 localTempBuffer.length = nextNode->readBuffer.length;
1007 localTempBuffer.data = nextNode->readBuffer.data;
86a2f789 1008 storeClientCopy(sc, http->storeEntry(),
8bcf08e0 1009 localTempBuffer, SendMoreData, this);
d88e3c49 1010 http->storeEntry()->releaseRequest();
3900307b 1011 http->storeEntry()->buffer();
7dc5f514 1012 HttpReply *rep = new HttpReply;
955394ce 1013 rep->setHeaders(Http::scOkay, NULL, "text/plain", http->request->prefixLen(), 0, squid_curtime);
db237875 1014 http->storeEntry()->replaceHttpReply(rep);
5cafad19 1015 http->request->swapOut(http->storeEntry());
86a2f789 1016 http->storeEntry()->complete();
edce4d98 1017}
1018
1019#define SENDING_BODY 0
1020#define SENDING_HDRSONLY 1
1021int
0655fa4d 1022clientReplyContext::checkTransferDone()
edce4d98 1023{
86a2f789 1024 StoreEntry *entry = http->storeEntry();
62e76326 1025
edce4d98 1026 if (entry == NULL)
62e76326 1027 return 0;
1028
1029 /*
edce4d98 1030 * For now, 'done_copying' is used for special cases like
1031 * Range and HEAD requests.
1032 */
0655fa4d 1033 if (http->flags.done_copying)
62e76326 1034 return 1;
1035
450fe1cb 1036 if (http->request->flags.chunkedReply && !flags.complete) {
4ad60609
AR
1037 // last-chunk was not sent
1038 return 0;
1039 }
1040
edce4d98 1041 /*
1042 * Handle STORE_OK objects.
1043 * objectLen(entry) will be set proprely.
26ac0430 1044 * RC: Does objectLen(entry) include the Headers?
0e3be1ea 1045 * RC: Yes.
edce4d98 1046 */
1047 if (entry->store_status == STORE_OK) {
0655fa4d 1048 return storeOKTransferDone();
e39b9382 1049 } else {
0655fa4d 1050 return storeNotOKTransferDone();
edce4d98 1051 }
e39b9382 1052}
1053
1054int
1055clientReplyContext::storeOKTransferDone() const
1056{
aa1a691e
AR
1057 assert(http->storeEntry()->objectLen() >= 0);
1058 assert(http->storeEntry()->objectLen() >= headers_sz);
47f6e231 1059 if (http->out.offset >= http->storeEntry()->objectLen() - headers_sz) {
26ac0430
AJ
1060 debugs(88,3,HERE << "storeOKTransferDone " <<
1061 " out.offset=" << http->out.offset <<
1062 " objectLen()=" << http->storeEntry()->objectLen() <<
1063 " headers_sz=" << headers_sz);
62e76326 1064 return 1;
47f6e231 1065 }
62e76326 1066
e39b9382 1067 return 0;
1068}
1069
1070int
1071clientReplyContext::storeNotOKTransferDone() const
1072{
edce4d98 1073 /*
1074 * Now, handle STORE_PENDING objects
1075 */
86a2f789 1076 MemObject *mem = http->storeEntry()->mem_obj;
edce4d98 1077 assert(mem != NULL);
1078 assert(http->request != NULL);
0e3be1ea 1079 /* mem->reply was wrong because it uses the UPSTREAM header length!!! */
8bcf08e0 1080 HttpReply const *curReply = mem->getReply();
62e76326 1081
e39b9382 1082 if (headers_sz == 0)
62e76326 1083 /* haven't found end of headers yet */
1084 return 0;
e39b9382 1085
edce4d98 1086 /*
1087 * Figure out how much data we are supposed to send.
1088 * If we are sending a body and we don't have a content-length,
1089 * then we must wait for the object to become STORE_OK.
1090 */
8bcf08e0 1091 if (curReply->content_length < 0)
62e76326 1092 return 0;
e39b9382 1093
8bcf08e0 1094 int64_t expectedLength = curReply->content_length + http->out.headers_sz;
62e76326 1095
e39b9382 1096 if (http->out.size < expectedLength)
62e76326 1097 return 0;
47f6e231 1098 else {
26ac0430
AJ
1099 debugs(88,3,HERE << "storeNotOKTransferDone " <<
1100 " out.size=" << http->out.size <<
1101 " expectedLength=" << expectedLength);
62e76326 1102 return 1;
47f6e231 1103 }
edce4d98 1104}
1105
edce4d98 1106/* A write has completed, what is the next status based on the
1107 * canonical request data?
1108 * 1 something is wrong
1109 * 0 nothing is wrong.
1110 *
1111 */
1112int
59a1efb2 1113clientHttpRequestStatus(int fd, ClientHttpRequest const *http)
edce4d98 1114{
062f7f4e
FC
1115#if SIZEOF_INT64_T == 4
1116 if (http->out.size > 0x7FFF0000) {
e0236918
FC
1117 debugs(88, DBG_IMPORTANT, "WARNING: closing FD " << fd << " to prevent out.size counter overflow");
1118 debugs(88, DBG_IMPORTANT, "\tclient " << http->getConn()->peer);
1119 debugs(88, DBG_IMPORTANT, "\treceived " << http->out.size << " bytes");
1120 debugs(88, DBG_IMPORTANT, "\tURI " << http->log_uri);
062f7f4e
FC
1121 return 1;
1122 }
1123
1124 if (http->out.offset > 0x7FFF0000) {
e0236918
FC
1125 debugs(88, DBG_IMPORTANT, "WARNING: closing FD " << fd < " to prevent out.offset counter overflow");
1126 debugs(88, DBG_IMPORTANT, "\tclient " << http->getConn()->peer);
1127 debugs(88, DBG_IMPORTANT, "\treceived " << http->out.size << " bytes, offset " << http->out.offset);
1128 debugs(88, DBG_IMPORTANT, "\tURI " << http->log_uri);
062f7f4e
FC
1129 return 1;
1130 }
1131
1132#endif
edce4d98 1133 return 0;
1134}
1135
62e76326 1136/* Preconditions:
edce4d98 1137 * *http is a valid structure.
1138 * fd is either -1, or an open fd.
1139 *
1140 * TODO: enumify this
1141 *
1142 * This function is used by any http request sink, to determine the status
1143 * of the object.
1144 */
1145clientStream_status_t
59a1efb2 1146clientReplyStatus(clientStreamNode * aNode, ClientHttpRequest * http)
edce4d98 1147{
0655fa4d 1148 clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1149 assert (context);
1150 assert (context->http == http);
1151 return context->replyStatus();
1152}
1153
1154clientStream_status_t
1155clientReplyContext::replyStatus()
1156{
edce4d98 1157 int done;
1158 /* Here because lower nodes don't need it */
62e76326 1159
3daaed1a 1160 if (http->storeEntry() == NULL) {
1161 debugs(88, 5, "clientReplyStatus: no storeEntry");
62e76326 1162 return STREAM_FAILED; /* yuck, but what can we do? */
3daaed1a 1163 }
62e76326 1164
3daaed1a 1165 if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) {
62e76326 1166 /* TODO: Could upstream read errors (result.flags.error) be
1167 * lost, and result in undersize requests being considered
1168 * complete. Should we tcp reset such connections ?
1169 */
3daaed1a 1170 debugs(88, 5, "clientReplyStatus: aborted storeEntry");
62e76326 1171 return STREAM_FAILED;
3daaed1a 1172 }
62e76326 1173
0655fa4d 1174 if ((done = checkTransferDone()) != 0 || flags.complete) {
1bfe9ade 1175 debugs(88, 5, "clientReplyStatus: transfer is DONE: " << done << flags.complete);
62e76326 1176 /* Ok we're finished, but how? */
1177
4ad60609
AR
1178 const int64_t expectedBodySize =
1179 http->storeEntry()->getReply()->bodySize(http->request->method);
450fe1cb 1180 if (!http->request->flags.proxyKeepalive && expectedBodySize < 0) {
bf8fe701 1181 debugs(88, 5, "clientReplyStatus: closing, content_length < 0");
62e76326 1182 return STREAM_FAILED;
1183 }
1184
1185 if (!done) {
bf8fe701 1186 debugs(88, 5, "clientReplyStatus: closing, !done, but read 0 bytes");
62e76326 1187 return STREAM_FAILED;
1188 }
1189
4ad60609 1190 if (expectedBodySize >= 0 && !http->gotEnough()) {
bf8fe701 1191 debugs(88, 5, "clientReplyStatus: client didn't get all it expected");
62e76326 1192 return STREAM_UNPLANNED_COMPLETE;
1193 }
1194
ca6d5de2
AR
1195 debugs(88, 5, "clientReplyStatus: stream complete; keepalive=" <<
1196 http->request->flags.proxyKeepalive);
1197 return STREAM_COMPLETE;
edce4d98 1198 }
62e76326 1199
16611143 1200 // XXX: Should this be checked earlier? We could return above w/o checking.
1201 if (reply->receivedBodyTooLarge(*http->request, http->out.offset - 4096)) {
b51aec66 1202 /* 4096 is a margin for the HTTP headers included in out.offset */
bf8fe701 1203 debugs(88, 5, "clientReplyStatus: client reply body is too large");
62e76326 1204 return STREAM_FAILED;
0e3be1ea 1205 }
62e76326 1206
edce4d98 1207 return STREAM_NONE;
1208}
1209
62e76326 1210/* Responses with no body will not have a content-type header,
edce4d98 1211 * which breaks the rep_mime_type acl, which
1212 * coincidentally, is the most common acl for reply access lists.
1213 * A better long term fix for this is to allow acl matchs on the various
26ac0430
AJ
1214 * status codes, and then supply a default ruleset that puts these
1215 * codes before any user defines access entries. That way the user
edce4d98 1216 * can choose to block these responses where appropriate, but won't get
1217 * mysterious breakages.
1218 */
0655fa4d 1219bool
955394ce 1220clientReplyContext::alwaysAllowResponse(Http::StatusCode sline) const
edce4d98 1221{
4ef4b952 1222 bool result;
1223
edce4d98 1224 switch (sline) {
62e76326 1225
955394ce 1226 case Http::scContinue:
62e76326 1227
955394ce 1228 case Http::scSwitchingProtocols:
62e76326 1229
955394ce 1230 case Http::scProcessing:
62e76326 1231
955394ce 1232 case Http::scNoContent:
62e76326 1233
955394ce 1234 case Http::scNotModified:
4ef4b952 1235 result = true;
62e76326 1236 break;
1237
edce4d98 1238 default:
4ef4b952 1239 result = false;
edce4d98 1240 }
4ef4b952 1241
1242 return result;
edce4d98 1243}
1244
a5c8d64d
AJ
1245/**
1246 * Generate the reply headers sent to client.
1247 *
1248 * Filters out unwanted entries and hop-by-hop from original reply header
1249 * then adds extra entries if we have more info than origin server
1250 * then adds Squid specific entries
edce4d98 1251 */
0655fa4d 1252void
1253clientReplyContext::buildReplyHeader()
edce4d98 1254{
7dc5f514 1255 HttpHeader *hdr = &reply->header;
c8be6d7b 1256 int is_hit = logTypeIsATcpHit(http->logType);
190154cf 1257 HttpRequest *request = http->request;
edce4d98 1258#if DONT_FILTER_THESE
1259 /* but you might want to if you run Squid as an HTTP accelerator */
a9925b40 1260 /* hdr->delById(HDR_ACCEPT_RANGES); */
1261 hdr->delById(HDR_ETAG);
edce4d98 1262#endif
62e76326 1263
edce4d98 1264 if (is_hit)
a9925b40 1265 hdr->delById(HDR_SET_COOKIE);
f89d4012 1266 // TODO: RFC 2965 : Must honour Cache-Control: no-cache="set-cookie2" and remove header.
62e76326 1267
23c6d7e7 1268 // if there is not configured a peer proxy with login=PASS or login=PASSTHRU option enabled
dcf3665b 1269 // remove the Proxy-Authenticate header
23c6d7e7 1270 if ( !request->peer_login || (strcmp(request->peer_login,"PASS") != 0 && strcmp(request->peer_login,"PASSTHRU") != 0))
26ac0430 1271 reply->header.delById(HDR_PROXY_AUTHENTICATE);
dcf3665b 1272
2cdeea82 1273 reply->header.removeHopByHopEntries();
62e76326 1274
1275 // if (request->range)
7dc5f514 1276 // clientBuildRangeHeader(http, reply);
a5c8d64d 1277
edce4d98 1278 /*
1279 * Add a estimated Age header on cache hits.
1280 */
1281 if (is_hit) {
62e76326 1282 /*
1283 * Remove any existing Age header sent by upstream caches
1284 * (note that the existing header is passed along unmodified
1285 * on cache misses)
1286 */
a9925b40 1287 hdr->delById(HDR_AGE);
62e76326 1288 /*
1289 * This adds the calculated object age. Note that the details of the
1290 * age calculation is performed by adjusting the timestamp in
3900307b 1291 * StoreEntry::timestampsSet(), not here.
62e76326 1292 */
bbe58ab5 1293 if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
a9925b40 1294 hdr->delById(HDR_DATE);
1295 hdr->insertTime(HDR_DATE, squid_curtime);
90fa5816
AJ
1296 } else if (http->getConn() && http->getConn()->port->actAsOrigin) {
1297 // Swap the Date: header to current time if we are simulating an origin
fd49b08b
A
1298 HttpHeaderEntry *h = hdr->findEntry(HDR_DATE);
1299 if (h)
1300 hdr->putExt("X-Origin-Date", h->value.termedBuf());
90fa5816
AJ
1301 hdr->delById(HDR_DATE);
1302 hdr->insertTime(HDR_DATE, squid_curtime);
fd49b08b
A
1303 h = hdr->findEntry(HDR_EXPIRES);
1304 if (h && http->storeEntry()->expires >= 0) {
1305 hdr->putExt("X-Origin-Expires", h->value.termedBuf());
1306 hdr->delById(HDR_EXPIRES);
1307 hdr->insertTime(HDR_EXPIRES, squid_curtime + http->storeEntry()->expires - http->storeEntry()->timestamp);
1308 }
90fa5816
AJ
1309 if (http->storeEntry()->timestamp <= squid_curtime) {
1310 // put X-Cache-Age: instead of Age:
fd49b08b
A
1311 char age[64];
1312 snprintf(age, sizeof(age), "%ld", (long int) squid_curtime - http->storeEntry()->timestamp);
1313 hdr->putExt("X-Cache-Age", age);
1314 }
a81e7089 1315 } else if (http->storeEntry()->timestamp <= squid_curtime) {
a9925b40 1316 hdr->putInt(HDR_AGE,
1317 squid_curtime - http->storeEntry()->timestamp);
62e76326 1318 /* Signal old objects. NB: rfc 2616 is not clear,
1319 * by implication, on whether we should do this to all
1320 * responses, or only cache hits.
1321 * 14.46 states it ONLY applys for heuristically caclulated
1322 * freshness values, 13.2.4 doesn't specify the same limitation.
1323 * We interpret RFC 2616 under the combination.
1324 */
1325 /* TODO: if maxage or s-maxage is present, don't do this */
1326
86a2f789 1327 if (squid_curtime - http->storeEntry()->timestamp >= 86400) {
8bcf08e0
FC
1328 char tbuf[512];
1329 snprintf (tbuf, sizeof(tbuf), "%s %s %s",
62e76326 1330 "113", ThisCache,
1331 "This cache hit is still fresh and more than 1 day old");
8bcf08e0 1332 hdr->putStr(HDR_WARNING, tbuf);
62e76326 1333 }
1334 }
a5c8d64d 1335 }
528b2c61 1336
a5c8d64d
AJ
1337 /* RFC 2616: Section 14.18
1338 *
1339 * Add a Date: header if missing.
1340 * We have access to a clock therefore are required to amend any shortcoming in servers.
1341 *
1342 * NP: done after Age: to prevent ENTRY_SPECIAL double-handling this header.
1343 */
1344 if ( !hdr->has(HDR_DATE) ) {
f0dedeb5 1345 if (!http->storeEntry())
a5c8d64d 1346 hdr->insertTime(HDR_DATE, squid_curtime);
f0dedeb5
AJ
1347 else if (http->storeEntry()->timestamp > 0)
1348 hdr->insertTime(HDR_DATE, http->storeEntry()->timestamp);
1349 else {
d9d98242 1350 debugs(88,DBG_IMPORTANT,"BUG 3279: HTTP reply without Date:");
e78ae89a
AJ
1351 /* dump something useful about the problem */
1352 http->storeEntry()->dump(DBG_IMPORTANT);
f0dedeb5 1353 }
edce4d98 1354 }
62e76326 1355
bcfba8bd 1356 // add Warnings required by RFC 2616 if serving a stale hit
450fe1cb 1357 if (http->request->flags.staleIfHit && logTypeIsATcpHit(http->logType)) {
bcfba8bd 1358 hdr->putWarning(110, "Response is stale");
450fe1cb 1359 if (http->request->flags.needValidation)
bcfba8bd
AR
1360 hdr->putWarning(111, "Revalidation failed");
1361 }
1362
0bd9aa82 1363 /* Filter unproxyable authentication types */
ddd31ed3 1364 if (http->logType != LOG_TCP_DENIED &&
d67acb4e 1365 hdr->has(HDR_WWW_AUTHENTICATE)) {
62e76326 1366 HttpHeaderPos pos = HttpHeaderInitPos;
1367 HttpHeaderEntry *e;
1368
26ac0430 1369 int connection_auth_blocked = 0;
a9925b40 1370 while ((e = hdr->getEntry(&pos))) {
d67acb4e 1371 if (e->id == HDR_WWW_AUTHENTICATE) {
5b4117d8 1372 const char *value = e->value.rawBuf();
62e76326 1373
1374 if ((strncasecmp(value, "NTLM", 4) == 0 &&
1375 (value[4] == '\0' || value[4] == ' '))
1376 ||
1377 (strncasecmp(value, "Negotiate", 9) == 0 &&
d67acb4e 1378 (value[9] == '\0' || value[9] == ' '))
26ac0430
AJ
1379 ||
1380 (strncasecmp(value, "Kerberos", 8) == 0 &&
1381 (value[8] == '\0' || value[8] == ' '))) {
450fe1cb 1382 if (request->flags.connectionAuthDisabled) {
26ac0430 1383 hdr->delAt(pos, connection_auth_blocked);
d67acb4e
AJ
1384 continue;
1385 }
e857372a 1386 request->flags.mustKeepalive = true;
45e5102d 1387 if (!request->flags.accelerated && !request->flags.intercepted) {
d67acb4e 1388 httpHeaderPutStrf(hdr, HDR_PROXY_SUPPORT, "Session-Based-Authentication");
26ac0430
AJ
1389 /*
1390 We send "[Proxy-]Connection: Proxy-Support" header to mark
1391 Proxy-Support as a hop-by-hop header for intermediaries that do not
1392 understand the semantics of this header. The RFC should have included
1393 this recommendation.
1394 */
d67acb4e
AJ
1395 httpHeaderPutStrf(hdr, HDR_CONNECTION, "Proxy-support");
1396 }
1397 break;
26ac0430 1398 }
62e76326 1399 }
1400 }
d67acb4e
AJ
1401
1402 if (connection_auth_blocked)
ba9fb01d 1403 hdr->refreshMask();
0bd9aa82 1404 }
62e76326 1405
2f1431ea 1406#if USE_AUTH
edce4d98 1407 /* Handle authentication headers */
26ac0430 1408 if (http->logType == LOG_TCP_DENIED &&
9b769c67
AJ
1409 ( reply->sline.status() == Http::scProxyAuthenticationRequired ||
1410 reply->sline.status() == Http::scUnauthorized)
26ac0430
AJ
1411 ) {
1412 /* Add authentication header */
1413 /*! \todo alter errorstate to be accel on|off aware. The 0 on the next line
1414 * depends on authenticate behaviour: all schemes to date send no extra
1415 * data on 407/401 responses, and do not check the accel state on 401/407
1416 * responses
1417 */
1418 authenticateFixHeader(reply, request->auth_user_request, request, 0, 1);
a33a428a
AJ
1419 } else if (request->auth_user_request != NULL)
1420 authenticateFixHeader(reply, request->auth_user_request, request, http->flags.accel, 0);
2f1431ea 1421#endif
62e76326 1422
edce4d98 1423 /* Append X-Cache */
1424 httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",
62e76326 1425 is_hit ? "HIT" : "MISS", getMyHostname());
1426
edce4d98 1427#if USE_CACHE_DIGESTS
1428 /* Append X-Cache-Lookup: -- temporary hack, to be removed @?@ @?@ */
1429 httpHeaderPutStrf(hdr, HDR_X_CACHE_LOOKUP, "%s from %s:%d",
0655fa4d 1430 lookup_type ? lookup_type : "NONE",
62e76326 1431 getMyHostname(), getMyPort());
1432
edce4d98 1433#endif
62e76326 1434
45e5102d 1435 const bool maySendChunkedReply = !request->multipartRangeRequest() &&
f7701e72 1436 reply->sline.protocol == AnyP::PROTO_HTTP && // response is HTTP
526ed14e 1437 (request->http_ver >= Http::ProtocolVersion(1, 1));
4ad60609 1438
f529bb20 1439 /* Check whether we should send keep-alive */
9b769c67 1440 if (!Config.onoff.error_pconns && reply->sline.status() >= 400 && !request->flags.mustKeepalive) {
bf8fe701 1441 debugs(33, 3, "clientBuildReplyHeader: Error, don't keep-alive");
e857372a 1442 request->flags.proxyKeepalive = false;
450fe1cb 1443 } else if (!Config.onoff.client_pconns && !request->flags.mustKeepalive) {
17b57873 1444 debugs(33, 2, "clientBuildReplyHeader: Connection Keep-Alive not requested by admin or client");
e857372a 1445 request->flags.proxyKeepalive = false;
450fe1cb 1446 } else if (request->flags.proxyKeepalive && shutting_down) {
f529bb20 1447 debugs(88, 3, "clientBuildReplyHeader: Shutting down, don't keep-alive.");
e857372a 1448 request->flags.proxyKeepalive = false;
450fe1cb 1449 } else if (request->flags.connectionAuth && !reply->keep_alive) {
26ac0430 1450 debugs(33, 2, "clientBuildReplyHeader: Connection oriented auth but server side non-persistent");
e857372a 1451 request->flags.proxyKeepalive = false;
4ad60609 1452 } else if (reply->bodySize(request->method) < 0 && !maySendChunkedReply) {
17b57873 1453 debugs(88, 3, "clientBuildReplyHeader: can't keep-alive, unknown body size" );
e857372a 1454 request->flags.proxyKeepalive = false;
450fe1cb 1455 } else if (fdUsageHigh()&& !request->flags.mustKeepalive) {
17b57873 1456 debugs(88, 3, "clientBuildReplyHeader: Not many unused FDs, can't keep-alive");
e857372a 1457 request->flags.proxyKeepalive = false;
450fe1cb 1458 } else if (request->flags.sslBumped && !reply->persistent()) {
49f1c7f7
AR
1459 // We do not really have to close, but we pretend we are a tunnel.
1460 debugs(88, 3, "clientBuildReplyHeader: bumped reply forces close");
e857372a 1461 request->flags.proxyKeepalive = false;
693cb033
CT
1462 } else if (request->pinnedConnection() && !reply->persistent()) {
1463 // The peer wants to close the pinned connection
1464 debugs(88, 3, "pinned reply forces close");
e857372a 1465 request->flags.proxyKeepalive = false;
17b57873 1466 }
d67acb4e 1467
4ad60609
AR
1468 // Decide if we send chunked reply
1469 if (maySendChunkedReply &&
450fe1cb 1470 request->flags.proxyKeepalive &&
4cb2536f 1471 reply->bodySize(request->method) < 0) {
4ad60609 1472 debugs(88, 3, "clientBuildReplyHeader: chunked reply");
e857372a 1473 request->flags.chunkedReply = true;
4ad60609
AR
1474 hdr->putStr(HDR_TRANSFER_ENCODING, "chunked");
1475 }
f529bb20 1476
4dea3fdd 1477 /* Append VIA */
92caea77 1478 if (Config.onoff.via) {
62e76326 1479 LOCAL_ARRAY(char, bbuf, MAX_URL + 32);
30abd221 1480 String strVia;
26ac0430 1481 hdr->getList(HDR_VIA, &strVia);
88331887 1482 snprintf(bbuf, MAX_URL + 32, "%d.%d %s",
7dc5f514 1483 reply->sline.version.major,
1484 reply->sline.version.minor,
62e76326 1485 ThisCache);
1486 strListAdd(&strVia, bbuf, ',');
a9925b40 1487 hdr->delById(HDR_VIA);
5b4117d8 1488 hdr->putStr(HDR_VIA, strVia.termedBuf());
c8be6d7b 1489 }
95e78500 1490 /* Signal keep-alive or close explicitly */
450fe1cb 1491 hdr->putStr(HDR_CONNECTION, request->flags.proxyKeepalive ? "keep-alive" : "close");
62e76326 1492
edce4d98 1493#if ADD_X_REQUEST_URI
1494 /*
1495 * Knowing the URI of the request is useful when debugging persistent
1496 * connections in a client; we cannot guarantee the order of http headers,
1497 * but X-Request-URI is likely to be the very last header to ease use from a
1498 * debugger [hdr->entries.count-1].
1499 */
a9925b40 1500 hdr->putStr(HDR_X_REQUEST_URI,
1501 http->memOjbect()->url ? http->memObject()->url : http->uri);
62e76326 1502
edce4d98 1503#endif
62e76326 1504
45cca89d
AJ
1505 /* Surrogate-Control requires Surrogate-Capability from upstream to pass on */
1506 if ( hdr->has(HDR_SURROGATE_CONTROL) ) {
1507 if (!request->header.has(HDR_SURROGATE_CAPABILITY)) {
1508 hdr->delById(HDR_SURROGATE_CONTROL);
1509 }
1510 /* TODO: else case: drop any controls intended specifically for our surrogate ID */
1511 }
1512
8c01ada0 1513 httpHdrMangleList(hdr, request, ROR_REPLY);
edce4d98 1514}
1515
0655fa4d 1516void
b297bcd0 1517clientReplyContext::cloneReply()
edce4d98 1518{
7dc5f514 1519 assert(reply == NULL);
0655fa4d 1520
b248c2a3
AJ
1521 reply = http->storeEntry()->getReply()->clone();
1522 HTTPMSGLOCK(reply);
7dc5f514 1523
0c3d3f65 1524 if (reply->sline.protocol == AnyP::PROTO_HTTP) {
871c031f 1525 /* RFC 2616 requires us to advertise our 1.1 version (but only on real HTTP traffic) */
526ed14e 1526 reply->sline.version = Http::ProtocolVersion(1,1);
e77d7ef0 1527 }
0655fa4d 1528
1529 /* do header conversions */
1530 buildReplyHeader();
edce4d98 1531}
1532
7681a3b9
AR
1533/// Safely disposes of an entry pointing to a cache hit that we do not want.
1534/// We cannot just ignore the entry because it may be locking or otherwise
1535/// holding an associated cache resource of some sort.
1536void
1537clientReplyContext::forgetHit()
1538{
1539 StoreEntry *e = http->storeEntry();
1540 assert(e); // or we are not dealing with a hit
1541 // We probably have not locked the entry earlier, unfortunately. We lock it
1542 // now so that we can unlock two lines later (and trigger cleanup).
1543 // Ideally, ClientHttpRequest::storeEntry() should lock/unlock, but it is
1544 // used so inconsistently that simply adding locking there leads to bugs.
1bfe9ade 1545 e->lock("clientReplyContext::forgetHit");
7681a3b9 1546 http->storeEntry(NULL);
1bfe9ade 1547 e->unlock("clientReplyContext::forgetHit"); // may delete e
7681a3b9
AR
1548}
1549
e6ccf245 1550void
1551clientReplyContext::identifyStoreObject()
edce4d98 1552{
190154cf 1553 HttpRequest *r = http->request;
62e76326 1554
45e5102d 1555 if (r->flags.cachable || r->flags.internal) {
62e76326 1556 lookingforstore = 5;
1557 StoreEntry::getPublicByRequest (this, r);
559da936 1558 } else {
62e76326 1559 identifyFoundObject (NullStoreEntry::getInstance());
559da936 1560 }
e6ccf245 1561}
1562
7d5f62a4
AJ
1563/**
1564 * Check state of the current StoreEntry object.
1565 * to see if we can determine the final status of the request.
1566 */
e6ccf245 1567void
1568clientReplyContext::identifyFoundObject(StoreEntry *newEntry)
1569{
1570 StoreEntry *e = newEntry;
190154cf 1571 HttpRequest *r = http->request;
62e76326 1572
d85b8894 1573 /** \li If the entry received isNull() then we ignore it. */
e6ccf245 1574 if (e->isNull()) {
86a2f789 1575 http->storeEntry(NULL);
e6ccf245 1576 } else {
86a2f789 1577 http->storeEntry(e);
e6ccf245 1578 }
62e76326 1579
86a2f789 1580 e = http->storeEntry();
a12a049a 1581
7d5f62a4 1582 /* Release IP-cache entries on reload */
d85b8894 1583 /** \li If the request has no-cache flag set or some no_cache HACK in operation we
7d5f62a4
AJ
1584 * 'invalidate' the cached IP entries for this request ???
1585 */
17852883 1586 if (r->flags.noCache || r->flags.noCacheHack())
cc192b50 1587 ipcacheInvalidateNegative(r->GetHost());
45e5102d 1588
edce4d98 1589#if USE_CACHE_DIGESTS
86a2f789 1590 lookup_type = http->storeEntry() ? "HIT" : "MISS";
edce4d98 1591#endif
62e76326 1592
86a2f789 1593 if (NULL == http->storeEntry()) {
d85b8894 1594 /** \li If no StoreEntry object is current assume this object isn't in the cache set MISS*/
7d5f62a4 1595 debugs(85, 3, "clientProcessRequest2: StoreEntry is NULL - MISS");
62e76326 1596 http->logType = LOG_TCP_MISS;
1597 doGetMoreData();
1598 return;
edce4d98 1599 }
62e76326 1600
edce4d98 1601 if (Config.onoff.offline) {
d85b8894 1602 /** \li If we are running in offline mode set to HIT */
e7b93a55 1603 debugs(85, 3, "clientProcessRequest2: offline HIT " << *e);
62e76326 1604 http->logType = LOG_TCP_HIT;
1605 doGetMoreData();
1606 return;
edce4d98 1607 }
62e76326 1608
edce4d98 1609 if (http->redirect.status) {
d85b8894 1610 /** \li If redirection status is True force this to be a MISS */
539283df 1611 debugs(85, 3, "REDIRECT status forced StoreEntry to NULL (no body on 3XX responses) " << *e);
7681a3b9 1612 forgetHit();
16cd3193 1613 http->logType = LOG_TCP_REDIRECT;
62e76326 1614 doGetMoreData();
1615 return;
edce4d98 1616 }
62e76326 1617
3900307b 1618 if (!e->validToSend()) {
e7b93a55 1619 debugs(85, 3, "clientProcessRequest2: !storeEntryValidToSend MISS " << *e);
7681a3b9 1620 forgetHit();
62e76326 1621 http->logType = LOG_TCP_MISS;
1622 doGetMoreData();
1623 return;
edce4d98 1624 }
62e76326 1625
edce4d98 1626 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
d85b8894 1627 /* \li Special entries are always hits, no matter what the client says */
e7b93a55 1628 debugs(85, 3, "clientProcessRequest2: ENTRY_SPECIAL HIT " << *e);
62e76326 1629 http->logType = LOG_TCP_HIT;
1630 doGetMoreData();
1631 return;
edce4d98 1632 }
62e76326 1633
450fe1cb 1634 if (r->flags.noCache) {
e7b93a55 1635 debugs(85, 3, "clientProcessRequest2: no-cache REFRESH MISS " << *e);
7681a3b9 1636 forgetHit();
62e76326 1637 http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
1638 doGetMoreData();
1639 return;
edce4d98 1640 }
62e76326 1641
e7b93a55 1642 debugs(85, 3, "clientProcessRequest2: default HIT " << *e);
e6ccf245 1643 http->logType = LOG_TCP_HIT;
1644 doGetMoreData();
edce4d98 1645}
1646
d85b8894
AJ
1647/**
1648 * Request more data from the store for the client Stream
edce4d98 1649 * This is *the* entry point to this module.
1650 *
1651 * Preconditions:
d85b8894
AJ
1652 * - This is the head of the list.
1653 * - There is at least one more node.
1654 * - Data context is not null
edce4d98 1655 */
1656void
59a1efb2 1657clientGetMoreData(clientStreamNode * aNode, ClientHttpRequest * http)
edce4d98 1658{
edce4d98 1659 /* Test preconditions */
e6ccf245 1660 assert(aNode != NULL);
1661 assert(cbdataReferenceValid(aNode));
e6ccf245 1662 assert(aNode->node.prev == NULL);
1663 assert(aNode->node.next != NULL);
0655fa4d 1664 clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1665 assert (context);
edce4d98 1666 assert(context->http == http);
1667
0655fa4d 1668 clientStreamNode *next = ( clientStreamNode *)aNode->node.next->data;
62e76326 1669
edce4d98 1670 if (!context->ourNode)
62e76326 1671 context->ourNode = aNode;
1672
e6ccf245 1673 /* no cbdatareference, this is only used once, and safely */
edce4d98 1674 if (context->flags.storelogiccomplete) {
62e76326 1675 StoreIOBuffer tempBuffer;
1676 tempBuffer.offset = next->readBuffer.offset + context->headers_sz;
1677 tempBuffer.length = next->readBuffer.length;
1678 tempBuffer.data = next->readBuffer.data;
1679
86a2f789 1680 storeClientCopy(context->sc, http->storeEntry(),
0655fa4d 1681 tempBuffer, clientReplyContext::SendMoreData, context);
62e76326 1682 return;
edce4d98 1683 }
62e76326 1684
c2a7cefd 1685 if (context->http->request->method == Http::METHOD_PURGE) {
62e76326 1686 context->purgeRequest();
1687 return;
edce4d98 1688 }
62e76326 1689
e18b8316 1690 // OPTIONS with Max-Forwards:0 handled in clientProcessRequest()
fc90edc3 1691
c2a7cefd 1692 if (context->http->request->method == Http::METHOD_TRACE) {
34ee6e73 1693 if (context->http->request->header.getInt64(HDR_MAX_FORWARDS) == 0) {
0655fa4d 1694 context->traceReply(aNode);
62e76326 1695 return;
1696 }
1697
1698 /* continue forwarding, not finished yet. */
1699 http->logType = LOG_TCP_MISS;
1700
1701 context->doGetMoreData();
edce4d98 1702 } else
62e76326 1703 context->identifyStoreObject();
e6ccf245 1704}
1705
1706void
1707clientReplyContext::doGetMoreData()
1708{
edce4d98 1709 /* We still have to do store logic processing - vary, cache hit etc */
86a2f789 1710 if (http->storeEntry() != NULL) {
62e76326 1711 /* someone found the object in the cache for us */
8bcf08e0 1712 StoreIOBuffer localTempBuffer;
34266cde 1713
1bfe9ade 1714 http->storeEntry()->lock("clientReplyContext::doGetMoreData");
62e76326 1715
c877c0bc
AR
1716 MemObject *mem_obj = http->storeEntry()->makeMemObject();
1717 if (!mem_obj->hasUris()) {
62e76326 1718 /*
1719 * This if-block exists because we don't want to clobber
1720 * a preexiting mem_obj->method value if the mem_obj
1721 * already exists. For example, when a HEAD request
1722 * is a cache hit for a GET response, we want to keep
1723 * the method as GET.
1724 */
c877c0bc 1725 mem_obj->setUris(storeId(), http->log_uri, http->request->method);
a8a0b1c2
EC
1726 /**
1727 * Here we can see if the object was
1728 * created using URL or alternative StoreID from helper.
1729 */
c877c0bc 1730 debugs(88, 3, "storeId: " << http->storeEntry()->mem_obj->storeId());
62e76326 1731 }
1732
86a2f789 1733 sc = storeClientListAdd(http->storeEntry(), this);
9a0a18de 1734#if USE_DELAY_POOLS
62e76326 1735 sc->setDelayId(DelayId::DelayClient(http));
edce4d98 1736#endif
62e76326 1737
1738 assert(http->logType == LOG_TCP_HIT);
1739 reqofs = 0;
1740 /* guarantee nothing has been sent yet! */
1741 assert(http->out.size == 0);
1742 assert(http->out.offset == 0);
425de4c8
AJ
1743
1744 if (Ip::Qos::TheConfig.isHitTosActive()) {
73c36fd9 1745 Ip::Qos::doTosLocalHit(http->getConn()->clientConnection);
425de4c8
AJ
1746 }
1747
1748 if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
73c36fd9 1749 Ip::Qos::doNfmarkLocalHit(http->getConn()->clientConnection);
b86ab75b 1750 }
425de4c8 1751
8bcf08e0
FC
1752 localTempBuffer.offset = reqofs;
1753 localTempBuffer.length = getNextNode()->readBuffer.length;
1754 localTempBuffer.data = getNextNode()->readBuffer.data;
1755 storeClientCopy(sc, http->storeEntry(), localTempBuffer, CacheHit, this);
edce4d98 1756 } else {
62e76326 1757 /* MISS CASE, http->logType is already set! */
0655fa4d 1758 processMiss();
edce4d98 1759 }
1760}
1761
d85b8894 1762/** The next node has removed itself from the stream. */
edce4d98 1763void
59a1efb2 1764clientReplyDetach(clientStreamNode * node, ClientHttpRequest * http)
edce4d98 1765{
d85b8894 1766 /** detach from the stream */
edce4d98 1767 clientStreamDetach(node, http);
1768}
1769
d85b8894
AJ
1770/**
1771 * Accepts chunk of a http message in buf, parses prefix, filters headers and
edce4d98 1772 * such, writes processed message to the message recipient
1773 */
1774void
0655fa4d 1775clientReplyContext::SendMoreData(void *data, StoreIOBuffer result)
edce4d98 1776{
e6ccf245 1777 clientReplyContext *context = static_cast<clientReplyContext *>(data);
528b2c61 1778 context->sendMoreData (result);
1779}
1780
1781void
1782clientReplyContext::makeThisHead()
1783{
1784 /* At least, I think thats what this does */
1785 dlinkDelete(&http->active, &ClientActiveRequests);
1786 dlinkAdd(http, &http->active, &ClientActiveRequests);
1787}
1788
1789bool
1790clientReplyContext::errorInStream(StoreIOBuffer const &result, size_t const &sizeToProcess)const
1791{
1792 return /* aborted request */
86a2f789 1793 (http->storeEntry() && EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) ||
62e76326 1794 /* Upstream read error */ (result.flags.error) ||
1795 /* Upstream EOF */ (sizeToProcess == 0);
528b2c61 1796}
1797
1798void
1799clientReplyContext::sendStreamError(StoreIOBuffer const &result)
1800{
d85b8894 1801 /** call clientWriteComplete so the client socket gets closed
af6a12ee 1802 *
d85b8894 1803 * We call into the stream, because we don't know that there is a
528b2c61 1804 * client socket!
1805 */
26ac0430 1806 debugs(88, 5, "clientReplyContext::sendStreamError: A stream error has occured, marking as complete and sending no data.");
8bcf08e0 1807 StoreIOBuffer localTempBuffer;
528b2c61 1808 flags.complete = 1;
e857372a 1809 http->request->flags.streamError = true;
8bcf08e0 1810 localTempBuffer.flags.error = result.flags.error;
528b2c61 1811 clientStreamCallback((clientStreamNode*)http->client_stream.head->data, http, NULL,
8bcf08e0 1812 localTempBuffer);
528b2c61 1813}
1814
1815void
1816clientReplyContext::pushStreamData(StoreIOBuffer const &result, char *source)
1817{
8bcf08e0 1818 StoreIOBuffer localTempBuffer;
62e76326 1819
528b2c61 1820 if (result.length == 0) {
bf8fe701 1821 debugs(88, 5, "clientReplyContext::pushStreamData: marking request as complete due to 0 length store result");
62e76326 1822 flags.complete = 1;
528b2c61 1823 }
62e76326 1824
43ae1d95 1825 assert(result.offset - headers_sz == next()->readBuffer.offset);
8bcf08e0
FC
1826 localTempBuffer.offset = result.offset - headers_sz;
1827 localTempBuffer.length = result.length;
62e76326 1828
8bcf08e0
FC
1829 if (localTempBuffer.length)
1830 localTempBuffer.data = source;
62e76326 1831
528b2c61 1832 clientStreamCallback((clientStreamNode*)http->client_stream.head->data, http, NULL,
8bcf08e0 1833 localTempBuffer);
528b2c61 1834}
1835
1836clientStreamNode *
1837clientReplyContext::next() const
1838{
1839 assert ( (clientStreamNode*)http->client_stream.head->next->data == getNextNode());
1840 return getNextNode();
1841}
1842
b51aec66 1843void
16611143 1844clientReplyContext::sendBodyTooLargeError()
b51aec66 1845{
b7ac5457 1846 Ip::Address tmp_noaddr;
4dd643d5 1847 tmp_noaddr.setNoAddr(); // TODO: make a global const
af0d9bca 1848 http->logType = LOG_TCP_DENIED_REPLY;
955394ce 1849 ErrorState *err = clientBuildError(ERR_TOO_BIG, Http::scForbidden, NULL,
73c36fd9 1850 http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmp_noaddr,
26ac0430 1851 http->request);
16611143 1852 removeClientStoreReference(&(sc), http);
1853 HTTPMSGUNLOCK(reply);
1854 startError(err);
26ac0430 1855
b51aec66 1856}
1857
79c8035e
AR
1858/// send 412 (Precondition Failed) to client
1859void
1860clientReplyContext::sendPreconditionFailedError()
1861{
1862 http->logType = LOG_TCP_HIT;
1863 ErrorState *const err =
955394ce 1864 clientBuildError(ERR_PRECONDITION_FAILED, Http::scPreconditionFailed,
73c36fd9 1865 NULL, http->getConn()->clientConnection->remote, http->request);
79c8035e
AR
1866 removeClientStoreReference(&sc, http);
1867 HTTPMSGUNLOCK(reply);
1868 startError(err);
1869}
1870
a2bd457d
AJ
1871/// send 304 (Not Modified) to client
1872void
1873clientReplyContext::sendNotModified()
1874{
1875 StoreEntry *e = http->storeEntry();
1876 const time_t timestamp = e->timestamp;
1877 HttpReply *const temprep = e->getReply()->make304();
1878 http->logType = LOG_TCP_IMS_HIT;
1879 removeClientStoreReference(&sc, http);
f206b652 1880 createStoreEntry(http->request->method, RequestFlags());
a2bd457d
AJ
1881 e = http->storeEntry();
1882 // Copy timestamp from the original entry so the 304
1883 // reply has a meaningful Age: header.
90fa5816 1884 e->timestampsSet();
a2bd457d
AJ
1885 e->timestamp = timestamp;
1886 e->replaceHttpReply(temprep);
1887 e->complete();
1888 /*
1889 * TODO: why put this in the store and then serialise it and
1890 * then parse it again. Simply mark the request complete in
1891 * our context and write the reply struct to the client side.
1892 */
1893 triggerInitialStoreRead();
1894}
1895
1896/// send 304 (Not Modified) or 412 (Precondition Failed) to client
1897/// depending on request method
1898void
1899clientReplyContext::sendNotModifiedOrPreconditionFailedError()
1900{
c2a7cefd
AJ
1901 if (http->request->method == Http::METHOD_GET ||
1902 http->request->method == Http::METHOD_HEAD)
a2bd457d
AJ
1903 sendNotModified();
1904 else
1905 sendPreconditionFailedError();
1906}
1907
4993f571 1908void
1909clientReplyContext::processReplyAccess ()
1910{
b50e327b 1911 /* NP: this should probably soft-fail to a zero-sized-reply error ?? */
7dc5f514 1912 assert(reply);
b50e327b
AJ
1913
1914 /** Don't block our own responses or HTTP status messages */
16611143 1915 if (http->logType == LOG_TCP_DENIED ||
26ac0430 1916 http->logType == LOG_TCP_DENIED_REPLY ||
9b769c67 1917 alwaysAllowResponse(reply->sline.status())) {
acbf9428 1918 headers_sz = reply->hdr_sz;
2efeb0b7 1919 processReplyAccessResult(ACCESS_ALLOWED);
26ac0430 1920 return;
230a8cc6 1921 }
26ac0430 1922
b50e327b 1923 /** Check for reply to big error */
16611143 1924 if (reply->expectedBodyTooLarge(*http->request)) {
1925 sendBodyTooLargeError();
62e76326 1926 return;
1927 }
1928
7dc5f514 1929 headers_sz = reply->hdr_sz;
230a8cc6 1930
b50e327b 1931 /** check for absent access controls (permit by default) */
230a8cc6 1932 if (!Config.accessList.reply) {
2efeb0b7 1933 processReplyAccessResult(ACCESS_ALLOWED);
26ac0430 1934 return;
230a8cc6 1935 }
1936
b50e327b 1937 /** Process http_reply_access lists */
127dce76
AR
1938 ACLFilledChecklist *replyChecklist =
1939 clientAclChecklistCreate(Config.accessList.reply, http);
b248c2a3
AJ
1940 replyChecklist->reply = reply;
1941 HTTPMSGLOCK(replyChecklist->reply);
0655fa4d 1942 replyChecklist->nonBlockingCheck(ProcessReplyAccessResult, this);
4993f571 1943}
1944
1945void
2efeb0b7 1946clientReplyContext::ProcessReplyAccessResult(allow_t rv, void *voidMe)
4993f571 1947{
1948 clientReplyContext *me = static_cast<clientReplyContext *>(voidMe);
0655fa4d 1949 me->processReplyAccessResult(rv);
4993f571 1950}
1951
1952void
2efeb0b7 1953clientReplyContext::processReplyAccessResult(const allow_t &accessAllowed)
4993f571 1954{
26ac0430 1955 debugs(88, 2, "The reply for " << RequestMethodStr(http->request->method)
2efeb0b7 1956 << " " << http->uri << " is " << accessAllowed << ", because it matched '"
bf8fe701 1957 << (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" );
62e76326 1958
2efeb0b7 1959 if (accessAllowed != ACCESS_ALLOWED) {
62e76326 1960 ErrorState *err;
0185bd6f 1961 err_type page_id;
9ce7856a 1962 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
0185bd6f 1963
c466369c 1964 http->logType = LOG_TCP_DENIED_REPLY;
1965
0185bd6f 1966 if (page_id == ERR_NONE)
1967 page_id = ERR_ACCESS_DENIED;
1968
b7ac5457 1969 Ip::Address tmp_noaddr;
4dd643d5 1970 tmp_noaddr.setNoAddr();
955394ce 1971 err = clientBuildError(page_id, Http::scForbidden, NULL,
73c36fd9 1972 http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmp_noaddr,
26ac0430 1973 http->request);
0185bd6f 1974
86a2f789 1975 removeClientStoreReference(&sc, http);
0185bd6f 1976
7dc5f514 1977 HTTPMSGUNLOCK(reply);
0185bd6f 1978
3c2c1d31 1979 startError(err);
1980
62e76326 1981 return;
1982 }
1983
0976f8db 1984 /* Ok, the reply is allowed, */
1985 http->loggingEntry(http->storeEntry());
1986
7dc5f514 1987 ssize_t body_size = reqofs - reply->hdr_sz;
b297bcd0 1988 if (body_size < 0) {
26ac0430
AJ
1989 reqofs = reply->hdr_sz;
1990 body_size = 0;
b297bcd0 1991 }
0976f8db 1992
bf8fe701 1993 debugs(88, 3, "clientReplyContext::sendMoreData: Appending " <<
1994 (int) body_size << " bytes after " << reply->hdr_sz <<
1995 " bytes of headers");
0976f8db 1996
f41735ea 1997#if USE_SQUID_ESI
62e76326 1998
9b769c67
AJ
1999 if (http->flags.accel && reply->sline.status() != Http::scForbidden &&
2000 !alwaysAllowResponse(reply->sline.status()) &&
7dc5f514 2001 esiEnableProcessing(reply)) {
bf8fe701 2002 debugs(88, 2, "Enabling ESI processing for " << http->uri);
43ae1d95 2003 clientStreamInsertHead(&http->client_stream, esiStreamRead,
2004 esiProcessStream, esiStreamDetach, esiStreamStatus, NULL);
2005 }
2006
5ef38a13 2007#endif
2008
c2a7cefd 2009 if (http->request->method == Http::METHOD_HEAD) {
62e76326 2010 /* do not forward body for HEAD replies */
2011 body_size = 0;
be4d35dc 2012 http->flags.done_copying = true;
62e76326 2013 flags.complete = 1;
2014 }
2015
2016 assert (!flags.headersSent);
2017 flags.headersSent = true;
2018
8bcf08e0 2019 StoreIOBuffer localTempBuffer;
62e76326 2020 char *buf = next()->readBuffer.data;
e85eb4e1 2021 char *body_buf = buf + reply->hdr_sz;
62e76326 2022
49ea0125 2023 //Server side may disable ranges under some circumstances.
2024
2025 if ((!http->request->range))
2026 next()->readBuffer.offset = 0;
2027
e85eb4e1
AJ
2028 body_buf -= next()->readBuffer.offset;
2029
62e76326 2030 if (next()->readBuffer.offset != 0) {
2031 if (next()->readBuffer.offset > body_size) {
2324cda2 2032 /* Can't use any of the body we received. send nothing */
8bcf08e0
FC
2033 localTempBuffer.length = 0;
2034 localTempBuffer.data = NULL;
62e76326 2035 } else {
8bcf08e0
FC
2036 localTempBuffer.length = body_size - next()->readBuffer.offset;
2037 localTempBuffer.data = body_buf + next()->readBuffer.offset;
62e76326 2038 }
2039 } else {
8bcf08e0
FC
2040 localTempBuffer.length = body_size;
2041 localTempBuffer.data = body_buf;
62e76326 2042 }
2043
0655fa4d 2044 /* TODO??: move the data in the buffer back by the request header size */
62e76326 2045 clientStreamCallback((clientStreamNode *)http->client_stream.head->data,
8bcf08e0 2046 http, reply, localTempBuffer);
62e76326 2047
2048 return;
4993f571 2049}
2050
528b2c61 2051void
2052clientReplyContext::sendMoreData (StoreIOBuffer result)
2053{
50c09fc4 2054 if (deleting)
2055 return;
2056
86a2f789 2057 StoreEntry *entry = http->storeEntry();
50c09fc4 2058
1cf238db 2059 ConnStateData * conn = http->getConn();
50c09fc4 2060
ea25a575
TH
2061 // too late, our conn is closing
2062 // TODO: should we also quit?
2063 if (conn == NULL) {
2064 debugs(33,3, "not sending more data to a closed connection" );
2065 return;
2066 }
2067 if (!conn->isOpen()) {
2068 debugs(33,3, "not sending more data to closing connection " << conn->clientConnection);
9af2f95b
AR
2069 return;
2070 }
693cb033
CT
2071 if (conn->pinning.zeroReply) {
2072 debugs(33,3, "not sending more data after a pinned zero reply " << conn->clientConnection);
9af2f95b
AR
2073 return;
2074 }
50c09fc4 2075
528b2c61 2076 char *buf = next()->readBuffer.data;
50c09fc4 2077
da33c835 2078 if (buf != result.data) {
62e76326 2079 /* we've got to copy some data */
2080 assert(result.length <= next()->readBuffer.length);
41d00cd3 2081 memcpy(buf, result.data, result.length);
edce4d98 2082 }
62e76326 2083
73c36fd9 2084 if (reqofs==0 && !logTypeIsATcpHit(http->logType) && Comm::IsConnOpen(conn->clientConnection)) {
425de4c8 2085 if (Ip::Qos::TheConfig.isHitTosActive()) {
73c36fd9 2086 Ip::Qos::doTosLocalMiss(conn->clientConnection, http->request->hier.code);
425de4c8
AJ
2087 }
2088 if (Ip::Qos::TheConfig.isHitNfmarkActive()) {
73c36fd9 2089 Ip::Qos::doNfmarkLocalMiss(conn->clientConnection, http->request->hier.code);
452bb8e2 2090 }
7172612f 2091 }
7172612f 2092
ab9c533b
HN
2093 /* We've got the final data to start pushing... */
2094 flags.storelogiccomplete = 1;
2095
4993f571 2096 reqofs += result.length;
2097
2098 assert(reqofs <= HTTP_REQBUF_SZ || flags.headersSent);
62e76326 2099
edce4d98 2100 assert(http->request != NULL);
62e76326 2101
edce4d98 2102 /* ESI TODO: remove this assert once everything is stable */
2103 assert(http->client_stream.head->data
62e76326 2104 && cbdataReferenceValid(http->client_stream.head->data));
528b2c61 2105
2106 makeThisHead();
62e76326 2107
bf8fe701 2108 debugs(88, 5, "clientReplyContext::sendMoreData: " << http->uri << ", " <<
47f6e231 2109 reqofs << " bytes (" << result.length <<
bf8fe701 2110 " new bytes)");
47f6e231 2111 debugs(88, 5, "clientReplyContext::sendMoreData:"
73c36fd9 2112 << conn->clientConnection <<
26ac0430
AJ
2113 " '" << entry->url() << "'" <<
2114 " out.offset=" << http->out.offset);
528b2c61 2115
edce4d98 2116 /* update size of the request */
4993f571 2117 reqsize = reqofs;
62e76326 2118
4993f571 2119 if (errorInStream(result, reqofs)) {
62e76326 2120 sendStreamError(result);
2121 return;
edce4d98 2122 }
528b2c61 2123
2124 if (flags.headersSent) {
62e76326 2125 pushStreamData (result, buf);
2126 return;
edce4d98 2127 }
62e76326 2128
b297bcd0 2129 cloneReply();
21b92762 2130
b297bcd0 2131 /* handle headers */
21b92762 2132
b297bcd0 2133 if (Config.onoff.log_mime_hdrs) {
26ac0430 2134 size_t k;
43ae1d95 2135
26ac0430 2136 if ((k = headersEnd(buf, reqofs))) {
41ebd397
CT
2137 safe_free(http->al->headers.reply);
2138 http->al->headers.reply = (char *)xcalloc(k + 1, 1);
2139 xstrncpy(http->al->headers.reply, buf, k);
26ac0430 2140 }
edce4d98 2141 }
b297bcd0
HN
2142
2143 holdingBuffer = result;
2144 processReplyAccess();
2145 return;
edce4d98 2146}
2147
edce4d98 2148/* Using this breaks the client layering just a little!
2149 */
0655fa4d 2150void
f206b652 2151clientReplyContext::createStoreEntry(const HttpRequestMethod& m, RequestFlags reqFlags)
edce4d98 2152{
0655fa4d 2153 assert(http != NULL);
edce4d98 2154 /*
2155 * For erroneous requests, we might not have a h->request,
2156 * so make a fake one.
2157 */
62e76326 2158
b248c2a3
AJ
2159 if (http->request == NULL) {
2160 http->request = new HttpRequest(m, AnyP::PROTO_NONE, null_string);
2161 HTTPMSGLOCK(http->request);
2162 }
62e76326 2163
a8a0b1c2 2164 StoreEntry *e = storeCreateEntry(storeId(), http->log_uri, reqFlags, m);
62e76326 2165
9a9954ba
AR
2166 // Make entry collapsable ASAP, to increase collapsing chances for others.
2167 // TODO: why is !.needValidation required here?
2168 if (Config.onoff.collapsed_forwarding && reqFlags.cachable &&
2169 !reqFlags.needValidation &&
2170 (m == Http::METHOD_GET || m == Http::METHOD_HEAD)) {
2171 // make the entry available for future requests now
2172 Store::Root().allowCollapsing(e, reqFlags, m);
2173 }
2174
0655fa4d 2175 sc = storeClientListAdd(e, this);
62e76326 2176
9a0a18de 2177#if USE_DELAY_POOLS
0655fa4d 2178 sc->setDelayId(DelayId::DelayClient(http));
edce4d98 2179#endif
62e76326 2180
0655fa4d 2181 reqofs = 0;
62e76326 2182
0655fa4d 2183 reqsize = 0;
62e76326 2184
edce4d98 2185 /* I don't think this is actually needed! -- adrian */
0655fa4d 2186 /* http->reqbuf = http->norm_reqbuf; */
2187 // assert(http->reqbuf == http->norm_reqbuf);
edce4d98 2188 /* The next line is illegal because we don't know if the client stream
26ac0430 2189 * buffers have been set up
edce4d98 2190 */
0655fa4d 2191 // storeClientCopy(http->sc, e, 0, HTTP_REQBUF_SZ, http->reqbuf,
2192 // SendMoreData, this);
edce4d98 2193 /* So, we mark the store logic as complete */
8bcf08e0 2194 flags.storelogiccomplete = 1;
62e76326 2195
edce4d98 2196 /* and get the caller to request a read, from whereever they are */
62e76326 2197 /* NOTE: after ANY data flows down the pipe, even one step,
26ac0430 2198 * this function CAN NOT be used to manage errors
edce4d98 2199 */
86a2f789 2200 http->storeEntry(e);
edce4d98 2201}
2202
2203ErrorState *
955394ce 2204clientBuildError(err_type page_id, Http::StatusCode status, char const *url,
b7ac5457 2205 Ip::Address &src_addr, HttpRequest * request)
edce4d98 2206{
913524f0 2207 ErrorState *err = new ErrorState(page_id, status, request);
cc192b50 2208 err->src_addr = src_addr;
62e76326 2209
edce4d98 2210 if (url)
62e76326 2211 err->url = xstrdup(url);
2212
edce4d98 2213 return err;
2214}