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