]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_reply.cc
HTTP 1.1: Forward OPTIONS requests properly
[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"
2d2b0bb7 49#include "auth/UserRequest.h"
f41735ea 50#if USE_SQUID_ESI
f99c2cfe 51#include "esi/Esi.h"
43ae1d95 52#endif
528b2c61 53#include "MemObject.h"
7172612f 54#include "fde.h"
127dce76
AR
55#include "acl/FilledChecklist.h"
56#include "acl/Gadgets.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,
e57c1885 96 IpAddress &addr, HttpRequest * failedrequest, const 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) {
5b4117d8 1294 const char *value = e->value.rawBuf();
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;
af6a12ee 1360 } else if (!Config.onoff.client_pconns && !request->flags.must_keepalive) {
17b57873 1361 debugs(33, 2, "clientBuildReplyHeader: Connection Keep-Alive not requested by admin or client");
f5e45ad8 1362 request->flags.proxy_keepalive = 0;
af6a12ee 1363 } else if (request->flags.proxy_keepalive && shutting_down) {
f529bb20 1364 debugs(88, 3, "clientBuildReplyHeader: Shutting down, don't keep-alive.");
1365 request->flags.proxy_keepalive = 0;
af6a12ee 1366 } else if (request->flags.connection_auth && !reply->keep_alive) {
26ac0430
AJ
1367 debugs(33, 2, "clientBuildReplyHeader: Connection oriented auth but server side non-persistent");
1368 request->flags.proxy_keepalive = 0;
af6a12ee 1369 } else if (reply->bodySize(request->method) < 0) {
17b57873
AJ
1370 debugs(88, 3, "clientBuildReplyHeader: can't keep-alive, unknown body size" );
1371 request->flags.proxy_keepalive = 0;
af6a12ee 1372 } else if (fdUsageHigh()&& !request->flags.must_keepalive) {
17b57873
AJ
1373 debugs(88, 3, "clientBuildReplyHeader: Not many unused FDs, can't keep-alive");
1374 request->flags.proxy_keepalive = 0;
1375 }
d67acb4e 1376
f529bb20 1377
4dea3fdd 1378 /* Append VIA */
92caea77 1379 if (Config.onoff.via) {
62e76326 1380 LOCAL_ARRAY(char, bbuf, MAX_URL + 32);
30abd221 1381 String strVia;
26ac0430 1382 hdr->getList(HDR_VIA, &strVia);
62e76326 1383 snprintf(bbuf, sizeof(bbuf), "%d.%d %s",
7dc5f514 1384 reply->sline.version.major,
1385 reply->sline.version.minor,
62e76326 1386 ThisCache);
1387 strListAdd(&strVia, bbuf, ',');
a9925b40 1388 hdr->delById(HDR_VIA);
5b4117d8 1389 hdr->putStr(HDR_VIA, strVia.termedBuf());
c8be6d7b 1390 }
edce4d98 1391 /* Signal keep-alive if needed */
a9925b40 1392 hdr->putStr(http->flags.accel ? HDR_CONNECTION : HDR_PROXY_CONNECTION,
1393 request->flags.proxy_keepalive ? "keep-alive" : "close");
62e76326 1394
edce4d98 1395#if ADD_X_REQUEST_URI
1396 /*
1397 * Knowing the URI of the request is useful when debugging persistent
1398 * connections in a client; we cannot guarantee the order of http headers,
1399 * but X-Request-URI is likely to be the very last header to ease use from a
1400 * debugger [hdr->entries.count-1].
1401 */
a9925b40 1402 hdr->putStr(HDR_X_REQUEST_URI,
1403 http->memOjbect()->url ? http->memObject()->url : http->uri);
62e76326 1404
edce4d98 1405#endif
62e76326 1406
8c01ada0 1407 httpHdrMangleList(hdr, request, ROR_REPLY);
edce4d98 1408}
1409
1410
0655fa4d 1411void
b297bcd0 1412clientReplyContext::cloneReply()
edce4d98 1413{
7dc5f514 1414 assert(reply == NULL);
0655fa4d 1415
b297bcd0 1416 HttpReply *rep = http->storeEntry()->getReply()->clone();
7dc5f514 1417
1418 reply = HTTPMSGLOCK(rep);
1419
0655fa4d 1420 /* enforce 1.0 reply version */
7dc5f514 1421 reply->sline.version = HttpVersion(1,0);
0655fa4d 1422
1423 /* do header conversions */
1424 buildReplyHeader();
edce4d98 1425}
1426
e6ccf245 1427void
1428clientReplyContext::identifyStoreObject()
edce4d98 1429{
190154cf 1430 HttpRequest *r = http->request;
62e76326 1431
e6ccf245 1432 if (r->flags.cachable || r->flags.internal) {
62e76326 1433 lookingforstore = 5;
1434 StoreEntry::getPublicByRequest (this, r);
559da936 1435 } else {
62e76326 1436 identifyFoundObject (NullStoreEntry::getInstance());
559da936 1437 }
e6ccf245 1438}
1439
7d5f62a4
AJ
1440/**
1441 * Check state of the current StoreEntry object.
1442 * to see if we can determine the final status of the request.
1443 */
e6ccf245 1444void
1445clientReplyContext::identifyFoundObject(StoreEntry *newEntry)
1446{
1447 StoreEntry *e = newEntry;
190154cf 1448 HttpRequest *r = http->request;
62e76326 1449
d85b8894 1450 /** \li If the entry received isNull() then we ignore it. */
e6ccf245 1451 if (e->isNull()) {
86a2f789 1452 http->storeEntry(NULL);
e6ccf245 1453 } else {
86a2f789 1454 http->storeEntry(e);
e6ccf245 1455 }
62e76326 1456
86a2f789 1457 e = http->storeEntry();
a12a049a 1458
7d5f62a4 1459 /* Release IP-cache entries on reload */
d85b8894 1460 /** \li If the request has no-cache flag set or some no_cache HACK in operation we
7d5f62a4
AJ
1461 * 'invalidate' the cached IP entries for this request ???
1462 */
a12a049a 1463 if (r->flags.nocache) {
1464
1465#if USE_DNSSERVERS
62e76326 1466
cc192b50 1467 ipcacheInvalidate(r->GetHost());
62e76326 1468
a12a049a 1469#else
1470
cc192b50 1471 ipcacheInvalidateNegative(r->GetHost());
a12a049a 1472
1473#endif /* USE_DNSSERVERS */
1474
1475 }
1476
edce4d98 1477#if HTTP_VIOLATIONS
62e76326 1478
a12a049a 1479 else if (r->flags.nocache_hack) {
1480
1481#if USE_DNSSERVERS
1482
cc192b50 1483 ipcacheInvalidate(r->GetHost());
62e76326 1484
a12a049a 1485#else
1486
cc192b50 1487 ipcacheInvalidateNegative(r->GetHost());
a12a049a 1488
1489#endif /* USE_DNSSERVERS */
1490
1491 }
1492
1493#endif /* HTTP_VIOLATIONS */
edce4d98 1494#if USE_CACHE_DIGESTS
62e76326 1495
86a2f789 1496 lookup_type = http->storeEntry() ? "HIT" : "MISS";
62e76326 1497
edce4d98 1498#endif
62e76326 1499
86a2f789 1500 if (NULL == http->storeEntry()) {
d85b8894 1501 /** \li If no StoreEntry object is current assume this object isn't in the cache set MISS*/
7d5f62a4 1502 debugs(85, 3, "clientProcessRequest2: StoreEntry is NULL - MISS");
62e76326 1503 http->logType = LOG_TCP_MISS;
1504 doGetMoreData();
1505 return;
edce4d98 1506 }
62e76326 1507
edce4d98 1508 if (Config.onoff.offline) {
d85b8894 1509 /** \li If we are running in offline mode set to HIT */
bf8fe701 1510 debugs(85, 3, "clientProcessRequest2: offline HIT");
62e76326 1511 http->logType = LOG_TCP_HIT;
1512 doGetMoreData();
1513 return;
edce4d98 1514 }
62e76326 1515
edce4d98 1516 if (http->redirect.status) {
d85b8894 1517 /** \li If redirection status is True force this to be a MISS */
7d5f62a4 1518 debugs(85, 3, "clientProcessRequest2: redirectStatus forced StoreEntry to NULL - MISS");
86a2f789 1519 http->storeEntry(NULL);
62e76326 1520 http->logType = LOG_TCP_MISS;
1521 doGetMoreData();
1522 return;
edce4d98 1523 }
62e76326 1524
3900307b 1525 if (!e->validToSend()) {
bf8fe701 1526 debugs(85, 3, "clientProcessRequest2: !storeEntryValidToSend MISS" );
86a2f789 1527 http->storeEntry(NULL);
62e76326 1528 http->logType = LOG_TCP_MISS;
1529 doGetMoreData();
1530 return;
edce4d98 1531 }
62e76326 1532
edce4d98 1533 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
d85b8894 1534 /* \li Special entries are always hits, no matter what the client says */
bf8fe701 1535 debugs(85, 3, "clientProcessRequest2: ENTRY_SPECIAL HIT");
62e76326 1536 http->logType = LOG_TCP_HIT;
1537 doGetMoreData();
1538 return;
edce4d98 1539 }
62e76326 1540
edce4d98 1541 if (r->flags.nocache) {
bf8fe701 1542 debugs(85, 3, "clientProcessRequest2: no-cache REFRESH MISS");
86a2f789 1543 http->storeEntry(NULL);
62e76326 1544 http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
1545 doGetMoreData();
1546 return;
edce4d98 1547 }
62e76326 1548
bf8fe701 1549 debugs(85, 3, "clientProcessRequest2: default HIT");
e6ccf245 1550 http->logType = LOG_TCP_HIT;
1551 doGetMoreData();
edce4d98 1552}
1553
d85b8894
AJ
1554/**
1555 * Request more data from the store for the client Stream
edce4d98 1556 * This is *the* entry point to this module.
1557 *
1558 * Preconditions:
d85b8894
AJ
1559 * - This is the head of the list.
1560 * - There is at least one more node.
1561 * - Data context is not null
edce4d98 1562 */
1563void
59a1efb2 1564clientGetMoreData(clientStreamNode * aNode, ClientHttpRequest * http)
edce4d98 1565{
edce4d98 1566 /* Test preconditions */
e6ccf245 1567 assert(aNode != NULL);
1568 assert(cbdataReferenceValid(aNode));
e6ccf245 1569 assert(aNode->node.prev == NULL);
1570 assert(aNode->node.next != NULL);
0655fa4d 1571 clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1572 assert (context);
edce4d98 1573 assert(context->http == http);
1574
559da936 1575
0655fa4d 1576 clientStreamNode *next = ( clientStreamNode *)aNode->node.next->data;
62e76326 1577
edce4d98 1578 if (!context->ourNode)
62e76326 1579 context->ourNode = aNode;
1580
e6ccf245 1581 /* no cbdatareference, this is only used once, and safely */
edce4d98 1582 if (context->flags.storelogiccomplete) {
62e76326 1583 StoreIOBuffer tempBuffer;
1584 tempBuffer.offset = next->readBuffer.offset + context->headers_sz;
1585 tempBuffer.length = next->readBuffer.length;
1586 tempBuffer.data = next->readBuffer.data;
1587
86a2f789 1588 storeClientCopy(context->sc, http->storeEntry(),
0655fa4d 1589 tempBuffer, clientReplyContext::SendMoreData, context);
62e76326 1590 return;
edce4d98 1591 }
62e76326 1592
edce4d98 1593 if (context->http->request->method == METHOD_PURGE) {
62e76326 1594 context->purgeRequest();
1595 return;
edce4d98 1596 }
62e76326 1597
fc90edc3
AJ
1598 /* TODO: handle OPTIONS request on max_forwards == 0 as well */
1599
edce4d98 1600 if (context->http->request->method == METHOD_TRACE) {
62e76326 1601 if (context->http->request->max_forwards == 0) {
0655fa4d 1602 context->traceReply(aNode);
62e76326 1603 return;
1604 }
1605
1606 /* continue forwarding, not finished yet. */
1607 http->logType = LOG_TCP_MISS;
1608
1609 context->doGetMoreData();
edce4d98 1610 } else
62e76326 1611 context->identifyStoreObject();
e6ccf245 1612}
1613
1614void
1615clientReplyContext::doGetMoreData()
1616{
edce4d98 1617 /* We still have to do store logic processing - vary, cache hit etc */
86a2f789 1618 if (http->storeEntry() != NULL) {
62e76326 1619 /* someone found the object in the cache for us */
1620 StoreIOBuffer tempBuffer;
34266cde 1621
3d0ac046 1622 http->storeEntry()->lock();
62e76326 1623
86a2f789 1624 if (http->storeEntry()->mem_obj == NULL) {
62e76326 1625 /*
1626 * This if-block exists because we don't want to clobber
1627 * a preexiting mem_obj->method value if the mem_obj
1628 * already exists. For example, when a HEAD request
1629 * is a cache hit for a GET response, we want to keep
1630 * the method as GET.
1631 */
3900307b 1632 http->storeEntry()->createMemObject(http->uri, http->log_uri);
7d5f62a4 1633 http->storeEntry()->mem_obj->method = http->request->method;
62e76326 1634 }
1635
86a2f789 1636 sc = storeClientListAdd(http->storeEntry(), this);
edce4d98 1637#if DELAY_POOLS
62e76326 1638
1639 sc->setDelayId(DelayId::DelayClient(http));
edce4d98 1640#endif
62e76326 1641
1642 assert(http->logType == LOG_TCP_HIT);
1643 reqofs = 0;
1644 /* guarantee nothing has been sent yet! */
1645 assert(http->out.size == 0);
1646 assert(http->out.offset == 0);
b86ab75b 1647#if USE_ZPH_QOS
575cb927
AJ
1648 if (Config.zph.tos_local_hit) {
1649 debugs(33, 2, "ZPH Local hit, TOS=" << Config.zph.tos_local_hit);
1650 comm_set_tos(http->getConn()->fd, Config.zph.tos_local_hit);
b86ab75b
AJ
1651 }
1652#endif /* USE_ZPH_QOS */
62e76326 1653 tempBuffer.offset = reqofs;
1654 tempBuffer.length = getNextNode()->readBuffer.length;
1655 tempBuffer.data = getNextNode()->readBuffer.data;
559da936 1656 storeClientCopy(sc, http->storeEntry(), tempBuffer, CacheHit, this);
edce4d98 1657 } else {
62e76326 1658 /* MISS CASE, http->logType is already set! */
0655fa4d 1659 processMiss();
edce4d98 1660 }
1661}
1662
d85b8894 1663/** The next node has removed itself from the stream. */
edce4d98 1664void
59a1efb2 1665clientReplyDetach(clientStreamNode * node, ClientHttpRequest * http)
edce4d98 1666{
d85b8894 1667 /** detach from the stream */
edce4d98 1668 clientStreamDetach(node, http);
1669}
1670
d85b8894
AJ
1671/**
1672 * Accepts chunk of a http message in buf, parses prefix, filters headers and
edce4d98 1673 * such, writes processed message to the message recipient
1674 */
1675void
0655fa4d 1676clientReplyContext::SendMoreData(void *data, StoreIOBuffer result)
edce4d98 1677{
e6ccf245 1678 clientReplyContext *context = static_cast<clientReplyContext *>(data);
528b2c61 1679 context->sendMoreData (result);
1680}
1681
1682void
1683clientReplyContext::makeThisHead()
1684{
1685 /* At least, I think thats what this does */
1686 dlinkDelete(&http->active, &ClientActiveRequests);
1687 dlinkAdd(http, &http->active, &ClientActiveRequests);
1688}
1689
1690bool
1691clientReplyContext::errorInStream(StoreIOBuffer const &result, size_t const &sizeToProcess)const
1692{
1693 return /* aborted request */
86a2f789 1694 (http->storeEntry() && EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) ||
62e76326 1695 /* Upstream read error */ (result.flags.error) ||
1696 /* Upstream EOF */ (sizeToProcess == 0);
528b2c61 1697}
1698
1699void
1700clientReplyContext::sendStreamError(StoreIOBuffer const &result)
1701{
d85b8894 1702 /** call clientWriteComplete so the client socket gets closed
af6a12ee 1703 *
d85b8894 1704 * We call into the stream, because we don't know that there is a
528b2c61 1705 * client socket!
1706 */
26ac0430 1707 debugs(88, 5, "clientReplyContext::sendStreamError: A stream error has occured, marking as complete and sending no data.");
528b2c61 1708 StoreIOBuffer tempBuffer;
1709 flags.complete = 1;
1710 tempBuffer.flags.error = result.flags.error;
1711 clientStreamCallback((clientStreamNode*)http->client_stream.head->data, http, NULL,
62e76326 1712 tempBuffer);
528b2c61 1713}
1714
1715void
1716clientReplyContext::pushStreamData(StoreIOBuffer const &result, char *source)
1717{
1718 StoreIOBuffer tempBuffer;
62e76326 1719
528b2c61 1720 if (result.length == 0) {
bf8fe701 1721 debugs(88, 5, "clientReplyContext::pushStreamData: marking request as complete due to 0 length store result");
62e76326 1722 flags.complete = 1;
528b2c61 1723 }
62e76326 1724
43ae1d95 1725 assert(result.offset - headers_sz == next()->readBuffer.offset);
528b2c61 1726 tempBuffer.offset = result.offset - headers_sz;
1727 tempBuffer.length = result.length;
62e76326 1728
43ae1d95 1729 if (tempBuffer.length)
1730 tempBuffer.data = source;
62e76326 1731
528b2c61 1732 clientStreamCallback((clientStreamNode*)http->client_stream.head->data, http, NULL,
62e76326 1733 tempBuffer);
528b2c61 1734}
1735
1736clientStreamNode *
1737clientReplyContext::next() const
1738{
1739 assert ( (clientStreamNode*)http->client_stream.head->next->data == getNextNode());
1740 return getNextNode();
1741}
1742
b51aec66 1743void
16611143 1744clientReplyContext::sendBodyTooLargeError()
b51aec66 1745{
ad61a2b4 1746 IpAddress tmp_noaddr;
26ac0430 1747 tmp_noaddr.SetNoAddr(); // TODO: make a global const
16611143 1748 ErrorState *err = clientBuildError(ERR_TOO_BIG, HTTP_FORBIDDEN, NULL,
26ac0430
AJ
1749 http->getConn() != NULL ? http->getConn()->peer : tmp_noaddr,
1750 http->request);
16611143 1751 removeClientStoreReference(&(sc), http);
1752 HTTPMSGUNLOCK(reply);
1753 startError(err);
26ac0430 1754
b51aec66 1755}
1756
4993f571 1757void
1758clientReplyContext::processReplyAccess ()
1759{
b50e327b 1760 /* NP: this should probably soft-fail to a zero-sized-reply error ?? */
7dc5f514 1761 assert(reply);
b50e327b
AJ
1762
1763 /** Don't block our own responses or HTTP status messages */
16611143 1764 if (http->logType == LOG_TCP_DENIED ||
26ac0430
AJ
1765 http->logType == LOG_TCP_DENIED_REPLY ||
1766 alwaysAllowResponse(reply->sline.status)) {
acbf9428 1767 headers_sz = reply->hdr_sz;
26ac0430
AJ
1768 processReplyAccessResult(1);
1769 return;
230a8cc6 1770 }
26ac0430 1771
b50e327b 1772 /** Check for reply to big error */
16611143 1773 if (reply->expectedBodyTooLarge(*http->request)) {
1774 sendBodyTooLargeError();
62e76326 1775 return;
1776 }
1777
7dc5f514 1778 headers_sz = reply->hdr_sz;
230a8cc6 1779
b50e327b 1780 /** check for absent access controls (permit by default) */
230a8cc6 1781 if (!Config.accessList.reply) {
26ac0430
AJ
1782 processReplyAccessResult(1);
1783 return;
230a8cc6 1784 }
1785
b50e327b 1786 /** Process http_reply_access lists */
127dce76
AR
1787 ACLFilledChecklist *replyChecklist =
1788 clientAclChecklistCreate(Config.accessList.reply, http);
7dc5f514 1789 replyChecklist->reply = HTTPMSGLOCK(reply);
0655fa4d 1790 replyChecklist->nonBlockingCheck(ProcessReplyAccessResult, this);
4993f571 1791}
1792
1793void
0655fa4d 1794clientReplyContext::ProcessReplyAccessResult (int rv, void *voidMe)
4993f571 1795{
1796 clientReplyContext *me = static_cast<clientReplyContext *>(voidMe);
0655fa4d 1797 me->processReplyAccessResult(rv);
4993f571 1798}
1799
1800void
0655fa4d 1801clientReplyContext::processReplyAccessResult(bool accessAllowed)
4993f571 1802{
26ac0430
AJ
1803 debugs(88, 2, "The reply for " << RequestMethodStr(http->request->method)
1804 << " " << http->uri << " is "
1805 << ( accessAllowed ? "ALLOWED" : "DENIED")
1806 << ", because it matched '"
bf8fe701 1807 << (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" );
62e76326 1808
230a8cc6 1809 if (!accessAllowed) {
62e76326 1810 ErrorState *err;
0185bd6f 1811 err_type page_id;
9ce7856a 1812 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
0185bd6f 1813
c466369c 1814 http->logType = LOG_TCP_DENIED_REPLY;
1815
0185bd6f 1816 if (page_id == ERR_NONE)
1817 page_id = ERR_ACCESS_DENIED;
1818
ad61a2b4 1819 IpAddress tmp_noaddr;
26ac0430 1820 tmp_noaddr.SetNoAddr();
cc192b50 1821 err = clientBuildError(page_id, HTTP_FORBIDDEN, NULL,
26ac0430
AJ
1822 http->getConn() != NULL ? http->getConn()->peer : tmp_noaddr,
1823 http->request);
0185bd6f 1824
86a2f789 1825 removeClientStoreReference(&sc, http);
0185bd6f 1826
7dc5f514 1827 HTTPMSGUNLOCK(reply);
0185bd6f 1828
3c2c1d31 1829 startError(err);
1830
0185bd6f 1831
62e76326 1832 return;
1833 }
1834
0976f8db 1835 /* Ok, the reply is allowed, */
1836 http->loggingEntry(http->storeEntry());
1837
7dc5f514 1838 ssize_t body_size = reqofs - reply->hdr_sz;
b297bcd0 1839 if (body_size < 0) {
26ac0430
AJ
1840 reqofs = reply->hdr_sz;
1841 body_size = 0;
b297bcd0 1842 }
0976f8db 1843
bf8fe701 1844 debugs(88, 3, "clientReplyContext::sendMoreData: Appending " <<
1845 (int) body_size << " bytes after " << reply->hdr_sz <<
1846 " bytes of headers");
0976f8db 1847
f41735ea 1848#if USE_SQUID_ESI
62e76326 1849
7dc5f514 1850 if (http->flags.accel && reply->sline.status != HTTP_FORBIDDEN &&
1851 !alwaysAllowResponse(reply->sline.status) &&
1852 esiEnableProcessing(reply)) {
bf8fe701 1853 debugs(88, 2, "Enabling ESI processing for " << http->uri);
43ae1d95 1854 clientStreamInsertHead(&http->client_stream, esiStreamRead,
1855 esiProcessStream, esiStreamDetach, esiStreamStatus, NULL);
1856 }
1857
5ef38a13 1858#endif
1859
62e76326 1860 if (http->request->method == METHOD_HEAD) {
1861 /* do not forward body for HEAD replies */
1862 body_size = 0;
1863 http->flags.done_copying = 1;
1864 flags.complete = 1;
1865 }
1866
1867 assert (!flags.headersSent);
1868 flags.headersSent = true;
1869
1870 StoreIOBuffer tempBuffer;
1871 char *buf = next()->readBuffer.data;
e85eb4e1 1872 char *body_buf = buf + reply->hdr_sz;
62e76326 1873
49ea0125 1874 //Server side may disable ranges under some circumstances.
1875
1876 if ((!http->request->range))
1877 next()->readBuffer.offset = 0;
1878
e85eb4e1
AJ
1879 body_buf -= next()->readBuffer.offset;
1880
62e76326 1881 if (next()->readBuffer.offset != 0) {
1882 if (next()->readBuffer.offset > body_size) {
2324cda2 1883 /* Can't use any of the body we received. send nothing */
62e76326 1884 tempBuffer.length = 0;
1885 tempBuffer.data = NULL;
1886 } else {
1887 tempBuffer.length = body_size - next()->readBuffer.offset;
1888 tempBuffer.data = body_buf + next()->readBuffer.offset;
1889 }
1890 } else {
1891 tempBuffer.length = body_size;
1892 tempBuffer.data = body_buf;
1893 }
1894
0655fa4d 1895 /* TODO??: move the data in the buffer back by the request header size */
62e76326 1896 clientStreamCallback((clientStreamNode *)http->client_stream.head->data,
7dc5f514 1897 http, reply, tempBuffer);
62e76326 1898
1899 return;
4993f571 1900}
1901
528b2c61 1902void
1903clientReplyContext::sendMoreData (StoreIOBuffer result)
1904{
50c09fc4 1905 if (deleting)
1906 return;
1907
86a2f789 1908 StoreEntry *entry = http->storeEntry();
50c09fc4 1909
1cf238db 1910 ConnStateData * conn = http->getConn();
50c09fc4 1911
94a396a3 1912 int fd = conn != NULL ? conn->fd : -1;
9af2f95b
AR
1913 if (fd >= 0 && fd_table[fd].closing()) { // too late, our conn is closing
1914 // TODO: should we also quit when fd is negative?
1915 debugs(33,3, HERE << "not sending more data to a closing FD " << fd);
1916 return;
1917 }
50c09fc4 1918
528b2c61 1919 char *buf = next()->readBuffer.data;
50c09fc4 1920
c8be6d7b 1921 char *body_buf = buf;
edce4d98 1922
da33c835 1923 if (buf != result.data) {
62e76326 1924 /* we've got to copy some data */
1925 assert(result.length <= next()->readBuffer.length);
1926 xmemcpy(buf, result.data, result.length);
1927 body_buf = buf;
edce4d98 1928 }
62e76326 1929
452bb8e2 1930#if USE_ZPH_QOS
26ac0430 1931 if (reqofs==0 && !logTypeIsATcpHit(http->logType)) {
9af2f95b 1932 assert(fd >= 0); // the beginning of this method implies fd may be -1
452bb8e2 1933 int tos = 0;
575cb927
AJ
1934 if (Config.zph.tos_sibling_hit && http->request->hier.code==SIBLING_HIT ) {
1935 tos = Config.zph.tos_sibling_hit;
1936 debugs(33, 2, "ZPH: Sibling Peer hit with hier.code=" << http->request->hier.code << ", TOS=" << tos);
1937 } else if (Config.zph.tos_parent_hit && http->request->hier.code==PARENT_HIT) {
1938 tos = Config.zph.tos_parent_hit;
1939 debugs(33, 2, "ZPH: Parent Peer hit with hier.code=" << http->request->hier.code << ", TOS=" << tos);
1940 } else if (Config.zph.preserve_miss_tos && Config.zph.preserve_miss_tos_mask) {
1941 tos = fd_table[fd].upstreamTOS & Config.zph.preserve_miss_tos_mask;
452bb8e2
AJ
1942 debugs(33, 2, "ZPH: Preserving TOS on miss, TOS="<<tos);
1943 }
1944 comm_set_tos(fd,tos);
7172612f 1945 }
26ac0430 1946#endif
7172612f 1947
ab9c533b
HN
1948 /* We've got the final data to start pushing... */
1949 flags.storelogiccomplete = 1;
1950
4993f571 1951 reqofs += result.length;
1952
1953 assert(reqofs <= HTTP_REQBUF_SZ || flags.headersSent);
62e76326 1954
edce4d98 1955 assert(http->request != NULL);
62e76326 1956
edce4d98 1957 /* ESI TODO: remove this assert once everything is stable */
1958 assert(http->client_stream.head->data
62e76326 1959 && cbdataReferenceValid(http->client_stream.head->data));
528b2c61 1960
1961 makeThisHead();
62e76326 1962
bf8fe701 1963 debugs(88, 5, "clientReplyContext::sendMoreData: " << http->uri << ", " <<
47f6e231 1964 reqofs << " bytes (" << result.length <<
bf8fe701 1965 " new bytes)");
47f6e231 1966 debugs(88, 5, "clientReplyContext::sendMoreData:"
26ac0430
AJ
1967 " FD " << fd <<
1968 " '" << entry->url() << "'" <<
1969 " out.offset=" << http->out.offset);
528b2c61 1970
edce4d98 1971 /* update size of the request */
4993f571 1972 reqsize = reqofs;
62e76326 1973
4993f571 1974 if (errorInStream(result, reqofs)) {
62e76326 1975 sendStreamError(result);
1976 return;
edce4d98 1977 }
528b2c61 1978
1979 if (flags.headersSent) {
62e76326 1980 pushStreamData (result, buf);
1981 return;
edce4d98 1982 }
62e76326 1983
b297bcd0 1984 cloneReply();
21b92762 1985
b297bcd0 1986 /* handle headers */
21b92762 1987
b297bcd0 1988 if (Config.onoff.log_mime_hdrs) {
26ac0430 1989 size_t k;
43ae1d95 1990
26ac0430
AJ
1991 if ((k = headersEnd(buf, reqofs))) {
1992 safe_free(http->al.headers.reply);
1993 http->al.headers.reply = (char *)xcalloc(k + 1, 1);
1994 xstrncpy(http->al.headers.reply, buf, k);
1995 }
edce4d98 1996 }
b297bcd0
HN
1997
1998 holdingBuffer = result;
1999 processReplyAccess();
2000 return;
edce4d98 2001}
2002
62e76326 2003
edce4d98 2004
2005/* Using this breaks the client layering just a little!
2006 */
0655fa4d 2007void
60745f24 2008clientReplyContext::createStoreEntry(const HttpRequestMethod& m, request_flags flags)
edce4d98 2009{
0655fa4d 2010 assert(http != NULL);
edce4d98 2011 /*
2012 * For erroneous requests, we might not have a h->request,
2013 * so make a fake one.
2014 */
62e76326 2015
0655fa4d 2016 if (http->request == NULL)
6dd9f4bd 2017 http->request = HTTPMSGLOCK(new HttpRequest(m, PROTO_NONE, null_string));
62e76326 2018
0655fa4d 2019 StoreEntry *e = storeCreateEntry(http->uri, http->log_uri, flags, m);
62e76326 2020
0655fa4d 2021 sc = storeClientListAdd(e, this);
62e76326 2022
edce4d98 2023#if DELAY_POOLS
62e76326 2024
0655fa4d 2025 sc->setDelayId(DelayId::DelayClient(http));
62e76326 2026
edce4d98 2027#endif
62e76326 2028
0655fa4d 2029 reqofs = 0;
62e76326 2030
0655fa4d 2031 reqsize = 0;
62e76326 2032
edce4d98 2033 /* I don't think this is actually needed! -- adrian */
0655fa4d 2034 /* http->reqbuf = http->norm_reqbuf; */
2035 // assert(http->reqbuf == http->norm_reqbuf);
edce4d98 2036 /* The next line is illegal because we don't know if the client stream
26ac0430 2037 * buffers have been set up
edce4d98 2038 */
0655fa4d 2039 // storeClientCopy(http->sc, e, 0, HTTP_REQBUF_SZ, http->reqbuf,
2040 // SendMoreData, this);
edce4d98 2041 /* So, we mark the store logic as complete */
0655fa4d 2042 this->flags.storelogiccomplete = 1;
62e76326 2043
edce4d98 2044 /* and get the caller to request a read, from whereever they are */
62e76326 2045 /* NOTE: after ANY data flows down the pipe, even one step,
26ac0430 2046 * this function CAN NOT be used to manage errors
edce4d98 2047 */
86a2f789 2048 http->storeEntry(e);
edce4d98 2049}
2050
2051ErrorState *
2052clientBuildError(err_type page_id, http_status status, char const *url,
ad61a2b4 2053 IpAddress &src_addr, HttpRequest * request)
edce4d98 2054{
2cc81f1f 2055 ErrorState *err = errorCon(page_id, status, request);
cc192b50 2056 err->src_addr = src_addr;
62e76326 2057
edce4d98 2058 if (url)
62e76326 2059 err->url = xstrdup(url);
2060
edce4d98 2061 return err;
2062}