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