]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
ACL Optimization: inline currentAnswer() methods to reduce copying
[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
f7f3304a 45#include "squid-old.h"
c0941a6a
AR
46#include "acl/FilledChecklist.h"
47#include "acl/Gadgets.h"
a83c6ed6 48#if USE_ADAPTATION
62c7f90e 49#include "adaptation/AccessCheck.h"
1adcebc3 50#include "adaptation/Answer.h"
a22e6cd3 51#include "adaptation/Iterator.h"
abd4b611 52#include "adaptation/Service.h"
3ff65596
AR
53#if ICAP_CLIENT
54#include "adaptation/icap/History.h"
55#endif
de31d06f 56#endif
2f1431ea 57#if USE_AUTH
27bc2077 58#include "auth/UserRequest.h"
2f1431ea 59#endif
27bc2077
AJ
60#include "clientStream.h"
61#include "client_side.h"
62#include "client_side_reply.h"
63#include "client_side_request.h"
64#include "ClientRequestContext.h"
5c336a3b 65#include "comm/Connection.h"
ec41b64c 66#include "comm/Write.h"
27bc2077 67#include "compat/inet_pton.h"
2bd84e5f 68#include "errorpage.h"
27bc2077 69#include "fde.h"
31971e6a 70#include "format/Token.h"
ce394734 71#include "HttpHdrCc.h"
27bc2077
AJ
72#include "HttpReply.h"
73#include "HttpRequest.h"
425de4c8 74#include "ip/QosConfig.h"
27bc2077
AJ
75#include "MemObject.h"
76#include "ProtoPort.h"
77#include "Store.h"
78#include "SquidTime.h"
79#include "wordlist.h"
64b66b76 80#include "err_detail_type.h"
4db984be
CT
81#if USE_SSL
82#include "ssl/support.h"
2bd84e5f 83#include "ssl/ServerBump.h"
4db984be 84#endif
3ff65596
AR
85
86
edce4d98 87#if LINGERING_CLOSE
88#define comm_close comm_lingering_close
89#endif
90
91static const char *const crlf = "\r\n";
92
609c620a 93#if FOLLOW_X_FORWARDED_FOR
2efeb0b7 94static void clientFollowXForwardedForCheck(allow_t answer, void *data);
609c620a 95#endif /* FOLLOW_X_FORWARDED_FOR */
3d674977 96
2bd84e5f
CT
97extern ErrorState *clientBuildError(err_type, http_status, char const *url, Ip::Address &, HttpRequest *);
98
8e2745f4 99CBDATA_CLASS_INIT(ClientRequestContext);
100
101void *
102ClientRequestContext::operator new (size_t size)
103{
104 assert (size == sizeof(ClientRequestContext));
105 CBDATA_INIT_TYPE(ClientRequestContext);
106 ClientRequestContext *result = cbdataAlloc(ClientRequestContext);
aa625860 107 return result;
8e2745f4 108}
62e76326 109
8e2745f4 110void
111ClientRequestContext::operator delete (void *address)
112{
113 ClientRequestContext *t = static_cast<ClientRequestContext *>(address);
aa625860 114 cbdataFree(t);
8e2745f4 115}
116
edce4d98 117/* Local functions */
edce4d98 118/* other */
2efeb0b7 119static void clientAccessCheckDoneWrapper(allow_t, void *);
e0c0d54c 120#if USE_SSL
2efeb0b7 121static void sslBumpAccessCheckDoneWrapper(allow_t, void *);
e0c0d54c 122#endif
59a1efb2 123static int clientHierarchical(ClientHttpRequest * http);
124static void clientInterpretRequestHeaders(ClientHttpRequest * http);
de31d06f 125static RH clientRedirectDoneWrapper;
2efeb0b7 126static void checkNoCacheDoneWrapper(allow_t, void *);
e6ccf245 127extern "C" CSR clientGetMoreData;
128extern "C" CSS clientReplyStatus;
129extern "C" CSD clientReplyDetach;
528b2c61 130static void checkFailureRatio(err_type, hier_code);
edce4d98 131
8e2745f4 132ClientRequestContext::~ClientRequestContext()
133{
de31d06f 134 /*
a546b04b 135 * Release our "lock" on our parent, ClientHttpRequest, if we
136 * still have one
de31d06f 137 */
a546b04b 138
139 if (http)
140 cbdataReferenceDone(http);
62e76326 141
2bd84e5f 142 delete error;
5f8252d2 143 debugs(85,3, HERE << this << " ClientRequestContext destructed");
8e2745f4 144}
145
2bd84e5f 146ClientRequestContext::ClientRequestContext(ClientHttpRequest *anHttp) : http(cbdataReference(anHttp)), acl_checklist (NULL), redirect_state (REDIRECT_NONE), error(NULL), readNextRequest(false)
edce4d98 147{
57abaac0 148 http_access_done = false;
149 redirect_done = false;
150 no_cache_done = false;
151 interpreted_req_hdrs = false;
e0c0d54c
CT
152#if USE_SSL
153 sslBumpCheckDone = false;
154#endif
0b86805b 155 debugs(85,3, HERE << this << " ClientRequestContext constructed");
edce4d98 156}
157
528b2c61 158CBDATA_CLASS_INIT(ClientHttpRequest);
8e2745f4 159
528b2c61 160void *
161ClientHttpRequest::operator new (size_t size)
162{
163 assert (size == sizeof (ClientHttpRequest));
164 CBDATA_INIT_TYPE(ClientHttpRequest);
165 ClientHttpRequest *result = cbdataAlloc(ClientHttpRequest);
aa625860 166 return result;
528b2c61 167}
168
62e76326 169void
528b2c61 170ClientHttpRequest::operator delete (void *address)
171{
aa625860 172 ClientHttpRequest *t = static_cast<ClientHttpRequest *>(address);
173 cbdataFree(t);
528b2c61 174}
175
26ac0430 176ClientHttpRequest::ClientHttpRequest(ConnStateData * aConn) :
a83c6ed6 177#if USE_ADAPTATION
26ac0430 178 AsyncJob("ClientHttpRequest"),
1cf238db 179#endif
26ac0430 180 loggingEntry_(NULL)
528b2c61 181{
1cf238db 182 start_time = current_time;
a0355e95 183 setConn(aConn);
a14f38d0 184 al.tcpClient = clientConnection = aConn->clientConnection;
a0355e95 185 dlinkAdd(this, &active, &ClientActiveRequests);
a83c6ed6 186#if USE_ADAPTATION
b044675d 187 request_satisfaction_mode = false;
188#endif
e0c0d54c
CT
189#if USE_SSL
190 sslBumpNeed = needUnknown;
191#endif
528b2c61 192}
193
0655fa4d 194/*
195 * returns true if client specified that the object must come from the cache
196 * without contacting origin server
197 */
198bool
199ClientHttpRequest::onlyIfCached()const
200{
201 assert(request);
202 return request->cache_control &&
4ce6e3b5 203 request->cache_control->onlyIfCached();
0655fa4d 204}
205
528b2c61 206/*
207 * This function is designed to serve a fairly specific purpose.
208 * Occasionally our vBNS-connected caches can talk to each other, but not
209 * the rest of the world. Here we try to detect frequent failures which
210 * make the cache unusable (e.g. DNS lookup and connect() failures). If
211 * the failure:success ratio goes above 1.0 then we go into "hit only"
212 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
213 * will only fetch HITs from us if they are using the ICP protocol. We
214 * stay in this mode for 5 minutes.
26ac0430 215 *
528b2c61 216 * Duane W., Sept 16, 1996
217 */
218
219#define FAILURE_MODE_TIME 300
220
221static void
222checkFailureRatio(err_type etype, hier_code hcode)
223{
224 static double magic_factor = 100.0;
225 double n_good;
226 double n_bad;
62e76326 227
528b2c61 228 if (hcode == HIER_NONE)
62e76326 229 return;
230
528b2c61 231 n_good = magic_factor / (1.0 + request_failure_ratio);
62e76326 232
528b2c61 233 n_bad = magic_factor - n_good;
62e76326 234
528b2c61 235 switch (etype) {
62e76326 236
528b2c61 237 case ERR_DNS_FAIL:
62e76326 238
528b2c61 239 case ERR_CONNECT_FAIL:
3712be3f 240 case ERR_SECURE_CONNECT_FAIL:
62e76326 241
528b2c61 242 case ERR_READ_ERROR:
62e76326 243 n_bad++;
244 break;
245
528b2c61 246 default:
62e76326 247 n_good++;
528b2c61 248 }
62e76326 249
528b2c61 250 request_failure_ratio = n_bad / n_good;
62e76326 251
528b2c61 252 if (hit_only_mode_until > squid_curtime)
62e76326 253 return;
254
528b2c61 255 if (request_failure_ratio < 1.0)
62e76326 256 return;
257
bf8fe701 258 debugs(33, 0, "Failure Ratio at "<< std::setw(4)<<
259 std::setprecision(3) << request_failure_ratio);
62e76326 260
bf8fe701 261 debugs(33, 0, "Going into hit-only-mode for " <<
262 FAILURE_MODE_TIME / 60 << " minutes...");
62e76326 263
528b2c61 264 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
62e76326 265
528b2c61 266 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
267}
268
269ClientHttpRequest::~ClientHttpRequest()
270{
bf8fe701 271 debugs(33, 3, "httpRequestFree: " << uri);
72bdee4c 272 PROF_start(httpRequestFree);
62e76326 273
5f8252d2 274 // Even though freeResources() below may destroy the request,
275 // we no longer set request->body_pipe to NULL here
276 // because we did not initiate that pipe (ConnStateData did)
62e76326 277
528b2c61 278 /* the ICP check here was erroneous
26ac0430 279 * - StoreEntry::releaseRequest was always called if entry was valid
528b2c61 280 */
281 assert(logType < LOG_TYPE_MAX);
9ce7856a 282
528b2c61 283 logRequest();
9ce7856a 284
0976f8db 285 loggingEntry(NULL);
286
528b2c61 287 if (request)
62e76326 288 checkFailureRatio(request->errType, al.hier.code);
289
528b2c61 290 freeResources();
62e76326 291
a83c6ed6
AR
292#if USE_ADAPTATION
293 announceInitiatorAbort(virginHeadSource);
9d4d7c5e 294
a83c6ed6
AR
295 if (adaptedBodySource != NULL)
296 stopConsumingFrom(adaptedBodySource);
de31d06f 297#endif
9ce7856a 298
de31d06f 299 if (calloutContext)
300 delete calloutContext;
301
be364179
AJ
302 clientConnection = NULL;
303
26ac0430
AJ
304 if (conn_)
305 cbdataReferenceDone(conn_);
1cf238db 306
528b2c61 307 /* moving to the next connection is handled by the context free */
308 dlinkDelete(&active, &ClientActiveRequests);
9ce7856a 309
72bdee4c 310 PROF_stop(httpRequestFree);
528b2c61 311}
62e76326 312
11992b6f
AJ
313/**
314 * Create a request and kick it off
315 *
316 * \retval 0 success
317 * \retval -1 failure
318 *
69660be0 319 * TODO: Pass in the buffers to be used in the inital Read request, as they are
320 * determined by the user
edce4d98 321 */
11992b6f 322int
60745f24 323clientBeginRequest(const HttpRequestMethod& method, char const *url, CSCB * streamcallback,
0655fa4d 324 CSD * streamdetach, ClientStreamData streamdata, HttpHeader const *header,
62e76326 325 char *tailbuf, size_t taillen)
edce4d98 326{
327 size_t url_sz;
a0355e95 328 ClientHttpRequest *http = new ClientHttpRequest(NULL);
190154cf 329 HttpRequest *request;
528b2c61 330 StoreIOBuffer tempBuffer;
1cf238db 331 http->start_time = current_time;
edce4d98 332 /* this is only used to adjust the connection offset in client_side.c */
333 http->req_sz = 0;
c8be6d7b 334 tempBuffer.length = taillen;
335 tempBuffer.data = tailbuf;
edce4d98 336 /* client stream setup */
337 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 338 clientReplyStatus, new clientReplyContext(http), streamcallback,
62e76326 339 streamdetach, streamdata, tempBuffer);
edce4d98 340 /* make it visible in the 'current acctive requests list' */
edce4d98 341 /* Set flags */
a46d2c0e 342 /* internal requests only makes sense in an
343 * accelerator today. TODO: accept flags ? */
344 http->flags.accel = 1;
edce4d98 345 /* allow size for url rewriting */
346 url_sz = strlen(url) + Config.appendDomainLen + 5;
e6ccf245 347 http->uri = (char *)xcalloc(url_sz, 1);
edce4d98 348 strcpy(http->uri, url);
349
c21ad0f5 350 if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
bf8fe701 351 debugs(85, 5, "Invalid URL: " << http->uri);
62e76326 352 return -1;
edce4d98 353 }
62e76326 354
69660be0 355 /*
11992b6f 356 * now update the headers in request with our supplied headers. urlParse
69660be0 357 * should return a blank header set, but we use Update to be sure of
358 * correctness.
edce4d98 359 */
360 if (header)
a9925b40 361 request->header.update(header, NULL);
62e76326 362
edce4d98 363 http->log_uri = xstrdup(urlCanonicalClean(request));
62e76326 364
edce4d98 365 /* http struct now ready */
366
69660be0 367 /*
368 * build new header list *? TODO
edce4d98 369 */
370 request->flags.accelerated = http->flags.accel;
62e76326 371
a46d2c0e 372 request->flags.internalclient = 1;
373
374 /* this is an internally created
375 * request, not subject to acceleration
376 * target overrides */
69660be0 377 /*
378 * FIXME? Do we want to detect and handle internal requests of internal
379 * objects ?
380 */
edce4d98 381
382 /* Internally created requests cannot have bodies today */
383 request->content_length = 0;
62e76326 384
cc192b50 385 request->client_addr.SetNoAddr();
62e76326 386
3d674977
AJ
387#if FOLLOW_X_FORWARDED_FOR
388 request->indirect_client_addr.SetNoAddr();
389#endif /* FOLLOW_X_FORWARDED_FOR */
26ac0430 390
cc192b50 391 request->my_addr.SetNoAddr(); /* undefined for internal requests */
62e76326 392
cc192b50 393 request->my_addr.SetPort(0);
62e76326 394
3872be7c
AJ
395 /* Our version is HTTP/1.1 */
396 HttpVersion http_ver(1,1);
edce4d98 397 request->http_ver = http_ver;
62e76326 398
6dd9f4bd 399 http->request = HTTPMSGLOCK(request);
edce4d98 400
401 /* optional - skip the access check ? */
de31d06f 402 http->calloutContext = new ClientRequestContext(http);
403
57abaac0 404 http->calloutContext->http_access_done = false;
de31d06f 405
57abaac0 406 http->calloutContext->redirect_done = true;
de31d06f 407
57abaac0 408 http->calloutContext->no_cache_done = true;
de31d06f 409
410 http->doCallouts();
62e76326 411
edce4d98 412 return 0;
413}
414
de31d06f 415bool
416ClientRequestContext::httpStateIsValid()
417{
418 ClientHttpRequest *http_ = http;
419
420 if (cbdataReferenceValid(http_))
421 return true;
422
423 http = NULL;
424
425 cbdataReferenceDone(http_);
426
427 return false;
428}
429
3d674977
AJ
430#if FOLLOW_X_FORWARDED_FOR
431/**
a9044668 432 * clientFollowXForwardedForCheck() checks the content of X-Forwarded-For:
3d674977
AJ
433 * against the followXFF ACL, or cleans up and passes control to
434 * clientAccessCheck().
d096ace1
AJ
435 *
436 * The trust model here is a little ambiguous. So to clarify the logic:
437 * - we may always use the direct client address as the client IP.
a9044668 438 * - these trust tests merey tell whether we trust given IP enough to believe the
d096ace1
AJ
439 * IP string which it appended to the X-Forwarded-For: header.
440 * - if at any point we don't trust what an IP adds we stop looking.
441 * - at that point the current contents of indirect_client_addr are the value set
442 * by the last previously trusted IP.
443 * ++ indirect_client_addr contains the remote direct client from the trusted peers viewpoint.
3d674977 444 */
3d674977 445static void
2efeb0b7 446clientFollowXForwardedForCheck(allow_t answer, void *data)
3d674977
AJ
447{
448 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
3d674977
AJ
449
450 if (!calloutContext->httpStateIsValid())
451 return;
452
d096ace1
AJ
453 ClientHttpRequest *http = calloutContext->http;
454 HttpRequest *request = http->request;
455
3d674977
AJ
456 /*
457 * answer should be be ACCESS_ALLOWED or ACCESS_DENIED if we are
458 * called as a result of ACL checks, or -1 if we are called when
459 * there's nothing left to do.
460 */
461 if (answer == ACCESS_ALLOWED &&
26ac0430 462 request->x_forwarded_for_iterator.size () != 0) {
d096ace1 463
3d674977 464 /*
d096ace1
AJ
465 * Remove the last comma-delimited element from the
466 * x_forwarded_for_iterator and use it to repeat the cycle.
467 */
3d674977
AJ
468 const char *p;
469 const char *asciiaddr;
470 int l;
b7ac5457 471 Ip::Address addr;
bb790702 472 p = request->x_forwarded_for_iterator.termedBuf();
3d674977
AJ
473 l = request->x_forwarded_for_iterator.size();
474
475 /*
476 * XXX x_forwarded_for_iterator should really be a list of
477 * IP addresses, but it's a String instead. We have to
478 * walk backwards through the String, biting off the last
479 * comma-delimited part each time. As long as the data is in
480 * a String, we should probably implement and use a variant of
481 * strListGetItem() that walks backwards instead of forwards
482 * through a comma-separated list. But we don't even do that;
483 * we just do the work in-line here.
484 */
485 /* skip trailing space and commas */
486 while (l > 0 && (p[l-1] == ',' || xisspace(p[l-1])))
487 l--;
488 request->x_forwarded_for_iterator.cut(l);
489 /* look for start of last item in list */
490 while (l > 0 && ! (p[l-1] == ',' || xisspace(p[l-1])))
491 l--;
492 asciiaddr = p+l;
fafd0efa 493 if ((addr = asciiaddr)) {
3d674977
AJ
494 request->indirect_client_addr = addr;
495 request->x_forwarded_for_iterator.cut(l);
d096ace1
AJ
496 calloutContext->acl_checklist = clientAclChecklistCreate(Config.accessList.followXFF, http);
497 if (!Config.onoff.acl_uses_indirect_client) {
498 /* override the default src_addr tested if we have to go deeper than one level into XFF */
499 Filled(calloutContext->acl_checklist)->src_addr = request->indirect_client_addr;
3d674977 500 }
d096ace1 501 calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
3d674977
AJ
502 return;
503 }
504 } /*if (answer == ACCESS_ALLOWED &&
505 request->x_forwarded_for_iterator.size () != 0)*/
506
507 /* clean up, and pass control to clientAccessCheck */
26ac0430 508 if (Config.onoff.log_uses_indirect_client) {
3d674977
AJ
509 /*
510 * Ensure that the access log shows the indirect client
511 * instead of the direct client.
512 */
513 ConnStateData *conn = http->getConn();
514 conn->log_addr = request->indirect_client_addr;
515 }
516 request->x_forwarded_for_iterator.clean();
517 request->flags.done_follow_x_forwarded_for = 1;
518
d096ace1
AJ
519 if (answer != ACCESS_ALLOWED && answer != ACCESS_DENIED) {
520 debugs(28, DBG_CRITICAL, "ERROR: Processing X-Forwarded-For. Stopping at IP address: " << request->indirect_client_addr );
493d3865
AJ
521 }
522
523 /* process actual access ACL as normal. */
524 calloutContext->clientAccessCheck();
3d674977
AJ
525}
526#endif /* FOLLOW_X_FORWARDED_FOR */
527
fe97983f
AJ
528static void
529hostHeaderIpVerifyWrapper(const ipcache_addrs* ia, const DnsLookupDetails &dns, void *data)
530{
531 ClientRequestContext *c = static_cast<ClientRequestContext*>(data);
532 c->hostHeaderIpVerify(ia, dns);
533}
534
535void
536ClientRequestContext::hostHeaderIpVerify(const ipcache_addrs* ia, const DnsLookupDetails &dns)
537{
538 Comm::ConnectionPointer clientConn = http->getConn()->clientConnection;
539
540 // note the DNS details for the transaction stats.
541 http->request->recordLookup(dns);
542
543 if (ia != NULL && ia->count > 0) {
544 // Is the NAT destination IP in DNS?
545 for (int i = 0; i < ia->count; i++) {
546 if (clientConn->local.matchIPAddr(ia->in_addrs[i]) == 0) {
547 debugs(85, 3, HERE << "validate IP " << clientConn->local << " possible from Host:");
548 http->doCallouts();
549 return;
550 }
551 debugs(85, 3, HERE << "validate IP " << clientConn->local << " non-match from Host: IP " << ia->in_addrs[i]);
552 }
553 }
554 debugs(85, 3, HERE << "FAIL: validate IP " << clientConn->local << " possible from Host:");
05b28f84 555 hostHeaderVerifyFailed("local IP", "any domain IP");
fe97983f
AJ
556}
557
558void
05b28f84 559ClientRequestContext::hostHeaderVerifyFailed(const char *A, const char *B)
fe97983f 560{
2962f8b8
AJ
561 // IP address validation for Host: failed. Admin wants to ignore them.
562 // NP: we do not yet handle CONNECT tunnels well, so ignore for them
563 if (!Config.onoff.hostStrictVerify && http->request->method != METHOD_CONNECT) {
564 debugs(85, 3, "SECURITY ALERT: Host header forgery detected on " << http->getConn()->clientConnection <<
565 " (" << A << " does not match " << B << ") on URL: " << urlCanonical(http->request));
566
567 // NP: it is tempting to use 'flags.nocache' but that is all about READing cache data.
568 // The problems here are about WRITE for new cache content, which means flags.cachable
569 http->request->flags.cachable = 0; // MUST NOT cache (for now)
570 // XXX: when we have updated the cache key to base on raw-IP + URI this cacheable limit can go.
571 http->request->flags.hierarchical = 0; // MUST NOT pass to peers (for now)
572 // XXX: when we have sorted out the best way to relay requests properly to peers this hierarchical limit can go.
573 return;
574 }
575
8f489ad7
AJ
576 debugs(85, DBG_IMPORTANT, "SECURITY ALERT: Host header forgery detected on " <<
577 http->getConn()->clientConnection << " (" << A << " does not match " << B << ")");
578 debugs(85, DBG_IMPORTANT, "SECURITY ALERT: By user agent: " << http->request->header.getStr(HDR_USER_AGENT));
579 debugs(85, DBG_IMPORTANT, "SECURITY ALERT: on URL: " << urlCanonical(http->request));
fe97983f
AJ
580
581 // IP address validation for Host: failed. reject the connection.
582 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
583 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
584 assert (repContext);
2962f8b8 585 repContext->setReplyToError(ERR_CONFLICT_HOST, HTTP_CONFLICT,
fe97983f
AJ
586 http->request->method, NULL,
587 http->getConn()->clientConnection->remote,
588 http->request,
589 NULL,
590#if USE_AUTH
591 http->getConn() != NULL && http->getConn()->auth_user_request != NULL ?
592 http->getConn()->auth_user_request : http->request->auth_user_request);
593#else
594 NULL);
595#endif
596 node = (clientStreamNode *)http->client_stream.tail->data;
597 clientStreamRead(node, http, node->readBuffer);
598}
599
600void
601ClientRequestContext::hostHeaderVerify()
602{
603 // Require a Host: header.
604 const char *host = http->request->header.getStr(HDR_HOST);
fe97983f
AJ
605
606 if (!host) {
607 // TODO: dump out the HTTP/1.1 error about missing host header.
608 // otherwise this is fine, can't forge a header value when its not even set.
609 debugs(85, 3, HERE << "validate skipped with no Host: header present.");
610 http->doCallouts();
611 return;
612 }
613
8f489ad7
AJ
614 if (http->request->flags.internal) {
615 // TODO: kill this when URL handling allows partial URLs out of accel mode
616 // and we no longer screw with the URL just to add our internal host there
617 debugs(85, 6, HERE << "validate skipped due to internal composite URL.");
618 http->doCallouts();
619 return;
620 }
621
fe97983f
AJ
622 // Locate if there is a port attached, strip ready for IP lookup
623 char *portStr = NULL;
91663dce
AJ
624 char *hostB = xstrdup(host);
625 host = hostB;
fe97983f
AJ
626 if (host[0] == '[') {
627 // IPv6 literal.
fe97983f 628 portStr = strchr(hostB, ']');
91663dce
AJ
629 if (portStr && *(++portStr) != ':') {
630 portStr = NULL;
fe97983f 631 }
91663dce 632 } else {
fe97983f 633 // Domain or IPv4 literal with port
fe97983f 634 portStr = strrchr(hostB, ':');
91663dce
AJ
635 }
636
637 uint16_t port = 0;
638 if (portStr) {
639 *portStr = '\0'; // strip the ':'
640 if (*(++portStr) != '\0')
641 port = xatoi(portStr);
fe97983f
AJ
642 }
643
644 debugs(85, 3, HERE << "validate host=" << host << ", port=" << port << ", portStr=" << (portStr?portStr:"NULL"));
645 if (http->request->flags.intercepted || http->request->flags.spoof_client_ip) {
ba4d9da0 646 // verify the Host: port (if any) matches the apparent destination
fe97983f 647 if (portStr && port != http->getConn()->clientConnection->local.GetPort()) {
05b28f84
AJ
648 debugs(85, 3, HERE << "FAIL on validate port " << http->getConn()->clientConnection->local.GetPort() <<
649 " matches Host: port " << port << " (" << portStr << ")");
650 hostHeaderVerifyFailed("intercepted port", portStr);
ba4d9da0
AJ
651 } else {
652 // XXX: match the scheme default port against the apparent destination
fe97983f 653
ba4d9da0
AJ
654 // verify the destination DNS is one of the Host: headers IPs
655 ipcache_nbgethostbyname(host, hostHeaderIpVerifyWrapper, this);
656 }
06059513 657 } else if (!Config.onoff.hostStrictVerify) {
90529125
AJ
658 debugs(85, 3, HERE << "validate skipped.");
659 http->doCallouts();
8f489ad7
AJ
660 } else if (strlen(host) != strlen(http->request->GetHost())) {
661 // Verify forward-proxy requested URL domain matches the Host: header
662 debugs(85, 3, HERE << "FAIL on validate URL domain length " << http->request->GetHost() << " matches Host: " << host);
663 hostHeaderVerifyFailed(host, http->request->GetHost());
664 } else if (matchDomainName(host, http->request->GetHost()) != 0) {
ba4d9da0 665 // Verify forward-proxy requested URL domain matches the Host: header
fe97983f 666 debugs(85, 3, HERE << "FAIL on validate URL domain " << http->request->GetHost() << " matches Host: " << host);
05b28f84 667 hostHeaderVerifyFailed(host, http->request->GetHost());
ba4d9da0
AJ
668 } else if (portStr && port != http->request->port) {
669 // Verify forward-proxy requested URL domain matches the Host: header
670 debugs(85, 3, HERE << "FAIL on validate URL port " << http->request->port << " matches Host: port " << portStr);
671 hostHeaderVerifyFailed("URL port", portStr);
65d2fdbf 672 } else if (!portStr && http->request->method != METHOD_CONNECT && http->request->port != urlDefaultPort(http->request->protocol)) {
ba4d9da0 673 // Verify forward-proxy requested URL domain matches the Host: header
65d2fdbf 674 // Special case: we don't have a default-port to check for CONNECT. Assume URL is correct.
ba4d9da0
AJ
675 debugs(85, 3, HERE << "FAIL on validate URL port " << http->request->port << " matches Host: default port " << urlDefaultPort(http->request->protocol));
676 hostHeaderVerifyFailed("URL port", "default port");
677 } else {
678 // Okay no problem.
679 debugs(85, 3, HERE << "validate passed.");
2962f8b8 680 http->request->flags.hostVerified = 1;
ba4d9da0 681 http->doCallouts();
fe97983f 682 }
ba4d9da0 683 safe_free(hostB);
fe97983f
AJ
684}
685
edce4d98 686/* This is the entry point for external users of the client_side routines */
687void
de31d06f 688ClientRequestContext::clientAccessCheck()
edce4d98 689{
3d674977
AJ
690#if FOLLOW_X_FORWARDED_FOR
691 if (!http->request->flags.done_follow_x_forwarded_for &&
26ac0430
AJ
692 Config.accessList.followXFF &&
693 http->request->header.has(HDR_X_FORWARDED_FOR)) {
d096ace1
AJ
694
695 /* we always trust the direct client address for actual use */
696 http->request->indirect_client_addr = http->request->client_addr;
fafd0efa 697 http->request->indirect_client_addr.SetPort(0);
d096ace1
AJ
698
699 /* setup the XFF iterator for processing */
700 http->request->x_forwarded_for_iterator = http->request->header.getList(HDR_X_FORWARDED_FOR);
701
702 /* begin by checking to see if we trust direct client enough to walk XFF */
703 acl_checklist = clientAclChecklistCreate(Config.accessList.followXFF, http);
704 acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, this);
3d674977
AJ
705 return;
706 }
707#endif /* FOLLOW_X_FORWARDED_FOR */
493d3865 708
b50e327b
AJ
709 if (Config.accessList.http) {
710 acl_checklist = clientAclChecklistCreate(Config.accessList.http, http);
711 acl_checklist->nonBlockingCheck(clientAccessCheckDoneWrapper, this);
712 } else {
713 debugs(0, DBG_CRITICAL, "No http_access configuration found. This will block ALL traffic");
714 clientAccessCheckDone(ACCESS_DENIED);
715 }
edce4d98 716}
717
533493da
AJ
718/**
719 * Identical in operation to clientAccessCheck() but performed later using different configured ACL list.
720 * The default here is to allow all. Since the earlier http_access should do a default deny all.
721 * This check is just for a last-minute denial based on adapted request headers.
722 */
723void
724ClientRequestContext::clientAccessCheck2()
725{
726 if (Config.accessList.adapted_http) {
727 acl_checklist = clientAclChecklistCreate(Config.accessList.adapted_http, http);
728 acl_checklist->nonBlockingCheck(clientAccessCheckDoneWrapper, this);
729 } else {
327e2131 730 debugs(85, 2, HERE << "No adapted_http_access configuration. default: ALLOW");
533493da
AJ
731 clientAccessCheckDone(ACCESS_ALLOWED);
732 }
733}
734
edce4d98 735void
2efeb0b7 736clientAccessCheckDoneWrapper(allow_t answer, void *data)
edce4d98 737{
de31d06f 738 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
fbade053 739
de31d06f 740 if (!calloutContext->httpStateIsValid())
62e76326 741 return;
62e76326 742
de31d06f 743 calloutContext->clientAccessCheckDone(answer);
744}
745
ed6163ef
AJ
746bool
747ClientRequestContext::maybeSendAuthChallenge(const allow_t &answer)
de31d06f 748{
749 acl_checklist = NULL;
edce4d98 750 err_type page_id;
751 http_status status;
f5691f9c 752
2f1431ea
AJ
753#if USE_AUTH
754 char const *proxy_auth_msg = "<null>";
94a396a3 755 if (http->getConn() != NULL && http->getConn()->auth_user_request != NULL)
f5691f9c 756 proxy_auth_msg = http->getConn()->auth_user_request->denyMessage("<null>");
757 else if (http->request->auth_user_request != NULL)
758 proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
2f1431ea 759#endif
62e76326 760
ed6163ef
AJ
761 bool auth_challenge = false;
762 switch (answer) {
763 case ACCESS_ALLOWED:
764 case ACCESS_AUTH_EXPIRED_OK:
765 // No authentication challenge on these ACL results
766 return auth_challenge;
767
768 case ACCESS_DENIED:
769 case ACCESS_DUNNO:
770 // MAYBE challenge on these ACL results
771 auth_challenge |= aclIsProxyAuth(AclMatchedName);
772 break;
773
774 case ACCESS_AUTH_REQUIRED:
775 case ACCESS_AUTH_EXPIRED_BAD:
776 // Send an auth challenge or error
777 auth_challenge = true;
778 }
309347ef 779
ed6163ef
AJ
780 // auth has a grace period where credentials can be expired but okay not to challenge.
781 debugs(85, 5, "Access Denied: " << http->uri);
782 debugs(85, 5, "AclMatchedName = " << (AclMatchedName ? AclMatchedName : "<null>"));
2f1431ea 783#if USE_AUTH
ed6163ef
AJ
784 if (auth_challenge)
785 debugs(33, 5, "Proxy Auth Message = " << (proxy_auth_msg ? proxy_auth_msg : "<null>"));
2f1431ea 786#endif
9ce7856a 787
ed6163ef
AJ
788 /*
789 * NOTE: get page_id here, based on AclMatchedName because if
790 * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
791 * the clientCreateStoreEntry() call just below. Pedro Ribeiro
792 * <pribeiro@isel.pt>
793 */
794 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, auth_challenge);
9ce7856a 795
ed6163ef 796 http->logType = LOG_TCP_DENIED;
62e76326 797
ed6163ef 798 if (auth_challenge) {
2f1431ea 799#if USE_AUTH
ed6163ef
AJ
800 if (http->request->flags.sslBumped) {
801 /*SSL Bumped request, authentication is not possible*/
2f1431ea 802 status = HTTP_FORBIDDEN;
ed6163ef
AJ
803 } else if (!http->flags.accel) {
804 /* Proxy authorisation needed */
805 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
62e76326 806 } else {
ed6163ef
AJ
807 /* WWW authorisation needed */
808 status = HTTP_UNAUTHORIZED;
62e76326 809 }
ed6163ef
AJ
810#else
811 // need auth, but not possible to do.
812 status = HTTP_FORBIDDEN;
813#endif
814 if (page_id == ERR_NONE)
815 page_id = ERR_CACHE_ACCESS_DENIED;
816 } else {
817 status = HTTP_FORBIDDEN;
818
819 if (page_id == ERR_NONE)
820 page_id = ERR_ACCESS_DENIED;
821 }
62e76326 822
ed6163ef
AJ
823 Ip::Address tmpnoaddr;
824 tmpnoaddr.SetNoAddr();
2bd84e5f
CT
825 error = clientBuildError(page_id, status,
826 NULL,
827 http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmpnoaddr,
828 http->request
829 );
830
831 error->auth_user_request =
832 http->getConn() != NULL && http->getConn()->auth_user_request != NULL ?
833 http->getConn()->auth_user_request : http->request->auth_user_request;
834
835 readNextRequest = true;
ed6163ef
AJ
836 return true;
837}
838
839void
840ClientRequestContext::clientAccessCheckDone(const allow_t &answer)
841{
842 debugs(85, 2, "The request " <<
843 RequestMethodStr(http->request->method) << " " <<
844 http->uri << " is " << answer <<
845 ", because it matched '" <<
846 (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" );
847
2bd84e5f
CT
848 maybeSendAuthChallenge(answer);
849
309347ef 850 /* ACCESS_ALLOWED (or auth in grace period ACCESS_AUTH_EXPIRED_OK) continues here ... */
de31d06f 851 safe_free(http->uri);
852
853 http->uri = xstrdup(urlCanonical(http->request));
854
855 http->doCallouts();
856}
857
a83c6ed6 858#if USE_ADAPTATION
de31d06f 859void
79628299 860ClientHttpRequest::noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer g)
de31d06f 861{
a83c6ed6 862 debugs(93,3,HERE << this << " adaptationAclCheckDone called");
6ec67de9 863
e1381638 864#if ICAP_CLIENT
79628299 865 Adaptation::Icap::History::Pointer ih = request->icapHistory();
e1381638 866 if (ih != NULL) {
79628299
CT
867 if (getConn() != NULL) {
868 ih->rfc931 = getConn()->clientConnection->rfc931;
e1381638 869#if USE_SSL
79628299
CT
870 assert(getConn()->clientConnection != NULL);
871 ih->ssluser = sslGetUserEmail(fd_table[getConn()->clientConnection->fd].ssl);
e1381638 872#endif
3ff65596 873 }
79628299
CT
874 ih->log_uri = log_uri;
875 ih->req_sz = req_sz;
3ff65596
AR
876 }
877#endif
878
a22e6cd3
AR
879 if (!g) {
880 debugs(85,3, HERE << "no adaptation needed");
79628299 881 doCallouts();
5f8252d2 882 return;
883 }
de31d06f 884
79628299 885 startAdaptation(g);
edce4d98 886}
887
de31d06f 888#endif
889
14cc8559 890static void
2efeb0b7 891clientRedirectAccessCheckDone(allow_t answer, void *data)
14cc8559 892{
893 ClientRequestContext *context = (ClientRequestContext *)data;
ed6163ef 894
2bd84e5f 895 if (!context->maybeSendAuthChallenge(answer) && answer == ACCESS_ALLOWED)
ed6163ef 896 redirectStart(context->http, clientRedirectDoneWrapper, context);
14cc8559 897 else
de31d06f 898 context->clientRedirectDone(NULL);
14cc8559 899}
900
de31d06f 901void
902ClientRequestContext::clientRedirectStart()
14cc8559 903{
48e7baac 904 debugs(33, 5, HERE << "'" << http->uri << "'");
14cc8559 905
14cc8559 906 if (Config.accessList.redirector) {
de31d06f 907 acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
908 acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
14cc8559 909 } else
de31d06f 910 redirectStart(http, clientRedirectDoneWrapper, this);
14cc8559 911}
912
edce4d98 913static int
59a1efb2 914clientHierarchical(ClientHttpRequest * http)
edce4d98 915{
916 const char *url = http->uri;
190154cf 917 HttpRequest *request = http->request;
60745f24 918 HttpRequestMethod method = request->method;
edce4d98 919 const wordlist *p = NULL;
920
2962f8b8
AJ
921 // intercepted requests MUST NOT (yet) be sent to peers unless verified
922 if (!request->flags.hostVerified && (request->flags.intercepted || request->flags.spoof_client_ip))
923 return 0;
924
69660be0 925 /*
926 * IMS needs a private key, so we can use the hierarchy for IMS only if our
927 * neighbors support private keys
928 */
62e76326 929
edce4d98 930 if (request->flags.ims && !neighbors_do_private_keys)
62e76326 931 return 0;
932
69660be0 933 /*
934 * This is incorrect: authenticating requests can be sent via a hierarchy
06b97e72 935 * (they can even be cached if the correct headers are set on the reply)
edce4d98 936 */
937 if (request->flags.auth)
62e76326 938 return 0;
939
edce4d98 940 if (method == METHOD_TRACE)
62e76326 941 return 1;
942
edce4d98 943 if (method != METHOD_GET)
62e76326 944 return 0;
945
edce4d98 946 /* scan hierarchy_stoplist */
947 for (p = Config.hierarchy_stoplist; p; p = p->next)
62e76326 948 if (strstr(url, p->key))
949 return 0;
950
edce4d98 951 if (request->flags.loopdetect)
62e76326 952 return 0;
953
0c3d3f65 954 if (request->protocol == AnyP::PROTO_HTTP)
62e76326 955 return httpCachable(method);
956
0c3d3f65 957 if (request->protocol == AnyP::PROTO_GOPHER)
62e76326 958 return gopherCachable(request);
959
39a19cb7 960 if (request->protocol == AnyP::PROTO_CACHE_OBJECT)
62e76326 961 return 0;
962
edce4d98 963 return 1;
964}
965
46a1f562
HN
966static void
967clientCheckPinning(ClientHttpRequest * http)
968{
969 HttpRequest *request = http->request;
970 HttpHeader *req_hdr = &request->header;
971 ConnStateData *http_conn = http->getConn();
972
973 /* Internal requests such as those from ESI includes may be without
974 * a client connection
975 */
976 if (!http_conn)
f54f527e 977 return;
46a1f562
HN
978
979 request->flags.connection_auth_disabled = http_conn->port->connection_auth_disabled;
980 if (!request->flags.connection_auth_disabled) {
73c36fd9 981 if (Comm::IsConnOpen(http_conn->pinning.serverConnection)) {
46a1f562
HN
982 if (http_conn->pinning.auth) {
983 request->flags.connection_auth = 1;
984 request->flags.auth = 1;
985 } else {
986 request->flags.connection_proxy_auth = 1;
987 }
b1cf2350
AJ
988 // These should already be linked correctly.
989 assert(request->clientConnectionManager == http_conn);
46a1f562
HN
990 }
991 }
992
993 /* check if connection auth is used, and flag as candidate for pinning
994 * in such case.
995 * Note: we may need to set flags.connection_auth even if the connection
996 * is already pinned if it was pinned earlier due to proxy auth
997 */
998 if (!request->flags.connection_auth) {
999 if (req_hdr->has(HDR_AUTHORIZATION) || req_hdr->has(HDR_PROXY_AUTHORIZATION)) {
1000 HttpHeaderPos pos = HttpHeaderInitPos;
1001 HttpHeaderEntry *e;
1002 int may_pin = 0;
1003 while ((e = req_hdr->getEntry(&pos))) {
1004 if (e->id == HDR_AUTHORIZATION || e->id == HDR_PROXY_AUTHORIZATION) {
1005 const char *value = e->value.rawBuf();
1006 if (strncasecmp(value, "NTLM ", 5) == 0
1007 ||
1008 strncasecmp(value, "Negotiate ", 10) == 0
1009 ||
1010 strncasecmp(value, "Kerberos ", 9) == 0) {
1011 if (e->id == HDR_AUTHORIZATION) {
1012 request->flags.connection_auth = 1;
1013 may_pin = 1;
1014 } else {
1015 request->flags.connection_proxy_auth = 1;
1016 may_pin = 1;
1017 }
1018 }
1019 }
1020 }
1021 if (may_pin && !request->pinnedConnection()) {
b1cf2350
AJ
1022 // These should already be linked correctly. Just need the ServerConnection to pinn.
1023 assert(request->clientConnectionManager == http_conn);
46a1f562
HN
1024 }
1025 }
1026 }
1027}
1028
edce4d98 1029static void
59a1efb2 1030clientInterpretRequestHeaders(ClientHttpRequest * http)
edce4d98 1031{
190154cf 1032 HttpRequest *request = http->request;
0ef77270 1033 HttpHeader *req_hdr = &request->header;
edce4d98 1034 int no_cache = 0;
edce4d98 1035 const char *str;
62e76326 1036
edce4d98 1037 request->imslen = -1;
a9925b40 1038 request->ims = req_hdr->getTime(HDR_IF_MODIFIED_SINCE);
62e76326 1039
edce4d98 1040 if (request->ims > 0)
62e76326 1041 request->flags.ims = 1;
1042
432bc83c
HN
1043 if (!request->flags.ignore_cc) {
1044 if (req_hdr->has(HDR_PRAGMA)) {
1045 String s = req_hdr->getList(HDR_PRAGMA);
62e76326 1046
432bc83c
HN
1047 if (strListIsMember(&s, "no-cache", ','))
1048 no_cache++;
30abd221 1049
432bc83c
HN
1050 s.clean();
1051 }
62e76326 1052
432bc83c 1053 if (request->cache_control)
4ce6e3b5 1054 if (request->cache_control->noCache())
432bc83c 1055 no_cache++;
62e76326 1056
432bc83c
HN
1057 /*
1058 * Work around for supporting the Reload button in IE browsers when Squid
1059 * is used as an accelerator or transparent proxy, by turning accelerated
1060 * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
1061 * actually only fixed in SP1, but we can't tell whether we are talking to
1062 * SP1 or not so all 5.5 versions are treated 'normally').
1063 */
1064 if (Config.onoff.ie_refresh) {
1065 if (http->flags.accel && request->flags.ims) {
1066 if ((str = req_hdr->getStr(HDR_USER_AGENT))) {
1067 if (strstr(str, "MSIE 5.01") != NULL)
1068 no_cache++;
1069 else if (strstr(str, "MSIE 5.0") != NULL)
1070 no_cache++;
1071 else if (strstr(str, "MSIE 4.") != NULL)
1072 no_cache++;
1073 else if (strstr(str, "MSIE 3.") != NULL)
1074 no_cache++;
1075 }
62e76326 1076 }
1077 }
edce4d98 1078 }
914b89a2 1079
1080 if (request->method == METHOD_OTHER) {
26ac0430 1081 no_cache++;
60745f24 1082 }
62e76326 1083
edce4d98 1084 if (no_cache) {
626096be 1085#if USE_HTTP_VIOLATIONS
62e76326 1086
1087 if (Config.onoff.reload_into_ims)
1088 request->flags.nocache_hack = 1;
1089 else if (refresh_nocache_hack)
1090 request->flags.nocache_hack = 1;
1091 else
edce4d98 1092#endif
62e76326 1093
1094 request->flags.nocache = 1;
edce4d98 1095 }
62e76326 1096
0ef77270 1097 /* ignore range header in non-GETs or non-HEADs */
1098 if (request->method == METHOD_GET || request->method == METHOD_HEAD) {
56713d9a
AR
1099 // XXX: initialize if we got here without HttpRequest::parseHeader()
1100 if (!request->range)
1101 request->range = req_hdr->getRange();
62e76326 1102
1103 if (request->range) {
1104 request->flags.range = 1;
1105 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
1106 /* XXX: This is suboptimal. We should give the stream the range set,
1107 * and thereby let the top of the stream set the offset when the
26ac0430 1108 * size becomes known. As it is, we will end up requesting from 0
62e76326 1109 * for evey -X range specification.
1110 * RBC - this may be somewhat wrong. We should probably set the range
1111 * iter up at this point.
1112 */
1113 node->readBuffer.offset = request->range->lowestOffset(0);
1114 http->range_iter.pos = request->range->begin();
1115 http->range_iter.valid = true;
1116 }
edce4d98 1117 }
62e76326 1118
0ef77270 1119 /* Only HEAD and GET requests permit a Range or Request-Range header.
1120 * If these headers appear on any other type of request, delete them now.
1121 */
1122 else {
1123 req_hdr->delById(HDR_RANGE);
1124 req_hdr->delById(HDR_REQUEST_RANGE);
56713d9a 1125 delete request->range;
0ef77270 1126 request->range = NULL;
1127 }
1128
a9925b40 1129 if (req_hdr->has(HDR_AUTHORIZATION))
62e76326 1130 request->flags.auth = 1;
1131
46a1f562 1132 clientCheckPinning(http);
d67acb4e 1133
edce4d98 1134 if (request->login[0] != '\0')
62e76326 1135 request->flags.auth = 1;
1136
a9925b40 1137 if (req_hdr->has(HDR_VIA)) {
30abd221 1138 String s = req_hdr->getList(HDR_VIA);
62e76326 1139 /*
3c4fcf0f 1140 * ThisCache cannot be a member of Via header, "1.1 ThisCache" can.
62e76326 1141 * Note ThisCache2 has a space prepended to the hostname so we don't
1142 * accidentally match super-domains.
1143 */
1144
1145 if (strListIsSubstr(&s, ThisCache2, ',')) {
1146 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
1147 request, (ObjPackMethod) & httpRequestPack);
1148 request->flags.loopdetect = 1;
1149 }
1150
21f6708d 1151#if USE_FORW_VIA_DB
bb790702 1152 fvdbCountVia(s.termedBuf());
62e76326 1153
edce4d98 1154#endif
62e76326 1155
30abd221 1156 s.clean();
edce4d98 1157 }
62e76326 1158
21f6708d 1159#if USE_FORW_VIA_DB
62e76326 1160
a9925b40 1161 if (req_hdr->has(HDR_X_FORWARDED_FOR)) {
30abd221 1162 String s = req_hdr->getList(HDR_X_FORWARDED_FOR);
bb790702 1163 fvdbCountForw(s.termedBuf());
30abd221 1164 s.clean();
edce4d98 1165 }
62e76326 1166
edce4d98 1167#endif
62e76326 1168
610ee341 1169 request->flags.cachable = http->request->cacheable();
62e76326 1170
edce4d98 1171 if (clientHierarchical(http))
62e76326 1172 request->flags.hierarchical = 1;
1173
bf8fe701 1174 debugs(85, 5, "clientInterpretRequestHeaders: REQ_NOCACHE = " <<
1175 (request->flags.nocache ? "SET" : "NOT SET"));
1176 debugs(85, 5, "clientInterpretRequestHeaders: REQ_CACHABLE = " <<
1177 (request->flags.cachable ? "SET" : "NOT SET"));
1178 debugs(85, 5, "clientInterpretRequestHeaders: REQ_HIERARCHICAL = " <<
1179 (request->flags.hierarchical ? "SET" : "NOT SET"));
62e76326 1180
edce4d98 1181}
1182
1183void
de31d06f 1184clientRedirectDoneWrapper(void *data, char *result)
edce4d98 1185{
de31d06f 1186 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
db02222f 1187
de31d06f 1188 if (!calloutContext->httpStateIsValid())
62e76326 1189 return;
62e76326 1190
de31d06f 1191 calloutContext->clientRedirectDone(result);
1192}
1193
1194void
1195ClientRequestContext::clientRedirectDone(char *result)
1196{
190154cf 1197 HttpRequest *old_request = http->request;
bf8fe701 1198 debugs(85, 5, "clientRedirectDone: '" << http->uri << "' result=" << (result ? result : "NULL"));
de31d06f 1199 assert(redirect_state == REDIRECT_PENDING);
1200 redirect_state = REDIRECT_DONE;
62e76326 1201
edce4d98 1202 if (result) {
62e76326 1203 http_status status = (http_status) atoi(result);
1204
1205 if (status == HTTP_MOVED_PERMANENTLY
1206 || status == HTTP_MOVED_TEMPORARILY
1207 || status == HTTP_SEE_OTHER
1208 || status == HTTP_TEMPORARY_REDIRECT) {
1209 char *t = result;
1210
1211 if ((t = strchr(result, ':')) != NULL) {
1212 http->redirect.status = status;
1213 http->redirect.location = xstrdup(t + 1);
e5b677f0 1214 // TODO: validate the URL produced here is RFC 2616 compliant absolute URI
62e76326 1215 } else {
e5b677f0
AJ
1216 if (old_request->http_ver < HttpVersion(1,1))
1217 debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid 302 redirect Location: " << result);
1218 else
1219 debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid 303 redirect Location: " << result);
62e76326 1220 }
e5b677f0 1221 } else if (strcmp(result, http->uri)) {
9be14530
AJ
1222 // XXX: validate the URL properly *without* generating a whole new request object right here.
1223 // XXX: the clone() should be done only AFTER we know the new URL is valid.
1224 HttpRequest *new_request = old_request->clone();
1225 if (urlParse(old_request->method, result, new_request)) {
1226 debugs(61,2, HERE << "URL-rewriter diverts URL from " << urlCanonical(old_request) << " to " << urlCanonical(new_request));
1227
1228 // update the new request to flag the re-writing was done on it
1229 new_request->flags.redirected = 1;
1230
1231 // unlink bodypipe from the old request. Not needed there any longer.
1232 if (old_request->body_pipe != NULL) {
1233 old_request->body_pipe = NULL;
1234 debugs(61,2, HERE << "URL-rewriter diverts body_pipe " << new_request->body_pipe <<
1235 " from request " << old_request << " to " << new_request);
1236 }
1237
1238 // update the current working ClientHttpRequest fields
1239 safe_free(http->uri);
1240 http->uri = xstrdup(urlCanonical(new_request));
1241 HTTPMSGUNLOCK(old_request);
1242 http->request = HTTPMSGLOCK(new_request);
1243 } else {
e5b677f0 1244 debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid request: " <<
c9fd01b4 1245 old_request->method << " " << result << " " << old_request->http_ver);
9be14530
AJ
1246 delete new_request;
1247 }
74b48915 1248 }
edce4d98 1249 }
62e76326 1250
edce4d98 1251 /* FIXME PIPELINE: This is innacurate during pipelining */
62e76326 1252
73c36fd9
AJ
1253 if (http->getConn() != NULL && Comm::IsConnOpen(http->getConn()->clientConnection))
1254 fd_note(http->getConn()->clientConnection->fd, http->uri);
62e76326 1255
c8be6d7b 1256 assert(http->uri);
62e76326 1257
de31d06f 1258 http->doCallouts();
edce4d98 1259}
1260
b50e327b
AJ
1261/** Test cache allow/deny configuration
1262 * Sets flags.cachable=1 if caching is not denied.
1263 */
edce4d98 1264void
8e2745f4 1265ClientRequestContext::checkNoCache()
edce4d98 1266{
b50e327b
AJ
1267 if (Config.accessList.noCache) {
1268 acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
1269 acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, this);
1270 } else {
1271 /* unless otherwise specified, we try to cache. */
2efeb0b7 1272 checkNoCacheDone(ACCESS_ALLOWED);
b50e327b 1273 }
edce4d98 1274}
1275
de31d06f 1276static void
2efeb0b7 1277checkNoCacheDoneWrapper(allow_t answer, void *data)
edce4d98 1278{
de31d06f 1279 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
e4a67a80 1280
de31d06f 1281 if (!calloutContext->httpStateIsValid())
1282 return;
1283
1284 calloutContext->checkNoCacheDone(answer);
8e2745f4 1285}
4fb35c3c 1286
8e2745f4 1287void
2efeb0b7 1288ClientRequestContext::checkNoCacheDone(const allow_t &answer)
62e76326 1289{
8e2745f4 1290 acl_checklist = NULL;
2efeb0b7 1291 http->request->flags.cachable = (answer == ACCESS_ALLOWED);
de31d06f 1292 http->doCallouts();
edce4d98 1293}
1294
e0c0d54c
CT
1295#if USE_SSL
1296bool
1297ClientRequestContext::sslBumpAccessCheck()
1298{
1299 if (http->request->method == METHOD_CONNECT &&
379e8c1c 1300 !http->request->flags.spoof_client_ip && // is not a fake ssl-bumped request from a https port
f8901ea9 1301 Config.accessList.ssl_bump && http->getConn()->port->sslBump) {
e0c0d54c
CT
1302 debugs(85, 5, HERE << "SslBump possible, checking ACL");
1303
1304 ACLFilledChecklist *acl_checklist = clientAclChecklistCreate(Config.accessList.ssl_bump, http);
1305 acl_checklist->nonBlockingCheck(sslBumpAccessCheckDoneWrapper, this);
1306 return true;
f8901ea9 1307 } else {
e0c0d54c
CT
1308 http->sslBumpNeeded(false);
1309 return false;
1310 }
1311}
1312
f8901ea9 1313/**
e0c0d54c
CT
1314 * A wrapper function to use the ClientRequestContext::sslBumpAccessCheckDone method
1315 * as ACLFilledChecklist callback
1316 */
1317static void
2efeb0b7 1318sslBumpAccessCheckDoneWrapper(allow_t answer, void *data)
e0c0d54c
CT
1319{
1320 ClientRequestContext *calloutContext = static_cast<ClientRequestContext *>(data);
ed6163ef 1321 calloutContext->sslBumpAccessCheckDone(answer);
e0c0d54c
CT
1322}
1323
1324void
ed6163ef 1325ClientRequestContext::sslBumpAccessCheckDone(const allow_t &answer)
e0c0d54c 1326{
ed6163ef
AJ
1327 if (!httpStateIsValid())
1328 return;
1329
ed6163ef 1330 http->sslBumpNeeded(answer == ACCESS_ALLOWED);
e0c0d54c
CT
1331 http->doCallouts();
1332}
1333#endif
1334
69660be0 1335/*
1336 * Identify requests that do not go through the store and client side stream
1337 * and forward them to the appropriate location. All other requests, request
1338 * them.
edce4d98 1339 */
1340void
8e2745f4 1341ClientHttpRequest::processRequest()
edce4d98 1342{
60745f24 1343 debugs(85, 4, "clientProcessRequest: " << RequestMethodStr(request->method) << " '" << uri << "'");
62e76326 1344
31281814 1345 if (request->method == METHOD_CONNECT && !redirect.status) {
3712be3f 1346#if USE_SSL
31281814
AJ
1347 if (sslBumpNeeded()) {
1348 sslBumpStart();
1349 return;
1350 }
3712be3f 1351#endif
62e76326 1352 logType = LOG_TCP_MISS;
f84dd7eb 1353 getConn()->stopReading(); // tunnels read for themselves
11007d4b 1354 tunnelStart(this, &out.size, &al.http.code);
62e76326 1355 return;
edce4d98 1356 }
62e76326 1357
8e2745f4 1358 httpStart();
1359}
1360
1361void
1362ClientHttpRequest::httpStart()
1363{
559da936 1364 PROF_start(httpStart);
8e2745f4 1365 logType = LOG_TAG_NONE;
38e16f92 1366 debugs(85, 4, "ClientHttpRequest::httpStart: " << Format::log_tags[logType] << " for '" << uri << "'");
bf8fe701 1367
edce4d98 1368 /* no one should have touched this */
8e2745f4 1369 assert(out.offset == 0);
edce4d98 1370 /* Use the Stream Luke */
8e2745f4 1371 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
1372 clientStreamRead(node, this, node->readBuffer);
559da936 1373 PROF_stop(httpStart);
edce4d98 1374}
0655fa4d 1375
3712be3f 1376#if USE_SSL
1377
3712be3f 1378bool
26ac0430
AJ
1379ClientHttpRequest::sslBumpNeeded() const
1380{
e0c0d54c
CT
1381 assert(sslBumpNeed != needUnknown);
1382 return (sslBumpNeed == needConfirmed);
1383}
3712be3f 1384
e0c0d54c
CT
1385void
1386ClientHttpRequest::sslBumpNeeded(bool isNeeded)
1387{
1388 debugs(83, 3, HERE << "sslBump required: "<< (isNeeded ? "Yes" : "No"));
1389 sslBumpNeed = (isNeeded ? needConfirmed : needNot);
3712be3f 1390}
1391
1392// called when comm_write has completed
1393static void
e0d28505 1394SslBumpEstablish(const Comm::ConnectionPointer &, char *, size_t, comm_err_t errflag, int, void *data)
3712be3f 1395{
1396 ClientHttpRequest *r = static_cast<ClientHttpRequest*>(data);
1397 debugs(85, 5, HERE << "responded to CONNECT: " << r << " ? " << errflag);
1398
1399 assert(r && cbdataReferenceValid(r));
1400 r->sslBumpEstablish(errflag);
1401}
1402
1403void
1404ClientHttpRequest::sslBumpEstablish(comm_err_t errflag)
1405{
1406 // Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up
1407 if (errflag == COMM_ERR_CLOSING)
1408 return;
1409
1410 if (errflag) {
1fa35be8 1411 debugs(85, 3, HERE << "CONNECT response failure in SslBump: " << errflag);
bbc83914 1412 getConn()->clientConnection->close();
3712be3f 1413 return;
1414 }
1415
40d8be97
AR
1416 // We lack HttpReply which logRequest() uses to log the status code.
1417 // TODO: Use HttpReply instead of the "200 Connection established" string.
1418 al.http.code = 200;
1419
21512911
CT
1420#if USE_AUTH
1421 // Preserve authentication info for the ssl-bumped request
1422 if (request->auth_user_request != NULL)
1423 getConn()->auth_user_request = request->auth_user_request;
1424#endif
03f00a11 1425
65ba7f04 1426 getConn()->switchToHttps(request);
3712be3f 1427}
1428
1429void
1430ClientHttpRequest::sslBumpStart()
1431{
73c36fd9 1432 debugs(85, 5, HERE << "Confirming CONNECT tunnel on FD " << getConn()->clientConnection);
3712be3f 1433 // send an HTTP 200 response to kick client SSL negotiation
73c36fd9 1434 debugs(33, 7, HERE << "Confirming CONNECT tunnel on FD " << getConn()->clientConnection);
3712be3f 1435
1436 // TODO: Unify with tunnel.cc and add a Server(?) header
b0388924 1437 static const char *const conn_established = "HTTP/1.1 200 Connection established\r\n\r\n";
ec41b64c
AJ
1438 AsyncCall::Pointer call = commCbCall(85, 5, "ClientSocketContext::sslBumpEstablish",
1439 CommIoCbPtrFun(&SslBumpEstablish, this));
73c36fd9 1440 Comm::Write(getConn()->clientConnection, conn_established, strlen(conn_established), call, NULL);
3712be3f 1441}
1442
1443#endif
1444
0655fa4d 1445bool
1446ClientHttpRequest::gotEnough() const
1447{
86a2f789 1448 /** TODO: should be querying the stream. */
7173d5b0 1449 int64_t contentLength =
06a5ae20 1450 memObject()->getReply()->bodySize(request->method);
0655fa4d 1451 assert(contentLength >= 0);
1452
1453 if (out.offset < contentLength)
1454 return false;
1455
1456 return true;
1457}
1458
86a2f789 1459void
1460ClientHttpRequest::storeEntry(StoreEntry *newEntry)
1461{
1462 entry_ = newEntry;
1463}
1464
0976f8db 1465void
1466ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
1467{
1468 if (loggingEntry_)
97b5e68f 1469 loggingEntry_->unlock();
0976f8db 1470
1471 loggingEntry_ = newEntry;
1472
1473 if (loggingEntry_)
3d0ac046 1474 loggingEntry_->lock();
0976f8db 1475}
86a2f789 1476
de31d06f 1477/*
1478 * doCallouts() - This function controls the order of "callout"
1479 * executions, including non-blocking access control checks, the
1480 * redirector, and ICAP. Previously, these callouts were chained
1481 * together such that "clientAccessCheckDone()" would call
1482 * "clientRedirectStart()" and so on.
1483 *
1484 * The ClientRequestContext (aka calloutContext) class holds certain
1485 * state data for the callout/callback operations. Previously
1486 * ClientHttpRequest would sort of hand off control to ClientRequestContext
1487 * for a short time. ClientRequestContext would then delete itself
1488 * and pass control back to ClientHttpRequest when all callouts
1489 * were finished.
1490 *
1491 * This caused some problems for ICAP because we want to make the
1492 * ICAP callout after checking ACLs, but before checking the no_cache
1493 * list. We can't stuff the ICAP state into the ClientRequestContext
1494 * class because we still need the ICAP state after ClientRequestContext
1495 * goes away.
1496 *
1497 * Note that ClientRequestContext is created before the first call
1498 * to doCallouts().
1499 *
1500 * If one of the callouts notices that ClientHttpRequest is no
1501 * longer valid, it should call cbdataReferenceDone() so that
1502 * ClientHttpRequest's reference count goes to zero and it will get
1503 * deleted. ClientHttpRequest will then delete ClientRequestContext.
1504 *
1505 * Note that we set the _done flags here before actually starting
1506 * the callout. This is strictly for convenience.
1507 */
1508
425de4c8
AJ
1509extern tos_t aclMapTOS (acl_tos * head, ACLChecklist * ch);
1510extern nfmark_t aclMapNfmark (acl_nfmark * head, ACLChecklist * ch);
057f5854 1511
de31d06f 1512void
1513ClientHttpRequest::doCallouts()
1514{
1515 assert(calloutContext);
1516
6fca33e0
CT
1517 /*Save the original request for logging purposes*/
1518 if (!calloutContext->http->al.request)
105d1937 1519 calloutContext->http->al.request = HTTPMSGLOCK(request);
6fca33e0 1520
fe97983f 1521 // CVE-2009-0801: verify the Host: header is consistent with other known details.
2bd84e5f 1522 if (!calloutContext->error && !calloutContext->host_header_verify_done) {
fe97983f
AJ
1523 debugs(83, 3, HERE << "Doing calloutContext->hostHeaderVerify()");
1524 calloutContext->host_header_verify_done = true;
1525 calloutContext->hostHeaderVerify();
1526 return;
1527 }
1528
2bd84e5f 1529 if (!calloutContext->error && !calloutContext->http_access_done) {
3712be3f 1530 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
57abaac0 1531 calloutContext->http_access_done = true;
de31d06f 1532 calloutContext->clientAccessCheck();
1533 return;
1534 }
1535
a83c6ed6 1536#if USE_ADAPTATION
2bd84e5f 1537 if (!calloutContext->error && !calloutContext->adaptation_acl_check_done) {
a83c6ed6 1538 calloutContext->adaptation_acl_check_done = true;
abd4b611 1539 if (Adaptation::AccessCheck::Start(
26ac0430 1540 Adaptation::methodReqmod, Adaptation::pointPreCache,
79628299 1541 request, NULL, this))
abd4b611 1542 return; // will call callback
de31d06f 1543 }
de31d06f 1544#endif
1545
2bd84e5f 1546 if (!calloutContext->error && !calloutContext->redirect_done) {
57abaac0 1547 calloutContext->redirect_done = true;
de31d06f 1548 assert(calloutContext->redirect_state == REDIRECT_NONE);
1549
1550 if (Config.Program.redirect) {
3712be3f 1551 debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
de31d06f 1552 calloutContext->redirect_state = REDIRECT_PENDING;
1553 calloutContext->clientRedirectStart();
1554 return;
1555 }
1556 }
1557
2bd84e5f 1558 if (!calloutContext->error && !calloutContext->adapted_http_access_done) {
533493da
AJ
1559 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck2()");
1560 calloutContext->adapted_http_access_done = true;
1561 calloutContext->clientAccessCheck2();
1562 return;
1563 }
1564
2bd84e5f 1565 if (!calloutContext->error && !calloutContext->interpreted_req_hdrs) {
3712be3f 1566 debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
57abaac0 1567 calloutContext->interpreted_req_hdrs = 1;
1568 clientInterpretRequestHeaders(this);
1569 }
1570
2bd84e5f 1571 if (!calloutContext->error && !calloutContext->no_cache_done) {
57abaac0 1572 calloutContext->no_cache_done = true;
de31d06f 1573
1574 if (Config.accessList.noCache && request->flags.cachable) {
3712be3f 1575 debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
de31d06f 1576 calloutContext->checkNoCache();
1577 return;
1578 }
1579 }
1580
425de4c8
AJ
1581 if (!calloutContext->tosToClientDone) {
1582 calloutContext->tosToClientDone = true;
73c36fd9 1583 if (getConn() != NULL && Comm::IsConnOpen(getConn()->clientConnection)) {
c0941a6a 1584 ACLFilledChecklist ch(NULL, request, NULL);
057f5854 1585 ch.src_addr = request->client_addr;
1586 ch.my_addr = request->my_addr;
425de4c8 1587 tos_t tos = aclMapTOS(Ip::Qos::TheConfig.tosToClient, &ch);
3712be3f 1588 if (tos)
73c36fd9 1589 Ip::Qos::setSockTos(getConn()->clientConnection, tos);
425de4c8
AJ
1590 }
1591 }
1592
1593 if (!calloutContext->nfmarkToClientDone) {
1594 calloutContext->nfmarkToClientDone = true;
73c36fd9 1595 if (getConn() != NULL && Comm::IsConnOpen(getConn()->clientConnection)) {
425de4c8
AJ
1596 ACLFilledChecklist ch(NULL, request, NULL);
1597 ch.src_addr = request->client_addr;
1598 ch.my_addr = request->my_addr;
1599 nfmark_t mark = aclMapNfmark(Ip::Qos::TheConfig.nfmarkToClient, &ch);
1600 if (mark)
73c36fd9 1601 Ip::Qos::setSockNfmark(getConn()->clientConnection, mark);
3712be3f 1602 }
057f5854 1603 }
1604
d2565320 1605#if USE_SSL
2bd84e5f
CT
1606 // We need to check for SSL bump even if the calloutContext->error is set
1607 // because we are handling with different way the error inside SSL-bump
e0c0d54c
CT
1608 if (!calloutContext->sslBumpCheckDone) {
1609 calloutContext->sslBumpCheckDone = true;
1610 if (calloutContext->sslBumpAccessCheck())
1611 return;
1612 /* else no ssl bump required*/
1613 }
d2565320 1614#endif
e0c0d54c 1615
2bd84e5f
CT
1616 if (calloutContext->error) {
1617 const char *uri = urlCanonical(request);
1618 StoreEntry *e= storeCreateEntry(uri, uri, request->flags, request->method);
1619#if USE_SSL
1620 if (sslBumpNeeded() &&
1621 calloutContext->error->httpStatus != HTTP_PROXY_AUTHENTICATION_REQUIRED) {
1622 // set final error but delay sending until we bump
1623 Ssl::ServerBump *srvBump = new Ssl::ServerBump(request, e);
1624 errorAppendEntry(e, calloutContext->error);
1625 calloutContext->error = NULL;
1626 getConn()->setServerBump(srvBump);
1627 e->unlock();
1628 } else
1629#endif
1630 {
1631 // send the error to the client
1632 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1633 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1634 assert (repContext);
1635 repContext->setReplyToStoreEntry(e);
1636 errorAppendEntry(e, calloutContext->error);
1637 calloutContext->error = NULL;
1638 if (calloutContext->readNextRequest)
1639 getConn()->flags.readMore = true; // resume any pipeline reads.
1640 node = (clientStreamNode *)client_stream.tail->data;
1641 clientStreamRead(node, this, node->readBuffer);
1642 e->unlock();
1643 // Stop here.
1644 return;
1645 }
1646 }
1647
de31d06f 1648 cbdataReferenceDone(calloutContext->http);
1649 delete calloutContext;
1650 calloutContext = NULL;
de31d06f 1651#if HEADERS_LOG
1652
1653 headersLog(0, 1, request->method, request);
1654#endif
1655
58d7150d 1656 debugs(83, 3, HERE << "calling processRequest()");
de31d06f 1657 processRequest();
3ff65596
AR
1658
1659#if ICAP_CLIENT
1660 Adaptation::Icap::History::Pointer ih = request->icapHistory();
1661 if (ih != NULL)
1662 ih->logType = logType;
1663#endif
de31d06f 1664}
1665
32d002cb 1666#if !_USE_INLINE_
86a2f789 1667#include "client_side_request.cci"
1668#endif
de31d06f 1669
a83c6ed6 1670#if USE_ADAPTATION
a22e6cd3
AR
1671/// Initiate an asynchronous adaptation transaction which will call us back.
1672void
1673ClientHttpRequest::startAdaptation(const Adaptation::ServiceGroupPointer &g)
3b299123 1674{
a22e6cd3 1675 debugs(85, 3, HERE << "adaptation needed for " << this);
a83c6ed6
AR
1676 assert(!virginHeadSource);
1677 assert(!adaptedBodySource);
a22e6cd3 1678 virginHeadSource = initiateAdaptation(
4cb2536f 1679 new Adaptation::Iterator(request, NULL, g));
a83c6ed6 1680
e1381638 1681 // we could try to guess whether we can bypass this adaptation
a22e6cd3 1682 // initiation failure, but it should not really happen
4299f876 1683 Must(initiated(virginHeadSource));
de31d06f 1684}
1685
1686void
3af10ac0 1687ClientHttpRequest::noteAdaptationAnswer(const Adaptation::Answer &answer)
de31d06f 1688{
de31d06f 1689 assert(cbdataReferenceValid(this)); // indicates bug
3af10ac0
AR
1690 clearAdaptation(virginHeadSource);
1691 assert(!adaptedBodySource);
1692
1693 switch (answer.kind) {
1694 case Adaptation::Answer::akForward:
1695 handleAdaptedHeader(answer.message);
1696 break;
1697
1698 case Adaptation::Answer::akBlock:
1699 handleAdaptationBlock(answer);
1700 break;
1701
1702 case Adaptation::Answer::akError:
1703 handleAdaptationFailure(ERR_DETAIL_CLT_REQMOD_ABORT, !answer.final);
1704 break;
1705 }
1706}
1707
1708void
ec4d1a1d 1709ClientHttpRequest::handleAdaptedHeader(HttpMsg *msg)
3af10ac0 1710{
5f8252d2 1711 assert(msg);
1712
b044675d 1713 if (HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg)) {
200ac359 1714 /*
5f8252d2 1715 * Replace the old request with the new request.
200ac359 1716 */
6dd9f4bd 1717 HTTPMSGUNLOCK(request);
1718 request = HTTPMSGLOCK(new_req);
200ac359 1719 /*
1720 * Store the new URI for logging
1721 */
1722 xfree(uri);
1723 uri = xstrdup(urlCanonical(request));
1724 setLogUri(this, urlCanonicalClean(request));
914b89a2 1725 assert(request->method.id());
b044675d 1726 } else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
1727 debugs(85,3,HERE << "REQMOD reply is HTTP reply");
1728
5f8252d2 1729 // subscribe to receive reply body
1730 if (new_rep->body_pipe != NULL) {
a83c6ed6 1731 adaptedBodySource = new_rep->body_pipe;
d222a56c
HN
1732 int consumer_ok = adaptedBodySource->setConsumerIfNotLate(this);
1733 assert(consumer_ok);
5f8252d2 1734 }
1735
b044675d 1736 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1737 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1738 repContext->createStoreEntry(request->method, request->flags);
1739
1740 EBIT_CLR(storeEntry()->flags, ENTRY_FWD_HDR_WAIT);
1741 request_satisfaction_mode = true;
1742 request_satisfaction_offset = 0;
1743 storeEntry()->replaceHttpReply(new_rep);
97ae5196 1744 storeEntry()->timestampsSet();
cb4c4288 1745
a83c6ed6 1746 if (!adaptedBodySource) // no body
cb4c4288 1747 storeEntry()->complete();
b044675d 1748 clientGetMoreData(node, this);
200ac359 1749 }
de31d06f 1750
5f8252d2 1751 // we are done with getting headers (but may be receiving body)
a83c6ed6 1752 clearAdaptation(virginHeadSource);
5f8252d2 1753
b044675d 1754 if (!request_satisfaction_mode)
1755 doCallouts();
de31d06f 1756}
1757
1758void
3af10ac0 1759ClientHttpRequest::handleAdaptationBlock(const Adaptation::Answer &answer)
de31d06f 1760{
3af10ac0
AR
1761 request->detailError(ERR_ACCESS_DENIED, ERR_DETAIL_REQMOD_BLOCK);
1762 AclMatchedName = answer.ruleId.termedBuf();
1763 assert(calloutContext);
1764 calloutContext->clientAccessCheckDone(ACCESS_DENIED);
1765 AclMatchedName = NULL;
de31d06f 1766}
1767
0ad2b63b
CT
1768void
1769ClientHttpRequest::resumeBodyStorage()
1770{
e83cdc25 1771 if (!adaptedBodySource)
0ad2b63b
CT
1772 return;
1773
1774 noteMoreBodyDataAvailable(adaptedBodySource);
1775}
1776
de31d06f 1777void
1cf238db 1778ClientHttpRequest::noteMoreBodyDataAvailable(BodyPipe::Pointer)
de31d06f 1779{
5f8252d2 1780 assert(request_satisfaction_mode);
a83c6ed6 1781 assert(adaptedBodySource != NULL);
5f8252d2 1782
0ad2b63b
CT
1783 if (size_t contentSize = adaptedBodySource->buf().contentSize()) {
1784 // XXX: entry->bytesWanted returns contentSize-1 if entry can accept data.
1785 // We have to add 1 to avoid suspending forever.
1786 const size_t bytesWanted = storeEntry()->bytesWanted(Range<size_t>(0,contentSize));
1787 const size_t spaceAvailable = bytesWanted > 0 ? (bytesWanted + 1) : 0;
1788
1789 if (spaceAvailable < contentSize ) {
1790 // No or partial body data consuming
1791 typedef NullaryMemFunT<ClientHttpRequest> Dialer;
1792 AsyncCall::Pointer call = asyncCall(93, 5, "ClientHttpRequest::resumeBodyStorage",
1793 Dialer(this, &ClientHttpRequest::resumeBodyStorage));
1794 storeEntry()->deferProducer(call);
1795 }
1796
1797 // XXX: bytesWanted API does not allow us to write just one byte!
1798 if (!spaceAvailable && contentSize > 1)
1799 return;
1800
1801 if (spaceAvailable < contentSize )
1802 contentSize = spaceAvailable;
1803
a83c6ed6 1804 BodyPipeCheckout bpc(*adaptedBodySource);
0ad2b63b 1805 const StoreIOBuffer ioBuf(&bpc.buf, request_satisfaction_offset, contentSize);
5f8252d2 1806 storeEntry()->write(ioBuf);
0ad2b63b
CT
1807 // assume StoreEntry::write() writes the entire ioBuf
1808 request_satisfaction_offset += ioBuf.length;
4ce0e99b 1809 bpc.buf.consume(contentSize);
5f8252d2 1810 bpc.checkIn();
1811 }
1812
a83c6ed6 1813 if (adaptedBodySource->exhausted())
5f8252d2 1814 endRequestSatisfaction();
1815 // else wait for more body data
de31d06f 1816}
1817
1818void
1cf238db 1819ClientHttpRequest::noteBodyProductionEnded(BodyPipe::Pointer)
de31d06f 1820{
a83c6ed6 1821 assert(!virginHeadSource);
0ad2b63b
CT
1822 // should we end request satisfaction now?
1823 if (adaptedBodySource != NULL && adaptedBodySource->exhausted())
5f8252d2 1824 endRequestSatisfaction();
5f8252d2 1825}
3b299123 1826
5f8252d2 1827void
26ac0430
AJ
1828ClientHttpRequest::endRequestSatisfaction()
1829{
5f8252d2 1830 debugs(85,4, HERE << this << " ends request satisfaction");
1831 assert(request_satisfaction_mode);
a83c6ed6 1832 stopConsumingFrom(adaptedBodySource);
3b299123 1833
5f8252d2 1834 // TODO: anything else needed to end store entry formation correctly?
1835 storeEntry()->complete();
1836}
de31d06f 1837
5f8252d2 1838void
1cf238db 1839ClientHttpRequest::noteBodyProducerAborted(BodyPipe::Pointer)
5f8252d2 1840{
a83c6ed6
AR
1841 assert(!virginHeadSource);
1842 stopConsumingFrom(adaptedBodySource);
eae3a9a6
AR
1843
1844 debugs(85,3, HERE << "REQMOD body production failed");
1845 if (request_satisfaction_mode) { // too late to recover or serve an error
1846 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_CLT_REQMOD_RESP_BODY);
73c36fd9 1847 const Comm::ConnectionPointer c = getConn()->clientConnection;
e7cea0ed
AJ
1848 Must(Comm::IsConnOpen(c));
1849 c->close(); // drastic, but we may be writing a response already
eae3a9a6
AR
1850 } else {
1851 handleAdaptationFailure(ERR_DETAIL_CLT_REQMOD_REQ_BODY);
1852 }
5f8252d2 1853}
3b299123 1854
5f8252d2 1855void
64b66b76 1856ClientHttpRequest::handleAdaptationFailure(int errDetail, bool bypassable)
5f8252d2 1857{
a83c6ed6 1858 debugs(85,3, HERE << "handleAdaptationFailure(" << bypassable << ")");
3b299123 1859
5f8252d2 1860 const bool usedStore = storeEntry() && !storeEntry()->isEmpty();
1861 const bool usedPipe = request->body_pipe != NULL &&
26ac0430 1862 request->body_pipe->consumedSize() > 0;
3b299123 1863
9d4d7c5e 1864 if (bypassable && !usedStore && !usedPipe) {
1865 debugs(85,3, HERE << "ICAP REQMOD callout failed, bypassing: " << calloutContext);
5f8252d2 1866 if (calloutContext)
1867 doCallouts();
1868 return;
1869 }
3b299123 1870
5f8252d2 1871 debugs(85,3, HERE << "ICAP REQMOD callout failed, responding with error");
3b299123 1872
5f8252d2 1873 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1874 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1875 assert(repContext);
de31d06f 1876
26ac0430 1877 // The original author of the code also wanted to pass an errno to
5f8252d2 1878 // setReplyToError, but it seems unlikely that the errno reflects the
1879 // true cause of the error at this point, so I did not pass it.
2bd84e5f
CT
1880 if (calloutContext) {
1881 Ip::Address noAddr;
1882 noAddr.SetNoAddr();
1883 ConnStateData * c = getConn();
1884 calloutContext->error = clientBuildError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
1885 NULL,
1886 c != NULL ? c->clientConnection->remote : noAddr,
1887 request
1888 );
1889
1890 calloutContext->error->auth_user_request =
1891 c != NULL && c->auth_user_request != NULL ? c->auth_user_request : request->auth_user_request;
129fe2a1 1892 calloutContext->error->detailError(errDetail);
2bd84e5f
CT
1893 calloutContext->readNextRequest = true;
1894 c->expectNoForwarding();
1895 doCallouts();
1896 }
1897 //else if(calloutContext == NULL) is it possible?
de31d06f 1898}
1899
1900#endif