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