3
* $Id: client_side_reply.cc,v 1.114 2006/10/02 11:35:39 adrian Exp $
5
* DEBUG: section 88 Client-side Reply Routines
6
* AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c)
8
* SQUID Web Proxy Cache http://www.squid-cache.org/
9
* ----------------------------------------------------------
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.
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.
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.
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.
37
#include "client_side_reply.h"
38
#include "errorpage.h"
39
#include "StoreClient.h"
41
#include "HttpReply.h"
42
#include "HttpRequest.h"
45
#include "clientStream.h"
46
#include "AuthUserRequest.h"
50
#if ICAP_CLIENT_RESPMOD_POSTCACHE
51
#include "ICAPClient.h"
53
#include "MemObject.h"
54
#include "ACLChecklist.h"
57
#include "DelayPools.h"
59
#include "client_side.h"
60
#include "SquidTime.h"
62
CBDATA_CLASS_INIT(clientReplyContext);
65
extern "C" CSS clientReplyStatus;
66
extern ErrorState *clientBuildError(err_type, http_status, char const *,
68
struct IN_ADDR *, HttpRequest *);
72
clientReplyContext::~clientReplyContext()
75
/* This may trigger a callback back into SendMoreData as the cbdata
78
removeClientStoreReference(&sc, http);
79
/* old_entry might still be set if we didn't yet get the reply
80
* code in HandleIMSReply() */
81
removeStoreReference(&old_sc, &old_entry);
82
safe_free(tempBuffer.data);
83
cbdataReferenceDone(http);
87
clientReplyContext::clientReplyContext(ClientHttpRequest *clientContext) : http (cbdataReference(clientContext)), old_entry (NULL), old_sc(NULL), deleting(false)
90
/* create an error in the store awaiting the client side to read it. */
91
/* This may be better placed in the clientStream logic, but it has not been
95
clientReplyContext::setReplyToError(
96
err_type err, http_status status, method_t method, char const *uri,
98
struct IN_ADDR *addr, HttpRequest * failedrequest, char *unparsedrequest,
99
auth_user_request_t * auth_user_request)
101
ErrorState *errstate =
102
clientBuildError(err, status, uri, addr, failedrequest);
105
errstate->request_hdrs = xstrdup(unparsedrequest);
107
if (status == HTTP_NOT_IMPLEMENTED && http->request)
108
/* prevent confusion over whether we default to persistent or not */
109
http->request->flags.proxy_keepalive = 0;
111
http->al.http.code = errstate->httpStatus;
113
createStoreEntry(method, request_flags());
115
if (auth_user_request)
117
errstate->auth_user_request = auth_user_request;
119
errstate->auth_user_request->lock()
124
assert(errstate->callback_data == NULL);
125
errorAppendEntry(http->storeEntry(), errstate);
126
/* Now the caller reads to get this */
130
clientReplyContext::removeStoreReference(store_client ** scp,
134
store_client *sc = *scp;
136
if ((e = *ep) != NULL) {
138
storeUnregister(sc, e, this);
145
clientReplyContext::removeClientStoreReference(store_client **scp, ClientHttpRequest *http)
147
StoreEntry *reference = http->storeEntry();
148
removeStoreReference(scp, &reference);
149
http->storeEntry(reference);
153
clientReplyContext::operator new (size_t byteCount)
155
/* derived classes with different sizes must implement their own new */
156
assert (byteCount == sizeof (clientReplyContext));
157
CBDATA_INIT_TYPE(clientReplyContext);
158
return cbdataAlloc(clientReplyContext);
162
clientReplyContext::operator delete (void *address)
164
clientReplyContext * tmp = (clientReplyContext *)address;
169
clientReplyContext::saveState()
171
assert(old_sc == NULL);
172
debug(88, 3)("clientReplyContext::saveState: saving store context\n");
173
old_entry = http->storeEntry();
175
old_reqsize = reqsize;
176
tempBuffer.offset = reqofs;
177
/* Prevent accessing the now saved entries */
178
http->storeEntry(NULL);
185
clientReplyContext::restoreState()
187
assert(old_sc != NULL);
188
debug(88, 3)("clientReplyContext::restoreState: Restoring store context\n");
189
removeClientStoreReference(&sc, http);
190
http->storeEntry(old_entry);
192
reqsize = old_reqsize;
193
reqofs = tempBuffer.offset;
194
/* Prevent accessed the old saved entries */
198
tempBuffer.offset = 0;
202
clientReplyContext::startError(ErrorState * err)
204
createStoreEntry(http->request->method, request_flags());
205
triggerInitialStoreRead();
206
errorAppendEntry(http->storeEntry(), err);
210
clientReplyContext::getNextNode() const
212
return (clientStreamNode *)ourNode->node.next->data;
215
/* This function is wrong - the client parameters don't include the
219
clientReplyContext::triggerInitialStoreRead()
221
/* when confident, 0 becomes reqofs, and then this factors into
225
StoreIOBuffer tempBuffer (next()->readBuffer.length, 0, next()->readBuffer.data);
226
storeClientCopy(sc, http->storeEntry(), tempBuffer, SendMoreData, this);
229
/* there is an expired entry in the store.
230
* setup a temporary buffer area and perform an IMS to the origin
233
clientReplyContext::processExpired()
235
char *url = http->uri;
236
StoreEntry *entry = NULL;
237
debug(88, 3)("clientReplyContext::processExpired: '%s'", http->uri);
238
assert(http->storeEntry()->lastmod >= 0);
240
* check if we are allowed to contact other servers
241
* @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return
242
* a stale entry *if* it matches client requirements
245
if (http->onlyIfCached()) {
246
processOnlyIfCachedMiss();
250
http->request->flags.refresh = 1;
251
#if STORE_CLIENT_LIST_DEBUG
252
/* Prevent a race with the store client memory free routines
254
assert(storeClientIsThisAClient(sc, this));
256
/* Prepare to make a new temporary request */
258
entry = storeCreateEntry(url,
259
http->log_uri, http->request->flags, http->request->method);
260
/* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
261
sc = storeClientListAdd(entry, this);
263
/* delay_id is already set on original store client */
264
sc->setDelayId(DelayId::DelayClient(http));
267
http->request->lastmod = old_entry->lastmod;
268
debug(88, 5)("clientReplyContext::processExpired : lastmod %ld",
269
(long int) entry->lastmod);
270
http->storeEntry(entry);
271
assert(http->out.offset == 0);
274
* A refcounted pointer so that FwdState stays around as long as
275
* this clientReplyContext does
277
FwdState::fwdStart(http->getConn().getRaw() != NULL ? http->getConn()->fd : -1,
280
/* Register with storage manager to receive updates when data comes in. */
282
if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
283
debug(88, 0) ("clientReplyContext::processExpired: Found ENTRY_ABORTED object");
286
/* start counting the length from 0 */
287
StoreIOBuffer tempBuffer(HTTP_REQBUF_SZ, 0, tempbuf);
288
storeClientCopy(sc, entry, tempBuffer, HandleIMSReply, this);
294
clientReplyContext::sendClientUpstreamResponse()
296
StoreIOBuffer tempresult;
297
removeStoreReference(&old_sc, &old_entry);
298
/* here the data to send is the data we just recieved */
299
tempBuffer.offset = 0;
301
/* sendMoreData tracks the offset as well.
302
* Force it back to zero */
304
assert(!EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED));
305
/* TODO: provide sendMoreData with the ready parsed reply */
306
tempresult.length = reqsize;
307
tempresult.data = tempbuf;
308
sendMoreData(tempresult);
312
clientReplyContext::HandleIMSReply(void *data, StoreIOBuffer result)
314
clientReplyContext *context = (clientReplyContext *)data;
315
context->handleIMSReply(result);
319
clientReplyContext::sendClientOldEntry()
321
/* Get the old request back */
323
/* here the data to send is in the next nodes buffers already */
324
assert(!EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED));
325
/* sendMoreData tracks the offset as well.
326
* Force it back to zero */
328
StoreIOBuffer tempresult (reqsize, reqofs, next()->readBuffer.data);
329
sendMoreData(tempresult);
332
/* This is the workhorse of the HandleIMSReply callback.
334
* It is called when we've got data back from the origin following our
335
* IMS request to revalidate a stale entry.
338
clientReplyContext::handleIMSReply(StoreIOBuffer result)
343
debug(88, 3) ("handleIMSReply: %s, %lu bytes\n",
344
storeUrl(http->storeEntry()),
345
(long unsigned) result.length);
347
if (http->storeEntry() == NULL)
350
if (result.flags.error && !EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
353
/* update size of the request */
354
reqsize = result.length + reqofs;
356
const http_status status = http->storeEntry()->getReply()->sline.status;
358
// request to origin was aborted
359
if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) {
360
debug(88, 3) ("handleIMSReply: request to origin aborted '%s', sending old entry to client\n", storeUrl(http->storeEntry()));
361
http->logType = LOG_TCP_REFRESH_FAIL;
362
sendClientOldEntry();
365
// we have a partial reply from the origin
366
else if (STORE_PENDING == http->storeEntry()->store_status && 0 == status) {
367
// header is too large, send old entry
369
if (reqsize >= HTTP_REQBUF_SZ) {
370
debug(88, 3) ("handleIMSReply: response from origin is too large '%s', sending old entry to client\n", storeUrl(http->storeEntry()));
371
http->logType = LOG_TCP_REFRESH_FAIL;
372
sendClientOldEntry();
375
// everything looks fine, we're just waiting for more data
377
debug(88, 3) ("handleIMSReply: incomplete headers for '%s', waiting for more data\n", storeUrl(http->storeEntry()));
383
// we have a reply from the origin
385
HttpReply *old_rep = (HttpReply *) old_entry->getReply();
387
// origin replied 304
389
if (status == HTTP_NOT_MODIFIED) {
390
http->logType = LOG_TCP_REFRESH_UNMODIFIED;
392
// update headers on existing entry
393
HttpReply *old_rep = (HttpReply *) old_entry->getReply();
394
old_rep->updateOnNotModified(http->storeEntry()->getReply());
395
storeTimestampsSet(old_entry);
397
// if client sent IMS
399
if (http->request->flags.ims) {
400
// forward the 304 from origin
401
debug(88,3) ("handleIMSReply: origin replied 304, revalidating existing entry and forwarding 304 to client\n");
402
sendClientUpstreamResponse();
404
// send existing entry, it's still valid
405
debug(88,3) ("handleIMSReply: origin replied 304, revalidating existing entry and sending %d to client\n", old_rep->sline.status);
406
sendClientOldEntry();
410
// origin replied with a non-error code
411
else if (status > HTTP_STATUS_NONE && status < HTTP_INTERNAL_SERVER_ERROR) {
412
// forward response from origin
413
http->logType = LOG_TCP_REFRESH_MODIFIED;
414
debug(88,3) ("handleIMSReply: origin replied %d, replacing existing entry and forwarding to client\n", status);
415
sendClientUpstreamResponse();
418
// origin replied with an error
420
// ignore and let client have old entry
421
http->logType = LOG_TCP_REFRESH_FAIL;
422
debug(88,3) ("handleIMSReply: origin replied with error %d, sending old entry (%d) to client\n", status, old_rep->sline.status);
423
sendClientOldEntry();
428
extern "C" CSR clientGetMoreData;
429
extern "C" CSD clientReplyDetach;
432
* clientCacheHit should only be called until the HTTP reply headers
433
* have been parsed. Normally this should be a single call, but
434
* it might take more than one. As soon as we have the headers,
435
* we hand off to clientSendMoreData, processExpired, or
439
clientReplyContext::CacheHit(void *data, StoreIOBuffer result)
441
clientReplyContext *context = (clientReplyContext *)data;
442
context->cacheHit (result);
446
clientReplyContext::cacheHit(StoreIOBuffer result)
451
StoreEntry *e = http->storeEntry();
453
HttpRequest *r = http->request;
455
debug(88, 3) ("clientCacheHit: %s, %ud bytes\n", http->uri, (unsigned int)result.length);
457
if (http->storeEntry() == NULL) {
458
debug(88, 3) ("clientCacheHit: request aborted\n");
460
} else if (result.flags.error) {
461
/* swap in failure */
462
debug(88, 3) ("clientCacheHit: swapin failure for %s\n", http->uri);
463
http->logType = LOG_TCP_SWAPFAIL_MISS;
464
removeClientStoreReference(&sc, http);
469
if (result.length == 0) {
470
/* the store couldn't get enough data from the file for us to id the
473
/* treat as a miss */
474
http->logType = LOG_TCP_MISS;
479
assert(!EBIT_TEST(e->flags, ENTRY_ABORTED));
480
/* update size of the request */
481
reqsize = result.length + reqofs;
483
if (e->getReply()->sline.status == 0) {
485
* we don't have full reply headers yet; either wait for more or
486
* punt to clientProcessMiss.
489
if (e->mem_status == IN_MEMORY || e->store_status == STORE_OK) {
491
} else if (result.length + reqofs >= HTTP_REQBUF_SZ
492
&& http->out.offset == 0) {
495
debug(88, 3) ("clientCacheHit: waiting for HTTP reply headers\n");
496
reqofs += result.length;
497
assert(reqofs <= HTTP_REQBUF_SZ);
498
/* get the next users' buffer */
499
StoreIOBuffer tempBuffer;
500
tempBuffer.offset = http->out.offset + reqofs;
501
tempBuffer.length = next()->readBuffer.length - reqofs;
502
tempBuffer.data = next()->readBuffer.data + reqofs;
503
storeClientCopy(sc, e,
504
tempBuffer, CacheHit, this);
511
* Got the headers, now grok them
513
assert(http->logType == LOG_TCP_HIT);
515
switch (varyEvaluateMatch(e, r)) {
518
/* No variance detected. Continue as normal */
522
/* This is the correct entity for this request. Continue */
523
debug(88, 2) ("clientProcessHit: Vary MATCH!\n");
527
/* This is not the correct entity for this request. We need
528
* to requery the cache.
530
removeClientStoreReference(&sc, http);
532
/* Note: varyEvalyateMatch updates the request with vary information
533
* so we only get here once. (it also takes care of cancelling loops)
535
debug(88, 2) ("clientProcessHit: Vary detected!\n");
536
clientGetMoreData(ourNode, http);
540
/* varyEvaluateMatch found a object loop. Process as miss */
541
debug(88, 1) ("clientProcessHit: Vary object loop!\n");
546
if (r->method == METHOD_PURGE) {
547
removeClientStoreReference(&sc, http);
553
if (storeCheckNegativeHit(e)
555
&& !r->flags.nocache_hack
558
http->logType = LOG_TCP_NEGATIVE_HIT;
559
sendMoreData(result);
560
} else if (!Config.onoff.offline && refreshCheckHTTP(e, r) && !http->flags.internal) {
561
debug(88, 5) ("clientCacheHit: in refreshCheck() block\n");
563
* We hold a stale copy; it needs to be validated
566
* The 'need_validation' flag is used to prevent forwarding
567
* loops between siblings. If our copy of the object is stale,
568
* then we should probably only use parents for the validation
569
* request. Otherwise two siblings could generate a loop if
570
* both have a stale version of the object.
572
r->flags.need_validation = 1;
574
if (e->lastmod < 0) {
576
* Previous reply didn't have a Last-Modified header,
577
* we cannot revalidate it.
579
http->logType = LOG_TCP_MISS;
581
} else if (r->flags.nocache) {
583
* This did not match a refresh pattern that overrides no-cache
584
* we should honour the client no-cache header.
586
http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
588
} else if (r->protocol == PROTO_HTTP) {
590
* Object needs to be revalidated
591
* XXX This could apply to FTP as well, if Last-Modified is known.
596
* We don't know how to re-validate other protocols. Handle
597
* them as if the object has expired.
599
http->logType = LOG_TCP_MISS;
602
} else if (r->flags.ims) {
604
* Handle If-Modified-Since requests from the client
607
if (e->getReply()->sline.status != HTTP_OK) {
608
debug(88, 4) ("clientCacheHit: Reply code %d != 200\n",
609
e->getReply()->sline.status);
610
http->logType = LOG_TCP_MISS;
612
} else if (e->modifiedSince(http->request)) {
613
http->logType = LOG_TCP_IMS_HIT;
614
sendMoreData(result);
616
time_t const timestamp = e->timestamp;
617
HttpReply *temprep = e->getReply()->make304();
618
http->logType = LOG_TCP_IMS_HIT;
619
removeClientStoreReference(&sc, http);
620
createStoreEntry(http->request->method,
622
e = http->storeEntry();
624
* Copy timestamp from the original entry so the 304
625
* reply has a meaningful Age: header.
627
e->timestamp = timestamp;
628
e->replaceHttpReply(temprep);
630
/* TODO: why put this in the store and then serialise it and then parse it again.
631
* Simply mark the request complete in our context and
632
* write the reply struct to the client side
634
triggerInitialStoreRead();
638
* plain ol' cache hit
641
if (e->store_status != STORE_OK)
642
http->logType = LOG_TCP_MISS;
643
else if (e->mem_status == IN_MEMORY)
644
http->logType = LOG_TCP_MEM_HIT;
645
else if (Config.onoff.offline)
646
http->logType = LOG_TCP_OFFLINE_HIT;
648
sendMoreData(result);
653
* Prepare to fetch the object as it's a cache miss of some kind.
656
clientReplyContext::processMiss()
658
char *url = http->uri;
659
HttpRequest *r = http->request;
660
ErrorState *err = NULL;
661
debug(88, 4) ("clientProcessMiss: '%s %s'\n",
662
RequestMethodStr[r->method], url);
664
* We might have a left-over StoreEntry from a failed cache hit
668
if (http->storeEntry()) {
669
if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
670
debug(88, 0) ("clientProcessMiss: miss on a special object (%s).\n",
672
debug(88, 0) ("\tlog_type = %s\n", log_tags[http->logType]);
673
storeEntryDump(http->storeEntry(), 1);
676
removeClientStoreReference(&sc, http);
679
if (r->method == METHOD_PURGE) {
684
if (http->onlyIfCached()) {
685
processOnlyIfCachedMiss();
690
* Deny loops when running in accelerator/transproxy mode.
692
if (http->flags.accel && r->flags.loopdetect) {
693
http->al.http.code = HTTP_FORBIDDEN;
695
clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL,
696
&http->getConn()->peer.sin_addr, http->request);
697
createStoreEntry(r->method, request_flags());
698
errorAppendEntry(http->storeEntry(), err);
699
triggerInitialStoreRead();
702
assert(http->out.offset == 0);
703
createStoreEntry(r->method, r->flags);
704
triggerInitialStoreRead();
706
if (http->redirect.status) {
707
HttpReply *rep = new HttpReply;
708
#if LOG_TCP_REDIRECTS
710
http->logType = LOG_TCP_REDIRECT;
713
storeReleaseRequest(http->storeEntry());
714
rep->redirect(http->redirect.status, http->redirect.location);
715
http->storeEntry()->replaceHttpReply(rep);
716
http->storeEntry()->complete();
720
if (http->flags.internal)
721
r->protocol = PROTO_INTERNAL;
723
FwdState::fwdStart(http->getConn().getRaw() != NULL ? http->getConn()->fd : -1,
730
* client issued a request with an only-if-cached cache-control directive;
731
* we did not find a cached object that can be returned without
732
* contacting other servers;
733
* respond with a 504 (Gateway Timeout) as suggested in [RFC 2068]
736
clientReplyContext::processOnlyIfCachedMiss()
738
ErrorState *err = NULL;
739
debug(88, 4) ("clientProcessOnlyIfCachedMiss: '%s %s'\n",
740
RequestMethodStr[http->request->method], http->uri);
741
http->al.http.code = HTTP_GATEWAY_TIMEOUT;
742
err = clientBuildError(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT, NULL,
743
&http->getConn()->peer.sin_addr, http->request);
744
removeClientStoreReference(&sc, http);
749
clientReplyContext::purgeRequestFindObjectToPurge()
751
/* Try to find a base entry */
752
http->flags.purging = 1;
754
StoreEntry::getPublicByRequestMethod(this, http->request, METHOD_GET);
758
clientReplyContext::created(StoreEntry *newEntry)
760
if (lookingforstore == 1)
761
purgeFoundGet(newEntry);
762
else if (lookingforstore == 2)
763
purgeFoundHead(newEntry);
764
else if (lookingforstore == 3)
765
purgeDoPurgeGet(newEntry);
766
else if (lookingforstore == 4)
767
purgeDoPurgeHead(newEntry);
768
else if (lookingforstore == 5)
769
identifyFoundObject(newEntry);
773
clientReplyContext::purgeFoundGet(StoreEntry *newEntry)
775
if (newEntry->isNull()) {
777
StoreEntry::getPublicByRequestMethod(this, http->request, METHOD_HEAD);
779
purgeFoundObject (newEntry);
783
clientReplyContext::purgeFoundHead(StoreEntry *newEntry)
785
if (newEntry->isNull())
788
purgeFoundObject (newEntry);
792
clientReplyContext::purgeFoundObject(StoreEntry *entry)
794
assert (entry && !entry->isNull());
796
if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
797
http->logType = LOG_TCP_DENIED;
799
clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL,
800
&http->getConn()->peer.sin_addr, http->request);
805
StoreIOBuffer tempBuffer;
806
/* Swap in the metadata */
807
http->storeEntry(entry);
809
http->storeEntry()->lock()
812
storeCreateMemObject(http->storeEntry(), http->uri, http->log_uri);
814
http->storeEntry()->mem_obj->method = http->request->method;
816
sc = storeClientListAdd(http->storeEntry(), this);
818
http->logType = LOG_TCP_HIT;
822
tempBuffer.offset = http->out.offset;
824
tempBuffer.length = next()->readBuffer.length;
826
tempBuffer.data = next()->readBuffer.data;
828
storeClientCopy(sc, http->storeEntry(),
829
tempBuffer, CacheHit, this);
833
clientReplyContext::purgeRequest()
835
debug(88, 3) ("Config2.onoff.enable_purge = %d\n",
836
Config2.onoff.enable_purge);
838
if (!Config2.onoff.enable_purge) {
839
http->logType = LOG_TCP_DENIED;
841
clientBuildError(ERR_ACCESS_DENIED, HTTP_FORBIDDEN, NULL,
842
&http->getConn()->peer.sin_addr, http->request);
847
/* Release both IP cache */
848
ipcacheInvalidate(http->request->host);
850
if (!http->flags.purging)
851
purgeRequestFindObjectToPurge();
857
clientReplyContext::purgeDoMissPurge()
859
http->logType = LOG_TCP_MISS;
861
StoreEntry::getPublicByRequestMethod(this,http->request, METHOD_GET);
865
clientReplyContext::purgeDoPurgeGet(StoreEntry *newEntry)
868
/* Move to new() when that is created */
869
purgeStatus = HTTP_NOT_FOUND;
871
if (!newEntry->isNull()) {
872
/* Release the cached URI */
873
debug(88, 4) ("clientPurgeRequest: GET '%s'\n", storeUrl(newEntry));
875
purgeStatus = HTTP_OK;
879
StoreEntry::getPublicByRequestMethod(this, http->request, METHOD_HEAD);
883
clientReplyContext::purgeDoPurgeHead(StoreEntry *newEntry)
885
if (newEntry && !newEntry->isNull()) {
886
debug(88, 4) ("clientPurgeRequest: HEAD '%s'\n", storeUrl(newEntry));
888
purgeStatus = HTTP_OK;
891
/* And for Vary, release the base URI if none of the headers was included in the request */
893
if (http->request->vary_headers
894
&& !strstr(http->request->vary_headers, "=")) {
895
StoreEntry *entry = storeGetPublic(urlCanonical(http->request), METHOD_GET);
898
debug(88, 4) ("clientPurgeRequest: Vary GET '%s'\n",
901
purgeStatus = HTTP_OK;
904
entry = storeGetPublic(urlCanonical(http->request), METHOD_HEAD);
907
debug(88, 4) ("clientPurgeRequest: Vary HEAD '%s'\n",
910
purgeStatus = HTTP_OK;
915
* Make a new entry to hold the reply to be written
918
/* FIXME: This doesn't need to go through the store. Simply
919
* push down the client chain
921
createStoreEntry(http->request->method, request_flags());
923
triggerInitialStoreRead();
925
HttpReply *rep = new HttpReply;
927
HttpVersion version(1,0);
929
rep->setHeaders(version, purgeStatus, NULL, NULL, 0, 0, -1);
931
http->storeEntry()->replaceHttpReply(rep);
933
http->storeEntry()->complete();
937
clientReplyContext::traceReply(clientStreamNode * node)
939
clientStreamNode *next = (clientStreamNode *)node->node.next->data;
940
StoreIOBuffer tempBuffer;
941
assert(http->request->max_forwards == 0);
942
createStoreEntry(http->request->method, request_flags());
943
tempBuffer.offset = next->readBuffer.offset + headers_sz;
944
tempBuffer.length = next->readBuffer.length;
945
tempBuffer.data = next->readBuffer.data;
946
storeClientCopy(sc, http->storeEntry(),
947
tempBuffer, SendMoreData, this);
948
storeReleaseRequest(http->storeEntry());
949
storeBuffer(http->storeEntry());
950
HttpReply *rep = new HttpReply;
951
HttpVersion version(1,0);
952
rep->setHeaders(version, HTTP_OK, NULL, "text/plain",
953
http->request->prefixLen(), 0, squid_curtime);
954
http->storeEntry()->replaceHttpReply(rep);
955
http->request->swapOut(http->storeEntry());
956
http->storeEntry()->complete();
959
#define SENDING_BODY 0
960
#define SENDING_HDRSONLY 1
962
clientReplyContext::checkTransferDone()
964
StoreEntry *entry = http->storeEntry();
970
* For now, 'done_copying' is used for special cases like
971
* Range and HEAD requests.
973
if (http->flags.done_copying)
977
* Handle STORE_OK objects.
978
* objectLen(entry) will be set proprely.
979
* RC: Does objectLen(entry) include the Headers?
982
if (entry->store_status == STORE_OK) {
983
return storeOKTransferDone();
985
return storeNotOKTransferDone();
990
clientReplyContext::storeOKTransferDone() const
992
if (http->out.offset >= objectLen(http->storeEntry()) - headers_sz)
999
clientReplyContext::storeNotOKTransferDone() const
1002
* Now, handle STORE_PENDING objects
1004
MemObject *mem = http->storeEntry()->mem_obj;
1005
assert(mem != NULL);
1006
assert(http->request != NULL);
1007
/* mem->reply was wrong because it uses the UPSTREAM header length!!! */
1008
HttpReply const *reply = mem->getReply();
1010
if (headers_sz == 0)
1011
/* haven't found end of headers yet */
1014
int sending = SENDING_BODY;
1016
if (reply->sline.status == HTTP_NO_CONTENT ||
1017
reply->sline.status == HTTP_NOT_MODIFIED ||
1018
reply->sline.status < HTTP_OK ||
1019
http->request->method == METHOD_HEAD)
1020
sending = SENDING_HDRSONLY;
1023
* Figure out how much data we are supposed to send.
1024
* If we are sending a body and we don't have a content-length,
1025
* then we must wait for the object to become STORE_OK.
1027
if (reply->content_length < 0)
1030
size_t expectedLength = http->out.headers_sz + reply->content_length;
1032
if (http->out.size < expectedLength)
1039
/* A write has completed, what is the next status based on the
1040
* canonical request data?
1041
* 1 something is wrong
1042
* 0 nothing is wrong.
1046
clientHttpRequestStatus(int fd, ClientHttpRequest const *http)
1048
#if SIZEOF_SIZE_T == 4
1050
if (http->out.size > 0x7FFF0000) {
1051
debug(88, 1) ("WARNING: closing FD %d to prevent counter overflow\n",
1053
debug(88, 1) ("\tclient %s\n",
1054
inet_ntoa(http->getConn().getRaw() != NULL ? http->getConn()->peer.sin_addr : no_addr));
1055
debug(88, 1) ("\treceived %d bytes\n", (int) http->out.size);
1056
debug(88, 1) ("\tURI %s\n", http->log_uri);
1061
#if SIZEOF_OFF_T == 4
1062
if (http->out.offset > 0x7FFF0000) {
1063
debug(88, 1) ("WARNING: closing FD %d to prevent counter overflow\n",
1065
debug(88, 1) ("\tclient %s\n",
1066
inet_ntoa(http->getConn().getRaw() != NULL ? http->getConn()->peer.sin_addr : no_addr));
1067
debug(88, 1) ("\treceived %d bytes (offset %d)\n", (int) http->out.size,
1068
(int) http->out.offset);
1069
debug(88, 1) ("\tURI %s\n", http->log_uri);
1078
* *http is a valid structure.
1079
* fd is either -1, or an open fd.
1081
* TODO: enumify this
1083
* This function is used by any http request sink, to determine the status
1086
clientStream_status_t
1087
clientReplyStatus(clientStreamNode * aNode, ClientHttpRequest * http)
1089
clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1091
assert (context->http == http);
1092
return context->replyStatus();
1095
clientStream_status_t
1096
clientReplyContext::replyStatus()
1099
/* Here because lower nodes don't need it */
1101
if (http->storeEntry() == NULL)
1102
return STREAM_FAILED; /* yuck, but what can we do? */
1104
if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
1105
/* TODO: Could upstream read errors (result.flags.error) be
1106
* lost, and result in undersize requests being considered
1107
* complete. Should we tcp reset such connections ?
1109
return STREAM_FAILED;
1111
if ((done = checkTransferDone()) != 0 || flags.complete) {
1112
debug(88, 5) ("clientReplyStatus: transfer is DONE\n");
1113
/* Ok we're finished, but how? */
1115
if (http->storeEntry()->getReply()->bodySize(http->request->method) < 0) {
1116
debug(88, 5) ("clientReplyStatus: closing, content_length < 0\n");
1117
return STREAM_FAILED;
1121
debug(88, 5) ("clientReplyStatus: closing, !done, but read 0 bytes\n");
1122
return STREAM_FAILED;
1125
if (!http->gotEnough()) {
1126
debug(88, 5) ("clientReplyStatus: client didn't get all it expected\n");
1127
return STREAM_UNPLANNED_COMPLETE;
1130
if (http->request->flags.proxy_keepalive) {
1131
debug(88, 5) ("clientReplyStatus: stream complete and can keepalive\n");
1132
return STREAM_COMPLETE;
1135
debug(88, 5) ("clientReplyStatus: stream was not expected to complete!\n");
1136
return STREAM_UNPLANNED_COMPLETE;
1139
if (http->isReplyBodyTooLarge(http->out.offset - 4096)) {
1140
/* 4096 is a margin for the HTTP headers included in out.offset */
1141
debug(88, 5) ("clientReplyStatus: client reply body is too large\n");
1142
return STREAM_FAILED;
1148
/* Responses with no body will not have a content-type header,
1149
* which breaks the rep_mime_type acl, which
1150
* coincidentally, is the most common acl for reply access lists.
1151
* A better long term fix for this is to allow acl matchs on the various
1152
* status codes, and then supply a default ruleset that puts these
1153
* codes before any user defines access entries. That way the user
1154
* can choose to block these responses where appropriate, but won't get
1155
* mysterious breakages.
1158
clientReplyContext::alwaysAllowResponse(http_status sline) const
1166
case HTTP_SWITCHING_PROTOCOLS:
1168
case HTTP_PROCESSING:
1170
case HTTP_NO_CONTENT:
1172
case HTTP_NOT_MODIFIED:
1184
* filters out unwanted entries from original reply header
1185
* adds extra entries if we have more info than origin server
1186
* adds Squid specific entries
1189
clientReplyContext::buildReplyHeader()
1191
HttpHeader *hdr = &reply->header;
1192
int is_hit = logTypeIsATcpHit(http->logType);
1193
HttpRequest *request = http->request;
1194
#if DONT_FILTER_THESE
1195
/* but you might want to if you run Squid as an HTTP accelerator */
1196
/* hdr->delById(HDR_ACCEPT_RANGES); */
1197
hdr->delById(HDR_ETAG);
1200
hdr->delById(HDR_PROXY_CONNECTION);
1201
/* here: Keep-Alive is a field-name, not a connection directive! */
1202
hdr->delByName("Keep-Alive");
1203
/* remove Set-Cookie if a hit */
1206
hdr->delById(HDR_SET_COOKIE);
1209
* Be sure to obey the Connection header
1211
reply->header.removeConnectionHeaderEntries();
1213
// if (request->range)
1214
// clientBuildRangeHeader(http, reply);
1216
* Add a estimated Age header on cache hits.
1220
* Remove any existing Age header sent by upstream caches
1221
* (note that the existing header is passed along unmodified
1224
hdr->delById(HDR_AGE);
1226
* This adds the calculated object age. Note that the details of the
1227
* age calculation is performed by adjusting the timestamp in
1228
* storeTimestampsSet(), not here.
1230
* BROWSER WORKAROUND: IE sometimes hangs when receiving a 0 Age
1231
* header, so don't use it unless there is a age to report. Please
1232
* note that Age is only used to make a conservative estimation of
1233
* the objects age, so a Age: 0 header does not add any useful
1234
* information to the reply in any case.
1237
if (NULL == http->storeEntry())
1239
else if (http->storeEntry()->timestamp < 0)
1242
if (EBIT_TEST(http->storeEntry()->flags, ENTRY_SPECIAL)) {
1243
hdr->delById(HDR_DATE);
1244
hdr->insertTime(HDR_DATE, squid_curtime);
1245
} else if (http->storeEntry()->timestamp < squid_curtime) {
1246
hdr->putInt(HDR_AGE,
1247
squid_curtime - http->storeEntry()->timestamp);
1248
/* Signal old objects. NB: rfc 2616 is not clear,
1249
* by implication, on whether we should do this to all
1250
* responses, or only cache hits.
1251
* 14.46 states it ONLY applys for heuristically caclulated
1252
* freshness values, 13.2.4 doesn't specify the same limitation.
1253
* We interpret RFC 2616 under the combination.
1255
/* TODO: if maxage or s-maxage is present, don't do this */
1257
if (squid_curtime - http->storeEntry()->timestamp >= 86400) {
1259
snprintf (tempbuf, sizeof(tempbuf), "%s %s %s",
1261
"This cache hit is still fresh and more than 1 day old");
1262
hdr->putStr(HDR_WARNING, tempbuf);
1268
/* Filter unproxyable authentication types */
1269
if (http->logType != LOG_TCP_DENIED &&
1270
(hdr->has(HDR_WWW_AUTHENTICATE) || hdr->has(HDR_PROXY_AUTHENTICATE))) {
1271
HttpHeaderPos pos = HttpHeaderInitPos;
1274
while ((e = hdr->getEntry(&pos))) {
1275
if (e->id == HDR_WWW_AUTHENTICATE || e->id == HDR_PROXY_AUTHENTICATE) {
1276
const char *value = e->value.buf();
1278
if ((strncasecmp(value, "NTLM", 4) == 0 &&
1279
(value[4] == '\0' || value[4] == ' '))
1281
(strncasecmp(value, "Negotiate", 9) == 0 &&
1282
(value[9] == '\0' || value[9] == ' ')))
1288
/* Handle authentication headers */
1289
if (request->auth_user_request)
1290
authenticateFixHeader(reply, request->auth_user_request, request,
1291
http->flags.accel, 0);
1293
/* Append X-Cache */
1294
httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",
1295
is_hit ? "HIT" : "MISS", getMyHostname());
1297
#if USE_CACHE_DIGESTS
1298
/* Append X-Cache-Lookup: -- temporary hack, to be removed @?@ @?@ */
1299
httpHeaderPutStrf(hdr, HDR_X_CACHE_LOOKUP, "%s from %s:%d",
1300
lookup_type ? lookup_type : "NONE",
1301
getMyHostname(), getMyPort());
1305
if (reply->bodySize(request->method) < 0) {
1308
("clientBuildReplyHeader: can't keep-alive, unknown body size\n");
1309
request->flags.proxy_keepalive = 0;
1312
if (fdUsageHigh()&& !request->flags.must_keepalive) {
1313
debug(88, 3) ("clientBuildReplyHeader: Not many unused FDs, can't keep-alive\n");
1314
request->flags.proxy_keepalive = 0;
1317
if (!Config.onoff.error_pconns && reply->sline.status >= 400 && !request->flags.must_keepalive) {
1318
debug(33, 3) ("clientBuildReplyHeader: Error, don't keep-alive\n");
1319
request->flags.proxy_keepalive = 0;
1322
if (!Config.onoff.client_pconns && !request->flags.must_keepalive)
1323
request->flags.proxy_keepalive = 0;
1327
LOCAL_ARRAY(char, bbuf, MAX_URL + 32);
1329
hdr->getList(HDR_VIA, &strVia);
1330
snprintf(bbuf, sizeof(bbuf), "%d.%d %s",
1331
reply->sline.version.major,
1332
reply->sline.version.minor,
1334
strListAdd(&strVia, bbuf, ',');
1335
hdr->delById(HDR_VIA);
1336
hdr->putStr(HDR_VIA, strVia.buf());
1338
/* Signal keep-alive if needed */
1339
hdr->putStr(http->flags.accel ? HDR_CONNECTION : HDR_PROXY_CONNECTION,
1340
request->flags.proxy_keepalive ? "keep-alive" : "close");
1342
#if ADD_X_REQUEST_URI
1344
* Knowing the URI of the request is useful when debugging persistent
1345
* connections in a client; we cannot guarantee the order of http headers,
1346
* but X-Request-URI is likely to be the very last header to ease use from a
1347
* debugger [hdr->entries.count-1].
1349
hdr->putStr(HDR_X_REQUEST_URI,
1350
http->memOjbect()->url ? http->memObject()->url : http->uri);
1354
httpHdrMangleList(hdr, request, ROR_REPLY);
1359
clientReplyContext::buildReply(const char *buf, size_t size)
1361
size_t k = headersEnd(buf, size);
1366
assert(reply == NULL);
1368
HttpReply *rep = new HttpReply;
1370
reply = HTTPMSGLOCK(rep);
1372
if (!reply->parseCharBuf(buf, k)) {
1373
/* parsing failure, get rid of the invalid reply */
1374
HTTPMSGUNLOCK(reply);
1376
if (http->request->range) {
1377
debugs(0,0,HERE << "look for bug here");
1378
/* this will fail and destroy request->range */
1379
// clientBuildRangeHeader(http, reply);
1385
/* enforce 1.0 reply version */
1386
reply->sline.version = HttpVersion(1,0);
1388
/* do header conversions */
1393
clientReplyContext::identifyStoreObject()
1395
HttpRequest *r = http->request;
1397
if (r->flags.cachable || r->flags.internal) {
1398
lookingforstore = 5;
1399
StoreEntry::getPublicByRequest (this, r);
1401
identifyFoundObject (NullStoreEntry::getInstance());
1406
clientReplyContext::identifyFoundObject(StoreEntry *newEntry)
1408
StoreEntry *e = newEntry;
1409
HttpRequest *r = http->request;
1412
http->storeEntry(NULL);
1414
http->storeEntry(e);
1417
e = http->storeEntry();
1418
/* Release IP-cache entries on reload */
1420
if (r->flags.nocache) {
1424
ipcacheInvalidate(r->host);
1428
ipcacheInvalidateNegative(r->host);
1430
#endif /* USE_DNSSERVERS */
1436
else if (r->flags.nocache_hack) {
1440
ipcacheInvalidate(r->host);
1444
ipcacheInvalidateNegative(r->host);
1446
#endif /* USE_DNSSERVERS */
1450
#endif /* HTTP_VIOLATIONS */
1451
#if USE_CACHE_DIGESTS
1453
lookup_type = http->storeEntry() ? "HIT" : "MISS";
1457
if (NULL == http->storeEntry()) {
1458
/* this object isn't in the cache */
1459
debug(85, 3) ("clientProcessRequest2: storeGet() MISS\n");
1460
http->logType = LOG_TCP_MISS;
1465
if (Config.onoff.offline) {
1466
debug(85, 3) ("clientProcessRequest2: offline HIT\n");
1467
http->logType = LOG_TCP_HIT;
1472
if (http->redirect.status) {
1473
/* force this to be a miss */
1474
http->storeEntry(NULL);
1475
http->logType = LOG_TCP_MISS;
1480
if (!storeEntryValidToSend(e)) {
1481
debug(85, 3) ("clientProcessRequest2: !storeEntryValidToSend MISS\n");
1482
http->storeEntry(NULL);
1483
http->logType = LOG_TCP_MISS;
1488
if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1489
/* Special entries are always hits, no matter what the client says */
1490
debug(85, 3) ("clientProcessRequest2: ENTRY_SPECIAL HIT\n");
1491
http->logType = LOG_TCP_HIT;
1496
if (r->flags.nocache) {
1497
debug(85, 3) ("clientProcessRequest2: no-cache REFRESH MISS\n");
1498
http->storeEntry(NULL);
1499
http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
1504
debug(85, 3) ("clientProcessRequest2: default HIT\n");
1505
http->logType = LOG_TCP_HIT;
1509
/* Request more data from the store for the client Stream
1510
* This is *the* entry point to this module.
1513
* This is the head of the list.
1514
* There is at least one more node.
1515
* data context is not null
1518
clientGetMoreData(clientStreamNode * aNode, ClientHttpRequest * http)
1520
/* Test preconditions */
1521
assert(aNode != NULL);
1522
assert(cbdataReferenceValid(aNode));
1523
assert(aNode->node.prev == NULL);
1524
assert(aNode->node.next != NULL);
1525
clientReplyContext *context = dynamic_cast<clientReplyContext *>(aNode->data.getRaw());
1527
assert(context->http == http);
1530
clientStreamNode *next = ( clientStreamNode *)aNode->node.next->data;
1532
if (!context->ourNode)
1533
context->ourNode = aNode;
1535
/* no cbdatareference, this is only used once, and safely */
1536
if (context->flags.storelogiccomplete) {
1537
StoreIOBuffer tempBuffer;
1538
tempBuffer.offset = next->readBuffer.offset + context->headers_sz;
1539
tempBuffer.length = next->readBuffer.length;
1540
tempBuffer.data = next->readBuffer.data;
1542
storeClientCopy(context->sc, http->storeEntry(),
1543
tempBuffer, clientReplyContext::SendMoreData, context);
1547
if (context->http->request->method == METHOD_PURGE) {
1548
context->purgeRequest();
1552
if (context->http->request->method == METHOD_TRACE) {
1553
if (context->http->request->max_forwards == 0) {
1554
context->traceReply(aNode);
1558
/* continue forwarding, not finished yet. */
1559
http->logType = LOG_TCP_MISS;
1561
context->doGetMoreData();
1563
context->identifyStoreObject();
1567
clientReplyContext::doGetMoreData()
1569
/* We still have to do store logic processing - vary, cache hit etc */
1570
if (http->storeEntry() != NULL) {
1571
/* someone found the object in the cache for us */
1572
StoreIOBuffer tempBuffer;
1574
http->storeEntry()->lock()
1578
if (http->storeEntry()->mem_obj == NULL) {
1580
* This if-block exists because we don't want to clobber
1581
* a preexiting mem_obj->method value if the mem_obj
1582
* already exists. For example, when a HEAD request
1583
* is a cache hit for a GET response, we want to keep
1584
* the method as GET.
1586
storeCreateMemObject(http->storeEntry(), http->uri,
1588
http->storeEntry()->mem_obj->method =
1589
http->request->method;
1592
sc = storeClientListAdd(http->storeEntry(), this);
1595
sc->setDelayId(DelayId::DelayClient(http));
1598
assert(http->logType == LOG_TCP_HIT);
1600
/* guarantee nothing has been sent yet! */
1601
assert(http->out.size == 0);
1602
assert(http->out.offset == 0);
1603
tempBuffer.offset = reqofs;
1604
tempBuffer.length = getNextNode()->readBuffer.length;
1605
tempBuffer.data = getNextNode()->readBuffer.data;
1606
storeClientCopy(sc, http->storeEntry(), tempBuffer, CacheHit, this);
1608
/* MISS CASE, http->logType is already set! */
1613
/* the next node has removed itself from the stream. */
1615
clientReplyDetach(clientStreamNode * node, ClientHttpRequest * http)
1617
/* detach from the stream */
1618
clientStreamDetach(node, http);
1622
* accepts chunk of a http message in buf, parses prefix, filters headers and
1623
* such, writes processed message to the message recipient
1626
clientReplyContext::SendMoreData(void *data, StoreIOBuffer result)
1628
clientReplyContext *context = static_cast<clientReplyContext *>(data);
1629
context->sendMoreData (result);
1633
clientReplyContext::makeThisHead()
1635
/* At least, I think thats what this does */
1636
dlinkDelete(&http->active, &ClientActiveRequests);
1637
dlinkAdd(http, &http->active, &ClientActiveRequests);
1641
clientReplyContext::errorInStream(StoreIOBuffer const &result, size_t const &sizeToProcess)const
1643
return /* aborted request */
1644
(http->storeEntry() && EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) ||
1645
/* Upstream read error */ (result.flags.error) ||
1646
/* Upstream EOF */ (sizeToProcess == 0);
1650
clientReplyContext::sendStreamError(StoreIOBuffer const &result)
1652
/* call clientWriteComplete so the client socket gets closed */
1653
/* We call into the stream, because we don't know that there is a
1656
debug(88,5)("clientReplyContext::sendStreamError: A stream error has occured, marking as complete and sending no data.\n");
1657
StoreIOBuffer tempBuffer;
1659
tempBuffer.flags.error = result.flags.error;
1660
clientStreamCallback((clientStreamNode*)http->client_stream.head->data, http, NULL,
1665
clientReplyContext::pushStreamData(StoreIOBuffer const &result, char *source)
1667
StoreIOBuffer tempBuffer;
1669
if (result.length == 0) {
1670
debug (88,5)("clientReplyContext::pushStreamData: marking request as complete due to 0 length store result\n");
1674
assert(result.offset - headers_sz == next()->readBuffer.offset);
1675
tempBuffer.offset = result.offset - headers_sz;
1676
tempBuffer.length = result.length;
1678
if (tempBuffer.length)
1679
tempBuffer.data = source;
1681
clientStreamCallback((clientStreamNode*)http->client_stream.head->data, http, NULL,
1686
clientReplyContext::next() const
1688
assert ( (clientStreamNode*)http->client_stream.head->next->data == getNextNode());
1689
return getNextNode();
1693
clientReplyContext::waitForMoreData ()
1695
debug(88,5)("clientReplyContext::waitForMoreData: Waiting for more data to parse reply headers in client side.\n");
1696
/* We don't have enough to parse the metadata yet */
1697
/* TODO: the store should give us out of band metadata and
1698
* obsolete this routine
1700
/* wait for more to arrive */
1705
clientReplyContext::startSendProcess()
1707
debug(88,5)("clientReplyContext::startSendProcess: triggering store read to SendMoreData\n");
1708
assert(reqofs <= HTTP_REQBUF_SZ);
1709
/* TODO: copy into the supplied buffer */
1710
StoreIOBuffer tempBuffer;
1711
tempBuffer.offset = reqofs;
1712
tempBuffer.length = next()->readBuffer.length - reqofs;
1713
tempBuffer.data = next()->readBuffer.data + reqofs;
1714
storeClientCopy(sc, http->storeEntry(),
1715
tempBuffer, SendMoreData, this);
1719
* Calculates the maximum size allowed for an HTTP response
1722
clientReplyContext::buildMaxBodySize(HttpReply * reply)
1724
acl_size_t *l = Config.ReplyBodySize;
1727
if (http->logType == LOG_TCP_DENIED)
1730
ch = clientAclChecklistCreate(NULL, http);
1732
ch->reply = HTTPMSGLOCK(reply);
1734
for (l = Config.ReplyBodySize; l; l = l -> next) {
1735
if (ch->matchAclListFast(l->aclList)) {
1736
if (l->size != static_cast<size_t>(-1)) {
1737
debug(58, 3) ("clientReplyContext: Setting maxBodySize to %ld\n", (long int) l->size);
1738
http->maxReplyBodySize(l->size);
1749
clientReplyContext::processReplyAccess ()
1752
buildMaxBodySize(reply);
1754
if (http->isReplyBodyTooLarge(reply->content_length)) {
1756
clientBuildError(ERR_TOO_BIG, HTTP_FORBIDDEN, NULL,
1757
http->getConn().getRaw() != NULL ? &http->getConn()->peer.sin_addr : &no_addr,
1759
removeClientStoreReference(&sc, http);
1760
HTTPMSGUNLOCK(reply);
1765
headers_sz = reply->hdr_sz;
1766
ACLChecklist *replyChecklist;
1767
replyChecklist = clientAclChecklistCreate(Config.accessList.reply, http);
1768
replyChecklist->reply = HTTPMSGLOCK(reply);
1769
replyChecklist->nonBlockingCheck(ProcessReplyAccessResult, this);
1773
clientReplyContext::ProcessReplyAccessResult (int rv, void *voidMe)
1775
clientReplyContext *me = static_cast<clientReplyContext *>(voidMe);
1776
me->processReplyAccessResult(rv);
1780
clientReplyContext::processReplyAccessResult(bool accessAllowed)
1782
debug(88, 2) ("The reply for %s %s is %s, because it matched '%s'\n",
1783
RequestMethodStr[http->request->method], http->uri,
1784
accessAllowed ? "ALLOWED" : "DENIED",
1785
AclMatchedName ? AclMatchedName : "NO ACL's");
1787
if (!accessAllowed && reply->sline.status != HTTP_FORBIDDEN
1788
&& !alwaysAllowResponse(reply->sline.status)) {
1789
/* the if above is slightly broken, but there is no way
1790
* to tell if this is a squid generated error page, or one from
1791
* upstream at this point. */
1794
page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
1796
if (page_id == ERR_NONE)
1797
page_id = ERR_ACCESS_DENIED;
1800
clientBuildError(page_id, HTTP_FORBIDDEN, NULL,
1801
http->getConn().getRaw() != NULL ? &http->getConn()->peer.sin_addr : &no_addr,
1804
removeClientStoreReference(&sc, http);
1806
HTTPMSGUNLOCK(reply);
1810
http->logType = LOG_TCP_DENIED_REPLY;
1815
/* Ok, the reply is allowed, */
1816
http->loggingEntry(http->storeEntry());
1818
ssize_t body_size = reqofs - reply->hdr_sz;
1820
assert(body_size >= 0);
1823
("clientReplyContext::sendMoreData: Appending %d bytes after %d bytes of headers\n",
1824
(int) body_size, reply->hdr_sz);
1828
if (http->flags.accel && reply->sline.status != HTTP_FORBIDDEN &&
1829
!alwaysAllowResponse(reply->sline.status) &&
1830
esiEnableProcessing(reply)) {
1831
debug(88, 2) ("Enabling ESI processing for %s\n", http->uri);
1832
clientStreamInsertHead(&http->client_stream, esiStreamRead,
1833
esiProcessStream, esiStreamDetach, esiStreamStatus, NULL);
1838
#if ICAP_CLIENT_RESPMOD_POSTCACHE
1840
debug(88, 0) ("Enabling ICAP processing for %s\n", http->uri);
1842
clientStreamInsertHead(&http->client_stream, icapclientStreamRead,
1843
icapclientProcessStream,
1844
icapclientStreamDetach,
1845
icapclientStreamStatus,
1850
if (http->request->method == METHOD_HEAD) {
1851
/* do not forward body for HEAD replies */
1853
http->flags.done_copying = 1;
1857
assert (!flags.headersSent);
1858
flags.headersSent = true;
1860
StoreIOBuffer tempBuffer;
1861
char *buf = next()->readBuffer.data;
1862
char *body_buf = buf + reply->hdr_sz;
1864
//Server side may disable ranges under some circumstances.
1866
if ((!http->request->range))
1867
next()->readBuffer.offset = 0;
1869
if (next()->readBuffer.offset != 0) {
1870
if (next()->readBuffer.offset > body_size) {
1871
/* Can't use any of the body we recieved. send nothing */
1872
tempBuffer.length = 0;
1873
tempBuffer.data = NULL;
1875
tempBuffer.length = body_size - next()->readBuffer.offset;
1876
tempBuffer.data = body_buf + next()->readBuffer.offset;
1879
tempBuffer.length = body_size;
1880
tempBuffer.data = body_buf;
1883
/* TODO??: move the data in the buffer back by the request header size */
1884
clientStreamCallback((clientStreamNode *)http->client_stream.head->data,
1885
http, reply, tempBuffer);
1891
clientReplyContext::sendMoreData (StoreIOBuffer result)
1896
StoreEntry *entry = http->storeEntry();
1898
ConnStateData::Pointer conn = http->getConn();
1900
int fd = conn.getRaw() != NULL ? conn->fd : -1;
1902
char *buf = next()->readBuffer.data;
1904
char *body_buf = buf;
1906
/* This is always valid until we get the headers as metadata from
1908
* Then it becomes reqofs == next->readBuffer.offset()
1910
assert(reqofs == 0 || flags.storelogiccomplete);
1912
if (flags.headersSent && buf != result.data) {
1913
/* we've got to copy some data */
1914
assert(result.length <= next()->readBuffer.length);
1915
xmemcpy(buf, result.data, result.length);
1917
} else if (!flags.headersSent &&
1918
buf + reqofs !=result.data) {
1919
/* we've got to copy some data */
1920
assert(result.length + reqofs <= next()->readBuffer.length);
1921
xmemcpy(buf + reqofs, result.data, result.length);
1925
/* We've got the final data to start pushing... */
1926
flags.storelogiccomplete = 1;
1928
reqofs += result.length;
1930
assert(reqofs <= HTTP_REQBUF_SZ || flags.headersSent);
1932
assert(http->request != NULL);
1934
/* ESI TODO: remove this assert once everything is stable */
1935
assert(http->client_stream.head->data
1936
&& cbdataReferenceValid(http->client_stream.head->data));
1940
debug(88, 5) ("clientReplyContext::sendMoreData: %s, %d bytes (%u new bytes)\n",
1941
http->uri, (int) reqofs, (unsigned int)result.length);
1943
debug(88, 5) ("clientReplyContext::sendMoreData: FD %d '%s', out.offset=%ld \n",
1944
fd, storeUrl(entry), (long int) http->out.offset);
1946
/* update size of the request */
1949
if (http->request->flags.resetTCP()) {
1950
/* yuck. FIXME: move to client_side.c */
1953
comm_reset_close(fd);
1958
if (errorInStream(result, reqofs)) {
1959
sendStreamError(result);
1963
if (flags.headersSent) {
1964
pushStreamData (result, buf);
1968
buildReply(buf, reqofs);
1969
ssize_t body_size = reqofs;
1973
/* handle headers */
1975
if (Config.onoff.log_mime_hdrs) {
1978
if ((k = headersEnd(buf, reqofs))) {
1979
safe_free(http->al.headers.reply);
1980
http->al.headers.reply = (char *)xcalloc(k + 1, 1);
1981
xstrncpy(http->al.headers.reply, buf, k);
1985
holdingBuffer = result;
1986
processReplyAccess();
1989
} else if (reqofs < HTTP_REQBUF_SZ && entry->store_status == STORE_PENDING) {
1992
} else if (http->request->method == METHOD_HEAD) {
1994
* If we are here, then store_status == STORE_OK and it
1995
* seems we have a HEAD repsponse which is missing the
1996
* empty end-of-headers line (home.mira.net, phttpd/0.99.72
1997
* does this). Because buildReply() fails we just
1998
* call this reply a body, set the done_copying flag and
2001
/* RBC: Note that this is seriously broken, as we *need* the
2002
* metadata to allow further client modules to work. As such
2003
* webservers are seriously broken, this is probably not
2004
* going to get fixed.. perhapos we should remove it?
2006
debug (88,0)("Broken head response - probably phttpd/0.99.72\n");
2007
http->flags.done_copying = 1;
2010
* And as this is a malformed HTTP reply we cannot keep
2011
* the connection persistent
2013
http->request->flags.proxy_keepalive = 0;
2015
assert(body_buf && body_size);
2016
StoreIOBuffer tempBuffer (body_size, 0 ,body_buf);
2017
clientStreamCallback((clientStreamNode *)http->client_stream.head->data,
2018
http, NULL, tempBuffer);
2020
debug (88,0)("clientReplyContext::sendMoreData: Unable to parse reply headers within a single HTTP_REQBUF_SZ length buffer\n");
2021
StoreIOBuffer tempBuffer;
2022
tempBuffer.flags.error = 1;
2023
/* XXX FIXME: make an html error page here */
2024
sendStreamError(tempBuffer);
2027
fatal ("clientReplyContext::sendMoreData: Unreachable code reached \n");
2032
/* Using this breaks the client layering just a little!
2035
clientReplyContext::createStoreEntry(method_t m, request_flags flags)
2037
assert(http != NULL);
2039
* For erroneous requests, we might not have a h->request,
2040
* so make a fake one.
2043
if (http->request == NULL)
2044
http->request = HTTPMSGLOCK(new HttpRequest(m, PROTO_NONE, null_string));
2046
StoreEntry *e = storeCreateEntry(http->uri, http->log_uri, flags, m);
2048
sc = storeClientListAdd(e, this);
2052
sc->setDelayId(DelayId::DelayClient(http));
2060
/* I don't think this is actually needed! -- adrian */
2061
/* http->reqbuf = http->norm_reqbuf; */
2062
// assert(http->reqbuf == http->norm_reqbuf);
2063
/* The next line is illegal because we don't know if the client stream
2064
* buffers have been set up
2066
// storeClientCopy(http->sc, e, 0, HTTP_REQBUF_SZ, http->reqbuf,
2067
// SendMoreData, this);
2068
/* So, we mark the store logic as complete */
2069
this->flags.storelogiccomplete = 1;
2071
/* and get the caller to request a read, from whereever they are */
2072
/* NOTE: after ANY data flows down the pipe, even one step,
2073
* this function CAN NOT be used to manage errors
2075
http->storeEntry(e);
2079
clientBuildError(err_type page_id, http_status status, char const *url,
2081
struct IN_ADDR * src_addr, HttpRequest * request)
2083
ErrorState *err = errorCon(page_id, status, request);
2084
err->src_addr = *src_addr;
2087
err->url = xstrdup(url);