]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
Fix compile error when cpu profiling is enabled.
[thirdparty/squid.git] / src / client_side_request.cc
CommitLineData
edce4d98 1
2/*
60745f24 3 * $Id: client_side_request.cc,v 1.101 2008/01/20 08:54:28 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 */
60745f24 274clientBeginRequest(const HttpRequestMethod& 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 " <<
60745f24 403 RequestMethodStr(http->request->method) << " " <<
bf8fe701 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;
60745f24 556 HttpRequestMethod method = request->method;
edce4d98 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 }
60745f24 659
660 if (METHOD_OTHER == request->method) {
661 no_cache++;
662 }
62e76326 663
edce4d98 664#endif
665 if (no_cache) {
666#if HTTP_VIOLATIONS
62e76326 667
668 if (Config.onoff.reload_into_ims)
669 request->flags.nocache_hack = 1;
670 else if (refresh_nocache_hack)
671 request->flags.nocache_hack = 1;
672 else
edce4d98 673#endif
62e76326 674
675 request->flags.nocache = 1;
edce4d98 676 }
62e76326 677
0ef77270 678 /* ignore range header in non-GETs or non-HEADs */
679 if (request->method == METHOD_GET || request->method == METHOD_HEAD) {
a9925b40 680 request->range = req_hdr->getRange();
62e76326 681
682 if (request->range) {
683 request->flags.range = 1;
684 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
685 /* XXX: This is suboptimal. We should give the stream the range set,
686 * and thereby let the top of the stream set the offset when the
687 * size becomes known. As it is, we will end up requesting from 0
688 * for evey -X range specification.
689 * RBC - this may be somewhat wrong. We should probably set the range
690 * iter up at this point.
691 */
692 node->readBuffer.offset = request->range->lowestOffset(0);
693 http->range_iter.pos = request->range->begin();
694 http->range_iter.valid = true;
695 }
edce4d98 696 }
62e76326 697
0ef77270 698 /* Only HEAD and GET requests permit a Range or Request-Range header.
699 * If these headers appear on any other type of request, delete them now.
700 */
701 else {
702 req_hdr->delById(HDR_RANGE);
703 req_hdr->delById(HDR_REQUEST_RANGE);
704 request->range = NULL;
705 }
706
a9925b40 707 if (req_hdr->has(HDR_AUTHORIZATION))
62e76326 708 request->flags.auth = 1;
709
edce4d98 710 if (request->login[0] != '\0')
62e76326 711 request->flags.auth = 1;
712
a9925b40 713 if (req_hdr->has(HDR_VIA)) {
30abd221 714 String s = req_hdr->getList(HDR_VIA);
62e76326 715 /*
716 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
717 * Note ThisCache2 has a space prepended to the hostname so we don't
718 * accidentally match super-domains.
719 */
720
721 if (strListIsSubstr(&s, ThisCache2, ',')) {
722 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
723 request, (ObjPackMethod) & httpRequestPack);
724 request->flags.loopdetect = 1;
725 }
726
edce4d98 727#if FORW_VIA_DB
30abd221 728 fvdbCountVia(s.buf());
62e76326 729
edce4d98 730#endif
62e76326 731
30abd221 732 s.clean();
edce4d98 733 }
62e76326 734
5ad35449 735/**
736 \todo --enable-useragent-log and --enable-referer-log. We should
737 probably drop those two as the custom log formats accomplish pretty much the same thing..
738*/
edce4d98 739#if USE_USERAGENT_LOG
a9925b40 740 if ((str = req_hdr->getStr(HDR_USER_AGENT)))
b4306cba 741 logUserAgent(fqdnFromAddr(http->getConn()->log_addr), str);
62e76326 742
edce4d98 743#endif
744#if USE_REFERER_LOG
62e76326 745
a9925b40 746 if ((str = req_hdr->getStr(HDR_REFERER)))
b4306cba 747 logReferer(fqdnFromAddr(http->getConn()->log_addr), str, http->log_uri);
62e76326 748
edce4d98 749#endif
750#if FORW_VIA_DB
62e76326 751
a9925b40 752 if (req_hdr->has(HDR_X_FORWARDED_FOR)) {
30abd221 753 String s = req_hdr->getList(HDR_X_FORWARDED_FOR);
754 fvdbCountForw(s.buf());
755 s.clean();
edce4d98 756 }
62e76326 757
edce4d98 758#endif
759 if (request->method == METHOD_TRACE) {
a9925b40 760 request->max_forwards = req_hdr->getInt(HDR_MAX_FORWARDS);
edce4d98 761 }
62e76326 762
610ee341 763 request->flags.cachable = http->request->cacheable();
62e76326 764
edce4d98 765 if (clientHierarchical(http))
62e76326 766 request->flags.hierarchical = 1;
767
bf8fe701 768 debugs(85, 5, "clientInterpretRequestHeaders: REQ_NOCACHE = " <<
769 (request->flags.nocache ? "SET" : "NOT SET"));
770 debugs(85, 5, "clientInterpretRequestHeaders: REQ_CACHABLE = " <<
771 (request->flags.cachable ? "SET" : "NOT SET"));
772 debugs(85, 5, "clientInterpretRequestHeaders: REQ_HIERARCHICAL = " <<
773 (request->flags.hierarchical ? "SET" : "NOT SET"));
62e76326 774
edce4d98 775}
776
777void
de31d06f 778clientRedirectDoneWrapper(void *data, char *result)
edce4d98 779{
de31d06f 780 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
db02222f 781
de31d06f 782 if (!calloutContext->httpStateIsValid())
62e76326 783 return;
62e76326 784
de31d06f 785 calloutContext->clientRedirectDone(result);
786}
787
788void
789ClientRequestContext::clientRedirectDone(char *result)
790{
190154cf 791 HttpRequest *new_request = NULL;
792 HttpRequest *old_request = http->request;
bf8fe701 793 debugs(85, 5, "clientRedirectDone: '" << http->uri << "' result=" << (result ? result : "NULL"));
de31d06f 794 assert(redirect_state == REDIRECT_PENDING);
795 redirect_state = REDIRECT_DONE;
62e76326 796
edce4d98 797 if (result) {
62e76326 798 http_status status = (http_status) atoi(result);
799
800 if (status == HTTP_MOVED_PERMANENTLY
801 || status == HTTP_MOVED_TEMPORARILY
802 || status == HTTP_SEE_OTHER
803 || status == HTTP_TEMPORARY_REDIRECT) {
804 char *t = result;
805
806 if ((t = strchr(result, ':')) != NULL) {
807 http->redirect.status = status;
808 http->redirect.location = xstrdup(t + 1);
809 } else {
bf8fe701 810 debugs(85, 1, "clientRedirectDone: bad input: " << result);
62e76326 811 }
2baf58c3 812 } else if (strcmp(result, http->uri))
c21ad0f5 813 new_request = HttpRequest::CreateFromUrlAndMethod(result, old_request->method);
edce4d98 814 }
62e76326 815
edce4d98 816 if (new_request) {
62e76326 817 safe_free(http->uri);
818 http->uri = xstrdup(urlCanonical(new_request));
819 new_request->http_ver = old_request->http_ver;
a9925b40 820 new_request->header.append(&old_request->header);
62e76326 821 new_request->client_addr = old_request->client_addr;
822 new_request->my_addr = old_request->my_addr;
62e76326 823 new_request->flags = old_request->flags;
3c1f01bc 824 new_request->flags.redirected = 1;
62e76326 825
826 if (old_request->auth_user_request) {
827 new_request->auth_user_request = old_request->auth_user_request;
4f0ef8e8 828 AUTHUSERREQUESTLOCK(new_request->auth_user_request, "new request");
62e76326 829 }
830
5f8252d2 831 if (old_request->body_pipe != NULL) {
832 new_request->body_pipe = old_request->body_pipe;
833 old_request->body_pipe = NULL;
834 debugs(0,0,HERE << "redirecting body_pipe " << new_request->body_pipe << " from request " << old_request << " to " << new_request);
62e76326 835 }
836
837 new_request->content_length = old_request->content_length;
abb929f0 838 new_request->extacl_user = old_request->extacl_user;
839 new_request->extacl_passwd = old_request->extacl_passwd;
62e76326 840 new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
6dd9f4bd 841 HTTPMSGUNLOCK(old_request);
842 http->request = HTTPMSGLOCK(new_request);
edce4d98 843 }
62e76326 844
edce4d98 845 /* FIXME PIPELINE: This is innacurate during pipelining */
62e76326 846
5f8252d2 847 if (http->getConn() != NULL)
98242069 848 fd_note(http->getConn()->fd, http->uri);
62e76326 849
c8be6d7b 850 assert(http->uri);
62e76326 851
de31d06f 852 http->doCallouts();
edce4d98 853}
854
855void
8e2745f4 856ClientRequestContext::checkNoCache()
edce4d98 857{
de31d06f 858 acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
57abaac0 859 acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, this);
edce4d98 860}
861
de31d06f 862static void
863checkNoCacheDoneWrapper(int answer, void *data)
edce4d98 864{
de31d06f 865 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
e4a67a80 866
de31d06f 867 if (!calloutContext->httpStateIsValid())
868 return;
869
870 calloutContext->checkNoCacheDone(answer);
8e2745f4 871}
4fb35c3c 872
8e2745f4 873void
874ClientRequestContext::checkNoCacheDone(int answer)
62e76326 875{
8e2745f4 876 acl_checklist = NULL;
de31d06f 877 http->request->flags.cachable = answer;
878 http->doCallouts();
edce4d98 879}
880
69660be0 881/*
882 * Identify requests that do not go through the store and client side stream
883 * and forward them to the appropriate location. All other requests, request
884 * them.
edce4d98 885 */
886void
8e2745f4 887ClientHttpRequest::processRequest()
edce4d98 888{
60745f24 889 debugs(85, 4, "clientProcessRequest: " << RequestMethodStr(request->method) << " '" << uri << "'");
62e76326 890
2baf58c3 891 if (request->method == METHOD_CONNECT && !redirect.status) {
62e76326 892 logType = LOG_TCP_MISS;
11007d4b 893 tunnelStart(this, &out.size, &al.http.code);
62e76326 894 return;
edce4d98 895 }
62e76326 896
8e2745f4 897 httpStart();
898}
899
900void
901ClientHttpRequest::httpStart()
902{
559da936 903 PROF_start(httpStart);
8e2745f4 904 logType = LOG_TAG_NONE;
bf8fe701 905 debugs(85, 4, "ClientHttpRequest::httpStart: " << log_tags[logType] << " for '" << uri << "'");
906
edce4d98 907 /* no one should have touched this */
8e2745f4 908 assert(out.offset == 0);
edce4d98 909 /* Use the Stream Luke */
8e2745f4 910 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
911 clientStreamRead(node, this, node->readBuffer);
559da936 912 PROF_stop(httpStart);
edce4d98 913}
0655fa4d 914
915bool
916ClientHttpRequest::gotEnough() const
917{
86a2f789 918 /** TODO: should be querying the stream. */
7173d5b0 919 int64_t contentLength =
06a5ae20 920 memObject()->getReply()->bodySize(request->method);
0655fa4d 921 assert(contentLength >= 0);
922
923 if (out.offset < contentLength)
924 return false;
925
926 return true;
927}
928
b51aec66 929void
47f6e231 930ClientHttpRequest::maxReplyBodySize(int64_t clen)
b51aec66 931{
932 maxReplyBodySize_ = clen;
933}
934
47f6e231 935int64_t
b51aec66 936ClientHttpRequest::maxReplyBodySize() const
937{
938 return maxReplyBodySize_;
939}
940
941bool
47f6e231 942ClientHttpRequest::isReplyBodyTooLarge(int64_t clen) const
b51aec66 943{
944 if (0 == maxReplyBodySize())
945 return 0; /* disabled */
946
947 if (clen < 0)
948 return 0; /* unknown */
949
950 return clen > maxReplyBodySize();
951}
86a2f789 952
953void
954ClientHttpRequest::storeEntry(StoreEntry *newEntry)
955{
956 entry_ = newEntry;
957}
958
0976f8db 959void
960ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
961{
962 if (loggingEntry_)
97b5e68f 963 loggingEntry_->unlock();
0976f8db 964
965 loggingEntry_ = newEntry;
966
967 if (loggingEntry_)
34266cde 968 loggingEntry_->lock()
969
970 ;
0976f8db 971}
86a2f789 972
de31d06f 973/*
974 * doCallouts() - This function controls the order of "callout"
975 * executions, including non-blocking access control checks, the
976 * redirector, and ICAP. Previously, these callouts were chained
977 * together such that "clientAccessCheckDone()" would call
978 * "clientRedirectStart()" and so on.
979 *
980 * The ClientRequestContext (aka calloutContext) class holds certain
981 * state data for the callout/callback operations. Previously
982 * ClientHttpRequest would sort of hand off control to ClientRequestContext
983 * for a short time. ClientRequestContext would then delete itself
984 * and pass control back to ClientHttpRequest when all callouts
985 * were finished.
986 *
987 * This caused some problems for ICAP because we want to make the
988 * ICAP callout after checking ACLs, but before checking the no_cache
989 * list. We can't stuff the ICAP state into the ClientRequestContext
990 * class because we still need the ICAP state after ClientRequestContext
991 * goes away.
992 *
993 * Note that ClientRequestContext is created before the first call
994 * to doCallouts().
995 *
996 * If one of the callouts notices that ClientHttpRequest is no
997 * longer valid, it should call cbdataReferenceDone() so that
998 * ClientHttpRequest's reference count goes to zero and it will get
999 * deleted. ClientHttpRequest will then delete ClientRequestContext.
1000 *
1001 * Note that we set the _done flags here before actually starting
1002 * the callout. This is strictly for convenience.
1003 */
1004
057f5854 1005extern int aclMapTOS (acl_tos * head, ACLChecklist * ch);
1006
de31d06f 1007void
1008ClientHttpRequest::doCallouts()
1009{
1010 assert(calloutContext);
1011
1012 if (!calloutContext->http_access_done) {
58d7150d 1013 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
57abaac0 1014 calloutContext->http_access_done = true;
de31d06f 1015 calloutContext->clientAccessCheck();
1016 return;
1017 }
1018
1019#if ICAP_CLIENT
ab7ac359 1020 if (TheICAPConfig.onoff && !calloutContext->icap_acl_check_done) {
58d7150d 1021 debugs(83, 3, HERE << "Doing calloutContext->icapAccessCheck()");
57abaac0 1022 calloutContext->icap_acl_check_done = true;
de31d06f 1023 calloutContext->icapAccessCheck();
1024 return;
1025 }
1026
1027#endif
1028
1029 if (!calloutContext->redirect_done) {
57abaac0 1030 calloutContext->redirect_done = true;
de31d06f 1031 assert(calloutContext->redirect_state == REDIRECT_NONE);
1032
1033 if (Config.Program.redirect) {
58d7150d 1034 debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
de31d06f 1035 calloutContext->redirect_state = REDIRECT_PENDING;
1036 calloutContext->clientRedirectStart();
1037 return;
1038 }
1039 }
1040
57abaac0 1041 if (!calloutContext->interpreted_req_hdrs) {
58d7150d 1042 debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
57abaac0 1043 calloutContext->interpreted_req_hdrs = 1;
1044 clientInterpretRequestHeaders(this);
1045 }
1046
de31d06f 1047 if (!calloutContext->no_cache_done) {
57abaac0 1048 calloutContext->no_cache_done = true;
de31d06f 1049
1050 if (Config.accessList.noCache && request->flags.cachable) {
58d7150d 1051 debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
de31d06f 1052 calloutContext->checkNoCache();
1053 return;
1054 }
1055 }
1056
057f5854 1057 if (!calloutContext->clientside_tos_done) {
1058 calloutContext->clientside_tos_done = true;
1059 if (getConn() != NULL) {
1060 ACLChecklist ch;
1061 ch.src_addr = request->client_addr;
1062 ch.my_addr = request->my_addr;
057f5854 1063 ch.request = HTTPMSGLOCK(request);
1064 int tos = aclMapTOS(Config.accessList.clientside_tos, &ch);
1065 if (tos)
1066 comm_set_tos(getConn()->fd, tos);
1067 }
1068 }
1069
de31d06f 1070 cbdataReferenceDone(calloutContext->http);
1071 delete calloutContext;
1072 calloutContext = NULL;
de31d06f 1073#if HEADERS_LOG
1074
1075 headersLog(0, 1, request->method, request);
1076#endif
1077
58d7150d 1078 debugs(83, 3, HERE << "calling processRequest()");
de31d06f 1079 processRequest();
1080}
1081
86a2f789 1082#ifndef _USE_INLINE_
1083#include "client_side_request.cci"
1084#endif
de31d06f 1085
1086#if ICAP_CLIENT
1087/*
5f8252d2 1088 * Initiate an ICAP transaction. Return false on errors.
1089 * The caller must handle errors.
de31d06f 1090 */
5f8252d2 1091bool
1092ClientHttpRequest::startIcap(ICAPServiceRep::Pointer service)
3b299123 1093{
5f8252d2 1094 debugs(85, 3, HERE << this << " ClientHttpRequest::startIcap() called");
1095 if (!service) {
bf8fe701 1096 debugs(85, 3, "ClientHttpRequest::startIcap fails: lack of service");
5f8252d2 1097 return false;
3b299123 1098 }
5f8252d2 1099 if (service->broken()) {
bf8fe701 1100 debugs(85, 3, "ClientHttpRequest::startIcap fails: broken service");
5f8252d2 1101 return false;
3b299123 1102 }
1103
5f8252d2 1104 assert(!icapHeadSource);
1105 assert(!icapBodySource);
9d4d7c5e 1106 icapHeadSource = initiateIcap(
1107 new ICAPModXactLauncher(this, request, NULL, service));
5f8252d2 1108 return true;
de31d06f 1109}
1110
1111void
9d4d7c5e 1112ClientHttpRequest::noteIcapAnswer(HttpMsg *msg)
de31d06f 1113{
de31d06f 1114 assert(cbdataReferenceValid(this)); // indicates bug
5f8252d2 1115 assert(msg);
1116
b044675d 1117 if (HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg)) {
200ac359 1118 /*
5f8252d2 1119 * Replace the old request with the new request.
200ac359 1120 */
6dd9f4bd 1121 HTTPMSGUNLOCK(request);
1122 request = HTTPMSGLOCK(new_req);
200ac359 1123 /*
1124 * Store the new URI for logging
1125 */
1126 xfree(uri);
1127 uri = xstrdup(urlCanonical(request));
1128 setLogUri(this, urlCanonicalClean(request));
1129 assert(request->method);
b044675d 1130 } else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
1131 debugs(85,3,HERE << "REQMOD reply is HTTP reply");
1132
5f8252d2 1133 // subscribe to receive reply body
1134 if (new_rep->body_pipe != NULL) {
1135 icapBodySource = new_rep->body_pipe;
1136 assert(icapBodySource->setConsumerIfNotLate(this));
1137 }
1138
b044675d 1139 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1140 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1141 repContext->createStoreEntry(request->method, request->flags);
1142
1143 EBIT_CLR(storeEntry()->flags, ENTRY_FWD_HDR_WAIT);
1144 request_satisfaction_mode = true;
1145 request_satisfaction_offset = 0;
1146 storeEntry()->replaceHttpReply(new_rep);
cb4c4288 1147
1148 if (!icapBodySource) // no body
1149 storeEntry()->complete();
b044675d 1150 clientGetMoreData(node, this);
200ac359 1151 }
de31d06f 1152
5f8252d2 1153 // we are done with getting headers (but may be receiving body)
9d4d7c5e 1154 clearIcap(icapHeadSource);
5f8252d2 1155
b044675d 1156 if (!request_satisfaction_mode)
1157 doCallouts();
de31d06f 1158}
1159
1160void
9d4d7c5e 1161ClientHttpRequest::noteIcapQueryAbort(bool final)
de31d06f 1162{
9d4d7c5e 1163 clearIcap(icapHeadSource);
5f8252d2 1164 assert(!icapBodySource);
9d4d7c5e 1165 handleIcapFailure(!final);
de31d06f 1166}
1167
1168void
5f8252d2 1169ClientHttpRequest::noteMoreBodyDataAvailable(BodyPipe &)
de31d06f 1170{
5f8252d2 1171 assert(request_satisfaction_mode);
1172 assert(icapBodySource != NULL);
1173
1174 if (const size_t contentSize = icapBodySource->buf().contentSize()) {
1175 BodyPipeCheckout bpc(*icapBodySource);
1176 const StoreIOBuffer ioBuf(&bpc.buf, request_satisfaction_offset);
1177 storeEntry()->write(ioBuf);
1178 // assume can write everything
1179 request_satisfaction_offset += contentSize;
1180 bpc.buf.consume(contentSize);
1181 bpc.checkIn();
1182 }
1183
1184 if (icapBodySource->exhausted())
1185 endRequestSatisfaction();
1186 // else wait for more body data
de31d06f 1187}
1188
1189void
5f8252d2 1190ClientHttpRequest::noteBodyProductionEnded(BodyPipe &)
de31d06f 1191{
5f8252d2 1192 assert(!icapHeadSource);
1193 if (icapBodySource != NULL) { // did not end request satisfaction yet
1194 // We do not expect more because noteMoreBodyDataAvailable always
1195 // consumes everything. We do not even have a mechanism to consume
1196 // leftovers after noteMoreBodyDataAvailable notifications seize.
1197 assert(icapBodySource->exhausted());
1198 endRequestSatisfaction();
1199 }
1200}
3b299123 1201
5f8252d2 1202void
1203ClientHttpRequest::endRequestSatisfaction() {
1204 debugs(85,4, HERE << this << " ends request satisfaction");
1205 assert(request_satisfaction_mode);
1206 stopConsumingFrom(icapBodySource);
3b299123 1207
5f8252d2 1208 // TODO: anything else needed to end store entry formation correctly?
1209 storeEntry()->complete();
1210}
de31d06f 1211
5f8252d2 1212void
1213ClientHttpRequest::noteBodyProducerAborted(BodyPipe &)
1214{
1215 assert(!icapHeadSource);
1216 stopConsumingFrom(icapBodySource);
1217 handleIcapFailure();
1218}
3b299123 1219
5f8252d2 1220void
9d4d7c5e 1221ClientHttpRequest::handleIcapFailure(bool bypassable)
5f8252d2 1222{
9d4d7c5e 1223 debugs(85,3, HERE << "handleIcapFailure(" << bypassable << ")");
3b299123 1224
5f8252d2 1225 const bool usedStore = storeEntry() && !storeEntry()->isEmpty();
1226 const bool usedPipe = request->body_pipe != NULL &&
1227 request->body_pipe->consumedSize() > 0;
3b299123 1228
9d4d7c5e 1229 if (bypassable && !usedStore && !usedPipe) {
1230 debugs(85,3, HERE << "ICAP REQMOD callout failed, bypassing: " << calloutContext);
5f8252d2 1231 if (calloutContext)
1232 doCallouts();
1233 return;
1234 }
3b299123 1235
5f8252d2 1236 debugs(85,3, HERE << "ICAP REQMOD callout failed, responding with error");
3b299123 1237
5f8252d2 1238 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1239 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1240 assert(repContext);
de31d06f 1241
5f8252d2 1242 // The original author of the code also wanted to pass an errno to
1243 // setReplyToError, but it seems unlikely that the errno reflects the
1244 // true cause of the error at this point, so I did not pass it.
b70ba605 1245 IPAddress noAddr;
1246 noAddr.SetNoAddr();
5f8252d2 1247 ConnStateData::Pointer c = getConn();
1248 repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
1249 request->method, NULL,
b70ba605 1250 (c != NULL ? c->peer : noAddr), request, NULL,
5f8252d2 1251 (c != NULL && c->auth_user_request ?
1252 c->auth_user_request : request->auth_user_request));
de31d06f 1253
5f8252d2 1254 node = (clientStreamNode *)client_stream.tail->data;
1255 clientStreamRead(node, this, node->readBuffer);
de31d06f 1256}
1257
1258#endif