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