]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
Bug #1021: Supplementary groups membership initialization for children processes
[thirdparty/squid.git] / src / client_side_request.cc
CommitLineData
edce4d98 1
2/*
14cc8559 3 * $Id: client_side_request.cc,v 1.38 2004/10/18 12:05:37 hno Exp $
69660be0 4 *
ae45c4de 5 * DEBUG: section 85 Client-side Request Routines
6 * AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c)
69660be0 7 *
edce4d98 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
69660be0 10 *
11 * Squid is the result of efforts by numerous individuals from the Internet
12 * community; see the CONTRIBUTORS file for full details. Many organizations
13 * have provided support for Squid's development; see the SPONSORS file for
14 * full details. Squid is Copyrighted (C) 2001 by the Regents of the
15 * University of California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other sources; see the
17 * CREDITS file for full details.
18 *
19 * This program is free software; you can redistribute it and/or modify it under
20 * the terms of the GNU General Public License as published by the Free
21 * Software Foundation; either version 2 of the License, or (at your option)
22 * any later version.
23 *
24 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
25 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
26 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
27 * details.
28 *
29 * You should have received a copy of the GNU General Public License along with
30 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
31 * Place, Suite 330, Boston, MA 02111, USA.
32 *
edce4d98 33 */
34
35
69660be0 36/*
37 * General logic of request processing:
38 *
39 * We run a series of tests to determine if access will be permitted, and to do
40 * any redirection. Then we call into the result clientStream to retrieve data.
41 * From that point on it's up to reply management.
edce4d98 42 */
43
44#include "squid.h"
c8be6d7b 45#include "clientStream.h"
46#include "client_side_request.h"
f5691f9c 47#include "AuthUserRequest.h"
528b2c61 48#include "HttpRequest.h"
8000a965 49#include "ACLChecklist.h"
50#include "ACL.h"
a46d2c0e 51#include "client_side.h"
0655fa4d 52#include "client_side_reply.h"
53#include "Store.h"
54#include "HttpReply.h"
86a2f789 55#include "MemObject.h"
edce4d98 56
57#if LINGERING_CLOSE
58#define comm_close comm_lingering_close
59#endif
60
61static const char *const crlf = "\r\n";
62
0655fa4d 63class ClientRequestContext : public RefCountable
62e76326 64{
65
66public:
8e2745f4 67 void *operator new(size_t);
68 void operator delete(void *);
8e2745f4 69
70 ClientRequestContext();
71 ClientRequestContext(ClientHttpRequest *);
72 ~ClientRequestContext();
62e76326 73
8e2745f4 74 void checkNoCache();
75
4fb35c3c 76 ACLChecklist *acl_checklist; /* need ptr back so we can unreg if needed */
edce4d98 77 int redirect_state;
78 clientHttpRequest *http;
62e76326 79
80private:
8e2745f4 81 CBDATA_CLASS(ClientRequestContext);
82 static void CheckNoCacheDone(int answer, void *data);
83 void checkNoCacheDone(int answer);
84};
edce4d98 85
8e2745f4 86CBDATA_CLASS_INIT(ClientRequestContext);
87
88void *
89ClientRequestContext::operator new (size_t size)
90{
91 assert (size == sizeof(ClientRequestContext));
92 CBDATA_INIT_TYPE(ClientRequestContext);
93 ClientRequestContext *result = cbdataAlloc(ClientRequestContext);
94 /* Mark result as being owned - we want the refcounter to do the delete
95 * call */
96 cbdataReference(result);
97 return result;
98}
62e76326 99
8e2745f4 100void
101ClientRequestContext::operator delete (void *address)
102{
103 ClientRequestContext *t = static_cast<ClientRequestContext *>(address);
104 cbdataFree(address);
105 /* And allow the memory to be freed */
106 cbdataReferenceDone (t);
107}
108
edce4d98 109/* Local functions */
edce4d98 110/* other */
edce4d98 111static void clientAccessCheckDone(int, void *);
edce4d98 112static int clientCachable(clientHttpRequest * http);
113static int clientHierarchical(clientHttpRequest * http);
114static void clientInterpretRequestHeaders(clientHttpRequest * http);
14cc8559 115static void clientRedirectStart(ClientRequestContext *context);
edce4d98 116static RH clientRedirectDone;
e6ccf245 117extern "C" CSR clientGetMoreData;
118extern "C" CSS clientReplyStatus;
119extern "C" CSD clientReplyDetach;
528b2c61 120static void checkFailureRatio(err_type, hier_code);
edce4d98 121
8e2745f4 122ClientRequestContext::~ClientRequestContext()
123{
124 if (http)
62e76326 125 cbdataReferenceDone(http);
126
8e2745f4 127 if (acl_checklist)
00d77d6b 128 delete acl_checklist;
8e2745f4 129}
130
131ClientRequestContext::ClientRequestContext() : acl_checklist (NULL), redirect_state (REDIRECT_NONE), http(NULL)
62e76326 132{}
edce4d98 133
8e2745f4 134ClientRequestContext::ClientRequestContext(ClientHttpRequest *newHttp) : acl_checklist (NULL), redirect_state (REDIRECT_NONE), http(cbdataReference(newHttp))
edce4d98 135{
8e2745f4 136 assert (newHttp != NULL);
edce4d98 137}
138
528b2c61 139CBDATA_CLASS_INIT(ClientHttpRequest);
8e2745f4 140
528b2c61 141void *
142ClientHttpRequest::operator new (size_t size)
143{
144 assert (size == sizeof (ClientHttpRequest));
145 CBDATA_INIT_TYPE(ClientHttpRequest);
146 ClientHttpRequest *result = cbdataAlloc(ClientHttpRequest);
147 /* Mark result as being owned - we want the refcounter to do the delete
148 * call */
149 cbdataReference(result);
150 return result;
151}
152
62e76326 153void
528b2c61 154ClientHttpRequest::operator delete (void *address)
155{
156 ClientHttpRequest *temp = static_cast<ClientHttpRequest *>(address);
157 cbdataFree(address);
158 /* And allow the memory to be freed */
159 cbdataReferenceDone (temp);
160}
161
0976f8db 162ClientHttpRequest::ClientHttpRequest() : loggingEntry_(NULL)
528b2c61 163{
164 /* reset range iterator */
165 start = current_time;
166}
167
0655fa4d 168/*
169 * returns true if client specified that the object must come from the cache
170 * without contacting origin server
171 */
172bool
173ClientHttpRequest::onlyIfCached()const
174{
175 assert(request);
176 return request->cache_control &&
177 EBIT_TEST(request->cache_control->mask, CC_ONLY_IF_CACHED);
178}
179
528b2c61 180/*
181 * This function is designed to serve a fairly specific purpose.
182 * Occasionally our vBNS-connected caches can talk to each other, but not
183 * the rest of the world. Here we try to detect frequent failures which
184 * make the cache unusable (e.g. DNS lookup and connect() failures). If
185 * the failure:success ratio goes above 1.0 then we go into "hit only"
186 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
187 * will only fetch HITs from us if they are using the ICP protocol. We
188 * stay in this mode for 5 minutes.
189 *
190 * Duane W., Sept 16, 1996
191 */
192
193#define FAILURE_MODE_TIME 300
194
195static void
196checkFailureRatio(err_type etype, hier_code hcode)
197{
198 static double magic_factor = 100.0;
199 double n_good;
200 double n_bad;
62e76326 201
528b2c61 202 if (hcode == HIER_NONE)
62e76326 203 return;
204
528b2c61 205 n_good = magic_factor / (1.0 + request_failure_ratio);
62e76326 206
528b2c61 207 n_bad = magic_factor - n_good;
62e76326 208
528b2c61 209 switch (etype) {
62e76326 210
528b2c61 211 case ERR_DNS_FAIL:
62e76326 212
528b2c61 213 case ERR_CONNECT_FAIL:
62e76326 214
528b2c61 215 case ERR_READ_ERROR:
62e76326 216 n_bad++;
217 break;
218
528b2c61 219 default:
62e76326 220 n_good++;
528b2c61 221 }
62e76326 222
528b2c61 223 request_failure_ratio = n_bad / n_good;
62e76326 224
528b2c61 225 if (hit_only_mode_until > squid_curtime)
62e76326 226 return;
227
528b2c61 228 if (request_failure_ratio < 1.0)
62e76326 229 return;
230
528b2c61 231 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio);
62e76326 232
528b2c61 233 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
62e76326 234 FAILURE_MODE_TIME / 60);
235
528b2c61 236 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
62e76326 237
528b2c61 238 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
239}
240
241ClientHttpRequest::~ClientHttpRequest()
242{
243 debug(33, 3) ("httpRequestFree: %s\n", uri);
244 /* if body_connection !NULL, then ProcessBody has not
245 * found the end of the body yet
246 */
62e76326 247
a2ac85d9 248 if (request && request->body_connection.getRaw() != NULL)
62e76326 249 clientAbortBody(request); /* abort body transter */
250
528b2c61 251 /* the ICP check here was erroneous
252 * - storeReleaseRequest was always called if entry was valid
253 */
254 assert(logType < LOG_TYPE_MAX);
62e76326 255
528b2c61 256 logRequest();
62e76326 257
0976f8db 258 loggingEntry(NULL);
259
528b2c61 260 if (request)
62e76326 261 checkFailureRatio(request->errType, al.hier.code);
262
528b2c61 263 freeResources();
62e76326 264
528b2c61 265 /* moving to the next connection is handled by the context free */
266 dlinkDelete(&active, &ClientActiveRequests);
267}
62e76326 268
edce4d98 269/* Create a request and kick it off */
69660be0 270/*
271 * TODO: Pass in the buffers to be used in the inital Read request, as they are
272 * determined by the user
edce4d98 273 */
274int /* returns nonzero on failure */
275clientBeginRequest(method_t method, char const *url, CSCB * streamcallback,
0655fa4d 276 CSD * streamdetach, ClientStreamData streamdata, HttpHeader const *header,
62e76326 277 char *tailbuf, size_t taillen)
edce4d98 278{
279 size_t url_sz;
450e0c10 280 HttpVersion http_ver (1, 0);
528b2c61 281 clientHttpRequest *http = new ClientHttpRequest;
190154cf 282 HttpRequest *request;
528b2c61 283 StoreIOBuffer tempBuffer;
edce4d98 284 http->http_ver = http_ver;
98242069 285 http->setConn(NULL);
edce4d98 286 http->start = current_time;
287 /* this is only used to adjust the connection offset in client_side.c */
288 http->req_sz = 0;
c8be6d7b 289 tempBuffer.length = taillen;
290 tempBuffer.data = tailbuf;
edce4d98 291 /* client stream setup */
292 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 293 clientReplyStatus, new clientReplyContext(http), streamcallback,
62e76326 294 streamdetach, streamdata, tempBuffer);
edce4d98 295 /* make it visible in the 'current acctive requests list' */
296 dlinkAdd(http, &http->active, &ClientActiveRequests);
297 /* Set flags */
a46d2c0e 298 /* internal requests only makes sense in an
299 * accelerator today. TODO: accept flags ? */
300 http->flags.accel = 1;
edce4d98 301 /* allow size for url rewriting */
302 url_sz = strlen(url) + Config.appendDomainLen + 5;
e6ccf245 303 http->uri = (char *)xcalloc(url_sz, 1);
edce4d98 304 strcpy(http->uri, url);
305
306 if ((request = urlParse(method, http->uri)) == NULL) {
62e76326 307 debug(85, 5) ("Invalid URL: %s\n", http->uri);
308 return -1;
edce4d98 309 }
62e76326 310
69660be0 311 /*
312 * now update the headers in request with our supplied headers. urLParse
313 * should return a blank header set, but we use Update to be sure of
314 * correctness.
edce4d98 315 */
316 if (header)
62e76326 317 httpHeaderUpdate(&request->header, header, NULL);
318
edce4d98 319 http->log_uri = xstrdup(urlCanonicalClean(request));
62e76326 320
edce4d98 321 /* http struct now ready */
322
69660be0 323 /*
324 * build new header list *? TODO
edce4d98 325 */
326 request->flags.accelerated = http->flags.accel;
62e76326 327
a46d2c0e 328 request->flags.internalclient = 1;
329
330 /* this is an internally created
331 * request, not subject to acceleration
332 * target overrides */
69660be0 333 /*
334 * FIXME? Do we want to detect and handle internal requests of internal
335 * objects ?
336 */
edce4d98 337
338 /* Internally created requests cannot have bodies today */
339 request->content_length = 0;
62e76326 340
edce4d98 341 request->client_addr = no_addr;
62e76326 342
edce4d98 343 request->my_addr = no_addr; /* undefined for internal requests */
62e76326 344
edce4d98 345 request->my_port = 0;
62e76326 346
edce4d98 347 request->http_ver = http_ver;
62e76326 348
edce4d98 349 http->request = requestLink(request);
350
351 /* optional - skip the access check ? */
352 clientAccessCheck(http);
62e76326 353
edce4d98 354 return 0;
355}
356
edce4d98 357/* This is the entry point for external users of the client_side routines */
358void
8e2745f4 359clientAccessCheck(ClientHttpRequest *http)
edce4d98 360{
8e2745f4 361 ClientRequestContext *context = new ClientRequestContext(http);
edce4d98 362 context->acl_checklist =
62e76326 363 clientAclChecklistCreate(Config.accessList.http, http);
225b7b10 364 context->acl_checklist->nonBlockingCheck(clientAccessCheckDone, context);
edce4d98 365}
366
367void
368clientAccessCheckDone(int answer, void *data)
369{
8e2745f4 370 ClientRequestContext *context = (ClientRequestContext *)data;
62e76326 371
7d31d5fa 372 context->acl_checklist = NULL;
fbade053 373 clientHttpRequest *http_ = context->http;
374
375 if (!cbdataReferenceValid (http_)) {
00d77d6b 376 delete context;
62e76326 377 return;
fbade053 378 }
62e76326 379
edce4d98 380 clientHttpRequest *http = context->http;
381 err_type page_id;
382 http_status status;
edce4d98 383 debug(85, 2) ("The request %s %s is %s, because it matched '%s'\n",
62e76326 384 RequestMethodStr[http->request->method], http->uri,
385 answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED",
386 AclMatchedName ? AclMatchedName : "NO ACL's");
f5691f9c 387 char const *proxy_auth_msg = "<null>";
388
389 if (http->getConn().getRaw() != NULL && http->getConn()->auth_user_request != NULL)
390 proxy_auth_msg = http->getConn()->auth_user_request->denyMessage("<null>");
391 else if (http->request->auth_user_request != NULL)
392 proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
62e76326 393
edce4d98 394 if (answer == ACCESS_ALLOWED) {
62e76326 395 safe_free(http->uri);
396 http->uri = xstrdup(urlCanonical(http->request));
397 assert(context->redirect_state == REDIRECT_NONE);
398 context->redirect_state = REDIRECT_PENDING;
14cc8559 399 clientRedirectStart(context);
edce4d98 400 } else {
62e76326 401 /* Send an error */
402 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
00d77d6b 403 delete context;
62e76326 404 debug(85, 5) ("Access Denied: %s\n", http->uri);
405 debug(85, 5) ("AclMatchedName = %s\n",
406 AclMatchedName ? AclMatchedName : "<null>");
407 debug(85, 5) ("Proxy Auth Message = %s\n",
408 proxy_auth_msg ? proxy_auth_msg : "<null>");
409 /*
410 * NOTE: get page_id here, based on AclMatchedName because if
411 * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
412 * the clientCreateStoreEntry() call just below. Pedro Ribeiro
413 * <pribeiro@isel.pt>
414 */
415 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
416 http->logType = LOG_TCP_DENIED;
417
418 if (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName)) {
419 if (!http->flags.accel) {
420 /* Proxy authorisation needed */
421 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
422 } else {
423 /* WWW authorisation needed */
424 status = HTTP_UNAUTHORIZED;
425 }
426
427 if (page_id == ERR_NONE)
428 page_id = ERR_CACHE_ACCESS_DENIED;
429 } else {
430 status = HTTP_FORBIDDEN;
431
432 if (page_id == ERR_NONE)
433 page_id = ERR_ACCESS_DENIED;
434 }
435
0655fa4d 436 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
437 assert (repContext);
438 repContext->setReplyToError(page_id, status,
439 http->request->method, NULL,
a2ac85d9 440 http->getConn().getRaw() != NULL ? &http->getConn()->peer.sin_addr : &no_addr, http->request,
441 NULL, http->getConn().getRaw() != NULL
98242069 442 && http->getConn()->auth_user_request ? http->getConn()->
0655fa4d 443 auth_user_request : http->request->auth_user_request);
62e76326 444 node = (clientStreamNode *)http->client_stream.tail->data;
445 clientStreamRead(node, http, node->readBuffer);
edce4d98 446 }
447}
448
14cc8559 449static void
450clientRedirectAccessCheckDone(int answer, void *data)
451{
452 ClientRequestContext *context = (ClientRequestContext *)data;
453 clientHttpRequest *http = context->http;
454 context->acl_checklist = NULL;
455
456 if (answer == ACCESS_ALLOWED)
457 redirectStart(http, clientRedirectDone, context);
458 else
459 clientRedirectDone(context, NULL);
460}
461
462static void
463clientRedirectStart(ClientRequestContext *context)
464{
465 clientHttpRequest *http = context->http;
466 debug(33, 5) ("clientRedirectStart: '%s'\n", http->uri);
467
468 if (Config.Program.redirect == NULL) {
469 clientRedirectDone(context, NULL);
470 return;
471 }
472
473 if (Config.accessList.redirector) {
474 context->acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
475 context->acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, context);
476 } else
477 redirectStart(http, clientRedirectDone, context);
478}
479
edce4d98 480static int
481clientCachable(clientHttpRequest * http)
482{
190154cf 483 HttpRequest *req = http->request;
edce4d98 484 method_t method = req->method;
62e76326 485
edce4d98 486 if (req->protocol == PROTO_HTTP)
62e76326 487 return httpCachable(method);
488
edce4d98 489 /* FTP is always cachable */
490 if (req->protocol == PROTO_WAIS)
62e76326 491 return 0;
492
69660be0 493 /*
494 * The below looks questionable: what non HTTP protocols use connect,
495 * trace, put and post? RC
edce4d98 496 */
497 if (method == METHOD_CONNECT)
62e76326 498 return 0;
499
edce4d98 500 if (method == METHOD_TRACE)
62e76326 501 return 0;
502
edce4d98 503 if (method == METHOD_PUT)
62e76326 504 return 0;
505
edce4d98 506 if (method == METHOD_POST)
a46d2c0e 507 return 0;
508
509 /* XXX POST may be cached sometimes.. ignored
510
511 * for now */
edce4d98 512 if (req->protocol == PROTO_GOPHER)
62e76326 513 return gopherCachable(req);
514
edce4d98 515 if (req->protocol == PROTO_CACHEOBJ)
62e76326 516 return 0;
517
edce4d98 518 return 1;
519}
520
521static int
522clientHierarchical(clientHttpRequest * http)
523{
524 const char *url = http->uri;
190154cf 525 HttpRequest *request = http->request;
edce4d98 526 method_t method = request->method;
527 const wordlist *p = NULL;
528
69660be0 529 /*
530 * IMS needs a private key, so we can use the hierarchy for IMS only if our
531 * neighbors support private keys
532 */
62e76326 533
edce4d98 534 if (request->flags.ims && !neighbors_do_private_keys)
62e76326 535 return 0;
536
69660be0 537 /*
538 * This is incorrect: authenticating requests can be sent via a hierarchy
539 * (they can even be cached if the correct headers are set on the reply
edce4d98 540 */
541 if (request->flags.auth)
62e76326 542 return 0;
543
edce4d98 544 if (method == METHOD_TRACE)
62e76326 545 return 1;
546
edce4d98 547 if (method != METHOD_GET)
62e76326 548 return 0;
549
edce4d98 550 /* scan hierarchy_stoplist */
551 for (p = Config.hierarchy_stoplist; p; p = p->next)
62e76326 552 if (strstr(url, p->key))
553 return 0;
554
edce4d98 555 if (request->flags.loopdetect)
62e76326 556 return 0;
557
edce4d98 558 if (request->protocol == PROTO_HTTP)
62e76326 559 return httpCachable(method);
560
edce4d98 561 if (request->protocol == PROTO_GOPHER)
62e76326 562 return gopherCachable(request);
563
edce4d98 564 if (request->protocol == PROTO_WAIS)
62e76326 565 return 0;
566
edce4d98 567 if (request->protocol == PROTO_CACHEOBJ)
62e76326 568 return 0;
569
edce4d98 570 return 1;
571}
572
573
574static void
575clientInterpretRequestHeaders(clientHttpRequest * http)
576{
190154cf 577 HttpRequest *request = http->request;
edce4d98 578 const HttpHeader *req_hdr = &request->header;
579 int no_cache = 0;
a787b56a 580#if !(ESI) || defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG)
62e76326 581
edce4d98 582 const char *str;
583#endif
62e76326 584
edce4d98 585 request->imslen = -1;
586 request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE);
62e76326 587
edce4d98 588 if (request->ims > 0)
62e76326 589 request->flags.ims = 1;
590
edce4d98 591#if ESI
69660be0 592 /*
593 * We ignore Cache-Control as per the Edge Architecture Section 3. See
594 * www.esi.org for more information.
edce4d98 595 */
596#else
62e76326 597
edce4d98 598 if (httpHeaderHas(req_hdr, HDR_PRAGMA)) {
62e76326 599 String s = httpHeaderGetList(req_hdr, HDR_PRAGMA);
600
601 if (strListIsMember(&s, "no-cache", ','))
602 no_cache++;
603
604 s.clean();
edce4d98 605 }
62e76326 606
edce4d98 607 request->cache_control = httpHeaderGetCc(req_hdr);
62e76326 608
edce4d98 609 if (request->cache_control)
62e76326 610 if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
611 no_cache++;
612
69660be0 613 /*
62e76326 614 * Work around for supporting the Reload button in IE browsers when Squid
615 * is used as an accelerator or transparent proxy, by turning accelerated
616 * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
617 * actually only fixed in SP1, but we can't tell whether we are talking to
618 * SP1 or not so all 5.5 versions are treated 'normally').
619 */
edce4d98 620 if (Config.onoff.ie_refresh) {
62e76326 621 if (http->flags.accel && request->flags.ims) {
622 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT))) {
623 if (strstr(str, "MSIE 5.01") != NULL)
624 no_cache++;
625 else if (strstr(str, "MSIE 5.0") != NULL)
626 no_cache++;
627 else if (strstr(str, "MSIE 4.") != NULL)
628 no_cache++;
629 else if (strstr(str, "MSIE 3.") != NULL)
630 no_cache++;
631 }
632 }
edce4d98 633 }
62e76326 634
edce4d98 635#endif
636 if (no_cache) {
637#if HTTP_VIOLATIONS
62e76326 638
639 if (Config.onoff.reload_into_ims)
640 request->flags.nocache_hack = 1;
641 else if (refresh_nocache_hack)
642 request->flags.nocache_hack = 1;
643 else
edce4d98 644#endif
62e76326 645
646 request->flags.nocache = 1;
edce4d98 647 }
62e76326 648
edce4d98 649 /* ignore range header in non-GETs */
650 if (request->method == METHOD_GET) {
62e76326 651 request->range = httpHeaderGetRange(req_hdr);
652
653 if (request->range) {
654 request->flags.range = 1;
655 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
656 /* XXX: This is suboptimal. We should give the stream the range set,
657 * and thereby let the top of the stream set the offset when the
658 * size becomes known. As it is, we will end up requesting from 0
659 * for evey -X range specification.
660 * RBC - this may be somewhat wrong. We should probably set the range
661 * iter up at this point.
662 */
663 node->readBuffer.offset = request->range->lowestOffset(0);
664 http->range_iter.pos = request->range->begin();
665 http->range_iter.valid = true;
666 }
edce4d98 667 }
62e76326 668
edce4d98 669 if (httpHeaderHas(req_hdr, HDR_AUTHORIZATION))
62e76326 670 request->flags.auth = 1;
671
edce4d98 672 if (request->login[0] != '\0')
62e76326 673 request->flags.auth = 1;
674
edce4d98 675 if (httpHeaderHas(req_hdr, HDR_VIA)) {
62e76326 676 String s = httpHeaderGetList(req_hdr, HDR_VIA);
677 /*
678 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
679 * Note ThisCache2 has a space prepended to the hostname so we don't
680 * accidentally match super-domains.
681 */
682
683 if (strListIsSubstr(&s, ThisCache2, ',')) {
684 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
685 request, (ObjPackMethod) & httpRequestPack);
686 request->flags.loopdetect = 1;
687 }
688
edce4d98 689#if FORW_VIA_DB
62e76326 690 fvdbCountVia(s.buf());
691
edce4d98 692#endif
62e76326 693
694 s.clean();
edce4d98 695 }
62e76326 696
edce4d98 697#if USE_USERAGENT_LOG
698 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT)))
7928e475 699 logUserAgent(fqdnFromAddr(http->getConn().getRaw() ? http->getConn()->log_addr : no_addr), str);
62e76326 700
edce4d98 701#endif
702#if USE_REFERER_LOG
62e76326 703
edce4d98 704 if ((str = httpHeaderGetStr(req_hdr, HDR_REFERER)))
7928e475 705 logReferer(fqdnFromAddr(http->getConn().getRaw() ? http->getConn()->log_addr : no_addr), str, http->log_uri);
62e76326 706
edce4d98 707#endif
708#if FORW_VIA_DB
62e76326 709
edce4d98 710 if (httpHeaderHas(req_hdr, HDR_X_FORWARDED_FOR)) {
62e76326 711 String s = httpHeaderGetList(req_hdr, HDR_X_FORWARDED_FOR);
712 fvdbCountForw(s.buf());
713 s.clean();
edce4d98 714 }
62e76326 715
edce4d98 716#endif
717 if (request->method == METHOD_TRACE) {
62e76326 718 request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS);
edce4d98 719 }
62e76326 720
edce4d98 721 if (clientCachable(http))
62e76326 722 request->flags.cachable = 1;
723
edce4d98 724 if (clientHierarchical(http))
62e76326 725 request->flags.hierarchical = 1;
726
edce4d98 727 debug(85, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n",
62e76326 728 request->flags.nocache ? "SET" : "NOT SET");
729
edce4d98 730 debug(85, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n",
62e76326 731 request->flags.cachable ? "SET" : "NOT SET");
732
edce4d98 733 debug(85, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n",
62e76326 734 request->flags.hierarchical ? "SET" : "NOT SET");
edce4d98 735}
736
737void
738clientRedirectDone(void *data, char *result)
739{
8e2745f4 740 ClientRequestContext *context = (ClientRequestContext *)data;
db02222f 741 clientHttpRequest *http_ = context->http;
742
743 if (!cbdataReferenceValid (http_)) {
00d77d6b 744 delete context;
62e76326 745 return;
db02222f 746 }
62e76326 747
edce4d98 748 clientHttpRequest *http = context->http;
190154cf 749 HttpRequest *new_request = NULL;
750 HttpRequest *old_request = http->request;
edce4d98 751 debug(85, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri,
62e76326 752 result ? result : "NULL");
edce4d98 753 assert(context->redirect_state == REDIRECT_PENDING);
754 context->redirect_state = REDIRECT_DONE;
62e76326 755
edce4d98 756 if (result) {
62e76326 757 http_status status = (http_status) atoi(result);
758
759 if (status == HTTP_MOVED_PERMANENTLY
760 || status == HTTP_MOVED_TEMPORARILY
761 || status == HTTP_SEE_OTHER
762 || status == HTTP_TEMPORARY_REDIRECT) {
763 char *t = result;
764
765 if ((t = strchr(result, ':')) != NULL) {
766 http->redirect.status = status;
767 http->redirect.location = xstrdup(t + 1);
768 } else {
769 debug(85, 1) ("clientRedirectDone: bad input: %s\n", result);
770 }
771 }
772
773 if (strcmp(result, http->uri))
774 new_request = urlParse(old_request->method, result);
edce4d98 775 }
62e76326 776
edce4d98 777 if (new_request) {
62e76326 778 safe_free(http->uri);
779 http->uri = xstrdup(urlCanonical(new_request));
780 new_request->http_ver = old_request->http_ver;
781 httpHeaderAppend(&new_request->header, &old_request->header);
782 new_request->client_addr = old_request->client_addr;
47b0c1fa 783 new_request->client_port = old_request->client_port;
62e76326 784 new_request->my_addr = old_request->my_addr;
785 new_request->my_port = old_request->my_port;
786 new_request->flags = old_request->flags;
787
788 if (old_request->auth_user_request) {
789 new_request->auth_user_request = old_request->auth_user_request;
f5691f9c 790
791 new_request->auth_user_request->lock()
792
793 ;
62e76326 794 }
795
a2ac85d9 796 if (old_request->body_connection.getRaw() != NULL) {
62e76326 797 new_request->body_connection = old_request->body_connection;
798 old_request->body_connection = NULL;
799 }
800
801 new_request->content_length = old_request->content_length;
abb929f0 802 new_request->extacl_user = old_request->extacl_user;
803 new_request->extacl_passwd = old_request->extacl_passwd;
62e76326 804 new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
805 requestUnlink(old_request);
806 http->request = requestLink(new_request);
edce4d98 807 }
62e76326 808
edce4d98 809 clientInterpretRequestHeaders(http);
810#if HEADERS_LOG
62e76326 811
edce4d98 812 headersLog(0, 1, request->method, request);
813#endif
814 /* FIXME PIPELINE: This is innacurate during pipelining */
62e76326 815
a2ac85d9 816 if (http->getConn().getRaw() != NULL)
98242069 817 fd_note(http->getConn()->fd, http->uri);
62e76326 818
c8be6d7b 819 assert(http->uri);
62e76326 820
8e2745f4 821 context->checkNoCache();
edce4d98 822}
823
824void
8e2745f4 825ClientRequestContext::checkNoCache()
edce4d98 826{
edce4d98 827 if (Config.accessList.noCache && http->request->flags.cachable) {
62e76326 828 acl_checklist =
829 clientAclChecklistCreate(Config.accessList.noCache, http);
830 acl_checklist->nonBlockingCheck(CheckNoCacheDone, cbdataReference(this));
edce4d98 831 } else {
62e76326 832 CheckNoCacheDone(http->request->flags.cachable, cbdataReference(this));
edce4d98 833 }
834}
835
836void
8e2745f4 837ClientRequestContext::CheckNoCacheDone(int answer, void *data)
edce4d98 838{
4fb35c3c 839 void *temp;
e4a67a80 840#ifndef PURIFY
841
842 bool valid =
843#endif
844 cbdataReferenceValidDone(data, &temp);
8e2745f4 845 /* acl NB calls cannot invalidate cbdata in the normal course of things */
4fb35c3c 846 assert (valid);
8e2745f4 847 ClientRequestContext *context = (ClientRequestContext *)temp;
848 context->checkNoCacheDone(answer);
849}
4fb35c3c 850
8e2745f4 851void
852ClientRequestContext::checkNoCacheDone(int answer)
62e76326 853{
8e2745f4 854 acl_checklist = NULL;
855 clientHttpRequest *http_ = http;
8e2745f4 856
3b1b4c07 857 if (!cbdataReferenceValid (http_)) {
00d77d6b 858 delete this;
62e76326 859 return;
3b1b4c07 860 }
62e76326 861
00d77d6b 862 delete this;
8e2745f4 863 http_->request->flags.cachable = answer;
864 http_->processRequest();
edce4d98 865}
866
69660be0 867/*
868 * Identify requests that do not go through the store and client side stream
869 * and forward them to the appropriate location. All other requests, request
870 * them.
edce4d98 871 */
872void
8e2745f4 873ClientHttpRequest::processRequest()
edce4d98 874{
edce4d98 875 debug(85, 4) ("clientProcessRequest: %s '%s'\n",
62e76326 876 RequestMethodStr[request->method], uri);
877
8e2745f4 878 if (request->method == METHOD_CONNECT) {
62e76326 879 logType = LOG_TCP_MISS;
880 sslStart(this, &out.size, &al.http.code);
881 return;
edce4d98 882 }
62e76326 883
8e2745f4 884 httpStart();
885}
886
887void
888ClientHttpRequest::httpStart()
889{
890 logType = LOG_TAG_NONE;
891 debug(85, 4) ("ClientHttpRequest::httpStart: %s for '%s'\n",
62e76326 892 log_tags[logType], uri);
edce4d98 893 /* no one should have touched this */
8e2745f4 894 assert(out.offset == 0);
edce4d98 895 /* Use the Stream Luke */
8e2745f4 896 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
897 clientStreamRead(node, this, node->readBuffer);
edce4d98 898}
0655fa4d 899
900bool
901ClientHttpRequest::gotEnough() const
902{
86a2f789 903 /** TODO: should be querying the stream. */
0655fa4d 904 int contentLength =
86a2f789 905 httpReplyBodySize(request->method, memObject()->getReply());
0655fa4d 906 assert(contentLength >= 0);
907
908 if (out.offset < contentLength)
909 return false;
910
911 return true;
912}
913
b51aec66 914void
915ClientHttpRequest::maxReplyBodySize(ssize_t clen)
916{
917 maxReplyBodySize_ = clen;
918}
919
920ssize_t
921ClientHttpRequest::maxReplyBodySize() const
922{
923 return maxReplyBodySize_;
924}
925
926bool
927ClientHttpRequest::isReplyBodyTooLarge(ssize_t clen) const
928{
929 if (0 == maxReplyBodySize())
930 return 0; /* disabled */
931
932 if (clen < 0)
933 return 0; /* unknown */
934
935 return clen > maxReplyBodySize();
936}
86a2f789 937
938void
939ClientHttpRequest::storeEntry(StoreEntry *newEntry)
940{
941 entry_ = newEntry;
942}
943
0976f8db 944void
945ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
946{
947 if (loggingEntry_)
948 storeUnlockObject(loggingEntry_);
949
950 loggingEntry_ = newEntry;
951
952 if (loggingEntry_)
953 storeLockObject(loggingEntry_);
954}
86a2f789 955
956#ifndef _USE_INLINE_
957#include "client_side_request.cci"
958#endif