]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
HTTP 1.1: Forward OPTIONS requests properly
[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"
2d2b0bb7 48#include "auth/UserRequest.h"
528b2c61 49#include "HttpRequest.h"
3712be3f 50#include "ProtoPort.h"
c0941a6a
AR
51#include "acl/FilledChecklist.h"
52#include "acl/Gadgets.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;
bb790702 429 p = request->x_forwarded_for_iterator.termedBuf();
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
bb790702 900 fvdbCountVia(s.termedBuf());
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);
bb790702 926 fvdbCountForw(s.termedBuf());
30abd221 927 s.clean();
edce4d98 928 }
62e76326 929
edce4d98 930#endif
fc90edc3
AJ
931 if (request->method == METHOD_TRACE || request->method == METHOD_OPTIONS) {
932 request->max_forwards = req_hdr->getInt64(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
c0941a6a 1116 ACLFilledChecklist check(Config.accessList.ssl_bump, request, NULL);
3712be3f 1117 check.src_addr = request->client_addr;
26ac0430 1118 check.my_addr = request->my_addr;
26ac0430 1119 return check.fastCheck() == 1;
3712be3f 1120}
1121
1122// called when comm_write has completed
1123static void
1124SslBumpEstablish(int, char *, size_t, comm_err_t errflag, int, void *data)
1125{
1126 ClientHttpRequest *r = static_cast<ClientHttpRequest*>(data);
1127 debugs(85, 5, HERE << "responded to CONNECT: " << r << " ? " << errflag);
1128
1129 assert(r && cbdataReferenceValid(r));
1130 r->sslBumpEstablish(errflag);
1131}
1132
1133void
1134ClientHttpRequest::sslBumpEstablish(comm_err_t errflag)
1135{
1136 // Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up
1137 if (errflag == COMM_ERR_CLOSING)
1138 return;
1139
1140 if (errflag) {
1141 getConn()->startClosing("CONNECT response failure in SslBump");
1142 return;
1143 }
1144
1145 getConn()->switchToHttps();
1146}
1147
1148void
1149ClientHttpRequest::sslBumpStart()
1150{
1151 debugs(85, 5, HERE << "ClientHttpRequest::sslBumpStart");
1152
1153 // send an HTTP 200 response to kick client SSL negotiation
1154 const int fd = getConn()->fd;
1155 debugs(33, 7, HERE << "Confirming CONNECT tunnel on FD " << fd);
1156
1157 // TODO: Unify with tunnel.cc and add a Server(?) header
1158 static const char *const conn_established =
1159 "HTTP/1.0 200 Connection established\r\n\r\n";
1160 comm_write(fd, conn_established, strlen(conn_established),
26ac0430 1161 &SslBumpEstablish, this, NULL);
3712be3f 1162}
1163
1164#endif
1165
0655fa4d 1166bool
1167ClientHttpRequest::gotEnough() const
1168{
86a2f789 1169 /** TODO: should be querying the stream. */
7173d5b0 1170 int64_t contentLength =
06a5ae20 1171 memObject()->getReply()->bodySize(request->method);
0655fa4d 1172 assert(contentLength >= 0);
1173
1174 if (out.offset < contentLength)
1175 return false;
1176
1177 return true;
1178}
1179
86a2f789 1180void
1181ClientHttpRequest::storeEntry(StoreEntry *newEntry)
1182{
1183 entry_ = newEntry;
1184}
1185
0976f8db 1186void
1187ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
1188{
1189 if (loggingEntry_)
97b5e68f 1190 loggingEntry_->unlock();
0976f8db 1191
1192 loggingEntry_ = newEntry;
1193
1194 if (loggingEntry_)
3d0ac046 1195 loggingEntry_->lock();
0976f8db 1196}
86a2f789 1197
de31d06f 1198/*
1199 * doCallouts() - This function controls the order of "callout"
1200 * executions, including non-blocking access control checks, the
1201 * redirector, and ICAP. Previously, these callouts were chained
1202 * together such that "clientAccessCheckDone()" would call
1203 * "clientRedirectStart()" and so on.
1204 *
1205 * The ClientRequestContext (aka calloutContext) class holds certain
1206 * state data for the callout/callback operations. Previously
1207 * ClientHttpRequest would sort of hand off control to ClientRequestContext
1208 * for a short time. ClientRequestContext would then delete itself
1209 * and pass control back to ClientHttpRequest when all callouts
1210 * were finished.
1211 *
1212 * This caused some problems for ICAP because we want to make the
1213 * ICAP callout after checking ACLs, but before checking the no_cache
1214 * list. We can't stuff the ICAP state into the ClientRequestContext
1215 * class because we still need the ICAP state after ClientRequestContext
1216 * goes away.
1217 *
1218 * Note that ClientRequestContext is created before the first call
1219 * to doCallouts().
1220 *
1221 * If one of the callouts notices that ClientHttpRequest is no
1222 * longer valid, it should call cbdataReferenceDone() so that
1223 * ClientHttpRequest's reference count goes to zero and it will get
1224 * deleted. ClientHttpRequest will then delete ClientRequestContext.
1225 *
1226 * Note that we set the _done flags here before actually starting
1227 * the callout. This is strictly for convenience.
1228 */
1229
057f5854 1230extern int aclMapTOS (acl_tos * head, ACLChecklist * ch);
1231
de31d06f 1232void
1233ClientHttpRequest::doCallouts()
1234{
1235 assert(calloutContext);
1236
1237 if (!calloutContext->http_access_done) {
3712be3f 1238 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
57abaac0 1239 calloutContext->http_access_done = true;
de31d06f 1240 calloutContext->clientAccessCheck();
1241 return;
1242 }
1243
a83c6ed6 1244#if USE_ADAPTATION
abd4b611 1245 if (!calloutContext->adaptation_acl_check_done) {
a83c6ed6 1246 calloutContext->adaptation_acl_check_done = true;
abd4b611 1247 if (Adaptation::AccessCheck::Start(
26ac0430
AJ
1248 Adaptation::methodReqmod, Adaptation::pointPreCache,
1249 request, NULL, adaptationAclCheckDoneWrapper, calloutContext))
abd4b611 1250 return; // will call callback
de31d06f 1251 }
de31d06f 1252#endif
1253
1254 if (!calloutContext->redirect_done) {
57abaac0 1255 calloutContext->redirect_done = true;
de31d06f 1256 assert(calloutContext->redirect_state == REDIRECT_NONE);
1257
1258 if (Config.Program.redirect) {
3712be3f 1259 debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
de31d06f 1260 calloutContext->redirect_state = REDIRECT_PENDING;
1261 calloutContext->clientRedirectStart();
1262 return;
1263 }
1264 }
1265
57abaac0 1266 if (!calloutContext->interpreted_req_hdrs) {
3712be3f 1267 debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
57abaac0 1268 calloutContext->interpreted_req_hdrs = 1;
1269 clientInterpretRequestHeaders(this);
1270 }
1271
de31d06f 1272 if (!calloutContext->no_cache_done) {
57abaac0 1273 calloutContext->no_cache_done = true;
de31d06f 1274
1275 if (Config.accessList.noCache && request->flags.cachable) {
3712be3f 1276 debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
de31d06f 1277 calloutContext->checkNoCache();
1278 return;
1279 }
1280 }
1281
057f5854 1282 if (!calloutContext->clientside_tos_done) {
1283 calloutContext->clientside_tos_done = true;
3712be3f 1284 if (getConn() != NULL) {
c0941a6a 1285 ACLFilledChecklist ch(NULL, request, NULL);
057f5854 1286 ch.src_addr = request->client_addr;
1287 ch.my_addr = request->my_addr;
3712be3f 1288 int tos = aclMapTOS(Config.accessList.clientside_tos, &ch);
1289 if (tos)
1290 comm_set_tos(getConn()->fd, tos);
1291 }
057f5854 1292 }
1293
de31d06f 1294 cbdataReferenceDone(calloutContext->http);
1295 delete calloutContext;
1296 calloutContext = NULL;
de31d06f 1297#if HEADERS_LOG
1298
1299 headersLog(0, 1, request->method, request);
1300#endif
1301
58d7150d 1302 debugs(83, 3, HERE << "calling processRequest()");
de31d06f 1303 processRequest();
1304}
1305
86a2f789 1306#ifndef _USE_INLINE_
1307#include "client_side_request.cci"
1308#endif
de31d06f 1309
a83c6ed6 1310#if USE_ADAPTATION
de31d06f 1311/*
26ac0430 1312 * Initiate an ICAP transaction. Return false on errors.
5f8252d2 1313 * The caller must handle errors.
de31d06f 1314 */
5f8252d2 1315bool
a83c6ed6 1316ClientHttpRequest::startAdaptation(Adaptation::ServicePointer service)
3b299123 1317{
a83c6ed6 1318 debugs(85, 3, HERE << this << " ClientHttpRequest::startAdaptation() called");
5f8252d2 1319 if (!service) {
a83c6ed6 1320 debugs(85, 3, "ClientHttpRequest::startAdaptation fails: lack of service");
5f8252d2 1321 return false;
3b299123 1322 }
5f8252d2 1323 if (service->broken()) {
a83c6ed6 1324 debugs(85, 3, "ClientHttpRequest::startAdaptation fails: broken service");
5f8252d2 1325 return false;
3b299123 1326 }
1327
a83c6ed6
AR
1328 assert(!virginHeadSource);
1329 assert(!adaptedBodySource);
1330 virginHeadSource = initiateAdaptation(service->makeXactLauncher(
26ac0430 1331 this, request, NULL));
a83c6ed6
AR
1332
1333 return virginHeadSource != NULL;
de31d06f 1334}
1335
1336void
a83c6ed6 1337ClientHttpRequest::noteAdaptationAnswer(HttpMsg *msg)
de31d06f 1338{
de31d06f 1339 assert(cbdataReferenceValid(this)); // indicates bug
5f8252d2 1340 assert(msg);
1341
b044675d 1342 if (HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg)) {
200ac359 1343 /*
5f8252d2 1344 * Replace the old request with the new request.
200ac359 1345 */
6dd9f4bd 1346 HTTPMSGUNLOCK(request);
1347 request = HTTPMSGLOCK(new_req);
200ac359 1348 /*
1349 * Store the new URI for logging
1350 */
1351 xfree(uri);
1352 uri = xstrdup(urlCanonical(request));
1353 setLogUri(this, urlCanonicalClean(request));
914b89a2 1354 assert(request->method.id());
b044675d 1355 } else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
1356 debugs(85,3,HERE << "REQMOD reply is HTTP reply");
1357
5f8252d2 1358 // subscribe to receive reply body
1359 if (new_rep->body_pipe != NULL) {
a83c6ed6
AR
1360 adaptedBodySource = new_rep->body_pipe;
1361 assert(adaptedBodySource->setConsumerIfNotLate(this));
5f8252d2 1362 }
1363
b044675d 1364 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1365 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1366 repContext->createStoreEntry(request->method, request->flags);
1367
1368 EBIT_CLR(storeEntry()->flags, ENTRY_FWD_HDR_WAIT);
1369 request_satisfaction_mode = true;
1370 request_satisfaction_offset = 0;
1371 storeEntry()->replaceHttpReply(new_rep);
cb4c4288 1372
a83c6ed6 1373 if (!adaptedBodySource) // no body
cb4c4288 1374 storeEntry()->complete();
b044675d 1375 clientGetMoreData(node, this);
200ac359 1376 }
de31d06f 1377
5f8252d2 1378 // we are done with getting headers (but may be receiving body)
a83c6ed6 1379 clearAdaptation(virginHeadSource);
5f8252d2 1380
b044675d 1381 if (!request_satisfaction_mode)
1382 doCallouts();
de31d06f 1383}
1384
1385void
a83c6ed6 1386ClientHttpRequest::noteAdaptationQueryAbort(bool final)
de31d06f 1387{
a83c6ed6
AR
1388 clearAdaptation(virginHeadSource);
1389 assert(!adaptedBodySource);
1390 handleAdaptationFailure(!final);
de31d06f 1391}
1392
1393void
1cf238db 1394ClientHttpRequest::noteMoreBodyDataAvailable(BodyPipe::Pointer)
de31d06f 1395{
5f8252d2 1396 assert(request_satisfaction_mode);
a83c6ed6 1397 assert(adaptedBodySource != NULL);
5f8252d2 1398
4ce0e99b 1399 if (const size_t contentSize = adaptedBodySource->buf().contentSize()) {
a83c6ed6 1400 BodyPipeCheckout bpc(*adaptedBodySource);
4ce0e99b 1401 const StoreIOBuffer ioBuf(&bpc.buf, request_satisfaction_offset);
5f8252d2 1402 storeEntry()->write(ioBuf);
1403 // assume can write everything
1404 request_satisfaction_offset += contentSize;
4ce0e99b 1405 bpc.buf.consume(contentSize);
5f8252d2 1406 bpc.checkIn();
1407 }
1408
a83c6ed6 1409 if (adaptedBodySource->exhausted())
5f8252d2 1410 endRequestSatisfaction();
1411 // else wait for more body data
de31d06f 1412}
1413
1414void
1cf238db 1415ClientHttpRequest::noteBodyProductionEnded(BodyPipe::Pointer)
de31d06f 1416{
a83c6ed6
AR
1417 assert(!virginHeadSource);
1418 if (adaptedBodySource != NULL) { // did not end request satisfaction yet
26ac0430 1419 // We do not expect more because noteMoreBodyDataAvailable always
5f8252d2 1420 // consumes everything. We do not even have a mechanism to consume
1421 // leftovers after noteMoreBodyDataAvailable notifications seize.
a83c6ed6 1422 assert(adaptedBodySource->exhausted());
5f8252d2 1423 endRequestSatisfaction();
1424 }
1425}
3b299123 1426
5f8252d2 1427void
26ac0430
AJ
1428ClientHttpRequest::endRequestSatisfaction()
1429{
5f8252d2 1430 debugs(85,4, HERE << this << " ends request satisfaction");
1431 assert(request_satisfaction_mode);
a83c6ed6 1432 stopConsumingFrom(adaptedBodySource);
3b299123 1433
5f8252d2 1434 // TODO: anything else needed to end store entry formation correctly?
1435 storeEntry()->complete();
1436}
de31d06f 1437
5f8252d2 1438void
1cf238db 1439ClientHttpRequest::noteBodyProducerAborted(BodyPipe::Pointer)
5f8252d2 1440{
a83c6ed6
AR
1441 assert(!virginHeadSource);
1442 stopConsumingFrom(adaptedBodySource);
1443 handleAdaptationFailure();
5f8252d2 1444}
3b299123 1445
5f8252d2 1446void
a83c6ed6 1447ClientHttpRequest::handleAdaptationFailure(bool bypassable)
5f8252d2 1448{
a83c6ed6 1449 debugs(85,3, HERE << "handleAdaptationFailure(" << bypassable << ")");
3b299123 1450
5f8252d2 1451 const bool usedStore = storeEntry() && !storeEntry()->isEmpty();
1452 const bool usedPipe = request->body_pipe != NULL &&
26ac0430 1453 request->body_pipe->consumedSize() > 0;
3b299123 1454
9d4d7c5e 1455 if (bypassable && !usedStore && !usedPipe) {
1456 debugs(85,3, HERE << "ICAP REQMOD callout failed, bypassing: " << calloutContext);
5f8252d2 1457 if (calloutContext)
1458 doCallouts();
1459 return;
1460 }
3b299123 1461
5f8252d2 1462 debugs(85,3, HERE << "ICAP REQMOD callout failed, responding with error");
3b299123 1463
5f8252d2 1464 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1465 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1466 assert(repContext);
de31d06f 1467
26ac0430 1468 // The original author of the code also wanted to pass an errno to
5f8252d2 1469 // setReplyToError, but it seems unlikely that the errno reflects the
1470 // true cause of the error at this point, so I did not pass it.
ad61a2b4 1471 IpAddress noAddr;
b70ba605 1472 noAddr.SetNoAddr();
1cf238db 1473 ConnStateData * c = getConn();
5f8252d2 1474 repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
26ac0430
AJ
1475 request->method, NULL,
1476 (c != NULL ? c->peer : noAddr), request, NULL,
1477 (c != NULL && c->auth_user_request ?
1478 c->auth_user_request : request->auth_user_request));
de31d06f 1479
5f8252d2 1480 node = (clientStreamNode *)client_stream.tail->data;
1481 clientStreamRead(node, this, node->readBuffer);
de31d06f 1482}
1483
1484#endif