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