]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
Author: Christos Tsantilas <chtsanti@users.sourceforge.net>
[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;
429 p = request->x_forwarded_for_iterator.buf();
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
AJ
511
512 acl_checklist = clientAclChecklistCreate(Config.accessList.http, http);
de31d06f 513 acl_checklist->nonBlockingCheck(clientAccessCheckDoneWrapper, this);
edce4d98 514}
515
516void
de31d06f 517clientAccessCheckDoneWrapper(int answer, void *data)
edce4d98 518{
de31d06f 519 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
fbade053 520
de31d06f 521 if (!calloutContext->httpStateIsValid())
62e76326 522 return;
62e76326 523
de31d06f 524 calloutContext->clientAccessCheckDone(answer);
525}
526
527void
528ClientRequestContext::clientAccessCheckDone(int answer)
529{
530 acl_checklist = NULL;
edce4d98 531 err_type page_id;
532 http_status status;
26ac0430
AJ
533 debugs(85, 2, "The request " <<
534 RequestMethodStr(http->request->method) << " " <<
535 http->uri << " is " <<
536 (answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED") <<
537 ", because it matched '" <<
538 (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" );
f5691f9c 539 char const *proxy_auth_msg = "<null>";
540
94a396a3 541 if (http->getConn() != NULL && http->getConn()->auth_user_request != NULL)
f5691f9c 542 proxy_auth_msg = http->getConn()->auth_user_request->denyMessage("<null>");
543 else if (http->request->auth_user_request != NULL)
544 proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
62e76326 545
de31d06f 546 if (answer != ACCESS_ALLOWED) {
62e76326 547 /* Send an error */
9ce7856a 548 int require_auth = (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName));
bf8fe701 549 debugs(85, 5, "Access Denied: " << http->uri);
550 debugs(85, 5, "AclMatchedName = " << (AclMatchedName ? AclMatchedName : "<null>"));
9ce7856a 551
552 if (require_auth)
bf8fe701 553 debugs(33, 5, "Proxy Auth Message = " << (proxy_auth_msg ? proxy_auth_msg : "<null>"));
9ce7856a 554
62e76326 555 /*
556 * NOTE: get page_id here, based on AclMatchedName because if
557 * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
558 * the clientCreateStoreEntry() call just below. Pedro Ribeiro
559 * <pribeiro@isel.pt>
560 */
9ce7856a 561 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, answer != ACCESS_REQ_PROXY_AUTH);
562
62e76326 563 http->logType = LOG_TCP_DENIED;
564
9ce7856a 565 if (require_auth) {
62e76326 566 if (!http->flags.accel) {
567 /* Proxy authorisation needed */
568 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
569 } else {
570 /* WWW authorisation needed */
571 status = HTTP_UNAUTHORIZED;
572 }
573
574 if (page_id == ERR_NONE)
575 page_id = ERR_CACHE_ACCESS_DENIED;
576 } else {
577 status = HTTP_FORBIDDEN;
578
579 if (page_id == ERR_NONE)
580 page_id = ERR_ACCESS_DENIED;
581 }
582
de31d06f 583 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
0655fa4d 584 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
585 assert (repContext);
ad61a2b4 586 IpAddress tmpnoaddr;
26ac0430 587 tmpnoaddr.SetNoAddr();
0655fa4d 588 repContext->setReplyToError(page_id, status,
589 http->request->method, NULL,
cc192b50 590 http->getConn() != NULL ? http->getConn()->peer : tmpnoaddr,
591 http->request,
592 NULL,
593 http->getConn() != NULL && http->getConn()->auth_user_request ?
594 http->getConn()->auth_user_request : http->request->auth_user_request);
595
62e76326 596 node = (clientStreamNode *)http->client_stream.tail->data;
597 clientStreamRead(node, http, node->readBuffer);
a546b04b 598 return;
edce4d98 599 }
de31d06f 600
601 /* ACCESS_ALLOWED continues here ... */
602 safe_free(http->uri);
603
604 http->uri = xstrdup(urlCanonical(http->request));
605
606 http->doCallouts();
607}
608
a83c6ed6 609#if USE_ADAPTATION
de31d06f 610static void
a83c6ed6 611adaptationAclCheckDoneWrapper(Adaptation::ServicePointer service, void *data)
de31d06f 612{
613 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
614
615 if (!calloutContext->httpStateIsValid())
616 return;
617
a83c6ed6 618 calloutContext->adaptationAclCheckDone(service);
de31d06f 619}
620
621void
a83c6ed6 622ClientRequestContext::adaptationAclCheckDone(Adaptation::ServicePointer service)
de31d06f 623{
a83c6ed6 624 debugs(93,3,HERE << this << " adaptationAclCheckDone called");
6ec67de9 625 assert(http);
626
a83c6ed6 627 if (http->startAdaptation(service))
de31d06f 628 return;
629
a83c6ed6 630 if (!service || service->cfg().bypass) {
5f8252d2 631 // handle ICAP start failure when no service was selected
632 // or where the selected service was optional
633 http->doCallouts();
634 return;
635 }
de31d06f 636
5f8252d2 637 // handle start failure for an essential ICAP service
a83c6ed6 638 http->handleAdaptationFailure();
edce4d98 639}
640
de31d06f 641#endif
642
14cc8559 643static void
644clientRedirectAccessCheckDone(int answer, void *data)
645{
646 ClientRequestContext *context = (ClientRequestContext *)data;
59a1efb2 647 ClientHttpRequest *http = context->http;
14cc8559 648 context->acl_checklist = NULL;
649
650 if (answer == ACCESS_ALLOWED)
de31d06f 651 redirectStart(http, clientRedirectDoneWrapper, context);
14cc8559 652 else
de31d06f 653 context->clientRedirectDone(NULL);
14cc8559 654}
655
de31d06f 656void
657ClientRequestContext::clientRedirectStart()
14cc8559 658{
bf8fe701 659 debugs(33, 5, "clientRedirectStart: '" << http->uri << "'");
14cc8559 660
14cc8559 661 if (Config.accessList.redirector) {
de31d06f 662 acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
663 acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
14cc8559 664 } else
de31d06f 665 redirectStart(http, clientRedirectDoneWrapper, this);
14cc8559 666}
667
edce4d98 668static int
59a1efb2 669clientHierarchical(ClientHttpRequest * http)
edce4d98 670{
671 const char *url = http->uri;
190154cf 672 HttpRequest *request = http->request;
60745f24 673 HttpRequestMethod method = request->method;
edce4d98 674 const wordlist *p = NULL;
675
69660be0 676 /*
677 * IMS needs a private key, so we can use the hierarchy for IMS only if our
678 * neighbors support private keys
679 */
62e76326 680
edce4d98 681 if (request->flags.ims && !neighbors_do_private_keys)
62e76326 682 return 0;
683
69660be0 684 /*
685 * This is incorrect: authenticating requests can be sent via a hierarchy
06b97e72 686 * (they can even be cached if the correct headers are set on the reply)
edce4d98 687 */
688 if (request->flags.auth)
62e76326 689 return 0;
690
edce4d98 691 if (method == METHOD_TRACE)
62e76326 692 return 1;
693
edce4d98 694 if (method != METHOD_GET)
62e76326 695 return 0;
696
edce4d98 697 /* scan hierarchy_stoplist */
698 for (p = Config.hierarchy_stoplist; p; p = p->next)
62e76326 699 if (strstr(url, p->key))
700 return 0;
701
edce4d98 702 if (request->flags.loopdetect)
62e76326 703 return 0;
704
edce4d98 705 if (request->protocol == PROTO_HTTP)
62e76326 706 return httpCachable(method);
707
edce4d98 708 if (request->protocol == PROTO_GOPHER)
62e76326 709 return gopherCachable(request);
710
edce4d98 711 if (request->protocol == PROTO_CACHEOBJ)
62e76326 712 return 0;
713
edce4d98 714 return 1;
715}
716
717
718static void
59a1efb2 719clientInterpretRequestHeaders(ClientHttpRequest * http)
edce4d98 720{
190154cf 721 HttpRequest *request = http->request;
0ef77270 722 HttpHeader *req_hdr = &request->header;
edce4d98 723 int no_cache = 0;
f41735ea 724#if !(USE_SQUID_ESI) || defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG)
62e76326 725
edce4d98 726 const char *str;
727#endif
62e76326 728
edce4d98 729 request->imslen = -1;
a9925b40 730 request->ims = req_hdr->getTime(HDR_IF_MODIFIED_SINCE);
62e76326 731
edce4d98 732 if (request->ims > 0)
62e76326 733 request->flags.ims = 1;
734
f41735ea 735#if USE_SQUID_ESI
69660be0 736 /*
737 * We ignore Cache-Control as per the Edge Architecture Section 3. See
738 * www.esi.org for more information.
edce4d98 739 */
740#else
62e76326 741
a9925b40 742 if (req_hdr->has(HDR_PRAGMA)) {
30abd221 743 String s = req_hdr->getList(HDR_PRAGMA);
62e76326 744
745 if (strListIsMember(&s, "no-cache", ','))
746 no_cache++;
30abd221 747
748 s.clean();
edce4d98 749 }
62e76326 750
edce4d98 751 if (request->cache_control)
62e76326 752 if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
753 no_cache++;
754
69660be0 755 /*
62e76326 756 * Work around for supporting the Reload button in IE browsers when Squid
757 * is used as an accelerator or transparent proxy, by turning accelerated
758 * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
759 * actually only fixed in SP1, but we can't tell whether we are talking to
760 * SP1 or not so all 5.5 versions are treated 'normally').
761 */
edce4d98 762 if (Config.onoff.ie_refresh) {
62e76326 763 if (http->flags.accel && request->flags.ims) {
a9925b40 764 if ((str = req_hdr->getStr(HDR_USER_AGENT))) {
62e76326 765 if (strstr(str, "MSIE 5.01") != NULL)
766 no_cache++;
767 else if (strstr(str, "MSIE 5.0") != NULL)
768 no_cache++;
769 else if (strstr(str, "MSIE 4.") != NULL)
770 no_cache++;
771 else if (strstr(str, "MSIE 3.") != NULL)
772 no_cache++;
773 }
774 }
edce4d98 775 }
914b89a2 776
777 if (request->method == METHOD_OTHER) {
26ac0430 778 no_cache++;
60745f24 779 }
62e76326 780
edce4d98 781#endif
782 if (no_cache) {
783#if HTTP_VIOLATIONS
62e76326 784
785 if (Config.onoff.reload_into_ims)
786 request->flags.nocache_hack = 1;
787 else if (refresh_nocache_hack)
788 request->flags.nocache_hack = 1;
789 else
edce4d98 790#endif
62e76326 791
792 request->flags.nocache = 1;
edce4d98 793 }
62e76326 794
0ef77270 795 /* ignore range header in non-GETs or non-HEADs */
796 if (request->method == METHOD_GET || request->method == METHOD_HEAD) {
a9925b40 797 request->range = req_hdr->getRange();
62e76326 798
799 if (request->range) {
800 request->flags.range = 1;
801 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
802 /* XXX: This is suboptimal. We should give the stream the range set,
803 * and thereby let the top of the stream set the offset when the
26ac0430 804 * size becomes known. As it is, we will end up requesting from 0
62e76326 805 * for evey -X range specification.
806 * RBC - this may be somewhat wrong. We should probably set the range
807 * iter up at this point.
808 */
809 node->readBuffer.offset = request->range->lowestOffset(0);
810 http->range_iter.pos = request->range->begin();
811 http->range_iter.valid = true;
812 }
edce4d98 813 }
62e76326 814
0ef77270 815 /* Only HEAD and GET requests permit a Range or Request-Range header.
816 * If these headers appear on any other type of request, delete them now.
817 */
818 else {
819 req_hdr->delById(HDR_RANGE);
820 req_hdr->delById(HDR_REQUEST_RANGE);
821 request->range = NULL;
822 }
823
a9925b40 824 if (req_hdr->has(HDR_AUTHORIZATION))
62e76326 825 request->flags.auth = 1;
826
d67acb4e
AJ
827 ConnStateData *http_conn = http->getConn();
828 assert(http_conn);
829 request->flags.connection_auth_disabled = http_conn->port->connection_auth_disabled;
830 if (!request->flags.connection_auth_disabled) {
26ac0430
AJ
831 if (http_conn->pinning.fd != -1) {
832 if (http_conn->pinning.auth) {
833 request->flags.connection_auth = 1;
834 request->flags.auth = 1;
835 } else {
836 request->flags.connection_proxy_auth = 1;
837 }
838 request->setPinnedConnection(http_conn);
839 }
d67acb4e
AJ
840 }
841
842 /* check if connection auth is used, and flag as candidate for pinning
843 * in such case.
844 * Note: we may need to set flags.connection_auth even if the connection
845 * is already pinned if it was pinned earlier due to proxy auth
846 */
847 if (!request->flags.connection_auth) {
26ac0430
AJ
848 if (req_hdr->has(HDR_AUTHORIZATION) || req_hdr->has(HDR_PROXY_AUTHORIZATION)) {
849 HttpHeaderPos pos = HttpHeaderInitPos;
850 HttpHeaderEntry *e;
851 int may_pin = 0;
852 while ((e = req_hdr->getEntry(&pos))) {
853 if (e->id == HDR_AUTHORIZATION || e->id == HDR_PROXY_AUTHORIZATION) {
854 const char *value = e->value.buf();
855 if (strncasecmp(value, "NTLM ", 5) == 0
856 ||
857 strncasecmp(value, "Negotiate ", 10) == 0
858 ||
859 strncasecmp(value, "Kerberos ", 9) == 0) {
860 if (e->id == HDR_AUTHORIZATION) {
861 request->flags.connection_auth = 1;
862 may_pin = 1;
863 } else {
864 request->flags.connection_proxy_auth = 1;
865 may_pin = 1;
866 }
867 }
868 }
869 }
870 if (may_pin && !request->pinnedConnection()) {
871 request->setPinnedConnection(http->getConn());
872 }
873 }
d67acb4e
AJ
874 }
875
876
edce4d98 877 if (request->login[0] != '\0')
62e76326 878 request->flags.auth = 1;
879
a9925b40 880 if (req_hdr->has(HDR_VIA)) {
30abd221 881 String s = req_hdr->getList(HDR_VIA);
62e76326 882 /*
883 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
884 * Note ThisCache2 has a space prepended to the hostname so we don't
885 * accidentally match super-domains.
886 */
887
888 if (strListIsSubstr(&s, ThisCache2, ',')) {
889 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
890 request, (ObjPackMethod) & httpRequestPack);
891 request->flags.loopdetect = 1;
892 }
893
edce4d98 894#if FORW_VIA_DB
30abd221 895 fvdbCountVia(s.buf());
62e76326 896
edce4d98 897#endif
62e76326 898
30abd221 899 s.clean();
edce4d98 900 }
62e76326 901
26ac0430
AJ
902 /**
903 \todo --enable-useragent-log and --enable-referer-log. We should
904 probably drop those two as the custom log formats accomplish pretty much the same thing..
905 */
edce4d98 906#if USE_USERAGENT_LOG
a9925b40 907 if ((str = req_hdr->getStr(HDR_USER_AGENT)))
b4306cba 908 logUserAgent(fqdnFromAddr(http->getConn()->log_addr), str);
62e76326 909
edce4d98 910#endif
911#if USE_REFERER_LOG
62e76326 912
a9925b40 913 if ((str = req_hdr->getStr(HDR_REFERER)))
b4306cba 914 logReferer(fqdnFromAddr(http->getConn()->log_addr), str, http->log_uri);
62e76326 915
edce4d98 916#endif
917#if FORW_VIA_DB
62e76326 918
a9925b40 919 if (req_hdr->has(HDR_X_FORWARDED_FOR)) {
30abd221 920 String s = req_hdr->getList(HDR_X_FORWARDED_FOR);
921 fvdbCountForw(s.buf());
922 s.clean();
edce4d98 923 }
62e76326 924
edce4d98 925#endif
926 if (request->method == METHOD_TRACE) {
a9925b40 927 request->max_forwards = req_hdr->getInt(HDR_MAX_FORWARDS);
edce4d98 928 }
62e76326 929
610ee341 930 request->flags.cachable = http->request->cacheable();
62e76326 931
edce4d98 932 if (clientHierarchical(http))
62e76326 933 request->flags.hierarchical = 1;
934
bf8fe701 935 debugs(85, 5, "clientInterpretRequestHeaders: REQ_NOCACHE = " <<
936 (request->flags.nocache ? "SET" : "NOT SET"));
937 debugs(85, 5, "clientInterpretRequestHeaders: REQ_CACHABLE = " <<
938 (request->flags.cachable ? "SET" : "NOT SET"));
939 debugs(85, 5, "clientInterpretRequestHeaders: REQ_HIERARCHICAL = " <<
940 (request->flags.hierarchical ? "SET" : "NOT SET"));
62e76326 941
edce4d98 942}
943
944void
de31d06f 945clientRedirectDoneWrapper(void *data, char *result)
edce4d98 946{
de31d06f 947 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
db02222f 948
de31d06f 949 if (!calloutContext->httpStateIsValid())
62e76326 950 return;
62e76326 951
de31d06f 952 calloutContext->clientRedirectDone(result);
953}
954
955void
956ClientRequestContext::clientRedirectDone(char *result)
957{
190154cf 958 HttpRequest *new_request = NULL;
959 HttpRequest *old_request = http->request;
bf8fe701 960 debugs(85, 5, "clientRedirectDone: '" << http->uri << "' result=" << (result ? result : "NULL"));
de31d06f 961 assert(redirect_state == REDIRECT_PENDING);
962 redirect_state = REDIRECT_DONE;
62e76326 963
edce4d98 964 if (result) {
62e76326 965 http_status status = (http_status) atoi(result);
966
967 if (status == HTTP_MOVED_PERMANENTLY
968 || status == HTTP_MOVED_TEMPORARILY
969 || status == HTTP_SEE_OTHER
970 || status == HTTP_TEMPORARY_REDIRECT) {
971 char *t = result;
972
973 if ((t = strchr(result, ':')) != NULL) {
974 http->redirect.status = status;
975 http->redirect.location = xstrdup(t + 1);
976 } else {
bf8fe701 977 debugs(85, 1, "clientRedirectDone: bad input: " << result);
62e76326 978 }
2baf58c3 979 } else if (strcmp(result, http->uri))
c21ad0f5 980 new_request = HttpRequest::CreateFromUrlAndMethod(result, old_request->method);
edce4d98 981 }
62e76326 982
edce4d98 983 if (new_request) {
62e76326 984 safe_free(http->uri);
985 http->uri = xstrdup(urlCanonical(new_request));
986 new_request->http_ver = old_request->http_ver;
a9925b40 987 new_request->header.append(&old_request->header);
62e76326 988 new_request->client_addr = old_request->client_addr;
3d674977
AJ
989#if FOLLOW_X_FORWARDED_FOR
990 new_request->indirect_client_addr = old_request->indirect_client_addr;
991#endif /* FOLLOW_X_FORWARDED_FOR */
62e76326 992 new_request->my_addr = old_request->my_addr;
62e76326 993 new_request->flags = old_request->flags;
3c1f01bc 994 new_request->flags.redirected = 1;
62e76326 995
996 if (old_request->auth_user_request) {
997 new_request->auth_user_request = old_request->auth_user_request;
4f0ef8e8 998 AUTHUSERREQUESTLOCK(new_request->auth_user_request, "new request");
62e76326 999 }
1000
5f8252d2 1001 if (old_request->body_pipe != NULL) {
1002 new_request->body_pipe = old_request->body_pipe;
1003 old_request->body_pipe = NULL;
1004 debugs(0,0,HERE << "redirecting body_pipe " << new_request->body_pipe << " from request " << old_request << " to " << new_request);
62e76326 1005 }
1006
1007 new_request->content_length = old_request->content_length;
abb929f0 1008 new_request->extacl_user = old_request->extacl_user;
1009 new_request->extacl_passwd = old_request->extacl_passwd;
62e76326 1010 new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
6dd9f4bd 1011 HTTPMSGUNLOCK(old_request);
1012 http->request = HTTPMSGLOCK(new_request);
edce4d98 1013 }
62e76326 1014
edce4d98 1015 /* FIXME PIPELINE: This is innacurate during pipelining */
62e76326 1016
5f8252d2 1017 if (http->getConn() != NULL)
98242069 1018 fd_note(http->getConn()->fd, http->uri);
62e76326 1019
c8be6d7b 1020 assert(http->uri);
62e76326 1021
de31d06f 1022 http->doCallouts();
edce4d98 1023}
1024
1025void
8e2745f4 1026ClientRequestContext::checkNoCache()
edce4d98 1027{
de31d06f 1028 acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
57abaac0 1029 acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, this);
edce4d98 1030}
1031
de31d06f 1032static void
1033checkNoCacheDoneWrapper(int answer, void *data)
edce4d98 1034{
de31d06f 1035 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
e4a67a80 1036
de31d06f 1037 if (!calloutContext->httpStateIsValid())
1038 return;
1039
1040 calloutContext->checkNoCacheDone(answer);
8e2745f4 1041}
4fb35c3c 1042
8e2745f4 1043void
1044ClientRequestContext::checkNoCacheDone(int answer)
62e76326 1045{
8e2745f4 1046 acl_checklist = NULL;
de31d06f 1047 http->request->flags.cachable = answer;
1048 http->doCallouts();
edce4d98 1049}
1050
69660be0 1051/*
1052 * Identify requests that do not go through the store and client side stream
1053 * and forward them to the appropriate location. All other requests, request
1054 * them.
edce4d98 1055 */
1056void
8e2745f4 1057ClientHttpRequest::processRequest()
edce4d98 1058{
60745f24 1059 debugs(85, 4, "clientProcessRequest: " << RequestMethodStr(request->method) << " '" << uri << "'");
62e76326 1060
3712be3f 1061#if USE_SSL
1062 if (request->method == METHOD_CONNECT && sslBumpNeeded()) {
1063 sslBumpStart();
1064 return;
1065 }
1066#endif
1067
2baf58c3 1068 if (request->method == METHOD_CONNECT && !redirect.status) {
62e76326 1069 logType = LOG_TCP_MISS;
11007d4b 1070 tunnelStart(this, &out.size, &al.http.code);
62e76326 1071 return;
edce4d98 1072 }
62e76326 1073
8e2745f4 1074 httpStart();
1075}
1076
1077void
1078ClientHttpRequest::httpStart()
1079{
559da936 1080 PROF_start(httpStart);
8e2745f4 1081 logType = LOG_TAG_NONE;
bf8fe701 1082 debugs(85, 4, "ClientHttpRequest::httpStart: " << log_tags[logType] << " for '" << uri << "'");
1083
edce4d98 1084 /* no one should have touched this */
8e2745f4 1085 assert(out.offset == 0);
edce4d98 1086 /* Use the Stream Luke */
8e2745f4 1087 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
1088 clientStreamRead(node, this, node->readBuffer);
559da936 1089 PROF_stop(httpStart);
edce4d98 1090}
0655fa4d 1091
3712be3f 1092#if USE_SSL
1093
1094// determines whether we should bump the CONNECT request
1095bool
26ac0430
AJ
1096ClientHttpRequest::sslBumpNeeded() const
1097{
3712be3f 1098 if (!getConn()->port->sslBump || !Config.accessList.ssl_bump)
1099 return false;
1100
1101 debugs(85, 5, HERE << "SslBump possible, checking ACL");
1102
1103 ACLChecklist check;
1104 check.src_addr = request->client_addr;
26ac0430
AJ
1105 check.my_addr = request->my_addr;
1106 check.request = HTTPMSGLOCK(request);
1107 check.accessList = cbdataReference(Config.accessList.ssl_bump);
1108 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
1109 return check.fastCheck() == 1;
3712be3f 1110}
1111
1112// called when comm_write has completed
1113static void
1114SslBumpEstablish(int, char *, size_t, comm_err_t errflag, int, void *data)
1115{
1116 ClientHttpRequest *r = static_cast<ClientHttpRequest*>(data);
1117 debugs(85, 5, HERE << "responded to CONNECT: " << r << " ? " << errflag);
1118
1119 assert(r && cbdataReferenceValid(r));
1120 r->sslBumpEstablish(errflag);
1121}
1122
1123void
1124ClientHttpRequest::sslBumpEstablish(comm_err_t errflag)
1125{
1126 // Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up
1127 if (errflag == COMM_ERR_CLOSING)
1128 return;
1129
1130 if (errflag) {
1131 getConn()->startClosing("CONNECT response failure in SslBump");
1132 return;
1133 }
1134
1135 getConn()->switchToHttps();
1136}
1137
1138void
1139ClientHttpRequest::sslBumpStart()
1140{
1141 debugs(85, 5, HERE << "ClientHttpRequest::sslBumpStart");
1142
1143 // send an HTTP 200 response to kick client SSL negotiation
1144 const int fd = getConn()->fd;
1145 debugs(33, 7, HERE << "Confirming CONNECT tunnel on FD " << fd);
1146
1147 // TODO: Unify with tunnel.cc and add a Server(?) header
1148 static const char *const conn_established =
1149 "HTTP/1.0 200 Connection established\r\n\r\n";
1150 comm_write(fd, conn_established, strlen(conn_established),
26ac0430 1151 &SslBumpEstablish, this, NULL);
3712be3f 1152}
1153
1154#endif
1155
0655fa4d 1156bool
1157ClientHttpRequest::gotEnough() const
1158{
86a2f789 1159 /** TODO: should be querying the stream. */
7173d5b0 1160 int64_t contentLength =
06a5ae20 1161 memObject()->getReply()->bodySize(request->method);
0655fa4d 1162 assert(contentLength >= 0);
1163
1164 if (out.offset < contentLength)
1165 return false;
1166
1167 return true;
1168}
1169
86a2f789 1170void
1171ClientHttpRequest::storeEntry(StoreEntry *newEntry)
1172{
1173 entry_ = newEntry;
1174}
1175
0976f8db 1176void
1177ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
1178{
1179 if (loggingEntry_)
97b5e68f 1180 loggingEntry_->unlock();
0976f8db 1181
1182 loggingEntry_ = newEntry;
1183
1184 if (loggingEntry_)
3d0ac046 1185 loggingEntry_->lock();
0976f8db 1186}
86a2f789 1187
de31d06f 1188/*
1189 * doCallouts() - This function controls the order of "callout"
1190 * executions, including non-blocking access control checks, the
1191 * redirector, and ICAP. Previously, these callouts were chained
1192 * together such that "clientAccessCheckDone()" would call
1193 * "clientRedirectStart()" and so on.
1194 *
1195 * The ClientRequestContext (aka calloutContext) class holds certain
1196 * state data for the callout/callback operations. Previously
1197 * ClientHttpRequest would sort of hand off control to ClientRequestContext
1198 * for a short time. ClientRequestContext would then delete itself
1199 * and pass control back to ClientHttpRequest when all callouts
1200 * were finished.
1201 *
1202 * This caused some problems for ICAP because we want to make the
1203 * ICAP callout after checking ACLs, but before checking the no_cache
1204 * list. We can't stuff the ICAP state into the ClientRequestContext
1205 * class because we still need the ICAP state after ClientRequestContext
1206 * goes away.
1207 *
1208 * Note that ClientRequestContext is created before the first call
1209 * to doCallouts().
1210 *
1211 * If one of the callouts notices that ClientHttpRequest is no
1212 * longer valid, it should call cbdataReferenceDone() so that
1213 * ClientHttpRequest's reference count goes to zero and it will get
1214 * deleted. ClientHttpRequest will then delete ClientRequestContext.
1215 *
1216 * Note that we set the _done flags here before actually starting
1217 * the callout. This is strictly for convenience.
1218 */
1219
057f5854 1220extern int aclMapTOS (acl_tos * head, ACLChecklist * ch);
1221
de31d06f 1222void
1223ClientHttpRequest::doCallouts()
1224{
1225 assert(calloutContext);
1226
1227 if (!calloutContext->http_access_done) {
3712be3f 1228 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
57abaac0 1229 calloutContext->http_access_done = true;
de31d06f 1230 calloutContext->clientAccessCheck();
1231 return;
1232 }
1233
a83c6ed6 1234#if USE_ADAPTATION
abd4b611 1235 if (!calloutContext->adaptation_acl_check_done) {
a83c6ed6 1236 calloutContext->adaptation_acl_check_done = true;
abd4b611 1237 if (Adaptation::AccessCheck::Start(
26ac0430
AJ
1238 Adaptation::methodReqmod, Adaptation::pointPreCache,
1239 request, NULL, adaptationAclCheckDoneWrapper, calloutContext))
abd4b611 1240 return; // will call callback
de31d06f 1241 }
de31d06f 1242#endif
1243
1244 if (!calloutContext->redirect_done) {
57abaac0 1245 calloutContext->redirect_done = true;
de31d06f 1246 assert(calloutContext->redirect_state == REDIRECT_NONE);
1247
1248 if (Config.Program.redirect) {
3712be3f 1249 debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
de31d06f 1250 calloutContext->redirect_state = REDIRECT_PENDING;
1251 calloutContext->clientRedirectStart();
1252 return;
1253 }
1254 }
1255
57abaac0 1256 if (!calloutContext->interpreted_req_hdrs) {
3712be3f 1257 debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
57abaac0 1258 calloutContext->interpreted_req_hdrs = 1;
1259 clientInterpretRequestHeaders(this);
1260 }
1261
de31d06f 1262 if (!calloutContext->no_cache_done) {
57abaac0 1263 calloutContext->no_cache_done = true;
de31d06f 1264
1265 if (Config.accessList.noCache && request->flags.cachable) {
3712be3f 1266 debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
de31d06f 1267 calloutContext->checkNoCache();
1268 return;
1269 }
1270 }
1271
057f5854 1272 if (!calloutContext->clientside_tos_done) {
1273 calloutContext->clientside_tos_done = true;
3712be3f 1274 if (getConn() != NULL) {
1275 ACLChecklist ch;
057f5854 1276 ch.src_addr = request->client_addr;
1277 ch.my_addr = request->my_addr;
057f5854 1278 ch.request = HTTPMSGLOCK(request);
3712be3f 1279 int tos = aclMapTOS(Config.accessList.clientside_tos, &ch);
1280 if (tos)
1281 comm_set_tos(getConn()->fd, tos);
1282 }
057f5854 1283 }
1284
de31d06f 1285 cbdataReferenceDone(calloutContext->http);
1286 delete calloutContext;
1287 calloutContext = NULL;
de31d06f 1288#if HEADERS_LOG
1289
1290 headersLog(0, 1, request->method, request);
1291#endif
1292
58d7150d 1293 debugs(83, 3, HERE << "calling processRequest()");
de31d06f 1294 processRequest();
1295}
1296
86a2f789 1297#ifndef _USE_INLINE_
1298#include "client_side_request.cci"
1299#endif
de31d06f 1300
a83c6ed6 1301#if USE_ADAPTATION
de31d06f 1302/*
26ac0430 1303 * Initiate an ICAP transaction. Return false on errors.
5f8252d2 1304 * The caller must handle errors.
de31d06f 1305 */
5f8252d2 1306bool
a83c6ed6 1307ClientHttpRequest::startAdaptation(Adaptation::ServicePointer service)
3b299123 1308{
a83c6ed6 1309 debugs(85, 3, HERE << this << " ClientHttpRequest::startAdaptation() called");
5f8252d2 1310 if (!service) {
a83c6ed6 1311 debugs(85, 3, "ClientHttpRequest::startAdaptation fails: lack of service");
5f8252d2 1312 return false;
3b299123 1313 }
5f8252d2 1314 if (service->broken()) {
a83c6ed6 1315 debugs(85, 3, "ClientHttpRequest::startAdaptation fails: broken service");
5f8252d2 1316 return false;
3b299123 1317 }
1318
a83c6ed6
AR
1319 assert(!virginHeadSource);
1320 assert(!adaptedBodySource);
1321 virginHeadSource = initiateAdaptation(service->makeXactLauncher(
26ac0430 1322 this, request, NULL));
a83c6ed6
AR
1323
1324 return virginHeadSource != NULL;
de31d06f 1325}
1326
1327void
a83c6ed6 1328ClientHttpRequest::noteAdaptationAnswer(HttpMsg *msg)
de31d06f 1329{
de31d06f 1330 assert(cbdataReferenceValid(this)); // indicates bug
5f8252d2 1331 assert(msg);
1332
b044675d 1333 if (HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg)) {
200ac359 1334 /*
5f8252d2 1335 * Replace the old request with the new request.
200ac359 1336 */
6dd9f4bd 1337 HTTPMSGUNLOCK(request);
1338 request = HTTPMSGLOCK(new_req);
200ac359 1339 /*
1340 * Store the new URI for logging
1341 */
1342 xfree(uri);
1343 uri = xstrdup(urlCanonical(request));
1344 setLogUri(this, urlCanonicalClean(request));
914b89a2 1345 assert(request->method.id());
b044675d 1346 } else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
1347 debugs(85,3,HERE << "REQMOD reply is HTTP reply");
1348
5f8252d2 1349 // subscribe to receive reply body
1350 if (new_rep->body_pipe != NULL) {
a83c6ed6
AR
1351 adaptedBodySource = new_rep->body_pipe;
1352 assert(adaptedBodySource->setConsumerIfNotLate(this));
5f8252d2 1353 }
1354
b044675d 1355 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1356 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1357 repContext->createStoreEntry(request->method, request->flags);
1358
1359 EBIT_CLR(storeEntry()->flags, ENTRY_FWD_HDR_WAIT);
1360 request_satisfaction_mode = true;
1361 request_satisfaction_offset = 0;
1362 storeEntry()->replaceHttpReply(new_rep);
cb4c4288 1363
a83c6ed6 1364 if (!adaptedBodySource) // no body
cb4c4288 1365 storeEntry()->complete();
b044675d 1366 clientGetMoreData(node, this);
200ac359 1367 }
de31d06f 1368
5f8252d2 1369 // we are done with getting headers (but may be receiving body)
a83c6ed6 1370 clearAdaptation(virginHeadSource);
5f8252d2 1371
b044675d 1372 if (!request_satisfaction_mode)
1373 doCallouts();
de31d06f 1374}
1375
1376void
a83c6ed6 1377ClientHttpRequest::noteAdaptationQueryAbort(bool final)
de31d06f 1378{
a83c6ed6
AR
1379 clearAdaptation(virginHeadSource);
1380 assert(!adaptedBodySource);
1381 handleAdaptationFailure(!final);
de31d06f 1382}
1383
1384void
1cf238db 1385ClientHttpRequest::noteMoreBodyDataAvailable(BodyPipe::Pointer)
de31d06f 1386{
5f8252d2 1387 assert(request_satisfaction_mode);
a83c6ed6 1388 assert(adaptedBodySource != NULL);
5f8252d2 1389
a83c6ed6
AR
1390 if (const size_t contentSize = adaptedBodySource->buf().contentSize()) {
1391 BodyPipeCheckout bpc(*adaptedBodySource);
5f8252d2 1392 const StoreIOBuffer ioBuf(&bpc.buf, request_satisfaction_offset);
1393 storeEntry()->write(ioBuf);
1394 // assume can write everything
1395 request_satisfaction_offset += contentSize;
1396 bpc.buf.consume(contentSize);
1397 bpc.checkIn();
1398 }
1399
a83c6ed6 1400 if (adaptedBodySource->exhausted())
5f8252d2 1401 endRequestSatisfaction();
1402 // else wait for more body data
de31d06f 1403}
1404
1405void
1cf238db 1406ClientHttpRequest::noteBodyProductionEnded(BodyPipe::Pointer)
de31d06f 1407{
a83c6ed6
AR
1408 assert(!virginHeadSource);
1409 if (adaptedBodySource != NULL) { // did not end request satisfaction yet
26ac0430 1410 // We do not expect more because noteMoreBodyDataAvailable always
5f8252d2 1411 // consumes everything. We do not even have a mechanism to consume
1412 // leftovers after noteMoreBodyDataAvailable notifications seize.
a83c6ed6 1413 assert(adaptedBodySource->exhausted());
5f8252d2 1414 endRequestSatisfaction();
1415 }
1416}
3b299123 1417
5f8252d2 1418void
26ac0430
AJ
1419ClientHttpRequest::endRequestSatisfaction()
1420{
5f8252d2 1421 debugs(85,4, HERE << this << " ends request satisfaction");
1422 assert(request_satisfaction_mode);
a83c6ed6 1423 stopConsumingFrom(adaptedBodySource);
3b299123 1424
5f8252d2 1425 // TODO: anything else needed to end store entry formation correctly?
1426 storeEntry()->complete();
1427}
de31d06f 1428
5f8252d2 1429void
1cf238db 1430ClientHttpRequest::noteBodyProducerAborted(BodyPipe::Pointer)
5f8252d2 1431{
a83c6ed6
AR
1432 assert(!virginHeadSource);
1433 stopConsumingFrom(adaptedBodySource);
1434 handleAdaptationFailure();
5f8252d2 1435}
3b299123 1436
5f8252d2 1437void
a83c6ed6 1438ClientHttpRequest::handleAdaptationFailure(bool bypassable)
5f8252d2 1439{
a83c6ed6 1440 debugs(85,3, HERE << "handleAdaptationFailure(" << bypassable << ")");
3b299123 1441
5f8252d2 1442 const bool usedStore = storeEntry() && !storeEntry()->isEmpty();
1443 const bool usedPipe = request->body_pipe != NULL &&
26ac0430 1444 request->body_pipe->consumedSize() > 0;
3b299123 1445
9d4d7c5e 1446 if (bypassable && !usedStore && !usedPipe) {
1447 debugs(85,3, HERE << "ICAP REQMOD callout failed, bypassing: " << calloutContext);
5f8252d2 1448 if (calloutContext)
1449 doCallouts();
1450 return;
1451 }
3b299123 1452
5f8252d2 1453 debugs(85,3, HERE << "ICAP REQMOD callout failed, responding with error");
3b299123 1454
5f8252d2 1455 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1456 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1457 assert(repContext);
de31d06f 1458
26ac0430 1459 // The original author of the code also wanted to pass an errno to
5f8252d2 1460 // setReplyToError, but it seems unlikely that the errno reflects the
1461 // true cause of the error at this point, so I did not pass it.
ad61a2b4 1462 IpAddress noAddr;
b70ba605 1463 noAddr.SetNoAddr();
1cf238db 1464 ConnStateData * c = getConn();
5f8252d2 1465 repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
26ac0430
AJ
1466 request->method, NULL,
1467 (c != NULL ? c->peer : noAddr), request, NULL,
1468 (c != NULL && c->auth_user_request ?
1469 c->auth_user_request : request->auth_user_request));
de31d06f 1470
5f8252d2 1471 node = (clientStreamNode *)client_stream.tail->data;
1472 clientStreamRead(node, this, node->readBuffer);
de31d06f 1473}
1474
1475#endif