]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
Document the 'carp' cache_peer option
[thirdparty/squid.git] / src / client_side_request.cc
CommitLineData
edce4d98 1
2/*
3f38a55e 3 * $Id: client_side_request.cc,v 1.17 2003/02/21 19:53:01 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"
e6ccf245 47#include "authenticate.h"
528b2c61 48#include "HttpRequest.h"
8000a965 49#include "ACLChecklist.h"
50#include "ACL.h"
edce4d98 51
52#if LINGERING_CLOSE
53#define comm_close comm_lingering_close
54#endif
55
56static const char *const crlf = "\r\n";
57
8e2745f4 58class ClientRequestContext {
59 public:
60 void *operator new(size_t);
61 void operator delete(void *);
62 void deleteSelf() const;
63
64 ClientRequestContext();
65 ClientRequestContext(ClientHttpRequest *);
66 ~ClientRequestContext();
67
68 void checkNoCache();
69
4fb35c3c 70 ACLChecklist *acl_checklist; /* need ptr back so we can unreg if needed */
edce4d98 71 int redirect_state;
72 clientHttpRequest *http;
8e2745f4 73 private:
74 CBDATA_CLASS(ClientRequestContext);
75 static void CheckNoCacheDone(int answer, void *data);
76 void checkNoCacheDone(int answer);
77};
edce4d98 78
8e2745f4 79CBDATA_CLASS_INIT(ClientRequestContext);
80
81void *
82ClientRequestContext::operator new (size_t size)
83{
84 assert (size == sizeof(ClientRequestContext));
85 CBDATA_INIT_TYPE(ClientRequestContext);
86 ClientRequestContext *result = cbdataAlloc(ClientRequestContext);
87 /* Mark result as being owned - we want the refcounter to do the delete
88 * call */
89 cbdataReference(result);
90 return result;
91}
92
93void
94ClientRequestContext::operator delete (void *address)
95{
96 ClientRequestContext *t = static_cast<ClientRequestContext *>(address);
97 cbdataFree(address);
98 /* And allow the memory to be freed */
99 cbdataReferenceDone (t);
100}
101
102void
103ClientRequestContext::deleteSelf() const
104{
105 delete this;
106}
edce4d98 107
108/* Local functions */
edce4d98 109/* other */
edce4d98 110static void clientAccessCheckDone(int, void *);
edce4d98 111static int clientCachable(clientHttpRequest * http);
112static int clientHierarchical(clientHttpRequest * http);
113static void clientInterpretRequestHeaders(clientHttpRequest * http);
114static RH clientRedirectDone;
e6ccf245 115extern "C" CSR clientGetMoreData;
116extern "C" CSS clientReplyStatus;
117extern "C" CSD clientReplyDetach;
528b2c61 118static void checkFailureRatio(err_type, hier_code);
edce4d98 119
8e2745f4 120ClientRequestContext::~ClientRequestContext()
121{
122 if (http)
123 cbdataReferenceDone(http);
124 if (acl_checklist)
8000a965 125 delete acl_checklist;
8e2745f4 126}
127
128ClientRequestContext::ClientRequestContext() : acl_checklist (NULL), redirect_state (REDIRECT_NONE), http(NULL)
edce4d98 129{
edce4d98 130}
131
8e2745f4 132ClientRequestContext::ClientRequestContext(ClientHttpRequest *newHttp) : acl_checklist (NULL), redirect_state (REDIRECT_NONE), http(cbdataReference(newHttp))
edce4d98 133{
8e2745f4 134 assert (newHttp != NULL);
edce4d98 135}
136
528b2c61 137CBDATA_CLASS_INIT(ClientHttpRequest);
8e2745f4 138
528b2c61 139void *
140ClientHttpRequest::operator new (size_t size)
141{
142 assert (size == sizeof (ClientHttpRequest));
143 CBDATA_INIT_TYPE(ClientHttpRequest);
144 ClientHttpRequest *result = cbdataAlloc(ClientHttpRequest);
145 /* Mark result as being owned - we want the refcounter to do the delete
146 * call */
147 cbdataReference(result);
148 return result;
149}
150
151void
152ClientHttpRequest::operator delete (void *address)
153{
154 ClientHttpRequest *temp = static_cast<ClientHttpRequest *>(address);
155 cbdataFree(address);
156 /* And allow the memory to be freed */
157 cbdataReferenceDone (temp);
158}
159
160void
161ClientHttpRequest::deleteSelf() const
162{
163 delete this;
164}
165
166ClientHttpRequest::ClientHttpRequest()
167{
168 /* reset range iterator */
169 start = current_time;
170}
171
172/*
173 * This function is designed to serve a fairly specific purpose.
174 * Occasionally our vBNS-connected caches can talk to each other, but not
175 * the rest of the world. Here we try to detect frequent failures which
176 * make the cache unusable (e.g. DNS lookup and connect() failures). If
177 * the failure:success ratio goes above 1.0 then we go into "hit only"
178 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
179 * will only fetch HITs from us if they are using the ICP protocol. We
180 * stay in this mode for 5 minutes.
181 *
182 * Duane W., Sept 16, 1996
183 */
184
185#define FAILURE_MODE_TIME 300
186
187static void
188checkFailureRatio(err_type etype, hier_code hcode)
189{
190 static double magic_factor = 100.0;
191 double n_good;
192 double n_bad;
193 if (hcode == HIER_NONE)
194 return;
195 n_good = magic_factor / (1.0 + request_failure_ratio);
196 n_bad = magic_factor - n_good;
197 switch (etype) {
198 case ERR_DNS_FAIL:
199 case ERR_CONNECT_FAIL:
200 case ERR_READ_ERROR:
201 n_bad++;
202 break;
203 default:
204 n_good++;
205 }
206 request_failure_ratio = n_bad / n_good;
207 if (hit_only_mode_until > squid_curtime)
208 return;
209 if (request_failure_ratio < 1.0)
210 return;
211 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio);
212 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
213 FAILURE_MODE_TIME / 60);
214 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
215 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
216}
217
218ClientHttpRequest::~ClientHttpRequest()
219{
220 debug(33, 3) ("httpRequestFree: %s\n", uri);
221 /* if body_connection !NULL, then ProcessBody has not
222 * found the end of the body yet
223 */
224 if (request && request->body_connection)
225 clientAbortBody(request); /* abort body transter */
226 /* the ICP check here was erroneous
227 * - storeReleaseRequest was always called if entry was valid
228 */
229 assert(logType < LOG_TYPE_MAX);
230 logRequest();
231 if (request)
232 checkFailureRatio(request->errType, al.hier.code);
233 freeResources();
234 /* moving to the next connection is handled by the context free */
235 dlinkDelete(&active, &ClientActiveRequests);
236}
237
edce4d98 238/* Create a request and kick it off */
69660be0 239/*
240 * TODO: Pass in the buffers to be used in the inital Read request, as they are
241 * determined by the user
edce4d98 242 */
243int /* returns nonzero on failure */
244clientBeginRequest(method_t method, char const *url, CSCB * streamcallback,
245 CSD * streamdetach, void *streamdata, HttpHeader const *header,
246 char *tailbuf, size_t taillen)
247{
248 size_t url_sz;
249 http_version_t http_ver =
250 {1, 0};
528b2c61 251 clientHttpRequest *http = new ClientHttpRequest;
edce4d98 252 request_t *request;
528b2c61 253 StoreIOBuffer tempBuffer;
edce4d98 254 http->http_ver = http_ver;
255 http->conn = NULL;
256 http->start = current_time;
257 /* this is only used to adjust the connection offset in client_side.c */
258 http->req_sz = 0;
c8be6d7b 259 tempBuffer.length = taillen;
260 tempBuffer.data = tailbuf;
edce4d98 261 /* client stream setup */
262 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
263 clientReplyStatus, clientReplyNewContext(http), streamcallback,
c8be6d7b 264 streamdetach, streamdata, tempBuffer);
edce4d98 265 /* make it visible in the 'current acctive requests list' */
266 dlinkAdd(http, &http->active, &ClientActiveRequests);
267 /* Set flags */
69660be0 268 http->flags.accel = 1; /* internal requests only makes sense in an
edce4d98 269 * accelerator today. TODO: accept flags ? */
270 /* allow size for url rewriting */
271 url_sz = strlen(url) + Config.appendDomainLen + 5;
e6ccf245 272 http->uri = (char *)xcalloc(url_sz, 1);
edce4d98 273 strcpy(http->uri, url);
274
275 if ((request = urlParse(method, http->uri)) == NULL) {
276 debug(85, 5) ("Invalid URL: %s\n", http->uri);
277 return -1;
278 }
69660be0 279 /*
280 * now update the headers in request with our supplied headers. urLParse
281 * should return a blank header set, but we use Update to be sure of
282 * correctness.
edce4d98 283 */
284 if (header)
285 httpHeaderUpdate(&request->header, header, NULL);
286 http->log_uri = xstrdup(urlCanonicalClean(request));
287 /* http struct now ready */
288
69660be0 289 /*
290 * build new header list *? TODO
edce4d98 291 */
292 request->flags.accelerated = http->flags.accel;
69660be0 293 request->flags.internalclient = 1; /* this is an internally created
294 * request, not subject to acceleration
295 * target overrides */
296 /*
297 * FIXME? Do we want to detect and handle internal requests of internal
298 * objects ?
299 */
edce4d98 300
301 /* Internally created requests cannot have bodies today */
302 request->content_length = 0;
303 request->client_addr = no_addr;
304 request->my_addr = no_addr; /* undefined for internal requests */
305 request->my_port = 0;
306 request->http_ver = http_ver;
307 http->request = requestLink(request);
308
309 /* optional - skip the access check ? */
310 clientAccessCheck(http);
311 return 0;
312}
313
edce4d98 314/* This is the entry point for external users of the client_side routines */
315void
8e2745f4 316clientAccessCheck(ClientHttpRequest *http)
edce4d98 317{
8e2745f4 318 ClientRequestContext *context = new ClientRequestContext(http);
edce4d98 319 context->acl_checklist =
320 clientAclChecklistCreate(Config.accessList.http, http);
225b7b10 321 context->acl_checklist->nonBlockingCheck(clientAccessCheckDone, context);
edce4d98 322}
323
324void
325clientAccessCheckDone(int answer, void *data)
326{
8e2745f4 327 ClientRequestContext *context = (ClientRequestContext *)data;
fbade053 328
329 clientHttpRequest *http_ = context->http;
330
331 if (!cbdataReferenceValid (http_)) {
332 context->deleteSelf();
333 return;
334 }
335
edce4d98 336 clientHttpRequest *http = context->http;
337 err_type page_id;
338 http_status status;
e6ccf245 339 char const *proxy_auth_msg = NULL;
edce4d98 340 debug(85, 2) ("The request %s %s is %s, because it matched '%s'\n",
341 RequestMethodStr[http->request->method], http->uri,
342 answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED",
343 AclMatchedName ? AclMatchedName : "NO ACL's");
344 proxy_auth_msg = authenticateAuthUserRequestMessage((http->conn
345 && http->conn->auth_user_request) ? http->conn->
346 auth_user_request : http->request->auth_user_request);
347 context->acl_checklist = NULL;
348 if (answer == ACCESS_ALLOWED) {
349 safe_free(http->uri);
350 http->uri = xstrdup(urlCanonical(http->request));
351 assert(context->redirect_state == REDIRECT_NONE);
352 context->redirect_state = REDIRECT_PENDING;
353 redirectStart(http, clientRedirectDone, context);
354 } else {
355 /* Send an error */
e6ccf245 356 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
8e2745f4 357 context->deleteSelf();
edce4d98 358 debug(85, 5) ("Access Denied: %s\n", http->uri);
359 debug(85, 5) ("AclMatchedName = %s\n",
360 AclMatchedName ? AclMatchedName : "<null>");
361 debug(85, 5) ("Proxy Auth Message = %s\n",
362 proxy_auth_msg ? proxy_auth_msg : "<null>");
363 /*
69660be0 364 * NOTE: get page_id here, based on AclMatchedName because if
365 * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
366 * the clientCreateStoreEntry() call just below. Pedro Ribeiro
367 * <pribeiro@isel.pt>
edce4d98 368 */
369 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
29b8d8d6 370 http->logType = LOG_TCP_DENIED;
edce4d98 371 if (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName)) {
372 if (!http->flags.accel) {
373 /* Proxy authorisation needed */
374 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
375 } else {
376 /* WWW authorisation needed */
377 status = HTTP_UNAUTHORIZED;
378 }
379 if (page_id == ERR_NONE)
380 page_id = ERR_CACHE_ACCESS_DENIED;
381 } else {
382 status = HTTP_FORBIDDEN;
383 if (page_id == ERR_NONE)
384 page_id = ERR_ACCESS_DENIED;
385 }
386 clientSetReplyToError(node->data, page_id, status,
387 http->request->method, NULL,
388 http->conn ? &http->conn->peer.sin_addr : &no_addr, http->request,
389 NULL, http->conn
390 && http->conn->auth_user_request ? http->conn->
391 auth_user_request : http->request->auth_user_request);
e6ccf245 392 node = (clientStreamNode *)http->client_stream.tail->data;
c8be6d7b 393 clientStreamRead(node, http, node->readBuffer);
edce4d98 394 }
395}
396
397static int
398clientCachable(clientHttpRequest * http)
399{
400 request_t *req = http->request;
401 method_t method = req->method;
402 if (req->protocol == PROTO_HTTP)
403 return httpCachable(method);
404 /* FTP is always cachable */
405 if (req->protocol == PROTO_WAIS)
406 return 0;
69660be0 407 /*
408 * The below looks questionable: what non HTTP protocols use connect,
409 * trace, put and post? RC
edce4d98 410 */
411 if (method == METHOD_CONNECT)
412 return 0;
413 if (method == METHOD_TRACE)
414 return 0;
415 if (method == METHOD_PUT)
416 return 0;
417 if (method == METHOD_POST)
69660be0 418 return 0; /* XXX POST may be cached sometimes.. ignored
419 * for now */
edce4d98 420 if (req->protocol == PROTO_GOPHER)
421 return gopherCachable(req);
422 if (req->protocol == PROTO_CACHEOBJ)
423 return 0;
424 return 1;
425}
426
427static int
428clientHierarchical(clientHttpRequest * http)
429{
430 const char *url = http->uri;
431 request_t *request = http->request;
432 method_t method = request->method;
433 const wordlist *p = NULL;
434
69660be0 435 /*
436 * IMS needs a private key, so we can use the hierarchy for IMS only if our
437 * neighbors support private keys
438 */
edce4d98 439 if (request->flags.ims && !neighbors_do_private_keys)
440 return 0;
69660be0 441 /*
442 * This is incorrect: authenticating requests can be sent via a hierarchy
443 * (they can even be cached if the correct headers are set on the reply
edce4d98 444 */
445 if (request->flags.auth)
446 return 0;
447 if (method == METHOD_TRACE)
448 return 1;
449 if (method != METHOD_GET)
450 return 0;
451 /* scan hierarchy_stoplist */
452 for (p = Config.hierarchy_stoplist; p; p = p->next)
453 if (strstr(url, p->key))
454 return 0;
455 if (request->flags.loopdetect)
456 return 0;
457 if (request->protocol == PROTO_HTTP)
458 return httpCachable(method);
459 if (request->protocol == PROTO_GOPHER)
460 return gopherCachable(request);
461 if (request->protocol == PROTO_WAIS)
462 return 0;
463 if (request->protocol == PROTO_CACHEOBJ)
464 return 0;
465 return 1;
466}
467
468
469static void
470clientInterpretRequestHeaders(clientHttpRequest * http)
471{
472 request_t *request = http->request;
473 const HttpHeader *req_hdr = &request->header;
474 int no_cache = 0;
475#if !defined(ESI) || defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG)
476 const char *str;
477#endif
478 request->imslen = -1;
479 request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE);
480 if (request->ims > 0)
481 request->flags.ims = 1;
482#if ESI
69660be0 483 /*
484 * We ignore Cache-Control as per the Edge Architecture Section 3. See
485 * www.esi.org for more information.
edce4d98 486 */
487#else
488 if (httpHeaderHas(req_hdr, HDR_PRAGMA)) {
489 String s = httpHeaderGetList(req_hdr, HDR_PRAGMA);
490 if (strListIsMember(&s, "no-cache", ','))
491 no_cache++;
528b2c61 492 s.clean();
edce4d98 493 }
494 request->cache_control = httpHeaderGetCc(req_hdr);
495 if (request->cache_control)
496 if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
497 no_cache++;
69660be0 498 /*
499 * Work around for supporting the Reload button in IE browsers when Squid
500 * is used as an accelerator or transparent proxy, by turning accelerated
501 * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
502 * actually only fixed in SP1, but we can't tell whether we are talking to
503 * SP1 or not so all 5.5 versions are treated 'normally').
edce4d98 504 */
505 if (Config.onoff.ie_refresh) {
506 if (http->flags.accel && request->flags.ims) {
507 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT))) {
508 if (strstr(str, "MSIE 5.01") != NULL)
509 no_cache++;
510 else if (strstr(str, "MSIE 5.0") != NULL)
511 no_cache++;
512 else if (strstr(str, "MSIE 4.") != NULL)
513 no_cache++;
514 else if (strstr(str, "MSIE 3.") != NULL)
515 no_cache++;
516 }
517 }
518 }
519#endif
520 if (no_cache) {
521#if HTTP_VIOLATIONS
522 if (Config.onoff.reload_into_ims)
523 request->flags.nocache_hack = 1;
524 else if (refresh_nocache_hack)
525 request->flags.nocache_hack = 1;
526 else
527#endif
528 request->flags.nocache = 1;
529 }
530 /* ignore range header in non-GETs */
531 if (request->method == METHOD_GET) {
edce4d98 532 request->range = httpHeaderGetRange(req_hdr);
533 if (request->range) {
534 request->flags.range = 1;
528b2c61 535 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
536 /* XXX: This is suboptimal. We should give the stream the range set,
537 * and thereby let the top of the stream set the offset when the
538 * size becomes known. As it is, we will end up requesting from 0
539 * for evey -X range specification.
540 * RBC - this may be somewhat wrong. We should probably set the range
541 * iter up at this point.
542 */
543 node->readBuffer.offset = request->range->lowestOffset(0);
544 http->range_iter.pos = request->range->begin();
545 http->range_iter.valid = true;
edce4d98 546 }
547 }
548 if (httpHeaderHas(req_hdr, HDR_AUTHORIZATION))
549 request->flags.auth = 1;
550 if (request->login[0] != '\0')
551 request->flags.auth = 1;
552 if (httpHeaderHas(req_hdr, HDR_VIA)) {
553 String s = httpHeaderGetList(req_hdr, HDR_VIA);
554 /*
555 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
556 * Note ThisCache2 has a space prepended to the hostname so we don't
557 * accidentally match super-domains.
558 */
559 if (strListIsSubstr(&s, ThisCache2, ',')) {
560 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
561 request, (ObjPackMethod) & httpRequestPack);
562 request->flags.loopdetect = 1;
563 }
564#if FORW_VIA_DB
528b2c61 565 fvdbCountVia(s.buf());
edce4d98 566#endif
528b2c61 567 s.clean();
edce4d98 568 }
569#if USE_USERAGENT_LOG
570 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT)))
ff1e79fd 571 logUserAgent(fqdnFromAddr(http->conn ? http->conn->log_addr : no_addr), str);
edce4d98 572#endif
573#if USE_REFERER_LOG
574 if ((str = httpHeaderGetStr(req_hdr, HDR_REFERER)))
ff1e79fd 575 logReferer(fqdnFromAddr(http->conn ? http->conn->log_addr : no_addr), str, http->log_uri);
edce4d98 576#endif
577#if FORW_VIA_DB
578 if (httpHeaderHas(req_hdr, HDR_X_FORWARDED_FOR)) {
579 String s = httpHeaderGetList(req_hdr, HDR_X_FORWARDED_FOR);
528b2c61 580 fvdbCountForw(s.buf());
581 s.clean();
edce4d98 582 }
583#endif
584 if (request->method == METHOD_TRACE) {
585 request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS);
586 }
587 if (clientCachable(http))
588 request->flags.cachable = 1;
589 if (clientHierarchical(http))
590 request->flags.hierarchical = 1;
591 debug(85, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n",
592 request->flags.nocache ? "SET" : "NOT SET");
593 debug(85, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n",
594 request->flags.cachable ? "SET" : "NOT SET");
595 debug(85, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n",
596 request->flags.hierarchical ? "SET" : "NOT SET");
597}
598
599void
600clientRedirectDone(void *data, char *result)
601{
8e2745f4 602 ClientRequestContext *context = (ClientRequestContext *)data;
db02222f 603 clientHttpRequest *http_ = context->http;
604
605 if (!cbdataReferenceValid (http_)) {
606 context->deleteSelf();
607 return;
608 }
609
edce4d98 610 clientHttpRequest *http = context->http;
611 request_t *new_request = NULL;
612 request_t *old_request = http->request;
613 debug(85, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri,
614 result ? result : "NULL");
615 assert(context->redirect_state == REDIRECT_PENDING);
616 context->redirect_state = REDIRECT_DONE;
617 if (result) {
618 http_status status = (http_status) atoi(result);
619 if (status == HTTP_MOVED_PERMANENTLY
69660be0 620 || status == HTTP_MOVED_TEMPORARILY
621 || status == HTTP_SEE_OTHER
622 || status == HTTP_TEMPORARY_REDIRECT) {
edce4d98 623 char *t = result;
624 if ((t = strchr(result, ':')) != NULL) {
625 http->redirect.status = status;
626 http->redirect.location = xstrdup(t + 1);
627 } else {
628 debug(85, 1) ("clientRedirectDone: bad input: %s\n", result);
629 }
630 }
631 if (strcmp(result, http->uri))
632 new_request = urlParse(old_request->method, result);
633 }
634 if (new_request) {
635 safe_free(http->uri);
636 http->uri = xstrdup(urlCanonical(new_request));
637 new_request->http_ver = old_request->http_ver;
638 httpHeaderAppend(&new_request->header, &old_request->header);
639 new_request->client_addr = old_request->client_addr;
640 new_request->my_addr = old_request->my_addr;
641 new_request->my_port = old_request->my_port;
3f38a55e 642 new_request->flags = old_request->flags;
edce4d98 643 if (old_request->auth_user_request) {
644 new_request->auth_user_request = old_request->auth_user_request;
645 authenticateAuthUserRequestLock(new_request->auth_user_request);
646 }
647 if (old_request->body_connection) {
648 new_request->body_connection = old_request->body_connection;
649 old_request->body_connection = NULL;
650 }
651 new_request->content_length = old_request->content_length;
652 new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
653 requestUnlink(old_request);
654 http->request = requestLink(new_request);
655 }
656 clientInterpretRequestHeaders(http);
657#if HEADERS_LOG
658 headersLog(0, 1, request->method, request);
659#endif
660 /* FIXME PIPELINE: This is innacurate during pipelining */
661 if (http->conn)
662 fd_note(http->conn->fd, http->uri);
c8be6d7b 663 assert(http->uri);
8e2745f4 664 context->checkNoCache();
edce4d98 665}
666
667void
8e2745f4 668ClientRequestContext::checkNoCache()
edce4d98 669{
edce4d98 670 if (Config.accessList.noCache && http->request->flags.cachable) {
8e2745f4 671 acl_checklist =
edce4d98 672 clientAclChecklistCreate(Config.accessList.noCache, http);
225b7b10 673 acl_checklist->nonBlockingCheck(CheckNoCacheDone, cbdataReference(this));
edce4d98 674 } else {
8e2745f4 675 CheckNoCacheDone(http->request->flags.cachable, cbdataReference(this));
edce4d98 676 }
677}
678
679void
8e2745f4 680ClientRequestContext::CheckNoCacheDone(int answer, void *data)
edce4d98 681{
4fb35c3c 682 void *temp;
683 bool valid = cbdataReferenceValidDone(data, &temp);
8e2745f4 684 /* acl NB calls cannot invalidate cbdata in the normal course of things */
4fb35c3c 685 assert (valid);
8e2745f4 686 ClientRequestContext *context = (ClientRequestContext *)temp;
687 context->checkNoCacheDone(answer);
688}
4fb35c3c 689
8e2745f4 690void
691ClientRequestContext::checkNoCacheDone(int answer)
692{
693 acl_checklist = NULL;
694 clientHttpRequest *http_ = http;
8e2745f4 695
3b1b4c07 696 if (!cbdataReferenceValid (http_)) {
697 deleteSelf();
4fb35c3c 698 return;
3b1b4c07 699 }
4fb35c3c 700
3b1b4c07 701 deleteSelf();
8e2745f4 702 http_->request->flags.cachable = answer;
703 http_->processRequest();
edce4d98 704}
705
69660be0 706/*
707 * Identify requests that do not go through the store and client side stream
708 * and forward them to the appropriate location. All other requests, request
709 * them.
edce4d98 710 */
711void
8e2745f4 712ClientHttpRequest::processRequest()
edce4d98 713{
edce4d98 714 debug(85, 4) ("clientProcessRequest: %s '%s'\n",
8e2745f4 715 RequestMethodStr[request->method], uri);
716 if (request->method == METHOD_CONNECT) {
717 logType = LOG_TCP_MISS;
718 sslStart(this, &out.size, &al.http.code);
edce4d98 719 return;
edce4d98 720 }
8e2745f4 721 httpStart();
722}
723
724void
725ClientHttpRequest::httpStart()
726{
727 logType = LOG_TAG_NONE;
728 debug(85, 4) ("ClientHttpRequest::httpStart: %s for '%s'\n",
729 log_tags[logType], uri);
edce4d98 730 /* no one should have touched this */
8e2745f4 731 assert(out.offset == 0);
edce4d98 732 /* Use the Stream Luke */
8e2745f4 733 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
734 clientStreamRead(node, this, node->readBuffer);
edce4d98 735}