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