]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
When reconfiguring, don't free the cacheSwap data structs, we can only
[thirdparty/squid.git] / src / client_side.cc
CommitLineData
3c66d057 1
dd11e0b7 2/*
bf8e5903 3 * $Id: client_side.cc,v 1.156 1997/11/21 17:51:46 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
7a2f978b 34static const char *const crlf = "\r\n";
fc5d6f7f 35static const char *const proxy_auth_line =
36"Proxy-Authenticate: Basic realm=\"Squid proxy-caching web server\"\r\n";
7a2f978b 37
38#define REQUEST_BUF_SIZE 4096
39#define FAILURE_MODE_TIME 300
40
41/* Local functions */
42
43static CWCB icpHandleIMSComplete;
fc5d6f7f 44static CWCB clientWriteComplete;
45static CWCB clientShortWriteComplete;
7a2f978b 46static PF clientReadRequest;
47static PF connStateFree;
48static PF requestTimeout;
49static STCB icpGetHeadersForIMS;
50static char *icpConstruct304reply(struct _http_reply *);
51static int CheckQuickAbort2(const clientHttpRequest *);
52static int icpCheckTransferDone(clientHttpRequest *);
53static void CheckQuickAbort(clientHttpRequest *);
05098026 54#if CHECK_FAILURE_IS_BROKE
7a2f978b 55static void checkFailureRatio(log_type, hier_code);
05098026 56#endif
7a2f978b 57static void clientProcessMISS(int, clientHttpRequest *);
58static void clientAppendReplyHeader(char *, const char *, size_t *, size_t);
59size_t clientBuildReplyHeader(clientHttpRequest *, char *, size_t *, char *, size_t);
60static clientHttpRequest *parseHttpRequest(ConnStateData *, method_t *, int *, char **, size_t *);
582b6456 61static RH clientRedirectDone;
382d851a 62static STCB icpHandleIMSReply;
f5b8bbc4 63static int clientGetsOldEntry(StoreEntry * new, StoreEntry * old, request_t * request);
64static int checkAccelOnly(clientHttpRequest *);
7a2f978b 65static ERCB clientErrorComplete;
66static STCB clientSendMoreData;
67static STCB clientCacheHit;
68static void icpParseRequestHeaders(clientHttpRequest *);
69static void icpProcessRequest(int, clientHttpRequest *);
fc5d6f7f 70static char *clientConstructProxyAuthReply(clientHttpRequest * http);
7a2f978b 71
72
ea6f43cd 73
38d7734b 74static int
382d851a 75checkAccelOnly(clientHttpRequest * http)
38d7734b 76{
77 /* return TRUE if someone makes a proxy request to us and
78 * we are in httpd-accel only mode */
f1dc9b30 79 if (!Config2.Accel.on)
38d7734b 80 return 0;
17a0a4ee 81 if (Config.onoff.accel_with_proxy)
38d7734b 82 return 0;
382d851a 83 if (http->request->protocol == PROTO_CACHEOBJ)
38d7734b 84 return 0;
382d851a 85 if (http->accel)
38d7734b 86 return 0;
87 return 1;
88}
89
b8d8561b 90void
382d851a 91clientAccessCheck(void *data)
f88bb09c 92{
382d851a 93 clientHttpRequest *http = data;
94 ConnStateData *conn = http->conn;
75e88d56 95 char *browser;
17a0a4ee 96 if (Config.onoff.ident_lookup && conn->ident.state == IDENT_NONE) {
382d851a 97 identStart(-1, conn, clientAccessCheck);
ba1d2afa 98 return;
99 }
382d851a 100 if (checkAccelOnly(http)) {
101 clientAccessCheckDone(0, http);
75e88d56 102 return;
f88bb09c 103 }
f182d1c5 104 browser = mime_get_header(http->request->headers, "User-Agent");
f1dc9b30 105 http->acl_checklist = aclChecklistCreate(Config.accessList.http,
382d851a 106 http->request,
107 conn->peer.sin_addr,
75e88d56 108 browser,
382d851a 109 conn->ident.ident);
110 aclNBCheck(http->acl_checklist, clientAccessCheckDone, http);
f88bb09c 111}
112
fc5d6f7f 113static char *
114clientConstructProxyAuthReply(clientHttpRequest * http)
115{
116 LOCAL_ARRAY(char, buf, 8192);
117 LOCAL_ARRAY(char, content, 4096);
118 char *hdr;
119 memset(buf, '\0', 8192);
120 memset(content, '\0', 4096);
121 snprintf(content, 4096,
122 "<TITLE>Cache Access Denied</TITLE>\n"
123 "<H2>Cache Access Denied</H2>\n"
124 "<P>\n"
125 "Sorry, you are not currently allowed to request:\n"
126 "<PRE> %s</PRE>\n"
127 "from this cache until you have authenticated yourself.\n"
128 "\n<p>"
129 "You need to use Netscape version 2.0 or greater, or Microsoft\n"
130 "Internet Explorer 3.0 or an HTTP/1.1 compliant browser for this\n"
131 "to work. Please contact the <a href=\"mailto:%s\">cache\n"
132 "administrator</a> if you have difficulties authenticating\n"
133 "yourself, or\n"
134 "<a href=\"http://%s/cgi-bin/chpasswd.cgi\">change</a> your\n"
135 "default password.\n"
136 "<P>\n"
137 "%s\n"
138 "<HR>\n"
139 "<ADDRESS>\n"
140 "Generated by %s/%s@%s\n"
141 "</ADDRESS>\n",
142 http->url,
143 Config.adminEmail,
144 getMyHostname(),
145 Config.errHtmlText,
146 appname,
147 version_string,
148 getMyHostname());
149 hdr = httpReplyHeader(1.0,
150 HTTP_PROXY_AUTHENTICATION_REQUIRED,
151 "text/html",
152 strlen(content),
9daad6f0 153 0,
fc5d6f7f 154 squid_curtime);
155 snprintf(buf, 8192, "%s%s\r\n%s",
156 hdr,
157 proxy_auth_line,
158 content);
159 return buf;
160}
161
b8d8561b 162void
75e88d56 163clientAccessCheckDone(int answer, void *data)
f88bb09c 164{
382d851a 165 clientHttpRequest *http = data;
166 ConnStateData *conn = http->conn;
167 int fd = conn->fd;
e92d33a5 168 char *redirectUrl = NULL;
fc5d6f7f 169 char *buf;
9b312a19 170 ErrorState *err = NULL;
a3d5953d 171 debug(33, 5) ("clientAccessCheckDone: '%s' answer=%d\n", http->url, answer);
382d851a 172 http->acl_checklist = NULL;
fc5d6f7f 173 if (answer == ACCESS_ALLOWED) {
382d851a 174 urlCanonical(http->request, http->url);
ce66013b 175 assert(http->redirect_state == REDIRECT_NONE);
382d851a 176 http->redirect_state = REDIRECT_PENDING;
177 redirectStart(http, clientRedirectDone, http);
fc5d6f7f 178 } else if (answer == ACCESS_REQ_PROXY_AUTH) {
179 http->al.http.code = HTTP_PROXY_AUTHENTICATION_REQUIRED;
180 http->log_type = LOG_TCP_DENIED;
181 buf = clientConstructProxyAuthReply(http);
182 comm_write(fd,
183 xstrdup(buf),
184 strlen(buf),
185 clientShortWriteComplete,
186 http,
187 xfree);
f88bb09c 188 } else {
a3d5953d 189 debug(33, 5) ("Access Denied: %s\n", http->url);
79a15e0a 190 http->log_type = LOG_TCP_DENIED;
85034133 191 redirectUrl = aclGetDenyInfoUrl(&Config.denyInfoList, AclMatchedName);
e92d33a5 192 if (redirectUrl) {
fe40a877 193 err = errorCon(ERR_ACCESS_DENIED, HTTP_MOVED_TEMPORARILY);
194 err->request = requestLink(http->request);
195 err->src_addr = http->conn->peer.sin_addr;
9b312a19 196 err->redirect_url = xstrdup(redirectUrl);
fe40a877 197 errorSend(fd, err);
e92d33a5 198 } else {
d2989683 199 /* NOTE: don't use HTTP_UNAUTHORIZED because then the
437e2060 200 * stupid browser wants us to authenticate */
fe40a877 201 err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
202 err->request = requestLink(http->request);
203 err->src_addr = http->conn->peer.sin_addr;
204 errorSend(fd, err);
e92d33a5 205 }
f88bb09c 206 }
207}
208
b8d8561b 209static void
210clientRedirectDone(void *data, char *result)
f88bb09c 211{
382d851a 212 clientHttpRequest *http = data;
213 int fd = http->conn->fd;
88738790 214 size_t l;
c0cdaf99 215 request_t *new_request = NULL;
382d851a 216 request_t *old_request = http->request;
a3d5953d 217 debug(33, 5) ("clientRedirectDone: '%s' result=%s\n", http->url,
f88bb09c 218 result ? result : "NULL");
ce66013b 219 assert(http->redirect_state == REDIRECT_PENDING);
382d851a 220 http->redirect_state = REDIRECT_DONE;
c0cdaf99 221 if (result)
20cc1450 222 new_request = urlParse(old_request->method, result);
c0cdaf99 223 if (new_request) {
382d851a 224 safe_free(http->url);
88738790 225 /* need to malloc because the URL returned by the redirector might
226 * not be big enough to append the local domain
227 * -- David Lamkin drl@net-tel.co.uk */
228 l = strlen(result) + Config.appendDomainLen + 5;
229 http->url = xcalloc(l, 1);
230 xstrncpy(http->url, result, l);
20cc1450 231 new_request->http_ver = old_request->http_ver;
2357f74a 232 new_request->headers = old_request->headers;
233 new_request->headers_sz = old_request->headers_sz;
20cc1450 234 requestUnlink(old_request);
382d851a 235 http->request = requestLink(new_request);
236 urlCanonical(http->request, http->url);
f88bb09c 237 }
382d851a 238 icpParseRequestHeaders(http);
239 fd_note(fd, http->url);
240 icpProcessRequest(fd, http);
e81957b7 241}
242
52d4522b 243void
244icpProcessExpired(int fd, void *data)
620da955 245{
382d851a 246 clientHttpRequest *http = data;
247 char *url = http->url;
620da955 248 StoreEntry *entry = NULL;
249
a3d5953d 250 debug(33, 3) ("icpProcessExpired: FD %d '%s'\n", fd, http->url);
620da955 251
79a15e0a 252 EBIT_SET(http->request->flags, REQ_REFRESH);
382d851a 253 http->old_entry = http->entry;
620da955 254 entry = storeCreateEntry(url,
88738790 255 http->log_url,
382d851a 256 http->request->flags,
257 http->request->method);
620da955 258 /* NOTE, don't call storeLockObject(), storeCreateEntry() does it */
fe96bbe6 259 storeClientListAdd(entry, http);
260 storeClientListAdd(http->old_entry, http);
620da955 261
382d851a 262 entry->lastmod = http->old_entry->lastmod;
a3d5953d 263 debug(33, 5) ("icpProcessExpired: setting lmt = %d\n",
620da955 264 entry->lastmod);
265
d1a43e28 266 entry->refcount++; /* EXPIRED CASE */
382d851a 267 http->entry = entry;
268 http->out.offset = 0;
382d851a 269 protoDispatch(fd, http->entry, http->request);
f990cccc 270 /* Register with storage manager to receive updates when data comes in. */
d89d1fb6 271 storeClientCopy(entry,
fe96bbe6 272 http->out.offset,
d89d1fb6 273 http->out.offset,
274 4096,
275 get_free_4k_page(),
276 icpHandleIMSReply,
277 http);
620da955 278}
279
91f4d519 280static int
281clientGetsOldEntry(StoreEntry * new_entry, StoreEntry * old_entry, request_t * request)
282{
283 /* If the reply is anything but "Not Modified" then
284 * we must forward it to the client */
285 if (new_entry->mem_obj->reply->code != 304) {
a3d5953d 286 debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", new_entry->mem_obj->reply->code);
91f4d519 287 return 0;
288 }
289 /* If the client did not send IMS in the request, then it
290 * must get the old object, not this "Not Modified" reply */
79a15e0a 291 if (!EBIT_TEST(request->flags, REQ_IMS)) {
a3d5953d 292 debug(33, 5) ("clientGetsOldEntry: YES, no client IMS\n");
91f4d519 293 return 1;
294 }
295 /* If the client IMS time is prior to the entry LASTMOD time we
296 * need to send the old object */
297 if (modifiedSince(old_entry, request)) {
a3d5953d 298 debug(33, 5) ("clientGetsOldEntry: YES, modified since %d\n", request->ims);
91f4d519 299 return 1;
300 }
a3d5953d 301 debug(33, 5) ("clientGetsOldEntry: NO, new one is fine\n");
91f4d519 302 return 0;
303}
304
305
52d4522b 306static void
02be0294 307icpHandleIMSReply(void *data, char *buf, ssize_t size)
620da955 308{
382d851a 309 clientHttpRequest *http = data;
310 int fd = http->conn->fd;
311 StoreEntry *entry = http->entry;
620da955 312 MemObject *mem = entry->mem_obj;
9fb13bb6 313 const char *url = storeUrl(entry);
e92d33a5 314 int unlink_request = 0;
76cff1d4 315 StoreEntry *oldentry;
9fb13bb6 316 debug(33, 3) ("icpHandleIMSReply: FD %d '%s'\n", fd, url);
76a5501f 317 put_free_4k_page(buf);
318 buf = NULL;
620da955 319 /* unregister this handler */
070d29eb 320 if (size < 0 || entry->store_status == STORE_ABORTED) {
9fb13bb6 321 debug(33, 3) ("icpHandleIMSReply: ABORTED '%s'\n", url);
c54e9052 322 /* We have an existing entry, but failed to validate it */
accc6d47 323 /* Its okay to send the old one anyway */
324 http->log_type = LOG_TCP_REFRESH_FAIL_HIT;
325 storeUnregister(entry, http);
326 storeUnlockObject(entry);
327 entry = http->entry = http->old_entry;
328 entry->refcount++;
a18b8172 329 } else if (mem->reply->code == 0) {
9fb13bb6 330 debug(33, 3) ("icpHandleIMSReply: Incomplete headers for '%s'\n", url);
d89d1fb6 331 storeClientCopy(entry,
fe96bbe6 332 http->out.offset + size,
d89d1fb6 333 http->out.offset,
334 4096,
335 get_free_4k_page(),
336 icpHandleIMSReply,
337 http);
52d4522b 338 return;
382d851a 339 } else if (clientGetsOldEntry(entry, http->old_entry, http->request)) {
620da955 340 /* We initiated the IMS request, the client is not expecting
76cff1d4 341 * 304, so put the good one back. First, make sure the old entry
342 * headers have been loaded from disk. */
382d851a 343 oldentry = http->old_entry;
382d851a 344 http->log_type = LOG_TCP_REFRESH_HIT;
d89d1fb6 345 if (oldentry->mem_obj->request == NULL) {
346 oldentry->mem_obj->request = requestLink(mem->request);
347 unlink_request = 1;
e92d33a5 348 }
64763c37 349 /* Don't memcpy() the whole reply structure here. For example,
350 * www.thegist.com (Netscape/1.13) returns a content-length for
351 * 304's which seems to be the length of the 304 HEADERS!!! and
352 * not the body they refer to. */
353 storeCopyNotModifiedReplyHeaders(entry->mem_obj, oldentry->mem_obj);
d89d1fb6 354 storeTimestampsSet(oldentry);
382d851a 355 storeUnregister(entry, http);
6d54aea8 356 storeUnlockObject(entry);
382d851a 357 entry = http->entry = oldentry;
41fad779 358 entry->timestamp = squid_curtime;
657266fe 359 if (unlink_request) {
e92d33a5 360 requestUnlink(entry->mem_obj->request);
657266fe 361 entry->mem_obj->request = NULL;
362 }
620da955 363 } else {
364 /* the client can handle this reply, whatever it is */
382d851a 365 http->log_type = LOG_TCP_REFRESH_MISS;
d1a43e28 366 if (mem->reply->code == 304) {
382d851a 367 http->old_entry->timestamp = squid_curtime;
368 http->old_entry->refcount++;
369 http->log_type = LOG_TCP_REFRESH_HIT;
d1a43e28 370 }
382d851a 371 storeUnregister(http->old_entry, http);
372 storeUnlockObject(http->old_entry);
620da955 373 }
382d851a 374 http->old_entry = NULL; /* done with old_entry */
e8479da9 375 /* use clientCacheHit() here as the callback because we might
6a54c60e 376 * be swapping in from disk, and the file might not really be
377 * there */
d89d1fb6 378 storeClientCopy(entry,
fe96bbe6 379 http->out.offset,
d89d1fb6 380 http->out.offset,
381 4096,
382 get_free_4k_page(),
e8479da9 383 clientCacheHit,
d89d1fb6 384 http);
620da955 385}
91f4d519 386
387int
304d07cb 388modifiedSince(StoreEntry * entry, request_t * request)
91f4d519 389{
390 int object_length;
391 MemObject *mem = entry->mem_obj;
9fb13bb6 392 debug(33, 3) ("modifiedSince: '%s'\n", storeUrl(entry));
91f4d519 393 if (entry->lastmod < 0)
394 return 1;
395 /* Find size of the object */
396 if (mem->reply->content_length)
397 object_length = mem->reply->content_length;
398 else
399 object_length = entry->object_len - mem->reply->hdr_sz;
400 if (entry->lastmod > request->ims) {
a3d5953d 401 debug(33, 3) ("--> YES: entry newer than client\n");
91f4d519 402 return 1;
403 } else if (entry->lastmod < request->ims) {
a3d5953d 404 debug(33, 3) ("--> NO: entry older than client\n");
91f4d519 405 return 0;
406 } else if (request->imslen < 0) {
a3d5953d 407 debug(33, 3) ("--> NO: same LMT, no client length\n");
91f4d519 408 return 0;
409 } else if (request->imslen == object_length) {
a3d5953d 410 debug(33, 3) ("--> NO: same LMT, same length\n");
91f4d519 411 return 0;
412 } else {
a3d5953d 413 debug(33, 3) ("--> YES: same LMT, different length\n");
91f4d519 414 return 1;
415 }
416}
b3b64e58 417
418char *
382d851a 419clientConstructTraceEcho(clientHttpRequest * http)
b3b64e58 420{
421 LOCAL_ARRAY(char, line, 256);
422 LOCAL_ARRAY(char, buf, 8192);
423 size_t len;
424 memset(buf, '\0', 8192);
042461c3 425 snprintf(buf, 8192, "HTTP/1.0 200 OK\r\n");
56878878 426 snprintf(line, 256, "Date: %s\r\n", mkrfc1123(squid_curtime));
b3b64e58 427 strcat(buf, line);
042461c3 428 snprintf(line, 256, "Server: Squid/%s\r\n", SQUID_VERSION);
b3b64e58 429 strcat(buf, line);
042461c3 430 snprintf(line, 256, "Content-Type: message/http\r\n");
b3b64e58 431 strcat(buf, line);
432 strcat(buf, "\r\n");
433 len = strlen(buf);
382d851a 434 httpBuildRequestHeader(http->request,
435 http->request,
b3b64e58 436 NULL, /* entry */
b3b64e58 437 NULL, /* in_len */
438 buf + len,
439 8192 - len,
603a02fd 440 http->conn->fd,
441 0); /* flags */
382d851a 442 http->log_type = LOG_TCP_MISS;
4869e8a1 443 http->http_code = HTTP_OK;
b3b64e58 444 return buf;
445}
a90eae18 446
447void
382d851a 448clientPurgeRequest(clientHttpRequest * http)
a90eae18 449{
382d851a 450 int fd = http->conn->fd;
9b312a19 451 char *msg;
a90eae18 452 StoreEntry *entry;
9b312a19 453 ErrorState *err = NULL;
9fb13bb6 454 const cache_key *k;
9b312a19 455 debug(33, 3) ("Config.onoff.enable_purge = %d\n", Config.onoff.enable_purge);
17a0a4ee 456 if (!Config.onoff.enable_purge) {
79a15e0a 457 http->log_type = LOG_TCP_DENIED;
fe40a877 458 err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
9b312a19 459 err->request = requestLink(http->request);
460 err->src_addr = http->conn->peer.sin_addr;
9b312a19 461 errorSend(fd, err);
a90eae18 462 return;
463 }
382d851a 464 http->log_type = LOG_TCP_MISS;
9fb13bb6 465 k = storeKeyPublic(http->url, METHOD_GET);
466 if ((entry = storeGet(k)) == NULL) {
9b312a19 467 http->http_code = HTTP_NOT_FOUND;
a90eae18 468 } else {
469 storeRelease(entry);
9b312a19 470 http->http_code = HTTP_OK;
a90eae18 471 }
9b312a19 472 msg = httpReplyHeader(1.0, http->http_code, NULL, 0, 0, -1);
473 if (strlen(msg) < 8190)
474 strcat(msg, "\r\n");
475 comm_write(fd, xstrdup(msg), strlen(msg), clientWriteComplete, http, xfree);
a90eae18 476}
e33ba616 477
478int
ea3a2a69 479checkNegativeHit(StoreEntry * e)
e33ba616 480{
79a15e0a 481 if (!EBIT_TEST(e->flag, ENTRY_NEGCACHED))
ea3a2a69 482 return 0;
483 if (e->expires <= squid_curtime)
484 return 0;
485 if (e->store_status != STORE_OK)
486 return 0;
487 return 1;
e33ba616 488}
7a2f978b 489
490static void
491httpRequestFree(void *data)
492{
493 clientHttpRequest *http = data;
494 clientHttpRequest **H;
495 ConnStateData *conn = http->conn;
496 StoreEntry *entry = http->entry;
497 request_t *request = http->request;
498 MemObject *mem = NULL;
499 debug(12, 3) ("httpRequestFree: %s\n", storeUrl(entry));
500 if (!icpCheckTransferDone(http)) {
501 if (entry)
502 storeUnregister(entry, http); /* unregister BEFORE abort */
503 CheckQuickAbort(http);
504 entry = http->entry; /* reset, IMS might have changed it */
505 if (entry && entry->ping_status == PING_WAITING)
506 storeReleaseRequest(entry);
79d39a72 507 protoUnregister(entry, request);
7a2f978b 508 }
509 assert(http->log_type < LOG_TYPE_MAX);
510 if (entry)
511 mem = entry->mem_obj;
512 if (http->out.size || http->log_type) {
513 http->al.icp.opcode = 0;
514 http->al.url = http->url;
515 if (mem) {
516 http->al.http.code = mem->reply->code;
517 http->al.http.content_type = mem->reply->content_type;
518 }
519 http->al.cache.caddr = conn->log_addr;
520 http->al.cache.size = http->out.size;
521 http->al.cache.code = http->log_type;
522 http->al.cache.msec = tvSubMsec(http->start, current_time);
523 http->al.cache.ident = conn->ident.ident;
524 if (request) {
525 http->al.http.method = request->method;
526 http->al.headers.request = request->headers;
527 http->al.hier = request->hier;
528 }
529 accessLogLog(&http->al);
530 HTTPCacheInfo->proto_count(HTTPCacheInfo,
531 request ? request->protocol : PROTO_NONE,
532 http->log_type);
533 clientdbUpdate(conn->peer.sin_addr, http->log_type, PROTO_HTTP);
534 }
535 if (http->redirect_state == REDIRECT_PENDING)
536 redirectUnregister(http->url, http);
537 if (http->acl_checklist)
538 aclChecklistFree(http->acl_checklist);
05098026 539#if CHECK_FAILURE_IS_BROKE
7a2f978b 540 checkFailureRatio(http->log_type, http->al.hier.code);
05098026 541#endif
7a2f978b 542 safe_free(http->url);
543 safe_free(http->log_url);
544 safe_free(http->al.headers.reply);
545 if (entry) {
546 http->entry = NULL;
547 storeUnregister(entry, http);
548 storeUnlockObject(entry);
549 }
550 /* old_entry might still be set if we didn't yet get the reply
551 * code in icpHandleIMSReply() */
552 if (http->old_entry) {
553 storeUnregister(http->old_entry, http);
554 storeUnlockObject(http->old_entry);
555 http->old_entry = NULL;
556 }
557 requestUnlink(http->request);
558 assert(http != http->next);
559 assert(http->conn->chr != NULL);
560 H = &http->conn->chr;
561 while (*H) {
562 if (*H == http)
563 break;
564 H = &(*H)->next;
565 }
566 assert(*H != NULL);
567 *H = http->next;
568 http->next = NULL;
569 cbdataFree(http);
570}
571
572/* This is a handler normally called by comm_close() */
573static void
574connStateFree(int fd, void *data)
575{
576 ConnStateData *connState = data;
577 clientHttpRequest *http;
578 debug(12, 3) ("connStateFree: FD %d\n", fd);
579 assert(connState != NULL);
79d39a72 580 while ((http = connState->chr) != NULL) {
7a2f978b 581 assert(http->conn == connState);
582 assert(connState->chr != connState->chr->next);
583 httpRequestFree(http);
584 }
585 if (connState->ident.fd > -1)
586 comm_close(connState->ident.fd);
587 safe_free(connState->in.buf);
588 meta_data.misc -= connState->in.size;
589 pconnHistCount(0, connState->nrequests);
590 cbdataFree(connState);
591}
592
593static void
594icpParseRequestHeaders(clientHttpRequest * http)
595{
596 request_t *request = http->request;
597 char *request_hdr = request->headers;
598 char *t = NULL;
599 request->ims = -2;
600 request->imslen = -1;
601 if ((t = mime_get_header(request_hdr, "If-Modified-Since"))) {
79a15e0a 602 EBIT_SET(request->flags, REQ_IMS);
7a2f978b 603 request->ims = parse_rfc1123(t);
604 while ((t = strchr(t, ';'))) {
605 for (t++; isspace(*t); t++);
606 if (strncasecmp(t, "length=", 7) == 0)
607 request->imslen = atoi(t + 7);
608 }
609 }
610 if ((t = mime_get_header(request_hdr, "Pragma"))) {
611 if (!strcasecmp(t, "no-cache"))
79a15e0a 612 EBIT_SET(request->flags, REQ_NOCACHE);
7a2f978b 613 }
614 if (mime_get_header(request_hdr, "Range")) {
79a15e0a 615 EBIT_SET(request->flags, REQ_NOCACHE);
616 EBIT_SET(request->flags, REQ_RANGE);
7a2f978b 617 } else if (mime_get_header(request_hdr, "Request-Range")) {
79a15e0a 618 EBIT_SET(request->flags, REQ_NOCACHE);
619 EBIT_SET(request->flags, REQ_RANGE);
7a2f978b 620 }
621 if (mime_get_header(request_hdr, "Authorization"))
79a15e0a 622 EBIT_SET(request->flags, REQ_AUTH);
7a2f978b 623 if (request->login[0] != '\0')
79a15e0a 624 EBIT_SET(request->flags, REQ_AUTH);
7a2f978b 625 if ((t = mime_get_header(request_hdr, "Proxy-Connection"))) {
626 if (!strcasecmp(t, "Keep-Alive"))
79a15e0a 627 EBIT_SET(request->flags, REQ_PROXY_KEEPALIVE);
7a2f978b 628 }
629 if ((t = mime_get_header(request_hdr, "Via")))
630 if (strstr(t, ThisCache)) {
631 if (!http->accel) {
632 debug(12, 1) ("WARNING: Forwarding loop detected for '%s'\n",
633 http->url);
634 debug(12, 1) ("--> %s\n", t);
635 }
79a15e0a 636 EBIT_SET(request->flags, REQ_LOOPDETECT);
7a2f978b 637 }
638#if USE_USERAGENT_LOG
639 if ((t = mime_get_header(request_hdr, "User-Agent")))
640 logUserAgent(fqdnFromAddr(http->conn->peer.sin_addr), t);
641#endif
642 request->max_age = -1;
643 if ((t = mime_get_header(request_hdr, "Cache-control"))) {
644 if (!strncasecmp(t, "Max-age=", 8))
645 request->max_age = atoi(t + 8);
646 }
647 if (request->method == METHOD_TRACE) {
648 if ((t = mime_get_header(request_hdr, "Max-Forwards")))
649 request->max_forwards = atoi(t);
650 }
651}
652
653static int
654icpCachable(clientHttpRequest * http)
655{
656 const char *url = http->url;
657 request_t *req = http->request;
658 method_t method = req->method;
659 const wordlist *p;
660 for (p = Config.cache_stoplist; p; p = p->next) {
661 if (strstr(url, p->key))
662 return 0;
663 }
664 if (Config.cache_stop_relist)
665 if (aclMatchRegex(Config.cache_stop_relist, url))
666 return 0;
667 if (req->protocol == PROTO_HTTP)
668 return httpCachable(method);
669 /* FTP is always cachable */
670 if (req->protocol == PROTO_GOPHER)
671 return gopherCachable(url);
672 if (req->protocol == PROTO_WAIS)
673 return 0;
674 if (method == METHOD_CONNECT)
675 return 0;
676 if (method == METHOD_TRACE)
677 return 0;
678 if (req->protocol == PROTO_CACHEOBJ)
679 return 0;
680 return 1;
681}
682
683/* Return true if we can query our neighbors for this object */
684static int
685icpHierarchical(clientHttpRequest * http)
686{
687 const char *url = http->url;
688 request_t *request = http->request;
689 method_t method = request->method;
690 const wordlist *p = NULL;
691
692 /* IMS needs a private key, so we can use the hierarchy for IMS only
693 * if our neighbors support private keys */
79a15e0a 694 if (EBIT_TEST(request->flags, REQ_IMS) && !neighbors_do_private_keys)
7a2f978b 695 return 0;
79a15e0a 696 if (EBIT_TEST(request->flags, REQ_AUTH))
7a2f978b 697 return 0;
698 if (method == METHOD_TRACE)
699 return 1;
700 if (method != METHOD_GET)
701 return 0;
702 /* scan hierarchy_stoplist */
703 for (p = Config.hierarchy_stoplist; p; p = p->next)
704 if (strstr(url, p->key))
705 return 0;
79a15e0a 706 if (EBIT_TEST(request->flags, REQ_LOOPDETECT))
7a2f978b 707 return 0;
708 if (request->protocol == PROTO_HTTP)
709 return httpCachable(method);
710 if (request->protocol == PROTO_GOPHER)
711 return gopherCachable(url);
712 if (request->protocol == PROTO_WAIS)
713 return 0;
714 if (request->protocol == PROTO_CACHEOBJ)
715 return 0;
716 return 1;
717}
718
719static void
79a15e0a 720clientErrorComplete(int fd, void *data, size_t size)
7a2f978b 721{
722 clientHttpRequest *http = data;
723 if (http)
724 http->out.size += size;
725 comm_close(fd);
726}
727
728int
729isTcpHit(log_type code)
730{
731 /* this should be a bitmap for better optimization */
732 if (code == LOG_TCP_HIT)
733 return 1;
734 if (code == LOG_TCP_IMS_HIT)
735 return 1;
736 if (code == LOG_TCP_REFRESH_FAIL_HIT)
737 return 1;
738 if (code == LOG_TCP_REFRESH_HIT)
739 return 1;
740 if (code == LOG_TCP_NEGATIVE_HIT)
741 return 1;
742 if (code == LOG_TCP_MEM_HIT)
743 return 1;
744 return 0;
745}
746
747static void
748clientAppendReplyHeader(char *hdr, const char *line, size_t * sz, size_t max)
749{
750 size_t n = *sz + strlen(line) + 2;
751 if (n >= max)
752 return;
753 strcpy(hdr + (*sz), line);
754 strcat(hdr + (*sz), crlf);
755 *sz = n;
756}
757
758size_t
759clientBuildReplyHeader(clientHttpRequest * http,
760 char *hdr_in,
761 size_t * in_len,
762 char *hdr_out,
763 size_t out_sz)
764{
765 char *xbuf;
766 char *ybuf;
767 char *t = NULL;
768 char *end = NULL;
769 size_t len = 0;
770 size_t hdr_len = 0;
771 size_t l;
772 end = mime_headers_end(hdr_in);
773 if (end == NULL) {
774 debug(12, 3) ("clientBuildReplyHeader: DIDN'T FIND END-OF-HEADERS\n");
775 debug(12, 3) ("\n%s", hdr_in);
776 return 0;
777 }
778 xbuf = get_free_4k_page();
779 ybuf = get_free_4k_page();
780 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
781 hdr_len = t - hdr_in;
782 l = strcspn(t, crlf) + 1;
783 xstrncpy(xbuf, t, l > 4096 ? 4096 : l);
784 debug(12, 5) ("clientBuildReplyHeader: %s\n", xbuf);
785#if 0
786 if (strncasecmp(xbuf, "Accept-Ranges:", 14) == 0)
787 continue;
788 if (strncasecmp(xbuf, "Etag:", 5) == 0)
789 continue;
790#endif
791 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
792 continue;
793 if (strncasecmp(xbuf, "Connection:", 11) == 0)
794 continue;
795 if (strncasecmp(xbuf, "Keep-Alive:", 11) == 0)
796 continue;
797 if (strncasecmp(xbuf, "Set-Cookie:", 11) == 0)
798 if (isTcpHit(http->log_type))
799 continue;
800 clientAppendReplyHeader(hdr_out, xbuf, &len, out_sz - 512);
801 }
802 hdr_len = end - hdr_in;
803 /* Append X-Cache: */
804 snprintf(ybuf, 4096, "X-Cache: %s", isTcpHit(http->log_type) ? "HIT" : "MISS");
805 clientAppendReplyHeader(hdr_out, ybuf, &len, out_sz);
806 /* Append Proxy-Connection: */
79a15e0a 807 if (EBIT_TEST(http->request->flags, REQ_PROXY_KEEPALIVE)) {
7a2f978b 808 snprintf(ybuf, 4096, "Proxy-Connection: Keep-Alive");
809 clientAppendReplyHeader(hdr_out, ybuf, &len, out_sz);
810 }
811 clientAppendReplyHeader(hdr_out, null_string, &len, out_sz);
812 if (in_len)
813 *in_len = hdr_len;
814 if ((l = strlen(hdr_out)) != len) {
815 debug_trap("clientBuildReplyHeader: size mismatch");
816 len = l;
817 }
818 debug(12, 3) ("clientBuildReplyHeader: OUTPUT:\n%s\n", hdr_out);
819 put_free_4k_page(xbuf);
820 put_free_4k_page(ybuf);
821 return len;
822}
823
4c323c5f 824static void
7a2f978b 825clientCacheHit(void *data, char *buf, ssize_t size)
826{
827 clientHttpRequest *http = data;
828 if (size >= 0) {
829 clientSendMoreData(data, buf, size);
830 } else if (http->entry == NULL) {
831 debug(12, 3) ("clientCacheHit: request aborted\n");
832 } else {
833 /* swap in failure */
834 http->log_type = LOG_TCP_SWAPFAIL_MISS;
835 clientProcessMISS(http->conn->fd, http);
836 }
837}
838
4c323c5f 839static void
7a2f978b 840clientSendMoreData(void *data, char *buf, ssize_t size)
841{
842 clientHttpRequest *http = data;
843 StoreEntry *entry = http->entry;
844 ConnStateData *conn = http->conn;
845 int fd = conn->fd;
846 char *p = NULL;
847 size_t hdrlen;
848 size_t l = 0;
849 size_t writelen;
850 char *newbuf;
851 FREE *freefunc = put_free_4k_page;
852 int hack = 0;
853 char C = '\0';
854 assert(size <= SM_PAGE_SIZE);
855 debug(12, 5) ("clientSendMoreData: FD %d '%s', out.offset=%d\n",
856 fd, storeUrl(entry), http->out.offset);
857 if (conn->chr != http) {
858 /* there is another object in progress, defer this one */
859 debug(0, 0) ("clientSendMoreData: Deferring delivery of\n");
860 debug(0, 0) ("--> %s\n", storeUrl(entry));
861 debug(0, 0) ("--> because other requests are in front\n");
862 freefunc(buf);
863 return;
864 } else if (entry->store_status == STORE_ABORTED) {
865 freefunc(buf);
866 return;
867 } else if (size < 0) {
868 freefunc(buf);
869 return;
870 } else if (size == 0) {
871 clientWriteComplete(fd, NULL, 0, DISK_OK, http);
872 freefunc(buf);
873 return;
874 }
875 writelen = size;
876 if (http->out.offset == 0 && http->request->protocol != PROTO_CACHEOBJ) {
877 if (Config.onoff.log_mime_hdrs) {
878 if ((p = mime_headers_end(buf))) {
879 safe_free(http->al.headers.reply);
880 http->al.headers.reply = xcalloc(1 + p - buf, 1);
881 xstrncpy(http->al.headers.reply, buf, p - buf);
882 }
883 }
884 /* make sure 'buf' is null terminated somewhere */
885 if (size == SM_PAGE_SIZE) {
886 hack = 1;
887 size--;
888 C = *(buf + size);
889 }
890 *(buf + size) = '\0';
891 newbuf = get_free_8k_page();
892 hdrlen = 0;
893 l = clientBuildReplyHeader(http, buf, &hdrlen, newbuf, 8192);
894 if (hack)
895 *(buf + size++) = C;
896 if (l != 0) {
897 writelen = l + size - hdrlen;
898 assert(writelen <= 8192);
899 /*
900 * l is the length of the new headers in newbuf
901 * hdrlen is the length of the old headers in buf
902 * size - hdrlen is the amount of body in buf
903 */
904 debug(12, 3) ("clientSendMoreData: Appending %d bytes after headers\n",
905 (int) (size - hdrlen));
906 xmemcpy(newbuf + l, buf + hdrlen, size - hdrlen);
907 /* replace buf with newbuf */
908 freefunc(buf);
909 buf = newbuf;
910 freefunc = put_free_8k_page;
911 newbuf = NULL;
912 } else {
913 put_free_8k_page(newbuf);
914 newbuf = NULL;
915 if (size < SM_PAGE_SIZE && entry->store_status == STORE_PENDING) {
916 /* wait for more to arrive */
917 storeClientCopy(entry,
918 http->out.offset + size,
919 http->out.offset,
920 SM_PAGE_SIZE,
921 buf,
922 clientSendMoreData,
923 http);
924 return;
925 }
926 }
927 }
928 http->out.offset += size;
929 if (http->request->method == METHOD_HEAD) {
930 if ((p = mime_headers_end(buf))) {
931 *p = '\0';
932 writelen = p - buf;
933 /* force end */
934 http->out.offset = entry->mem_obj->inmem_hi;
935 }
936 }
937 comm_write(fd, buf, writelen, clientWriteComplete, http, freefunc);
938}
939
fc5d6f7f 940static void
79a15e0a 941clientWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
7a2f978b 942{
943 clientHttpRequest *http = data;
944 ConnStateData *conn;
945 StoreEntry *entry = http->entry;
946 http->out.size += size;
947 debug(12, 5) ("clientWriteComplete: FD %d, sz %d, err %d, off %d, len %d\n",
948 fd, size, errflag, http->out.offset, entry->object_len);
949 if (errflag) {
950 CheckQuickAbort(http);
951 /* Log the number of bytes that we managed to read */
952 HTTPCacheInfo->proto_touchobject(HTTPCacheInfo,
953 urlParseProtocol(storeUrl(entry)),
954 http->out.size);
955 comm_close(fd);
956 } else if (entry->store_status == STORE_ABORTED) {
957 HTTPCacheInfo->proto_touchobject(HTTPCacheInfo,
958 urlParseProtocol(storeUrl(entry)),
959 http->out.size);
960 comm_close(fd);
961 } else if (icpCheckTransferDone(http) || size == 0) {
962 debug(12, 5) ("clientWriteComplete: FD %d transfer is DONE\n", fd);
963 /* We're finished case */
964 HTTPCacheInfo->proto_touchobject(HTTPCacheInfo,
965 http->request->protocol,
966 http->out.size);
967 if (http->entry->mem_obj->reply->content_length <= 0) {
968 comm_close(fd);
79a15e0a 969 } else if (EBIT_TEST(http->request->flags, REQ_PROXY_KEEPALIVE)) {
7a2f978b 970 debug(12, 5) ("clientWriteComplete: FD %d Keeping Alive\n", fd);
971 conn = http->conn;
972 httpRequestFree(http);
79d39a72 973 if ((http = conn->chr) != NULL) {
7a2f978b 974 debug(12, 1) ("clientWriteComplete: FD %d Sending next request\n", fd);
975 storeClientCopy(entry,
976 http->out.offset,
977 http->out.offset,
978 SM_PAGE_SIZE,
979 get_free_4k_page(),
980 clientSendMoreData,
981 http);
982 } else {
983 debug(12, 5) ("clientWriteComplete: FD %d Setting read handler for next request\n", fd);
984 fd_note(fd, "Reading next request");
985 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
986 commSetTimeout(fd, 15, requestTimeout, conn);
987 }
988 } else {
989 comm_close(fd);
990 }
991 } else {
992 /* More data will be coming from primary server; register with
993 * storage manager. */
994 storeClientCopy(entry,
995 http->out.offset,
996 http->out.offset,
997 SM_PAGE_SIZE,
998 get_free_4k_page(),
999 clientSendMoreData,
1000 http);
1001 }
1002}
1003
1004static void
1005icpGetHeadersForIMS(void *data, char *buf, ssize_t size)
1006{
1007 clientHttpRequest *http = data;
1008 int fd = http->conn->fd;
1009 StoreEntry *entry = http->entry;
1010 MemObject *mem = entry->mem_obj;
1011 char *reply = NULL;
1012 assert(size <= SM_PAGE_SIZE);
1013 if (size < 0) {
1014 debug(12, 1) ("icpGetHeadersForIMS: storeClientCopy failed for '%s'\n",
1015 storeKeyText(entry->key));
1016 put_free_4k_page(buf);
1017 clientProcessMISS(fd, http);
1018 return;
1019 }
1020 if (mem->reply->code == 0) {
1021 if (entry->mem_status == IN_MEMORY) {
1022 put_free_4k_page(buf);
1023 clientProcessMISS(fd, http);
1024 return;
1025 }
1026 /* All headers are not yet available, wait for more data */
1027 storeClientCopy(entry,
1028 http->out.offset + size,
1029 http->out.offset,
1030 SM_PAGE_SIZE,
1031 buf,
1032 icpGetHeadersForIMS,
1033 http);
1034 return;
1035 }
1036 /* All headers are available, check if object is modified or not */
1037 /* ---------------------------------------------------------------
1038 * Removed check for reply->code != 200 because of a potential
1039 * problem with ICP. We will return a HIT for any public, cached
1040 * object. This includes other responses like 301, 410, as coded in
1041 * http.c. It is Bad(tm) to return UDP_HIT and then, if the reply
1042 * code is not 200, hand off to clientProcessMISS(), which may disallow
1043 * the request based on 'miss_access' rules. Alternatively, we might
1044 * consider requiring returning UDP_HIT only for 200's. This
1045 * problably means an entry->flag bit, which would be lost during
1046 * restart because the flags aren't preserved across restarts.
1047 * --DW 3/11/96.
1048 * ---------------------------------------------------------------- */
1049#ifdef CHECK_REPLY_CODE_NOTEQUAL_200
1050 /* Only objects with statuscode==200 can be "Not modified" */
1051 if (mem->reply->code != 200) {
1052 debug(12, 4) ("icpGetHeadersForIMS: Reply code %d!=200\n",
1053 mem->reply->code);
1054 put_free_4k_page(buf);
1055 clientProcessMISS(fd, http);
1056 return;
1057 }
1058 +
1059#endif
1060 http->log_type = LOG_TCP_IMS_HIT;
1061 entry->refcount++;
1062 if (modifiedSince(entry, http->request)) {
1063 storeClientCopy(entry,
1064 http->out.offset,
1065 http->out.offset,
1066 SM_PAGE_SIZE,
1067 buf,
1068 clientSendMoreData,
1069 http);
1070 return;
1071 }
1072 debug(12, 4) ("icpGetHeadersForIMS: Not modified '%s'\n", storeUrl(entry));
1073 reply = icpConstruct304reply(mem->reply);
1074 comm_write(fd,
1075 xstrdup(reply),
1076 strlen(reply),
1077 icpHandleIMSComplete,
1078 http,
1079 xfree);
1080}
1081
1082static void
79a15e0a 1083icpHandleIMSComplete(int fd, char *bufnotused, size_t size, int flag, void *data)
7a2f978b 1084{
1085 clientHttpRequest *http = data;
1086 StoreEntry *entry = http->entry;
1087 debug(12, 5) ("icpHandleIMSComplete: Not Modified sent '%s'\n", storeUrl(entry));
1088 HTTPCacheInfo->proto_touchobject(HTTPCacheInfo,
1089 http->request->protocol,
1090 size);
1091 /* Set up everything for the logging */
1092 storeUnregister(entry, http);
1093 storeUnlockObject(entry);
1094 http->entry = NULL;
1095 http->out.size += size;
1096 http->al.http.code = 304;
79a15e0a 1097 if (flag != COMM_ERR_CLOSING)
7a2f978b 1098 comm_close(fd);
1099}
1100
fc5d6f7f 1101static void
1102clientShortWriteComplete(int fd, char *bufnotused, size_t size, int flag, void *data)
1103{
1104 clientHttpRequest *http = data;
1105 http->out.size += size;
1106 if (flag != COMM_ERR_CLOSING)
1107 comm_close(fd);
1108}
1109
7a2f978b 1110/*
1111 * Below, we check whether the object is a hit or a miss. If it's a hit,
1112 * we check whether the object is still valid or whether it is a MISS_TTL.
1113 */
4c323c5f 1114static void
7a2f978b 1115icpProcessRequest(int fd, clientHttpRequest * http)
1116{
1117 char *url = http->url;
1118 const cache_key *pubkey;
1119 StoreEntry *entry = NULL;
1120 request_t *request = http->request;
1121 char *reply;
1122 debug(12, 4) ("icpProcessRequest: %s '%s'\n",
1123 RequestMethodStr[http->request->method],
1124 url);
1125 if (http->request->method == METHOD_CONNECT) {
1126 http->log_type = LOG_TCP_MISS;
1127 sslStart(fd, url, http->request, &http->out.size);
1128 return;
1129 } else if (request->method == METHOD_PURGE) {
1130 clientPurgeRequest(http);
1131 return;
1132 } else if (request->method == METHOD_TRACE) {
1133 if (request->max_forwards == 0) {
1134 reply = clientConstructTraceEcho(http);
1135 comm_write(fd,
1136 xstrdup(reply),
1137 strlen(reply),
1138 clientWriteComplete,
1139 http,
1140 xfree);
1141 return;
1142 }
1143 /* yes, continue */
1144 } else if (request->method != METHOD_GET) {
1145 http->log_type = LOG_TCP_MISS;
1146 passStart(fd, url, http->request, &http->out.size);
1147 return;
1148 }
1149 if (icpCachable(http))
79a15e0a 1150 EBIT_SET(request->flags, REQ_CACHABLE);
7a2f978b 1151 if (icpHierarchical(http))
79a15e0a 1152 EBIT_SET(request->flags, REQ_HIERARCHICAL);
7a2f978b 1153 debug(12, 5) ("icpProcessRequest: REQ_NOCACHE = %s\n",
79a15e0a 1154 EBIT_TEST(request->flags, REQ_NOCACHE) ? "SET" : "NOT SET");
7a2f978b 1155 debug(12, 5) ("icpProcessRequest: REQ_CACHABLE = %s\n",
79a15e0a 1156 EBIT_TEST(request->flags, REQ_CACHABLE) ? "SET" : "NOT SET");
7a2f978b 1157 debug(12, 5) ("icpProcessRequest: REQ_HIERARCHICAL = %s\n",
79a15e0a 1158 EBIT_TEST(request->flags, REQ_HIERARCHICAL) ? "SET" : "NOT SET");
7a2f978b 1159
1160 /* NOTE on HEAD requests: We currently don't cache HEAD reqeusts
1161 * at all, so look for the corresponding GET object, or just go
1162 * directly. The only way to get a TCP_HIT on a HEAD reqeust is
1163 * if someone already did a GET. Maybe we should turn HEAD
1164 * misses into full GET's? */
1165 if (http->request->method == METHOD_HEAD)
1166 pubkey = storeKeyPublic(http->url, METHOD_GET);
1167 else
1168 pubkey = storeKeyPublic(http->url, http->request->method);
1169
1170 if ((entry = storeGet(pubkey)) == NULL) {
1171 /* this object isn't in the cache */
1172 http->log_type = LOG_TCP_MISS;
79a15e0a 1173 } else if (EBIT_TEST(entry->flag, ENTRY_SPECIAL)) {
7a2f978b 1174 if (entry->mem_status == IN_MEMORY)
1175 http->log_type = LOG_TCP_MEM_HIT;
1176 else
1177 http->log_type = LOG_TCP_HIT;
1178 } else if (!storeEntryValidToSend(entry)) {
1179 http->log_type = LOG_TCP_MISS;
1180 storeRelease(entry);
1181 entry = NULL;
79a15e0a 1182 } else if (EBIT_TEST(request->flags, REQ_NOCACHE)) {
7a2f978b 1183 /* NOCACHE should always eject a negative cached object */
79a15e0a 1184 if (EBIT_TEST(entry->flag, ENTRY_NEGCACHED))
7a2f978b 1185 storeRelease(entry);
1186 /* NOCACHE+IMS should not eject a valid object */
79a15e0a 1187 else if (EBIT_TEST(request->flags, REQ_IMS))
7a2f978b 1188 (void) 0;
1189 /* Request-Range should not eject a valid object */
79a15e0a 1190 else if (EBIT_TEST(request->flags, REQ_RANGE))
7a2f978b 1191 (void) 0;
1192 else
1193 storeRelease(entry);
1194 ipcacheReleaseInvalid(http->request->host);
1195 entry = NULL;
1196 http->log_type = LOG_TCP_CLIENT_REFRESH;
1197 } else if (checkNegativeHit(entry)) {
1198 http->log_type = LOG_TCP_NEGATIVE_HIT;
1199 } else if (refreshCheck(entry, request, 0)) {
1200 /* The object is in the cache, but it needs to be validated. Use
1201 * LOG_TCP_REFRESH_MISS for the time being, maybe change it to
1202 * _HIT later in icpHandleIMSReply() */
1203 if (request->protocol == PROTO_HTTP)
1204 http->log_type = LOG_TCP_REFRESH_MISS;
1205 else
1206 http->log_type = LOG_TCP_MISS; /* XXX zoinks */
79a15e0a 1207 } else if (EBIT_TEST(request->flags, REQ_IMS)) {
7a2f978b 1208 /* User-initiated IMS request for something we think is valid */
1209 http->log_type = LOG_TCP_IMS_MISS;
1210 } else {
1211 if (entry->mem_status == IN_MEMORY)
1212 http->log_type = LOG_TCP_MEM_HIT;
1213 else
1214 http->log_type = LOG_TCP_HIT;
1215 }
1216 debug(12, 4) ("icpProcessRequest: %s for '%s'\n",
1217 log_tags[http->log_type],
1218 http->url);
1219 if (entry) {
1220 storeLockObject(entry);
1221 storeCreateMemObject(entry, http->url, http->log_url);
1222 storeClientListAdd(entry, http);
1223 }
1224 http->entry = entry; /* Save a reference to the object */
1225 http->out.offset = 0;
1226 switch (http->log_type) {
1227 case LOG_TCP_HIT:
1228 case LOG_TCP_NEGATIVE_HIT:
1229 case LOG_TCP_MEM_HIT:
1230 entry->refcount++; /* HIT CASE */
1231 storeClientCopy(entry,
1232 http->out.offset,
1233 http->out.offset,
1234 SM_PAGE_SIZE,
1235 get_free_4k_page(),
1236 clientCacheHit,
1237 http);
1238 break;
1239 case LOG_TCP_IMS_MISS:
1240 storeClientCopy(entry,
1241 http->out.offset,
1242 http->out.offset,
1243 SM_PAGE_SIZE,
1244 get_free_4k_page(),
1245 icpGetHeadersForIMS,
1246 http);
1247 break;
1248 case LOG_TCP_REFRESH_MISS:
1249 icpProcessExpired(fd, http);
1250 break;
1251 default:
1252 clientProcessMISS(fd, http);
1253 break;
1254 }
1255}
1256
1257/*
1258 * Prepare to fetch the object as it's a cache miss of some kind.
1259 */
1260static void
1261clientProcessMISS(int fd, clientHttpRequest * http)
1262{
1263 char *url = http->url;
1264 char *request_hdr = http->request->headers;
1265 StoreEntry *entry = NULL;
1266 aclCheck_t ch;
1267 int answer;
1268 ErrorState *err = NULL;
1269 debug(12, 4) ("clientProcessMISS: '%s %s'\n",
1270 RequestMethodStr[http->request->method], url);
1271 debug(12, 10) ("clientProcessMISS: request_hdr:\n%s\n", request_hdr);
1272
1273 /* Check if this host is allowed to fetch MISSES from us */
1274 memset(&ch, '\0', sizeof(aclCheck_t));
1275 ch.src_addr = http->conn->peer.sin_addr;
1276 ch.request = http->request;
1277 answer = aclCheckFast(Config.accessList.miss, &ch);
1278 if (answer == 0) {
1279 http->al.http.code = HTTP_FORBIDDEN;
1280 err = errorCon(ERR_CANNOT_FORWARD, HTTP_FORBIDDEN);
1281 err->request = requestLink(http->request);
1282 err->src_addr = http->conn->peer.sin_addr;
1283 err->callback = clientErrorComplete;
1284 err->callback_data = http;
1285 errorSend(fd, err);
1286 return;
1287 }
1288 /* Get rid of any references to a StoreEntry (if any) */
1289 if (http->entry) {
1290 storeUnregister(http->entry, http);
1291 storeUnlockObject(http->entry);
1292 http->entry = NULL;
1293 }
1294 entry = storeCreateEntry(url,
1295 http->log_url,
1296 http->request->flags,
1297 http->request->method);
1298 /* NOTE, don't call storeLockObject(), storeCreateEntry() does it */
1299 storeClientListAdd(entry, http);
1300 entry->mem_obj->fd = fd;
1301 entry->refcount++; /* MISS CASE */
1302 http->entry = entry;
1303 http->out.offset = 0;
1304 /* Register with storage manager to receive updates when data comes in. */
1305 storeClientCopy(entry,
1306 http->out.offset,
1307 http->out.offset,
1308 SM_PAGE_SIZE,
1309 get_free_4k_page(),
1310 clientSendMoreData,
1311 http);
1312 /* protoDispatch() needs to go after storeClientCopy() at least
1313 * for OBJCACHE requests */
1314 protoDispatch(fd, http->entry, http->request);
1315 return;
1316}
1317
1318/*
1319 * parseHttpRequest()
1320 *
1321 * Returns
1322 * NULL on error or incomplete request
1323 * a clientHttpRequest structure on success
1324 */
1325static clientHttpRequest *
1326parseHttpRequest(ConnStateData * conn, method_t * method_p, int *status,
1327 char **headers_p, size_t * headers_sz_p)
1328{
1329 char *inbuf = NULL;
1330 char *mstr = NULL;
1331 char *url = NULL;
1332 char *req_hdr = NULL;
7a2f978b 1333 float http_ver;
1334 char *token = NULL;
1335 char *t = NULL;
1336 char *end = NULL;
1337 int free_request = 0;
1338 size_t header_sz; /* size of headers, not including first line */
1339 size_t req_sz; /* size of whole request */
1340 size_t url_sz;
1341 method_t method;
1342 clientHttpRequest *http = NULL;
1343
1344 /* Make sure a complete line has been received */
1345 if (strchr(conn->in.buf, '\n') == NULL) {
1346 debug(12, 5) ("Incomplete request line, waiting for more data\n");
1347 *status = 0;
1348 return NULL;
1349 }
1350 /* Use xmalloc/xmemcpy instead of xstrdup because inbuf might
1351 * contain NULL bytes; especially for POST data */
1352 inbuf = xmalloc(conn->in.offset + 1);
1353 xmemcpy(inbuf, conn->in.buf, conn->in.offset);
1354 *(inbuf + conn->in.offset) = '\0';
1355
1356 /* Look for request method */
1357 if ((mstr = strtok(inbuf, "\t ")) == NULL) {
1358 debug(12, 1) ("parseHttpRequest: Can't get request method\n");
1359 xfree(inbuf);
1360 *status = -1;
1361 return NULL;
1362 }
1363 method = urlParseMethod(mstr);
1364 if (method == METHOD_NONE) {
1365 debug(12, 1) ("parseHttpRequest: Unsupported method '%s'\n", mstr);
1366 xfree(inbuf);
1367 *status = -1;
1368 return NULL;
1369 }
1370 debug(12, 5) ("parseHttpRequest: Method is '%s'\n", mstr);
1371
1372 /* look for URL */
1373 if ((url = strtok(NULL, "\r\n\t ")) == NULL) {
1374 debug(12, 1) ("parseHttpRequest: Missing URL\n");
1375 xfree(inbuf);
1376 *status = -1;
1377 return NULL;
1378 }
1379 debug(12, 5) ("parseHttpRequest: Request is '%s'\n", url);
1380
1381 token = strtok(NULL, null_string);
1382 for (t = token; t && *t && *t != '\n' && *t != '\r'; t++);
46c7dbf0 1383 if (t == NULL || *t == '\0' || t == token || strncmp(token, "HTTP/", 5)) {
7a2f978b 1384 debug(12, 3) ("parseHttpRequest: Missing HTTP identifier\n");
1385 xfree(inbuf);
1386 *status = -1;
1387 return NULL;
1388 }
84116757 1389 http_ver = (float) atof(token + 5);
7a2f978b 1390
1391 /* Check if headers are received */
1392 if ((end = mime_headers_end(t)) == NULL) {
1393 xfree(inbuf);
1394 *status = 0;
1395 return NULL;
1396 }
1397 while (isspace(*t))
1398 t++;
1399 req_hdr = t;
1400 header_sz = end - req_hdr;
1401 req_sz = end - inbuf;
1402
1403 /* Ok, all headers are received */
1404 http = xcalloc(1, sizeof(clientHttpRequest));
1405 cbdataAdd(http);
1406 http->http_ver = http_ver;
1407 http->conn = conn;
1408 http->start = current_time;
1409 http->req_sz = req_sz;
1410 *headers_sz_p = header_sz;
1411 *headers_p = xmalloc(header_sz + 1);
1412 xmemcpy(*headers_p, req_hdr, header_sz);
1413 *(*headers_p + header_sz) = '\0';
1414
1415 debug(12, 5) ("parseHttpRequest: Request Header is\n%s\n", *headers_p);
1416
1417 /* Assign http->url */
1418 if ((t = strchr(url, '\n'))) /* remove NL */
1419 *t = '\0';
1420 if ((t = strchr(url, '\r'))) /* remove CR */
1421 *t = '\0';
1422 if ((t = strchr(url, '#'))) /* remove HTML anchors */
1423 *t = '\0';
1424
1425 /* see if we running in Config2.Accel.on, if so got to convert it to URL */
1426 if (Config2.Accel.on && *url == '/') {
1427 /* prepend the accel prefix */
1428 if (vhost_mode) {
1429 /* Put the local socket IP address as the hostname */
1430 url_sz = strlen(url) + 32 + Config.appendDomainLen;
1431 http->url = xcalloc(url_sz, 1);
1432 snprintf(http->url, url_sz, "http://%s:%d%s",
1433 inet_ntoa(http->conn->me.sin_addr),
1434 (int) Config.Accel.port,
1435 url);
1436 debug(12, 5) ("VHOST REWRITE: '%s'\n", http->url);
1437 } else if (opt_accel_uses_host && (t = mime_get_header(req_hdr, "Host"))) {
1438 /* If a Host: header was specified, use it to build the URL
1439 * instead of the one in the Config file. */
1440 /*
1441 * XXX Use of the Host: header here opens a potential
1442 * security hole. There are no checks that the Host: value
1443 * corresponds to one of your servers. It might, for example,
1444 * refer to www.playboy.com. The 'dst' and/or 'dst_domain' ACL
1445 * types should be used to prevent httpd-accelerators
1446 * handling requests for non-local servers */
1447 strtok(t, " :/;@");
1448 url_sz = strlen(url) + 32 + Config.appendDomainLen;
1449 http->url = xcalloc(url_sz, 1);
1450 snprintf(http->url, url_sz, "http://%s:%d%s",
1451 t, (int) Config.Accel.port, url);
1452 } else {
1453 url_sz = strlen(Config2.Accel.prefix) + strlen(url) +
1454 Config.appendDomainLen + 1;
1455 http->url = xcalloc(url_sz, 1);
1456 snprintf(http->url, url_sz, "%s%s", Config2.Accel.prefix, url);
1457 }
1458 http->accel = 1;
1459 } else {
1460 /* URL may be rewritten later, so make extra room */
1461 url_sz = strlen(url) + Config.appendDomainLen + 5;
1462 http->url = xcalloc(url_sz, 1);
1463 strcpy(http->url, url);
1464 http->accel = 0;
1465 }
1466 http->log_url = xstrdup(http->url);
1467 debug(12, 5) ("parseHttpRequest: Complete request received\n");
1468 if (free_request)
1469 safe_free(url);
1470 xfree(inbuf);
1471 *method_p = method;
1472 *status = 1;
1473 return http;
1474}
1475
1476static int
79d39a72 1477clientReadDefer(int fdnotused, void *data)
7a2f978b 1478{
1479 ConnStateData *conn = data;
1480 return conn->defer.until > squid_curtime;
1481}
1482
1483static void
1484clientReadRequest(int fd, void *data)
1485{
1486 ConnStateData *conn = data;
1487 int parser_return_code = 0;
1488 int k;
1489 request_t *request = NULL;
1490 char *tmp;
1491 int size;
1492 int len;
1493 method_t method;
1494 clientHttpRequest *http = NULL;
1495 clientHttpRequest **H = NULL;
1496 char *headers;
1497 size_t headers_sz;
1498 ErrorState *err = NULL;
1499 fde *F = &fd_table[fd];
1500
1501 len = conn->in.size - conn->in.offset - 1;
1502 debug(12, 4) ("clientReadRequest: FD %d: reading request...\n", fd);
1503 size = read(fd, conn->in.buf + conn->in.offset, len);
1504 fd_bytes(fd, size, FD_READ);
6d3caf23 1505 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, Config.Timeout.read);
7a2f978b 1506
1507 if (size == 0) {
1508 if (conn->chr == NULL) {
1509 /* no current or pending requests */
1510 comm_close(fd);
1511 return;
1512 }
1513 /* It might be half-closed, we can't tell */
1514 debug(12, 5) ("clientReadRequest: FD %d closed?\n", fd);
79a15e0a 1515 EBIT_SET(F->flags, FD_SOCKET_EOF);
7a2f978b 1516 conn->defer.until = squid_curtime + 1;
1517 conn->defer.n++;
7a2f978b 1518 return;
1519 } else if (size < 0) {
6d3caf23 1520 if (!ignoreErrno(errno)) {
7a2f978b 1521 debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd, xstrerror());
1522 comm_close(fd);
1523 }
1524 return;
1525 }
1526 conn->in.offset += size;
1527 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
1528
1529 while (conn->in.offset > 0) {
1530 http = parseHttpRequest(conn,
1531 &method,
1532 &parser_return_code,
1533 &headers,
1534 &headers_sz);
1535 if (http) {
1536 assert(http->req_sz > 0);
1537 conn->in.offset -= http->req_sz;
1538 assert(conn->in.offset >= 0);
1539 if (conn->in.offset > 0) {
1540 tmp = xstrdup(conn->in.buf + http->req_sz);
1541 xstrncpy(conn->in.buf, tmp, conn->in.size);
1542 safe_free(tmp);
1543 }
1544 /* link */
1545 for (H = &conn->chr; *H; H = &(*H)->next);
1546 *H = http;
1547 conn->nrequests++;
f2908497 1548 Counter.client_http.requests++;
7a2f978b 1549 commSetTimeout(fd, Config.Timeout.lifetime, NULL, NULL);
1550 if ((request = urlParse(method, http->url)) == NULL) {
1551 debug(12, 5) ("Invalid URL: %s\n", http->url);
1552 err = errorCon(ERR_INVALID_URL, HTTP_BAD_REQUEST);
1553 err->src_addr = conn->peer.sin_addr;
1554 err->callback = clientErrorComplete;
1555 err->callback_data = http;
1556 err->url = xstrdup(http->url);
1557 http->al.http.code = err->http_status;
1558 errorSend(fd, err);
1559 safe_free(headers);
1560 break;
1561 }
1562 safe_free(http->log_url);
1563 http->log_url = xstrdup(urlCanonicalClean(request));
1564 request->client_addr = conn->peer.sin_addr;
1565 request->http_ver = http->http_ver;
1566 request->headers = headers;
1567 request->headers_sz = headers_sz;
1568 if (!urlCheckRequest(request)) {
1569 err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED);
1570 err->src_addr = conn->peer.sin_addr;
1571 err->callback = clientErrorComplete;
1572 err->callback_data = http;
1573 err->request = requestLink(request);
1574 http->al.http.code = err->http_status;
1575 errorSend(fd, err);
1576 return;
1577 }
1578 http->request = requestLink(request);
1579 clientAccessCheck(http);
1580 /* break here for NON-GET because most likely there is a
1581 * reqeust body following and we don't want to parse it
1582 * as though it was new request */
1583 if (request->method != METHOD_GET) {
1584 if (conn->in.offset) {
1585 request->body_sz = conn->in.offset;
1586 request->body = xmalloc(request->body_sz);
1587 xmemcpy(request->body, conn->in.buf, request->body_sz);
1588 conn->in.offset = 0;
1589 }
1590 break;
1591 }
7a2f978b 1592 continue; /* while offset > 0 */
1593 } else if (parser_return_code == 0) {
1594 /*
1595 * Partial request received; reschedule until parseHttpRequest()
1596 * is happy with the input
1597 */
1598 k = conn->in.size - 1 - conn->in.offset;
1599 if (k == 0) {
1600 if (conn->in.offset >= Config.maxRequestSize) {
1601 /* The request is too large to handle */
1602 debug(12, 0) ("Request won't fit in buffer.\n");
1603 debug(12, 0) ("Config 'request_size'= %d bytes.\n",
1604 Config.maxRequestSize);
1605 debug(12, 0) ("This request = %d bytes.\n",
1606 conn->in.offset);
1607 err = errorCon(ERR_INVALID_REQ, HTTP_REQUEST_ENTITY_TOO_LARGE);
1608 err->callback = clientErrorComplete;
1609 err->callback_data = NULL;
1610 errorSend(fd, err);
1611 return;
1612 }
1613 /* Grow the request memory area to accomodate for a large request */
1614 conn->in.size += REQUEST_BUF_SIZE;
1615 conn->in.buf = xrealloc(conn->in.buf, conn->in.size);
1616 meta_data.misc += REQUEST_BUF_SIZE;
1617 debug(12, 2) ("Handling a large request, offset=%d inbufsize=%d\n",
1618 conn->in.offset, conn->in.size);
1619 k = conn->in.size - 1 - conn->in.offset;
1620 }
7a2f978b 1621 break;
1622 } else {
1623 /* parser returned -1 */
f2908497 1624 Counter.client_http.requests++;
7a2f978b 1625 debug(12, 1) ("clientReadRequest: FD %d Invalid Request\n", fd);
1626 err = errorCon(ERR_INVALID_REQ, HTTP_BAD_REQUEST);
1627 err->callback = clientErrorComplete;
1628 err->callback_data = NULL;
bf8e5903 1629 err->request_hdrs = xstrdup(conn->in.buf);
7a2f978b 1630 errorSend(fd, err);
1631 return;
1632 }
1633 }
1634}
1635
1636/* general lifetime handler for HTTP requests */
1637static void
1638requestTimeout(int fd, void *data)
1639{
1640 ConnStateData *conn = data;
1641 ErrorState *err;
1642 debug(12, 2) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
1643 if (fd_table[fd].rwstate) {
1644 /* Some data has been sent to the client, just close the FD */
1645 comm_close(fd);
1646 } else if (conn->nrequests) {
1647 /* assume its a persistent connection; just close it */
1648 comm_close(fd);
1649 } else {
1650 /* Generate an error */
1651 err = errorCon(ERR_LIFETIME_EXP, HTTP_REQUEST_TIMEOUT);
1652 err->callback = clientErrorComplete;
1653 err->url = xstrdup("N/A");
1654 errorSend(fd, err);
1655 /* if we don't close() here, we still need a timeout handler! */
1656 commSetTimeout(fd, 30, requestTimeout, conn);
1657 }
1658}
1659
1660int
79d39a72 1661httpAcceptDefer(int fdnotused, void *notused)
7a2f978b 1662{
1663 return !fdstat_are_n_free_fd(RESERVED_FD);
1664}
1665
bf8e5903 1666/* Handle a new connection on HTTP socket. */
7a2f978b 1667void
1668httpAccept(int sock, void *notused)
1669{
1670 int fd = -1;
1671 ConnStateData *connState = NULL;
1672 struct sockaddr_in peer;
1673 struct sockaddr_in me;
1674 memset(&peer, '\0', sizeof(struct sockaddr_in));
1675 memset(&me, '\0', sizeof(struct sockaddr_in));
1676 commSetSelect(sock, COMM_SELECT_READ, httpAccept, NULL, 0);
1677 if ((fd = comm_accept(sock, &peer, &me)) < 0) {
1678 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
1679 sock, xstrerror());
1680 return;
1681 }
7a2f978b 1682 debug(12, 4) ("httpAccept: FD %d: accepted\n", fd);
1683 connState = xcalloc(1, sizeof(ConnStateData));
1684 connState->peer = peer;
1685 connState->log_addr = peer.sin_addr;
1686 connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
1687 connState->me = me;
1688 connState->fd = fd;
1689 connState->ident.fd = -1;
1690 connState->in.size = REQUEST_BUF_SIZE;
1691 connState->in.buf = xcalloc(connState->in.size, 1);
1692 cbdataAdd(connState);
1693 meta_data.misc += connState->in.size;
1694 comm_add_close_handler(fd, connStateFree, connState);
1695 if (Config.onoff.log_fqdn)
1696 fqdncache_gethostbyaddr(peer.sin_addr, FQDN_LOOKUP_IF_MISS);
1697 commSetTimeout(fd, Config.Timeout.request, requestTimeout, connState);
1698 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0);
1699 commSetDefer(fd, clientReadDefer, connState);
1700}
1701
1702void
1703AppendUdp(icpUdpData * item)
1704{
1705 item->next = NULL;
1706 if (UdpQueueHead == NULL) {
1707 UdpQueueHead = item;
1708 UdpQueueTail = item;
1709 } else if (UdpQueueTail == UdpQueueHead) {
1710 UdpQueueTail = item;
1711 UdpQueueHead->next = item;
1712 } else {
1713 UdpQueueTail->next = item;
1714 UdpQueueTail = item;
1715 }
1716}
1717
1718/* return 1 if the request should be aborted */
1719static int
1720CheckQuickAbort2(const clientHttpRequest * http)
1721{
1722 long curlen;
1723 long minlen;
1724 long expectlen;
1725
79a15e0a 1726 if (!EBIT_TEST(http->request->flags, REQ_CACHABLE))
7a2f978b 1727 return 1;
79a15e0a 1728 if (EBIT_TEST(http->entry->flag, KEY_PRIVATE))
7a2f978b 1729 return 1;
1730 if (http->entry->mem_obj == NULL)
1731 return 1;
1732 expectlen = http->entry->mem_obj->reply->content_length;
1733 curlen = http->entry->mem_obj->inmem_hi;
1734 minlen = Config.quickAbort.min;
1735 if (minlen < 0)
1736 /* disabled */
1737 return 0;
1738 if (curlen > expectlen)
1739 /* bad content length */
1740 return 1;
1741 if ((expectlen - curlen) < minlen)
1742 /* only little more left */
1743 return 0;
1744 if ((expectlen - curlen) > Config.quickAbort.max)
1745 /* too much left to go */
1746 return 1;
1747 if ((curlen / (expectlen / 128U)) > Config.quickAbort.pct)
1748 /* past point of no return */
1749 return 0;
1750 return 1;
1751}
1752
1753
1754static void
1755CheckQuickAbort(clientHttpRequest * http)
1756{
1757 StoreEntry *entry = http->entry;
1758 /* Note, set entry here because http->entry might get changed (for IMS
1759 * requests) during the storeAbort() call */
1760 if (entry == NULL)
1761 return;
1762 if (storePendingNClients(entry) > 1)
1763 return;
1764 if (entry->store_status != STORE_PENDING)
1765 return;
1766 if (CheckQuickAbort2(http) == 0)
1767 return;
1768 debug(12, 3) ("CheckQuickAbort: ABORTING %s\n", storeUrl(entry));
1769 storeAbort(entry, 1);
1770}
1771
1772static int
1773icpCheckTransferDone(clientHttpRequest * http)
1774{
1775 StoreEntry *entry = http->entry;
1776 MemObject *mem = NULL;
7a2f978b 1777 if (entry == NULL)
1778 return 0;
1779 if (entry->store_status != STORE_PENDING)
1780 if (http->out.offset >= entry->object_len)
1781 return 1;
1782 if ((mem = entry->mem_obj) == NULL)
1783 return 0;
1784 if (mem->reply->content_length == 0)
1785 return 0;
1786 if (http->out.offset >= mem->reply->content_length + mem->reply->hdr_sz)
1787 return 1;
1788 return 0;
1789}
1790
1791static char *
1792icpConstruct304reply(struct _http_reply *source)
1793{
1794 LOCAL_ARRAY(char, line, 256);
1795 LOCAL_ARRAY(char, reply, 8192);
7a2f978b 1796 memset(reply, '\0', 8192);
1797 strcpy(reply, "HTTP/1.0 304 Not Modified\r\n");
1798 if (source->date > -1) {
1799 snprintf(line, 256, "Date: %s\r\n", mkrfc1123(source->date));
1800 strcat(reply, line);
1801 }
1802 if ((int) strlen(source->content_type) > 0) {
1803 snprintf(line, 256, "Content-type: %s\r\n", source->content_type);
1804 strcat(reply, line);
1805 }
1806 if (source->content_length) {
1807 snprintf(line, 256, "Content-length: %d\r\n", source->content_length);
1808 strcat(reply, line);
1809 }
1810 if (source->expires > -1) {
1811 snprintf(line, 256, "Expires: %s\r\n", mkrfc1123(source->expires));
1812 strcat(reply, line);
1813 }
1814 if (source->last_modified > -1) {
1815 snprintf(line, 256, "Last-modified: %s\r\n",
1816 mkrfc1123(source->last_modified));
1817 strcat(reply, line);
1818 }
1819 strcat(reply, "\r\n");
1820 return reply;
1821}
1822
1823/*
1824 * This function is designed to serve a fairly specific purpose.
1825 * Occasionally our vBNS-connected caches can talk to each other, but not
1826 * the rest of the world. Here we try to detect frequent failures which
1827 * make the cache unusable (e.g. DNS lookup and connect() failures). If
1828 * the failure:success ratio goes above 1.0 then we go into "hit only"
1829 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
1830 * will only fetch HITs from us if they are using the ICP protocol. We
1831 * stay in this mode for 5 minutes.
1832 *
1833 * Duane W., Sept 16, 1996
1834 */
1835
05098026 1836#if CHECK_FAILURE_IS_BROKE
7a2f978b 1837static void
1838checkFailureRatio(log_type rcode, hier_code hcode)
1839{
1840 static double fail_ratio = 0.0;
1841 static double magic_factor = 100;
1842 double n_good;
1843 double n_bad;
1844 if (hcode == HIER_NONE)
1845 return;
1846 n_good = magic_factor / (1.0 + fail_ratio);
1847 n_bad = magic_factor - n_good;
1848 switch (rcode) {
1849 case ERR_DNS_FAIL:
1850 case ERR_CONNECT_FAIL:
1851 case ERR_READ_ERROR:
1852 n_bad++;
1853 break;
1854 default:
1855 n_good++;
1856 }
1857 fail_ratio = n_bad / n_good;
1858 if (hit_only_mode_until > squid_curtime)
1859 return;
1860 if (fail_ratio < 1.0)
1861 return;
1862 debug(12, 0) ("Failure Ratio at %4.2f\n", fail_ratio);
1863 debug(12, 0) ("Going into hit-only-mode for %d minutes...\n",
1864 FAILURE_MODE_TIME / 60);
1865 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
1866 fail_ratio = 0.8; /* reset to something less than 1.0 */
1867}
05098026 1868#endif