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