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