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