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