]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_reply.cc
I am changing debugging levels for these frequently-printed messages.
[thirdparty/squid.git] / src / client_side_reply.cc
CommitLineData
edce4d98 1
2/*
db237875 3 * $Id: client_side_reply.cc,v 1.96 2006/02/17 20:15:35 wessels Exp $
edce4d98 4 *
5 * DEBUG: section 88 Client-side Reply Routines
6 * AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c)
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36#include "squid.h"
0655fa4d 37#include "client_side_reply.h"
c8be6d7b 38#include "StoreClient.h"
e6ccf245 39#include "Store.h"
528b2c61 40#include "HttpReply.h"
41#include "HttpRequest.h"
b6b6f466 42#include "forward.h"
e6ccf245 43
c8be6d7b 44#include "clientStream.h"
f5691f9c 45#include "AuthUserRequest.h"
43ae1d95 46#if ESI
47#include "ESI.h"
48#endif
5ef38a13 49#if ICAP_CLIENT_RESPMOD_POSTCACHE
50#include "ICAPClient.h"
51#endif
528b2c61 52#include "MemObject.h"
4fb35c3c 53#include "ACLChecklist.h"
b0dd28ba 54#include "ACL.h"
b67e2c8c 55#if DELAY_POOLS
56#include "DelayPools.h"
57#endif
a46d2c0e 58#include "client_side.h"
e6ccf245 59
0655fa4d 60CBDATA_CLASS_INIT(clientReplyContext);
edce4d98 61
edce4d98 62/* Local functions */
e6ccf245 63extern "C" CSS clientReplyStatus;
edce4d98 64extern ErrorState *clientBuildError(err_type, http_status, char const *,
62e76326 65
ddfcbc22 66 struct IN_ADDR *, HttpRequest *);
edce4d98 67
edce4d98 68/* privates */
edce4d98 69
0655fa4d 70clientReplyContext::~clientReplyContext()
edce4d98 71{
50c09fc4 72 deleting = true;
73 /* This may trigger a callback back into SendMoreData as the cbdata
74 * is still valid
75 */
86a2f789 76 removeClientStoreReference(&sc, http);
edce4d98 77 /* old_entry might still be set if we didn't yet get the reply
0655fa4d 78 * code in HandleIMSReply() */
79 removeStoreReference(&old_sc, &old_entry);
80 safe_free(tempBuffer.data);
81 cbdataReferenceDone(http);
edce4d98 82}
83
59a1efb2 84clientReplyContext::clientReplyContext(ClientHttpRequest *clientContext) : http (cbdataReference(clientContext)), old_entry (NULL), old_sc(NULL), deleting(false)
0655fa4d 85{}
edce4d98 86
87/* create an error in the store awaiting the client side to read it. */
0655fa4d 88/* This may be better placed in the clientStream logic, but it has not been
89 * relocated there yet
90 */
edce4d98 91void
0655fa4d 92clientReplyContext::setReplyToError(
93 err_type err, http_status status, method_t method, char const *uri,
62e76326 94
ddfcbc22 95 struct IN_ADDR *addr, HttpRequest * failedrequest, char *unparsedrequest,
0655fa4d 96 auth_user_request_t * auth_user_request)
edce4d98 97{
edce4d98 98 ErrorState *errstate =
62e76326 99 clientBuildError(err, status, uri, addr, failedrequest);
100
edce4d98 101 if (unparsedrequest)
62e76326 102 errstate->request_hdrs = xstrdup(unparsedrequest);
edce4d98 103
0655fa4d 104 if (status == HTTP_NOT_IMPLEMENTED && http->request)
62e76326 105 /* prevent confusion over whether we default to persistent or not */
0655fa4d 106 http->request->flags.proxy_keepalive = 0;
62e76326 107
0655fa4d 108 http->al.http.code = errstate->httpStatus;
edce4d98 109
0655fa4d 110 createStoreEntry(method, request_flags());
62e76326 111
112 if (auth_user_request)
113 {
114 errstate->auth_user_request = auth_user_request;
f5691f9c 115
116 errstate->auth_user_request->lock()
117
118 ;
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;
137 storeUnlockObject(e);
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);
61fb2d9a 169 debug(88, 3)("clientReplyContext::saveState: saving store context\n");
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);
61fb2d9a 185 debug(88, 3)("clientReplyContext::restoreState: Restoring store context\n");
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;
0655fa4d 234 debug(88, 3)("clientReplyContext::processExpired: '%s'", 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);
edce4d98 257 /* NOTE, don't call storeLockObject(), 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;
265 debug(88, 5)("clientReplyContext::processExpired : lastmod %ld",
62e76326 266 (long int) entry->lastmod);
86a2f789 267 http->storeEntry(entry);
c8be6d7b 268 assert(http->out.offset == 0);
b6b6f466 269 FwdState::fwdStart(http->getConn().getRaw() != NULL ? http->getConn()->fd : -1,
270 http->storeEntry(),
271 http->request);
edce4d98 272 /* Register with storage manager to receive updates when data comes in. */
62e76326 273
edce4d98 274 if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
0655fa4d 275 debug(88, 0) ("clientReplyContext::processExpired: Found ENTRY_ABORTED object");
62e76326 276
c8be6d7b 277 {
62e76326 278 /* start counting the length from 0 */
0655fa4d 279 StoreIOBuffer tempBuffer(HTTP_REQBUF_SZ, 0, tempbuf);
280 storeClientCopy(sc, entry, tempBuffer, HandleIMSReply, this);
edce4d98 281 }
282}
283
0655fa4d 284bool
285clientReplyContext::clientGetsOldEntry()const
edce4d98 286{
86a2f789 287 const http_status status = http->storeEntry()->getReply()->sline.status;
62e76326 288
edce4d98 289 if (0 == status) {
62e76326 290 debug(88, 5) ("clientGetsOldEntry: YES, broken HTTP reply\n");
0655fa4d 291 return true;
edce4d98 292 }
62e76326 293
edce4d98 294 /* If the reply is a failure then send the old object as a last
295 * resort */
296 if (status >= 500 && status < 600) {
62e76326 297 debug(88, 3) ("clientGetsOldEntry: YES, failure reply=%d\n", status);
0655fa4d 298 return true;
edce4d98 299 }
62e76326 300
edce4d98 301 /* If the reply is anything but "Not Modified" then
302 * we must forward it to the client */
303 if (HTTP_NOT_MODIFIED != status) {
62e76326 304 debug(88, 5) ("clientGetsOldEntry: NO, reply=%d\n", status);
0655fa4d 305 return false;
edce4d98 306 }
62e76326 307
1ef68325 308 /* If the client did not send IMS in the request, then it
309 * must get the old object, not this "Not Modified" reply
310 * REGARDLESS of validation */
0655fa4d 311 if (!http->request->flags.ims) {
62e76326 312 debug(88, 5) ("clientGetsOldEntry: YES, no client IMS\n");
0655fa4d 313 return true;
1ef68325 314 }
62e76326 315
528b2c61 316 /* If key metadata in the reply are not consistent with the
317 * old entry, we must use the new reply.
318 * Note: this means that the server is sending garbage replies
319 * in that it has sent an IMS that is incompatible with our request!?
320 */
321 /* This is a duplicate call through the HandleIMS code path.
322 * Can we guarantee we don't need it elsewhere?
323 */
06a5ae20 324 if (!http->storeEntry()->getReply()->validatorsMatch(old_entry->getReply())) {
62e76326 325 debug(88, 5) ("clientGetsOldEntry: NO, Old object has been invalidated"
326 "by the new one\n");
0655fa4d 327 return false;
528b2c61 328 }
62e76326 329
edce4d98 330 /* If the client IMS time is prior to the entry LASTMOD time we
331 * need to send the old object */
0655fa4d 332 if (old_entry->modifiedSince(http->request)) {
62e76326 333 debug(88, 5) ("clientGetsOldEntry: YES, modified since %ld\n",
0655fa4d 334 (long int) http->request->ims);
335 return true;
edce4d98 336 }
62e76326 337
edce4d98 338 debug(88, 5) ("clientGetsOldEntry: NO, new one is fine\n");
0655fa4d 339 return false;
edce4d98 340}
341
528b2c61 342void
343clientReplyContext::sendClientUpstreamResponse()
344{
345 StoreIOBuffer tempresult;
346 http->logType = LOG_TCP_REFRESH_MISS;
0655fa4d 347 removeStoreReference(&old_sc, &old_entry);
528b2c61 348 /* here the data to send is the data we just recieved */
349 tempBuffer.offset = 0;
350 old_reqsize = 0;
0655fa4d 351 /* sendMoreData tracks the offset as well.
528b2c61 352 * Force it back to zero */
353 reqofs = 0;
86a2f789 354 assert(!EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED));
0655fa4d 355 /* TODO: provide sendMoreData with the ready parsed reply */
528b2c61 356 tempresult.length = reqsize;
357 tempresult.data = tempbuf;
0655fa4d 358 sendMoreData(tempresult);
528b2c61 359}
360
edce4d98 361void
0655fa4d 362clientReplyContext::HandleIMSReply(void *data, StoreIOBuffer result)
edce4d98 363{
e6ccf245 364 clientReplyContext *context = (clientReplyContext *)data;
0655fa4d 365 context->handleIMSReply(result);
366}
62e76326 367
0655fa4d 368void
369clientReplyContext::sendClientOldEntry()
370{
371 /* Get the old request back */
372 restoreState();
373 /* here the data to send is in the next nodes buffers already */
86a2f789 374 assert(!EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED));
0655fa4d 375 /* sendMoreData tracks the offset as well.
376 * Force it back to zero */
377 reqofs = 0;
378 StoreIOBuffer tempresult (reqsize, reqofs, next()->readBuffer.data);
379 sendMoreData(tempresult);
380}
62e76326 381
0655fa4d 382void
383clientReplyContext::cleanUpAfterIMSCheck()
384{
86a2f789 385 debug(88, 3) ("clientHandleIMSReply: ABORTED '%s'\n", storeUrl(http->storeEntry()));
0655fa4d 386 /* We have an existing entry, but failed to validate it */
387 /* Its okay to send the old one anyway */
388 http->logType = LOG_TCP_REFRESH_FAIL_HIT;
389 sendClientOldEntry();
390}
62e76326 391
0655fa4d 392void
393clientReplyContext::handlePartialIMSHeaders()
394{
395 /* more headers needed to decide */
396 debug(88, 3) ("clientHandleIMSReply: Incomplete headers for '%s'\n",
86a2f789 397 storeUrl(http->storeEntry()));
0655fa4d 398
399 if (reqsize >= HTTP_REQBUF_SZ) {
400 /* will not get any bigger than that */
401 debug(88, 3)
402 ("clientHandleIMSReply: Reply is too large '%s', using old entry\n",
86a2f789 403 storeUrl(http->storeEntry()));
0655fa4d 404 /* use old entry, this repeats the code above */
62e76326 405 http->logType = LOG_TCP_REFRESH_FAIL_HIT;
0655fa4d 406 sendClientOldEntry();
407 } else {
408 reqofs = reqsize;
409 waitForMoreData();
c8be6d7b 410 }
0655fa4d 411}
62e76326 412
0655fa4d 413void
414clientReplyContext::handleIMSGiveClientUpdatedOldEntry()
415{
416 /* We initiated the IMS request and the IMS is compatible with
417 * our object. As the client is not expecting
418 * 304, so put the good one back. First, make sure the old entry
419 * headers have been loaded from disk. */
420 http->logType = LOG_TCP_REFRESH_HIT;
421
06a5ae20 422 if (http->storeEntry()->getReply()->validatorsMatch(old_entry->getReply())) {
0655fa4d 423 int unlink_request = 0;
424
425 if (old_entry->mem_obj->request == NULL) {
6dd9f4bd 426 old_entry->mem_obj->request = HTTPMSGLOCK(http->memObject()->request);
0655fa4d 427 unlink_request = 1;
62e76326 428 }
429
0655fa4d 430 /* Don't memcpy() the whole reply structure here. For example,
431 * www.thegist.com (Netscape/1.13) returns a content-length for
432 * 304's which seems to be the length of the 304 HEADERS!!! and
433 * not the body they refer to. */
07947ad8 434 HttpReply *old_rep = (HttpReply *) old_entry->getReply();
435
06a5ae20 436 old_rep->updateOnNotModified(http->storeEntry()->getReply());
62e76326 437
0655fa4d 438 storeTimestampsSet(old_entry);
62e76326 439
0655fa4d 440 old_entry->timestamp = squid_curtime;
62e76326 441
6dd9f4bd 442 HTTPMSGUNLOCK(old_entry->mem_obj->request);
0655fa4d 443 }
62e76326 444
0655fa4d 445 sendClientOldEntry();
446}
447
448void
449clientReplyContext::handleIMSGiveClientNewEntry()
450{
451 /* The client gets the new entry,
452 * either as a 304 (they initiated the IMS) or
453 * as a full request from the upstream
454 * The new entry is *not* a 304 reply, or
455 * is a 304 that is incompatible with our cached entities.
456 */
62e76326 457
0655fa4d 458 if (http->request->flags.ims) {
459 /* The client asked for a IMS, and can deal
460 * with any reply
461 * XXX TODO: invalidate our object if it's not valid any more.
462 * Send the IMS reply to the client.
463 */
464 sendClientUpstreamResponse();
06a5ae20 465 } else if (http->storeEntry()->getReply()->validatorsMatch(old_entry->getReply())) {
0655fa4d 466 /* Our object is usable once updated */
467 /* the client did not ask for IMS, send the whole object
468 */
469 /* the client needs to get this reply */
470 StoreIOBuffer tempresult;
471 http->logType = LOG_TCP_REFRESH_MISS;
62e76326 472
86a2f789 473 if (HTTP_NOT_MODIFIED == http->storeEntry()->getReply()->sline.status) {
07947ad8 474 HttpReply *old_rep = (HttpReply *) old_entry->getReply();
06a5ae20 475 old_rep->updateOnNotModified(http->storeEntry()->getReply());
0655fa4d 476 storeTimestampsSet(old_entry);
477 http->logType = LOG_TCP_REFRESH_HIT;
478 }
62e76326 479
0655fa4d 480 removeStoreReference(&old_sc, &old_entry);
481 /* here the data to send is the data we just recieved */
482 tempBuffer.offset = 0;
483 old_reqsize = 0;
484 /* clientSendMoreData tracks the offset as well.
485 * Force it back to zero */
486 reqofs = 0;
86a2f789 487 assert(!EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED));
0655fa4d 488 /* TODO: provide SendMoreData with the ready parsed reply */
489 tempresult.length = reqsize;
490 tempresult.data = tempbuf;
491 sendMoreData(tempresult);
492 } else {
493 /* the client asked for the whole object, and
494 * 1) our object was stale
495 * 2) our internally generated IMS failed to validate
496 * 3) the server sent incompatible headers in it's reply
497 */
498 http->logType = LOG_TCP_REFRESH_MISS;
499 processMiss();
500 /* We start over for everything except IMS because:
501 * 1) HEAD requests will go straight through now
502 * 2) GET requests will go straight through now
503 * 3) IMS requests are a corner case. If the server
504 * decided to give us different data, we should give
505 * that to the client, which means returning our IMS request.
506 */
507 }
508}
62e76326 509
0655fa4d 510void
511clientReplyContext::handleIMSReply(StoreIOBuffer result)
512{
045a76ab 513 if (deleting)
514 return;
515
0655fa4d 516 debug(88, 3) ("clientHandleIMSReply: %s, %lu bytes\n",
86a2f789 517 storeUrl(http->storeEntry()),
0655fa4d 518 (long unsigned) result.length);
62e76326 519
86a2f789 520 if (http->storeEntry() == NULL)
0655fa4d 521 return;
62e76326 522
86a2f789 523 if (result.flags.error && !EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
62e76326 524 return;
62e76326 525
0655fa4d 526 /* update size of the request */
527 reqsize = result.length + reqofs;
62e76326 528
86a2f789 529 http_status status = http->storeEntry()->getReply()->sline.status;
62e76326 530
86a2f789 531 if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
0655fa4d 532 cleanUpAfterIMSCheck();
86a2f789 533 else if (STORE_PENDING == http->storeEntry()->store_status && 0 == status)
0655fa4d 534 handlePartialIMSHeaders();
535 else if (clientGetsOldEntry())
536 handleIMSGiveClientUpdatedOldEntry();
537 else
538 handleIMSGiveClientNewEntry();
edce4d98 539}
540
e6ccf245 541extern "C" CSR clientGetMoreData;
542extern "C" CSD clientReplyDetach;
edce4d98 543
544/*
545 * clientCacheHit should only be called until the HTTP reply headers
546 * have been parsed. Normally this should be a single call, but
547 * it might take more than one. As soon as we have the headers,
0655fa4d 548 * we hand off to clientSendMoreData, processExpired, or
549 * processMiss.
edce4d98 550 */
551void
0655fa4d 552clientReplyContext::CacheHit(void *data, StoreIOBuffer result)
edce4d98 553{
e6ccf245 554 clientReplyContext *context = (clientReplyContext *)data;
0655fa4d 555 context->cacheHit (result);
556}
557
558void
559clientReplyContext::cacheHit(StoreIOBuffer result)
560{
045a76ab 561 if (deleting)
562 return;
563
86a2f789 564 StoreEntry *e = http->storeEntry();
045a76ab 565
190154cf 566 HttpRequest *r = http->request;
045a76ab 567
c4b7a5a9 568 debug(88, 3) ("clientCacheHit: %s, %ud bytes\n", http->uri, (unsigned int)result.length);
62e76326 569
86a2f789 570 if (http->storeEntry() == NULL) {
62e76326 571 debug(88, 3) ("clientCacheHit: request aborted\n");
572 return;
c8be6d7b 573 } else if (result.flags.error) {
62e76326 574 /* swap in failure */
575 debug(88, 3) ("clientCacheHit: swapin failure for %s\n", http->uri);
576 http->logType = LOG_TCP_SWAPFAIL_MISS;
86a2f789 577 removeClientStoreReference(&sc, http);
0655fa4d 578 processMiss();
62e76326 579 return;
edce4d98 580 }
62e76326 581
d3b3ab85 582 if (result.length == 0) {
62e76326 583 /* the store couldn't get enough data from the file for us to id the
584 * object
585 */
586 /* treat as a miss */
587 http->logType = LOG_TCP_MISS;
0655fa4d 588 processMiss();
62e76326 589 return;
d3b3ab85 590 }
62e76326 591
edce4d98 592 assert(!EBIT_TEST(e->flags, ENTRY_ABORTED));
593 /* update size of the request */
0655fa4d 594 reqsize = result.length + reqofs;
62e76326 595
528b2c61 596 if (e->getReply()->sline.status == 0) {
62e76326 597 /*
598 * we don't have full reply headers yet; either wait for more or
599 * punt to clientProcessMiss.
600 */
601
602 if (e->mem_status == IN_MEMORY || e->store_status == STORE_OK) {
0655fa4d 603 processMiss();
604 } else if (result.length + reqofs >= HTTP_REQBUF_SZ
62e76326 605 && http->out.offset == 0) {
0655fa4d 606 processMiss();
62e76326 607 } else {
62e76326 608 debug(88, 3) ("clientCacheHit: waiting for HTTP reply headers\n");
0655fa4d 609 reqofs += result.length;
610 assert(reqofs <= HTTP_REQBUF_SZ);
62e76326 611 /* get the next users' buffer */
0655fa4d 612 StoreIOBuffer tempBuffer;
613 tempBuffer.offset = http->out.offset + reqofs;
614 tempBuffer.length = next()->readBuffer.length - reqofs;
615 tempBuffer.data = next()->readBuffer.data + reqofs;
616 storeClientCopy(sc, e,
617 tempBuffer, CacheHit, this);
62e76326 618 }
619
620 return;
edce4d98 621 }
62e76326 622
edce4d98 623 /*
624 * Got the headers, now grok them
625 */
29b8d8d6 626 assert(http->logType == LOG_TCP_HIT);
62e76326 627
edce4d98 628 switch (varyEvaluateMatch(e, r)) {
62e76326 629
edce4d98 630 case VARY_NONE:
62e76326 631 /* No variance detected. Continue as normal */
632 break;
633
edce4d98 634 case VARY_MATCH:
62e76326 635 /* This is the correct entity for this request. Continue */
636 debug(88, 2) ("clientProcessHit: Vary MATCH!\n");
637 break;
638
edce4d98 639 case VARY_OTHER:
62e76326 640 /* This is not the correct entity for this request. We need
641 * to requery the cache.
642 */
86a2f789 643 removeClientStoreReference(&sc, http);
62e76326 644 e = NULL;
645 /* Note: varyEvalyateMatch updates the request with vary information
646 * so we only get here once. (it also takes care of cancelling loops)
647 */
648 debug(88, 2) ("clientProcessHit: Vary detected!\n");
0655fa4d 649 clientGetMoreData(ourNode, http);
62e76326 650 return;
651
edce4d98 652 case VARY_CANCEL:
62e76326 653 /* varyEvaluateMatch found a object loop. Process as miss */
654 debug(88, 1) ("clientProcessHit: Vary object loop!\n");
0655fa4d 655 processMiss();
62e76326 656 return;
edce4d98 657 }
62e76326 658
edce4d98 659 if (r->method == METHOD_PURGE) {
86a2f789 660 removeClientStoreReference(&sc, http);
62e76326 661 e = NULL;
0655fa4d 662 purgeRequest();
62e76326 663 return;
edce4d98 664 }
62e76326 665
36e6655b 666 if (storeCheckNegativeHit(e)
667#if HTTP_VIOLATIONS
668 && !r->flags.nocache_hack
669#endif
670 ) {
62e76326 671 http->logType = LOG_TCP_NEGATIVE_HIT;
0655fa4d 672 sendMoreData(result);
f7cf9b8c 673 } else if (!Config.onoff.offline && refreshCheckHTTP(e, r) && !http->flags.internal) {
62e76326 674 debug(88, 5) ("clientCacheHit: in refreshCheck() block\n");
675 /*
676 * We hold a stale copy; it needs to be validated
677 */
678 /*
679 * The 'need_validation' flag is used to prevent forwarding
680 * loops between siblings. If our copy of the object is stale,
681 * then we should probably only use parents for the validation
682 * request. Otherwise two siblings could generate a loop if
683 * both have a stale version of the object.
684 */
685 r->flags.need_validation = 1;
686
687 if (e->lastmod < 0) {
688 /*
689 * Previous reply didn't have a Last-Modified header,
690 * we cannot revalidate it.
691 */
692 http->logType = LOG_TCP_MISS;
0655fa4d 693 processMiss();
62e76326 694 } else if (r->flags.nocache) {
695 /*
696 * This did not match a refresh pattern that overrides no-cache
697 * we should honour the client no-cache header.
698 */
699 http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
0655fa4d 700 processMiss();
62e76326 701 } else if (r->protocol == PROTO_HTTP) {
702 /*
703 * Object needs to be revalidated
704 * XXX This could apply to FTP as well, if Last-Modified is known.
705 */
706 http->logType = LOG_TCP_REFRESH_MISS;
0655fa4d 707 processExpired();
62e76326 708 } else {
709 /*
710 * We don't know how to re-validate other protocols. Handle
711 * them as if the object has expired.
712 */
713 http->logType = LOG_TCP_MISS;
0655fa4d 714 processMiss();
62e76326 715 }
edce4d98 716 } else if (r->flags.ims) {
62e76326 717 /*
718 * Handle If-Modified-Since requests from the client
719 */
720
721 if (e->getReply()->sline.status != HTTP_OK) {
722 debug(88, 4) ("clientCacheHit: Reply code %d != 200\n",
723 e->getReply()->sline.status);
724 http->logType = LOG_TCP_MISS;
0655fa4d 725 processMiss();
726 } else if (e->modifiedSince(http->request)) {
62e76326 727 http->logType = LOG_TCP_IMS_HIT;
0655fa4d 728 sendMoreData(result);
62e76326 729 } else {
0655fa4d 730 time_t const timestamp = e->timestamp;
06a5ae20 731 HttpReply *temprep = e->getReply()->make304();
62e76326 732 http->logType = LOG_TCP_IMS_HIT;
86a2f789 733 removeClientStoreReference(&sc, http);
0655fa4d 734 createStoreEntry(http->request->method,
735 request_flags());
86a2f789 736 e = http->storeEntry();
62e76326 737 /*
738 * Copy timestamp from the original entry so the 304
739 * reply has a meaningful Age: header.
740 */
741 e->timestamp = timestamp;
db237875 742 e->replaceHttpReply(temprep);
62e76326 743 e->complete();
744 /* TODO: why put this in the store and then serialise it and then parse it again.
745 * Simply mark the request complete in our context and
746 * write the reply struct to the client side
747 */
0655fa4d 748 triggerInitialStoreRead();
62e76326 749 }
edce4d98 750 } else {
62e76326 751 /*
752 * plain ol' cache hit
753 */
754
ffdf45f2 755 if (e->store_status != STORE_OK)
849b826a 756 http->logType = LOG_TCP_MISS;
ffdf45f2 757 else if (e->mem_status == IN_MEMORY)
62e76326 758 http->logType = LOG_TCP_MEM_HIT;
759 else if (Config.onoff.offline)
760 http->logType = LOG_TCP_OFFLINE_HIT;
761
0655fa4d 762 sendMoreData(result);
edce4d98 763 }
764}
765
766/*
767 * Prepare to fetch the object as it's a cache miss of some kind.
768 */
769void
0655fa4d 770clientReplyContext::processMiss()
edce4d98 771{
edce4d98 772 char *url = http->uri;
190154cf 773 HttpRequest *r = http->request;
edce4d98 774 ErrorState *err = NULL;
775 debug(88, 4) ("clientProcessMiss: '%s %s'\n",
62e76326 776 RequestMethodStr[r->method], url);
edce4d98 777 /*
778 * We might have a left-over StoreEntry from a failed cache hit
779 * or IMS request.
780 */
62e76326 781
86a2f789 782 if (http->storeEntry()) {
783 if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
62e76326 784 debug(88, 0) ("clientProcessMiss: miss on a special object (%s).\n",
785 url);
786 debug(88, 0) ("\tlog_type = %s\n", log_tags[http->logType]);
86a2f789 787 storeEntryDump(http->storeEntry(), 1);
62e76326 788 }
789
86a2f789 790 removeClientStoreReference(&sc, http);
edce4d98 791 }
62e76326 792
edce4d98 793 if (r->method == METHOD_PURGE) {
0655fa4d 794 purgeRequest();
62e76326 795 return;
edce4d98 796 }
62e76326 797
0655fa4d 798 if (http->onlyIfCached()) {
799 processOnlyIfCachedMiss();
62e76326 800 return;
edce4d98 801 }
62e76326 802
edce4d98 803 /*
804 * Deny loops when running in accelerator/transproxy mode.
805 */
806 if (http->flags.accel && r->flags.loopdetect) {
62e76326 807 http->al.http.code = HTTP_FORBIDDEN;
808 err =
809 clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL,
98242069 810 &http->getConn()->peer.sin_addr, http->request);
0655fa4d 811 createStoreEntry(r->method, request_flags());
86a2f789 812 errorAppendEntry(http->storeEntry(), err);
0655fa4d 813 triggerInitialStoreRead();
62e76326 814 return;
edce4d98 815 } else {
62e76326 816 assert(http->out.offset == 0);
0655fa4d 817 createStoreEntry(r->method, r->flags);
818 triggerInitialStoreRead();
62e76326 819
820 if (http->redirect.status) {
06a5ae20 821 HttpReply *rep = new HttpReply;
edce4d98 822#if LOG_TCP_REDIRECTS
62e76326 823
824 http->logType = LOG_TCP_REDIRECT;
edce4d98 825#endif
62e76326 826
86a2f789 827 storeReleaseRequest(http->storeEntry());
06a5ae20 828 rep->redirect(http->redirect.status, http->redirect.location);
db237875 829 http->storeEntry()->replaceHttpReply(rep);
86a2f789 830 http->storeEntry()->complete();
62e76326 831 return;
832 }
833
834 if (http->flags.internal)
835 r->protocol = PROTO_INTERNAL;
836
b6b6f466 837 FwdState::fwdStart(http->getConn().getRaw() != NULL ? http->getConn()->fd : -1,
838 http->storeEntry(),
839 r);
edce4d98 840 }
841}
842
843/*
844 * client issued a request with an only-if-cached cache-control directive;
845 * we did not find a cached object that can be returned without
846 * contacting other servers;
847 * respond with a 504 (Gateway Timeout) as suggested in [RFC 2068]
848 */
0655fa4d 849void
850clientReplyContext::processOnlyIfCachedMiss()
edce4d98 851{
edce4d98 852 ErrorState *err = NULL;
edce4d98 853 debug(88, 4) ("clientProcessOnlyIfCachedMiss: '%s %s'\n",
0655fa4d 854 RequestMethodStr[http->request->method], http->uri);
edce4d98 855 http->al.http.code = HTTP_GATEWAY_TIMEOUT;
856 err = clientBuildError(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT, NULL,
98242069 857 &http->getConn()->peer.sin_addr, http->request);
86a2f789 858 removeClientStoreReference(&sc, http);
0655fa4d 859 startError(err);
edce4d98 860}
861
862void
e6ccf245 863clientReplyContext::purgeRequestFindObjectToPurge()
864{
865 /* Try to find a base entry */
866 http->flags.purging = 1;
867 lookingforstore = 1;
3b13a8fd 868 StoreEntry::getPublicByRequestMethod(this, http->request, METHOD_GET);
e6ccf245 869}
870
871void
3b13a8fd 872clientReplyContext::created(StoreEntry *newEntry)
e6ccf245 873{
874 if (lookingforstore == 1)
62e76326 875 purgeFoundGet(newEntry);
e6ccf245 876 else if (lookingforstore == 2)
62e76326 877 purgeFoundHead(newEntry);
e6ccf245 878 else if (lookingforstore == 3)
62e76326 879 purgeDoPurgeGet(newEntry);
e6ccf245 880 else if (lookingforstore == 4)
62e76326 881 purgeDoPurgeHead(newEntry);
e6ccf245 882 else if (lookingforstore == 5)
62e76326 883 identifyFoundObject(newEntry);
e6ccf245 884}
885
886void
3b13a8fd 887clientReplyContext::purgeFoundGet(StoreEntry *newEntry)
e6ccf245 888{
889 if (newEntry->isNull()) {
62e76326 890 lookingforstore = 2;
891 StoreEntry::getPublicByRequestMethod(this, http->request, METHOD_HEAD);
e6ccf245 892 } else
62e76326 893 purgeFoundObject (newEntry);
e6ccf245 894}
895
896void
3b13a8fd 897clientReplyContext::purgeFoundHead(StoreEntry *newEntry)
e6ccf245 898{
899 if (newEntry->isNull())
62e76326 900 purgeDoMissPurge();
e6ccf245 901 else
62e76326 902 purgeFoundObject (newEntry);
e6ccf245 903}
62e76326 904
e6ccf245 905void
3b13a8fd 906clientReplyContext::purgeFoundObject(StoreEntry *entry)
e6ccf245 907{
908 assert (entry && !entry->isNull());
e4b9a2f0 909
910 if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
911 http->logType = LOG_TCP_DENIED;
912 ErrorState *err =
913 clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL,
914 &http->getConn()->peer.sin_addr, http->request);
915 startError(err);
916 return;
917 }
918
62e76326 919 StoreIOBuffer tempBuffer;
920 /* Swap in the metadata */
86a2f789 921 http->storeEntry(entry);
922 storeLockObject(http->storeEntry());
923 storeCreateMemObject(http->storeEntry(), http->uri, http->log_uri);
924 http->storeEntry()->mem_obj->method = http->request->method;
925 sc = storeClientListAdd(http->storeEntry(), this);
62e76326 926 http->logType = LOG_TCP_HIT;
927 reqofs = 0;
928 tempBuffer.offset = http->out.offset;
0655fa4d 929 tempBuffer.length = next()->readBuffer.length;
930 tempBuffer.data = next()->readBuffer.data;
86a2f789 931 storeClientCopy(sc, http->storeEntry(),
0655fa4d 932 tempBuffer, CacheHit, this);
e6ccf245 933}
934
935void
936clientReplyContext::purgeRequest()
edce4d98 937{
edce4d98 938 debug(88, 3) ("Config2.onoff.enable_purge = %d\n",
62e76326 939 Config2.onoff.enable_purge);
940
edce4d98 941 if (!Config2.onoff.enable_purge) {
62e76326 942 http->logType = LOG_TCP_DENIED;
943 ErrorState *err =
944 clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL,
98242069 945 &http->getConn()->peer.sin_addr, http->request);
0655fa4d 946 startError(err);
62e76326 947 return;
edce4d98 948 }
62e76326 949
edce4d98 950 /* Release both IP cache */
951 ipcacheInvalidate(http->request->host);
952
e6ccf245 953 if (!http->flags.purging)
62e76326 954 purgeRequestFindObjectToPurge();
e6ccf245 955 else
62e76326 956 purgeDoMissPurge();
e6ccf245 957}
958
959void
960clientReplyContext::purgeDoMissPurge()
961{
29b8d8d6 962 http->logType = LOG_TCP_MISS;
e6ccf245 963 lookingforstore = 3;
3b13a8fd 964 StoreEntry::getPublicByRequestMethod(this,http->request, METHOD_GET);
e6ccf245 965}
966
967void
3b13a8fd 968clientReplyContext::purgeDoPurgeGet(StoreEntry *newEntry)
e6ccf245 969{
970 assert (newEntry);
971 /* Move to new() when that is created */
972 purgeStatus = HTTP_NOT_FOUND;
973
974 if (!newEntry->isNull()) {
62e76326 975 /* Release the cached URI */
976 debug(88, 4) ("clientPurgeRequest: GET '%s'\n", storeUrl(newEntry));
977 storeRelease(newEntry);
978 purgeStatus = HTTP_OK;
edce4d98 979 }
62e76326 980
e6ccf245 981 lookingforstore = 4;
3b13a8fd 982 StoreEntry::getPublicByRequestMethod(this, http->request, METHOD_HEAD);
e6ccf245 983}
984
985void
3b13a8fd 986clientReplyContext::purgeDoPurgeHead(StoreEntry *newEntry)
e6ccf245 987{
4fc0ac76 988 if (newEntry && !newEntry->isNull()) {
62e76326 989 debug(88, 4) ("clientPurgeRequest: HEAD '%s'\n", storeUrl(newEntry));
990 storeRelease(newEntry);
991 purgeStatus = HTTP_OK;
edce4d98 992 }
62e76326 993
e6ccf245 994 HttpReply *r;
62e76326 995
edce4d98 996 /* And for Vary, release the base URI if none of the headers was included in the request */
62e76326 997
edce4d98 998 if (http->request->vary_headers
62e76326 999 && !strstr(http->request->vary_headers, "=")) {
1000 StoreEntry *entry = storeGetPublic(urlCanonical(http->request), METHOD_GET);
1001
1002 if (entry) {
1003 debug(88, 4) ("clientPurgeRequest: Vary GET '%s'\n",
1004 storeUrl(entry));
1005 storeRelease(entry);
1006 purgeStatus = HTTP_OK;
1007 }
1008
1009 entry = storeGetPublic(urlCanonical(http->request), METHOD_HEAD);
1010
1011 if (entry) {
1012 debug(88, 4) ("clientPurgeRequest: Vary HEAD '%s'\n",
1013 storeUrl(entry));
1014 storeRelease(entry);
1015 purgeStatus = HTTP_OK;
1016 }
edce4d98 1017 }
62e76326 1018
edce4d98 1019 /*
1020 * Make a new entry to hold the reply to be written
1021 * to the client.
1022 */
528b2c61 1023 /* FIXME: This doesn't need to go through the store. Simply
1024 * push down the client chain
1025 */
0655fa4d 1026 createStoreEntry(http->request->method, request_flags());
62e76326 1027
0655fa4d 1028 triggerInitialStoreRead();
62e76326 1029
06a5ae20 1030 r = new HttpReply;
62e76326 1031
450e0c10 1032 HttpVersion version(1,0);
62e76326 1033
06a5ae20 1034 r->setHeaders(version, purgeStatus, NULL, NULL, 0, 0, -1);
62e76326 1035
db237875 1036 http->storeEntry()->replaceHttpReply(r);
62e76326 1037
86a2f789 1038 http->storeEntry()->complete();
edce4d98 1039}
1040
1041void
0655fa4d 1042clientReplyContext::traceReply(clientStreamNode * node)
edce4d98 1043{
1044 HttpReply *rep;
e6ccf245 1045 clientStreamNode *next = (clientStreamNode *)node->node.next->data;
528b2c61 1046 StoreIOBuffer tempBuffer;
0655fa4d 1047 assert(http->request->max_forwards == 0);
1048 createStoreEntry(http->request->method, request_flags());
1049 tempBuffer.offset = next->readBuffer.offset + headers_sz;
c8be6d7b 1050 tempBuffer.length = next->readBuffer.length;
1051 tempBuffer.data = next->readBuffer.data;
86a2f789 1052 storeClientCopy(sc, http->storeEntry(),
0655fa4d 1053 tempBuffer, SendMoreData, this);
86a2f789 1054 storeReleaseRequest(http->storeEntry());
1055 storeBuffer(http->storeEntry());
06a5ae20 1056 rep = new HttpReply;
450e0c10 1057 HttpVersion version(1,0);
06a5ae20 1058 rep->setHeaders(version, HTTP_OK, NULL, "text/plain",
5cafad19 1059 http->request->prefixLen(), 0, squid_curtime);
db237875 1060 http->storeEntry()->replaceHttpReply(rep);
5cafad19 1061 http->request->swapOut(http->storeEntry());
86a2f789 1062 http->storeEntry()->complete();
edce4d98 1063}
1064
1065#define SENDING_BODY 0
1066#define SENDING_HDRSONLY 1
1067int
0655fa4d 1068clientReplyContext::checkTransferDone()
edce4d98 1069{
86a2f789 1070 StoreEntry *entry = http->storeEntry();
62e76326 1071
edce4d98 1072 if (entry == NULL)
62e76326 1073 return 0;
1074
1075 /*
edce4d98 1076 * For now, 'done_copying' is used for special cases like
1077 * Range and HEAD requests.
1078 */
0655fa4d 1079 if (http->flags.done_copying)
62e76326 1080 return 1;
1081
edce4d98 1082 /*
1083 * Handle STORE_OK objects.
1084 * objectLen(entry) will be set proprely.
1085 * RC: Does objectLen(entry) include the Headers?
0e3be1ea 1086 * RC: Yes.
edce4d98 1087 */
1088 if (entry->store_status == STORE_OK) {
0655fa4d 1089 return storeOKTransferDone();
e39b9382 1090 } else {
0655fa4d 1091 return storeNotOKTransferDone();
edce4d98 1092 }
e39b9382 1093}
1094
1095int
1096clientReplyContext::storeOKTransferDone() const
1097{
86a2f789 1098 if (http->out.offset >= objectLen(http->storeEntry()) - headers_sz)
62e76326 1099 return 1;
1100
e39b9382 1101 return 0;
1102}
1103
1104int
1105clientReplyContext::storeNotOKTransferDone() const
1106{
edce4d98 1107 /*
1108 * Now, handle STORE_PENDING objects
1109 */
86a2f789 1110 MemObject *mem = http->storeEntry()->mem_obj;
edce4d98 1111 assert(mem != NULL);
1112 assert(http->request != NULL);
0e3be1ea 1113 /* mem->reply was wrong because it uses the UPSTREAM header length!!! */
0353e724 1114 HttpReply const *reply = mem->getReply();
62e76326 1115
e39b9382 1116 if (headers_sz == 0)
62e76326 1117 /* haven't found end of headers yet */
1118 return 0;
e39b9382 1119
1120 int sending = SENDING_BODY;
62e76326 1121
e39b9382 1122 if (reply->sline.status == HTTP_NO_CONTENT ||
62e76326 1123 reply->sline.status == HTTP_NOT_MODIFIED ||
1124 reply->sline.status < HTTP_OK ||
1125 http->request->method == METHOD_HEAD)
1126 sending = SENDING_HDRSONLY;
1127
edce4d98 1128 /*
1129 * Figure out how much data we are supposed to send.
1130 * If we are sending a body and we don't have a content-length,
1131 * then we must wait for the object to become STORE_OK.
1132 */
0e3be1ea 1133 if (reply->content_length < 0)
62e76326 1134 return 0;
e39b9382 1135
1136 size_t expectedLength = http->out.headers_sz + reply->content_length;
62e76326 1137
e39b9382 1138 if (http->out.size < expectedLength)
62e76326 1139 return 0;
edce4d98 1140 else
62e76326 1141 return 1;
edce4d98 1142}
1143
1144
edce4d98 1145/* A write has completed, what is the next status based on the
1146 * canonical request data?
1147 * 1 something is wrong
1148 * 0 nothing is wrong.
1149 *
1150 */
1151int
59a1efb2 1152clientHttpRequestStatus(int fd, ClientHttpRequest const *http)
edce4d98 1153{
1154#if SIZEOF_SIZE_T == 4
62e76326 1155
edce4d98 1156 if (http->out.size > 0x7FFF0000) {
62e76326 1157 debug(88, 1) ("WARNING: closing FD %d to prevent counter overflow\n",
1158 fd);
1159 debug(88, 1) ("\tclient %s\n",
a2ac85d9 1160 inet_ntoa(http->getConn().getRaw() != NULL ? http->getConn()->peer.sin_addr : no_addr));
62e76326 1161 debug(88, 1) ("\treceived %d bytes\n", (int) http->out.size);
1162 debug(88, 1) ("\tURI %s\n", http->log_uri);
1163 return 1;
edce4d98 1164 }
62e76326 1165
edce4d98 1166#endif
1167#if SIZEOF_OFF_T == 4
1168 if (http->out.offset > 0x7FFF0000) {
62e76326 1169 debug(88, 1) ("WARNING: closing FD %d to prevent counter overflow\n",
1170 fd);
1171 debug(88, 1) ("\tclient %s\n",
a2ac85d9 1172 inet_ntoa(http->getConn().getRaw() != NULL ? http->getConn()->peer.sin_addr : no_addr));
62e76326 1173 debug(88, 1) ("\treceived %d bytes (offset %d)\n", (int) http->out.size,
1174 (int) http->out.offset);
1175 debug(88, 1) ("\tURI %s\n", http->log_uri);
1176 return 1;
edce4d98 1177 }
62e76326 1178
edce4d98 1179#endif
1180 return 0;
1181}
1182
62e76326 1183/* Preconditions:
edce4d98 1184 * *http is a valid structure.
1185 * fd is either -1, or an open fd.
1186 *
1187 * TODO: enumify this
1188 *
1189 * This function is used by any http request sink, to determine the status
1190 * of the object.
1191 */
1192clientStream_status_t
59a1efb2 1193clientReplyStatus(clientStreamNode * aNode, ClientHttpRequest * http)
edce4d98 1194{
0655fa4d 1195 clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1196 assert (context);
1197 assert (context->http == http);
1198 return context->replyStatus();
1199}
1200
1201clientStream_status_t
1202clientReplyContext::replyStatus()
1203{
edce4d98 1204 int done;
1205 /* Here because lower nodes don't need it */
62e76326 1206
86a2f789 1207 if (http->storeEntry() == NULL)
62e76326 1208 return STREAM_FAILED; /* yuck, but what can we do? */
1209
86a2f789 1210 if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
62e76326 1211 /* TODO: Could upstream read errors (result.flags.error) be
1212 * lost, and result in undersize requests being considered
1213 * complete. Should we tcp reset such connections ?
1214 */
1215 return STREAM_FAILED;
1216
0655fa4d 1217 if ((done = checkTransferDone()) != 0 || flags.complete) {
62e76326 1218 debug(88, 5) ("clientReplyStatus: transfer is DONE\n");
1219 /* Ok we're finished, but how? */
1220
06a5ae20 1221 if (http->storeEntry()->getReply()->bodySize(http->request->method) < 0) {
62e76326 1222 debug(88, 5) ("clientReplyStatus: closing, content_length < 0\n");
1223 return STREAM_FAILED;
1224 }
1225
1226 if (!done) {
1227 debug(88, 5) ("clientReplyStatus: closing, !done, but read 0 bytes\n");
1228 return STREAM_FAILED;
1229 }
1230
0655fa4d 1231 if (!http->gotEnough()) {
62e76326 1232 debug(88, 5) ("clientReplyStatus: client didn't get all it expected\n");
1233 return STREAM_UNPLANNED_COMPLETE;
1234 }
1235
1236 if (http->request->flags.proxy_keepalive) {
1237 debug(88, 5) ("clientReplyStatus: stream complete and can keepalive\n");
1238 return STREAM_COMPLETE;
1239 }
1240
1241 debug(88, 5) ("clientReplyStatus: stream was not expected to complete!\n");
1242 return STREAM_UNPLANNED_COMPLETE;
edce4d98 1243 }
62e76326 1244
b51aec66 1245 if (http->isReplyBodyTooLarge(http->out.offset - 4096)) {
1246 /* 4096 is a margin for the HTTP headers included in out.offset */
62e76326 1247 debug(88, 5) ("clientReplyStatus: client reply body is too large\n");
1248 return STREAM_FAILED;
0e3be1ea 1249 }
62e76326 1250
edce4d98 1251 return STREAM_NONE;
1252}
1253
62e76326 1254/* Responses with no body will not have a content-type header,
edce4d98 1255 * which breaks the rep_mime_type acl, which
1256 * coincidentally, is the most common acl for reply access lists.
1257 * A better long term fix for this is to allow acl matchs on the various
1258 * status codes, and then supply a default ruleset that puts these
1259 * codes before any user defines access entries. That way the user
1260 * can choose to block these responses where appropriate, but won't get
1261 * mysterious breakages.
1262 */
0655fa4d 1263bool
1264clientReplyContext::alwaysAllowResponse(http_status sline) const
edce4d98 1265{
4ef4b952 1266 bool result;
1267
edce4d98 1268 switch (sline) {
62e76326 1269
edce4d98 1270 case HTTP_CONTINUE:
62e76326 1271
edce4d98 1272 case HTTP_SWITCHING_PROTOCOLS:
62e76326 1273
edce4d98 1274 case HTTP_PROCESSING:
62e76326 1275
edce4d98 1276 case HTTP_NO_CONTENT:
62e76326 1277
edce4d98 1278 case HTTP_NOT_MODIFIED:
4ef4b952 1279 result = true;
62e76326 1280 break;
1281
edce4d98 1282 default:
4ef4b952 1283 result = false;
edce4d98 1284 }
4ef4b952 1285
1286 return result;
edce4d98 1287}
1288
528b2c61 1289void
0655fa4d 1290clientReplyContext::obeyConnectionHeader()
528b2c61 1291{
0655fa4d 1292 HttpHeader *hdr = &holdingReply->header;
924f73bc 1293 hdr->removeConnectionHeaderEntries();
528b2c61 1294}
1295
edce4d98 1296/*
1297 * filters out unwanted entries from original reply header
1298 * adds extra entries if we have more info than origin server
1299 * adds Squid specific entries
1300 */
0655fa4d 1301void
1302clientReplyContext::buildReplyHeader()
edce4d98 1303{
0655fa4d 1304 HttpHeader *hdr = &holdingReply->header;
c8be6d7b 1305 int is_hit = logTypeIsATcpHit(http->logType);
190154cf 1306 HttpRequest *request = http->request;
edce4d98 1307#if DONT_FILTER_THESE
1308 /* but you might want to if you run Squid as an HTTP accelerator */
1309 /* httpHeaderDelById(hdr, HDR_ACCEPT_RANGES); */
1310 httpHeaderDelById(hdr, HDR_ETAG);
1311#endif
62e76326 1312
edce4d98 1313 httpHeaderDelById(hdr, HDR_PROXY_CONNECTION);
1314 /* here: Keep-Alive is a field-name, not a connection directive! */
1315 httpHeaderDelByName(hdr, "Keep-Alive");
1316 /* remove Set-Cookie if a hit */
62e76326 1317
edce4d98 1318 if (is_hit)
62e76326 1319 httpHeaderDelById(hdr, HDR_SET_COOKIE);
1320
0655fa4d 1321 obeyConnectionHeader();
62e76326 1322
1323 // if (request->range)
0655fa4d 1324 // clientBuildRangeHeader(http, holdingReply);
edce4d98 1325 /*
1326 * Add a estimated Age header on cache hits.
1327 */
1328 if (is_hit) {
62e76326 1329 /*
1330 * Remove any existing Age header sent by upstream caches
1331 * (note that the existing header is passed along unmodified
1332 * on cache misses)
1333 */
1334 httpHeaderDelById(hdr, HDR_AGE);
1335 /*
1336 * This adds the calculated object age. Note that the details of the
1337 * age calculation is performed by adjusting the timestamp in
1338 * storeTimestampsSet(), not here.
1339 *
1340 * BROWSER WORKAROUND: IE sometimes hangs when receiving a 0 Age
1341 * header, so don't use it unless there is a age to report. Please
1342 * note that Age is only used to make a conservative estimation of
1343 * the objects age, so a Age: 0 header does not add any useful
1344 * information to the reply in any case.
1345 */
1346
86a2f789 1347 if (NULL == http->storeEntry())
62e76326 1348 (void) 0;
86a2f789 1349 else if (http->storeEntry()->timestamp < 0)
62e76326 1350 (void) 0;
bbe58ab5 1351
1352 if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
1353 httpHeaderDelById(hdr, HDR_DATE);
1354 httpHeaderInsertTime(hdr, HDR_DATE, squid_curtime);
1355 } else if (http->storeEntry()->timestamp < squid_curtime) {
62e76326 1356 httpHeaderPutInt(hdr, HDR_AGE,
86a2f789 1357 squid_curtime - http->storeEntry()->timestamp);
62e76326 1358 /* Signal old objects. NB: rfc 2616 is not clear,
1359 * by implication, on whether we should do this to all
1360 * responses, or only cache hits.
1361 * 14.46 states it ONLY applys for heuristically caclulated
1362 * freshness values, 13.2.4 doesn't specify the same limitation.
1363 * We interpret RFC 2616 under the combination.
1364 */
1365 /* TODO: if maxage or s-maxage is present, don't do this */
1366
86a2f789 1367 if (squid_curtime - http->storeEntry()->timestamp >= 86400) {
62e76326 1368 char tempbuf[512];
1369 snprintf (tempbuf, sizeof(tempbuf), "%s %s %s",
1370 "113", ThisCache,
1371 "This cache hit is still fresh and more than 1 day old");
1372 httpHeaderPutStr(hdr, HDR_WARNING, tempbuf);
1373 }
1374 }
528b2c61 1375
edce4d98 1376 }
62e76326 1377
0bd9aa82 1378 /* Filter unproxyable authentication types */
ddd31ed3 1379 if (http->logType != LOG_TCP_DENIED &&
62e76326 1380 (httpHeaderHas(hdr, HDR_WWW_AUTHENTICATE) || httpHeaderHas(hdr, HDR_PROXY_AUTHENTICATE))) {
1381 HttpHeaderPos pos = HttpHeaderInitPos;
1382 HttpHeaderEntry *e;
1383
1384 while ((e = httpHeaderGetEntry(hdr, &pos))) {
1385 if (e->id == HDR_WWW_AUTHENTICATE || e->id == HDR_PROXY_AUTHENTICATE) {
1386 const char *value = e->value.buf();
1387
1388 if ((strncasecmp(value, "NTLM", 4) == 0 &&
1389 (value[4] == '\0' || value[4] == ' '))
1390 ||
1391 (strncasecmp(value, "Negotiate", 9) == 0 &&
1392 (value[9] == '\0' || value[9] == ' ')))
1393 httpHeaderDelAt(hdr, pos);
1394 }
1395 }
0bd9aa82 1396 }
62e76326 1397
edce4d98 1398 /* Handle authentication headers */
1399 if (request->auth_user_request)
0655fa4d 1400 authenticateFixHeader(holdingReply, request->auth_user_request, request,
62e76326 1401 http->flags.accel, 0);
1402
edce4d98 1403 /* Append X-Cache */
1404 httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",
62e76326 1405 is_hit ? "HIT" : "MISS", getMyHostname());
1406
edce4d98 1407#if USE_CACHE_DIGESTS
1408 /* Append X-Cache-Lookup: -- temporary hack, to be removed @?@ @?@ */
1409 httpHeaderPutStrf(hdr, HDR_X_CACHE_LOOKUP, "%s from %s:%d",
0655fa4d 1410 lookup_type ? lookup_type : "NONE",
62e76326 1411 getMyHostname(), getMyPort());
1412
edce4d98 1413#endif
62e76326 1414
06a5ae20 1415 if (holdingReply->bodySize(request->method) < 0) {
62e76326 1416 debug(88,
1417 3)
1418 ("clientBuildReplyHeader: can't keep-alive, unknown body size\n");
1419 request->flags.proxy_keepalive = 0;
edce4d98 1420 }
62e76326 1421
f5e45ad8 1422 if (fdUsageHigh()&& !request->flags.must_keepalive) {
a1ca9253 1423 debug(88, 3) ("clientBuildReplyHeader: Not many unused FDs, can't keep-alive\n");
1424 request->flags.proxy_keepalive = 0;
1425 }
1426
f5e45ad8 1427 if (!Config.onoff.client_pconns && !request->flags.must_keepalive)
1428 request->flags.proxy_keepalive = 0;
1429
4dea3fdd 1430 /* Append VIA */
c8be6d7b 1431 {
62e76326 1432 LOCAL_ARRAY(char, bbuf, MAX_URL + 32);
1433 String strVia = httpHeaderGetList(hdr, HDR_VIA);
1434 snprintf(bbuf, sizeof(bbuf), "%d.%d %s",
0655fa4d 1435 holdingReply->sline.version.major,
1436 holdingReply->sline.version.minor,
62e76326 1437 ThisCache);
1438 strListAdd(&strVia, bbuf, ',');
1439 httpHeaderDelById(hdr, HDR_VIA);
1440 httpHeaderPutStr(hdr, HDR_VIA, strVia.buf());
1441 strVia.clean();
c8be6d7b 1442 }
edce4d98 1443 /* Signal keep-alive if needed */
1444 httpHeaderPutStr(hdr,
62e76326 1445 http->flags.accel ? HDR_CONNECTION : HDR_PROXY_CONNECTION,
1446 request->flags.proxy_keepalive ? "keep-alive" : "close");
1447
edce4d98 1448#if ADD_X_REQUEST_URI
1449 /*
1450 * Knowing the URI of the request is useful when debugging persistent
1451 * connections in a client; we cannot guarantee the order of http headers,
1452 * but X-Request-URI is likely to be the very last header to ease use from a
1453 * debugger [hdr->entries.count-1].
1454 */
1455 httpHeaderPutStr(hdr, HDR_X_REQUEST_URI,
86a2f789 1456 http->memOjbect()->url ? http->memObject()->url : http->uri);
62e76326 1457
edce4d98 1458#endif
62e76326 1459
8c01ada0 1460 httpHdrMangleList(hdr, request, ROR_REPLY);
edce4d98 1461}
1462
1463
0655fa4d 1464void
1465clientReplyContext::buildReply(const char *buf, size_t size)
edce4d98 1466{
edce4d98 1467 size_t k = headersEnd(buf, size);
62e76326 1468
0655fa4d 1469 if (!k)
1470 return;
1471
06a5ae20 1472 holdReply(new HttpReply);
0655fa4d 1473
59eed7dc 1474 if (!holdingReply->parseCharBuf(buf, k)) {
62e76326 1475 /* parsing failure, get rid of the invalid reply */
06a5ae20 1476 delete holdingReply;
0655fa4d 1477 holdReply (NULL);
06a5ae20 1478 /* This is wrong. ~HttpReply() should to the rep
62e76326 1479 * for us, and we can destroy our own range info
1480 */
1481
0655fa4d 1482 if (http->request->range) {
62e76326 1483 /* this will fail and destroy request->range */
0655fa4d 1484 // clientBuildRangeHeader(http, holdingReply);
62e76326 1485 }
b51aec66 1486
979da574 1487 return;
edce4d98 1488 }
62e76326 1489
0655fa4d 1490 /* enforce 1.0 reply version */
450e0c10 1491 holdingReply->sline.version = HttpVersion(1,0);
0655fa4d 1492
1493 /* do header conversions */
1494 buildReplyHeader();
edce4d98 1495}
1496
e6ccf245 1497void
1498clientReplyContext::identifyStoreObject()
edce4d98 1499{
190154cf 1500 HttpRequest *r = http->request;
62e76326 1501
e6ccf245 1502 if (r->flags.cachable || r->flags.internal) {
62e76326 1503 lookingforstore = 5;
1504 StoreEntry::getPublicByRequest (this, r);
e6ccf245 1505 } else
62e76326 1506 identifyFoundObject (NullStoreEntry::getInstance());
e6ccf245 1507}
1508
1509void
1510clientReplyContext::identifyFoundObject(StoreEntry *newEntry)
1511{
1512 StoreEntry *e = newEntry;
190154cf 1513 HttpRequest *r = http->request;
62e76326 1514
e6ccf245 1515 if (e->isNull()) {
86a2f789 1516 http->storeEntry(NULL);
e6ccf245 1517 } else {
86a2f789 1518 http->storeEntry(e);
e6ccf245 1519 }
62e76326 1520
86a2f789 1521 e = http->storeEntry();
a12a049a 1522 /* Release IP-cache entries on reload */
1523
1524 if (r->flags.nocache) {
1525
1526#if USE_DNSSERVERS
62e76326 1527
62e76326 1528 ipcacheInvalidate(r->host);
1529
a12a049a 1530#else
1531
1532 ipcacheInvalidateNegative(r->host);
1533
1534#endif /* USE_DNSSERVERS */
1535
1536 }
1537
edce4d98 1538#if HTTP_VIOLATIONS
62e76326 1539
a12a049a 1540 else if (r->flags.nocache_hack) {
1541
1542#if USE_DNSSERVERS
1543
62e76326 1544 ipcacheInvalidate(r->host);
1545
a12a049a 1546#else
1547
1548 ipcacheInvalidateNegative(r->host);
1549
1550#endif /* USE_DNSSERVERS */
1551
1552 }
1553
1554#endif /* HTTP_VIOLATIONS */
edce4d98 1555#if USE_CACHE_DIGESTS
62e76326 1556
86a2f789 1557 lookup_type = http->storeEntry() ? "HIT" : "MISS";
62e76326 1558
edce4d98 1559#endif
62e76326 1560
86a2f789 1561 if (NULL == http->storeEntry()) {
62e76326 1562 /* this object isn't in the cache */
1563 debug(85, 3) ("clientProcessRequest2: storeGet() MISS\n");
1564 http->logType = LOG_TCP_MISS;
1565 doGetMoreData();
1566 return;
edce4d98 1567 }
62e76326 1568
edce4d98 1569 if (Config.onoff.offline) {
62e76326 1570 debug(85, 3) ("clientProcessRequest2: offline HIT\n");
1571 http->logType = LOG_TCP_HIT;
1572 doGetMoreData();
1573 return;
edce4d98 1574 }
62e76326 1575
edce4d98 1576 if (http->redirect.status) {
62e76326 1577 /* force this to be a miss */
86a2f789 1578 http->storeEntry(NULL);
62e76326 1579 http->logType = LOG_TCP_MISS;
1580 doGetMoreData();
1581 return;
edce4d98 1582 }
62e76326 1583
edce4d98 1584 if (!storeEntryValidToSend(e)) {
62e76326 1585 debug(85, 3) ("clientProcessRequest2: !storeEntryValidToSend MISS\n");
86a2f789 1586 http->storeEntry(NULL);
62e76326 1587 http->logType = LOG_TCP_MISS;
1588 doGetMoreData();
1589 return;
edce4d98 1590 }
62e76326 1591
edce4d98 1592 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
62e76326 1593 /* Special entries are always hits, no matter what the client says */
1594 debug(85, 3) ("clientProcessRequest2: ENTRY_SPECIAL HIT\n");
1595 http->logType = LOG_TCP_HIT;
1596 doGetMoreData();
1597 return;
edce4d98 1598 }
62e76326 1599
edce4d98 1600 if (r->flags.nocache) {
62e76326 1601 debug(85, 3) ("clientProcessRequest2: no-cache REFRESH MISS\n");
86a2f789 1602 http->storeEntry(NULL);
62e76326 1603 http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
1604 doGetMoreData();
1605 return;
edce4d98 1606 }
62e76326 1607
edce4d98 1608 debug(85, 3) ("clientProcessRequest2: default HIT\n");
e6ccf245 1609 http->logType = LOG_TCP_HIT;
1610 doGetMoreData();
edce4d98 1611}
1612
1613/* Request more data from the store for the client Stream
1614 * This is *the* entry point to this module.
1615 *
1616 * Preconditions:
1617 * This is the head of the list.
1618 * There is at least one more node.
1619 * data context is not null
1620 */
1621void
59a1efb2 1622clientGetMoreData(clientStreamNode * aNode, ClientHttpRequest * http)
edce4d98 1623{
edce4d98 1624 /* Test preconditions */
e6ccf245 1625 assert(aNode != NULL);
1626 assert(cbdataReferenceValid(aNode));
e6ccf245 1627 assert(aNode->node.prev == NULL);
1628 assert(aNode->node.next != NULL);
0655fa4d 1629 clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1630 assert (context);
edce4d98 1631 assert(context->http == http);
1632
0655fa4d 1633 clientStreamNode *next = ( clientStreamNode *)aNode->node.next->data;
62e76326 1634
edce4d98 1635 if (!context->ourNode)
62e76326 1636 context->ourNode = aNode;
1637
e6ccf245 1638 /* no cbdatareference, this is only used once, and safely */
edce4d98 1639 if (context->flags.storelogiccomplete) {
62e76326 1640 StoreIOBuffer tempBuffer;
1641 tempBuffer.offset = next->readBuffer.offset + context->headers_sz;
1642 tempBuffer.length = next->readBuffer.length;
1643 tempBuffer.data = next->readBuffer.data;
1644
86a2f789 1645 storeClientCopy(context->sc, http->storeEntry(),
0655fa4d 1646 tempBuffer, clientReplyContext::SendMoreData, context);
62e76326 1647 return;
edce4d98 1648 }
62e76326 1649
edce4d98 1650 if (context->http->request->method == METHOD_PURGE) {
62e76326 1651 context->purgeRequest();
1652 return;
edce4d98 1653 }
62e76326 1654
edce4d98 1655 if (context->http->request->method == METHOD_TRACE) {
62e76326 1656 if (context->http->request->max_forwards == 0) {
0655fa4d 1657 context->traceReply(aNode);
62e76326 1658 return;
1659 }
1660
1661 /* continue forwarding, not finished yet. */
1662 http->logType = LOG_TCP_MISS;
1663
1664 context->doGetMoreData();
edce4d98 1665 } else
62e76326 1666 context->identifyStoreObject();
e6ccf245 1667}
1668
1669void
1670clientReplyContext::doGetMoreData()
1671{
edce4d98 1672 /* We still have to do store logic processing - vary, cache hit etc */
62e76326 1673
86a2f789 1674 if (http->storeEntry() != NULL) {
62e76326 1675 /* someone found the object in the cache for us */
1676 StoreIOBuffer tempBuffer;
86a2f789 1677 storeLockObject(http->storeEntry());
62e76326 1678
86a2f789 1679 if (http->storeEntry()->mem_obj == NULL) {
62e76326 1680 /*
1681 * This if-block exists because we don't want to clobber
1682 * a preexiting mem_obj->method value if the mem_obj
1683 * already exists. For example, when a HEAD request
1684 * is a cache hit for a GET response, we want to keep
1685 * the method as GET.
1686 */
86a2f789 1687 storeCreateMemObject(http->storeEntry(), http->uri,
62e76326 1688 http->log_uri);
86a2f789 1689 http->storeEntry()->mem_obj->method =
62e76326 1690 http->request->method;
1691 }
1692
86a2f789 1693 sc = storeClientListAdd(http->storeEntry(), this);
edce4d98 1694#if DELAY_POOLS
62e76326 1695
1696 sc->setDelayId(DelayId::DelayClient(http));
edce4d98 1697#endif
62e76326 1698
1699 assert(http->logType == LOG_TCP_HIT);
1700 reqofs = 0;
1701 /* guarantee nothing has been sent yet! */
1702 assert(http->out.size == 0);
1703 assert(http->out.offset == 0);
1704 tempBuffer.offset = reqofs;
1705 tempBuffer.length = getNextNode()->readBuffer.length;
1706 tempBuffer.data = getNextNode()->readBuffer.data;
86a2f789 1707 storeClientCopy(sc, http->storeEntry(),
0655fa4d 1708 tempBuffer, CacheHit, this);
edce4d98 1709 } else {
62e76326 1710 /* MISS CASE, http->logType is already set! */
0655fa4d 1711 processMiss();
edce4d98 1712 }
1713}
1714
1715/* the next node has removed itself from the stream. */
1716void
59a1efb2 1717clientReplyDetach(clientStreamNode * node, ClientHttpRequest * http)
edce4d98 1718{
1719 /* detach from the stream */
edce4d98 1720 clientStreamDetach(node, http);
1721}
1722
1723/*
1724 * accepts chunk of a http message in buf, parses prefix, filters headers and
1725 * such, writes processed message to the message recipient
1726 */
1727void
0655fa4d 1728clientReplyContext::SendMoreData(void *data, StoreIOBuffer result)
edce4d98 1729{
e6ccf245 1730 clientReplyContext *context = static_cast<clientReplyContext *>(data);
528b2c61 1731 context->sendMoreData (result);
1732}
1733
1734void
1735clientReplyContext::makeThisHead()
1736{
1737 /* At least, I think thats what this does */
1738 dlinkDelete(&http->active, &ClientActiveRequests);
1739 dlinkAdd(http, &http->active, &ClientActiveRequests);
1740}
1741
1742bool
1743clientReplyContext::errorInStream(StoreIOBuffer const &result, size_t const &sizeToProcess)const
1744{
1745 return /* aborted request */
86a2f789 1746 (http->storeEntry() && EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) ||
62e76326 1747 /* Upstream read error */ (result.flags.error) ||
1748 /* Upstream EOF */ (sizeToProcess == 0);
528b2c61 1749}
1750
1751void
1752clientReplyContext::sendStreamError(StoreIOBuffer const &result)
1753{
1754 /* call clientWriteComplete so the client socket gets closed */
1755 /* We call into the stream, because we don't know that there is a
1756 * client socket!
1757 */
1758 debug(88,5)("clientReplyContext::sendStreamError: A stream error has occured, marking as complete and sending no data.\n");
1759 StoreIOBuffer tempBuffer;
1760 flags.complete = 1;
1761 tempBuffer.flags.error = result.flags.error;
1762 clientStreamCallback((clientStreamNode*)http->client_stream.head->data, http, NULL,
62e76326 1763 tempBuffer);
528b2c61 1764}
1765
1766void
1767clientReplyContext::pushStreamData(StoreIOBuffer const &result, char *source)
1768{
1769 StoreIOBuffer tempBuffer;
62e76326 1770
528b2c61 1771 if (result.length == 0) {
62e76326 1772 debug (88,5)("clientReplyContext::pushStreamData: marking request as complete due to 0 length store result\n");
1773 flags.complete = 1;
528b2c61 1774 }
62e76326 1775
43ae1d95 1776 assert(result.offset - headers_sz == next()->readBuffer.offset);
528b2c61 1777 tempBuffer.offset = result.offset - headers_sz;
1778 tempBuffer.length = result.length;
62e76326 1779
43ae1d95 1780 if (tempBuffer.length)
1781 tempBuffer.data = source;
62e76326 1782
528b2c61 1783 clientStreamCallback((clientStreamNode*)http->client_stream.head->data, http, NULL,
62e76326 1784 tempBuffer);
528b2c61 1785}
1786
1787clientStreamNode *
1788clientReplyContext::next() const
1789{
1790 assert ( (clientStreamNode*)http->client_stream.head->next->data == getNextNode());
1791 return getNextNode();
1792}
1793
1794void
0655fa4d 1795clientReplyContext::waitForMoreData ()
528b2c61 1796{
1797 debug(88,5)("clientReplyContext::waitForMoreData: Waiting for more data to parse reply headers in client side.\n");
1798 /* We don't have enough to parse the metadata yet */
1799 /* TODO: the store should give us out of band metadata and
1800 * obsolete this routine
1801 */
1802 /* wait for more to arrive */
528b2c61 1803 startSendProcess();
1804}
1805
1806void
1807clientReplyContext::startSendProcess()
1808{
0655fa4d 1809 debug(88,5)("clientReplyContext::startSendProcess: triggering store read to SendMoreData\n");
528b2c61 1810 assert(reqofs <= HTTP_REQBUF_SZ);
1811 /* TODO: copy into the supplied buffer */
1812 StoreIOBuffer tempBuffer;
1813 tempBuffer.offset = reqofs;
1814 tempBuffer.length = next()->readBuffer.length - reqofs;
1815 tempBuffer.data = next()->readBuffer.data + reqofs;
86a2f789 1816 storeClientCopy(sc, http->storeEntry(),
0655fa4d 1817 tempBuffer, SendMoreData, this);
1818}
1819
1820void
1821clientReplyContext::holdReply(HttpReply *aReply)
1822{
1823 assert (!holdingReply || !aReply);
1824 holdingReply = aReply;
528b2c61 1825}
1826
b51aec66 1827/*
1828 * Calculates the maximum size allowed for an HTTP response
1829 */
1830void
1831clientReplyContext::buildMaxBodySize(HttpReply * reply)
1832{
ef1955a5 1833 acl_size_t *l = Config.ReplyBodySize;
1834 ACLChecklist *ch;
b51aec66 1835
8777d90e 1836 if (http->logType == LOG_TCP_DENIED)
1837 return;
1838
ef1955a5 1839 ch = clientAclChecklistCreate(NULL, http);
8777d90e 1840
ef1955a5 1841 ch->reply = reply;
1842
1843 for (l = Config.ReplyBodySize; l; l = l -> next) {
7684c4b1 1844 if (ch->matchAclListFast(l->aclList)) {
43cb430e 1845 if (l->size != static_cast<size_t>(-1)) {
1846 debug(58, 3) ("clientReplyContext: Setting maxBodySize to %ld\n", (long int) l->size);
ef1955a5 1847 http->maxReplyBodySize(l->size);
43cb430e 1848 }
b51aec66 1849
ef1955a5 1850 break;
1851 }
b51aec66 1852 }
ef1955a5 1853
1854 delete ch;
b51aec66 1855}
1856
4993f571 1857void
1858clientReplyContext::processReplyAccess ()
1859{
1860 HttpReply *rep = holdingReply;
0655fa4d 1861 holdReply(NULL);
b51aec66 1862 buildMaxBodySize(rep);
62e76326 1863
b51aec66 1864 if (http->isReplyBodyTooLarge(rep->content_length)) {
62e76326 1865 ErrorState *err =
1866 clientBuildError(ERR_TOO_BIG, HTTP_FORBIDDEN, NULL,
a2ac85d9 1867 http->getConn().getRaw() != NULL ? &http->getConn()->peer.sin_addr : &no_addr,
62e76326 1868 http->request);
86a2f789 1869 removeClientStoreReference(&sc, http);
0655fa4d 1870 startError(err);
06a5ae20 1871 delete rep;
62e76326 1872 return;
1873 }
1874
1875 headers_sz = rep->hdr_sz;
1876 ACLChecklist *replyChecklist;
1877 replyChecklist = clientAclChecklistCreate(Config.accessList.reply, http);
1878 replyChecklist->reply = rep;
0655fa4d 1879 holdReply (rep);
1880 replyChecklist->nonBlockingCheck(ProcessReplyAccessResult, this);
4993f571 1881}
1882
1883void
0655fa4d 1884clientReplyContext::ProcessReplyAccessResult (int rv, void *voidMe)
4993f571 1885{
1886 clientReplyContext *me = static_cast<clientReplyContext *>(voidMe);
0655fa4d 1887 me->processReplyAccessResult(rv);
4993f571 1888}
1889
1890void
0655fa4d 1891clientReplyContext::processReplyAccessResult(bool accessAllowed)
4993f571 1892{
62e76326 1893 debug(88, 2) ("The reply for %s %s is %s, because it matched '%s'\n",
1894 RequestMethodStr[http->request->method], http->uri,
1895 accessAllowed ? "ALLOWED" : "DENIED",
1896 AclMatchedName ? AclMatchedName : "NO ACL's");
1897 HttpReply *rep = holdingReply;
0655fa4d 1898 holdReply (NULL);
62e76326 1899
1900 if (!accessAllowed && rep->sline.status != HTTP_FORBIDDEN
0655fa4d 1901 && !alwaysAllowResponse(rep->sline.status)) {
62e76326 1902 /* the if above is slightly broken, but there is no way
1903 * to tell if this is a squid generated error page, or one from
1904 * upstream at this point. */
1905 ErrorState *err;
0185bd6f 1906 err_type page_id;
1907 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
1908
1909 if (page_id == ERR_NONE)
1910 page_id = ERR_ACCESS_DENIED;
1911
62e76326 1912 err =
0185bd6f 1913 clientBuildError(page_id, HTTP_FORBIDDEN, NULL,
a2ac85d9 1914 http->getConn().getRaw() != NULL ? &http->getConn()->peer.sin_addr : &no_addr,
62e76326 1915 http->request);
0185bd6f 1916
86a2f789 1917 removeClientStoreReference(&sc, http);
0185bd6f 1918
0655fa4d 1919 startError(err);
0185bd6f 1920
06a5ae20 1921 delete rep;
0185bd6f 1922
c2cf9bea 1923 http->logType = LOG_TCP_DENIED_REPLY;
0185bd6f 1924
62e76326 1925 return;
1926 }
1927
0976f8db 1928 /* Ok, the reply is allowed, */
1929 http->loggingEntry(http->storeEntry());
1930
62e76326 1931 ssize_t body_size = reqofs - rep->hdr_sz;
0976f8db 1932
62e76326 1933 assert(body_size >= 0);
0976f8db 1934
62e76326 1935 debug(88,3)
0655fa4d 1936 ("clientReplyContext::sendMoreData: Appending %d bytes after %d bytes of headers\n",
62e76326 1937 (int) body_size, rep->hdr_sz);
0976f8db 1938
43ae1d95 1939#if ESI
62e76326 1940
43ae1d95 1941 if (http->flags.accel && rep->sline.status != HTTP_FORBIDDEN &&
0655fa4d 1942 !alwaysAllowResponse(rep->sline.status) &&
43ae1d95 1943 esiEnableProcessing(rep)) {
1944 debug(88, 2) ("Enabling ESI processing for %s\n", http->uri);
1945 clientStreamInsertHead(&http->client_stream, esiStreamRead,
1946 esiProcessStream, esiStreamDetach, esiStreamStatus, NULL);
1947 }
1948
1949#endif
5ef38a13 1950
1951#if ICAP_CLIENT_RESPMOD_POSTCACHE
1952
1953 debug(88, 0) ("Enabling ICAP processing for %s\n", http->uri);
1954
1955 clientStreamInsertHead(&http->client_stream, icapclientStreamRead,
1956 icapclientProcessStream,
1957 icapclientStreamDetach,
1958 icapclientStreamStatus,
1959 NULL);
1960
1961#endif
1962
62e76326 1963 if (http->request->method == METHOD_HEAD) {
1964 /* do not forward body for HEAD replies */
1965 body_size = 0;
1966 http->flags.done_copying = 1;
1967 flags.complete = 1;
1968 }
1969
1970 assert (!flags.headersSent);
1971 flags.headersSent = true;
1972
1973 StoreIOBuffer tempBuffer;
1974 char *buf = next()->readBuffer.data;
1975 char *body_buf = buf + rep->hdr_sz;
1976
49ea0125 1977 //Server side may disable ranges under some circumstances.
1978
1979 if ((!http->request->range))
1980 next()->readBuffer.offset = 0;
1981
62e76326 1982 if (next()->readBuffer.offset != 0) {
1983 if (next()->readBuffer.offset > body_size) {
1984 /* Can't use any of the body we recieved. send nothing */
1985 tempBuffer.length = 0;
1986 tempBuffer.data = NULL;
1987 } else {
1988 tempBuffer.length = body_size - next()->readBuffer.offset;
1989 tempBuffer.data = body_buf + next()->readBuffer.offset;
1990 }
1991 } else {
1992 tempBuffer.length = body_size;
1993 tempBuffer.data = body_buf;
1994 }
1995
0655fa4d 1996 /* TODO??: move the data in the buffer back by the request header size */
62e76326 1997 clientStreamCallback((clientStreamNode *)http->client_stream.head->data,
1998 http, rep, tempBuffer);
1999
2000 return;
4993f571 2001}
2002
528b2c61 2003void
2004clientReplyContext::sendMoreData (StoreIOBuffer result)
2005{
50c09fc4 2006 if (deleting)
2007 return;
2008
86a2f789 2009 StoreEntry *entry = http->storeEntry();
50c09fc4 2010
a2ac85d9 2011 ConnStateData::Pointer conn = http->getConn();
50c09fc4 2012
a2ac85d9 2013 int fd = conn.getRaw() != NULL ? conn->fd : -1;
50c09fc4 2014
528b2c61 2015 char *buf = next()->readBuffer.data;
50c09fc4 2016
c8be6d7b 2017 char *body_buf = buf;
edce4d98 2018
62e76326 2019 /* This is always valid until we get the headers as metadata from
528b2c61 2020 * storeClientCopy.
2021 * Then it becomes reqofs == next->readBuffer.offset()
af29fa86 2022 */
528b2c61 2023 assert(reqofs == 0 || flags.storelogiccomplete);
af29fa86 2024
528b2c61 2025 if (flags.headersSent && buf != result.data) {
62e76326 2026 /* we've got to copy some data */
2027 assert(result.length <= next()->readBuffer.length);
2028 xmemcpy(buf, result.data, result.length);
2029 body_buf = buf;
528b2c61 2030 } else if (!flags.headersSent &&
62e76326 2031 buf + reqofs !=result.data) {
2032 /* we've got to copy some data */
2033 assert(result.length + reqofs <= next()->readBuffer.length);
2034 xmemcpy(buf + reqofs, result.data, result.length);
2035 body_buf = buf;
edce4d98 2036 }
62e76326 2037
edce4d98 2038 /* We've got the final data to start pushing... */
528b2c61 2039 flags.storelogiccomplete = 1;
edce4d98 2040
4993f571 2041 reqofs += result.length;
2042
2043 assert(reqofs <= HTTP_REQBUF_SZ || flags.headersSent);
62e76326 2044
edce4d98 2045 assert(http->request != NULL);
62e76326 2046
edce4d98 2047 /* ESI TODO: remove this assert once everything is stable */
2048 assert(http->client_stream.head->data
62e76326 2049 && cbdataReferenceValid(http->client_stream.head->data));
528b2c61 2050
2051 makeThisHead();
62e76326 2052
0655fa4d 2053 debug(88, 5) ("clientReplyContext::sendMoreData: %s, %d bytes (%u new bytes)\n",
62e76326 2054 http->uri, (int) reqofs, (unsigned int)result.length);
2055
0655fa4d 2056 debug(88, 5) ("clientReplyContext::sendMoreData: FD %d '%s', out.offset=%ld \n",
62e76326 2057 fd, storeUrl(entry), (long int) http->out.offset);
528b2c61 2058
edce4d98 2059 /* update size of the request */
4993f571 2060 reqsize = reqofs;
62e76326 2061
e429f975 2062 if (http->request->flags.resetTCP()) {
62e76326 2063 /* yuck. FIXME: move to client_side.c */
2064
2065 if (fd != -1)
2066 comm_reset_close(fd);
2067
2068 return;
528b2c61 2069 }
62e76326 2070
4993f571 2071 if (errorInStream(result, reqofs)) {
62e76326 2072 sendStreamError(result);
2073 return;
edce4d98 2074 }
528b2c61 2075
2076 if (flags.headersSent) {
62e76326 2077 pushStreamData (result, buf);
2078 return;
edce4d98 2079 }
62e76326 2080
0655fa4d 2081 buildReply(buf, reqofs);
4993f571 2082 ssize_t body_size = reqofs;
62e76326 2083
0655fa4d 2084 if (holdingReply) {
21b92762 2085
2086 /* handle headers */
2087
2088 if (Config.onoff.log_mime_hdrs) {
2089 size_t k;
2090
2091 if ((k = headersEnd(buf, reqofs))) {
2092 safe_free(http->al.headers.reply);
2093 http->al.headers.reply = (char *)xcalloc(k + 1, 1);
2094 xstrncpy(http->al.headers.reply, buf, k);
2095 }
2096 }
2097
62e76326 2098 holdingBuffer = result;
2099 processReplyAccess ();
2100 return;
43ae1d95 2101
4993f571 2102 } else if (reqofs < HTTP_REQBUF_SZ && entry->store_status == STORE_PENDING) {
0655fa4d 2103 waitForMoreData();
62e76326 2104 return;
528b2c61 2105 } else if (http->request->method == METHOD_HEAD) {
62e76326 2106 /*
2107 * If we are here, then store_status == STORE_OK and it
2108 * seems we have a HEAD repsponse which is missing the
2109 * empty end-of-headers line (home.mira.net, phttpd/0.99.72
0655fa4d 2110 * does this). Because buildReply() fails we just
62e76326 2111 * call this reply a body, set the done_copying flag and
2112 * continue...
2113 */
2114 /* RBC: Note that this is seriously broken, as we *need* the
2115 * metadata to allow further client modules to work. As such
2116 * webservers are seriously broken, this is probably not
2117 * going to get fixed.. perhapos we should remove it?
2118 */
2119 debug (88,0)("Broken head response - probably phttpd/0.99.72\n");
2120 http->flags.done_copying = 1;
2121 flags.complete = 1;
2122 /*
2123 * And as this is a malformed HTTP reply we cannot keep
2124 * the connection persistent
2125 */
2126 http->request->flags.proxy_keepalive = 0;
2127
62e76326 2128 assert(body_buf && body_size);
0655fa4d 2129 StoreIOBuffer tempBuffer (body_size, 0 ,body_buf);
62e76326 2130 clientStreamCallback((clientStreamNode *)http->client_stream.head->data,
2131 http, NULL, tempBuffer);
528b2c61 2132 } else {
62e76326 2133 debug (88,0)("clientReplyContext::sendMoreData: Unable to parse reply headers within a single HTTP_REQBUF_SZ length buffer\n");
2134 StoreIOBuffer tempBuffer;
2135 tempBuffer.flags.error = 1;
0655fa4d 2136 /* XXX FIXME: make an html error page here */
62e76326 2137 sendStreamError(tempBuffer);
2138 return;
edce4d98 2139 }
62e76326 2140
528b2c61 2141 fatal ("clientReplyContext::sendMoreData: Unreachable code reached \n");
edce4d98 2142}
2143
62e76326 2144
edce4d98 2145
2146/* Using this breaks the client layering just a little!
2147 */
0655fa4d 2148void
2149clientReplyContext::createStoreEntry(method_t m, request_flags flags)
edce4d98 2150{
0655fa4d 2151 assert(http != NULL);
edce4d98 2152 /*
2153 * For erroneous requests, we might not have a h->request,
2154 * so make a fake one.
2155 */
62e76326 2156
0655fa4d 2157 if (http->request == NULL)
6dd9f4bd 2158 http->request = HTTPMSGLOCK(new HttpRequest(m, PROTO_NONE, null_string));
62e76326 2159
0655fa4d 2160 StoreEntry *e = storeCreateEntry(http->uri, http->log_uri, flags, m);
62e76326 2161
0655fa4d 2162 sc = storeClientListAdd(e, this);
62e76326 2163
edce4d98 2164#if DELAY_POOLS
62e76326 2165
0655fa4d 2166 sc->setDelayId(DelayId::DelayClient(http));
62e76326 2167
edce4d98 2168#endif
62e76326 2169
0655fa4d 2170 reqofs = 0;
62e76326 2171
0655fa4d 2172 reqsize = 0;
62e76326 2173
edce4d98 2174 /* I don't think this is actually needed! -- adrian */
0655fa4d 2175 /* http->reqbuf = http->norm_reqbuf; */
2176 // assert(http->reqbuf == http->norm_reqbuf);
edce4d98 2177 /* The next line is illegal because we don't know if the client stream
2178 * buffers have been set up
2179 */
0655fa4d 2180 // storeClientCopy(http->sc, e, 0, HTTP_REQBUF_SZ, http->reqbuf,
2181 // SendMoreData, this);
edce4d98 2182 /* So, we mark the store logic as complete */
0655fa4d 2183 this->flags.storelogiccomplete = 1;
62e76326 2184
edce4d98 2185 /* and get the caller to request a read, from whereever they are */
62e76326 2186 /* NOTE: after ANY data flows down the pipe, even one step,
edce4d98 2187 * this function CAN NOT be used to manage errors
2188 */
86a2f789 2189 http->storeEntry(e);
edce4d98 2190}
2191
2192ErrorState *
2193clientBuildError(err_type page_id, http_status status, char const *url,
62e76326 2194
ddfcbc22 2195 struct IN_ADDR * src_addr, HttpRequest * request)
edce4d98 2196{
2197 ErrorState *err = errorCon(page_id, status);
2198 err->src_addr = *src_addr;
62e76326 2199
edce4d98 2200 if (url)
62e76326 2201 err->url = xstrdup(url);
2202
edce4d98 2203 if (request)
6dd9f4bd 2204 err->request = HTTPMSGLOCK(request);
62e76326 2205
edce4d98 2206 return err;
2207}