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