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