]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
Merged from trunk
[thirdparty/squid.git] / src / client_side_request.cc
CommitLineData
edce4d98 1
2/*
262a0e14 3 * $Id$
26ac0430 4 *
ae45c4de 5 * DEBUG: section 85 Client-side Request Routines
6 * AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c)
26ac0430 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:
26ac0430 39 *
69660be0 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"
3712be3f 50#include "ProtoPort.h"
8000a965 51#include "ACLChecklist.h"
52#include "ACL.h"
a46d2c0e 53#include "client_side.h"
0655fa4d 54#include "client_side_reply.h"
55#include "Store.h"
56#include "HttpReply.h"
86a2f789 57#include "MemObject.h"
de31d06f 58#include "ClientRequestContext.h"
985c86bc 59#include "SquidTime.h"
d295d770 60#include "wordlist.h"
5fefeec1 61#include "inet_pton.h"
de31d06f 62
a83c6ed6 63#if USE_ADAPTATION
62c7f90e 64#include "adaptation/AccessCheck.h"
abd4b611 65#include "adaptation/Service.h"
a83c6ed6 66static void adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data);
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
609c620a 75#if FOLLOW_X_FORWARDED_FOR
3d674977
AJ
76static void
77clientFollowXForwardedForCheck(int answer, void *data);
609c620a 78#endif /* FOLLOW_X_FORWARDED_FOR */
3d674977 79
8e2745f4 80CBDATA_CLASS_INIT(ClientRequestContext);
81
82void *
83ClientRequestContext::operator new (size_t size)
84{
85 assert (size == sizeof(ClientRequestContext));
86 CBDATA_INIT_TYPE(ClientRequestContext);
87 ClientRequestContext *result = cbdataAlloc(ClientRequestContext);
aa625860 88 return result;
8e2745f4 89}
62e76326 90
8e2745f4 91void
92ClientRequestContext::operator delete (void *address)
93{
94 ClientRequestContext *t = static_cast<ClientRequestContext *>(address);
aa625860 95 cbdataFree(t);
8e2745f4 96}
97
edce4d98 98/* Local functions */
edce4d98 99/* other */
de31d06f 100static void clientAccessCheckDoneWrapper(int, void *);
59a1efb2 101static int clientHierarchical(ClientHttpRequest * http);
102static void clientInterpretRequestHeaders(ClientHttpRequest * http);
de31d06f 103static RH clientRedirectDoneWrapper;
104static PF checkNoCacheDoneWrapper;
e6ccf245 105extern "C" CSR clientGetMoreData;
106extern "C" CSS clientReplyStatus;
107extern "C" CSD clientReplyDetach;
528b2c61 108static void checkFailureRatio(err_type, hier_code);
edce4d98 109
8e2745f4 110ClientRequestContext::~ClientRequestContext()
111{
de31d06f 112 /*
a546b04b 113 * Release our "lock" on our parent, ClientHttpRequest, if we
114 * still have one
de31d06f 115 */
a546b04b 116
117 if (http)
118 cbdataReferenceDone(http);
62e76326 119
5f8252d2 120 debugs(85,3, HERE << this << " ClientRequestContext destructed");
8e2745f4 121}
122
4e2eb5c3 123ClientRequestContext::ClientRequestContext(ClientHttpRequest *anHttp) : http(cbdataReference(anHttp)), acl_checklist (NULL), redirect_state (REDIRECT_NONE)
edce4d98 124{
57abaac0 125 http_access_done = false;
126 redirect_done = false;
127 no_cache_done = false;
128 interpreted_req_hdrs = false;
0b86805b 129 debugs(85,3, HERE << this << " ClientRequestContext constructed");
edce4d98 130}
131
528b2c61 132CBDATA_CLASS_INIT(ClientHttpRequest);
8e2745f4 133
528b2c61 134void *
135ClientHttpRequest::operator new (size_t size)
136{
137 assert (size == sizeof (ClientHttpRequest));
138 CBDATA_INIT_TYPE(ClientHttpRequest);
139 ClientHttpRequest *result = cbdataAlloc(ClientHttpRequest);
aa625860 140 return result;
528b2c61 141}
142
62e76326 143void
528b2c61 144ClientHttpRequest::operator delete (void *address)
145{
aa625860 146 ClientHttpRequest *t = static_cast<ClientHttpRequest *>(address);
147 cbdataFree(t);
528b2c61 148}
149
26ac0430 150ClientHttpRequest::ClientHttpRequest(ConnStateData * aConn) :
a83c6ed6 151#if USE_ADAPTATION
26ac0430 152 AsyncJob("ClientHttpRequest"),
1cf238db 153#endif
26ac0430 154 loggingEntry_(NULL)
528b2c61 155{
1cf238db 156 start_time = current_time;
a0355e95 157 setConn(aConn);
158 dlinkAdd(this, &active, &ClientActiveRequests);
a83c6ed6 159#if USE_ADAPTATION
b044675d 160 request_satisfaction_mode = false;
161#endif
528b2c61 162}
163
0655fa4d 164/*
165 * returns true if client specified that the object must come from the cache
166 * without contacting origin server
167 */
168bool
169ClientHttpRequest::onlyIfCached()const
170{
171 assert(request);
172 return request->cache_control &&
173 EBIT_TEST(request->cache_control->mask, CC_ONLY_IF_CACHED);
174}
175
528b2c61 176/*
177 * This function is designed to serve a fairly specific purpose.
178 * Occasionally our vBNS-connected caches can talk to each other, but not
179 * the rest of the world. Here we try to detect frequent failures which
180 * make the cache unusable (e.g. DNS lookup and connect() failures). If
181 * the failure:success ratio goes above 1.0 then we go into "hit only"
182 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
183 * will only fetch HITs from us if they are using the ICP protocol. We
184 * stay in this mode for 5 minutes.
26ac0430 185 *
528b2c61 186 * Duane W., Sept 16, 1996
187 */
188
189#define FAILURE_MODE_TIME 300
190
191static void
192checkFailureRatio(err_type etype, hier_code hcode)
193{
194 static double magic_factor = 100.0;
195 double n_good;
196 double n_bad;
62e76326 197
528b2c61 198 if (hcode == HIER_NONE)
62e76326 199 return;
200
528b2c61 201 n_good = magic_factor / (1.0 + request_failure_ratio);
62e76326 202
528b2c61 203 n_bad = magic_factor - n_good;
62e76326 204
528b2c61 205 switch (etype) {
62e76326 206
528b2c61 207 case ERR_DNS_FAIL:
62e76326 208
528b2c61 209 case ERR_CONNECT_FAIL:
3712be3f 210 case ERR_SECURE_CONNECT_FAIL:
62e76326 211
528b2c61 212 case ERR_READ_ERROR:
62e76326 213 n_bad++;
214 break;
215
528b2c61 216 default:
62e76326 217 n_good++;
528b2c61 218 }
62e76326 219
528b2c61 220 request_failure_ratio = n_bad / n_good;
62e76326 221
528b2c61 222 if (hit_only_mode_until > squid_curtime)
62e76326 223 return;
224
528b2c61 225 if (request_failure_ratio < 1.0)
62e76326 226 return;
227
bf8fe701 228 debugs(33, 0, "Failure Ratio at "<< std::setw(4)<<
229 std::setprecision(3) << request_failure_ratio);
62e76326 230
bf8fe701 231 debugs(33, 0, "Going into hit-only-mode for " <<
232 FAILURE_MODE_TIME / 60 << " minutes...");
62e76326 233
528b2c61 234 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
62e76326 235
528b2c61 236 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
237}
238
239ClientHttpRequest::~ClientHttpRequest()
240{
bf8fe701 241 debugs(33, 3, "httpRequestFree: " << uri);
72bdee4c 242 PROF_start(httpRequestFree);
62e76326 243
5f8252d2 244 // Even though freeResources() below may destroy the request,
245 // we no longer set request->body_pipe to NULL here
246 // because we did not initiate that pipe (ConnStateData did)
62e76326 247
528b2c61 248 /* the ICP check here was erroneous
26ac0430 249 * - StoreEntry::releaseRequest was always called if entry was valid
528b2c61 250 */
251 assert(logType < LOG_TYPE_MAX);
9ce7856a 252
528b2c61 253 logRequest();
9ce7856a 254
0976f8db 255 loggingEntry(NULL);
256
528b2c61 257 if (request)
62e76326 258 checkFailureRatio(request->errType, al.hier.code);
259
528b2c61 260 freeResources();
62e76326 261
a83c6ed6
AR
262#if USE_ADAPTATION
263 announceInitiatorAbort(virginHeadSource);
9d4d7c5e 264
a83c6ed6
AR
265 if (adaptedBodySource != NULL)
266 stopConsumingFrom(adaptedBodySource);
de31d06f 267#endif
9ce7856a 268
de31d06f 269 if (calloutContext)
270 delete calloutContext;
271
26ac0430
AJ
272 if (conn_)
273 cbdataReferenceDone(conn_);
1cf238db 274
528b2c61 275 /* moving to the next connection is handled by the context free */
276 dlinkDelete(&active, &ClientActiveRequests);
9ce7856a 277
72bdee4c 278 PROF_stop(httpRequestFree);
528b2c61 279}
62e76326 280
edce4d98 281/* Create a request and kick it off */
69660be0 282/*
283 * TODO: Pass in the buffers to be used in the inital Read request, as they are
284 * determined by the user
edce4d98 285 */
286int /* returns nonzero on failure */
60745f24 287clientBeginRequest(const HttpRequestMethod& method, char const *url, CSCB * streamcallback,
0655fa4d 288 CSD * streamdetach, ClientStreamData streamdata, HttpHeader const *header,
62e76326 289 char *tailbuf, size_t taillen)
edce4d98 290{
291 size_t url_sz;
450e0c10 292 HttpVersion http_ver (1, 0);
a0355e95 293 ClientHttpRequest *http = new ClientHttpRequest(NULL);
190154cf 294 HttpRequest *request;
528b2c61 295 StoreIOBuffer tempBuffer;
1cf238db 296 http->start_time = current_time;
edce4d98 297 /* this is only used to adjust the connection offset in client_side.c */
298 http->req_sz = 0;
c8be6d7b 299 tempBuffer.length = taillen;
300 tempBuffer.data = tailbuf;
edce4d98 301 /* client stream setup */
302 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 303 clientReplyStatus, new clientReplyContext(http), streamcallback,
62e76326 304 streamdetach, streamdata, tempBuffer);
edce4d98 305 /* make it visible in the 'current acctive requests list' */
edce4d98 306 /* Set flags */
a46d2c0e 307 /* internal requests only makes sense in an
308 * accelerator today. TODO: accept flags ? */
309 http->flags.accel = 1;
edce4d98 310 /* allow size for url rewriting */
311 url_sz = strlen(url) + Config.appendDomainLen + 5;
e6ccf245 312 http->uri = (char *)xcalloc(url_sz, 1);
edce4d98 313 strcpy(http->uri, url);
314
c21ad0f5 315 if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
bf8fe701 316 debugs(85, 5, "Invalid URL: " << http->uri);
62e76326 317 return -1;
edce4d98 318 }
62e76326 319
69660be0 320 /*
321 * now update the headers in request with our supplied headers. urLParse
322 * should return a blank header set, but we use Update to be sure of
323 * correctness.
edce4d98 324 */
325 if (header)
a9925b40 326 request->header.update(header, NULL);
62e76326 327
edce4d98 328 http->log_uri = xstrdup(urlCanonicalClean(request));
62e76326 329
edce4d98 330 /* http struct now ready */
331
69660be0 332 /*
333 * build new header list *? TODO
edce4d98 334 */
335 request->flags.accelerated = http->flags.accel;
62e76326 336
a46d2c0e 337 request->flags.internalclient = 1;
338
339 /* this is an internally created
340 * request, not subject to acceleration
341 * target overrides */
69660be0 342 /*
343 * FIXME? Do we want to detect and handle internal requests of internal
344 * objects ?
345 */
edce4d98 346
347 /* Internally created requests cannot have bodies today */
348 request->content_length = 0;
62e76326 349
cc192b50 350 request->client_addr.SetNoAddr();
62e76326 351
3d674977
AJ
352#if FOLLOW_X_FORWARDED_FOR
353 request->indirect_client_addr.SetNoAddr();
354#endif /* FOLLOW_X_FORWARDED_FOR */
26ac0430 355
cc192b50 356 request->my_addr.SetNoAddr(); /* undefined for internal requests */
62e76326 357
cc192b50 358 request->my_addr.SetPort(0);
62e76326 359
edce4d98 360 request->http_ver = http_ver;
62e76326 361
6dd9f4bd 362 http->request = HTTPMSGLOCK(request);
edce4d98 363
364 /* optional - skip the access check ? */
de31d06f 365 http->calloutContext = new ClientRequestContext(http);
366
57abaac0 367 http->calloutContext->http_access_done = false;
de31d06f 368
57abaac0 369 http->calloutContext->redirect_done = true;
de31d06f 370
57abaac0 371 http->calloutContext->no_cache_done = true;
de31d06f 372
373 http->doCallouts();
62e76326 374
edce4d98 375 return 0;
376}
377
de31d06f 378bool
379ClientRequestContext::httpStateIsValid()
380{
381 ClientHttpRequest *http_ = http;
382
383 if (cbdataReferenceValid(http_))
384 return true;
385
386 http = NULL;
387
388 cbdataReferenceDone(http_);
389
390 return false;
391}
392
3d674977
AJ
393#if FOLLOW_X_FORWARDED_FOR
394/**
395 * clientFollowXForwardedForCheck() checks the indirect_client_addr
396 * against the followXFF ACL, or cleans up and passes control to
397 * clientAccessCheck().
398 */
399
400static void
401clientFollowXForwardedForCheck(int answer, void *data)
402{
403 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
404 ClientHttpRequest *http = NULL;
405 HttpRequest *request = NULL;
406
407 if (!calloutContext->httpStateIsValid())
408 return;
409
26ac0430 410 http = calloutContext->http;
3d674977
AJ
411 request = http->request;
412 /*
413 * answer should be be ACCESS_ALLOWED or ACCESS_DENIED if we are
414 * called as a result of ACL checks, or -1 if we are called when
415 * there's nothing left to do.
416 */
417 if (answer == ACCESS_ALLOWED &&
26ac0430 418 request->x_forwarded_for_iterator.size () != 0) {
3d674977
AJ
419 /*
420 * The IP address currently in request->indirect_client_addr
421 * is trusted to use X-Forwarded-For. Remove the last
422 * comma-delimited element from x_forwarded_for_iterator and use
423 * it to to replace indirect_client_addr, then repeat the cycle.
424 */
425 const char *p;
426 const char *asciiaddr;
427 int l;
428 struct in_addr addr;
99524de7 429 p = request->x_forwarded_for_iterator.unsafeBuf();
3d674977
AJ
430 l = request->x_forwarded_for_iterator.size();
431
432 /*
433 * XXX x_forwarded_for_iterator should really be a list of
434 * IP addresses, but it's a String instead. We have to
435 * walk backwards through the String, biting off the last
436 * comma-delimited part each time. As long as the data is in
437 * a String, we should probably implement and use a variant of
438 * strListGetItem() that walks backwards instead of forwards
439 * through a comma-separated list. But we don't even do that;
440 * we just do the work in-line here.
441 */
442 /* skip trailing space and commas */
443 while (l > 0 && (p[l-1] == ',' || xisspace(p[l-1])))
444 l--;
445 request->x_forwarded_for_iterator.cut(l);
446 /* look for start of last item in list */
447 while (l > 0 && ! (p[l-1] == ',' || xisspace(p[l-1])))
448 l--;
449 asciiaddr = p+l;
26ac0430 450 if (xinet_pton(AF_INET, asciiaddr, &addr) != 0) {
3d674977
AJ
451 request->indirect_client_addr = addr;
452 request->x_forwarded_for_iterator.cut(l);
26ac0430 453 if (! Config.onoff.acl_uses_indirect_client) {
3d674977
AJ
454 /*
455 * If acl_uses_indirect_client is off, then it's impossible
456 * to follow more than one level of X-Forwarded-For.
457 */
458 request->x_forwarded_for_iterator.clean();
459 }
460 calloutContext->acl_checklist =
26ac0430 461 clientAclChecklistCreate(Config.accessList.followXFF, http);
3d674977
AJ
462 calloutContext->acl_checklist->
463 nonBlockingCheck(clientFollowXForwardedForCheck, data);
464 return;
465 }
466 } /*if (answer == ACCESS_ALLOWED &&
467 request->x_forwarded_for_iterator.size () != 0)*/
468
469 /* clean up, and pass control to clientAccessCheck */
26ac0430 470 if (Config.onoff.log_uses_indirect_client) {
3d674977
AJ
471 /*
472 * Ensure that the access log shows the indirect client
473 * instead of the direct client.
474 */
475 ConnStateData *conn = http->getConn();
476 conn->log_addr = request->indirect_client_addr;
477 }
478 request->x_forwarded_for_iterator.clean();
479 request->flags.done_follow_x_forwarded_for = 1;
480
493d3865
AJ
481 /* If follow XFF is denied, we reset the indirect_client_addr
482 to the direct client. Thats the one we are configured to check for */
483 if (answer == ACCESS_DENIED) {
484 request->indirect_client_addr = request->client_addr;
485 }
486 /* on a failure, leave it as undefined state ?? */
487 else if (answer != ACCESS_ALLOWED) {
488 debugs(28, DBG_CRITICAL, "Follow X-Forwarded-For encountered an error. Ignoring address: " << request->indirect_client_addr );
489 request->indirect_client_addr = request->client_addr;
490 }
491
492 /* process actual access ACL as normal. */
493 calloutContext->clientAccessCheck();
3d674977
AJ
494}
495#endif /* FOLLOW_X_FORWARDED_FOR */
496
edce4d98 497/* This is the entry point for external users of the client_side routines */
498void
de31d06f 499ClientRequestContext::clientAccessCheck()
edce4d98 500{
3d674977
AJ
501#if FOLLOW_X_FORWARDED_FOR
502 if (!http->request->flags.done_follow_x_forwarded_for &&
26ac0430
AJ
503 Config.accessList.followXFF &&
504 http->request->header.has(HDR_X_FORWARDED_FOR)) {
505 http->request->x_forwarded_for_iterator =
3d674977
AJ
506 http->request->header.getList(HDR_X_FORWARDED_FOR);
507 clientFollowXForwardedForCheck(ACCESS_ALLOWED, this);
508 return;
509 }
510#endif /* FOLLOW_X_FORWARDED_FOR */
493d3865 511
b50e327b
AJ
512 if (Config.accessList.http) {
513 acl_checklist = clientAclChecklistCreate(Config.accessList.http, http);
514 acl_checklist->nonBlockingCheck(clientAccessCheckDoneWrapper, this);
515 } else {
516 debugs(0, DBG_CRITICAL, "No http_access configuration found. This will block ALL traffic");
517 clientAccessCheckDone(ACCESS_DENIED);
518 }
edce4d98 519}
520
521void
de31d06f 522clientAccessCheckDoneWrapper(int answer, void *data)
edce4d98 523{
de31d06f 524 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
fbade053 525
de31d06f 526 if (!calloutContext->httpStateIsValid())
62e76326 527 return;
62e76326 528
de31d06f 529 calloutContext->clientAccessCheckDone(answer);
530}
531
532void
533ClientRequestContext::clientAccessCheckDone(int answer)
534{
535 acl_checklist = NULL;
edce4d98 536 err_type page_id;
537 http_status status;
26ac0430
AJ
538 debugs(85, 2, "The request " <<
539 RequestMethodStr(http->request->method) << " " <<
540 http->uri << " is " <<
541 (answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED") <<
542 ", because it matched '" <<
543 (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" );
f5691f9c 544 char const *proxy_auth_msg = "<null>";
545
94a396a3 546 if (http->getConn() != NULL && http->getConn()->auth_user_request != NULL)
f5691f9c 547 proxy_auth_msg = http->getConn()->auth_user_request->denyMessage("<null>");
548 else if (http->request->auth_user_request != NULL)
549 proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
62e76326 550
de31d06f 551 if (answer != ACCESS_ALLOWED) {
62e76326 552 /* Send an error */
9ce7856a 553 int require_auth = (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName));
bf8fe701 554 debugs(85, 5, "Access Denied: " << http->uri);
555 debugs(85, 5, "AclMatchedName = " << (AclMatchedName ? AclMatchedName : "<null>"));
9ce7856a 556
557 if (require_auth)
bf8fe701 558 debugs(33, 5, "Proxy Auth Message = " << (proxy_auth_msg ? proxy_auth_msg : "<null>"));
9ce7856a 559
62e76326 560 /*
561 * NOTE: get page_id here, based on AclMatchedName because if
562 * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
563 * the clientCreateStoreEntry() call just below. Pedro Ribeiro
564 * <pribeiro@isel.pt>
565 */
9ce7856a 566 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, answer != ACCESS_REQ_PROXY_AUTH);
567
62e76326 568 http->logType = LOG_TCP_DENIED;
569
9ce7856a 570 if (require_auth) {
62e76326 571 if (!http->flags.accel) {
572 /* Proxy authorisation needed */
573 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
574 } else {
575 /* WWW authorisation needed */
576 status = HTTP_UNAUTHORIZED;
577 }
578
579 if (page_id == ERR_NONE)
580 page_id = ERR_CACHE_ACCESS_DENIED;
581 } else {
582 status = HTTP_FORBIDDEN;
583
584 if (page_id == ERR_NONE)
585 page_id = ERR_ACCESS_DENIED;
586 }
587
de31d06f 588 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
0655fa4d 589 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
590 assert (repContext);
ad61a2b4 591 IpAddress tmpnoaddr;
26ac0430 592 tmpnoaddr.SetNoAddr();
0655fa4d 593 repContext->setReplyToError(page_id, status,
594 http->request->method, NULL,
cc192b50 595 http->getConn() != NULL ? http->getConn()->peer : tmpnoaddr,
596 http->request,
597 NULL,
598 http->getConn() != NULL && http->getConn()->auth_user_request ?
599 http->getConn()->auth_user_request : http->request->auth_user_request);
600
62e76326 601 node = (clientStreamNode *)http->client_stream.tail->data;
602 clientStreamRead(node, http, node->readBuffer);
a546b04b 603 return;
edce4d98 604 }
de31d06f 605
606 /* ACCESS_ALLOWED continues here ... */
607 safe_free(http->uri);
608
609 http->uri = xstrdup(urlCanonical(http->request));
610
611 http->doCallouts();
612}
613
a83c6ed6 614#if USE_ADAPTATION
de31d06f 615static void
a83c6ed6 616adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data)
de31d06f 617{
618 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
619
620 if (!calloutContext->httpStateIsValid())
621 return;
622
a83c6ed6 623 calloutContext->adaptationAclCheckDone(service);
de31d06f 624}
625
626void
a83c6ed6 627ClientRequestContext::adaptationAclCheckDone(Adaptation::ServicePointer service)
de31d06f 628{
a83c6ed6 629 debugs(93,3,HERE << this << " adaptationAclCheckDone called");
6ec67de9 630 assert(http);
631
a83c6ed6 632 if (http->startAdaptation(service))
de31d06f 633 return;
634
a83c6ed6 635 if (!service || service->cfg().bypass) {
5f8252d2 636 // handle ICAP start failure when no service was selected
637 // or where the selected service was optional
638 http->doCallouts();
639 return;
640 }
de31d06f 641
5f8252d2 642 // handle start failure for an essential ICAP service
a83c6ed6 643 http->handleAdaptationFailure();
edce4d98 644}
645
de31d06f 646#endif
647
14cc8559 648static void
649clientRedirectAccessCheckDone(int answer, void *data)
650{
651 ClientRequestContext *context = (ClientRequestContext *)data;
59a1efb2 652 ClientHttpRequest *http = context->http;
14cc8559 653 context->acl_checklist = NULL;
654
655 if (answer == ACCESS_ALLOWED)
de31d06f 656 redirectStart(http, clientRedirectDoneWrapper, context);
14cc8559 657 else
de31d06f 658 context->clientRedirectDone(NULL);
14cc8559 659}
660
de31d06f 661void
662ClientRequestContext::clientRedirectStart()
14cc8559 663{
bf8fe701 664 debugs(33, 5, "clientRedirectStart: '" << http->uri << "'");
14cc8559 665
14cc8559 666 if (Config.accessList.redirector) {
de31d06f 667 acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
668 acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
14cc8559 669 } else
de31d06f 670 redirectStart(http, clientRedirectDoneWrapper, this);
14cc8559 671}
672
edce4d98 673static int
59a1efb2 674clientHierarchical(ClientHttpRequest * http)
edce4d98 675{
676 const char *url = http->uri;
190154cf 677 HttpRequest *request = http->request;
60745f24 678 HttpRequestMethod method = request->method;
edce4d98 679 const wordlist *p = NULL;
680
69660be0 681 /*
682 * IMS needs a private key, so we can use the hierarchy for IMS only if our
683 * neighbors support private keys
684 */
62e76326 685
edce4d98 686 if (request->flags.ims && !neighbors_do_private_keys)
62e76326 687 return 0;
688
69660be0 689 /*
690 * This is incorrect: authenticating requests can be sent via a hierarchy
06b97e72 691 * (they can even be cached if the correct headers are set on the reply)
edce4d98 692 */
693 if (request->flags.auth)
62e76326 694 return 0;
695
edce4d98 696 if (method == METHOD_TRACE)
62e76326 697 return 1;
698
edce4d98 699 if (method != METHOD_GET)
62e76326 700 return 0;
701
edce4d98 702 /* scan hierarchy_stoplist */
703 for (p = Config.hierarchy_stoplist; p; p = p->next)
62e76326 704 if (strstr(url, p->key))
705 return 0;
706
edce4d98 707 if (request->flags.loopdetect)
62e76326 708 return 0;
709
edce4d98 710 if (request->protocol == PROTO_HTTP)
62e76326 711 return httpCachable(method);
712
edce4d98 713 if (request->protocol == PROTO_GOPHER)
62e76326 714 return gopherCachable(request);
715
edce4d98 716 if (request->protocol == PROTO_CACHEOBJ)
62e76326 717 return 0;
718
edce4d98 719 return 1;
720}
721
722
723static void
59a1efb2 724clientInterpretRequestHeaders(ClientHttpRequest * http)
edce4d98 725{
190154cf 726 HttpRequest *request = http->request;
0ef77270 727 HttpHeader *req_hdr = &request->header;
edce4d98 728 int no_cache = 0;
f41735ea 729#if !(USE_SQUID_ESI) || defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG)
62e76326 730
edce4d98 731 const char *str;
732#endif
62e76326 733
edce4d98 734 request->imslen = -1;
a9925b40 735 request->ims = req_hdr->getTime(HDR_IF_MODIFIED_SINCE);
62e76326 736
edce4d98 737 if (request->ims > 0)
62e76326 738 request->flags.ims = 1;
739
f41735ea 740#if USE_SQUID_ESI
69660be0 741 /*
742 * We ignore Cache-Control as per the Edge Architecture Section 3. See
743 * www.esi.org for more information.
edce4d98 744 */
745#else
62e76326 746
a9925b40 747 if (req_hdr->has(HDR_PRAGMA)) {
30abd221 748 String s = req_hdr->getList(HDR_PRAGMA);
62e76326 749
750 if (strListIsMember(&s, "no-cache", ','))
751 no_cache++;
30abd221 752
753 s.clean();
edce4d98 754 }
62e76326 755
edce4d98 756 if (request->cache_control)
62e76326 757 if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
758 no_cache++;
759
69660be0 760 /*
62e76326 761 * Work around for supporting the Reload button in IE browsers when Squid
762 * is used as an accelerator or transparent proxy, by turning accelerated
763 * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
764 * actually only fixed in SP1, but we can't tell whether we are talking to
765 * SP1 or not so all 5.5 versions are treated 'normally').
766 */
edce4d98 767 if (Config.onoff.ie_refresh) {
62e76326 768 if (http->flags.accel && request->flags.ims) {
a9925b40 769 if ((str = req_hdr->getStr(HDR_USER_AGENT))) {
62e76326 770 if (strstr(str, "MSIE 5.01") != NULL)
771 no_cache++;
772 else if (strstr(str, "MSIE 5.0") != NULL)
773 no_cache++;
774 else if (strstr(str, "MSIE 4.") != NULL)
775 no_cache++;
776 else if (strstr(str, "MSIE 3.") != NULL)
777 no_cache++;
778 }
779 }
edce4d98 780 }
914b89a2 781
782 if (request->method == METHOD_OTHER) {
26ac0430 783 no_cache++;
60745f24 784 }
62e76326 785
edce4d98 786#endif
787 if (no_cache) {
788#if HTTP_VIOLATIONS
62e76326 789
790 if (Config.onoff.reload_into_ims)
791 request->flags.nocache_hack = 1;
792 else if (refresh_nocache_hack)
793 request->flags.nocache_hack = 1;
794 else
edce4d98 795#endif
62e76326 796
797 request->flags.nocache = 1;
edce4d98 798 }
62e76326 799
0ef77270 800 /* ignore range header in non-GETs or non-HEADs */
801 if (request->method == METHOD_GET || request->method == METHOD_HEAD) {
a9925b40 802 request->range = req_hdr->getRange();
62e76326 803
804 if (request->range) {
805 request->flags.range = 1;
806 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
807 /* XXX: This is suboptimal. We should give the stream the range set,
808 * and thereby let the top of the stream set the offset when the
26ac0430 809 * size becomes known. As it is, we will end up requesting from 0
62e76326 810 * for evey -X range specification.
811 * RBC - this may be somewhat wrong. We should probably set the range
812 * iter up at this point.
813 */
814 node->readBuffer.offset = request->range->lowestOffset(0);
815 http->range_iter.pos = request->range->begin();
816 http->range_iter.valid = true;
817 }
edce4d98 818 }
62e76326 819
0ef77270 820 /* Only HEAD and GET requests permit a Range or Request-Range header.
821 * If these headers appear on any other type of request, delete them now.
822 */
823 else {
824 req_hdr->delById(HDR_RANGE);
825 req_hdr->delById(HDR_REQUEST_RANGE);
826 request->range = NULL;
827 }
828
a9925b40 829 if (req_hdr->has(HDR_AUTHORIZATION))
62e76326 830 request->flags.auth = 1;
831
d67acb4e
AJ
832 ConnStateData *http_conn = http->getConn();
833 assert(http_conn);
834 request->flags.connection_auth_disabled = http_conn->port->connection_auth_disabled;
835 if (!request->flags.connection_auth_disabled) {
26ac0430
AJ
836 if (http_conn->pinning.fd != -1) {
837 if (http_conn->pinning.auth) {
838 request->flags.connection_auth = 1;
839 request->flags.auth = 1;
840 } else {
841 request->flags.connection_proxy_auth = 1;
842 }
843 request->setPinnedConnection(http_conn);
844 }
d67acb4e
AJ
845 }
846
847 /* check if connection auth is used, and flag as candidate for pinning
848 * in such case.
849 * Note: we may need to set flags.connection_auth even if the connection
850 * is already pinned if it was pinned earlier due to proxy auth
851 */
852 if (!request->flags.connection_auth) {
26ac0430
AJ
853 if (req_hdr->has(HDR_AUTHORIZATION) || req_hdr->has(HDR_PROXY_AUTHORIZATION)) {
854 HttpHeaderPos pos = HttpHeaderInitPos;
855 HttpHeaderEntry *e;
856 int may_pin = 0;
857 while ((e = req_hdr->getEntry(&pos))) {
858 if (e->id == HDR_AUTHORIZATION || e->id == HDR_PROXY_AUTHORIZATION) {
5b4117d8 859 const char *value = e->value.rawBuf();
26ac0430
AJ
860 if (strncasecmp(value, "NTLM ", 5) == 0
861 ||
862 strncasecmp(value, "Negotiate ", 10) == 0
863 ||
864 strncasecmp(value, "Kerberos ", 9) == 0) {
865 if (e->id == HDR_AUTHORIZATION) {
866 request->flags.connection_auth = 1;
867 may_pin = 1;
868 } else {
869 request->flags.connection_proxy_auth = 1;
870 may_pin = 1;
871 }
872 }
873 }
874 }
875 if (may_pin && !request->pinnedConnection()) {
876 request->setPinnedConnection(http->getConn());
877 }
878 }
d67acb4e
AJ
879 }
880
881
edce4d98 882 if (request->login[0] != '\0')
62e76326 883 request->flags.auth = 1;
884
a9925b40 885 if (req_hdr->has(HDR_VIA)) {
30abd221 886 String s = req_hdr->getList(HDR_VIA);
62e76326 887 /*
888 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
889 * Note ThisCache2 has a space prepended to the hostname so we don't
890 * accidentally match super-domains.
891 */
892
893 if (strListIsSubstr(&s, ThisCache2, ',')) {
894 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
895 request, (ObjPackMethod) & httpRequestPack);
896 request->flags.loopdetect = 1;
897 }
898
edce4d98 899#if FORW_VIA_DB
99524de7 900 fvdbCountVia(s.unsafeBuf());
62e76326 901
edce4d98 902#endif
62e76326 903
30abd221 904 s.clean();
edce4d98 905 }
62e76326 906
26ac0430
AJ
907 /**
908 \todo --enable-useragent-log and --enable-referer-log. We should
909 probably drop those two as the custom log formats accomplish pretty much the same thing..
910 */
edce4d98 911#if USE_USERAGENT_LOG
a9925b40 912 if ((str = req_hdr->getStr(HDR_USER_AGENT)))
b4306cba 913 logUserAgent(fqdnFromAddr(http->getConn()->log_addr), str);
62e76326 914
edce4d98 915#endif
916#if USE_REFERER_LOG
62e76326 917
a9925b40 918 if ((str = req_hdr->getStr(HDR_REFERER)))
b4306cba 919 logReferer(fqdnFromAddr(http->getConn()->log_addr), str, http->log_uri);
62e76326 920
edce4d98 921#endif
922#if FORW_VIA_DB
62e76326 923
a9925b40 924 if (req_hdr->has(HDR_X_FORWARDED_FOR)) {
30abd221 925 String s = req_hdr->getList(HDR_X_FORWARDED_FOR);
99524de7 926 fvdbCountForw(s.unsafeBuf());
30abd221 927 s.clean();
edce4d98 928 }
62e76326 929
edce4d98 930#endif
931 if (request->method == METHOD_TRACE) {
a9925b40 932 request->max_forwards = req_hdr->getInt(HDR_MAX_FORWARDS);
edce4d98 933 }
62e76326 934
610ee341 935 request->flags.cachable = http->request->cacheable();
62e76326 936
edce4d98 937 if (clientHierarchical(http))
62e76326 938 request->flags.hierarchical = 1;
939
bf8fe701 940 debugs(85, 5, "clientInterpretRequestHeaders: REQ_NOCACHE = " <<
941 (request->flags.nocache ? "SET" : "NOT SET"));
942 debugs(85, 5, "clientInterpretRequestHeaders: REQ_CACHABLE = " <<
943 (request->flags.cachable ? "SET" : "NOT SET"));
944 debugs(85, 5, "clientInterpretRequestHeaders: REQ_HIERARCHICAL = " <<
945 (request->flags.hierarchical ? "SET" : "NOT SET"));
62e76326 946
edce4d98 947}
948
949void
de31d06f 950clientRedirectDoneWrapper(void *data, char *result)
edce4d98 951{
de31d06f 952 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
db02222f 953
de31d06f 954 if (!calloutContext->httpStateIsValid())
62e76326 955 return;
62e76326 956
de31d06f 957 calloutContext->clientRedirectDone(result);
958}
959
960void
961ClientRequestContext::clientRedirectDone(char *result)
962{
190154cf 963 HttpRequest *new_request = NULL;
964 HttpRequest *old_request = http->request;
bf8fe701 965 debugs(85, 5, "clientRedirectDone: '" << http->uri << "' result=" << (result ? result : "NULL"));
de31d06f 966 assert(redirect_state == REDIRECT_PENDING);
967 redirect_state = REDIRECT_DONE;
62e76326 968
edce4d98 969 if (result) {
62e76326 970 http_status status = (http_status) atoi(result);
971
972 if (status == HTTP_MOVED_PERMANENTLY
973 || status == HTTP_MOVED_TEMPORARILY
974 || status == HTTP_SEE_OTHER
975 || status == HTTP_TEMPORARY_REDIRECT) {
976 char *t = result;
977
978 if ((t = strchr(result, ':')) != NULL) {
979 http->redirect.status = status;
980 http->redirect.location = xstrdup(t + 1);
981 } else {
bf8fe701 982 debugs(85, 1, "clientRedirectDone: bad input: " << result);
62e76326 983 }
2baf58c3 984 } else if (strcmp(result, http->uri))
c21ad0f5 985 new_request = HttpRequest::CreateFromUrlAndMethod(result, old_request->method);
edce4d98 986 }
62e76326 987
edce4d98 988 if (new_request) {
62e76326 989 safe_free(http->uri);
990 http->uri = xstrdup(urlCanonical(new_request));
991 new_request->http_ver = old_request->http_ver;
a9925b40 992 new_request->header.append(&old_request->header);
62e76326 993 new_request->client_addr = old_request->client_addr;
3d674977
AJ
994#if FOLLOW_X_FORWARDED_FOR
995 new_request->indirect_client_addr = old_request->indirect_client_addr;
996#endif /* FOLLOW_X_FORWARDED_FOR */
62e76326 997 new_request->my_addr = old_request->my_addr;
62e76326 998 new_request->flags = old_request->flags;
3c1f01bc 999 new_request->flags.redirected = 1;
62e76326 1000
1001 if (old_request->auth_user_request) {
1002 new_request->auth_user_request = old_request->auth_user_request;
4f0ef8e8 1003 AUTHUSERREQUESTLOCK(new_request->auth_user_request, "new request");
62e76326 1004 }
1005
5f8252d2 1006 if (old_request->body_pipe != NULL) {
1007 new_request->body_pipe = old_request->body_pipe;
1008 old_request->body_pipe = NULL;
1009 debugs(0,0,HERE << "redirecting body_pipe " << new_request->body_pipe << " from request " << old_request << " to " << new_request);
62e76326 1010 }
1011
1012 new_request->content_length = old_request->content_length;
abb929f0 1013 new_request->extacl_user = old_request->extacl_user;
1014 new_request->extacl_passwd = old_request->extacl_passwd;
62e76326 1015 new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
6dd9f4bd 1016 HTTPMSGUNLOCK(old_request);
1017 http->request = HTTPMSGLOCK(new_request);
edce4d98 1018 }
62e76326 1019
edce4d98 1020 /* FIXME PIPELINE: This is innacurate during pipelining */
62e76326 1021
5f8252d2 1022 if (http->getConn() != NULL)
98242069 1023 fd_note(http->getConn()->fd, http->uri);
62e76326 1024
c8be6d7b 1025 assert(http->uri);
62e76326 1026
de31d06f 1027 http->doCallouts();
edce4d98 1028}
1029
b50e327b
AJ
1030/** Test cache allow/deny configuration
1031 * Sets flags.cachable=1 if caching is not denied.
1032 */
edce4d98 1033void
8e2745f4 1034ClientRequestContext::checkNoCache()
edce4d98 1035{
b50e327b
AJ
1036 if (Config.accessList.noCache) {
1037 acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
1038 acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, this);
1039 } else {
1040 /* unless otherwise specified, we try to cache. */
1041 checkNoCacheDone(1);
1042 }
edce4d98 1043}
1044
de31d06f 1045static void
1046checkNoCacheDoneWrapper(int answer, void *data)
edce4d98 1047{
de31d06f 1048 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
e4a67a80 1049
de31d06f 1050 if (!calloutContext->httpStateIsValid())
1051 return;
1052
1053 calloutContext->checkNoCacheDone(answer);
8e2745f4 1054}
4fb35c3c 1055
8e2745f4 1056void
1057ClientRequestContext::checkNoCacheDone(int answer)
62e76326 1058{
8e2745f4 1059 acl_checklist = NULL;
de31d06f 1060 http->request->flags.cachable = answer;
1061 http->doCallouts();
edce4d98 1062}
1063
69660be0 1064/*
1065 * Identify requests that do not go through the store and client side stream
1066 * and forward them to the appropriate location. All other requests, request
1067 * them.
edce4d98 1068 */
1069void
8e2745f4 1070ClientHttpRequest::processRequest()
edce4d98 1071{
60745f24 1072 debugs(85, 4, "clientProcessRequest: " << RequestMethodStr(request->method) << " '" << uri << "'");
62e76326 1073
3712be3f 1074#if USE_SSL
1075 if (request->method == METHOD_CONNECT && sslBumpNeeded()) {
1076 sslBumpStart();
1077 return;
1078 }
1079#endif
1080
2baf58c3 1081 if (request->method == METHOD_CONNECT && !redirect.status) {
62e76326 1082 logType = LOG_TCP_MISS;
11007d4b 1083 tunnelStart(this, &out.size, &al.http.code);
62e76326 1084 return;
edce4d98 1085 }
62e76326 1086
8e2745f4 1087 httpStart();
1088}
1089
1090void
1091ClientHttpRequest::httpStart()
1092{
559da936 1093 PROF_start(httpStart);
8e2745f4 1094 logType = LOG_TAG_NONE;
bf8fe701 1095 debugs(85, 4, "ClientHttpRequest::httpStart: " << log_tags[logType] << " for '" << uri << "'");
1096
edce4d98 1097 /* no one should have touched this */
8e2745f4 1098 assert(out.offset == 0);
edce4d98 1099 /* Use the Stream Luke */
8e2745f4 1100 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
1101 clientStreamRead(node, this, node->readBuffer);
559da936 1102 PROF_stop(httpStart);
edce4d98 1103}
0655fa4d 1104
3712be3f 1105#if USE_SSL
1106
1107// determines whether we should bump the CONNECT request
1108bool
26ac0430
AJ
1109ClientHttpRequest::sslBumpNeeded() const
1110{
3712be3f 1111 if (!getConn()->port->sslBump || !Config.accessList.ssl_bump)
1112 return false;
1113
1114 debugs(85, 5, HERE << "SslBump possible, checking ACL");
1115
1116 ACLChecklist check;
1117 check.src_addr = request->client_addr;
26ac0430
AJ
1118 check.my_addr = request->my_addr;
1119 check.request = HTTPMSGLOCK(request);
1120 check.accessList = cbdataReference(Config.accessList.ssl_bump);
1121 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
1122 return check.fastCheck() == 1;
3712be3f 1123}
1124
1125// called when comm_write has completed
1126static void
1127SslBumpEstablish(int, char *, size_t, comm_err_t errflag, int, void *data)
1128{
1129 ClientHttpRequest *r = static_cast<ClientHttpRequest*>(data);
1130 debugs(85, 5, HERE << "responded to CONNECT: " << r << " ? " << errflag);
1131
1132 assert(r && cbdataReferenceValid(r));
1133 r->sslBumpEstablish(errflag);
1134}
1135
1136void
1137ClientHttpRequest::sslBumpEstablish(comm_err_t errflag)
1138{
1139 // Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up
1140 if (errflag == COMM_ERR_CLOSING)
1141 return;
1142
1143 if (errflag) {
1144 getConn()->startClosing("CONNECT response failure in SslBump");
1145 return;
1146 }
1147
1148 getConn()->switchToHttps();
1149}
1150
1151void
1152ClientHttpRequest::sslBumpStart()
1153{
1154 debugs(85, 5, HERE << "ClientHttpRequest::sslBumpStart");
1155
1156 // send an HTTP 200 response to kick client SSL negotiation
1157 const int fd = getConn()->fd;
1158 debugs(33, 7, HERE << "Confirming CONNECT tunnel on FD " << fd);
1159
1160 // TODO: Unify with tunnel.cc and add a Server(?) header
1161 static const char *const conn_established =
1162 "HTTP/1.0 200 Connection established\r\n\r\n";
1163 comm_write(fd, conn_established, strlen(conn_established),
26ac0430 1164 &SslBumpEstablish, this, NULL);
3712be3f 1165}
1166
1167#endif
1168
0655fa4d 1169bool
1170ClientHttpRequest::gotEnough() const
1171{
86a2f789 1172 /** TODO: should be querying the stream. */
7173d5b0 1173 int64_t contentLength =
06a5ae20 1174 memObject()->getReply()->bodySize(request->method);
0655fa4d 1175 assert(contentLength >= 0);
1176
1177 if (out.offset < contentLength)
1178 return false;
1179
1180 return true;
1181}
1182
86a2f789 1183void
1184ClientHttpRequest::storeEntry(StoreEntry *newEntry)
1185{
1186 entry_ = newEntry;
1187}
1188
0976f8db 1189void
1190ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
1191{
1192 if (loggingEntry_)
97b5e68f 1193 loggingEntry_->unlock();
0976f8db 1194
1195 loggingEntry_ = newEntry;
1196
1197 if (loggingEntry_)
3d0ac046 1198 loggingEntry_->lock();
0976f8db 1199}
86a2f789 1200
de31d06f 1201/*
1202 * doCallouts() - This function controls the order of "callout"
1203 * executions, including non-blocking access control checks, the
1204 * redirector, and ICAP. Previously, these callouts were chained
1205 * together such that "clientAccessCheckDone()" would call
1206 * "clientRedirectStart()" and so on.
1207 *
1208 * The ClientRequestContext (aka calloutContext) class holds certain
1209 * state data for the callout/callback operations. Previously
1210 * ClientHttpRequest would sort of hand off control to ClientRequestContext
1211 * for a short time. ClientRequestContext would then delete itself
1212 * and pass control back to ClientHttpRequest when all callouts
1213 * were finished.
1214 *
1215 * This caused some problems for ICAP because we want to make the
1216 * ICAP callout after checking ACLs, but before checking the no_cache
1217 * list. We can't stuff the ICAP state into the ClientRequestContext
1218 * class because we still need the ICAP state after ClientRequestContext
1219 * goes away.
1220 *
1221 * Note that ClientRequestContext is created before the first call
1222 * to doCallouts().
1223 *
1224 * If one of the callouts notices that ClientHttpRequest is no
1225 * longer valid, it should call cbdataReferenceDone() so that
1226 * ClientHttpRequest's reference count goes to zero and it will get
1227 * deleted. ClientHttpRequest will then delete ClientRequestContext.
1228 *
1229 * Note that we set the _done flags here before actually starting
1230 * the callout. This is strictly for convenience.
1231 */
1232
057f5854 1233extern int aclMapTOS (acl_tos * head, ACLChecklist * ch);
1234
de31d06f 1235void
1236ClientHttpRequest::doCallouts()
1237{
1238 assert(calloutContext);
1239
1240 if (!calloutContext->http_access_done) {
3712be3f 1241 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
57abaac0 1242 calloutContext->http_access_done = true;
de31d06f 1243 calloutContext->clientAccessCheck();
1244 return;
1245 }
1246
a83c6ed6 1247#if USE_ADAPTATION
abd4b611 1248 if (!calloutContext->adaptation_acl_check_done) {
a83c6ed6 1249 calloutContext->adaptation_acl_check_done = true;
abd4b611 1250 if (Adaptation::AccessCheck::Start(
26ac0430
AJ
1251 Adaptation::methodReqmod, Adaptation::pointPreCache,
1252 request, NULL, adaptationAclCheckDoneWrapper, calloutContext))
abd4b611 1253 return; // will call callback
de31d06f 1254 }
de31d06f 1255#endif
1256
1257 if (!calloutContext->redirect_done) {
57abaac0 1258 calloutContext->redirect_done = true;
de31d06f 1259 assert(calloutContext->redirect_state == REDIRECT_NONE);
1260
1261 if (Config.Program.redirect) {
3712be3f 1262 debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
de31d06f 1263 calloutContext->redirect_state = REDIRECT_PENDING;
1264 calloutContext->clientRedirectStart();
1265 return;
1266 }
1267 }
1268
57abaac0 1269 if (!calloutContext->interpreted_req_hdrs) {
3712be3f 1270 debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
57abaac0 1271 calloutContext->interpreted_req_hdrs = 1;
1272 clientInterpretRequestHeaders(this);
1273 }
1274
de31d06f 1275 if (!calloutContext->no_cache_done) {
57abaac0 1276 calloutContext->no_cache_done = true;
de31d06f 1277
1278 if (Config.accessList.noCache && request->flags.cachable) {
3712be3f 1279 debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
de31d06f 1280 calloutContext->checkNoCache();
1281 return;
1282 }
1283 }
1284
057f5854 1285 if (!calloutContext->clientside_tos_done) {
1286 calloutContext->clientside_tos_done = true;
3712be3f 1287 if (getConn() != NULL) {
1288 ACLChecklist ch;
057f5854 1289 ch.src_addr = request->client_addr;
1290 ch.my_addr = request->my_addr;
057f5854 1291 ch.request = HTTPMSGLOCK(request);
3712be3f 1292 int tos = aclMapTOS(Config.accessList.clientside_tos, &ch);
1293 if (tos)
1294 comm_set_tos(getConn()->fd, tos);
1295 }
057f5854 1296 }
1297
de31d06f 1298 cbdataReferenceDone(calloutContext->http);
1299 delete calloutContext;
1300 calloutContext = NULL;
de31d06f 1301#if HEADERS_LOG
1302
1303 headersLog(0, 1, request->method, request);
1304#endif
1305
58d7150d 1306 debugs(83, 3, HERE << "calling processRequest()");
de31d06f 1307 processRequest();
1308}
1309
86a2f789 1310#ifndef _USE_INLINE_
1311#include "client_side_request.cci"
1312#endif
de31d06f 1313
a83c6ed6 1314#if USE_ADAPTATION
de31d06f 1315/*
26ac0430 1316 * Initiate an ICAP transaction. Return false on errors.
5f8252d2 1317 * The caller must handle errors.
de31d06f 1318 */
5f8252d2 1319bool
a83c6ed6 1320ClientHttpRequest::startAdaptation(Adaptation::ServicePointer service)
3b299123 1321{
a83c6ed6 1322 debugs(85, 3, HERE << this << " ClientHttpRequest::startAdaptation() called");
5f8252d2 1323 if (!service) {
a83c6ed6 1324 debugs(85, 3, "ClientHttpRequest::startAdaptation fails: lack of service");
5f8252d2 1325 return false;
3b299123 1326 }
5f8252d2 1327 if (service->broken()) {
a83c6ed6 1328 debugs(85, 3, "ClientHttpRequest::startAdaptation fails: broken service");
5f8252d2 1329 return false;
3b299123 1330 }
1331
a83c6ed6
AR
1332 assert(!virginHeadSource);
1333 assert(!adaptedBodySource);
1334 virginHeadSource = initiateAdaptation(service->makeXactLauncher(
26ac0430 1335 this, request, NULL));
a83c6ed6
AR
1336
1337 return virginHeadSource != NULL;
de31d06f 1338}
1339
1340void
a83c6ed6 1341ClientHttpRequest::noteAdaptationAnswer(HttpMsg *msg)
de31d06f 1342{
de31d06f 1343 assert(cbdataReferenceValid(this)); // indicates bug
5f8252d2 1344 assert(msg);
1345
b044675d 1346 if (HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg)) {
200ac359 1347 /*
5f8252d2 1348 * Replace the old request with the new request.
200ac359 1349 */
6dd9f4bd 1350 HTTPMSGUNLOCK(request);
1351 request = HTTPMSGLOCK(new_req);
200ac359 1352 /*
1353 * Store the new URI for logging
1354 */
1355 xfree(uri);
1356 uri = xstrdup(urlCanonical(request));
1357 setLogUri(this, urlCanonicalClean(request));
914b89a2 1358 assert(request->method.id());
b044675d 1359 } else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
1360 debugs(85,3,HERE << "REQMOD reply is HTTP reply");
1361
5f8252d2 1362 // subscribe to receive reply body
1363 if (new_rep->body_pipe != NULL) {
a83c6ed6
AR
1364 adaptedBodySource = new_rep->body_pipe;
1365 assert(adaptedBodySource->setConsumerIfNotLate(this));
5f8252d2 1366 }
1367
b044675d 1368 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1369 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1370 repContext->createStoreEntry(request->method, request->flags);
1371
1372 EBIT_CLR(storeEntry()->flags, ENTRY_FWD_HDR_WAIT);
1373 request_satisfaction_mode = true;
1374 request_satisfaction_offset = 0;
1375 storeEntry()->replaceHttpReply(new_rep);
cb4c4288 1376
a83c6ed6 1377 if (!adaptedBodySource) // no body
cb4c4288 1378 storeEntry()->complete();
b044675d 1379 clientGetMoreData(node, this);
200ac359 1380 }
de31d06f 1381
5f8252d2 1382 // we are done with getting headers (but may be receiving body)
a83c6ed6 1383 clearAdaptation(virginHeadSource);
5f8252d2 1384
b044675d 1385 if (!request_satisfaction_mode)
1386 doCallouts();
de31d06f 1387}
1388
1389void
a83c6ed6 1390ClientHttpRequest::noteAdaptationQueryAbort(bool final)
de31d06f 1391{
a83c6ed6
AR
1392 clearAdaptation(virginHeadSource);
1393 assert(!adaptedBodySource);
1394 handleAdaptationFailure(!final);
de31d06f 1395}
1396
1397void
1cf238db 1398ClientHttpRequest::noteMoreBodyDataAvailable(BodyPipe::Pointer)
de31d06f 1399{
5f8252d2 1400 assert(request_satisfaction_mode);
a83c6ed6 1401 assert(adaptedBodySource != NULL);
5f8252d2 1402
99524de7 1403 if (const size_t contentSize = adaptedBodySource->unsafeBuf().contentSize()) {
a83c6ed6 1404 BodyPipeCheckout bpc(*adaptedBodySource);
99524de7 1405 const StoreIOBuffer ioBuf(&bpc.unsafeBuf, request_satisfaction_offset);
5f8252d2 1406 storeEntry()->write(ioBuf);
1407 // assume can write everything
1408 request_satisfaction_offset += contentSize;
99524de7 1409 bpc.unsafeBuf.consume(contentSize);
5f8252d2 1410 bpc.checkIn();
1411 }
1412
a83c6ed6 1413 if (adaptedBodySource->exhausted())
5f8252d2 1414 endRequestSatisfaction();
1415 // else wait for more body data
de31d06f 1416}
1417
1418void
1cf238db 1419ClientHttpRequest::noteBodyProductionEnded(BodyPipe::Pointer)
de31d06f 1420{
a83c6ed6
AR
1421 assert(!virginHeadSource);
1422 if (adaptedBodySource != NULL) { // did not end request satisfaction yet
26ac0430 1423 // We do not expect more because noteMoreBodyDataAvailable always
5f8252d2 1424 // consumes everything. We do not even have a mechanism to consume
1425 // leftovers after noteMoreBodyDataAvailable notifications seize.
a83c6ed6 1426 assert(adaptedBodySource->exhausted());
5f8252d2 1427 endRequestSatisfaction();
1428 }
1429}
3b299123 1430
5f8252d2 1431void
26ac0430
AJ
1432ClientHttpRequest::endRequestSatisfaction()
1433{
5f8252d2 1434 debugs(85,4, HERE << this << " ends request satisfaction");
1435 assert(request_satisfaction_mode);
a83c6ed6 1436 stopConsumingFrom(adaptedBodySource);
3b299123 1437
5f8252d2 1438 // TODO: anything else needed to end store entry formation correctly?
1439 storeEntry()->complete();
1440}
de31d06f 1441
5f8252d2 1442void
1cf238db 1443ClientHttpRequest::noteBodyProducerAborted(BodyPipe::Pointer)
5f8252d2 1444{
a83c6ed6
AR
1445 assert(!virginHeadSource);
1446 stopConsumingFrom(adaptedBodySource);
1447 handleAdaptationFailure();
5f8252d2 1448}
3b299123 1449
5f8252d2 1450void
a83c6ed6 1451ClientHttpRequest::handleAdaptationFailure(bool bypassable)
5f8252d2 1452{
a83c6ed6 1453 debugs(85,3, HERE << "handleAdaptationFailure(" << bypassable << ")");
3b299123 1454
5f8252d2 1455 const bool usedStore = storeEntry() && !storeEntry()->isEmpty();
1456 const bool usedPipe = request->body_pipe != NULL &&
26ac0430 1457 request->body_pipe->consumedSize() > 0;
3b299123 1458
9d4d7c5e 1459 if (bypassable && !usedStore && !usedPipe) {
1460 debugs(85,3, HERE << "ICAP REQMOD callout failed, bypassing: " << calloutContext);
5f8252d2 1461 if (calloutContext)
1462 doCallouts();
1463 return;
1464 }
3b299123 1465
5f8252d2 1466 debugs(85,3, HERE << "ICAP REQMOD callout failed, responding with error");
3b299123 1467
5f8252d2 1468 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1469 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1470 assert(repContext);
de31d06f 1471
26ac0430 1472 // The original author of the code also wanted to pass an errno to
5f8252d2 1473 // setReplyToError, but it seems unlikely that the errno reflects the
1474 // true cause of the error at this point, so I did not pass it.
ad61a2b4 1475 IpAddress noAddr;
b70ba605 1476 noAddr.SetNoAddr();
1cf238db 1477 ConnStateData * c = getConn();
5f8252d2 1478 repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
26ac0430
AJ
1479 request->method, NULL,
1480 (c != NULL ? c->peer : noAddr), request, NULL,
1481 (c != NULL && c->auth_user_request ?
1482 c->auth_user_request : request->auth_user_request));
de31d06f 1483
5f8252d2 1484 node = (clientStreamNode *)client_stream.tail->data;
1485 clientStreamRead(node, this, node->readBuffer);
de31d06f 1486}
1487
1488#endif