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