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