]>
Commit | Line | Data |
---|---|---|
3c66d057 | 1 | |
dd11e0b7 | 2 | /* |
6a54c60e | 3 | * $Id: client_side.cc,v 1.129 1997/10/20 22:59:43 wessels Exp $ |
dd11e0b7 | 4 | * |
5 | * DEBUG: section 33 Client-side Routines | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
dd11e0b7 | 9 | * -------------------------------------------------------- |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
f88bb09c | 31 | |
32 | #include "squid.h" | |
33 | ||
582b6456 | 34 | static RH clientRedirectDone; |
382d851a | 35 | static STCB icpHandleIMSReply; |
304d07cb | 36 | static int clientGetsOldEntry _PARAMS((StoreEntry * new, StoreEntry * old, request_t * request)); |
382d851a | 37 | static int checkAccelOnly _PARAMS((clientHttpRequest *)); |
ea6f43cd | 38 | |
38d7734b | 39 | static int |
382d851a | 40 | checkAccelOnly(clientHttpRequest * http) |
38d7734b | 41 | { |
42 | /* return TRUE if someone makes a proxy request to us and | |
43 | * we are in httpd-accel only mode */ | |
f1dc9b30 | 44 | if (!Config2.Accel.on) |
38d7734b | 45 | return 0; |
17a0a4ee | 46 | if (Config.onoff.accel_with_proxy) |
38d7734b | 47 | return 0; |
382d851a | 48 | if (http->request->protocol == PROTO_CACHEOBJ) |
38d7734b | 49 | return 0; |
382d851a | 50 | if (http->accel) |
38d7734b | 51 | return 0; |
52 | return 1; | |
53 | } | |
54 | ||
b8d8561b | 55 | void |
382d851a | 56 | clientAccessCheck(void *data) |
f88bb09c | 57 | { |
382d851a | 58 | clientHttpRequest *http = data; |
59 | ConnStateData *conn = http->conn; | |
75e88d56 | 60 | char *browser; |
17a0a4ee | 61 | if (Config.onoff.ident_lookup && conn->ident.state == IDENT_NONE) { |
382d851a | 62 | identStart(-1, conn, clientAccessCheck); |
ba1d2afa | 63 | return; |
64 | } | |
382d851a | 65 | if (checkAccelOnly(http)) { |
66 | clientAccessCheckDone(0, http); | |
75e88d56 | 67 | return; |
f88bb09c | 68 | } |
f182d1c5 | 69 | browser = mime_get_header(http->request->headers, "User-Agent"); |
f1dc9b30 | 70 | http->acl_checklist = aclChecklistCreate(Config.accessList.http, |
382d851a | 71 | http->request, |
72 | conn->peer.sin_addr, | |
75e88d56 | 73 | browser, |
382d851a | 74 | conn->ident.ident); |
75 | aclNBCheck(http->acl_checklist, clientAccessCheckDone, http); | |
f88bb09c | 76 | } |
77 | ||
b8d8561b | 78 | void |
75e88d56 | 79 | clientAccessCheckDone(int answer, void *data) |
f88bb09c | 80 | { |
382d851a | 81 | clientHttpRequest *http = data; |
82 | ConnStateData *conn = http->conn; | |
83 | int fd = conn->fd; | |
e92d33a5 | 84 | char *redirectUrl = NULL; |
9b312a19 | 85 | ErrorState *err = NULL; |
a3d5953d | 86 | debug(33, 5) ("clientAccessCheckDone: '%s' answer=%d\n", http->url, answer); |
382d851a | 87 | http->acl_checklist = NULL; |
f88bb09c | 88 | if (answer) { |
382d851a | 89 | urlCanonical(http->request, http->url); |
90 | if (http->redirect_state != REDIRECT_NONE) | |
429fdbec | 91 | fatal_dump("clientAccessCheckDone: wrong redirect_state"); |
382d851a | 92 | http->redirect_state = REDIRECT_PENDING; |
93 | redirectStart(http, clientRedirectDone, http); | |
f88bb09c | 94 | } else { |
a3d5953d | 95 | debug(33, 5) ("Access Denied: %s\n", http->url); |
85034133 | 96 | redirectUrl = aclGetDenyInfoUrl(&Config.denyInfoList, AclMatchedName); |
9b312a19 | 97 | err = xcalloc(1, sizeof(ErrorState)); |
98 | err->type = ERR_ACCESS_DENIED; | |
99 | err->request = requestLink(http->request); | |
100 | err->src_addr = http->conn->peer.sin_addr; | |
e92d33a5 | 101 | if (redirectUrl) { |
9b312a19 | 102 | err->http_status = HTTP_MOVED_TEMPORARILY; |
103 | err->redirect_url = xstrdup(redirectUrl); | |
e92d33a5 | 104 | } else { |
d2989683 | 105 | /* NOTE: don't use HTTP_UNAUTHORIZED because then the |
437e2060 | 106 | * stupid browser wants us to authenticate */ |
d2989683 | 107 | err->http_status = HTTP_FORBIDDEN; |
e92d33a5 | 108 | } |
9b312a19 | 109 | errorSend(fd, err); |
f88bb09c | 110 | } |
111 | } | |
112 | ||
b8d8561b | 113 | static void |
114 | clientRedirectDone(void *data, char *result) | |
f88bb09c | 115 | { |
382d851a | 116 | clientHttpRequest *http = data; |
117 | int fd = http->conn->fd; | |
88738790 | 118 | size_t l; |
c0cdaf99 | 119 | request_t *new_request = NULL; |
382d851a | 120 | request_t *old_request = http->request; |
a3d5953d | 121 | debug(33, 5) ("clientRedirectDone: '%s' result=%s\n", http->url, |
f88bb09c | 122 | result ? result : "NULL"); |
382d851a | 123 | if (http->redirect_state != REDIRECT_PENDING) |
429fdbec | 124 | fatal_dump("clientRedirectDone: wrong redirect_state"); |
382d851a | 125 | http->redirect_state = REDIRECT_DONE; |
c0cdaf99 | 126 | if (result) |
20cc1450 | 127 | new_request = urlParse(old_request->method, result); |
c0cdaf99 | 128 | if (new_request) { |
382d851a | 129 | safe_free(http->url); |
88738790 | 130 | /* need to malloc because the URL returned by the redirector might |
131 | * not be big enough to append the local domain | |
132 | * -- David Lamkin drl@net-tel.co.uk */ | |
133 | l = strlen(result) + Config.appendDomainLen + 5; | |
134 | http->url = xcalloc(l, 1); | |
135 | xstrncpy(http->url, result, l); | |
20cc1450 | 136 | new_request->http_ver = old_request->http_ver; |
2357f74a | 137 | new_request->headers = old_request->headers; |
138 | new_request->headers_sz = old_request->headers_sz; | |
20cc1450 | 139 | requestUnlink(old_request); |
382d851a | 140 | http->request = requestLink(new_request); |
141 | urlCanonical(http->request, http->url); | |
f88bb09c | 142 | } |
382d851a | 143 | icpParseRequestHeaders(http); |
144 | fd_note(fd, http->url); | |
145 | icpProcessRequest(fd, http); | |
e81957b7 | 146 | } |
147 | ||
52d4522b | 148 | void |
149 | icpProcessExpired(int fd, void *data) | |
620da955 | 150 | { |
382d851a | 151 | clientHttpRequest *http = data; |
152 | char *url = http->url; | |
620da955 | 153 | StoreEntry *entry = NULL; |
154 | ||
a3d5953d | 155 | debug(33, 3) ("icpProcessExpired: FD %d '%s'\n", fd, http->url); |
620da955 | 156 | |
382d851a | 157 | BIT_SET(http->request->flags, REQ_REFRESH); |
158 | http->old_entry = http->entry; | |
620da955 | 159 | entry = storeCreateEntry(url, |
88738790 | 160 | http->log_url, |
382d851a | 161 | http->request->flags, |
162 | http->request->method); | |
620da955 | 163 | /* NOTE, don't call storeLockObject(), storeCreateEntry() does it */ |
fe96bbe6 | 164 | storeClientListAdd(entry, http); |
165 | storeClientListAdd(http->old_entry, http); | |
620da955 | 166 | |
382d851a | 167 | entry->lastmod = http->old_entry->lastmod; |
a3d5953d | 168 | debug(33, 5) ("icpProcessExpired: setting lmt = %d\n", |
620da955 | 169 | entry->lastmod); |
170 | ||
d1a43e28 | 171 | entry->refcount++; /* EXPIRED CASE */ |
382d851a | 172 | http->entry = entry; |
173 | http->out.offset = 0; | |
382d851a | 174 | protoDispatch(fd, http->entry, http->request); |
f990cccc | 175 | /* Register with storage manager to receive updates when data comes in. */ |
d89d1fb6 | 176 | storeClientCopy(entry, |
fe96bbe6 | 177 | http->out.offset, |
d89d1fb6 | 178 | http->out.offset, |
179 | 4096, | |
180 | get_free_4k_page(), | |
181 | icpHandleIMSReply, | |
182 | http); | |
620da955 | 183 | } |
184 | ||
91f4d519 | 185 | static int |
186 | clientGetsOldEntry(StoreEntry * new_entry, StoreEntry * old_entry, request_t * request) | |
187 | { | |
188 | /* If the reply is anything but "Not Modified" then | |
189 | * we must forward it to the client */ | |
190 | if (new_entry->mem_obj->reply->code != 304) { | |
a3d5953d | 191 | debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", new_entry->mem_obj->reply->code); |
91f4d519 | 192 | return 0; |
193 | } | |
194 | /* If the client did not send IMS in the request, then it | |
195 | * must get the old object, not this "Not Modified" reply */ | |
196 | if (!BIT_TEST(request->flags, REQ_IMS)) { | |
a3d5953d | 197 | debug(33, 5) ("clientGetsOldEntry: YES, no client IMS\n"); |
91f4d519 | 198 | return 1; |
199 | } | |
200 | /* If the client IMS time is prior to the entry LASTMOD time we | |
201 | * need to send the old object */ | |
202 | if (modifiedSince(old_entry, request)) { | |
a3d5953d | 203 | debug(33, 5) ("clientGetsOldEntry: YES, modified since %d\n", request->ims); |
91f4d519 | 204 | return 1; |
205 | } | |
a3d5953d | 206 | debug(33, 5) ("clientGetsOldEntry: NO, new one is fine\n"); |
91f4d519 | 207 | return 0; |
208 | } | |
209 | ||
210 | ||
620da955 | 211 | |
52d4522b | 212 | static void |
02be0294 | 213 | icpHandleIMSReply(void *data, char *buf, ssize_t size) |
620da955 | 214 | { |
382d851a | 215 | clientHttpRequest *http = data; |
216 | int fd = http->conn->fd; | |
217 | StoreEntry *entry = http->entry; | |
620da955 | 218 | MemObject *mem = entry->mem_obj; |
e92d33a5 | 219 | int unlink_request = 0; |
76cff1d4 | 220 | StoreEntry *oldentry; |
a3d5953d | 221 | debug(33, 3) ("icpHandleIMSReply: FD %d '%s'\n", fd, entry->url); |
620da955 | 222 | /* unregister this handler */ |
620da955 | 223 | if (entry->store_status == STORE_ABORTED) { |
9b312a19 | 224 | debug(33, 3) ("icpHandleIMSReply: ABORTED '%s'\n", entry->url); |
c54e9052 | 225 | /* We have an existing entry, but failed to validate it */ |
226 | if (BIT_SET(entry->flag, ENTRY_REVALIDATE)) { | |
227 | /* We can't send the old one, so send the abort message */ | |
a47b9029 | 228 | http->log_type = LOG_TCP_REFRESH_MISS; |
229 | storeUnregister(http->old_entry, http); | |
230 | storeUnlockObject(http->old_entry); | |
c54e9052 | 231 | } else { |
232 | /* Its okay to send the old one anyway */ | |
9b312a19 | 233 | http->log_type = LOG_TCP_REFRESH_FAIL_HIT; |
c54e9052 | 234 | storeUnregister(entry, http); |
235 | storeUnlockObject(entry); | |
236 | entry = http->entry = http->old_entry; | |
237 | entry->refcount++; | |
238 | } | |
a18b8172 | 239 | } else if (mem->reply->code == 0) { |
a3d5953d | 240 | debug(33, 3) ("icpHandleIMSReply: Incomplete headers for '%s'\n", |
b15fe823 | 241 | entry->url); |
d89d1fb6 | 242 | storeClientCopy(entry, |
fe96bbe6 | 243 | http->out.offset + size, |
d89d1fb6 | 244 | http->out.offset, |
245 | 4096, | |
246 | get_free_4k_page(), | |
247 | icpHandleIMSReply, | |
248 | http); | |
52d4522b | 249 | return; |
382d851a | 250 | } else if (clientGetsOldEntry(entry, http->old_entry, http->request)) { |
620da955 | 251 | /* We initiated the IMS request, the client is not expecting |
76cff1d4 | 252 | * 304, so put the good one back. First, make sure the old entry |
253 | * headers have been loaded from disk. */ | |
382d851a | 254 | oldentry = http->old_entry; |
382d851a | 255 | http->log_type = LOG_TCP_REFRESH_HIT; |
d89d1fb6 | 256 | if (oldentry->mem_obj->request == NULL) { |
257 | oldentry->mem_obj->request = requestLink(mem->request); | |
258 | unlink_request = 1; | |
e92d33a5 | 259 | } |
d89d1fb6 | 260 | memcpy(oldentry->mem_obj->reply, entry->mem_obj->reply, sizeof(struct _http_reply)); |
261 | storeTimestampsSet(oldentry); | |
382d851a | 262 | storeUnregister(entry, http); |
6d54aea8 | 263 | storeUnlockObject(entry); |
382d851a | 264 | entry = http->entry = oldentry; |
41fad779 | 265 | entry->timestamp = squid_curtime; |
657266fe | 266 | if (unlink_request) { |
e92d33a5 | 267 | requestUnlink(entry->mem_obj->request); |
657266fe | 268 | entry->mem_obj->request = NULL; |
269 | } | |
620da955 | 270 | } else { |
271 | /* the client can handle this reply, whatever it is */ | |
382d851a | 272 | http->log_type = LOG_TCP_REFRESH_MISS; |
d1a43e28 | 273 | if (mem->reply->code == 304) { |
382d851a | 274 | http->old_entry->timestamp = squid_curtime; |
275 | http->old_entry->refcount++; | |
276 | http->log_type = LOG_TCP_REFRESH_HIT; | |
d1a43e28 | 277 | } |
382d851a | 278 | storeUnregister(http->old_entry, http); |
279 | storeUnlockObject(http->old_entry); | |
620da955 | 280 | } |
382d851a | 281 | http->old_entry = NULL; /* done with old_entry */ |
e8479da9 | 282 | /* use clientCacheHit() here as the callback because we might |
6a54c60e | 283 | * be swapping in from disk, and the file might not really be |
284 | * there */ | |
d89d1fb6 | 285 | storeClientCopy(entry, |
fe96bbe6 | 286 | http->out.offset, |
d89d1fb6 | 287 | http->out.offset, |
288 | 4096, | |
289 | get_free_4k_page(), | |
e8479da9 | 290 | clientCacheHit, |
d89d1fb6 | 291 | http); |
620da955 | 292 | } |
91f4d519 | 293 | |
294 | int | |
304d07cb | 295 | modifiedSince(StoreEntry * entry, request_t * request) |
91f4d519 | 296 | { |
297 | int object_length; | |
298 | MemObject *mem = entry->mem_obj; | |
a3d5953d | 299 | debug(33, 3) ("modifiedSince: '%s'\n", entry->url); |
91f4d519 | 300 | if (entry->lastmod < 0) |
301 | return 1; | |
302 | /* Find size of the object */ | |
303 | if (mem->reply->content_length) | |
304 | object_length = mem->reply->content_length; | |
305 | else | |
306 | object_length = entry->object_len - mem->reply->hdr_sz; | |
307 | if (entry->lastmod > request->ims) { | |
a3d5953d | 308 | debug(33, 3) ("--> YES: entry newer than client\n"); |
91f4d519 | 309 | return 1; |
310 | } else if (entry->lastmod < request->ims) { | |
a3d5953d | 311 | debug(33, 3) ("--> NO: entry older than client\n"); |
91f4d519 | 312 | return 0; |
313 | } else if (request->imslen < 0) { | |
a3d5953d | 314 | debug(33, 3) ("--> NO: same LMT, no client length\n"); |
91f4d519 | 315 | return 0; |
316 | } else if (request->imslen == object_length) { | |
a3d5953d | 317 | debug(33, 3) ("--> NO: same LMT, same length\n"); |
91f4d519 | 318 | return 0; |
319 | } else { | |
a3d5953d | 320 | debug(33, 3) ("--> YES: same LMT, different length\n"); |
91f4d519 | 321 | return 1; |
322 | } | |
323 | } | |
b3b64e58 | 324 | |
325 | char * | |
382d851a | 326 | clientConstructTraceEcho(clientHttpRequest * http) |
b3b64e58 | 327 | { |
328 | LOCAL_ARRAY(char, line, 256); | |
329 | LOCAL_ARRAY(char, buf, 8192); | |
330 | size_t len; | |
331 | memset(buf, '\0', 8192); | |
042461c3 | 332 | snprintf(buf, 8192, "HTTP/1.0 200 OK\r\n"); |
56878878 | 333 | snprintf(line, 256, "Date: %s\r\n", mkrfc1123(squid_curtime)); |
b3b64e58 | 334 | strcat(buf, line); |
042461c3 | 335 | snprintf(line, 256, "Server: Squid/%s\r\n", SQUID_VERSION); |
b3b64e58 | 336 | strcat(buf, line); |
042461c3 | 337 | snprintf(line, 256, "Content-Type: message/http\r\n"); |
b3b64e58 | 338 | strcat(buf, line); |
339 | strcat(buf, "\r\n"); | |
340 | len = strlen(buf); | |
382d851a | 341 | httpBuildRequestHeader(http->request, |
342 | http->request, | |
b3b64e58 | 343 | NULL, /* entry */ |
b3b64e58 | 344 | NULL, /* in_len */ |
345 | buf + len, | |
346 | 8192 - len, | |
603a02fd | 347 | http->conn->fd, |
348 | 0); /* flags */ | |
382d851a | 349 | http->log_type = LOG_TCP_MISS; |
350 | http->http_code = 200; | |
b3b64e58 | 351 | return buf; |
352 | } | |
a90eae18 | 353 | |
354 | void | |
382d851a | 355 | clientPurgeRequest(clientHttpRequest * http) |
a90eae18 | 356 | { |
382d851a | 357 | int fd = http->conn->fd; |
9b312a19 | 358 | char *msg; |
a90eae18 | 359 | StoreEntry *entry; |
9b312a19 | 360 | ErrorState *err = NULL; |
361 | debug(33, 3) ("Config.onoff.enable_purge = %d\n", Config.onoff.enable_purge); | |
17a0a4ee | 362 | if (!Config.onoff.enable_purge) { |
9b312a19 | 363 | err = xcalloc(1, sizeof(ErrorState)); |
364 | err->type = ERR_ACCESS_DENIED; | |
365 | err->request = requestLink(http->request); | |
366 | err->src_addr = http->conn->peer.sin_addr; | |
d2989683 | 367 | err->http_status = HTTP_FORBIDDEN; |
9b312a19 | 368 | errorSend(fd, err); |
a90eae18 | 369 | return; |
370 | } | |
382d851a | 371 | http->log_type = LOG_TCP_MISS; |
372 | if ((entry = storeGet(http->url)) == NULL) { | |
9b312a19 | 373 | http->http_code = HTTP_NOT_FOUND; |
a90eae18 | 374 | } else { |
375 | storeRelease(entry); | |
9b312a19 | 376 | http->http_code = HTTP_OK; |
a90eae18 | 377 | } |
9b312a19 | 378 | msg = httpReplyHeader(1.0, http->http_code, NULL, 0, 0, -1); |
379 | if (strlen(msg) < 8190) | |
380 | strcat(msg, "\r\n"); | |
381 | comm_write(fd, xstrdup(msg), strlen(msg), clientWriteComplete, http, xfree); | |
a90eae18 | 382 | } |