]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
ICAP fixes mk2.
[thirdparty/squid.git] / src / client_side_request.cc
CommitLineData
edce4d98 1
2/*
5ad35449 3 * $Id: client_side_request.cc,v 1.99 2007/12/16 22:32:10 amosjeffries Exp $
69660be0 4 *
ae45c4de 5 * DEBUG: section 85 Client-side Request Routines
6 * AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c)
69660be0 7 *
edce4d98 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
0a9297f6 10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
edce4d98 34 */
35
36
69660be0 37/*
38 * General logic of request processing:
39 *
40 * We run a series of tests to determine if access will be permitted, and to do
41 * any redirection. Then we call into the result clientStream to retrieve data.
42 * From that point on it's up to reply management.
edce4d98 43 */
44
45#include "squid.h"
c8be6d7b 46#include "clientStream.h"
47#include "client_side_request.h"
f5691f9c 48#include "AuthUserRequest.h"
528b2c61 49#include "HttpRequest.h"
8000a965 50#include "ACLChecklist.h"
51#include "ACL.h"
a46d2c0e 52#include "client_side.h"
0655fa4d 53#include "client_side_reply.h"
54#include "Store.h"
55#include "HttpReply.h"
86a2f789 56#include "MemObject.h"
de31d06f 57#include "ClientRequestContext.h"
985c86bc 58#include "SquidTime.h"
d295d770 59#include "wordlist.h"
de31d06f 60
61#if ICAP_CLIENT
5f8252d2 62#include "ICAP/ICAPModXact.h"
de31d06f 63#include "ICAP/ICAPElements.h"
64#include "ICAP/ICAPConfig.h"
65static void icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data);
ab7ac359 66extern ICAPConfig TheICAPConfig;
de31d06f 67#endif
edce4d98 68
69#if LINGERING_CLOSE
70#define comm_close comm_lingering_close
71#endif
72
73static const char *const crlf = "\r\n";
74
8e2745f4 75CBDATA_CLASS_INIT(ClientRequestContext);
76
77void *
78ClientRequestContext::operator new (size_t size)
79{
80 assert (size == sizeof(ClientRequestContext));
81 CBDATA_INIT_TYPE(ClientRequestContext);
82 ClientRequestContext *result = cbdataAlloc(ClientRequestContext);
aa625860 83 return result;
8e2745f4 84}
62e76326 85
8e2745f4 86void
87ClientRequestContext::operator delete (void *address)
88{
89 ClientRequestContext *t = static_cast<ClientRequestContext *>(address);
aa625860 90 cbdataFree(t);
8e2745f4 91}
92
edce4d98 93/* Local functions */
edce4d98 94/* other */
de31d06f 95static void clientAccessCheckDoneWrapper(int, void *);
59a1efb2 96static int clientHierarchical(ClientHttpRequest * http);
97static void clientInterpretRequestHeaders(ClientHttpRequest * http);
de31d06f 98static RH clientRedirectDoneWrapper;
99static PF checkNoCacheDoneWrapper;
e6ccf245 100extern "C" CSR clientGetMoreData;
101extern "C" CSS clientReplyStatus;
102extern "C" CSD clientReplyDetach;
528b2c61 103static void checkFailureRatio(err_type, hier_code);
edce4d98 104
8e2745f4 105ClientRequestContext::~ClientRequestContext()
106{
de31d06f 107 /*
a546b04b 108 * Release our "lock" on our parent, ClientHttpRequest, if we
109 * still have one
de31d06f 110 */
a546b04b 111
112 if (http)
113 cbdataReferenceDone(http);
62e76326 114
5f8252d2 115 debugs(85,3, HERE << this << " ClientRequestContext destructed");
8e2745f4 116}
117
4e2eb5c3 118ClientRequestContext::ClientRequestContext(ClientHttpRequest *anHttp) : http(cbdataReference(anHttp)), acl_checklist (NULL), redirect_state (REDIRECT_NONE)
edce4d98 119{
57abaac0 120 http_access_done = false;
121 redirect_done = false;
122 no_cache_done = false;
123 interpreted_req_hdrs = false;
0b86805b 124 debugs(85,3, HERE << this << " ClientRequestContext constructed");
edce4d98 125}
126
528b2c61 127CBDATA_CLASS_INIT(ClientHttpRequest);
8e2745f4 128
528b2c61 129void *
130ClientHttpRequest::operator new (size_t size)
131{
132 assert (size == sizeof (ClientHttpRequest));
133 CBDATA_INIT_TYPE(ClientHttpRequest);
134 ClientHttpRequest *result = cbdataAlloc(ClientHttpRequest);
aa625860 135 return result;
528b2c61 136}
137
62e76326 138void
528b2c61 139ClientHttpRequest::operator delete (void *address)
140{
aa625860 141 ClientHttpRequest *t = static_cast<ClientHttpRequest *>(address);
142 cbdataFree(t);
528b2c61 143}
144
a0355e95 145ClientHttpRequest::ClientHttpRequest(ConnStateData::Pointer aConn) : loggingEntry_(NULL)
528b2c61 146{
528b2c61 147 start = current_time;
a0355e95 148 setConn(aConn);
149 dlinkAdd(this, &active, &ClientActiveRequests);
b044675d 150#if ICAP_CLIENT
b044675d 151 request_satisfaction_mode = false;
152#endif
528b2c61 153}
154
0655fa4d 155/*
156 * returns true if client specified that the object must come from the cache
157 * without contacting origin server
158 */
159bool
160ClientHttpRequest::onlyIfCached()const
161{
162 assert(request);
163 return request->cache_control &&
164 EBIT_TEST(request->cache_control->mask, CC_ONLY_IF_CACHED);
165}
166
528b2c61 167/*
168 * This function is designed to serve a fairly specific purpose.
169 * Occasionally our vBNS-connected caches can talk to each other, but not
170 * the rest of the world. Here we try to detect frequent failures which
171 * make the cache unusable (e.g. DNS lookup and connect() failures). If
172 * the failure:success ratio goes above 1.0 then we go into "hit only"
173 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
174 * will only fetch HITs from us if they are using the ICP protocol. We
175 * stay in this mode for 5 minutes.
176 *
177 * Duane W., Sept 16, 1996
178 */
179
180#define FAILURE_MODE_TIME 300
181
182static void
183checkFailureRatio(err_type etype, hier_code hcode)
184{
185 static double magic_factor = 100.0;
186 double n_good;
187 double n_bad;
62e76326 188
528b2c61 189 if (hcode == HIER_NONE)
62e76326 190 return;
191
528b2c61 192 n_good = magic_factor / (1.0 + request_failure_ratio);
62e76326 193
528b2c61 194 n_bad = magic_factor - n_good;
62e76326 195
528b2c61 196 switch (etype) {
62e76326 197
528b2c61 198 case ERR_DNS_FAIL:
62e76326 199
528b2c61 200 case ERR_CONNECT_FAIL:
62e76326 201
528b2c61 202 case ERR_READ_ERROR:
62e76326 203 n_bad++;
204 break;
205
528b2c61 206 default:
62e76326 207 n_good++;
528b2c61 208 }
62e76326 209
528b2c61 210 request_failure_ratio = n_bad / n_good;
62e76326 211
528b2c61 212 if (hit_only_mode_until > squid_curtime)
62e76326 213 return;
214
528b2c61 215 if (request_failure_ratio < 1.0)
62e76326 216 return;
217
bf8fe701 218 debugs(33, 0, "Failure Ratio at "<< std::setw(4)<<
219 std::setprecision(3) << request_failure_ratio);
62e76326 220
bf8fe701 221 debugs(33, 0, "Going into hit-only-mode for " <<
222 FAILURE_MODE_TIME / 60 << " minutes...");
62e76326 223
528b2c61 224 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
62e76326 225
528b2c61 226 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
227}
228
229ClientHttpRequest::~ClientHttpRequest()
230{
bf8fe701 231 debugs(33, 3, "httpRequestFree: " << uri);
72bdee4c 232 PROF_start(httpRequestFree);
62e76326 233
5f8252d2 234 // Even though freeResources() below may destroy the request,
235 // we no longer set request->body_pipe to NULL here
236 // because we did not initiate that pipe (ConnStateData did)
62e76326 237
528b2c61 238 /* the ICP check here was erroneous
d88e3c49 239 * - StoreEntry::releaseRequest was always called if entry was valid
528b2c61 240 */
241 assert(logType < LOG_TYPE_MAX);
9ce7856a 242
528b2c61 243 logRequest();
9ce7856a 244
0976f8db 245 loggingEntry(NULL);
246
528b2c61 247 if (request)
62e76326 248 checkFailureRatio(request->errType, al.hier.code);
249
528b2c61 250 freeResources();
62e76326 251
de31d06f 252#if ICAP_CLIENT
9d4d7c5e 253 announceInitiatorAbort(icapHeadSource);
254
5f8252d2 255 if (icapBodySource != NULL)
256 stopConsumingFrom(icapBodySource);
de31d06f 257#endif
9ce7856a 258
de31d06f 259 if (calloutContext)
260 delete calloutContext;
261
528b2c61 262 /* moving to the next connection is handled by the context free */
263 dlinkDelete(&active, &ClientActiveRequests);
9ce7856a 264
72bdee4c 265 PROF_stop(httpRequestFree);
528b2c61 266}
62e76326 267
edce4d98 268/* Create a request and kick it off */
69660be0 269/*
270 * TODO: Pass in the buffers to be used in the inital Read request, as they are
271 * determined by the user
edce4d98 272 */
273int /* returns nonzero on failure */
274clientBeginRequest(method_t method, char const *url, CSCB * streamcallback,
0655fa4d 275 CSD * streamdetach, ClientStreamData streamdata, HttpHeader const *header,
62e76326 276 char *tailbuf, size_t taillen)
edce4d98 277{
278 size_t url_sz;
450e0c10 279 HttpVersion http_ver (1, 0);
a0355e95 280 ClientHttpRequest *http = new ClientHttpRequest(NULL);
190154cf 281 HttpRequest *request;
528b2c61 282 StoreIOBuffer tempBuffer;
edce4d98 283 http->start = current_time;
284 /* this is only used to adjust the connection offset in client_side.c */
285 http->req_sz = 0;
c8be6d7b 286 tempBuffer.length = taillen;
287 tempBuffer.data = tailbuf;
edce4d98 288 /* client stream setup */
289 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 290 clientReplyStatus, new clientReplyContext(http), streamcallback,
62e76326 291 streamdetach, streamdata, tempBuffer);
edce4d98 292 /* make it visible in the 'current acctive requests list' */
edce4d98 293 /* Set flags */
a46d2c0e 294 /* internal requests only makes sense in an
295 * accelerator today. TODO: accept flags ? */
296 http->flags.accel = 1;
edce4d98 297 /* allow size for url rewriting */
298 url_sz = strlen(url) + Config.appendDomainLen + 5;
e6ccf245 299 http->uri = (char *)xcalloc(url_sz, 1);
edce4d98 300 strcpy(http->uri, url);
301
c21ad0f5 302 if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
bf8fe701 303 debugs(85, 5, "Invalid URL: " << http->uri);
62e76326 304 return -1;
edce4d98 305 }
62e76326 306
69660be0 307 /*
308 * now update the headers in request with our supplied headers. urLParse
309 * should return a blank header set, but we use Update to be sure of
310 * correctness.
edce4d98 311 */
312 if (header)
a9925b40 313 request->header.update(header, NULL);
62e76326 314
edce4d98 315 http->log_uri = xstrdup(urlCanonicalClean(request));
62e76326 316
edce4d98 317 /* http struct now ready */
318
69660be0 319 /*
320 * build new header list *? TODO
edce4d98 321 */
322 request->flags.accelerated = http->flags.accel;
62e76326 323
a46d2c0e 324 request->flags.internalclient = 1;
325
326 /* this is an internally created
327 * request, not subject to acceleration
328 * target overrides */
69660be0 329 /*
330 * FIXME? Do we want to detect and handle internal requests of internal
331 * objects ?
332 */
edce4d98 333
334 /* Internally created requests cannot have bodies today */
335 request->content_length = 0;
62e76326 336
cc192b50 337 request->client_addr.SetNoAddr();
62e76326 338
cc192b50 339 request->my_addr.SetNoAddr(); /* undefined for internal requests */
62e76326 340
cc192b50 341 request->my_addr.SetPort(0);
62e76326 342
edce4d98 343 request->http_ver = http_ver;
62e76326 344
6dd9f4bd 345 http->request = HTTPMSGLOCK(request);
edce4d98 346
347 /* optional - skip the access check ? */
de31d06f 348 http->calloutContext = new ClientRequestContext(http);
349
57abaac0 350 http->calloutContext->http_access_done = false;
de31d06f 351
57abaac0 352 http->calloutContext->redirect_done = true;
de31d06f 353
57abaac0 354 http->calloutContext->no_cache_done = true;
de31d06f 355
356 http->doCallouts();
62e76326 357
edce4d98 358 return 0;
359}
360
de31d06f 361bool
362ClientRequestContext::httpStateIsValid()
363{
364 ClientHttpRequest *http_ = http;
365
366 if (cbdataReferenceValid(http_))
367 return true;
368
369 http = NULL;
370
371 cbdataReferenceDone(http_);
372
373 return false;
374}
375
edce4d98 376/* This is the entry point for external users of the client_side routines */
377void
de31d06f 378ClientRequestContext::clientAccessCheck()
edce4d98 379{
de31d06f 380 acl_checklist =
62e76326 381 clientAclChecklistCreate(Config.accessList.http, http);
de31d06f 382 acl_checklist->nonBlockingCheck(clientAccessCheckDoneWrapper, this);
edce4d98 383}
384
385void
de31d06f 386clientAccessCheckDoneWrapper(int answer, void *data)
edce4d98 387{
de31d06f 388 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
fbade053 389
de31d06f 390 if (!calloutContext->httpStateIsValid())
62e76326 391 return;
62e76326 392
de31d06f 393 calloutContext->clientAccessCheckDone(answer);
394}
395
396void
397ClientRequestContext::clientAccessCheckDone(int answer)
398{
399 acl_checklist = NULL;
edce4d98 400 err_type page_id;
401 http_status status;
bf8fe701 402 debugs(85, 2, "The request " <<
403 RequestMethodStr[http->request->method] << " " <<
404 http->uri << " is " <<
405 (answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED") <<
406 ", because it matched '" <<
407 (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" );
f5691f9c 408 char const *proxy_auth_msg = "<null>";
409
94a396a3 410 if (http->getConn() != NULL && http->getConn()->auth_user_request != NULL)
f5691f9c 411 proxy_auth_msg = http->getConn()->auth_user_request->denyMessage("<null>");
412 else if (http->request->auth_user_request != NULL)
413 proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
62e76326 414
de31d06f 415 if (answer != ACCESS_ALLOWED) {
62e76326 416 /* Send an error */
9ce7856a 417 int require_auth = (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName));
bf8fe701 418 debugs(85, 5, "Access Denied: " << http->uri);
419 debugs(85, 5, "AclMatchedName = " << (AclMatchedName ? AclMatchedName : "<null>"));
9ce7856a 420
421 if (require_auth)
bf8fe701 422 debugs(33, 5, "Proxy Auth Message = " << (proxy_auth_msg ? proxy_auth_msg : "<null>"));
9ce7856a 423
62e76326 424 /*
425 * NOTE: get page_id here, based on AclMatchedName because if
426 * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
427 * the clientCreateStoreEntry() call just below. Pedro Ribeiro
428 * <pribeiro@isel.pt>
429 */
9ce7856a 430 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, answer != ACCESS_REQ_PROXY_AUTH);
431
62e76326 432 http->logType = LOG_TCP_DENIED;
433
9ce7856a 434 if (require_auth) {
62e76326 435 if (!http->flags.accel) {
436 /* Proxy authorisation needed */
437 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
438 } else {
439 /* WWW authorisation needed */
440 status = HTTP_UNAUTHORIZED;
441 }
442
443 if (page_id == ERR_NONE)
444 page_id = ERR_CACHE_ACCESS_DENIED;
445 } else {
446 status = HTTP_FORBIDDEN;
447
448 if (page_id == ERR_NONE)
449 page_id = ERR_ACCESS_DENIED;
450 }
451
de31d06f 452 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
0655fa4d 453 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
454 assert (repContext);
cc192b50 455 IPAddress tmpnoaddr; tmpnoaddr.SetNoAddr();
0655fa4d 456 repContext->setReplyToError(page_id, status,
457 http->request->method, NULL,
cc192b50 458 http->getConn() != NULL ? http->getConn()->peer : tmpnoaddr,
459 http->request,
460 NULL,
461 http->getConn() != NULL && http->getConn()->auth_user_request ?
462 http->getConn()->auth_user_request : http->request->auth_user_request);
463
62e76326 464 node = (clientStreamNode *)http->client_stream.tail->data;
465 clientStreamRead(node, http, node->readBuffer);
a546b04b 466 return;
edce4d98 467 }
de31d06f 468
469 /* ACCESS_ALLOWED continues here ... */
470 safe_free(http->uri);
471
472 http->uri = xstrdup(urlCanonical(http->request));
473
474 http->doCallouts();
475}
476
477#if ICAP_CLIENT
478void
479ClientRequestContext::icapAccessCheck()
480{
481 ICAPAccessCheck *icap_access_check;
482
e8fe1384 483 icap_access_check = new ICAPAccessCheck(ICAP::methodReqmod, ICAP::pointPreCache, http->request, NULL, icapAclCheckDoneWrapper, this);
484
485 if (icap_access_check != NULL) {
de31d06f 486 icap_access_check->check();
487 return;
488 }
489
490 http->doCallouts();
491}
492
493static void
494icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data)
495{
496 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
497
498 if (!calloutContext->httpStateIsValid())
499 return;
500
501 calloutContext->icapAclCheckDone(service);
502}
503
504void
505ClientRequestContext::icapAclCheckDone(ICAPServiceRep::Pointer service)
506{
6ec67de9 507 debugs(93,3,HERE << this << " icapAclCheckDone called");
6ec67de9 508 assert(http);
509
5f8252d2 510 if (http->startIcap(service))
de31d06f 511 return;
512
5f8252d2 513 if (!service || service->bypass) {
514 // handle ICAP start failure when no service was selected
515 // or where the selected service was optional
516 http->doCallouts();
517 return;
518 }
de31d06f 519
5f8252d2 520 // handle start failure for an essential ICAP service
521 http->handleIcapFailure();
edce4d98 522}
523
de31d06f 524#endif
525
14cc8559 526static void
527clientRedirectAccessCheckDone(int answer, void *data)
528{
529 ClientRequestContext *context = (ClientRequestContext *)data;
59a1efb2 530 ClientHttpRequest *http = context->http;
14cc8559 531 context->acl_checklist = NULL;
532
533 if (answer == ACCESS_ALLOWED)
de31d06f 534 redirectStart(http, clientRedirectDoneWrapper, context);
14cc8559 535 else
de31d06f 536 context->clientRedirectDone(NULL);
14cc8559 537}
538
de31d06f 539void
540ClientRequestContext::clientRedirectStart()
14cc8559 541{
bf8fe701 542 debugs(33, 5, "clientRedirectStart: '" << http->uri << "'");
14cc8559 543
14cc8559 544 if (Config.accessList.redirector) {
de31d06f 545 acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
546 acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
14cc8559 547 } else
de31d06f 548 redirectStart(http, clientRedirectDoneWrapper, this);
14cc8559 549}
550
edce4d98 551static int
59a1efb2 552clientHierarchical(ClientHttpRequest * http)
edce4d98 553{
554 const char *url = http->uri;
190154cf 555 HttpRequest *request = http->request;
edce4d98 556 method_t method = request->method;
557 const wordlist *p = NULL;
558
69660be0 559 /*
560 * IMS needs a private key, so we can use the hierarchy for IMS only if our
561 * neighbors support private keys
562 */
62e76326 563
edce4d98 564 if (request->flags.ims && !neighbors_do_private_keys)
62e76326 565 return 0;
566
69660be0 567 /*
568 * This is incorrect: authenticating requests can be sent via a hierarchy
06b97e72 569 * (they can even be cached if the correct headers are set on the reply)
edce4d98 570 */
571 if (request->flags.auth)
62e76326 572 return 0;
573
edce4d98 574 if (method == METHOD_TRACE)
62e76326 575 return 1;
576
edce4d98 577 if (method != METHOD_GET)
62e76326 578 return 0;
579
edce4d98 580 /* scan hierarchy_stoplist */
581 for (p = Config.hierarchy_stoplist; p; p = p->next)
62e76326 582 if (strstr(url, p->key))
583 return 0;
584
edce4d98 585 if (request->flags.loopdetect)
62e76326 586 return 0;
587
edce4d98 588 if (request->protocol == PROTO_HTTP)
62e76326 589 return httpCachable(method);
590
edce4d98 591 if (request->protocol == PROTO_GOPHER)
62e76326 592 return gopherCachable(request);
593
edce4d98 594 if (request->protocol == PROTO_CACHEOBJ)
62e76326 595 return 0;
596
edce4d98 597 return 1;
598}
599
600
601static void
59a1efb2 602clientInterpretRequestHeaders(ClientHttpRequest * http)
edce4d98 603{
190154cf 604 HttpRequest *request = http->request;
0ef77270 605 HttpHeader *req_hdr = &request->header;
edce4d98 606 int no_cache = 0;
f41735ea 607#if !(USE_SQUID_ESI) || defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG)
62e76326 608
edce4d98 609 const char *str;
610#endif
62e76326 611
edce4d98 612 request->imslen = -1;
a9925b40 613 request->ims = req_hdr->getTime(HDR_IF_MODIFIED_SINCE);
62e76326 614
edce4d98 615 if (request->ims > 0)
62e76326 616 request->flags.ims = 1;
617
f41735ea 618#if USE_SQUID_ESI
69660be0 619 /*
620 * We ignore Cache-Control as per the Edge Architecture Section 3. See
621 * www.esi.org for more information.
edce4d98 622 */
623#else
62e76326 624
a9925b40 625 if (req_hdr->has(HDR_PRAGMA)) {
30abd221 626 String s = req_hdr->getList(HDR_PRAGMA);
62e76326 627
628 if (strListIsMember(&s, "no-cache", ','))
629 no_cache++;
30abd221 630
631 s.clean();
edce4d98 632 }
62e76326 633
edce4d98 634 if (request->cache_control)
62e76326 635 if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
636 no_cache++;
637
69660be0 638 /*
62e76326 639 * Work around for supporting the Reload button in IE browsers when Squid
640 * is used as an accelerator or transparent proxy, by turning accelerated
641 * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
642 * actually only fixed in SP1, but we can't tell whether we are talking to
643 * SP1 or not so all 5.5 versions are treated 'normally').
644 */
edce4d98 645 if (Config.onoff.ie_refresh) {
62e76326 646 if (http->flags.accel && request->flags.ims) {
a9925b40 647 if ((str = req_hdr->getStr(HDR_USER_AGENT))) {
62e76326 648 if (strstr(str, "MSIE 5.01") != NULL)
649 no_cache++;
650 else if (strstr(str, "MSIE 5.0") != NULL)
651 no_cache++;
652 else if (strstr(str, "MSIE 4.") != NULL)
653 no_cache++;
654 else if (strstr(str, "MSIE 3.") != NULL)
655 no_cache++;
656 }
657 }
edce4d98 658 }
62e76326 659
edce4d98 660#endif
661 if (no_cache) {
662#if HTTP_VIOLATIONS
62e76326 663
664 if (Config.onoff.reload_into_ims)
665 request->flags.nocache_hack = 1;
666 else if (refresh_nocache_hack)
667 request->flags.nocache_hack = 1;
668 else
edce4d98 669#endif
62e76326 670
671 request->flags.nocache = 1;
edce4d98 672 }
62e76326 673
0ef77270 674 /* ignore range header in non-GETs or non-HEADs */
675 if (request->method == METHOD_GET || request->method == METHOD_HEAD) {
a9925b40 676 request->range = req_hdr->getRange();
62e76326 677
678 if (request->range) {
679 request->flags.range = 1;
680 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
681 /* XXX: This is suboptimal. We should give the stream the range set,
682 * and thereby let the top of the stream set the offset when the
683 * size becomes known. As it is, we will end up requesting from 0
684 * for evey -X range specification.
685 * RBC - this may be somewhat wrong. We should probably set the range
686 * iter up at this point.
687 */
688 node->readBuffer.offset = request->range->lowestOffset(0);
689 http->range_iter.pos = request->range->begin();
690 http->range_iter.valid = true;
691 }
edce4d98 692 }
62e76326 693
0ef77270 694 /* Only HEAD and GET requests permit a Range or Request-Range header.
695 * If these headers appear on any other type of request, delete them now.
696 */
697 else {
698 req_hdr->delById(HDR_RANGE);
699 req_hdr->delById(HDR_REQUEST_RANGE);
700 request->range = NULL;
701 }
702
a9925b40 703 if (req_hdr->has(HDR_AUTHORIZATION))
62e76326 704 request->flags.auth = 1;
705
edce4d98 706 if (request->login[0] != '\0')
62e76326 707 request->flags.auth = 1;
708
a9925b40 709 if (req_hdr->has(HDR_VIA)) {
30abd221 710 String s = req_hdr->getList(HDR_VIA);
62e76326 711 /*
712 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
713 * Note ThisCache2 has a space prepended to the hostname so we don't
714 * accidentally match super-domains.
715 */
716
717 if (strListIsSubstr(&s, ThisCache2, ',')) {
718 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
719 request, (ObjPackMethod) & httpRequestPack);
720 request->flags.loopdetect = 1;
721 }
722
edce4d98 723#if FORW_VIA_DB
30abd221 724 fvdbCountVia(s.buf());
62e76326 725
edce4d98 726#endif
62e76326 727
30abd221 728 s.clean();
edce4d98 729 }
62e76326 730
5ad35449 731/**
732 \todo --enable-useragent-log and --enable-referer-log. We should
733 probably drop those two as the custom log formats accomplish pretty much the same thing..
734*/
edce4d98 735#if USE_USERAGENT_LOG
a9925b40 736 if ((str = req_hdr->getStr(HDR_USER_AGENT)))
5ad35449 737 logUserAgent(fqdnFromAddr(http->getConn()->log_addr, str);
62e76326 738
edce4d98 739#endif
740#if USE_REFERER_LOG
62e76326 741
a9925b40 742 if ((str = req_hdr->getStr(HDR_REFERER)))
5ad35449 743 logReferer(fqdnFromAddr(http->getConn()->log_addr, str, http->log_uri);
62e76326 744
edce4d98 745#endif
746#if FORW_VIA_DB
62e76326 747
a9925b40 748 if (req_hdr->has(HDR_X_FORWARDED_FOR)) {
30abd221 749 String s = req_hdr->getList(HDR_X_FORWARDED_FOR);
750 fvdbCountForw(s.buf());
751 s.clean();
edce4d98 752 }
62e76326 753
edce4d98 754#endif
755 if (request->method == METHOD_TRACE) {
a9925b40 756 request->max_forwards = req_hdr->getInt(HDR_MAX_FORWARDS);
edce4d98 757 }
62e76326 758
610ee341 759 request->flags.cachable = http->request->cacheable();
62e76326 760
edce4d98 761 if (clientHierarchical(http))
62e76326 762 request->flags.hierarchical = 1;
763
bf8fe701 764 debugs(85, 5, "clientInterpretRequestHeaders: REQ_NOCACHE = " <<
765 (request->flags.nocache ? "SET" : "NOT SET"));
766 debugs(85, 5, "clientInterpretRequestHeaders: REQ_CACHABLE = " <<
767 (request->flags.cachable ? "SET" : "NOT SET"));
768 debugs(85, 5, "clientInterpretRequestHeaders: REQ_HIERARCHICAL = " <<
769 (request->flags.hierarchical ? "SET" : "NOT SET"));
62e76326 770
edce4d98 771}
772
773void
de31d06f 774clientRedirectDoneWrapper(void *data, char *result)
edce4d98 775{
de31d06f 776 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
db02222f 777
de31d06f 778 if (!calloutContext->httpStateIsValid())
62e76326 779 return;
62e76326 780
de31d06f 781 calloutContext->clientRedirectDone(result);
782}
783
784void
785ClientRequestContext::clientRedirectDone(char *result)
786{
190154cf 787 HttpRequest *new_request = NULL;
788 HttpRequest *old_request = http->request;
bf8fe701 789 debugs(85, 5, "clientRedirectDone: '" << http->uri << "' result=" << (result ? result : "NULL"));
de31d06f 790 assert(redirect_state == REDIRECT_PENDING);
791 redirect_state = REDIRECT_DONE;
62e76326 792
edce4d98 793 if (result) {
62e76326 794 http_status status = (http_status) atoi(result);
795
796 if (status == HTTP_MOVED_PERMANENTLY
797 || status == HTTP_MOVED_TEMPORARILY
798 || status == HTTP_SEE_OTHER
799 || status == HTTP_TEMPORARY_REDIRECT) {
800 char *t = result;
801
802 if ((t = strchr(result, ':')) != NULL) {
803 http->redirect.status = status;
804 http->redirect.location = xstrdup(t + 1);
805 } else {
bf8fe701 806 debugs(85, 1, "clientRedirectDone: bad input: " << result);
62e76326 807 }
2baf58c3 808 } else if (strcmp(result, http->uri))
c21ad0f5 809 new_request = HttpRequest::CreateFromUrlAndMethod(result, old_request->method);
edce4d98 810 }
62e76326 811
edce4d98 812 if (new_request) {
62e76326 813 safe_free(http->uri);
814 http->uri = xstrdup(urlCanonical(new_request));
815 new_request->http_ver = old_request->http_ver;
a9925b40 816 new_request->header.append(&old_request->header);
62e76326 817 new_request->client_addr = old_request->client_addr;
818 new_request->my_addr = old_request->my_addr;
62e76326 819 new_request->flags = old_request->flags;
3c1f01bc 820 new_request->flags.redirected = 1;
62e76326 821
822 if (old_request->auth_user_request) {
823 new_request->auth_user_request = old_request->auth_user_request;
4f0ef8e8 824 AUTHUSERREQUESTLOCK(new_request->auth_user_request, "new request");
62e76326 825 }
826
5f8252d2 827 if (old_request->body_pipe != NULL) {
828 new_request->body_pipe = old_request->body_pipe;
829 old_request->body_pipe = NULL;
830 debugs(0,0,HERE << "redirecting body_pipe " << new_request->body_pipe << " from request " << old_request << " to " << new_request);
62e76326 831 }
832
833 new_request->content_length = old_request->content_length;
abb929f0 834 new_request->extacl_user = old_request->extacl_user;
835 new_request->extacl_passwd = old_request->extacl_passwd;
62e76326 836 new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
6dd9f4bd 837 HTTPMSGUNLOCK(old_request);
838 http->request = HTTPMSGLOCK(new_request);
edce4d98 839 }
62e76326 840
edce4d98 841 /* FIXME PIPELINE: This is innacurate during pipelining */
62e76326 842
5f8252d2 843 if (http->getConn() != NULL)
98242069 844 fd_note(http->getConn()->fd, http->uri);
62e76326 845
c8be6d7b 846 assert(http->uri);
62e76326 847
de31d06f 848 http->doCallouts();
edce4d98 849}
850
851void
8e2745f4 852ClientRequestContext::checkNoCache()
edce4d98 853{
de31d06f 854 acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
57abaac0 855 acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, this);
edce4d98 856}
857
de31d06f 858static void
859checkNoCacheDoneWrapper(int answer, void *data)
edce4d98 860{
de31d06f 861 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
e4a67a80 862
de31d06f 863 if (!calloutContext->httpStateIsValid())
864 return;
865
866 calloutContext->checkNoCacheDone(answer);
8e2745f4 867}
4fb35c3c 868
8e2745f4 869void
870ClientRequestContext::checkNoCacheDone(int answer)
62e76326 871{
8e2745f4 872 acl_checklist = NULL;
de31d06f 873 http->request->flags.cachable = answer;
874 http->doCallouts();
edce4d98 875}
876
69660be0 877/*
878 * Identify requests that do not go through the store and client side stream
879 * and forward them to the appropriate location. All other requests, request
880 * them.
edce4d98 881 */
882void
8e2745f4 883ClientHttpRequest::processRequest()
edce4d98 884{
bf8fe701 885 debugs(85, 4, "clientProcessRequest: " << RequestMethodStr[request->method] << " '" << uri << "'");
62e76326 886
2baf58c3 887 if (request->method == METHOD_CONNECT && !redirect.status) {
62e76326 888 logType = LOG_TCP_MISS;
11007d4b 889 tunnelStart(this, &out.size, &al.http.code);
62e76326 890 return;
edce4d98 891 }
62e76326 892
8e2745f4 893 httpStart();
894}
895
896void
897ClientHttpRequest::httpStart()
898{
559da936 899 PROF_start(httpStart);
8e2745f4 900 logType = LOG_TAG_NONE;
bf8fe701 901 debugs(85, 4, "ClientHttpRequest::httpStart: " << log_tags[logType] << " for '" << uri << "'");
902
edce4d98 903 /* no one should have touched this */
8e2745f4 904 assert(out.offset == 0);
edce4d98 905 /* Use the Stream Luke */
8e2745f4 906 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
907 clientStreamRead(node, this, node->readBuffer);
559da936 908 PROF_stop(httpStart);
edce4d98 909}
0655fa4d 910
911bool
912ClientHttpRequest::gotEnough() const
913{
86a2f789 914 /** TODO: should be querying the stream. */
7173d5b0 915 int64_t contentLength =
06a5ae20 916 memObject()->getReply()->bodySize(request->method);
0655fa4d 917 assert(contentLength >= 0);
918
919 if (out.offset < contentLength)
920 return false;
921
922 return true;
923}
924
b51aec66 925void
47f6e231 926ClientHttpRequest::maxReplyBodySize(int64_t clen)
b51aec66 927{
928 maxReplyBodySize_ = clen;
929}
930
47f6e231 931int64_t
b51aec66 932ClientHttpRequest::maxReplyBodySize() const
933{
934 return maxReplyBodySize_;
935}
936
937bool
47f6e231 938ClientHttpRequest::isReplyBodyTooLarge(int64_t clen) const
b51aec66 939{
940 if (0 == maxReplyBodySize())
941 return 0; /* disabled */
942
943 if (clen < 0)
944 return 0; /* unknown */
945
946 return clen > maxReplyBodySize();
947}
86a2f789 948
949void
950ClientHttpRequest::storeEntry(StoreEntry *newEntry)
951{
952 entry_ = newEntry;
953}
954
0976f8db 955void
956ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
957{
958 if (loggingEntry_)
97b5e68f 959 loggingEntry_->unlock();
0976f8db 960
961 loggingEntry_ = newEntry;
962
963 if (loggingEntry_)
34266cde 964 loggingEntry_->lock()
965
966 ;
0976f8db 967}
86a2f789 968
de31d06f 969/*
970 * doCallouts() - This function controls the order of "callout"
971 * executions, including non-blocking access control checks, the
972 * redirector, and ICAP. Previously, these callouts were chained
973 * together such that "clientAccessCheckDone()" would call
974 * "clientRedirectStart()" and so on.
975 *
976 * The ClientRequestContext (aka calloutContext) class holds certain
977 * state data for the callout/callback operations. Previously
978 * ClientHttpRequest would sort of hand off control to ClientRequestContext
979 * for a short time. ClientRequestContext would then delete itself
980 * and pass control back to ClientHttpRequest when all callouts
981 * were finished.
982 *
983 * This caused some problems for ICAP because we want to make the
984 * ICAP callout after checking ACLs, but before checking the no_cache
985 * list. We can't stuff the ICAP state into the ClientRequestContext
986 * class because we still need the ICAP state after ClientRequestContext
987 * goes away.
988 *
989 * Note that ClientRequestContext is created before the first call
990 * to doCallouts().
991 *
992 * If one of the callouts notices that ClientHttpRequest is no
993 * longer valid, it should call cbdataReferenceDone() so that
994 * ClientHttpRequest's reference count goes to zero and it will get
995 * deleted. ClientHttpRequest will then delete ClientRequestContext.
996 *
997 * Note that we set the _done flags here before actually starting
998 * the callout. This is strictly for convenience.
999 */
1000
057f5854 1001extern int aclMapTOS (acl_tos * head, ACLChecklist * ch);
1002
de31d06f 1003void
1004ClientHttpRequest::doCallouts()
1005{
1006 assert(calloutContext);
1007
1008 if (!calloutContext->http_access_done) {
58d7150d 1009 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
57abaac0 1010 calloutContext->http_access_done = true;
de31d06f 1011 calloutContext->clientAccessCheck();
1012 return;
1013 }
1014
1015#if ICAP_CLIENT
ab7ac359 1016 if (TheICAPConfig.onoff && !calloutContext->icap_acl_check_done) {
58d7150d 1017 debugs(83, 3, HERE << "Doing calloutContext->icapAccessCheck()");
57abaac0 1018 calloutContext->icap_acl_check_done = true;
de31d06f 1019 calloutContext->icapAccessCheck();
1020 return;
1021 }
1022
1023#endif
1024
1025 if (!calloutContext->redirect_done) {
57abaac0 1026 calloutContext->redirect_done = true;
de31d06f 1027 assert(calloutContext->redirect_state == REDIRECT_NONE);
1028
1029 if (Config.Program.redirect) {
58d7150d 1030 debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
de31d06f 1031 calloutContext->redirect_state = REDIRECT_PENDING;
1032 calloutContext->clientRedirectStart();
1033 return;
1034 }
1035 }
1036
57abaac0 1037 if (!calloutContext->interpreted_req_hdrs) {
58d7150d 1038 debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
57abaac0 1039 calloutContext->interpreted_req_hdrs = 1;
1040 clientInterpretRequestHeaders(this);
1041 }
1042
de31d06f 1043 if (!calloutContext->no_cache_done) {
57abaac0 1044 calloutContext->no_cache_done = true;
de31d06f 1045
1046 if (Config.accessList.noCache && request->flags.cachable) {
58d7150d 1047 debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
de31d06f 1048 calloutContext->checkNoCache();
1049 return;
1050 }
1051 }
1052
057f5854 1053 if (!calloutContext->clientside_tos_done) {
1054 calloutContext->clientside_tos_done = true;
1055 if (getConn() != NULL) {
1056 ACLChecklist ch;
1057 ch.src_addr = request->client_addr;
1058 ch.my_addr = request->my_addr;
057f5854 1059 ch.request = HTTPMSGLOCK(request);
1060 int tos = aclMapTOS(Config.accessList.clientside_tos, &ch);
1061 if (tos)
1062 comm_set_tos(getConn()->fd, tos);
1063 }
1064 }
1065
de31d06f 1066 cbdataReferenceDone(calloutContext->http);
1067 delete calloutContext;
1068 calloutContext = NULL;
de31d06f 1069#if HEADERS_LOG
1070
1071 headersLog(0, 1, request->method, request);
1072#endif
1073
58d7150d 1074 debugs(83, 3, HERE << "calling processRequest()");
de31d06f 1075 processRequest();
1076}
1077
86a2f789 1078#ifndef _USE_INLINE_
1079#include "client_side_request.cci"
1080#endif
de31d06f 1081
1082#if ICAP_CLIENT
1083/*
5f8252d2 1084 * Initiate an ICAP transaction. Return false on errors.
1085 * The caller must handle errors.
de31d06f 1086 */
5f8252d2 1087bool
1088ClientHttpRequest::startIcap(ICAPServiceRep::Pointer service)
3b299123 1089{
5f8252d2 1090 debugs(85, 3, HERE << this << " ClientHttpRequest::startIcap() called");
1091 if (!service) {
bf8fe701 1092 debugs(85, 3, "ClientHttpRequest::startIcap fails: lack of service");
5f8252d2 1093 return false;
3b299123 1094 }
5f8252d2 1095 if (service->broken()) {
bf8fe701 1096 debugs(85, 3, "ClientHttpRequest::startIcap fails: broken service");
5f8252d2 1097 return false;
3b299123 1098 }
1099
5f8252d2 1100 assert(!icapHeadSource);
1101 assert(!icapBodySource);
9d4d7c5e 1102 icapHeadSource = initiateIcap(
1103 new ICAPModXactLauncher(this, request, NULL, service));
5f8252d2 1104 return true;
de31d06f 1105}
1106
1107void
9d4d7c5e 1108ClientHttpRequest::noteIcapAnswer(HttpMsg *msg)
de31d06f 1109{
de31d06f 1110 assert(cbdataReferenceValid(this)); // indicates bug
5f8252d2 1111 assert(msg);
1112
b044675d 1113 if (HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg)) {
200ac359 1114 /*
5f8252d2 1115 * Replace the old request with the new request.
200ac359 1116 */
6dd9f4bd 1117 HTTPMSGUNLOCK(request);
1118 request = HTTPMSGLOCK(new_req);
200ac359 1119 /*
1120 * Store the new URI for logging
1121 */
1122 xfree(uri);
1123 uri = xstrdup(urlCanonical(request));
1124 setLogUri(this, urlCanonicalClean(request));
1125 assert(request->method);
b044675d 1126 } else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
1127 debugs(85,3,HERE << "REQMOD reply is HTTP reply");
1128
5f8252d2 1129 // subscribe to receive reply body
1130 if (new_rep->body_pipe != NULL) {
1131 icapBodySource = new_rep->body_pipe;
1132 assert(icapBodySource->setConsumerIfNotLate(this));
1133 }
1134
b044675d 1135 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1136 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1137 repContext->createStoreEntry(request->method, request->flags);
1138
1139 EBIT_CLR(storeEntry()->flags, ENTRY_FWD_HDR_WAIT);
1140 request_satisfaction_mode = true;
1141 request_satisfaction_offset = 0;
1142 storeEntry()->replaceHttpReply(new_rep);
cb4c4288 1143
1144 if (!icapBodySource) // no body
1145 storeEntry()->complete();
b044675d 1146 clientGetMoreData(node, this);
200ac359 1147 }
de31d06f 1148
5f8252d2 1149 // we are done with getting headers (but may be receiving body)
9d4d7c5e 1150 clearIcap(icapHeadSource);
5f8252d2 1151
b044675d 1152 if (!request_satisfaction_mode)
1153 doCallouts();
de31d06f 1154}
1155
1156void
9d4d7c5e 1157ClientHttpRequest::noteIcapQueryAbort(bool final)
de31d06f 1158{
9d4d7c5e 1159 clearIcap(icapHeadSource);
5f8252d2 1160 assert(!icapBodySource);
9d4d7c5e 1161 handleIcapFailure(!final);
de31d06f 1162}
1163
1164void
5f8252d2 1165ClientHttpRequest::noteMoreBodyDataAvailable(BodyPipe &)
de31d06f 1166{
5f8252d2 1167 assert(request_satisfaction_mode);
1168 assert(icapBodySource != NULL);
1169
1170 if (const size_t contentSize = icapBodySource->buf().contentSize()) {
1171 BodyPipeCheckout bpc(*icapBodySource);
1172 const StoreIOBuffer ioBuf(&bpc.buf, request_satisfaction_offset);
1173 storeEntry()->write(ioBuf);
1174 // assume can write everything
1175 request_satisfaction_offset += contentSize;
1176 bpc.buf.consume(contentSize);
1177 bpc.checkIn();
1178 }
1179
1180 if (icapBodySource->exhausted())
1181 endRequestSatisfaction();
1182 // else wait for more body data
de31d06f 1183}
1184
1185void
5f8252d2 1186ClientHttpRequest::noteBodyProductionEnded(BodyPipe &)
de31d06f 1187{
5f8252d2 1188 assert(!icapHeadSource);
1189 if (icapBodySource != NULL) { // did not end request satisfaction yet
1190 // We do not expect more because noteMoreBodyDataAvailable always
1191 // consumes everything. We do not even have a mechanism to consume
1192 // leftovers after noteMoreBodyDataAvailable notifications seize.
1193 assert(icapBodySource->exhausted());
1194 endRequestSatisfaction();
1195 }
1196}
3b299123 1197
5f8252d2 1198void
1199ClientHttpRequest::endRequestSatisfaction() {
1200 debugs(85,4, HERE << this << " ends request satisfaction");
1201 assert(request_satisfaction_mode);
1202 stopConsumingFrom(icapBodySource);
3b299123 1203
5f8252d2 1204 // TODO: anything else needed to end store entry formation correctly?
1205 storeEntry()->complete();
1206}
de31d06f 1207
5f8252d2 1208void
1209ClientHttpRequest::noteBodyProducerAborted(BodyPipe &)
1210{
1211 assert(!icapHeadSource);
1212 stopConsumingFrom(icapBodySource);
1213 handleIcapFailure();
1214}
3b299123 1215
5f8252d2 1216void
9d4d7c5e 1217ClientHttpRequest::handleIcapFailure(bool bypassable)
5f8252d2 1218{
9d4d7c5e 1219 debugs(85,3, HERE << "handleIcapFailure(" << bypassable << ")");
3b299123 1220
5f8252d2 1221 const bool usedStore = storeEntry() && !storeEntry()->isEmpty();
1222 const bool usedPipe = request->body_pipe != NULL &&
1223 request->body_pipe->consumedSize() > 0;
3b299123 1224
9d4d7c5e 1225 if (bypassable && !usedStore && !usedPipe) {
1226 debugs(85,3, HERE << "ICAP REQMOD callout failed, bypassing: " << calloutContext);
5f8252d2 1227 if (calloutContext)
1228 doCallouts();
1229 return;
1230 }
3b299123 1231
5f8252d2 1232 debugs(85,3, HERE << "ICAP REQMOD callout failed, responding with error");
3b299123 1233
5f8252d2 1234 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1235 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1236 assert(repContext);
de31d06f 1237
5f8252d2 1238 // The original author of the code also wanted to pass an errno to
1239 // setReplyToError, but it seems unlikely that the errno reflects the
1240 // true cause of the error at this point, so I did not pass it.
b70ba605 1241 IPAddress noAddr;
1242 noAddr.SetNoAddr();
5f8252d2 1243 ConnStateData::Pointer c = getConn();
1244 repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
1245 request->method, NULL,
b70ba605 1246 (c != NULL ? c->peer : noAddr), request, NULL,
5f8252d2 1247 (c != NULL && c->auth_user_request ?
1248 c->auth_user_request : request->auth_user_request));
de31d06f 1249
5f8252d2 1250 node = (clientStreamNode *)client_stream.tail->data;
1251 clientStreamRead(node, this, node->readBuffer);
de31d06f 1252}
1253
1254#endif