]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
The check for "connection: close" in the ICAP header
[thirdparty/squid.git] / src / client_side_request.cc
CommitLineData
edce4d98 1
2/*
a546b04b 3 * $Id: client_side_request.cc,v 1.54 2005/12/06 00:01:23 wessels 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"
de31d06f 56#include "ClientRequestContext.h"
57
58#if ICAP_CLIENT
59#include "ICAP/ICAPClientReqmodPrecache.h"
60#include "ICAP/ICAPElements.h"
61#include "ICAP/ICAPConfig.h"
62static void icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data);
ab7ac359 63extern ICAPConfig TheICAPConfig;
de31d06f 64#endif
edce4d98 65
66#if LINGERING_CLOSE
67#define comm_close comm_lingering_close
68#endif
69
70static const char *const crlf = "\r\n";
71
8e2745f4 72CBDATA_CLASS_INIT(ClientRequestContext);
73
74void *
75ClientRequestContext::operator new (size_t size)
76{
77 assert (size == sizeof(ClientRequestContext));
78 CBDATA_INIT_TYPE(ClientRequestContext);
79 ClientRequestContext *result = cbdataAlloc(ClientRequestContext);
aa625860 80 return result;
8e2745f4 81}
62e76326 82
8e2745f4 83void
84ClientRequestContext::operator delete (void *address)
85{
86 ClientRequestContext *t = static_cast<ClientRequestContext *>(address);
aa625860 87 cbdataFree(t);
8e2745f4 88}
89
edce4d98 90/* Local functions */
edce4d98 91/* other */
de31d06f 92static void clientAccessCheckDoneWrapper(int, void *);
59a1efb2 93static int clientCachable(ClientHttpRequest * http);
94static int clientHierarchical(ClientHttpRequest * http);
95static void clientInterpretRequestHeaders(ClientHttpRequest * http);
de31d06f 96static RH clientRedirectDoneWrapper;
97static PF checkNoCacheDoneWrapper;
e6ccf245 98extern "C" CSR clientGetMoreData;
99extern "C" CSS clientReplyStatus;
100extern "C" CSD clientReplyDetach;
528b2c61 101static void checkFailureRatio(err_type, hier_code);
edce4d98 102
8e2745f4 103ClientRequestContext::~ClientRequestContext()
104{
de31d06f 105 /*
a546b04b 106 * Release our "lock" on our parent, ClientHttpRequest, if we
107 * still have one
de31d06f 108 */
a546b04b 109
110 if (http)
111 cbdataReferenceDone(http);
62e76326 112
8e2745f4 113 if (acl_checklist)
00d77d6b 114 delete acl_checklist;
8e2745f4 115}
116
de31d06f 117ClientRequestContext::ClientRequestContext(ClientHttpRequest *anHttp) : http(anHttp), acl_checklist (NULL), redirect_state (REDIRECT_NONE)
edce4d98 118{
de31d06f 119 (void) cbdataReference(http);
120 http_access_done = 0;
121 redirect_done = 0;
122 no_cache_done = 0;
edce4d98 123}
124
528b2c61 125CBDATA_CLASS_INIT(ClientHttpRequest);
8e2745f4 126
528b2c61 127void *
128ClientHttpRequest::operator new (size_t size)
129{
130 assert (size == sizeof (ClientHttpRequest));
131 CBDATA_INIT_TYPE(ClientHttpRequest);
132 ClientHttpRequest *result = cbdataAlloc(ClientHttpRequest);
aa625860 133 return result;
528b2c61 134}
135
62e76326 136void
528b2c61 137ClientHttpRequest::operator delete (void *address)
138{
aa625860 139 ClientHttpRequest *t = static_cast<ClientHttpRequest *>(address);
140 cbdataFree(t);
528b2c61 141}
142
0976f8db 143ClientHttpRequest::ClientHttpRequest() : loggingEntry_(NULL)
528b2c61 144{
145 /* reset range iterator */
146 start = current_time;
147}
148
0655fa4d 149/*
150 * returns true if client specified that the object must come from the cache
151 * without contacting origin server
152 */
153bool
154ClientHttpRequest::onlyIfCached()const
155{
156 assert(request);
157 return request->cache_control &&
158 EBIT_TEST(request->cache_control->mask, CC_ONLY_IF_CACHED);
159}
160
528b2c61 161/*
162 * This function is designed to serve a fairly specific purpose.
163 * Occasionally our vBNS-connected caches can talk to each other, but not
164 * the rest of the world. Here we try to detect frequent failures which
165 * make the cache unusable (e.g. DNS lookup and connect() failures). If
166 * the failure:success ratio goes above 1.0 then we go into "hit only"
167 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
168 * will only fetch HITs from us if they are using the ICP protocol. We
169 * stay in this mode for 5 minutes.
170 *
171 * Duane W., Sept 16, 1996
172 */
173
174#define FAILURE_MODE_TIME 300
175
176static void
177checkFailureRatio(err_type etype, hier_code hcode)
178{
179 static double magic_factor = 100.0;
180 double n_good;
181 double n_bad;
62e76326 182
528b2c61 183 if (hcode == HIER_NONE)
62e76326 184 return;
185
528b2c61 186 n_good = magic_factor / (1.0 + request_failure_ratio);
62e76326 187
528b2c61 188 n_bad = magic_factor - n_good;
62e76326 189
528b2c61 190 switch (etype) {
62e76326 191
528b2c61 192 case ERR_DNS_FAIL:
62e76326 193
528b2c61 194 case ERR_CONNECT_FAIL:
62e76326 195
528b2c61 196 case ERR_READ_ERROR:
62e76326 197 n_bad++;
198 break;
199
528b2c61 200 default:
62e76326 201 n_good++;
528b2c61 202 }
62e76326 203
528b2c61 204 request_failure_ratio = n_bad / n_good;
62e76326 205
528b2c61 206 if (hit_only_mode_until > squid_curtime)
62e76326 207 return;
208
528b2c61 209 if (request_failure_ratio < 1.0)
62e76326 210 return;
211
528b2c61 212 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio);
62e76326 213
528b2c61 214 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
62e76326 215 FAILURE_MODE_TIME / 60);
216
528b2c61 217 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
62e76326 218
528b2c61 219 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
220}
221
222ClientHttpRequest::~ClientHttpRequest()
223{
224 debug(33, 3) ("httpRequestFree: %s\n", uri);
225 /* if body_connection !NULL, then ProcessBody has not
226 * found the end of the body yet
227 */
62e76326 228
21b92762 229 if (request && request->body_connection.getRaw() != NULL) {
62e76326 230 clientAbortBody(request); /* abort body transter */
21b92762 231 request->body_connection = NULL;
232 }
62e76326 233
528b2c61 234 /* the ICP check here was erroneous
235 * - storeReleaseRequest was always called if entry was valid
236 */
237 assert(logType < LOG_TYPE_MAX);
62e76326 238
528b2c61 239 logRequest();
62e76326 240
0976f8db 241 loggingEntry(NULL);
242
528b2c61 243 if (request)
62e76326 244 checkFailureRatio(request->errType, al.hier.code);
245
528b2c61 246 freeResources();
62e76326 247
de31d06f 248#if ICAP_CLIENT
249
250 if (icap) {
251 delete icap;
252 cbdataReferenceDone(icap);
253 }
254
255#endif
256 if (calloutContext)
257 delete calloutContext;
258
528b2c61 259 /* moving to the next connection is handled by the context free */
260 dlinkDelete(&active, &ClientActiveRequests);
261}
62e76326 262
edce4d98 263/* Create a request and kick it off */
69660be0 264/*
265 * TODO: Pass in the buffers to be used in the inital Read request, as they are
266 * determined by the user
edce4d98 267 */
268int /* returns nonzero on failure */
269clientBeginRequest(method_t method, char const *url, CSCB * streamcallback,
0655fa4d 270 CSD * streamdetach, ClientStreamData streamdata, HttpHeader const *header,
62e76326 271 char *tailbuf, size_t taillen)
edce4d98 272{
273 size_t url_sz;
450e0c10 274 HttpVersion http_ver (1, 0);
59a1efb2 275 ClientHttpRequest *http = new ClientHttpRequest;
190154cf 276 HttpRequest *request;
528b2c61 277 StoreIOBuffer tempBuffer;
98242069 278 http->setConn(NULL);
edce4d98 279 http->start = current_time;
280 /* this is only used to adjust the connection offset in client_side.c */
281 http->req_sz = 0;
c8be6d7b 282 tempBuffer.length = taillen;
283 tempBuffer.data = tailbuf;
edce4d98 284 /* client stream setup */
285 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 286 clientReplyStatus, new clientReplyContext(http), streamcallback,
62e76326 287 streamdetach, streamdata, tempBuffer);
edce4d98 288 /* make it visible in the 'current acctive requests list' */
289 dlinkAdd(http, &http->active, &ClientActiveRequests);
290 /* Set flags */
a46d2c0e 291 /* internal requests only makes sense in an
292 * accelerator today. TODO: accept flags ? */
293 http->flags.accel = 1;
edce4d98 294 /* allow size for url rewriting */
295 url_sz = strlen(url) + Config.appendDomainLen + 5;
e6ccf245 296 http->uri = (char *)xcalloc(url_sz, 1);
edce4d98 297 strcpy(http->uri, url);
298
299 if ((request = urlParse(method, http->uri)) == NULL) {
62e76326 300 debug(85, 5) ("Invalid URL: %s\n", http->uri);
301 return -1;
edce4d98 302 }
62e76326 303
69660be0 304 /*
305 * now update the headers in request with our supplied headers. urLParse
306 * should return a blank header set, but we use Update to be sure of
307 * correctness.
edce4d98 308 */
309 if (header)
62e76326 310 httpHeaderUpdate(&request->header, header, NULL);
311
edce4d98 312 http->log_uri = xstrdup(urlCanonicalClean(request));
62e76326 313
edce4d98 314 /* http struct now ready */
315
69660be0 316 /*
317 * build new header list *? TODO
edce4d98 318 */
319 request->flags.accelerated = http->flags.accel;
62e76326 320
a46d2c0e 321 request->flags.internalclient = 1;
322
323 /* this is an internally created
324 * request, not subject to acceleration
325 * target overrides */
69660be0 326 /*
327 * FIXME? Do we want to detect and handle internal requests of internal
328 * objects ?
329 */
edce4d98 330
331 /* Internally created requests cannot have bodies today */
332 request->content_length = 0;
62e76326 333
edce4d98 334 request->client_addr = no_addr;
62e76326 335
edce4d98 336 request->my_addr = no_addr; /* undefined for internal requests */
62e76326 337
edce4d98 338 request->my_port = 0;
62e76326 339
edce4d98 340 request->http_ver = http_ver;
62e76326 341
edce4d98 342 http->request = requestLink(request);
343
344 /* optional - skip the access check ? */
de31d06f 345 http->calloutContext = new ClientRequestContext(http);
346
347 http->calloutContext->http_access_done = 0;
348
349 http->calloutContext->redirect_done = 1;
350
351 http->calloutContext->no_cache_done = 1;
352
353 http->doCallouts();
62e76326 354
edce4d98 355 return 0;
356}
357
de31d06f 358bool
359ClientRequestContext::httpStateIsValid()
360{
361 ClientHttpRequest *http_ = http;
362
363 if (cbdataReferenceValid(http_))
364 return true;
365
366 http = NULL;
367
368 cbdataReferenceDone(http_);
369
370 return false;
371}
372
edce4d98 373/* This is the entry point for external users of the client_side routines */
374void
de31d06f 375ClientRequestContext::clientAccessCheck()
edce4d98 376{
de31d06f 377 acl_checklist =
62e76326 378 clientAclChecklistCreate(Config.accessList.http, http);
de31d06f 379 acl_checklist->nonBlockingCheck(clientAccessCheckDoneWrapper, this);
edce4d98 380}
381
382void
de31d06f 383clientAccessCheckDoneWrapper(int answer, void *data)
edce4d98 384{
de31d06f 385 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
fbade053 386
de31d06f 387 if (!calloutContext->httpStateIsValid())
62e76326 388 return;
62e76326 389
de31d06f 390 calloutContext->clientAccessCheckDone(answer);
391}
392
393void
394ClientRequestContext::clientAccessCheckDone(int answer)
395{
396 acl_checklist = NULL;
edce4d98 397 err_type page_id;
398 http_status status;
edce4d98 399 debug(85, 2) ("The request %s %s is %s, because it matched '%s'\n",
62e76326 400 RequestMethodStr[http->request->method], http->uri,
401 answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED",
402 AclMatchedName ? AclMatchedName : "NO ACL's");
f5691f9c 403 char const *proxy_auth_msg = "<null>";
404
405 if (http->getConn().getRaw() != NULL && http->getConn()->auth_user_request != NULL)
406 proxy_auth_msg = http->getConn()->auth_user_request->denyMessage("<null>");
407 else if (http->request->auth_user_request != NULL)
408 proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
62e76326 409
de31d06f 410 if (answer != ACCESS_ALLOWED) {
62e76326 411 /* Send an error */
62e76326 412 debug(85, 5) ("Access Denied: %s\n", http->uri);
413 debug(85, 5) ("AclMatchedName = %s\n",
414 AclMatchedName ? AclMatchedName : "<null>");
415 debug(85, 5) ("Proxy Auth Message = %s\n",
416 proxy_auth_msg ? proxy_auth_msg : "<null>");
417 /*
418 * NOTE: get page_id here, based on AclMatchedName because if
419 * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
420 * the clientCreateStoreEntry() call just below. Pedro Ribeiro
421 * <pribeiro@isel.pt>
422 */
423 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
424 http->logType = LOG_TCP_DENIED;
425
426 if (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName)) {
427 if (!http->flags.accel) {
428 /* Proxy authorisation needed */
429 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
430 } else {
431 /* WWW authorisation needed */
432 status = HTTP_UNAUTHORIZED;
433 }
434
435 if (page_id == ERR_NONE)
436 page_id = ERR_CACHE_ACCESS_DENIED;
437 } else {
438 status = HTTP_FORBIDDEN;
439
440 if (page_id == ERR_NONE)
441 page_id = ERR_ACCESS_DENIED;
442 }
443
de31d06f 444 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
0655fa4d 445 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
446 assert (repContext);
447 repContext->setReplyToError(page_id, status,
448 http->request->method, NULL,
a2ac85d9 449 http->getConn().getRaw() != NULL ? &http->getConn()->peer.sin_addr : &no_addr, http->request,
450 NULL, http->getConn().getRaw() != NULL
98242069 451 && http->getConn()->auth_user_request ? http->getConn()->
0655fa4d 452 auth_user_request : http->request->auth_user_request);
62e76326 453 node = (clientStreamNode *)http->client_stream.tail->data;
454 clientStreamRead(node, http, node->readBuffer);
a546b04b 455 return;
edce4d98 456 }
de31d06f 457
458 /* ACCESS_ALLOWED continues here ... */
459 safe_free(http->uri);
460
461 http->uri = xstrdup(urlCanonical(http->request));
462
463 http->doCallouts();
464}
465
466#if ICAP_CLIENT
467void
468ClientRequestContext::icapAccessCheck()
469{
470 ICAPAccessCheck *icap_access_check;
471
e8fe1384 472 icap_access_check = new ICAPAccessCheck(ICAP::methodReqmod, ICAP::pointPreCache, http->request, NULL, icapAclCheckDoneWrapper, this);
473
474 if (icap_access_check != NULL) {
de31d06f 475 icap_access_check->check();
476 return;
477 }
478
479 http->doCallouts();
480}
481
482static void
483icapAclCheckDoneWrapper(ICAPServiceRep::Pointer service, void *data)
484{
485 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
486
487 if (!calloutContext->httpStateIsValid())
488 return;
489
490 calloutContext->icapAclCheckDone(service);
491}
492
493void
494ClientRequestContext::icapAclCheckDone(ICAPServiceRep::Pointer service)
495{
496 /*
497 * No matching ICAP service in the config file
498 */
499
500 if (service == NULL) {
501 http->doCallouts();
502 return;
503 }
504
505 /*
506 * Setup ICAP state and such. If successful, just return.
507 * We'll get back to doCallouts() after REQMOD is done.
508 */
509 if (0 == http->doIcap(service))
510 return;
511
512 /*
513 * If doIcap() fails, then we have to either return an error
514 * to the user, or keep going without ICAP.
515 */
516 fatal("Fix this case in ClientRequestContext::icapAclCheckDone()");
517
518 http->doCallouts();
edce4d98 519}
520
de31d06f 521#endif
522
14cc8559 523static void
524clientRedirectAccessCheckDone(int answer, void *data)
525{
526 ClientRequestContext *context = (ClientRequestContext *)data;
59a1efb2 527 ClientHttpRequest *http = context->http;
14cc8559 528 context->acl_checklist = NULL;
529
530 if (answer == ACCESS_ALLOWED)
de31d06f 531 redirectStart(http, clientRedirectDoneWrapper, context);
14cc8559 532 else
de31d06f 533 context->clientRedirectDone(NULL);
14cc8559 534}
535
de31d06f 536void
537ClientRequestContext::clientRedirectStart()
14cc8559 538{
14cc8559 539 debug(33, 5) ("clientRedirectStart: '%s'\n", http->uri);
540
14cc8559 541 if (Config.accessList.redirector) {
de31d06f 542 acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
543 acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
14cc8559 544 } else
de31d06f 545 redirectStart(http, clientRedirectDoneWrapper, this);
14cc8559 546}
547
edce4d98 548static int
59a1efb2 549clientCachable(ClientHttpRequest * http)
edce4d98 550{
190154cf 551 HttpRequest *req = http->request;
edce4d98 552 method_t method = req->method;
62e76326 553
edce4d98 554 if (req->protocol == PROTO_HTTP)
62e76326 555 return httpCachable(method);
556
edce4d98 557 /* FTP is always cachable */
558 if (req->protocol == PROTO_WAIS)
62e76326 559 return 0;
560
69660be0 561 /*
562 * The below looks questionable: what non HTTP protocols use connect,
563 * trace, put and post? RC
edce4d98 564 */
565 if (method == METHOD_CONNECT)
62e76326 566 return 0;
567
edce4d98 568 if (method == METHOD_TRACE)
62e76326 569 return 0;
570
edce4d98 571 if (method == METHOD_PUT)
62e76326 572 return 0;
573
edce4d98 574 if (method == METHOD_POST)
a46d2c0e 575 return 0;
576
577 /* XXX POST may be cached sometimes.. ignored
578
579 * for now */
edce4d98 580 if (req->protocol == PROTO_GOPHER)
62e76326 581 return gopherCachable(req);
582
edce4d98 583 if (req->protocol == PROTO_CACHEOBJ)
62e76326 584 return 0;
585
edce4d98 586 return 1;
587}
588
589static int
59a1efb2 590clientHierarchical(ClientHttpRequest * http)
edce4d98 591{
592 const char *url = http->uri;
190154cf 593 HttpRequest *request = http->request;
edce4d98 594 method_t method = request->method;
595 const wordlist *p = NULL;
596
69660be0 597 /*
598 * IMS needs a private key, so we can use the hierarchy for IMS only if our
599 * neighbors support private keys
600 */
62e76326 601
edce4d98 602 if (request->flags.ims && !neighbors_do_private_keys)
62e76326 603 return 0;
604
69660be0 605 /*
606 * This is incorrect: authenticating requests can be sent via a hierarchy
06b97e72 607 * (they can even be cached if the correct headers are set on the reply)
edce4d98 608 */
609 if (request->flags.auth)
62e76326 610 return 0;
611
edce4d98 612 if (method == METHOD_TRACE)
62e76326 613 return 1;
614
edce4d98 615 if (method != METHOD_GET)
62e76326 616 return 0;
617
edce4d98 618 /* scan hierarchy_stoplist */
619 for (p = Config.hierarchy_stoplist; p; p = p->next)
62e76326 620 if (strstr(url, p->key))
621 return 0;
622
edce4d98 623 if (request->flags.loopdetect)
62e76326 624 return 0;
625
edce4d98 626 if (request->protocol == PROTO_HTTP)
62e76326 627 return httpCachable(method);
628
edce4d98 629 if (request->protocol == PROTO_GOPHER)
62e76326 630 return gopherCachable(request);
631
edce4d98 632 if (request->protocol == PROTO_WAIS)
62e76326 633 return 0;
634
edce4d98 635 if (request->protocol == PROTO_CACHEOBJ)
62e76326 636 return 0;
637
edce4d98 638 return 1;
639}
640
641
642static void
59a1efb2 643clientInterpretRequestHeaders(ClientHttpRequest * http)
edce4d98 644{
190154cf 645 HttpRequest *request = http->request;
edce4d98 646 const HttpHeader *req_hdr = &request->header;
647 int no_cache = 0;
a787b56a 648#if !(ESI) || defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG)
62e76326 649
edce4d98 650 const char *str;
651#endif
62e76326 652
edce4d98 653 request->imslen = -1;
654 request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE);
62e76326 655
edce4d98 656 if (request->ims > 0)
62e76326 657 request->flags.ims = 1;
658
edce4d98 659#if ESI
69660be0 660 /*
661 * We ignore Cache-Control as per the Edge Architecture Section 3. See
662 * www.esi.org for more information.
edce4d98 663 */
664#else
62e76326 665
edce4d98 666 if (httpHeaderHas(req_hdr, HDR_PRAGMA)) {
62e76326 667 String s = httpHeaderGetList(req_hdr, HDR_PRAGMA);
668
669 if (strListIsMember(&s, "no-cache", ','))
670 no_cache++;
671
672 s.clean();
edce4d98 673 }
62e76326 674
edce4d98 675 if (request->cache_control)
62e76326 676 if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
677 no_cache++;
678
69660be0 679 /*
62e76326 680 * Work around for supporting the Reload button in IE browsers when Squid
681 * is used as an accelerator or transparent proxy, by turning accelerated
682 * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
683 * actually only fixed in SP1, but we can't tell whether we are talking to
684 * SP1 or not so all 5.5 versions are treated 'normally').
685 */
edce4d98 686 if (Config.onoff.ie_refresh) {
62e76326 687 if (http->flags.accel && request->flags.ims) {
688 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT))) {
689 if (strstr(str, "MSIE 5.01") != NULL)
690 no_cache++;
691 else if (strstr(str, "MSIE 5.0") != NULL)
692 no_cache++;
693 else if (strstr(str, "MSIE 4.") != NULL)
694 no_cache++;
695 else if (strstr(str, "MSIE 3.") != NULL)
696 no_cache++;
697 }
698 }
edce4d98 699 }
62e76326 700
edce4d98 701#endif
702 if (no_cache) {
703#if HTTP_VIOLATIONS
62e76326 704
705 if (Config.onoff.reload_into_ims)
706 request->flags.nocache_hack = 1;
707 else if (refresh_nocache_hack)
708 request->flags.nocache_hack = 1;
709 else
edce4d98 710#endif
62e76326 711
712 request->flags.nocache = 1;
edce4d98 713 }
62e76326 714
edce4d98 715 /* ignore range header in non-GETs */
716 if (request->method == METHOD_GET) {
62e76326 717 request->range = httpHeaderGetRange(req_hdr);
718
719 if (request->range) {
720 request->flags.range = 1;
721 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
722 /* XXX: This is suboptimal. We should give the stream the range set,
723 * and thereby let the top of the stream set the offset when the
724 * size becomes known. As it is, we will end up requesting from 0
725 * for evey -X range specification.
726 * RBC - this may be somewhat wrong. We should probably set the range
727 * iter up at this point.
728 */
729 node->readBuffer.offset = request->range->lowestOffset(0);
730 http->range_iter.pos = request->range->begin();
731 http->range_iter.valid = true;
732 }
edce4d98 733 }
62e76326 734
edce4d98 735 if (httpHeaderHas(req_hdr, HDR_AUTHORIZATION))
62e76326 736 request->flags.auth = 1;
737
edce4d98 738 if (request->login[0] != '\0')
62e76326 739 request->flags.auth = 1;
740
edce4d98 741 if (httpHeaderHas(req_hdr, HDR_VIA)) {
62e76326 742 String s = httpHeaderGetList(req_hdr, HDR_VIA);
743 /*
744 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
745 * Note ThisCache2 has a space prepended to the hostname so we don't
746 * accidentally match super-domains.
747 */
748
749 if (strListIsSubstr(&s, ThisCache2, ',')) {
750 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
751 request, (ObjPackMethod) & httpRequestPack);
752 request->flags.loopdetect = 1;
753 }
754
edce4d98 755#if FORW_VIA_DB
62e76326 756 fvdbCountVia(s.buf());
757
edce4d98 758#endif
62e76326 759
760 s.clean();
edce4d98 761 }
62e76326 762
edce4d98 763#if USE_USERAGENT_LOG
764 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT)))
7928e475 765 logUserAgent(fqdnFromAddr(http->getConn().getRaw() ? http->getConn()->log_addr : no_addr), str);
62e76326 766
edce4d98 767#endif
768#if USE_REFERER_LOG
62e76326 769
edce4d98 770 if ((str = httpHeaderGetStr(req_hdr, HDR_REFERER)))
7928e475 771 logReferer(fqdnFromAddr(http->getConn().getRaw() ? http->getConn()->log_addr : no_addr), str, http->log_uri);
62e76326 772
edce4d98 773#endif
774#if FORW_VIA_DB
62e76326 775
edce4d98 776 if (httpHeaderHas(req_hdr, HDR_X_FORWARDED_FOR)) {
62e76326 777 String s = httpHeaderGetList(req_hdr, HDR_X_FORWARDED_FOR);
778 fvdbCountForw(s.buf());
779 s.clean();
edce4d98 780 }
62e76326 781
edce4d98 782#endif
783 if (request->method == METHOD_TRACE) {
62e76326 784 request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS);
edce4d98 785 }
62e76326 786
edce4d98 787 if (clientCachable(http))
62e76326 788 request->flags.cachable = 1;
789
edce4d98 790 if (clientHierarchical(http))
62e76326 791 request->flags.hierarchical = 1;
792
edce4d98 793 debug(85, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n",
62e76326 794 request->flags.nocache ? "SET" : "NOT SET");
795
edce4d98 796 debug(85, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n",
62e76326 797 request->flags.cachable ? "SET" : "NOT SET");
798
edce4d98 799 debug(85, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n",
62e76326 800 request->flags.hierarchical ? "SET" : "NOT SET");
edce4d98 801}
802
803void
de31d06f 804clientRedirectDoneWrapper(void *data, char *result)
edce4d98 805{
de31d06f 806 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
db02222f 807
de31d06f 808 if (!calloutContext->httpStateIsValid())
62e76326 809 return;
62e76326 810
de31d06f 811 calloutContext->clientRedirectDone(result);
812}
813
814void
815ClientRequestContext::clientRedirectDone(char *result)
816{
190154cf 817 HttpRequest *new_request = NULL;
818 HttpRequest *old_request = http->request;
edce4d98 819 debug(85, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri,
62e76326 820 result ? result : "NULL");
de31d06f 821 assert(redirect_state == REDIRECT_PENDING);
822 redirect_state = REDIRECT_DONE;
62e76326 823
edce4d98 824 if (result) {
62e76326 825 http_status status = (http_status) atoi(result);
826
827 if (status == HTTP_MOVED_PERMANENTLY
828 || status == HTTP_MOVED_TEMPORARILY
829 || status == HTTP_SEE_OTHER
830 || status == HTTP_TEMPORARY_REDIRECT) {
831 char *t = result;
832
833 if ((t = strchr(result, ':')) != NULL) {
834 http->redirect.status = status;
835 http->redirect.location = xstrdup(t + 1);
836 } else {
837 debug(85, 1) ("clientRedirectDone: bad input: %s\n", result);
838 }
2baf58c3 839 } else if (strcmp(result, http->uri))
62e76326 840 new_request = urlParse(old_request->method, result);
edce4d98 841 }
62e76326 842
edce4d98 843 if (new_request) {
62e76326 844 safe_free(http->uri);
845 http->uri = xstrdup(urlCanonical(new_request));
846 new_request->http_ver = old_request->http_ver;
847 httpHeaderAppend(&new_request->header, &old_request->header);
848 new_request->client_addr = old_request->client_addr;
47b0c1fa 849 new_request->client_port = old_request->client_port;
62e76326 850 new_request->my_addr = old_request->my_addr;
851 new_request->my_port = old_request->my_port;
852 new_request->flags = old_request->flags;
3c1f01bc 853 new_request->flags.redirected = 1;
62e76326 854
855 if (old_request->auth_user_request) {
856 new_request->auth_user_request = old_request->auth_user_request;
f5691f9c 857
858 new_request->auth_user_request->lock()
859
860 ;
62e76326 861 }
862
a2ac85d9 863 if (old_request->body_connection.getRaw() != NULL) {
62e76326 864 new_request->body_connection = old_request->body_connection;
865 old_request->body_connection = NULL;
866 }
867
868 new_request->content_length = old_request->content_length;
abb929f0 869 new_request->extacl_user = old_request->extacl_user;
870 new_request->extacl_passwd = old_request->extacl_passwd;
62e76326 871 new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
872 requestUnlink(old_request);
873 http->request = requestLink(new_request);
edce4d98 874 }
62e76326 875
edce4d98 876 /* FIXME PIPELINE: This is innacurate during pipelining */
62e76326 877
a2ac85d9 878 if (http->getConn().getRaw() != NULL)
98242069 879 fd_note(http->getConn()->fd, http->uri);
62e76326 880
c8be6d7b 881 assert(http->uri);
62e76326 882
de31d06f 883 http->doCallouts();
edce4d98 884}
885
886void
8e2745f4 887ClientRequestContext::checkNoCache()
edce4d98 888{
de31d06f 889 acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
890 acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, cbdataReference(this));
edce4d98 891}
892
de31d06f 893static void
894checkNoCacheDoneWrapper(int answer, void *data)
edce4d98 895{
de31d06f 896 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
e4a67a80 897
de31d06f 898 if (!calloutContext->httpStateIsValid())
899 return;
900
901 calloutContext->checkNoCacheDone(answer);
8e2745f4 902}
4fb35c3c 903
8e2745f4 904void
905ClientRequestContext::checkNoCacheDone(int answer)
62e76326 906{
8e2745f4 907 acl_checklist = NULL;
de31d06f 908 http->request->flags.cachable = answer;
909 http->doCallouts();
edce4d98 910}
911
69660be0 912/*
913 * Identify requests that do not go through the store and client side stream
914 * and forward them to the appropriate location. All other requests, request
915 * them.
edce4d98 916 */
917void
8e2745f4 918ClientHttpRequest::processRequest()
edce4d98 919{
edce4d98 920 debug(85, 4) ("clientProcessRequest: %s '%s'\n",
62e76326 921 RequestMethodStr[request->method], uri);
922
2baf58c3 923 if (request->method == METHOD_CONNECT && !redirect.status) {
62e76326 924 logType = LOG_TCP_MISS;
925 sslStart(this, &out.size, &al.http.code);
926 return;
edce4d98 927 }
62e76326 928
8e2745f4 929 httpStart();
930}
931
932void
933ClientHttpRequest::httpStart()
934{
935 logType = LOG_TAG_NONE;
936 debug(85, 4) ("ClientHttpRequest::httpStart: %s for '%s'\n",
62e76326 937 log_tags[logType], uri);
edce4d98 938 /* no one should have touched this */
8e2745f4 939 assert(out.offset == 0);
edce4d98 940 /* Use the Stream Luke */
8e2745f4 941 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
942 clientStreamRead(node, this, node->readBuffer);
edce4d98 943}
0655fa4d 944
945bool
946ClientHttpRequest::gotEnough() const
947{
86a2f789 948 /** TODO: should be querying the stream. */
0655fa4d 949 int contentLength =
06a5ae20 950 memObject()->getReply()->bodySize(request->method);
0655fa4d 951 assert(contentLength >= 0);
952
953 if (out.offset < contentLength)
954 return false;
955
956 return true;
957}
958
b51aec66 959void
960ClientHttpRequest::maxReplyBodySize(ssize_t clen)
961{
962 maxReplyBodySize_ = clen;
963}
964
965ssize_t
966ClientHttpRequest::maxReplyBodySize() const
967{
968 return maxReplyBodySize_;
969}
970
971bool
972ClientHttpRequest::isReplyBodyTooLarge(ssize_t clen) const
973{
974 if (0 == maxReplyBodySize())
975 return 0; /* disabled */
976
977 if (clen < 0)
978 return 0; /* unknown */
979
980 return clen > maxReplyBodySize();
981}
86a2f789 982
983void
984ClientHttpRequest::storeEntry(StoreEntry *newEntry)
985{
986 entry_ = newEntry;
987}
988
0976f8db 989void
990ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
991{
992 if (loggingEntry_)
993 storeUnlockObject(loggingEntry_);
994
995 loggingEntry_ = newEntry;
996
997 if (loggingEntry_)
998 storeLockObject(loggingEntry_);
999}
86a2f789 1000
de31d06f 1001/*
1002 * doCallouts() - This function controls the order of "callout"
1003 * executions, including non-blocking access control checks, the
1004 * redirector, and ICAP. Previously, these callouts were chained
1005 * together such that "clientAccessCheckDone()" would call
1006 * "clientRedirectStart()" and so on.
1007 *
1008 * The ClientRequestContext (aka calloutContext) class holds certain
1009 * state data for the callout/callback operations. Previously
1010 * ClientHttpRequest would sort of hand off control to ClientRequestContext
1011 * for a short time. ClientRequestContext would then delete itself
1012 * and pass control back to ClientHttpRequest when all callouts
1013 * were finished.
1014 *
1015 * This caused some problems for ICAP because we want to make the
1016 * ICAP callout after checking ACLs, but before checking the no_cache
1017 * list. We can't stuff the ICAP state into the ClientRequestContext
1018 * class because we still need the ICAP state after ClientRequestContext
1019 * goes away.
1020 *
1021 * Note that ClientRequestContext is created before the first call
1022 * to doCallouts().
1023 *
1024 * If one of the callouts notices that ClientHttpRequest is no
1025 * longer valid, it should call cbdataReferenceDone() so that
1026 * ClientHttpRequest's reference count goes to zero and it will get
1027 * deleted. ClientHttpRequest will then delete ClientRequestContext.
1028 *
1029 * Note that we set the _done flags here before actually starting
1030 * the callout. This is strictly for convenience.
1031 */
1032
1033void
1034ClientHttpRequest::doCallouts()
1035{
1036 assert(calloutContext);
1037
1038 if (!calloutContext->http_access_done) {
1039 calloutContext->http_access_done = 1;
1040 calloutContext->clientAccessCheck();
1041 return;
1042 }
1043
1044#if ICAP_CLIENT
ab7ac359 1045 if (TheICAPConfig.onoff && !calloutContext->icap_acl_check_done) {
de31d06f 1046 calloutContext->icap_acl_check_done = 1;
1047 calloutContext->icapAccessCheck();
1048 return;
1049 }
1050
1051#endif
1052
1053 if (!calloutContext->redirect_done) {
1054 calloutContext->redirect_done = 1;
1055 assert(calloutContext->redirect_state == REDIRECT_NONE);
1056
1057 if (Config.Program.redirect) {
1058 calloutContext->redirect_state = REDIRECT_PENDING;
1059 calloutContext->clientRedirectStart();
1060 return;
1061 }
1062 }
1063
1064 if (!calloutContext->no_cache_done) {
1065 calloutContext->no_cache_done = 1;
1066
1067 if (Config.accessList.noCache && request->flags.cachable) {
1068 calloutContext->checkNoCache();
1069 return;
1070 }
1071 }
1072
1073 cbdataReferenceDone(calloutContext->http);
1074 delete calloutContext;
1075 calloutContext = NULL;
1076 clientInterpretRequestHeaders(this);
1077#if HEADERS_LOG
1078
1079 headersLog(0, 1, request->method, request);
1080#endif
1081
1082 processRequest();
1083}
1084
86a2f789 1085#ifndef _USE_INLINE_
1086#include "client_side_request.cci"
1087#endif
de31d06f 1088
1089#if ICAP_CLIENT
1090/*
1091 * Initiate an ICAP transaction. Return 0 if all is well, or -1 upon error.
1092 * Caller will handle error condition by generating a Squid error message
1093 * or take other action.
1094 */
1095int
1096ClientHttpRequest::doIcap(ICAPServiceRep::Pointer service)
1097{
1098 debug(85,3)("ClientHttpRequest::doIcap() called\n");
1099 assert(NULL == icap);
1100 icap = new ICAPClientReqmodPrecache(service);
1101 (void) cbdataReference(icap);
1102 icap->startReqMod(this, request);
1103 icap->doneSending();
1104 return 0;
1105}
1106
1107/*
1108 * Called by ICAPAnchor when it has space available for us.
1109 */
1110void
1111ClientHttpRequest::icapSpaceAvailable()
1112{
1113 debug(85,3)("ClientHttpRequest::icapSpaceAvailable() called\n");
1114}
1115
1116void
1117ClientHttpRequest::takeAdaptedHeaders(HttpMsg *msg)
1118{
1119 debug(85,3)("ClientHttpRequest::takeAdaptedHeaders() called\n");
1120 assert(cbdataReferenceValid(this)); // indicates bug
1121
1122 HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg);
1123 assert(new_req);
1124 /*
1125 * Replace the old request with the new request. First,
1126 * Move the "body_connection" over, then unlink old and
1127 * link new to the http state.
1128 */
1129 new_req->body_connection = request->body_connection;
1130 request->body_connection = NULL;
1131 requestUnlink(request);
1132 request = requestLink(new_req);
1133 /*
1134 * Store the new URI for logging
1135 */
1136 xfree(uri);
1137 uri = xstrdup(urlCanonical(request));
1138 setLogUri(this, urlCanonicalClean(request));
1139 assert(request->method);
1140
1141 doCallouts();
1142
1143 debug(85,3)("ClientHttpRequest::takeAdaptedHeaders() finished\n");
1144}
1145
1146void
1147ClientHttpRequest::takeAdaptedBody(MemBuf *buf)
1148{
1149 debug(85,3)("ClientHttpRequest::takeAdaptedBody() called\n");
1150}
1151
1152void
1153ClientHttpRequest::doneAdapting()
1154{
1155 debug(85,3)("ClientHttpRequest::doneAdapting() called\n");
1156}
1157
1158void
1159ClientHttpRequest::abortAdapting()
1160{
1161 debug(85,3)("ClientHttpRequest::abortAdapting() called\n");
1162
1163 if ((NULL == storeEntry()) || storeEntry()->isEmpty()) {
1164 debug(85,3)("WARNING: ICAP REQMOD callout failed, proceeding with original request\n");
1165 doCallouts();
1166#if ICAP_HARD_ERROR
1167
1168 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1169 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1170 assert (repContext);
1171 // Note if this code is ever used, clientBuildError() should be modified to
1172 // accept an errno arg
1173 repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
1174 request->method, NULL,
1175 getConn().getRaw() != NULL ? &getConn()->peer.sin_addr : &no_addr, request,
1176 NULL, getConn().getRaw() != NULL
1177 && getConn()->auth_user_request ? getConn()->
1178 auth_user_request : request->auth_user_request, errno);
1179 node = (clientStreamNode *)client_stream.tail->data;
1180 clientStreamRead(node, this, node->readBuffer);
1181#endif
1182
1183 return;
1184 }
1185
1186 debug(0,0)("write me at %s:%d\n", __FILE__,__LINE__);
1187}
1188
1189#endif