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