]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
gindent
[thirdparty/squid.git] / src / client_side.cc
CommitLineData
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 34static RH clientRedirectDone;
382d851a 35static STCB icpHandleIMSReply;
304d07cb 36static int clientGetsOldEntry _PARAMS((StoreEntry * new, StoreEntry * old, request_t * request));
382d851a 37static int checkAccelOnly _PARAMS((clientHttpRequest *));
ea6f43cd 38
38d7734b 39static int
382d851a 40checkAccelOnly(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 55void
382d851a 56clientAccessCheck(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 78void
75e88d56 79clientAccessCheckDone(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 113static void
114clientRedirectDone(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 148void
149icpProcessExpired(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 185static int
186clientGetsOldEntry(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 212static void
02be0294 213icpHandleIMSReply(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
294int
304d07cb 295modifiedSince(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
325char *
382d851a 326clientConstructTraceEcho(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
354void
382d851a 355clientPurgeRequest(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}