]>
Commit | Line | Data |
---|---|---|
edce4d98 | 1 | |
2 | /* | |
69660be0 | 3 | * $Id: client_side_request.cc,v 1.4 2002/09/23 03:59:15 wessels Exp $ |
4 | * | |
5 | * DEBUG: section 85 Client-side Request Routines AUTHOR: Robert Collins | |
6 | * (Originally Duane Wessels in client_side.c) | |
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" | |
45 | ||
46 | #if LINGERING_CLOSE | |
47 | #define comm_close comm_lingering_close | |
48 | #endif | |
49 | ||
50 | static const char *const crlf = "\r\n"; | |
51 | ||
52 | typedef struct _clientRequestContext { | |
53 | aclCheck_t *acl_checklist; /* need ptr back so we can unreg if needed */ | |
54 | int redirect_state; | |
55 | clientHttpRequest *http; | |
56 | } clientRequestContext; | |
57 | ||
58 | CBDATA_TYPE(clientRequestContext); | |
59 | ||
60 | /* Local functions */ | |
61 | /* clientRequestContext */ | |
62 | clientRequestContext *clientRequestContextNew(clientHttpRequest *); | |
63 | FREE clientRequestContextFree; | |
64 | /* other */ | |
65 | static int checkAccelOnly(clientHttpRequest *); | |
66 | static void clientAccessCheckDone(int, void *); | |
69660be0 | 67 | /* static */ aclCheck_t * |
68 | clientAclChecklistCreate(const acl_access * acl, | |
edce4d98 | 69 | const clientHttpRequest * http); |
70 | static int clientCachable(clientHttpRequest * http); | |
71 | static int clientHierarchical(clientHttpRequest * http); | |
72 | static void clientInterpretRequestHeaders(clientHttpRequest * http); | |
73 | static RH clientRedirectDone; | |
74 | static void clientCheckNoCache(clientRequestContext * context); | |
75 | static void clientCheckNoCacheDone(int answer, void *data); | |
76 | void clientProcessRequest(clientHttpRequest *); | |
77 | extern CSR clientGetMoreData; | |
78 | extern CSS clientReplyStatus; | |
79 | extern CSD clientReplyDetach; | |
80 | ||
81 | void | |
82 | clientRequestContextFree(void *data) | |
83 | { | |
84 | clientRequestContext *context = data; | |
85 | cbdataReferenceDone(context->http); | |
86 | if (context->acl_checklist) | |
87 | aclChecklistFree(context->acl_checklist); | |
88 | } | |
89 | ||
90 | clientRequestContext * | |
91 | clientRequestContextNew(clientHttpRequest * http) | |
92 | { | |
93 | clientRequestContext *rv; | |
94 | assert(http != NULL); | |
95 | CBDATA_INIT_TYPE_FREECB(clientRequestContext, clientRequestContextFree); | |
96 | rv = cbdataAlloc(clientRequestContext); | |
97 | rv->http = cbdataReference(http); | |
98 | return rv; | |
99 | } | |
100 | ||
101 | /* Create a request and kick it off */ | |
69660be0 | 102 | /* |
103 | * TODO: Pass in the buffers to be used in the inital Read request, as they are | |
104 | * determined by the user | |
edce4d98 | 105 | */ |
106 | int /* returns nonzero on failure */ | |
107 | clientBeginRequest(method_t method, char const *url, CSCB * streamcallback, | |
108 | CSD * streamdetach, void *streamdata, HttpHeader const *header, | |
109 | char *tailbuf, size_t taillen) | |
110 | { | |
111 | size_t url_sz; | |
112 | http_version_t http_ver = | |
113 | {1, 0}; | |
114 | clientHttpRequest *http = cbdataAlloc(clientHttpRequest); | |
115 | request_t *request; | |
116 | http->http_ver = http_ver; | |
117 | http->conn = NULL; | |
118 | http->start = current_time; | |
119 | /* this is only used to adjust the connection offset in client_side.c */ | |
120 | http->req_sz = 0; | |
121 | /* client stream setup */ | |
122 | clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach, | |
123 | clientReplyStatus, clientReplyNewContext(http), streamcallback, | |
124 | streamdetach, streamdata, tailbuf, taillen); | |
125 | /* make it visible in the 'current acctive requests list' */ | |
126 | dlinkAdd(http, &http->active, &ClientActiveRequests); | |
127 | /* Set flags */ | |
69660be0 | 128 | http->flags.accel = 1; /* internal requests only makes sense in an |
edce4d98 | 129 | * accelerator today. TODO: accept flags ? */ |
130 | /* allow size for url rewriting */ | |
131 | url_sz = strlen(url) + Config.appendDomainLen + 5; | |
132 | http->uri = xcalloc(url_sz, 1); | |
133 | strcpy(http->uri, url); | |
134 | ||
135 | if ((request = urlParse(method, http->uri)) == NULL) { | |
136 | debug(85, 5) ("Invalid URL: %s\n", http->uri); | |
137 | return -1; | |
138 | } | |
69660be0 | 139 | /* |
140 | * now update the headers in request with our supplied headers. urLParse | |
141 | * should return a blank header set, but we use Update to be sure of | |
142 | * correctness. | |
edce4d98 | 143 | */ |
144 | if (header) | |
145 | httpHeaderUpdate(&request->header, header, NULL); | |
146 | http->log_uri = xstrdup(urlCanonicalClean(request)); | |
147 | /* http struct now ready */ | |
148 | ||
69660be0 | 149 | /* |
150 | * build new header list *? TODO | |
edce4d98 | 151 | */ |
152 | request->flags.accelerated = http->flags.accel; | |
69660be0 | 153 | request->flags.internalclient = 1; /* this is an internally created |
154 | * request, not subject to acceleration | |
155 | * target overrides */ | |
156 | /* | |
157 | * FIXME? Do we want to detect and handle internal requests of internal | |
158 | * objects ? | |
159 | */ | |
edce4d98 | 160 | |
161 | /* Internally created requests cannot have bodies today */ | |
162 | request->content_length = 0; | |
163 | request->client_addr = no_addr; | |
164 | request->my_addr = no_addr; /* undefined for internal requests */ | |
165 | request->my_port = 0; | |
166 | request->http_ver = http_ver; | |
167 | http->request = requestLink(request); | |
168 | ||
169 | /* optional - skip the access check ? */ | |
170 | clientAccessCheck(http); | |
171 | return 0; | |
172 | } | |
173 | ||
174 | static int | |
175 | checkAccelOnly(clientHttpRequest * http) | |
176 | { | |
69660be0 | 177 | /* |
178 | * return TRUE if someone makes a proxy request to us and we are in | |
179 | * httpd-accel only mode | |
180 | */ | |
edce4d98 | 181 | if (!Config2.Accel.on) |
182 | return 0; | |
183 | if (Config.onoff.accel_with_proxy) | |
184 | return 0; | |
185 | if (http->request->protocol == PROTO_CACHEOBJ) | |
186 | return 0; | |
187 | if (http->flags.accel) | |
188 | return 0; | |
189 | if (http->request->method == METHOD_PURGE) | |
190 | return 0; | |
191 | return 1; | |
192 | } | |
193 | ||
194 | aclCheck_t * | |
195 | clientAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) | |
196 | { | |
197 | aclCheck_t *ch; | |
198 | ConnStateData *conn = http->conn; | |
199 | ch = aclChecklistCreate(acl, http->request, conn ? conn->rfc931 : dash_str); | |
200 | ||
201 | /* | |
69660be0 | 202 | * hack for ident ACL. It needs to get full addresses, and a place to store |
203 | * the ident result on persistent connections... | |
edce4d98 | 204 | */ |
205 | /* connection oriented auth also needs these two lines for it's operation. */ | |
69660be0 | 206 | /* |
207 | * Internal requests do not have a connection reference, because: A) their | |
208 | * byte count may be transformed before being applied to an outbound | |
209 | * connection B) they are internal - any limiting on them should be done on | |
210 | * the server end. | |
edce4d98 | 211 | */ |
212 | if (conn) | |
213 | ch->conn = cbdataReference(conn); /* unreferenced in acl.c */ | |
214 | ||
215 | return ch; | |
216 | } | |
217 | ||
218 | /* This is the entry point for external users of the client_side routines */ | |
219 | void | |
220 | clientAccessCheck(void *data) | |
221 | { | |
222 | clientHttpRequest *http = data; | |
223 | clientRequestContext *context = clientRequestContextNew(http); | |
224 | if (checkAccelOnly(http)) { | |
225 | /* deny proxy requests in accel_only mode */ | |
226 | debug(85, | |
227 | 1) ("clientAccessCheck: proxy request denied in accel_only mode\n"); | |
228 | clientAccessCheckDone(ACCESS_DENIED, context); | |
229 | return; | |
230 | } | |
231 | context->acl_checklist = | |
232 | clientAclChecklistCreate(Config.accessList.http, http); | |
233 | aclNBCheck(context->acl_checklist, clientAccessCheckDone, context); | |
234 | } | |
235 | ||
236 | void | |
237 | clientAccessCheckDone(int answer, void *data) | |
238 | { | |
239 | clientRequestContext *context = data; | |
240 | clientHttpRequest *http = context->http; | |
241 | err_type page_id; | |
242 | http_status status; | |
243 | char *proxy_auth_msg = NULL; | |
244 | debug(85, 2) ("The request %s %s is %s, because it matched '%s'\n", | |
245 | RequestMethodStr[http->request->method], http->uri, | |
246 | answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED", | |
247 | AclMatchedName ? AclMatchedName : "NO ACL's"); | |
248 | proxy_auth_msg = authenticateAuthUserRequestMessage((http->conn | |
249 | && http->conn->auth_user_request) ? http->conn-> | |
250 | auth_user_request : http->request->auth_user_request); | |
251 | context->acl_checklist = NULL; | |
252 | if (answer == ACCESS_ALLOWED) { | |
253 | safe_free(http->uri); | |
254 | http->uri = xstrdup(urlCanonical(http->request)); | |
255 | assert(context->redirect_state == REDIRECT_NONE); | |
256 | context->redirect_state = REDIRECT_PENDING; | |
257 | redirectStart(http, clientRedirectDone, context); | |
258 | } else { | |
259 | /* Send an error */ | |
260 | clientStreamNode *node = http->client_stream.tail->prev->data; | |
261 | cbdataFree(context); | |
262 | debug(85, 5) ("Access Denied: %s\n", http->uri); | |
263 | debug(85, 5) ("AclMatchedName = %s\n", | |
264 | AclMatchedName ? AclMatchedName : "<null>"); | |
265 | debug(85, 5) ("Proxy Auth Message = %s\n", | |
266 | proxy_auth_msg ? proxy_auth_msg : "<null>"); | |
267 | /* | |
69660be0 | 268 | * NOTE: get page_id here, based on AclMatchedName because if |
269 | * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in | |
270 | * the clientCreateStoreEntry() call just below. Pedro Ribeiro | |
271 | * <pribeiro@isel.pt> | |
edce4d98 | 272 | */ |
273 | page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName); | |
29b8d8d6 | 274 | http->logType = LOG_TCP_DENIED; |
edce4d98 | 275 | if (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName)) { |
276 | if (!http->flags.accel) { | |
277 | /* Proxy authorisation needed */ | |
278 | status = HTTP_PROXY_AUTHENTICATION_REQUIRED; | |
279 | } else { | |
280 | /* WWW authorisation needed */ | |
281 | status = HTTP_UNAUTHORIZED; | |
282 | } | |
283 | if (page_id == ERR_NONE) | |
284 | page_id = ERR_CACHE_ACCESS_DENIED; | |
285 | } else { | |
286 | status = HTTP_FORBIDDEN; | |
287 | if (page_id == ERR_NONE) | |
288 | page_id = ERR_ACCESS_DENIED; | |
289 | } | |
290 | clientSetReplyToError(node->data, page_id, status, | |
291 | http->request->method, NULL, | |
292 | http->conn ? &http->conn->peer.sin_addr : &no_addr, http->request, | |
293 | NULL, http->conn | |
294 | && http->conn->auth_user_request ? http->conn-> | |
295 | auth_user_request : http->request->auth_user_request); | |
296 | node = http->client_stream.tail->data; | |
297 | clientStreamRead(node, http, node->readoff, node->readlen, | |
298 | node->readbuf); | |
299 | } | |
300 | } | |
301 | ||
302 | static int | |
303 | clientCachable(clientHttpRequest * http) | |
304 | { | |
305 | request_t *req = http->request; | |
306 | method_t method = req->method; | |
307 | if (req->protocol == PROTO_HTTP) | |
308 | return httpCachable(method); | |
309 | /* FTP is always cachable */ | |
310 | if (req->protocol == PROTO_WAIS) | |
311 | return 0; | |
69660be0 | 312 | /* |
313 | * The below looks questionable: what non HTTP protocols use connect, | |
314 | * trace, put and post? RC | |
edce4d98 | 315 | */ |
316 | if (method == METHOD_CONNECT) | |
317 | return 0; | |
318 | if (method == METHOD_TRACE) | |
319 | return 0; | |
320 | if (method == METHOD_PUT) | |
321 | return 0; | |
322 | if (method == METHOD_POST) | |
69660be0 | 323 | return 0; /* XXX POST may be cached sometimes.. ignored |
324 | * for now */ | |
edce4d98 | 325 | if (req->protocol == PROTO_GOPHER) |
326 | return gopherCachable(req); | |
327 | if (req->protocol == PROTO_CACHEOBJ) | |
328 | return 0; | |
329 | return 1; | |
330 | } | |
331 | ||
332 | static int | |
333 | clientHierarchical(clientHttpRequest * http) | |
334 | { | |
335 | const char *url = http->uri; | |
336 | request_t *request = http->request; | |
337 | method_t method = request->method; | |
338 | const wordlist *p = NULL; | |
339 | ||
69660be0 | 340 | /* |
341 | * IMS needs a private key, so we can use the hierarchy for IMS only if our | |
342 | * neighbors support private keys | |
343 | */ | |
edce4d98 | 344 | if (request->flags.ims && !neighbors_do_private_keys) |
345 | return 0; | |
69660be0 | 346 | /* |
347 | * This is incorrect: authenticating requests can be sent via a hierarchy | |
348 | * (they can even be cached if the correct headers are set on the reply | |
edce4d98 | 349 | */ |
350 | if (request->flags.auth) | |
351 | return 0; | |
352 | if (method == METHOD_TRACE) | |
353 | return 1; | |
354 | if (method != METHOD_GET) | |
355 | return 0; | |
356 | /* scan hierarchy_stoplist */ | |
357 | for (p = Config.hierarchy_stoplist; p; p = p->next) | |
358 | if (strstr(url, p->key)) | |
359 | return 0; | |
360 | if (request->flags.loopdetect) | |
361 | return 0; | |
362 | if (request->protocol == PROTO_HTTP) | |
363 | return httpCachable(method); | |
364 | if (request->protocol == PROTO_GOPHER) | |
365 | return gopherCachable(request); | |
366 | if (request->protocol == PROTO_WAIS) | |
367 | return 0; | |
368 | if (request->protocol == PROTO_CACHEOBJ) | |
369 | return 0; | |
370 | return 1; | |
371 | } | |
372 | ||
373 | ||
374 | static void | |
375 | clientInterpretRequestHeaders(clientHttpRequest * http) | |
376 | { | |
377 | request_t *request = http->request; | |
378 | const HttpHeader *req_hdr = &request->header; | |
379 | int no_cache = 0; | |
380 | #if !defined(ESI) || defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG) | |
381 | const char *str; | |
382 | #endif | |
383 | request->imslen = -1; | |
384 | request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE); | |
385 | if (request->ims > 0) | |
386 | request->flags.ims = 1; | |
387 | #if ESI | |
69660be0 | 388 | /* |
389 | * We ignore Cache-Control as per the Edge Architecture Section 3. See | |
390 | * www.esi.org for more information. | |
edce4d98 | 391 | */ |
392 | #else | |
393 | if (httpHeaderHas(req_hdr, HDR_PRAGMA)) { | |
394 | String s = httpHeaderGetList(req_hdr, HDR_PRAGMA); | |
395 | if (strListIsMember(&s, "no-cache", ',')) | |
396 | no_cache++; | |
397 | stringClean(&s); | |
398 | } | |
399 | request->cache_control = httpHeaderGetCc(req_hdr); | |
400 | if (request->cache_control) | |
401 | if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE)) | |
402 | no_cache++; | |
69660be0 | 403 | /* |
404 | * Work around for supporting the Reload button in IE browsers when Squid | |
405 | * is used as an accelerator or transparent proxy, by turning accelerated | |
406 | * IMS request to no-cache requests. Now knows about IE 5.5 fix (is | |
407 | * actually only fixed in SP1, but we can't tell whether we are talking to | |
408 | * SP1 or not so all 5.5 versions are treated 'normally'). | |
edce4d98 | 409 | */ |
410 | if (Config.onoff.ie_refresh) { | |
411 | if (http->flags.accel && request->flags.ims) { | |
412 | if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT))) { | |
413 | if (strstr(str, "MSIE 5.01") != NULL) | |
414 | no_cache++; | |
415 | else if (strstr(str, "MSIE 5.0") != NULL) | |
416 | no_cache++; | |
417 | else if (strstr(str, "MSIE 4.") != NULL) | |
418 | no_cache++; | |
419 | else if (strstr(str, "MSIE 3.") != NULL) | |
420 | no_cache++; | |
421 | } | |
422 | } | |
423 | } | |
424 | #endif | |
425 | if (no_cache) { | |
426 | #if HTTP_VIOLATIONS | |
427 | if (Config.onoff.reload_into_ims) | |
428 | request->flags.nocache_hack = 1; | |
429 | else if (refresh_nocache_hack) | |
430 | request->flags.nocache_hack = 1; | |
431 | else | |
432 | #endif | |
433 | request->flags.nocache = 1; | |
434 | } | |
435 | /* ignore range header in non-GETs */ | |
436 | if (request->method == METHOD_GET) { | |
437 | /* | |
69660be0 | 438 | * Since we're not doing ranges atm, just set the flag if the header |
439 | * exists, and then free the range header info -- adrian | |
edce4d98 | 440 | */ |
441 | request->range = httpHeaderGetRange(req_hdr); | |
442 | if (request->range) { | |
443 | request->flags.range = 1; | |
444 | httpHdrRangeDestroy(request->range); | |
445 | request->range = NULL; | |
446 | } | |
447 | } | |
448 | if (httpHeaderHas(req_hdr, HDR_AUTHORIZATION)) | |
449 | request->flags.auth = 1; | |
450 | if (request->login[0] != '\0') | |
451 | request->flags.auth = 1; | |
452 | if (httpHeaderHas(req_hdr, HDR_VIA)) { | |
453 | String s = httpHeaderGetList(req_hdr, HDR_VIA); | |
454 | /* | |
455 | * ThisCache cannot be a member of Via header, "1.0 ThisCache" can. | |
456 | * Note ThisCache2 has a space prepended to the hostname so we don't | |
457 | * accidentally match super-domains. | |
458 | */ | |
459 | if (strListIsSubstr(&s, ThisCache2, ',')) { | |
460 | debugObj(33, 1, "WARNING: Forwarding loop detected for:\n", | |
461 | request, (ObjPackMethod) & httpRequestPack); | |
462 | request->flags.loopdetect = 1; | |
463 | } | |
464 | #if FORW_VIA_DB | |
465 | fvdbCountVia(strBuf(s)); | |
466 | #endif | |
467 | stringClean(&s); | |
468 | } | |
469 | #if USE_USERAGENT_LOG | |
470 | if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT))) | |
ff1e79fd | 471 | logUserAgent(fqdnFromAddr(http->conn ? http->conn->log_addr : no_addr), str); |
edce4d98 | 472 | #endif |
473 | #if USE_REFERER_LOG | |
474 | if ((str = httpHeaderGetStr(req_hdr, HDR_REFERER))) | |
ff1e79fd | 475 | logReferer(fqdnFromAddr(http->conn ? http->conn->log_addr : no_addr), str, http->log_uri); |
edce4d98 | 476 | #endif |
477 | #if FORW_VIA_DB | |
478 | if (httpHeaderHas(req_hdr, HDR_X_FORWARDED_FOR)) { | |
479 | String s = httpHeaderGetList(req_hdr, HDR_X_FORWARDED_FOR); | |
480 | fvdbCountForw(strBuf(s)); | |
481 | stringClean(&s); | |
482 | } | |
483 | #endif | |
484 | if (request->method == METHOD_TRACE) { | |
485 | request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS); | |
486 | } | |
487 | if (clientCachable(http)) | |
488 | request->flags.cachable = 1; | |
489 | if (clientHierarchical(http)) | |
490 | request->flags.hierarchical = 1; | |
491 | debug(85, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n", | |
492 | request->flags.nocache ? "SET" : "NOT SET"); | |
493 | debug(85, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n", | |
494 | request->flags.cachable ? "SET" : "NOT SET"); | |
495 | debug(85, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n", | |
496 | request->flags.hierarchical ? "SET" : "NOT SET"); | |
497 | } | |
498 | ||
499 | void | |
500 | clientRedirectDone(void *data, char *result) | |
501 | { | |
502 | clientRequestContext *context = data; | |
503 | clientHttpRequest *http = context->http; | |
504 | request_t *new_request = NULL; | |
505 | request_t *old_request = http->request; | |
506 | debug(85, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri, | |
507 | result ? result : "NULL"); | |
508 | assert(context->redirect_state == REDIRECT_PENDING); | |
509 | context->redirect_state = REDIRECT_DONE; | |
510 | if (result) { | |
511 | http_status status = (http_status) atoi(result); | |
512 | if (status == HTTP_MOVED_PERMANENTLY | |
69660be0 | 513 | || status == HTTP_MOVED_TEMPORARILY |
514 | || status == HTTP_SEE_OTHER | |
515 | || status == HTTP_TEMPORARY_REDIRECT) { | |
edce4d98 | 516 | char *t = result; |
517 | if ((t = strchr(result, ':')) != NULL) { | |
518 | http->redirect.status = status; | |
519 | http->redirect.location = xstrdup(t + 1); | |
520 | } else { | |
521 | debug(85, 1) ("clientRedirectDone: bad input: %s\n", result); | |
522 | } | |
523 | } | |
524 | if (strcmp(result, http->uri)) | |
525 | new_request = urlParse(old_request->method, result); | |
526 | } | |
527 | if (new_request) { | |
528 | safe_free(http->uri); | |
529 | http->uri = xstrdup(urlCanonical(new_request)); | |
530 | new_request->http_ver = old_request->http_ver; | |
531 | httpHeaderAppend(&new_request->header, &old_request->header); | |
532 | new_request->client_addr = old_request->client_addr; | |
533 | new_request->my_addr = old_request->my_addr; | |
534 | new_request->my_port = old_request->my_port; | |
535 | new_request->flags.redirected = 1; | |
536 | if (old_request->auth_user_request) { | |
537 | new_request->auth_user_request = old_request->auth_user_request; | |
538 | authenticateAuthUserRequestLock(new_request->auth_user_request); | |
539 | } | |
540 | if (old_request->body_connection) { | |
541 | new_request->body_connection = old_request->body_connection; | |
542 | old_request->body_connection = NULL; | |
543 | } | |
544 | new_request->content_length = old_request->content_length; | |
545 | new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive; | |
546 | requestUnlink(old_request); | |
547 | http->request = requestLink(new_request); | |
548 | } | |
549 | clientInterpretRequestHeaders(http); | |
550 | #if HEADERS_LOG | |
551 | headersLog(0, 1, request->method, request); | |
552 | #endif | |
553 | /* FIXME PIPELINE: This is innacurate during pipelining */ | |
554 | if (http->conn) | |
555 | fd_note(http->conn->fd, http->uri); | |
556 | clientCheckNoCache(context); | |
557 | } | |
558 | ||
559 | void | |
560 | clientCheckNoCache(clientRequestContext * context) | |
561 | { | |
562 | clientHttpRequest *http = context->http; | |
563 | if (Config.accessList.noCache && http->request->flags.cachable) { | |
564 | context->acl_checklist = | |
565 | clientAclChecklistCreate(Config.accessList.noCache, http); | |
566 | aclNBCheck(context->acl_checklist, clientCheckNoCacheDone, context); | |
567 | } else { | |
568 | clientCheckNoCacheDone(http->request->flags.cachable, context); | |
569 | } | |
570 | } | |
571 | ||
572 | void | |
573 | clientCheckNoCacheDone(int answer, void *data) | |
574 | { | |
575 | clientRequestContext *context = data; | |
576 | clientHttpRequest *http = context->http; | |
577 | http->request->flags.cachable = answer; | |
578 | context->acl_checklist = NULL; | |
579 | cbdataFree(context); | |
580 | clientProcessRequest(http); | |
581 | } | |
582 | ||
69660be0 | 583 | /* |
584 | * Identify requests that do not go through the store and client side stream | |
585 | * and forward them to the appropriate location. All other requests, request | |
586 | * them. | |
edce4d98 | 587 | */ |
588 | void | |
589 | clientProcessRequest(clientHttpRequest * http) | |
590 | { | |
591 | request_t *r = http->request; | |
592 | clientStreamNode *node; | |
593 | debug(85, 4) ("clientProcessRequest: %s '%s'\n", | |
594 | RequestMethodStr[r->method], http->uri); | |
595 | if (r->method == METHOD_CONNECT) { | |
29b8d8d6 | 596 | http->logType = LOG_TCP_MISS; |
edce4d98 | 597 | sslStart(http, &http->out.size, &http->al.http.code); |
598 | return; | |
599 | } else { | |
29b8d8d6 | 600 | http->logType = LOG_TAG_NONE; |
edce4d98 | 601 | } |
602 | debug(85, 4) ("clientProcessRequest: %s for '%s'\n", | |
29b8d8d6 | 603 | log_tags[http->logType], http->uri); |
edce4d98 | 604 | /* no one should have touched this */ |
605 | assert(http->out.offset == 0); | |
606 | /* Use the Stream Luke */ | |
607 | node = http->client_stream.tail->data; | |
608 | clientStreamRead(node, http, node->readoff, node->readlen, node->readbuf); | |
609 | } |