]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
Adjust refresh_pattern min-age to make 0 mean 0, not 1 second
[thirdparty/squid.git] / src / client_side_request.cc
CommitLineData
edce4d98 1
2/*
58d7150d 3 * $Id: client_side_request.cc,v 1.87 2007/05/18 18:26:01 wessels 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
501b58f9 115 if (acl_checklist) {
116 if (acl_checklist->asyncInProgress()) {
117 acl_checklist->markDeleteWhenDone();
118 } else {
119 delete acl_checklist;
120 }
121 }
6ec67de9 122
5f8252d2 123 debugs(85,3, HERE << this << " ClientRequestContext destructed");
8e2745f4 124}
125
4e2eb5c3 126ClientRequestContext::ClientRequestContext(ClientHttpRequest *anHttp) : http(cbdataReference(anHttp)), acl_checklist (NULL), redirect_state (REDIRECT_NONE)
edce4d98 127{
57abaac0 128 http_access_done = false;
129 redirect_done = false;
130 no_cache_done = false;
131 interpreted_req_hdrs = false;
0b86805b 132 debugs(85,3, HERE << this << " ClientRequestContext constructed");
edce4d98 133}
134
528b2c61 135CBDATA_CLASS_INIT(ClientHttpRequest);
8e2745f4 136
528b2c61 137void *
138ClientHttpRequest::operator new (size_t size)
139{
140 assert (size == sizeof (ClientHttpRequest));
141 CBDATA_INIT_TYPE(ClientHttpRequest);
142 ClientHttpRequest *result = cbdataAlloc(ClientHttpRequest);
aa625860 143 return result;
528b2c61 144}
145
62e76326 146void
528b2c61 147ClientHttpRequest::operator delete (void *address)
148{
aa625860 149 ClientHttpRequest *t = static_cast<ClientHttpRequest *>(address);
150 cbdataFree(t);
528b2c61 151}
152
a0355e95 153ClientHttpRequest::ClientHttpRequest(ConnStateData::Pointer aConn) : loggingEntry_(NULL)
528b2c61 154{
528b2c61 155 start = current_time;
a0355e95 156 setConn(aConn);
157 dlinkAdd(this, &active, &ClientActiveRequests);
b044675d 158#if ICAP_CLIENT
b044675d 159 request_satisfaction_mode = false;
160#endif
528b2c61 161}
162
0655fa4d 163/*
164 * returns true if client specified that the object must come from the cache
165 * without contacting origin server
166 */
167bool
168ClientHttpRequest::onlyIfCached()const
169{
170 assert(request);
171 return request->cache_control &&
172 EBIT_TEST(request->cache_control->mask, CC_ONLY_IF_CACHED);
173}
174
528b2c61 175/*
176 * This function is designed to serve a fairly specific purpose.
177 * Occasionally our vBNS-connected caches can talk to each other, but not
178 * the rest of the world. Here we try to detect frequent failures which
179 * make the cache unusable (e.g. DNS lookup and connect() failures). If
180 * the failure:success ratio goes above 1.0 then we go into "hit only"
181 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
182 * will only fetch HITs from us if they are using the ICP protocol. We
183 * stay in this mode for 5 minutes.
184 *
185 * Duane W., Sept 16, 1996
186 */
187
188#define FAILURE_MODE_TIME 300
189
190static void
191checkFailureRatio(err_type etype, hier_code hcode)
192{
193 static double magic_factor = 100.0;
194 double n_good;
195 double n_bad;
62e76326 196
528b2c61 197 if (hcode == HIER_NONE)
62e76326 198 return;
199
528b2c61 200 n_good = magic_factor / (1.0 + request_failure_ratio);
62e76326 201
528b2c61 202 n_bad = magic_factor - n_good;
62e76326 203
528b2c61 204 switch (etype) {
62e76326 205
528b2c61 206 case ERR_DNS_FAIL:
62e76326 207
528b2c61 208 case ERR_CONNECT_FAIL:
62e76326 209
528b2c61 210 case ERR_READ_ERROR:
62e76326 211 n_bad++;
212 break;
213
528b2c61 214 default:
62e76326 215 n_good++;
528b2c61 216 }
62e76326 217
528b2c61 218 request_failure_ratio = n_bad / n_good;
62e76326 219
528b2c61 220 if (hit_only_mode_until > squid_curtime)
62e76326 221 return;
222
528b2c61 223 if (request_failure_ratio < 1.0)
62e76326 224 return;
225
bf8fe701 226 debugs(33, 0, "Failure Ratio at "<< std::setw(4)<<
227 std::setprecision(3) << request_failure_ratio);
62e76326 228
bf8fe701 229 debugs(33, 0, "Going into hit-only-mode for " <<
230 FAILURE_MODE_TIME / 60 << " minutes...");
62e76326 231
528b2c61 232 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
62e76326 233
528b2c61 234 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
235}
236
237ClientHttpRequest::~ClientHttpRequest()
238{
bf8fe701 239 debugs(33, 3, "httpRequestFree: " << uri);
72bdee4c 240 PROF_start(httpRequestFree);
62e76326 241
5f8252d2 242 // Even though freeResources() below may destroy the request,
243 // we no longer set request->body_pipe to NULL here
244 // because we did not initiate that pipe (ConnStateData did)
62e76326 245
528b2c61 246 /* the ICP check here was erroneous
d88e3c49 247 * - StoreEntry::releaseRequest was always called if entry was valid
528b2c61 248 */
249 assert(logType < LOG_TYPE_MAX);
9ce7856a 250
528b2c61 251 logRequest();
9ce7856a 252
0976f8db 253 loggingEntry(NULL);
254
528b2c61 255 if (request)
62e76326 256 checkFailureRatio(request->errType, al.hier.code);
257
528b2c61 258 freeResources();
62e76326 259
de31d06f 260#if ICAP_CLIENT
9d4d7c5e 261 announceInitiatorAbort(icapHeadSource);
262
5f8252d2 263 if (icapBodySource != NULL)
264 stopConsumingFrom(icapBodySource);
de31d06f 265#endif
9ce7856a 266
de31d06f 267 if (calloutContext)
268 delete calloutContext;
269
528b2c61 270 /* moving to the next connection is handled by the context free */
271 dlinkDelete(&active, &ClientActiveRequests);
9ce7856a 272
72bdee4c 273 PROF_stop(httpRequestFree);
528b2c61 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) {
bf8fe701 311 debugs(85, 5, "Invalid URL: " << http->uri);
62e76326 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;
bf8fe701 410 debugs(85, 2, "The request " <<
411 RequestMethodStr[http->request->method] << " " <<
412 http->uri << " is " <<
413 (answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED") <<
414 ", because it matched '" <<
415 (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" );
f5691f9c 416 char const *proxy_auth_msg = "<null>";
417
94a396a3 418 if (http->getConn() != NULL && http->getConn()->auth_user_request != NULL)
f5691f9c 419 proxy_auth_msg = http->getConn()->auth_user_request->denyMessage("<null>");
420 else if (http->request->auth_user_request != NULL)
421 proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
62e76326 422
de31d06f 423 if (answer != ACCESS_ALLOWED) {
62e76326 424 /* Send an error */
9ce7856a 425 int require_auth = (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName));
bf8fe701 426 debugs(85, 5, "Access Denied: " << http->uri);
427 debugs(85, 5, "AclMatchedName = " << (AclMatchedName ? AclMatchedName : "<null>"));
9ce7856a 428
429 if (require_auth)
bf8fe701 430 debugs(33, 5, "Proxy Auth Message = " << (proxy_auth_msg ? proxy_auth_msg : "<null>"));
9ce7856a 431
62e76326 432 /*
433 * NOTE: get page_id here, based on AclMatchedName because if
434 * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
435 * the clientCreateStoreEntry() call just below. Pedro Ribeiro
436 * <pribeiro@isel.pt>
437 */
9ce7856a 438 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, answer != ACCESS_REQ_PROXY_AUTH);
439
62e76326 440 http->logType = LOG_TCP_DENIED;
441
9ce7856a 442 if (require_auth) {
62e76326 443 if (!http->flags.accel) {
444 /* Proxy authorisation needed */
445 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
446 } else {
447 /* WWW authorisation needed */
448 status = HTTP_UNAUTHORIZED;
449 }
450
451 if (page_id == ERR_NONE)
452 page_id = ERR_CACHE_ACCESS_DENIED;
453 } else {
454 status = HTTP_FORBIDDEN;
455
456 if (page_id == ERR_NONE)
457 page_id = ERR_ACCESS_DENIED;
458 }
459
de31d06f 460 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
0655fa4d 461 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
462 assert (repContext);
463 repContext->setReplyToError(page_id, status,
464 http->request->method, NULL,
94a396a3 465 http->getConn() != NULL ? &http->getConn()->peer.sin_addr : &no_addr, http->request,
466 NULL, http->getConn() != NULL
98242069 467 && http->getConn()->auth_user_request ? http->getConn()->
0655fa4d 468 auth_user_request : http->request->auth_user_request);
62e76326 469 node = (clientStreamNode *)http->client_stream.tail->data;
470 clientStreamRead(node, http, node->readBuffer);
a546b04b 471 return;
edce4d98 472 }
de31d06f 473
474 /* ACCESS_ALLOWED continues here ... */
475 safe_free(http->uri);
476
477 http->uri = xstrdup(urlCanonical(http->request));
478
479 http->doCallouts();
480}
481
482#if ICAP_CLIENT
483void
484ClientRequestContext::icapAccessCheck()
485{
486 ICAPAccessCheck *icap_access_check;
487
e8fe1384 488 icap_access_check = new ICAPAccessCheck(ICAP::methodReqmod, ICAP::pointPreCache, http->request, NULL, icapAclCheckDoneWrapper, this);
489
490 if (icap_access_check != NULL) {
de31d06f 491 icap_access_check->check();
492 return;
493 }
494
495 http->doCallouts();
496}
497
498static void
499icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data)
500{
501 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
502
503 if (!calloutContext->httpStateIsValid())
504 return;
505
506 calloutContext->icapAclCheckDone(service);
507}
508
509void
510ClientRequestContext::icapAclCheckDone(ICAPServiceRep::Pointer service)
511{
6ec67de9 512 debugs(93,3,HERE << this << " icapAclCheckDone called");
6ec67de9 513 assert(http);
514
5f8252d2 515 if (http->startIcap(service))
de31d06f 516 return;
517
5f8252d2 518 if (!service || service->bypass) {
519 // handle ICAP start failure when no service was selected
520 // or where the selected service was optional
521 http->doCallouts();
522 return;
523 }
de31d06f 524
5f8252d2 525 // handle start failure for an essential ICAP service
526 http->handleIcapFailure();
edce4d98 527}
528
de31d06f 529#endif
530
14cc8559 531static void
532clientRedirectAccessCheckDone(int answer, void *data)
533{
534 ClientRequestContext *context = (ClientRequestContext *)data;
59a1efb2 535 ClientHttpRequest *http = context->http;
14cc8559 536 context->acl_checklist = NULL;
537
538 if (answer == ACCESS_ALLOWED)
de31d06f 539 redirectStart(http, clientRedirectDoneWrapper, context);
14cc8559 540 else
de31d06f 541 context->clientRedirectDone(NULL);
14cc8559 542}
543
de31d06f 544void
545ClientRequestContext::clientRedirectStart()
14cc8559 546{
bf8fe701 547 debugs(33, 5, "clientRedirectStart: '" << http->uri << "'");
14cc8559 548
14cc8559 549 if (Config.accessList.redirector) {
de31d06f 550 acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
551 acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
14cc8559 552 } else
de31d06f 553 redirectStart(http, clientRedirectDoneWrapper, this);
14cc8559 554}
555
edce4d98 556static int
59a1efb2 557clientHierarchical(ClientHttpRequest * http)
edce4d98 558{
559 const char *url = http->uri;
190154cf 560 HttpRequest *request = http->request;
edce4d98 561 method_t method = request->method;
562 const wordlist *p = NULL;
563
69660be0 564 /*
565 * IMS needs a private key, so we can use the hierarchy for IMS only if our
566 * neighbors support private keys
567 */
62e76326 568
edce4d98 569 if (request->flags.ims && !neighbors_do_private_keys)
62e76326 570 return 0;
571
69660be0 572 /*
573 * This is incorrect: authenticating requests can be sent via a hierarchy
06b97e72 574 * (they can even be cached if the correct headers are set on the reply)
edce4d98 575 */
576 if (request->flags.auth)
62e76326 577 return 0;
578
edce4d98 579 if (method == METHOD_TRACE)
62e76326 580 return 1;
581
edce4d98 582 if (method != METHOD_GET)
62e76326 583 return 0;
584
edce4d98 585 /* scan hierarchy_stoplist */
586 for (p = Config.hierarchy_stoplist; p; p = p->next)
62e76326 587 if (strstr(url, p->key))
588 return 0;
589
edce4d98 590 if (request->flags.loopdetect)
62e76326 591 return 0;
592
edce4d98 593 if (request->protocol == PROTO_HTTP)
62e76326 594 return httpCachable(method);
595
edce4d98 596 if (request->protocol == PROTO_GOPHER)
62e76326 597 return gopherCachable(request);
598
edce4d98 599 if (request->protocol == PROTO_CACHEOBJ)
62e76326 600 return 0;
601
edce4d98 602 return 1;
603}
604
605
606static void
59a1efb2 607clientInterpretRequestHeaders(ClientHttpRequest * http)
edce4d98 608{
190154cf 609 HttpRequest *request = http->request;
0ef77270 610 HttpHeader *req_hdr = &request->header;
edce4d98 611 int no_cache = 0;
a787b56a 612#if !(ESI) || defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG)
62e76326 613
edce4d98 614 const char *str;
615#endif
62e76326 616
edce4d98 617 request->imslen = -1;
a9925b40 618 request->ims = req_hdr->getTime(HDR_IF_MODIFIED_SINCE);
62e76326 619
edce4d98 620 if (request->ims > 0)
62e76326 621 request->flags.ims = 1;
622
edce4d98 623#if ESI
69660be0 624 /*
625 * We ignore Cache-Control as per the Edge Architecture Section 3. See
626 * www.esi.org for more information.
edce4d98 627 */
628#else
62e76326 629
a9925b40 630 if (req_hdr->has(HDR_PRAGMA)) {
86c0aed6 631 string s = req_hdr->getList(HDR_PRAGMA);
62e76326 632
633 if (strListIsMember(&s, "no-cache", ','))
634 no_cache++;
edce4d98 635 }
62e76326 636
edce4d98 637 if (request->cache_control)
62e76326 638 if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
639 no_cache++;
640
69660be0 641 /*
62e76326 642 * Work around for supporting the Reload button in IE browsers when Squid
643 * is used as an accelerator or transparent proxy, by turning accelerated
644 * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
645 * actually only fixed in SP1, but we can't tell whether we are talking to
646 * SP1 or not so all 5.5 versions are treated 'normally').
647 */
edce4d98 648 if (Config.onoff.ie_refresh) {
62e76326 649 if (http->flags.accel && request->flags.ims) {
a9925b40 650 if ((str = req_hdr->getStr(HDR_USER_AGENT))) {
62e76326 651 if (strstr(str, "MSIE 5.01") != NULL)
652 no_cache++;
653 else if (strstr(str, "MSIE 5.0") != NULL)
654 no_cache++;
655 else if (strstr(str, "MSIE 4.") != NULL)
656 no_cache++;
657 else if (strstr(str, "MSIE 3.") != NULL)
658 no_cache++;
659 }
660 }
edce4d98 661 }
62e76326 662
edce4d98 663#endif
664 if (no_cache) {
665#if HTTP_VIOLATIONS
62e76326 666
667 if (Config.onoff.reload_into_ims)
668 request->flags.nocache_hack = 1;
669 else if (refresh_nocache_hack)
670 request->flags.nocache_hack = 1;
671 else
edce4d98 672#endif
62e76326 673
674 request->flags.nocache = 1;
edce4d98 675 }
62e76326 676
0ef77270 677 /* ignore range header in non-GETs or non-HEADs */
678 if (request->method == METHOD_GET || request->method == METHOD_HEAD) {
a9925b40 679 request->range = req_hdr->getRange();
62e76326 680
681 if (request->range) {
682 request->flags.range = 1;
683 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
684 /* XXX: This is suboptimal. We should give the stream the range set,
685 * and thereby let the top of the stream set the offset when the
686 * size becomes known. As it is, we will end up requesting from 0
687 * for evey -X range specification.
688 * RBC - this may be somewhat wrong. We should probably set the range
689 * iter up at this point.
690 */
691 node->readBuffer.offset = request->range->lowestOffset(0);
692 http->range_iter.pos = request->range->begin();
693 http->range_iter.valid = true;
694 }
edce4d98 695 }
62e76326 696
0ef77270 697 /* Only HEAD and GET requests permit a Range or Request-Range header.
698 * If these headers appear on any other type of request, delete them now.
699 */
700 else {
701 req_hdr->delById(HDR_RANGE);
702 req_hdr->delById(HDR_REQUEST_RANGE);
703 request->range = NULL;
704 }
705
a9925b40 706 if (req_hdr->has(HDR_AUTHORIZATION))
62e76326 707 request->flags.auth = 1;
708
edce4d98 709 if (request->login[0] != '\0')
62e76326 710 request->flags.auth = 1;
711
a9925b40 712 if (req_hdr->has(HDR_VIA)) {
86c0aed6 713 string s = req_hdr->getList(HDR_VIA);
62e76326 714 /*
715 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
716 * Note ThisCache2 has a space prepended to the hostname so we don't
717 * accidentally match super-domains.
718 */
719
720 if (strListIsSubstr(&s, ThisCache2, ',')) {
721 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
722 request, (ObjPackMethod) & httpRequestPack);
723 request->flags.loopdetect = 1;
724 }
725
edce4d98 726#if FORW_VIA_DB
86c0aed6 727 fvdbCountVia(s.c_str());
62e76326 728
edce4d98 729#endif
62e76326 730
86c0aed6 731 s.clear();
edce4d98 732 }
62e76326 733
edce4d98 734#if USE_USERAGENT_LOG
a9925b40 735 if ((str = req_hdr->getStr(HDR_USER_AGENT)))
94a396a3 736 logUserAgent(fqdnFromAddr(http->getConn() != NULL ? http->getConn()->log_addr : no_addr), str);
62e76326 737
edce4d98 738#endif
739#if USE_REFERER_LOG
62e76326 740
a9925b40 741 if ((str = req_hdr->getStr(HDR_REFERER)))
94a396a3 742 logReferer(fqdnFromAddr(http->getConn() != NULL ? http->getConn()->log_addr : no_addr), str, http->log_uri);
62e76326 743
edce4d98 744#endif
745#if FORW_VIA_DB
62e76326 746
a9925b40 747 if (req_hdr->has(HDR_X_FORWARDED_FOR)) {
86c0aed6 748 string s = req_hdr->getList(HDR_X_FORWARDED_FOR);
749 fvdbCountForw(s.c_str());
750 s.clear();
edce4d98 751 }
62e76326 752
edce4d98 753#endif
754 if (request->method == METHOD_TRACE) {
a9925b40 755 request->max_forwards = req_hdr->getInt(HDR_MAX_FORWARDS);
edce4d98 756 }
62e76326 757
610ee341 758 request->flags.cachable = http->request->cacheable();
62e76326 759
edce4d98 760 if (clientHierarchical(http))
62e76326 761 request->flags.hierarchical = 1;
762
bf8fe701 763 debugs(85, 5, "clientInterpretRequestHeaders: REQ_NOCACHE = " <<
764 (request->flags.nocache ? "SET" : "NOT SET"));
765 debugs(85, 5, "clientInterpretRequestHeaders: REQ_CACHABLE = " <<
766 (request->flags.cachable ? "SET" : "NOT SET"));
767 debugs(85, 5, "clientInterpretRequestHeaders: REQ_HIERARCHICAL = " <<
768 (request->flags.hierarchical ? "SET" : "NOT SET"));
62e76326 769
edce4d98 770}
771
772void
de31d06f 773clientRedirectDoneWrapper(void *data, char *result)
edce4d98 774{
de31d06f 775 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
db02222f 776
de31d06f 777 if (!calloutContext->httpStateIsValid())
62e76326 778 return;
62e76326 779
de31d06f 780 calloutContext->clientRedirectDone(result);
781}
782
783void
784ClientRequestContext::clientRedirectDone(char *result)
785{
190154cf 786 HttpRequest *new_request = NULL;
787 HttpRequest *old_request = http->request;
bf8fe701 788 debugs(85, 5, "clientRedirectDone: '" << http->uri << "' result=" << (result ? result : "NULL"));
de31d06f 789 assert(redirect_state == REDIRECT_PENDING);
790 redirect_state = REDIRECT_DONE;
62e76326 791
edce4d98 792 if (result) {
62e76326 793 http_status status = (http_status) atoi(result);
794
795 if (status == HTTP_MOVED_PERMANENTLY
796 || status == HTTP_MOVED_TEMPORARILY
797 || status == HTTP_SEE_OTHER
798 || status == HTTP_TEMPORARY_REDIRECT) {
799 char *t = result;
800
801 if ((t = strchr(result, ':')) != NULL) {
802 http->redirect.status = status;
803 http->redirect.location = xstrdup(t + 1);
804 } else {
bf8fe701 805 debugs(85, 1, "clientRedirectDone: bad input: " << result);
62e76326 806 }
2baf58c3 807 } else if (strcmp(result, http->uri))
c21ad0f5 808 new_request = HttpRequest::CreateFromUrlAndMethod(result, old_request->method);
edce4d98 809 }
62e76326 810
edce4d98 811 if (new_request) {
62e76326 812 safe_free(http->uri);
813 http->uri = xstrdup(urlCanonical(new_request));
814 new_request->http_ver = old_request->http_ver;
a9925b40 815 new_request->header.append(&old_request->header);
62e76326 816 new_request->client_addr = old_request->client_addr;
47b0c1fa 817 new_request->client_port = old_request->client_port;
62e76326 818 new_request->my_addr = old_request->my_addr;
819 new_request->my_port = old_request->my_port;
820 new_request->flags = old_request->flags;
3c1f01bc 821 new_request->flags.redirected = 1;
62e76326 822
823 if (old_request->auth_user_request) {
824 new_request->auth_user_request = old_request->auth_user_request;
4f0ef8e8 825 AUTHUSERREQUESTLOCK(new_request->auth_user_request, "new request");
62e76326 826 }
827
5f8252d2 828 if (old_request->body_pipe != NULL) {
829 new_request->body_pipe = old_request->body_pipe;
830 old_request->body_pipe = NULL;
831 debugs(0,0,HERE << "redirecting body_pipe " << new_request->body_pipe << " from request " << old_request << " to " << new_request);
62e76326 832 }
833
834 new_request->content_length = old_request->content_length;
abb929f0 835 new_request->extacl_user = old_request->extacl_user;
836 new_request->extacl_passwd = old_request->extacl_passwd;
62e76326 837 new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
6dd9f4bd 838 HTTPMSGUNLOCK(old_request);
839 http->request = HTTPMSGLOCK(new_request);
edce4d98 840 }
62e76326 841
edce4d98 842 /* FIXME PIPELINE: This is innacurate during pipelining */
62e76326 843
5f8252d2 844 if (http->getConn() != NULL)
98242069 845 fd_note(http->getConn()->fd, http->uri);
62e76326 846
c8be6d7b 847 assert(http->uri);
62e76326 848
de31d06f 849 http->doCallouts();
edce4d98 850}
851
852void
8e2745f4 853ClientRequestContext::checkNoCache()
edce4d98 854{
de31d06f 855 acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
57abaac0 856 acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, this);
edce4d98 857}
858
de31d06f 859static void
860checkNoCacheDoneWrapper(int answer, void *data)
edce4d98 861{
de31d06f 862 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
e4a67a80 863
de31d06f 864 if (!calloutContext->httpStateIsValid())
865 return;
866
867 calloutContext->checkNoCacheDone(answer);
8e2745f4 868}
4fb35c3c 869
8e2745f4 870void
871ClientRequestContext::checkNoCacheDone(int answer)
62e76326 872{
8e2745f4 873 acl_checklist = NULL;
de31d06f 874 http->request->flags.cachable = answer;
875 http->doCallouts();
edce4d98 876}
877
69660be0 878/*
879 * Identify requests that do not go through the store and client side stream
880 * and forward them to the appropriate location. All other requests, request
881 * them.
edce4d98 882 */
883void
8e2745f4 884ClientHttpRequest::processRequest()
edce4d98 885{
bf8fe701 886 debugs(85, 4, "clientProcessRequest: " << RequestMethodStr[request->method] << " '" << uri << "'");
62e76326 887
2baf58c3 888 if (request->method == METHOD_CONNECT && !redirect.status) {
62e76326 889 logType = LOG_TCP_MISS;
890 sslStart(this, &out.size, &al.http.code);
891 return;
edce4d98 892 }
62e76326 893
8e2745f4 894 httpStart();
895}
896
897void
898ClientHttpRequest::httpStart()
899{
559da936 900 PROF_start(httpStart);
8e2745f4 901 logType = LOG_TAG_NONE;
bf8fe701 902 debugs(85, 4, "ClientHttpRequest::httpStart: " << log_tags[logType] << " for '" << uri << "'");
903
edce4d98 904 /* no one should have touched this */
8e2745f4 905 assert(out.offset == 0);
edce4d98 906 /* Use the Stream Luke */
8e2745f4 907 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
908 clientStreamRead(node, this, node->readBuffer);
559da936 909 PROF_stop(httpStart);
edce4d98 910}
0655fa4d 911
912bool
913ClientHttpRequest::gotEnough() const
914{
86a2f789 915 /** TODO: should be querying the stream. */
0655fa4d 916 int contentLength =
06a5ae20 917 memObject()->getReply()->bodySize(request->method);
0655fa4d 918 assert(contentLength >= 0);
919
920 if (out.offset < contentLength)
921 return false;
922
923 return true;
924}
925
b51aec66 926void
927ClientHttpRequest::maxReplyBodySize(ssize_t clen)
928{
929 maxReplyBodySize_ = clen;
930}
931
932ssize_t
933ClientHttpRequest::maxReplyBodySize() const
934{
935 return maxReplyBodySize_;
936}
937
938bool
939ClientHttpRequest::isReplyBodyTooLarge(ssize_t clen) const
940{
941 if (0 == maxReplyBodySize())
942 return 0; /* disabled */
943
944 if (clen < 0)
945 return 0; /* unknown */
946
947 return clen > maxReplyBodySize();
948}
86a2f789 949
950void
951ClientHttpRequest::storeEntry(StoreEntry *newEntry)
952{
953 entry_ = newEntry;
954}
955
0976f8db 956void
957ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
958{
959 if (loggingEntry_)
97b5e68f 960 loggingEntry_->unlock();
0976f8db 961
962 loggingEntry_ = newEntry;
963
964 if (loggingEntry_)
34266cde 965 loggingEntry_->lock()
966
967 ;
0976f8db 968}
86a2f789 969
de31d06f 970/*
971 * doCallouts() - This function controls the order of "callout"
972 * executions, including non-blocking access control checks, the
973 * redirector, and ICAP. Previously, these callouts were chained
974 * together such that "clientAccessCheckDone()" would call
975 * "clientRedirectStart()" and so on.
976 *
977 * The ClientRequestContext (aka calloutContext) class holds certain
978 * state data for the callout/callback operations. Previously
979 * ClientHttpRequest would sort of hand off control to ClientRequestContext
980 * for a short time. ClientRequestContext would then delete itself
981 * and pass control back to ClientHttpRequest when all callouts
982 * were finished.
983 *
984 * This caused some problems for ICAP because we want to make the
985 * ICAP callout after checking ACLs, but before checking the no_cache
986 * list. We can't stuff the ICAP state into the ClientRequestContext
987 * class because we still need the ICAP state after ClientRequestContext
988 * goes away.
989 *
990 * Note that ClientRequestContext is created before the first call
991 * to doCallouts().
992 *
993 * If one of the callouts notices that ClientHttpRequest is no
994 * longer valid, it should call cbdataReferenceDone() so that
995 * ClientHttpRequest's reference count goes to zero and it will get
996 * deleted. ClientHttpRequest will then delete ClientRequestContext.
997 *
998 * Note that we set the _done flags here before actually starting
999 * the callout. This is strictly for convenience.
1000 */
1001
1002void
1003ClientHttpRequest::doCallouts()
1004{
1005 assert(calloutContext);
1006
1007 if (!calloutContext->http_access_done) {
58d7150d 1008 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
57abaac0 1009 calloutContext->http_access_done = true;
de31d06f 1010 calloutContext->clientAccessCheck();
1011 return;
1012 }
1013
1014#if ICAP_CLIENT
ab7ac359 1015 if (TheICAPConfig.onoff && !calloutContext->icap_acl_check_done) {
58d7150d 1016 debugs(83, 3, HERE << "Doing calloutContext->icapAccessCheck()");
57abaac0 1017 calloutContext->icap_acl_check_done = true;
de31d06f 1018 calloutContext->icapAccessCheck();
1019 return;
1020 }
1021
1022#endif
1023
1024 if (!calloutContext->redirect_done) {
57abaac0 1025 calloutContext->redirect_done = true;
de31d06f 1026 assert(calloutContext->redirect_state == REDIRECT_NONE);
1027
1028 if (Config.Program.redirect) {
58d7150d 1029 debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
de31d06f 1030 calloutContext->redirect_state = REDIRECT_PENDING;
1031 calloutContext->clientRedirectStart();
1032 return;
1033 }
1034 }
1035
57abaac0 1036 if (!calloutContext->interpreted_req_hdrs) {
58d7150d 1037 debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
57abaac0 1038 calloutContext->interpreted_req_hdrs = 1;
1039 clientInterpretRequestHeaders(this);
1040 }
1041
de31d06f 1042 if (!calloutContext->no_cache_done) {
57abaac0 1043 calloutContext->no_cache_done = true;
de31d06f 1044
1045 if (Config.accessList.noCache && request->flags.cachable) {
58d7150d 1046 debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
de31d06f 1047 calloutContext->checkNoCache();
1048 return;
1049 }
1050 }
1051
1052 cbdataReferenceDone(calloutContext->http);
1053 delete calloutContext;
1054 calloutContext = NULL;
de31d06f 1055#if HEADERS_LOG
1056
1057 headersLog(0, 1, request->method, request);
1058#endif
1059
58d7150d 1060 debugs(83, 3, HERE << "calling processRequest()");
de31d06f 1061 processRequest();
1062}
1063
86a2f789 1064#ifndef _USE_INLINE_
1065#include "client_side_request.cci"
1066#endif
de31d06f 1067
1068#if ICAP_CLIENT
1069/*
5f8252d2 1070 * Initiate an ICAP transaction. Return false on errors.
1071 * The caller must handle errors.
de31d06f 1072 */
5f8252d2 1073bool
1074ClientHttpRequest::startIcap(ICAPServiceRep::Pointer service)
3b299123 1075{
5f8252d2 1076 debugs(85, 3, HERE << this << " ClientHttpRequest::startIcap() called");
1077 if (!service) {
bf8fe701 1078 debugs(85, 3, "ClientHttpRequest::startIcap fails: lack of service");
5f8252d2 1079 return false;
3b299123 1080 }
5f8252d2 1081 if (service->broken()) {
bf8fe701 1082 debugs(85, 3, "ClientHttpRequest::startIcap fails: broken service");
5f8252d2 1083 return false;
3b299123 1084 }
1085
5f8252d2 1086 assert(!icapHeadSource);
1087 assert(!icapBodySource);
9d4d7c5e 1088 icapHeadSource = initiateIcap(
1089 new ICAPModXactLauncher(this, request, NULL, service));
5f8252d2 1090 return true;
de31d06f 1091}
1092
1093void
9d4d7c5e 1094ClientHttpRequest::noteIcapAnswer(HttpMsg *msg)
de31d06f 1095{
de31d06f 1096 assert(cbdataReferenceValid(this)); // indicates bug
5f8252d2 1097 assert(msg);
1098
b044675d 1099 if (HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg)) {
200ac359 1100 /*
5f8252d2 1101 * Replace the old request with the new request.
200ac359 1102 */
6dd9f4bd 1103 HTTPMSGUNLOCK(request);
1104 request = HTTPMSGLOCK(new_req);
200ac359 1105 /*
1106 * Store the new URI for logging
1107 */
1108 xfree(uri);
1109 uri = xstrdup(urlCanonical(request));
1110 setLogUri(this, urlCanonicalClean(request));
1111 assert(request->method);
b044675d 1112 } else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
1113 debugs(85,3,HERE << "REQMOD reply is HTTP reply");
1114
5f8252d2 1115 // subscribe to receive reply body
1116 if (new_rep->body_pipe != NULL) {
1117 icapBodySource = new_rep->body_pipe;
1118 assert(icapBodySource->setConsumerIfNotLate(this));
1119 }
1120
b044675d 1121 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1122 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1123 repContext->createStoreEntry(request->method, request->flags);
1124
1125 EBIT_CLR(storeEntry()->flags, ENTRY_FWD_HDR_WAIT);
1126 request_satisfaction_mode = true;
1127 request_satisfaction_offset = 0;
1128 storeEntry()->replaceHttpReply(new_rep);
1129 clientGetMoreData(node, this);
200ac359 1130 }
de31d06f 1131
5f8252d2 1132 // we are done with getting headers (but may be receiving body)
9d4d7c5e 1133 clearIcap(icapHeadSource);
5f8252d2 1134
b044675d 1135 if (!request_satisfaction_mode)
1136 doCallouts();
de31d06f 1137}
1138
1139void
9d4d7c5e 1140ClientHttpRequest::noteIcapQueryAbort(bool final)
de31d06f 1141{
9d4d7c5e 1142 clearIcap(icapHeadSource);
5f8252d2 1143 assert(!icapBodySource);
9d4d7c5e 1144 handleIcapFailure(!final);
de31d06f 1145}
1146
1147void
5f8252d2 1148ClientHttpRequest::noteMoreBodyDataAvailable(BodyPipe &)
de31d06f 1149{
5f8252d2 1150 assert(request_satisfaction_mode);
1151 assert(icapBodySource != NULL);
1152
1153 if (const size_t contentSize = icapBodySource->buf().contentSize()) {
1154 BodyPipeCheckout bpc(*icapBodySource);
1155 const StoreIOBuffer ioBuf(&bpc.buf, request_satisfaction_offset);
1156 storeEntry()->write(ioBuf);
1157 // assume can write everything
1158 request_satisfaction_offset += contentSize;
1159 bpc.buf.consume(contentSize);
1160 bpc.checkIn();
1161 }
1162
1163 if (icapBodySource->exhausted())
1164 endRequestSatisfaction();
1165 // else wait for more body data
de31d06f 1166}
1167
1168void
5f8252d2 1169ClientHttpRequest::noteBodyProductionEnded(BodyPipe &)
de31d06f 1170{
5f8252d2 1171 assert(!icapHeadSource);
1172 if (icapBodySource != NULL) { // did not end request satisfaction yet
1173 // We do not expect more because noteMoreBodyDataAvailable always
1174 // consumes everything. We do not even have a mechanism to consume
1175 // leftovers after noteMoreBodyDataAvailable notifications seize.
1176 assert(icapBodySource->exhausted());
1177 endRequestSatisfaction();
1178 }
1179}
3b299123 1180
5f8252d2 1181void
1182ClientHttpRequest::endRequestSatisfaction() {
1183 debugs(85,4, HERE << this << " ends request satisfaction");
1184 assert(request_satisfaction_mode);
1185 stopConsumingFrom(icapBodySource);
3b299123 1186
5f8252d2 1187 // TODO: anything else needed to end store entry formation correctly?
1188 storeEntry()->complete();
1189}
de31d06f 1190
5f8252d2 1191void
1192ClientHttpRequest::noteBodyProducerAborted(BodyPipe &)
1193{
1194 assert(!icapHeadSource);
1195 stopConsumingFrom(icapBodySource);
1196 handleIcapFailure();
1197}
3b299123 1198
5f8252d2 1199void
9d4d7c5e 1200ClientHttpRequest::handleIcapFailure(bool bypassable)
5f8252d2 1201{
9d4d7c5e 1202 debugs(85,3, HERE << "handleIcapFailure(" << bypassable << ")");
3b299123 1203
5f8252d2 1204 const bool usedStore = storeEntry() && !storeEntry()->isEmpty();
1205 const bool usedPipe = request->body_pipe != NULL &&
1206 request->body_pipe->consumedSize() > 0;
3b299123 1207
9d4d7c5e 1208 if (bypassable && !usedStore && !usedPipe) {
1209 debugs(85,3, HERE << "ICAP REQMOD callout failed, bypassing: " << calloutContext);
5f8252d2 1210 if (calloutContext)
1211 doCallouts();
1212 return;
1213 }
3b299123 1214
5f8252d2 1215 debugs(85,3, HERE << "ICAP REQMOD callout failed, responding with error");
3b299123 1216
5f8252d2 1217 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1218 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1219 assert(repContext);
de31d06f 1220
5f8252d2 1221 // The original author of the code also wanted to pass an errno to
1222 // setReplyToError, but it seems unlikely that the errno reflects the
1223 // true cause of the error at this point, so I did not pass it.
1224 ConnStateData::Pointer c = getConn();
1225 repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
1226 request->method, NULL,
1227 (c != NULL ? &c->peer.sin_addr : &no_addr), request, NULL,
1228 (c != NULL && c->auth_user_request ?
1229 c->auth_user_request : request->auth_user_request));
de31d06f 1230
5f8252d2 1231 node = (clientStreamNode *)client_stream.tail->data;
1232 clientStreamRead(node, this, node->readBuffer);
de31d06f 1233}
1234
1235#endif