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