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