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