]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side.cc
indent -br -ce -i4 -ci4 -l80 -nlp -npcs -npsl -d0 -sc -di0 -psl
[thirdparty/squid.git] / src / client_side.cc
1
2 /*
3 * $Id: client_side.cc,v 1.514 2000/12/05 09:15:58 wessels Exp $
4 *
5 * DEBUG: section 33 Client-side Routines
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
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 the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * the Regents of the University of California. Please see the
16 * COPYRIGHT file for full details. Squid incorporates software
17 * developed and/or copyrighted by other sources. Please see the
18 * CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37
38 #if IPF_TRANSPARENT
39 #if HAVE_SYS_IOCTL_H
40 #include <sys/ioctl.h>
41 #endif
42 #include <netinet/tcp.h>
43 #include <net/if.h>
44 #if HAVE_IP_FIL_COMPAT_H
45 #include <ip_fil_compat.h>
46 #elif HAVE_NETINET_IP_FIL_COMPAT_H
47 #include <netinet/ip_fil_compat.h>
48 #elif HAVE_IP_COMPAT_H
49 #include <ip_compat.h>
50 #elif HAVE_NETINET_IP_COMPAT_H
51 #include <netinet/ip_compat.h>
52 #endif
53 #if HAVE_IP_FIL_H
54 #include <ip_fil.h>
55 #elif HAVE_NETINET_IP_FIL_H
56 #include <netinet/ip_fil.h>
57 #endif
58 #if HAVE_IP_NAT_H
59 #include <ip_nat.h>
60 #elif HAVE_NETINET_IP_NAT_H
61 #include <netinet/ip_nat.h>
62 #endif
63 #endif
64
65
66
67 #if LINGERING_CLOSE
68 #define comm_close comm_lingering_close
69 #endif
70
71 static const char *const crlf = "\r\n";
72
73 #define FAILURE_MODE_TIME 300
74
75 /* Local functions */
76
77 static CWCB clientWriteComplete;
78 static CWCB clientWriteBodyComplete;
79 static PF clientReadRequest;
80 static PF connStateFree;
81 static PF requestTimeout;
82 static PF clientLifetimeTimeout;
83 static int clientCheckTransferDone(clientHttpRequest *);
84 static int clientGotNotEnough(clientHttpRequest *);
85 static void checkFailureRatio(err_type, hier_code);
86 static void clientProcessMiss(clientHttpRequest *);
87 static void clientBuildReplyHeader(clientHttpRequest * http, HttpReply * rep);
88 static clientHttpRequest *parseHttpRequestAbort(ConnStateData * conn, const char *uri);
89 static clientHttpRequest *parseHttpRequest(ConnStateData *, method_t *, int *, char **, size_t *);
90 static RH clientRedirectDone;
91 static void clientCheckNoCache(clientHttpRequest *);
92 static void clientCheckNoCacheDone(int answer, void *data);
93 static STCB clientHandleIMSReply;
94 static int clientGetsOldEntry(StoreEntry * new, StoreEntry * old, request_t * request);
95 static int checkAccelOnly(clientHttpRequest *);
96 #if USE_IDENT
97 static IDCB clientIdentDone;
98 #endif
99 static int clientOnlyIfCached(clientHttpRequest * http);
100 static STCB clientSendMoreData;
101 static STCB clientCacheHit;
102 static void clientSetKeepaliveFlag(clientHttpRequest *);
103 static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb);
104 static void clientPackTermBound(String boundary, MemBuf * mb);
105 static void clientInterpretRequestHeaders(clientHttpRequest *);
106 static void clientProcessRequest(clientHttpRequest *);
107 static void clientProcessExpired(void *data);
108 static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http);
109 static int clientCachable(clientHttpRequest * http);
110 static int clientHierarchical(clientHttpRequest * http);
111 static int clientCheckContentLength(request_t * r);
112 static DEFER httpAcceptDefer;
113 static log_type clientProcessRequest2(clientHttpRequest * http);
114 static int clientReplyBodyTooLarge(int clen);
115 static int clientRequestBodyTooLarge(int clen);
116
117 static int
118 checkAccelOnly(clientHttpRequest * http)
119 {
120 /* return TRUE if someone makes a proxy request to us and
121 * we are in httpd-accel only mode */
122 if (!Config2.Accel.on)
123 return 0;
124 if (Config.onoff.accel_with_proxy)
125 return 0;
126 if (http->request->protocol == PROTO_CACHEOBJ)
127 return 0;
128 if (http->flags.accel)
129 return 0;
130 return 1;
131 }
132
133 #if USE_IDENT
134 static void
135 clientIdentDone(const char *ident, void *data)
136 {
137 ConnStateData *conn = data;
138 if (ident)
139 xstrncpy(conn->ident, ident, sizeof(conn->ident));
140 else
141 xstrncpy(conn->ident, "-", sizeof(conn->ident));
142 }
143 #endif
144
145 static aclCheck_t *
146 clientAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http)
147 {
148 aclCheck_t *ch;
149 ConnStateData *conn = http->conn;
150 ch = aclChecklistCreate(acl,
151 http->request,
152 conn->ident);
153 #if USE_IDENT
154 /*
155 * hack for ident ACL. It needs to get full addresses, and a
156 * place to store the ident result on persistent connections...
157 */
158 ch->conn = conn;
159 cbdataLock(ch->conn);
160 #endif
161 return ch;
162 }
163
164 void
165 clientAccessCheck(void *data)
166 {
167 clientHttpRequest *http = data;
168 if (checkAccelOnly(http)) {
169 /* deny proxy requests in accel_only mode */
170 debug(33, 1) ("clientAccessCheck: proxy request denied in accel_only mode\n");
171 clientAccessCheckDone(ACCESS_DENIED, http);
172 return;
173 }
174 http->acl_checklist = clientAclChecklistCreate(Config.accessList.http, http);
175 aclNBCheck(http->acl_checklist, clientAccessCheckDone, http);
176 }
177
178 /*
179 * returns true if client specified that the object must come from the cache
180 * without contacting origin server
181 */
182 static int
183 clientOnlyIfCached(clientHttpRequest * http)
184 {
185 const request_t *r = http->request;
186 assert(r);
187 return r->cache_control &&
188 EBIT_TEST(r->cache_control->mask, CC_ONLY_IF_CACHED);
189 }
190
191 StoreEntry *
192 clientCreateStoreEntry(clientHttpRequest * h, method_t m, request_flags flags)
193 {
194 StoreEntry *e;
195 /*
196 * For erroneous requests, we might not have a h->request,
197 * so make a fake one.
198 */
199 if (h->request == NULL)
200 h->request = requestLink(requestCreate(m, PROTO_NONE, null_string));
201 e = storeCreateEntry(h->uri, h->log_uri, flags, m);
202 h->sc = storeClientListAdd(e, h);
203 #if DELAY_POOLS
204 delaySetStoreClient(h->sc, delayClient(h->request));
205 #endif
206 storeClientCopy(h->sc, e, 0, 0, CLIENT_SOCK_SZ,
207 memAllocate(MEM_CLIENT_SOCK_BUF), clientSendMoreData, h);
208 return e;
209 }
210
211 void
212 clientAccessCheckDone(int answer, void *data)
213 {
214 clientHttpRequest *http = data;
215 int page_id = -1;
216 http_status status;
217 ErrorState *err = NULL;
218 debug(33, 2) ("The request %s %s is %s, because it matched '%s'\n",
219 RequestMethodStr[http->request->method], http->uri,
220 answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED",
221 AclMatchedName ? AclMatchedName : "NO ACL's");
222 http->acl_checklist = NULL;
223 if (answer == ACCESS_ALLOWED) {
224 safe_free(http->uri);
225 http->uri = xstrdup(urlCanonical(http->request));
226 assert(http->redirect_state == REDIRECT_NONE);
227 http->redirect_state = REDIRECT_PENDING;
228 redirectStart(http, clientRedirectDone, http);
229 } else {
230 debug(33, 5) ("Access Denied: %s\n", http->uri);
231 debug(33, 5) ("AclMatchedName = %s\n",
232 AclMatchedName ? AclMatchedName : "<null>");
233 /*
234 * NOTE: get page_id here, based on AclMatchedName because
235 * if USE_DELAY_POOLS is enabled, then AclMatchedName gets
236 * clobbered in the clientCreateStoreEntry() call
237 * just below. Pedro Ribeiro <pribeiro@isel.pt>
238 */
239 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
240 http->log_type = LOG_TCP_DENIED;
241 http->entry = clientCreateStoreEntry(http, http->request->method,
242 null_request_flags);
243 if (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName)) {
244 if (!http->flags.accel) {
245 /* Proxy authorisation needed */
246 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
247 } else {
248 /* WWW authorisation needed */
249 status = HTTP_UNAUTHORIZED;
250 }
251 if (page_id <= 0)
252 page_id = ERR_CACHE_ACCESS_DENIED;
253 } else {
254 status = HTTP_FORBIDDEN;
255 if (page_id <= 0)
256 page_id = ERR_ACCESS_DENIED;
257 }
258 err = errorCon(page_id, status);
259 err->request = requestLink(http->request);
260 err->src_addr = http->conn->peer.sin_addr;
261 errorAppendEntry(http->entry, err);
262 }
263 }
264
265 static void
266 clientRedirectDone(void *data, char *result)
267 {
268 clientHttpRequest *http = data;
269 request_t *new_request = NULL;
270 request_t *old_request = http->request;
271 debug(33, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri,
272 result ? result : "NULL");
273 assert(http->redirect_state == REDIRECT_PENDING);
274 http->redirect_state = REDIRECT_DONE;
275 if (result) {
276 http_status status = atoi(result);
277 if (status == 301 || status == 302) {
278 char *t = result;
279 if ((t = strchr(result, ':')) != NULL) {
280 http->redirect.status = status;
281 http->redirect.location = xstrdup(t + 1);
282 } else {
283 debug(33, 1) ("clientRedirectDone: bad input: %s\n", result);
284 }
285 }
286 if (strcmp(result, http->uri))
287 new_request = urlParse(old_request->method, result);
288 }
289 if (new_request) {
290 safe_free(http->uri);
291 http->uri = xstrdup(urlCanonical(new_request));
292 new_request->http_ver = old_request->http_ver;
293 httpHeaderAppend(&new_request->header, &old_request->header);
294 new_request->client_addr = old_request->client_addr;
295 new_request->my_addr = old_request->my_addr;
296 new_request->my_port = old_request->my_port;
297 new_request->flags.redirected = 1;
298 if (old_request->user_ident[0])
299 xstrncpy(new_request->user_ident, old_request->user_ident,
300 USER_IDENT_SZ);
301 if (old_request->body) {
302 new_request->body = xmalloc(old_request->body_sz);
303 xmemcpy(new_request->body, old_request->body, old_request->body_sz);
304 new_request->body_sz = old_request->body_sz;
305 }
306 new_request->content_length = old_request->content_length;
307 new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
308 requestUnlink(old_request);
309 http->request = requestLink(new_request);
310 }
311 clientInterpretRequestHeaders(http);
312 #if HEADERS_LOG
313 headersLog(0, 1, request->method, request);
314 #endif
315 fd_note(http->conn->fd, http->uri);
316 clientCheckNoCache(http);
317 }
318
319 static void
320 clientCheckNoCache(clientHttpRequest * http)
321 {
322 if (Config.accessList.noCache && http->request->flags.cachable) {
323 http->acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
324 aclNBCheck(http->acl_checklist, clientCheckNoCacheDone, http);
325 } else {
326 clientCheckNoCacheDone(http->request->flags.cachable, http);
327 }
328 }
329
330 void
331 clientCheckNoCacheDone(int answer, void *data)
332 {
333 clientHttpRequest *http = data;
334 http->request->flags.cachable = answer;
335 http->acl_checklist = NULL;
336 clientProcessRequest(http);
337 }
338
339 static void
340 clientProcessExpired(void *data)
341 {
342 clientHttpRequest *http = data;
343 char *url = http->uri;
344 StoreEntry *entry = NULL;
345 debug(33, 3) ("clientProcessExpired: '%s'\n", http->uri);
346 assert(http->entry->lastmod >= 0);
347 /*
348 * check if we are allowed to contact other servers
349 * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return
350 * a stale entry *if* it matches client requirements
351 */
352 if (clientOnlyIfCached(http)) {
353 clientProcessOnlyIfCachedMiss(http);
354 return;
355 }
356 http->request->flags.refresh = 1;
357 http->old_entry = http->entry;
358 http->old_sc = http->sc;
359 /*
360 * Assert that 'http' is already a client of old_entry. If
361 * it is not, then the beginning of the object data might get
362 * freed from memory before we need to access it.
363 */
364 assert(http->sc->callback_data == http);
365 entry = storeCreateEntry(url,
366 http->log_uri,
367 http->request->flags,
368 http->request->method);
369 /* NOTE, don't call storeLockObject(), storeCreateEntry() does it */
370 http->sc = storeClientListAdd(entry, http);
371 #if DELAY_POOLS
372 /* delay_id is already set on original store client */
373 delaySetStoreClient(http->sc, delayClient(http->request));
374 #endif
375 http->request->lastmod = http->old_entry->lastmod;
376 debug(33, 5) ("clientProcessExpired: lastmod %d\n", (int) entry->lastmod);
377 http->entry = entry;
378 http->out.offset = 0;
379 fwdStart(http->conn->fd, http->entry, http->request);
380 /* Register with storage manager to receive updates when data comes in. */
381 if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
382 debug(33, 0) ("clientProcessExpired: found ENTRY_ABORTED object\n");
383 storeClientCopy(http->sc, entry,
384 http->out.offset,
385 http->out.offset,
386 CLIENT_SOCK_SZ,
387 memAllocate(MEM_CLIENT_SOCK_BUF),
388 clientHandleIMSReply,
389 http);
390 }
391
392 static int
393 clientGetsOldEntry(StoreEntry * new_entry, StoreEntry * old_entry, request_t * request)
394 {
395 const http_status status = new_entry->mem_obj->reply->sline.status;
396 if (0 == status) {
397 debug(33, 5) ("clientGetsOldEntry: YES, broken HTTP reply\n");
398 return 1;
399 }
400 /* If the reply is a failure then send the old object as a last
401 * resort */
402 if (status >= 500 && status < 600) {
403 debug(33, 3) ("clientGetsOldEntry: YES, failure reply=%d\n", status);
404 return 1;
405 }
406 /* If the reply is anything but "Not Modified" then
407 * we must forward it to the client */
408 if (HTTP_NOT_MODIFIED != status) {
409 debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", status);
410 return 0;
411 }
412 /* If the client did not send IMS in the request, then it
413 * must get the old object, not this "Not Modified" reply */
414 if (!request->flags.ims) {
415 debug(33, 5) ("clientGetsOldEntry: YES, no client IMS\n");
416 return 1;
417 }
418 /* If the client IMS time is prior to the entry LASTMOD time we
419 * need to send the old object */
420 if (modifiedSince(old_entry, request)) {
421 debug(33, 5) ("clientGetsOldEntry: YES, modified since %d\n",
422 (int) request->ims);
423 return 1;
424 }
425 debug(33, 5) ("clientGetsOldEntry: NO, new one is fine\n");
426 return 0;
427 }
428
429
430 static void
431 clientHandleIMSReply(void *data, char *buf, ssize_t size)
432 {
433 clientHttpRequest *http = data;
434 StoreEntry *entry = http->entry;
435 MemObject *mem;
436 const char *url = storeUrl(entry);
437 int unlink_request = 0;
438 StoreEntry *oldentry;
439 int recopy = 1;
440 http_status status;
441 debug(33, 3) ("clientHandleIMSReply: %s, %d bytes\n", url, (int) size);
442 if (entry == NULL) {
443 memFree(buf, MEM_CLIENT_SOCK_BUF);
444 return;
445 }
446 if (size < 0 && !EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
447 memFree(buf, MEM_CLIENT_SOCK_BUF);
448 return;
449 }
450 mem = entry->mem_obj;
451 status = mem->reply->sline.status;
452 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
453 debug(33, 3) ("clientHandleIMSReply: ABORTED '%s'\n", url);
454 /* We have an existing entry, but failed to validate it */
455 /* Its okay to send the old one anyway */
456 http->log_type = LOG_TCP_REFRESH_FAIL_HIT;
457 storeUnregister(http->sc, entry, http);
458 storeUnlockObject(entry);
459 entry = http->entry = http->old_entry;
460 http->sc = http->old_sc;
461 } else if (STORE_PENDING == entry->store_status && 0 == status) {
462 debug(33, 3) ("clientHandleIMSReply: Incomplete headers for '%s'\n", url);
463 if (size >= CLIENT_SOCK_SZ) {
464 /* will not get any bigger than that */
465 debug(33, 3) ("clientHandleIMSReply: Reply is too large '%s', using old entry\n", url);
466 /* use old entry, this repeats the code abovez */
467 http->log_type = LOG_TCP_REFRESH_FAIL_HIT;
468 storeUnregister(http->sc, entry, http);
469 storeUnlockObject(entry);
470 entry = http->entry = http->old_entry;
471 http->sc = http->old_sc;
472 /* continue */
473 } else {
474 storeClientCopy(http->sc, entry,
475 http->out.offset + size,
476 http->out.offset,
477 CLIENT_SOCK_SZ,
478 buf,
479 clientHandleIMSReply,
480 http);
481 return;
482 }
483 } else if (clientGetsOldEntry(entry, http->old_entry, http->request)) {
484 /* We initiated the IMS request, the client is not expecting
485 * 304, so put the good one back. First, make sure the old entry
486 * headers have been loaded from disk. */
487 oldentry = http->old_entry;
488 http->log_type = LOG_TCP_REFRESH_HIT;
489 if (oldentry->mem_obj->request == NULL) {
490 oldentry->mem_obj->request = requestLink(mem->request);
491 unlink_request = 1;
492 }
493 /* Don't memcpy() the whole reply structure here. For example,
494 * www.thegist.com (Netscape/1.13) returns a content-length for
495 * 304's which seems to be the length of the 304 HEADERS!!! and
496 * not the body they refer to. */
497 httpReplyUpdateOnNotModified(oldentry->mem_obj->reply, mem->reply);
498 storeTimestampsSet(oldentry);
499 storeUnregister(http->sc, entry, http);
500 http->sc = http->old_sc;
501 storeUnlockObject(entry);
502 entry = http->entry = oldentry;
503 entry->timestamp = squid_curtime;
504 if (unlink_request) {
505 requestUnlink(entry->mem_obj->request);
506 entry->mem_obj->request = NULL;
507 }
508 } else {
509 /* the client can handle this reply, whatever it is */
510 http->log_type = LOG_TCP_REFRESH_MISS;
511 if (HTTP_NOT_MODIFIED == mem->reply->sline.status) {
512 httpReplyUpdateOnNotModified(http->old_entry->mem_obj->reply,
513 mem->reply);
514 storeTimestampsSet(http->old_entry);
515 http->log_type = LOG_TCP_REFRESH_HIT;
516 }
517 storeUnregister(http->old_sc, http->old_entry, http);
518 storeUnlockObject(http->old_entry);
519 recopy = 0;
520 }
521 http->old_entry = NULL; /* done with old_entry */
522 http->old_sc = NULL;
523 assert(!EBIT_TEST(entry->flags, ENTRY_ABORTED));
524 if (recopy) {
525 storeClientCopy(http->sc, entry,
526 http->out.offset,
527 http->out.offset,
528 CLIENT_SOCK_SZ,
529 buf,
530 clientSendMoreData,
531 http);
532 } else {
533 clientSendMoreData(data, buf, size);
534 }
535 }
536
537 int
538 modifiedSince(StoreEntry * entry, request_t * request)
539 {
540 int object_length;
541 MemObject *mem = entry->mem_obj;
542 time_t mod_time = entry->lastmod;
543 debug(33, 3) ("modifiedSince: '%s'\n", storeUrl(entry));
544 if (mod_time < 0)
545 mod_time = entry->timestamp;
546 debug(33, 3) ("modifiedSince: mod_time = %d\n", (int) mod_time);
547 if (mod_time < 0)
548 return 1;
549 /* Find size of the object */
550 object_length = mem->reply->content_length;
551 if (object_length < 0)
552 object_length = contentLen(entry);
553 if (mod_time > request->ims) {
554 debug(33, 3) ("--> YES: entry newer than client\n");
555 return 1;
556 } else if (mod_time < request->ims) {
557 debug(33, 3) ("--> NO: entry older than client\n");
558 return 0;
559 } else if (request->imslen < 0) {
560 debug(33, 3) ("--> NO: same LMT, no client length\n");
561 return 0;
562 } else if (request->imslen == object_length) {
563 debug(33, 3) ("--> NO: same LMT, same length\n");
564 return 0;
565 } else {
566 debug(33, 3) ("--> YES: same LMT, different length\n");
567 return 1;
568 }
569 }
570
571 void
572 clientPurgeRequest(clientHttpRequest * http)
573 {
574 StoreEntry *entry;
575 ErrorState *err = NULL;
576 HttpReply *r;
577 http_status status;
578 http_version_t version;
579 debug(33, 3) ("Config2.onoff.enable_purge = %d\n", Config2.onoff.enable_purge);
580 if (!Config2.onoff.enable_purge) {
581 http->log_type = LOG_TCP_DENIED;
582 err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
583 err->request = requestLink(http->request);
584 err->src_addr = http->conn->peer.sin_addr;
585 http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags);
586 errorAppendEntry(http->entry, err);
587 return;
588 }
589 http->log_type = LOG_TCP_MISS;
590 /* Release both IP and object cache entries */
591 ipcacheInvalidate(http->request->host);
592 if ((entry = storeGetPublic(http->uri, METHOD_GET)) == NULL) {
593 status = HTTP_NOT_FOUND;
594 } else {
595 storeRelease(entry);
596 status = HTTP_OK;
597 }
598 debug(33, 4) ("clientPurgeRequest: Not modified '%s'\n",
599 storeUrl(entry));
600 /*
601 * Make a new entry to hold the reply to be written
602 * to the client.
603 */
604 http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags);
605 httpReplyReset(r = http->entry->mem_obj->reply);
606 httpBuildVersion(&version, 1, 0);
607 httpReplySetHeaders(r, version, status, NULL, NULL, 0, 0, -1);
608 httpReplySwapOut(r, http->entry);
609 storeComplete(http->entry);
610 }
611
612 int
613 checkNegativeHit(StoreEntry * e)
614 {
615 if (!EBIT_TEST(e->flags, ENTRY_NEGCACHED))
616 return 0;
617 if (e->expires <= squid_curtime)
618 return 0;
619 if (e->store_status != STORE_OK)
620 return 0;
621 return 1;
622 }
623
624 void
625 clientUpdateCounters(clientHttpRequest * http)
626 {
627 int svc_time = tvSubMsec(http->start, current_time);
628 ping_data *i;
629 HierarchyLogEntry *H;
630 statCounter.client_http.requests++;
631 if (isTcpHit(http->log_type))
632 statCounter.client_http.hits++;
633 if (http->log_type == LOG_TCP_HIT)
634 statCounter.client_http.disk_hits++;
635 else if (http->log_type == LOG_TCP_MEM_HIT)
636 statCounter.client_http.mem_hits++;
637 if (http->request->err_type != ERR_NONE)
638 statCounter.client_http.errors++;
639 statHistCount(&statCounter.client_http.all_svc_time, svc_time);
640 /*
641 * The idea here is not to be complete, but to get service times
642 * for only well-defined types. For example, we don't include
643 * LOG_TCP_REFRESH_FAIL_HIT because its not really a cache hit
644 * (we *tried* to validate it, but failed).
645 */
646 switch (http->log_type) {
647 case LOG_TCP_REFRESH_HIT:
648 statHistCount(&statCounter.client_http.nh_svc_time, svc_time);
649 break;
650 case LOG_TCP_IMS_HIT:
651 statHistCount(&statCounter.client_http.nm_svc_time, svc_time);
652 break;
653 case LOG_TCP_HIT:
654 case LOG_TCP_MEM_HIT:
655 case LOG_TCP_OFFLINE_HIT:
656 statHistCount(&statCounter.client_http.hit_svc_time, svc_time);
657 break;
658 case LOG_TCP_MISS:
659 case LOG_TCP_CLIENT_REFRESH_MISS:
660 statHistCount(&statCounter.client_http.miss_svc_time, svc_time);
661 break;
662 default:
663 /* make compiler warnings go away */
664 break;
665 }
666 H = &http->request->hier;
667 switch (H->alg) {
668 case PEER_SA_DIGEST:
669 statCounter.cd.times_used++;
670 break;
671 case PEER_SA_ICP:
672 statCounter.icp.times_used++;
673 i = &H->ping;
674 if (0 != i->stop.tv_sec && 0 != i->start.tv_sec)
675 statHistCount(&statCounter.icp.query_svc_time,
676 tvSubUsec(i->start, i->stop));
677 if (i->timeout)
678 statCounter.icp.query_timeouts++;
679 break;
680 case PEER_SA_NETDB:
681 statCounter.netdb.times_used++;
682 break;
683 default:
684 break;
685 }
686 }
687
688 static void
689 httpRequestFree(void *data)
690 {
691 clientHttpRequest *http = data;
692 clientHttpRequest **H;
693 ConnStateData *conn = http->conn;
694 StoreEntry *e;
695 request_t *request = http->request;
696 MemObject *mem = NULL;
697 debug(33, 3) ("httpRequestFree: %s\n", storeUrl(http->entry));
698 if (!clientCheckTransferDone(http)) {
699 #if MYSTERIOUS_CODE
700 /*
701 * DW: this seems odd here, is it really needed? It causes
702 * incomplete transfers to get logged with "000" status
703 * code because http->entry becomes NULL.
704 */
705 if ((e = http->entry)) {
706 http->entry = NULL;
707 storeUnregister(http->sc, e, http);
708 storeUnlockObject(e);
709 }
710 #endif
711 if (http->entry && http->entry->ping_status == PING_WAITING)
712 storeReleaseRequest(http->entry);
713 }
714 assert(http->log_type < LOG_TYPE_MAX);
715 if (http->entry)
716 mem = http->entry->mem_obj;
717 if (http->out.size || http->log_type) {
718 http->al.icp.opcode = ICP_INVALID;
719 http->al.url = http->log_uri;
720 debug(33, 9) ("httpRequestFree: al.url='%s'\n", http->al.url);
721 if (mem) {
722 http->al.http.code = mem->reply->sline.status;
723 http->al.http.content_type = strBuf(mem->reply->content_type);
724 }
725 http->al.cache.caddr = conn->log_addr;
726 http->al.cache.size = http->out.size;
727 http->al.cache.code = http->log_type;
728 http->al.cache.msec = tvSubMsec(http->start, current_time);
729 if (request) {
730 Packer p;
731 MemBuf mb;
732 memBufDefInit(&mb);
733 packerToMemInit(&p, &mb);
734 httpHeaderPackInto(&request->header, &p);
735 http->al.http.method = request->method;
736 http->al.http.version = request->http_ver;
737 http->al.headers.request = xstrdup(mb.buf);
738 http->al.hier = request->hier;
739 if (request->user_ident[0])
740 http->al.cache.ident = request->user_ident;
741 else
742 http->al.cache.ident = conn->ident;
743 packerClean(&p);
744 memBufClean(&mb);
745 }
746 accessLogLog(&http->al);
747 clientUpdateCounters(http);
748 clientdbUpdate(conn->peer.sin_addr, http->log_type, PROTO_HTTP, http->out.size);
749 }
750 if (http->acl_checklist)
751 aclChecklistFree(http->acl_checklist);
752 if (request)
753 checkFailureRatio(request->err_type, http->al.hier.code);
754 safe_free(http->uri);
755 safe_free(http->log_uri);
756 safe_free(http->al.headers.request);
757 safe_free(http->al.headers.reply);
758 safe_free(http->redirect.location);
759 stringClean(&http->range_iter.boundary);
760 if ((e = http->entry)) {
761 http->entry = NULL;
762 storeUnregister(http->sc, e, http);
763 http->sc = NULL;
764 storeUnlockObject(e);
765 }
766 /* old_entry might still be set if we didn't yet get the reply
767 * code in clientHandleIMSReply() */
768 if ((e = http->old_entry)) {
769 http->old_entry = NULL;
770 storeUnregister(http->old_sc, e, http);
771 http->old_sc = NULL;
772 storeUnlockObject(e);
773 }
774 requestUnlink(http->request);
775 assert(http != http->next);
776 assert(http->conn->chr != NULL);
777 H = &http->conn->chr;
778 while (*H) {
779 if (*H == http)
780 break;
781 H = &(*H)->next;
782 }
783 assert(*H != NULL);
784 *H = http->next;
785 http->next = NULL;
786 dlinkDelete(&http->active, &ClientActiveRequests);
787 cbdataFree(http);
788 }
789
790 /* This is a handler normally called by comm_close() */
791 static void
792 connStateFree(int fd, void *data)
793 {
794 ConnStateData *connState = data;
795 clientHttpRequest *http;
796 debug(33, 3) ("connStateFree: FD %d\n", fd);
797 assert(connState != NULL);
798 clientdbEstablished(connState->peer.sin_addr, -1); /* decrement */
799 while ((http = connState->chr) != NULL) {
800 assert(http->conn == connState);
801 assert(connState->chr != connState->chr->next);
802 httpRequestFree(http);
803 }
804 if (connState->in.size == CLIENT_REQ_BUF_SZ)
805 memFree(connState->in.buf, MEM_CLIENT_REQ_BUF);
806 else
807 safe_free(connState->in.buf);
808 /* XXX account connState->in.buf */
809 pconnHistCount(0, connState->nrequests);
810 cbdataFree(connState);
811 #ifdef _SQUID_LINUX_
812 /* prevent those nasty RST packets */
813 {
814 char buf[SQUID_TCP_SO_RCVBUF];
815 while (read(fd, buf, SQUID_TCP_SO_RCVBUF) > 0);
816 }
817 #endif
818 }
819
820 static void
821 clientInterpretRequestHeaders(clientHttpRequest * http)
822 {
823 request_t *request = http->request;
824 const HttpHeader *req_hdr = &request->header;
825 int no_cache = 0;
826 #if defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG)
827 const char *str;
828 #endif
829 request->imslen = -1;
830 request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE);
831 if (request->ims > 0)
832 request->flags.ims = 1;
833 if (httpHeaderHas(req_hdr, HDR_PRAGMA)) {
834 String s = httpHeaderGetList(req_hdr, HDR_PRAGMA);
835 if (strListIsMember(&s, "no-cache", ','))
836 no_cache++;
837 stringClean(&s);
838 }
839 request->cache_control = httpHeaderGetCc(req_hdr);
840 if (request->cache_control)
841 if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
842 no_cache++;
843 if (no_cache) {
844 #if HTTP_VIOLATIONS
845 if (Config.onoff.reload_into_ims)
846 request->flags.nocache_hack = 1;
847 else if (refresh_nocache_hack)
848 request->flags.nocache_hack = 1;
849 else
850 #endif
851 request->flags.nocache = 1;
852 }
853 /* ignore range header in non-GETs */
854 if (request->method == METHOD_GET) {
855 request->range = httpHeaderGetRange(req_hdr);
856 if (request->range)
857 request->flags.range = 1;
858 }
859 if (httpHeaderHas(req_hdr, HDR_AUTHORIZATION))
860 request->flags.auth = 1;
861 if (request->login[0] != '\0')
862 request->flags.auth = 1;
863 if (httpHeaderHas(req_hdr, HDR_VIA)) {
864 String s = httpHeaderGetList(req_hdr, HDR_VIA);
865 /*
866 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
867 * Note ThisCache2 has a space prepended to the hostname so we don't
868 * accidentally match super-domains.
869 */
870 if (strListIsSubstr(&s, ThisCache2, ',')) {
871 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
872 request, (ObjPackMethod) & httpRequestPack);
873 request->flags.loopdetect = 1;
874 }
875 #if FORW_VIA_DB
876 fvdbCountVia(strBuf(s));
877 #endif
878 stringClean(&s);
879 }
880 #if USE_USERAGENT_LOG
881 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT)))
882 logUserAgent(fqdnFromAddr(http->conn->peer.sin_addr), str);
883 #endif
884 #if USE_REFERER_LOG
885 if ((str = httpHeaderGetStr(req_hdr, HDR_REFERER)))
886 logReferer(fqdnFromAddr(http->conn->peer.sin_addr), str,
887 http->log_uri);
888 #endif
889 #if FORW_VIA_DB
890 if (httpHeaderHas(req_hdr, HDR_X_FORWARDED_FOR)) {
891 String s = httpHeaderGetList(req_hdr, HDR_X_FORWARDED_FOR);
892 fvdbCountForw(strBuf(s));
893 stringClean(&s);
894 }
895 #endif
896 if (request->method == METHOD_TRACE) {
897 request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS);
898 }
899 if (clientCachable(http))
900 request->flags.cachable = 1;
901 if (clientHierarchical(http))
902 request->flags.hierarchical = 1;
903 debug(33, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n",
904 request->flags.nocache ? "SET" : "NOT SET");
905 debug(33, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n",
906 request->flags.cachable ? "SET" : "NOT SET");
907 debug(33, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n",
908 request->flags.hierarchical ? "SET" : "NOT SET");
909 }
910
911 /*
912 * clientSetKeepaliveFlag() sets request->flags.proxy_keepalive.
913 * This is the client-side persistent connection flag. We need
914 * to set this relatively early in the request processing
915 * to handle hacks for broken servers and clients.
916 */
917 static void
918 clientSetKeepaliveFlag(clientHttpRequest * http)
919 {
920 request_t *request = http->request;
921 const HttpHeader *req_hdr = &request->header;
922 debug(33, 3) ("clientSetKeepaliveFlag: http_ver = %d.%d\n",
923 request->http_ver.major, request->http_ver.minor);
924 debug(33, 3) ("clientSetKeepaliveFlag: method = %s\n",
925 RequestMethodStr[request->method]);
926 if (!Config.onoff.client_pconns)
927 request->flags.proxy_keepalive = 0;
928 else if (httpMsgIsPersistent(request->http_ver, req_hdr))
929 request->flags.proxy_keepalive = 1;
930 }
931
932 static int
933 clientCheckContentLength(request_t * r)
934 {
935 switch (r->method) {
936 case METHOD_PUT:
937 case METHOD_POST:
938 /* PUT/POST requires a request entity */
939 return (r->content_length >= 0);
940 case METHOD_GET:
941 case METHOD_HEAD:
942 /* We do not want to see a request entity on GET/HEAD requests */
943 return (r->content_length <= 0);
944 default:
945 /* For other types of requests we don't care */
946 return 1;
947 }
948 /* NOT REACHED */
949 }
950
951 static int
952 clientCachable(clientHttpRequest * http)
953 {
954 const char *url = http->uri;
955 request_t *req = http->request;
956 method_t method = req->method;
957 if (req->protocol == PROTO_HTTP)
958 return httpCachable(method);
959 /* FTP is always cachable */
960 if (req->protocol == PROTO_GOPHER)
961 return gopherCachable(url);
962 if (req->protocol == PROTO_WAIS)
963 return 0;
964 if (method == METHOD_CONNECT)
965 return 0;
966 if (method == METHOD_TRACE)
967 return 0;
968 if (req->protocol == PROTO_CACHEOBJ)
969 return 0;
970 return 1;
971 }
972
973 /* Return true if we can query our neighbors for this object */
974 static int
975 clientHierarchical(clientHttpRequest * http)
976 {
977 const char *url = http->uri;
978 request_t *request = http->request;
979 method_t method = request->method;
980 const wordlist *p = NULL;
981
982 /* IMS needs a private key, so we can use the hierarchy for IMS only
983 * if our neighbors support private keys */
984 if (request->flags.ims && !neighbors_do_private_keys)
985 return 0;
986 if (request->flags.auth)
987 return 0;
988 if (method == METHOD_TRACE)
989 return 1;
990 if (method != METHOD_GET)
991 return 0;
992 /* scan hierarchy_stoplist */
993 for (p = Config.hierarchy_stoplist; p; p = p->next)
994 if (strstr(url, p->key))
995 return 0;
996 if (request->flags.loopdetect)
997 return 0;
998 if (request->protocol == PROTO_HTTP)
999 return httpCachable(method);
1000 if (request->protocol == PROTO_GOPHER)
1001 return gopherCachable(url);
1002 if (request->protocol == PROTO_WAIS)
1003 return 0;
1004 if (request->protocol == PROTO_CACHEOBJ)
1005 return 0;
1006 return 1;
1007 }
1008
1009 int
1010 isTcpHit(log_type code)
1011 {
1012 /* this should be a bitmap for better optimization */
1013 if (code == LOG_TCP_HIT)
1014 return 1;
1015 if (code == LOG_TCP_IMS_HIT)
1016 return 1;
1017 if (code == LOG_TCP_REFRESH_FAIL_HIT)
1018 return 1;
1019 if (code == LOG_TCP_REFRESH_HIT)
1020 return 1;
1021 if (code == LOG_TCP_NEGATIVE_HIT)
1022 return 1;
1023 if (code == LOG_TCP_MEM_HIT)
1024 return 1;
1025 if (code == LOG_TCP_OFFLINE_HIT)
1026 return 1;
1027 return 0;
1028 }
1029
1030 /*
1031 * returns true if If-Range specs match reply, false otherwise
1032 */
1033 static int
1034 clientIfRangeMatch(clientHttpRequest * http, HttpReply * rep)
1035 {
1036 const TimeOrTag spec = httpHeaderGetTimeOrTag(&http->request->header, HDR_IF_RANGE);
1037 /* check for parsing falure */
1038 if (!spec.valid)
1039 return 0;
1040 /* got an ETag? */
1041 if (spec.tag.str) {
1042 ETag rep_tag = httpHeaderGetETag(&rep->header, HDR_ETAG);
1043 debug(33, 3) ("clientIfRangeMatch: ETags: %s and %s\n",
1044 spec.tag.str, rep_tag.str ? rep_tag.str : "<none>");
1045 if (!rep_tag.str)
1046 return 0; /* entity has no etag to compare with! */
1047 if (spec.tag.weak || rep_tag.weak) {
1048 debug(33, 1) ("clientIfRangeMatch: Weak ETags are not allowed in If-Range: %s ? %s\n",
1049 spec.tag.str, rep_tag.str);
1050 return 0; /* must use strong validator for sub-range requests */
1051 }
1052 return etagIsEqual(&rep_tag, &spec.tag);
1053 }
1054 /* got modification time? */
1055 if (spec.time >= 0) {
1056 return http->entry->lastmod <= spec.time;
1057 }
1058 assert(0); /* should not happen */
1059 return 0;
1060 }
1061
1062 /* returns expected content length for multi-range replies
1063 * note: assumes that httpHdrRangeCanonize has already been called
1064 * warning: assumes that HTTP headers for individual ranges at the
1065 * time of the actuall assembly will be exactly the same as
1066 * the headers when clientMRangeCLen() is called */
1067 static int
1068 clientMRangeCLen(clientHttpRequest * http)
1069 {
1070 int clen = 0;
1071 HttpHdrRangePos pos = HttpHdrRangeInitPos;
1072 const HttpHdrRangeSpec *spec;
1073 MemBuf mb;
1074
1075 assert(http->entry->mem_obj);
1076
1077 memBufDefInit(&mb);
1078 while ((spec = httpHdrRangeGetSpec(http->request->range, &pos))) {
1079
1080 /* account for headers for this range */
1081 memBufReset(&mb);
1082 clientPackRangeHdr(http->entry->mem_obj->reply,
1083 spec, http->range_iter.boundary, &mb);
1084 clen += mb.size;
1085
1086 /* account for range content */
1087 clen += spec->length;
1088
1089 debug(33, 6) ("clientMRangeCLen: (clen += %d + %d) == %d\n",
1090 mb.size, spec->length, clen);
1091 }
1092 /* account for the terminating boundary */
1093 memBufReset(&mb);
1094 clientPackTermBound(http->range_iter.boundary, &mb);
1095 clen += mb.size;
1096
1097 memBufClean(&mb);
1098 return clen;
1099 }
1100
1101 /* adds appropriate Range headers if needed */
1102 static void
1103 clientBuildRangeHeader(clientHttpRequest * http, HttpReply * rep)
1104 {
1105 HttpHeader *hdr = rep ? &rep->header : 0;
1106 const char *range_err = NULL;
1107 request_t *request = http->request;
1108 assert(request->range);
1109 /* check if we still want to do ranges */
1110 if (!rep)
1111 range_err = "no [parse-able] reply";
1112 else if (rep->sline.status != HTTP_OK)
1113 range_err = "wrong status code";
1114 else if (httpHeaderHas(hdr, HDR_CONTENT_RANGE))
1115 range_err = "origin server does ranges";
1116 else if (rep->content_length < 0)
1117 range_err = "unknown length";
1118 else if (rep->content_length != http->entry->mem_obj->reply->content_length)
1119 range_err = "INCONSISTENT length"; /* a bug? */
1120 else if (httpHeaderHas(&http->request->header, HDR_IF_RANGE) && !clientIfRangeMatch(http, rep))
1121 range_err = "If-Range match failed";
1122 else if (!httpHdrRangeCanonize(http->request->range, rep->content_length))
1123 range_err = "canonization failed";
1124 else if (httpHdrRangeIsComplex(http->request->range))
1125 range_err = "too complex range header";
1126 else if (!request->flags.cachable) /* from we_do_ranges in http.c */
1127 range_err = "non-cachable request";
1128 /* get rid of our range specs on error */
1129 if (range_err) {
1130 debug(33, 3) ("clientBuildRangeHeader: will not do ranges: %s.\n", range_err);
1131 httpHdrRangeDestroy(http->request->range);
1132 http->request->range = NULL;
1133 } else {
1134 const int spec_count = http->request->range->specs.count;
1135 int actual_clen = -1;
1136
1137 debug(33, 3) ("clientBuildRangeHeader: range spec count: %d virgin clen: %d\n",
1138 spec_count, rep->content_length);
1139 assert(spec_count > 0);
1140 /* ETags should not be returned with Partial Content replies? */
1141 httpHeaderDelById(hdr, HDR_ETAG);
1142 /* append appropriate header(s) */
1143 if (spec_count == 1) {
1144 HttpHdrRangePos pos = HttpHdrRangeInitPos;
1145 const HttpHdrRangeSpec *spec = httpHdrRangeGetSpec(http->request->range, &pos);
1146 assert(spec);
1147 /* append Content-Range */
1148 httpHeaderAddContRange(hdr, *spec, rep->content_length);
1149 /* set new Content-Length to the actual number of bytes
1150 * transmitted in the message-body */
1151 actual_clen = spec->length;
1152 } else {
1153 /* multipart! */
1154 /* generate boundary string */
1155 http->range_iter.boundary = httpHdrRangeBoundaryStr(http);
1156 /* delete old Content-Type, add ours */
1157 httpHeaderDelById(hdr, HDR_CONTENT_TYPE);
1158 httpHeaderPutStrf(hdr, HDR_CONTENT_TYPE,
1159 "multipart/byteranges; boundary=\"%s\"",
1160 strBuf(http->range_iter.boundary));
1161 /* Content-Length is not required in multipart responses
1162 * but it is always nice to have one */
1163 actual_clen = clientMRangeCLen(http);
1164 }
1165
1166 /* replace Content-Length header */
1167 assert(actual_clen >= 0);
1168 httpHeaderDelById(hdr, HDR_CONTENT_LENGTH);
1169 httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, actual_clen);
1170 debug(33, 3) ("clientBuildRangeHeader: actual content length: %d\n", actual_clen);
1171 }
1172 }
1173
1174 /*
1175 * filters out unwanted entries from original reply header
1176 * adds extra entries if we have more info than origin server
1177 * adds Squid specific entries
1178 */
1179 static void
1180 clientBuildReplyHeader(clientHttpRequest * http, HttpReply * rep)
1181 {
1182 HttpHeader *hdr = &rep->header;
1183 int is_hit = isTcpHit(http->log_type);
1184 request_t *request = http->request;
1185 #if DONT_FILTER_THESE
1186 /* but you might want to if you run Squid as an HTTP accelerator */
1187 /* httpHeaderDelById(hdr, HDR_ACCEPT_RANGES); */
1188 httpHeaderDelById(hdr, HDR_ETAG);
1189 #endif
1190 httpHeaderDelById(hdr, HDR_PROXY_CONNECTION);
1191 /* here: Keep-Alive is a field-name, not a connection directive! */
1192 httpHeaderDelByName(hdr, "Keep-Alive");
1193 /* remove Set-Cookie if a hit */
1194 if (is_hit)
1195 httpHeaderDelById(hdr, HDR_SET_COOKIE);
1196 /* handle Connection header */
1197 if (httpHeaderHas(hdr, HDR_CONNECTION)) {
1198 /* anything that matches Connection list member will be deleted */
1199 String strConnection = httpHeaderGetList(hdr, HDR_CONNECTION);
1200 const HttpHeaderEntry *e;
1201 HttpHeaderPos pos = HttpHeaderInitPos;
1202 /*
1203 * think: on-average-best nesting of the two loops (hdrEntry
1204 * and strListItem) @?@
1205 */
1206 /*
1207 * maybe we should delete standard stuff ("keep-alive","close")
1208 * from strConnection first?
1209 */
1210 while ((e = httpHeaderGetEntry(hdr, &pos))) {
1211 if (strListIsMember(&strConnection, strBuf(e->name), ','))
1212 httpHeaderDelAt(hdr, pos);
1213 }
1214 httpHeaderDelById(hdr, HDR_CONNECTION);
1215 stringClean(&strConnection);
1216 }
1217 /* Handle Ranges */
1218 if (request->range)
1219 clientBuildRangeHeader(http, rep);
1220 /*
1221 * Add a estimated Age header on cache hits.
1222 */
1223 if (is_hit) {
1224 /*
1225 * Remove any existing Age header sent by upstream caches
1226 * (note that the existing header is passed along unmodified
1227 * on cache misses)
1228 */
1229 httpHeaderDelById(hdr, HDR_AGE);
1230 /*
1231 * This adds the calculated object age. Note that the details of the
1232 * age calculation is performed by adjusting the timestamp in
1233 * storeTimestampsSet(), not here.
1234 *
1235 * BROWSER WORKAROUND: IE sometimes hangs when receiving a 0 Age
1236 * header, so don't use it unless there is a age to report. Please
1237 * note that Age is only used to make a conservative estimation of
1238 * the objects age, so a Age: 0 header does not add any useful
1239 * information to the reply in any case.
1240 */
1241 if (http->entry->timestamp > -1)
1242 if (http->entry->timestamp < squid_curtime)
1243 httpHeaderPutInt(hdr, HDR_AGE,
1244 squid_curtime - http->entry->timestamp);
1245 }
1246 /* Append X-Cache */
1247 httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",
1248 is_hit ? "HIT" : "MISS", getMyHostname());
1249 #if USE_CACHE_DIGESTS
1250 /* Append X-Cache-Lookup: -- temporary hack, to be removed @?@ @?@ */
1251 httpHeaderPutStrf(hdr, HDR_X_CACHE_LOOKUP, "%s from %s:%d",
1252 http->lookup_type ? http->lookup_type : "NONE",
1253 getMyHostname(), ntohs(Config.Sockaddr.http->s.sin_port));
1254 #endif
1255 if (httpReplyBodySize(request->method, rep) < 0) {
1256 debug(33, 3) ("clientBuildReplyHeader: can't keep-alive, unknown body size\n");
1257 request->flags.proxy_keepalive = 0;
1258 }
1259 /* Signal keep-alive if needed */
1260 httpHeaderPutStr(hdr,
1261 http->flags.accel ? HDR_CONNECTION : HDR_PROXY_CONNECTION,
1262 request->flags.proxy_keepalive ? "keep-alive" : "close");
1263 #if ADD_X_REQUEST_URI
1264 /*
1265 * Knowing the URI of the request is useful when debugging persistent
1266 * connections in a client; we cannot guarantee the order of http headers,
1267 * but X-Request-URI is likely to be the very last header to ease use from a
1268 * debugger [hdr->entries.count-1].
1269 */
1270 httpHeaderPutStr(hdr, HDR_X_REQUEST_URI,
1271 http->entry->mem_obj->url ? http->entry->mem_obj->url : http->uri);
1272 #endif
1273 }
1274
1275 static HttpReply *
1276 clientBuildReply(clientHttpRequest * http, const char *buf, size_t size)
1277 {
1278 HttpReply *rep = httpReplyCreate();
1279 size_t k = headersEnd(buf, size);
1280 if (k && httpReplyParse(rep, buf, k)) {
1281 /* enforce 1.0 reply version */
1282 httpBuildVersion(&rep->sline.version, 1, 0);
1283 /* do header conversions */
1284 clientBuildReplyHeader(http, rep);
1285 /* if we do ranges, change status to "Partial Content" */
1286 if (http->request->range)
1287 httpStatusLineSet(&rep->sline, rep->sline.version,
1288 HTTP_PARTIAL_CONTENT, NULL);
1289 } else {
1290 /* parsing failure, get rid of the invalid reply */
1291 httpReplyDestroy(rep);
1292 rep = NULL;
1293 /* if we were going to do ranges, backoff */
1294 if (http->request->range) {
1295 /* this will fail and destroy request->range */
1296 clientBuildRangeHeader(http, rep);
1297 }
1298 }
1299 return rep;
1300 }
1301
1302 /*
1303 * clientCacheHit should only be called until the HTTP reply headers
1304 * have been parsed. Normally this should be a single call, but
1305 * it might take more than one. As soon as we have the headers,
1306 * we hand off to clientSendMoreData, clientProcessExpired, or
1307 * clientProcessMiss.
1308 */
1309 static void
1310 clientCacheHit(void *data, char *buf, ssize_t size)
1311 {
1312 clientHttpRequest *http = data;
1313 StoreEntry *e = http->entry;
1314 MemObject *mem;
1315 request_t *r = http->request;
1316 debug(33, 3) ("clientCacheHit: %s, %d bytes\n", http->uri, (int) size);
1317 if (http->entry == NULL) {
1318 memFree(buf, MEM_CLIENT_SOCK_BUF);
1319 debug(33, 3) ("clientCacheHit: request aborted\n");
1320 return;
1321 } else if (size < 0) {
1322 /* swap in failure */
1323 memFree(buf, MEM_CLIENT_SOCK_BUF);
1324 debug(33, 3) ("clientCacheHit: swapin failure for %s\n", http->uri);
1325 http->log_type = LOG_TCP_SWAPFAIL_MISS;
1326 if ((e = http->entry)) {
1327 http->entry = NULL;
1328 storeUnregister(http->sc, e, http);
1329 http->sc = NULL;
1330 storeUnlockObject(e);
1331 }
1332 clientProcessMiss(http);
1333 return;
1334 }
1335 assert(size > 0);
1336 mem = e->mem_obj;
1337 assert(!EBIT_TEST(e->flags, ENTRY_ABORTED));
1338 if (mem->reply->sline.status == 0) {
1339 /*
1340 * we don't have full reply headers yet; either wait for more or
1341 * punt to clientProcessMiss.
1342 */
1343 if (e->mem_status == IN_MEMORY || e->store_status == STORE_OK) {
1344 memFree(buf, MEM_CLIENT_SOCK_BUF);
1345 clientProcessMiss(http);
1346 } else if (size == CLIENT_SOCK_SZ && http->out.offset == 0) {
1347 memFree(buf, MEM_CLIENT_SOCK_BUF);
1348 clientProcessMiss(http);
1349 } else {
1350 debug(33, 3) ("clientCacheHit: waiting for HTTP reply headers\n");
1351 storeClientCopy(http->sc, e,
1352 http->out.offset + size,
1353 http->out.offset,
1354 CLIENT_SOCK_SZ,
1355 buf,
1356 clientCacheHit,
1357 http);
1358 }
1359 return;
1360 }
1361 /*
1362 * Got the headers, now grok them
1363 */
1364 assert(http->log_type == LOG_TCP_HIT);
1365 if (checkNegativeHit(e)) {
1366 http->log_type = LOG_TCP_NEGATIVE_HIT;
1367 clientSendMoreData(data, buf, size);
1368 } else if (r->method == METHOD_HEAD) {
1369 /*
1370 * RFC 2068 seems to indicate there is no "conditional HEAD"
1371 * request. We cannot validate a cached object for a HEAD
1372 * request, nor can we return 304.
1373 */
1374 if (e->mem_status == IN_MEMORY)
1375 http->log_type = LOG_TCP_MEM_HIT;
1376 clientSendMoreData(data, buf, size);
1377 } else if (refreshCheckHTTP(e, r) && !http->flags.internal) {
1378 debug(33, 5) ("clientCacheHit: in refreshCheck() block\n");
1379 /*
1380 * We hold a stale copy; it needs to be validated
1381 */
1382 /*
1383 * The 'need_validation' flag is used to prevent forwarding
1384 * loops between siblings. If our copy of the object is stale,
1385 * then we should probably only use parents for the validation
1386 * request. Otherwise two siblings could generate a loop if
1387 * both have a stale version of the object.
1388 */
1389 r->flags.need_validation = 1;
1390 if (e->lastmod < 0) {
1391 /*
1392 * Previous reply didn't have a Last-Modified header,
1393 * we cannot revalidate it.
1394 */
1395 http->log_type = LOG_TCP_MISS;
1396 clientProcessMiss(http);
1397 } else if (r->flags.nocache) {
1398 /*
1399 * This did not match a refresh pattern that overrides no-cache
1400 * we should honour the client no-cache header.
1401 */
1402 http->log_type = LOG_TCP_CLIENT_REFRESH_MISS;
1403 clientProcessMiss(http);
1404 } else if (r->protocol == PROTO_HTTP) {
1405 /*
1406 * Object needs to be revalidated
1407 * XXX This could apply to FTP as well, if Last-Modified is known.
1408 */
1409 http->log_type = LOG_TCP_REFRESH_MISS;
1410 clientProcessExpired(http);
1411 } else {
1412 /*
1413 * We don't know how to re-validate other protocols. Handle
1414 * them as if the object has expired.
1415 */
1416 http->log_type = LOG_TCP_MISS;
1417 clientProcessMiss(http);
1418 }
1419 memFree(buf, MEM_CLIENT_SOCK_BUF);
1420 } else if (r->flags.ims) {
1421 /*
1422 * Handle If-Modified-Since requests from the client
1423 */
1424 if (mem->reply->sline.status != HTTP_OK) {
1425 debug(33, 4) ("clientCacheHit: Reply code %d != 200\n",
1426 mem->reply->sline.status);
1427 memFree(buf, MEM_CLIENT_SOCK_BUF);
1428 http->log_type = LOG_TCP_MISS;
1429 clientProcessMiss(http);
1430 } else if (modifiedSince(e, http->request)) {
1431 http->log_type = LOG_TCP_IMS_HIT;
1432 clientSendMoreData(data, buf, size);
1433 } else {
1434 time_t timestamp = e->timestamp;
1435 MemBuf mb = httpPacked304Reply(e->mem_obj->reply);
1436 http->log_type = LOG_TCP_IMS_HIT;
1437 memFree(buf, MEM_CLIENT_SOCK_BUF);
1438 storeUnregister(http->sc, e, http);
1439 http->sc = NULL;
1440 storeUnlockObject(e);
1441 e = clientCreateStoreEntry(http, http->request->method, null_request_flags);
1442 /*
1443 * Copy timestamp from the original entry so the 304
1444 * reply has a meaningful Age: header.
1445 */
1446 e->timestamp = timestamp;
1447 http->entry = e;
1448 httpReplyParse(e->mem_obj->reply, mb.buf, mb.size);
1449 storeAppend(e, mb.buf, mb.size);
1450 memBufClean(&mb);
1451 storeComplete(e);
1452 }
1453 } else {
1454 /*
1455 * plain ol' cache hit
1456 */
1457 if (e->mem_status == IN_MEMORY)
1458 http->log_type = LOG_TCP_MEM_HIT;
1459 else if (Config.onoff.offline)
1460 http->log_type = LOG_TCP_OFFLINE_HIT;
1461 clientSendMoreData(data, buf, size);
1462 }
1463 }
1464
1465 /* put terminating boundary for multiparts */
1466 static void
1467 clientPackTermBound(String boundary, MemBuf * mb)
1468 {
1469 memBufPrintf(mb, "\r\n--%s--\r\n", strBuf(boundary));
1470 debug(33, 6) ("clientPackTermBound: buf offset: %d\n", mb->size);
1471 }
1472
1473 /* appends a "part" HTTP header (as in a multi-part/range reply) to the buffer */
1474 static void
1475 clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
1476 {
1477 HttpHeader hdr;
1478 Packer p;
1479 assert(rep);
1480 assert(spec);
1481
1482 /* put boundary */
1483 debug(33, 5) ("clientPackRangeHdr: appending boundary: %s\n", strBuf(boundary));
1484 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
1485 memBufPrintf(mb, "\r\n--%s\r\n", strBuf(boundary));
1486
1487 /* stuff the header with required entries and pack it */
1488 httpHeaderInit(&hdr, hoReply);
1489 if (httpHeaderHas(&rep->header, HDR_CONTENT_TYPE))
1490 httpHeaderPutStr(&hdr, HDR_CONTENT_TYPE, httpHeaderGetStr(&rep->header, HDR_CONTENT_TYPE));
1491 httpHeaderAddContRange(&hdr, *spec, rep->content_length);
1492 packerToMemInit(&p, mb);
1493 httpHeaderPackInto(&hdr, &p);
1494 packerClean(&p);
1495 httpHeaderClean(&hdr);
1496
1497 /* append <crlf> (we packed a header, not a reply) */
1498 memBufPrintf(mb, crlf);
1499 }
1500
1501 /*
1502 * extracts a "range" from *buf and appends them to mb, updating
1503 * all offsets and such.
1504 */
1505 static void
1506 clientPackRange(clientHttpRequest * http,
1507 HttpHdrRangeIter * i,
1508 const char **buf,
1509 ssize_t * size,
1510 MemBuf * mb)
1511 {
1512 const ssize_t copy_sz = i->debt_size <= *size ? i->debt_size : *size;
1513 off_t body_off = http->out.offset - i->prefix_size;
1514 assert(*size > 0);
1515 assert(i->spec);
1516 /*
1517 * intersection of "have" and "need" ranges must not be empty
1518 */
1519 assert(body_off < i->spec->offset + i->spec->length);
1520 assert(body_off + *size > i->spec->offset);
1521 /*
1522 * put boundary and headers at the beginning of a range in a
1523 * multi-range
1524 */
1525 if (http->request->range->specs.count > 1 && i->debt_size == i->spec->length) {
1526 assert(http->entry->mem_obj);
1527 clientPackRangeHdr(
1528 http->entry->mem_obj->reply, /* original reply */
1529 i->spec, /* current range */
1530 i->boundary, /* boundary, the same for all */
1531 mb
1532 );
1533 }
1534 /*
1535 * append content
1536 */
1537 debug(33, 3) ("clientPackRange: appending %d bytes\n", copy_sz);
1538 memBufAppend(mb, *buf, copy_sz);
1539 /*
1540 * update offsets
1541 */
1542 *size -= copy_sz;
1543 i->debt_size -= copy_sz;
1544 body_off += copy_sz;
1545 *buf += copy_sz;
1546 http->out.offset = body_off + i->prefix_size; /* sync */
1547 /*
1548 * paranoid check
1549 */
1550 assert(*size >= 0 && i->debt_size >= 0);
1551 }
1552
1553 /* returns true if there is still data available to pack more ranges
1554 * increments iterator "i"
1555 * used by clientPackMoreRanges */
1556 static int
1557 clientCanPackMoreRanges(const clientHttpRequest * http, HttpHdrRangeIter * i, ssize_t size)
1558 {
1559 /* first update "i" if needed */
1560 if (!i->debt_size) {
1561 if ((i->spec = httpHdrRangeGetSpec(http->request->range, &i->pos)))
1562 i->debt_size = i->spec->length;
1563 }
1564 assert(!i->debt_size == !i->spec); /* paranoid sync condition */
1565 /* continue condition: need_more_data && have_more_data */
1566 return i->spec && size > 0;
1567 }
1568
1569 /* extracts "ranges" from buf and appends them to mb, updating all offsets and such */
1570 /* returns true if we need more data */
1571 static int
1572 clientPackMoreRanges(clientHttpRequest * http, const char *buf, ssize_t size, MemBuf * mb)
1573 {
1574 HttpHdrRangeIter *i = &http->range_iter;
1575 /* offset in range specs does not count the prefix of an http msg */
1576 off_t body_off = http->out.offset - i->prefix_size;
1577 assert(size >= 0);
1578 /* check: reply was parsed and range iterator was initialized */
1579 assert(i->prefix_size > 0);
1580 /* filter out data according to range specs */
1581 while (clientCanPackMoreRanges(http, i, size)) {
1582 off_t start; /* offset of still missing data */
1583 assert(i->spec);
1584 start = i->spec->offset + i->spec->length - i->debt_size;
1585 debug(33, 3) ("clientPackMoreRanges: in: offset: %d size: %d\n",
1586 (int) body_off, size);
1587 debug(33, 3) ("clientPackMoreRanges: out: start: %d spec[%d]: [%d, %d), len: %d debt: %d\n",
1588 (int) start, (int) i->pos, i->spec->offset, (int) (i->spec->offset + i->spec->length), i->spec->length, i->debt_size);
1589 assert(body_off <= start); /* we did not miss it */
1590 /* skip up to start */
1591 if (body_off + size > start) {
1592 const size_t skip_size = start - body_off;
1593 body_off = start;
1594 size -= skip_size;
1595 buf += skip_size;
1596 } else {
1597 /* has not reached start yet */
1598 body_off += size;
1599 size = 0;
1600 buf = NULL;
1601 }
1602 /* put next chunk if any */
1603 if (size) {
1604 http->out.offset = body_off + i->prefix_size; /* sync */
1605 clientPackRange(http, i, &buf, &size, mb);
1606 body_off = http->out.offset - i->prefix_size; /* sync */
1607 }
1608 }
1609 assert(!i->debt_size == !i->spec); /* paranoid sync condition */
1610 debug(33, 3) ("clientPackMoreRanges: buf exhausted: in: offset: %d size: %d need_more: %d\n",
1611 (int) body_off, size, i->debt_size);
1612 if (i->debt_size) {
1613 debug(33, 3) ("clientPackMoreRanges: need more: spec[%d]: [%d, %d), len: %d\n",
1614 (int) i->pos, i->spec->offset, (int) (i->spec->offset + i->spec->length), i->spec->length);
1615 /* skip the data we do not need if possible */
1616 if (i->debt_size == i->spec->length) /* at the start of the cur. spec */
1617 body_off = i->spec->offset;
1618 else
1619 assert(body_off == i->spec->offset + i->spec->length - i->debt_size);
1620 } else if (http->request->range->specs.count > 1) {
1621 /* put terminating boundary for multiparts */
1622 clientPackTermBound(i->boundary, mb);
1623 }
1624 http->out.offset = body_off + i->prefix_size; /* sync */
1625 return i->debt_size > 0;
1626 }
1627
1628 static int
1629 clientReplyBodyTooLarge(int clen)
1630 {
1631 if (0 == Config.maxReplyBodySize)
1632 return 0; /* disabled */
1633 if (clen < 0)
1634 return 0; /* unknown */
1635 if (clen > Config.maxReplyBodySize)
1636 return 1; /* too large */
1637 return 0;
1638 }
1639
1640 static int
1641 clientRequestBodyTooLarge(int clen)
1642 {
1643 if (0 == Config.maxRequestBodySize)
1644 return 0; /* disabled */
1645 if (clen < 0)
1646 return 0; /* unknown, bug? */
1647 if (clen > Config.maxRequestBodySize)
1648 return 1; /* too large */
1649 return 0;
1650 }
1651
1652 /*
1653 * accepts chunk of a http message in buf, parses prefix, filters headers and
1654 * such, writes processed message to the client's socket
1655 */
1656 static void
1657 clientSendMoreData(void *data, char *buf, ssize_t size)
1658 {
1659 clientHttpRequest *http = data;
1660 StoreEntry *entry = http->entry;
1661 ConnStateData *conn = http->conn;
1662 int fd = conn->fd;
1663 HttpReply *rep = NULL;
1664 const char *body_buf = buf;
1665 ssize_t body_size = size;
1666 MemBuf mb;
1667 ssize_t check_size = 0;
1668 debug(33, 5) ("clientSendMoreData: %s, %d bytes\n", http->uri, (int) size);
1669 assert(size <= CLIENT_SOCK_SZ);
1670 assert(http->request != NULL);
1671 dlinkDelete(&http->active, &ClientActiveRequests);
1672 dlinkAdd(http, &http->active, &ClientActiveRequests);
1673 debug(33, 5) ("clientSendMoreData: FD %d '%s', out.offset=%d \n",
1674 fd, storeUrl(entry), (int) http->out.offset);
1675 if (conn->chr != http) {
1676 /* there is another object in progress, defer this one */
1677 debug(33, 1) ("clientSendMoreData: Deferring %s\n", storeUrl(entry));
1678 memFree(buf, MEM_CLIENT_SOCK_BUF);
1679 return;
1680 } else if (entry && EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1681 /* call clientWriteComplete so the client socket gets closed */
1682 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
1683 memFree(buf, MEM_CLIENT_SOCK_BUF);
1684 return;
1685 } else if (size < 0) {
1686 /* call clientWriteComplete so the client socket gets closed */
1687 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
1688 memFree(buf, MEM_CLIENT_SOCK_BUF);
1689 return;
1690 } else if (size == 0) {
1691 /* call clientWriteComplete so the client socket gets closed */
1692 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
1693 memFree(buf, MEM_CLIENT_SOCK_BUF);
1694 return;
1695 }
1696 if (http->out.offset == 0) {
1697 if (Config.onoff.log_mime_hdrs) {
1698 size_t k;
1699 if ((k = headersEnd(buf, size))) {
1700 safe_free(http->al.headers.reply);
1701 http->al.headers.reply = xcalloc(k + 1, 1);
1702 xstrncpy(http->al.headers.reply, buf, k);
1703 }
1704 }
1705 rep = clientBuildReply(http, buf, size);
1706 if (rep && clientReplyBodyTooLarge(rep->content_length)) {
1707 ErrorState *err = errorCon(ERR_TOO_BIG, HTTP_FORBIDDEN);
1708 err->request = requestLink(http->request);
1709 storeUnregister(http->sc, http->entry, http);
1710 http->sc = NULL;
1711 storeUnlockObject(http->entry);
1712 http->entry = clientCreateStoreEntry(http, http->request->method,
1713 null_request_flags);
1714 errorAppendEntry(http->entry, err);
1715 httpReplyDestroy(rep);
1716 return;
1717 } else if (rep) {
1718 body_size = size - rep->hdr_sz;
1719 assert(body_size >= 0);
1720 body_buf = buf + rep->hdr_sz;
1721 http->range_iter.prefix_size = rep->hdr_sz;
1722 debug(33, 3) ("clientSendMoreData: Appending %d bytes after %d bytes of headers\n",
1723 body_size, rep->hdr_sz);
1724 } else if (size < CLIENT_SOCK_SZ && entry->store_status == STORE_PENDING) {
1725 /* wait for more to arrive */
1726 storeClientCopy(http->sc, entry,
1727 http->out.offset + size,
1728 http->out.offset,
1729 CLIENT_SOCK_SZ,
1730 buf,
1731 clientSendMoreData,
1732 http);
1733 return;
1734 }
1735 /* reset range iterator */
1736 http->range_iter.pos = HttpHdrRangeInitPos;
1737 } else if (!http->request->range) {
1738 /* Avoid copying to MemBuf for non-range requests */
1739 /* Note, if we're here, then 'rep' is known to be NULL */
1740 http->out.offset += body_size;
1741 comm_write(fd, buf, size, clientWriteBodyComplete, http, NULL);
1742 /* NULL because clientWriteBodyComplete frees it */
1743 return;
1744 }
1745 if (http->request->method == METHOD_HEAD) {
1746 if (rep) {
1747 /* do not forward body for HEAD replies */
1748 body_size = 0;
1749 http->flags.done_copying = 1;
1750 } else {
1751 /*
1752 * If we are here, then store_status == STORE_OK and it
1753 * seems we have a HEAD repsponse which is missing the
1754 * empty end-of-headers line (home.mira.net, phttpd/0.99.72
1755 * does this). Because clientBuildReply() fails we just
1756 * call this reply a body, set the done_copying flag and
1757 * continue...
1758 */
1759 http->flags.done_copying = 1;
1760 }
1761 }
1762 /* write headers and/or body if any */
1763 assert(rep || (body_buf && body_size));
1764 /* init mb; put status line and headers if any */
1765 if (rep) {
1766 mb = httpReplyPack(rep);
1767 http->out.offset += rep->hdr_sz;
1768 check_size += rep->hdr_sz;
1769 #if HEADERS_LOG
1770 headersLog(0, 0, http->request->method, rep);
1771 #endif
1772 httpReplyDestroy(rep);
1773 rep = NULL;
1774 } else {
1775 memBufDefInit(&mb);
1776 }
1777 /* append body if any */
1778 if (http->request->range) {
1779 /* Only GET requests should have ranges */
1780 assert(http->request->method == METHOD_GET);
1781 /* clientPackMoreRanges() updates http->out.offset */
1782 /* force the end of the transfer if we are done */
1783 if (!clientPackMoreRanges(http, body_buf, body_size, &mb))
1784 http->flags.done_copying = 1;
1785 } else if (body_buf && body_size) {
1786 http->out.offset += body_size;
1787 check_size += body_size;
1788 memBufAppend(&mb, body_buf, body_size);
1789 }
1790 if (!http->request->range && http->request->method == METHOD_GET)
1791 assert(check_size == size);
1792 /* write */
1793 comm_write_mbuf(fd, mb, clientWriteComplete, http);
1794 /* if we don't do it, who will? */
1795 memFree(buf, MEM_CLIENT_SOCK_BUF);
1796 }
1797
1798 /*
1799 * clientWriteBodyComplete is called for MEM_CLIENT_SOCK_BUF's
1800 * written directly to the client socket, versus copying to a MemBuf
1801 * and going through comm_write_mbuf. Most non-range responses after
1802 * the headers probably go through here.
1803 */
1804 static void
1805 clientWriteBodyComplete(int fd, char *buf, size_t size, int errflag, void *data)
1806 {
1807 /*
1808 * NOTE: clientWriteComplete doesn't currently use its "buf"
1809 * (second) argument, so we pass in NULL.
1810 */
1811 clientWriteComplete(fd, NULL, size, errflag, data);
1812 memFree(buf, MEM_CLIENT_SOCK_BUF);
1813 }
1814
1815 static void
1816 clientKeepaliveNextRequest(clientHttpRequest * http)
1817 {
1818 ConnStateData *conn = http->conn;
1819 StoreEntry *entry;
1820 debug(33, 3) ("clientKeepaliveNextRequest: FD %d\n", conn->fd);
1821 conn->defer.until = 0; /* Kick it to read a new request */
1822 httpRequestFree(http);
1823 if ((http = conn->chr) == NULL) {
1824 debug(33, 5) ("clientKeepaliveNextRequest: FD %d reading next req\n",
1825 conn->fd);
1826 fd_note(conn->fd, "Reading next request");
1827 /*
1828 * Set the timeout BEFORE calling clientReadRequest().
1829 */
1830 commSetTimeout(conn->fd, Config.Timeout.pconn, requestTimeout, conn);
1831 /*
1832 * CYGWIN has a problem and is blocking on read() requests when there
1833 * is no data present.
1834 * This hack may hit performance a little, but it's better than
1835 * blocking!.
1836 */
1837 #ifdef _SQUID_CYGWIN_
1838 commSetSelect(conn->fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
1839 #else
1840 clientReadRequest(conn->fd, conn); /* Read next request */
1841 #endif
1842 /*
1843 * Note, the FD may be closed at this point.
1844 */
1845 } else if ((entry = http->entry) == NULL) {
1846 /*
1847 * this request is in progress, maybe doing an ACL or a redirect,
1848 * execution will resume after the operation completes.
1849 */
1850 } else {
1851 debug(33, 1) ("clientKeepaliveNextRequest: FD %d Sending next\n",
1852 conn->fd);
1853 assert(entry);
1854 if (0 == storeClientCopyPending(http->sc, entry, http)) {
1855 if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
1856 debug(33, 0) ("clientKeepaliveNextRequest: ENTRY_ABORTED\n");
1857 storeClientCopy(http->sc, entry,
1858 http->out.offset,
1859 http->out.offset,
1860 CLIENT_SOCK_SZ,
1861 memAllocate(MEM_CLIENT_SOCK_BUF),
1862 clientSendMoreData,
1863 http);
1864 }
1865 }
1866 }
1867
1868 static void
1869 clientWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
1870 {
1871 clientHttpRequest *http = data;
1872 StoreEntry *entry = http->entry;
1873 int done;
1874 http->out.size += size;
1875 debug(33, 5) ("clientWriteComplete: FD %d, sz %d, err %d, off %d, len %d\n",
1876 fd, size, errflag, (int) http->out.offset, entry ? objectLen(entry) : 0);
1877 if (size > 0) {
1878 kb_incr(&statCounter.client_http.kbytes_out, size);
1879 if (isTcpHit(http->log_type))
1880 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
1881 }
1882 if (errflag) {
1883 /*
1884 * just close the socket, httpRequestFree will abort if needed
1885 */
1886 comm_close(fd);
1887 } else if (NULL == entry) {
1888 comm_close(fd); /* yuk */
1889 } else if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1890 comm_close(fd);
1891 } else if ((done = clientCheckTransferDone(http)) != 0 || size == 0) {
1892 debug(33, 5) ("clientWriteComplete: FD %d transfer is DONE\n", fd);
1893 /* We're finished case */
1894 if (httpReplyBodySize(http->request->method, entry->mem_obj->reply) < 0) {
1895 debug(33, 5) ("clientWriteComplete: closing, content_length < 0\n");
1896 comm_close(fd);
1897 } else if (!done) {
1898 debug(33, 5) ("clientWriteComplete: closing, !done\n");
1899 comm_close(fd);
1900 } else if (clientGotNotEnough(http)) {
1901 debug(33, 5) ("clientWriteComplete: client didn't get all it expected\n");
1902 comm_close(fd);
1903 } else if (http->request->flags.proxy_keepalive) {
1904 debug(33, 5) ("clientWriteComplete: FD %d Keeping Alive\n", fd);
1905 clientKeepaliveNextRequest(http);
1906 } else {
1907 comm_close(fd);
1908 }
1909 } else if (clientReplyBodyTooLarge((int) http->out.offset)) {
1910 comm_close(fd);
1911 } else {
1912 /* More data will be coming from primary server; register with
1913 * storage manager. */
1914 if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
1915 debug(33, 0) ("clientWriteComplete 2: ENTRY_ABORTED\n");
1916 storeClientCopy(http->sc, entry,
1917 http->out.offset,
1918 http->out.offset,
1919 CLIENT_SOCK_SZ,
1920 memAllocate(MEM_CLIENT_SOCK_BUF),
1921 clientSendMoreData,
1922 http);
1923 }
1924 }
1925
1926 /*
1927 * client issued a request with an only-if-cached cache-control directive;
1928 * we did not find a cached object that can be returned without
1929 * contacting other servers;
1930 * respond with a 504 (Gateway Timeout) as suggested in [RFC 2068]
1931 */
1932 static void
1933 clientProcessOnlyIfCachedMiss(clientHttpRequest * http)
1934 {
1935 char *url = http->uri;
1936 request_t *r = http->request;
1937 ErrorState *err = NULL;
1938 debug(33, 4) ("clientProcessOnlyIfCachedMiss: '%s %s'\n",
1939 RequestMethodStr[r->method], url);
1940 http->al.http.code = HTTP_GATEWAY_TIMEOUT;
1941 err = errorCon(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT);
1942 err->request = requestLink(r);
1943 err->src_addr = http->conn->peer.sin_addr;
1944 if (http->entry) {
1945 storeUnregister(http->sc, http->entry, http);
1946 http->sc = NULL;
1947 storeUnlockObject(http->entry);
1948 }
1949 http->entry = clientCreateStoreEntry(http, r->method, null_request_flags);
1950 errorAppendEntry(http->entry, err);
1951 }
1952
1953 /*
1954 * Return true if we should force a cache miss on this range request.
1955 * entry must be non-NULL.
1956 */
1957 static int
1958 clientCheckRangeForceMiss(StoreEntry * entry, HttpHdrRange * range)
1959 {
1960 /*
1961 * If the range_offset_limit is NOT in effect, there
1962 * is no reason to force a miss.
1963 */
1964 if (0 == httpHdrRangeOffsetLimit(range))
1965 return 0;
1966 /*
1967 * Here, we know it's possibly a hit. If we already have the
1968 * whole object cached, we won't force a miss.
1969 */
1970 if (STORE_OK == entry->store_status)
1971 return 0; /* we have the whole object */
1972 /*
1973 * Now we have a hit on a PENDING object. We need to see
1974 * if the part we want is already cached. If so, we don't
1975 * force a miss.
1976 */
1977 assert(NULL != entry->mem_obj);
1978 if (httpHdrRangeFirstOffset(range) <= entry->mem_obj->inmem_hi)
1979 return 0;
1980 /*
1981 * Even though we have a PENDING copy of the object, we
1982 * don't want to wait to reach the first range offset,
1983 * so we force a miss for a new range request to the
1984 * origin.
1985 */
1986 return 1;
1987 }
1988
1989 static log_type
1990 clientProcessRequest2(clientHttpRequest * http)
1991 {
1992 request_t *r = http->request;
1993 StoreEntry *e;
1994 e = http->entry = storeGetPublic(http->uri, r->method);
1995 if (r->method == METHOD_HEAD && e == NULL) {
1996 /* We can generate a HEAD reply from a cached GET object */
1997 e = http->entry = storeGetPublic(http->uri, METHOD_GET);
1998 }
1999 /* Release negatively cached IP-cache entries on reload */
2000 if (r->flags.nocache)
2001 ipcacheInvalidate(r->host);
2002 #if HTTP_VIOLATIONS
2003 else if (r->flags.nocache_hack)
2004 ipcacheInvalidate(r->host);
2005 #endif
2006 #if USE_CACHE_DIGESTS
2007 http->lookup_type = e ? "HIT" : "MISS";
2008 #endif
2009 if (NULL == e) {
2010 /* this object isn't in the cache */
2011 debug(33, 3) ("clientProcessRequest2: storeGet() MISS\n");
2012 return LOG_TCP_MISS;
2013 }
2014 if (Config.onoff.offline) {
2015 debug(33, 3) ("clientProcessRequest2: offline HIT\n");
2016 http->entry = e;
2017 return LOG_TCP_HIT;
2018 }
2019 if (http->redirect.status) {
2020 /* force this to be a miss */
2021 http->entry = NULL;
2022 return LOG_TCP_MISS;
2023 }
2024 if (!storeEntryValidToSend(e)) {
2025 debug(33, 3) ("clientProcessRequest2: !storeEntryValidToSend MISS\n");
2026 http->entry = NULL;
2027 return LOG_TCP_MISS;
2028 }
2029 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
2030 /* Special entries are always hits, no matter what the client says */
2031 debug(33, 3) ("clientProcessRequest2: ENTRY_SPECIAL HIT\n");
2032 http->entry = e;
2033 return LOG_TCP_HIT;
2034 }
2035 #if HTTP_VIOLATIONS
2036 if (e->store_status == STORE_PENDING) {
2037 if (r->flags.nocache || r->flags.nocache_hack) {
2038 debug(33, 3) ("Clearing no-cache for STORE_PENDING request\n\t%s\n",
2039 storeUrl(e));
2040 r->flags.nocache = 0;
2041 r->flags.nocache_hack = 0;
2042 }
2043 }
2044 #endif
2045 if (r->flags.nocache) {
2046 debug(33, 3) ("clientProcessRequest2: no-cache REFRESH MISS\n");
2047 http->entry = NULL;
2048 ipcacheInvalidate(r->host);
2049 return LOG_TCP_CLIENT_REFRESH_MISS;
2050 }
2051 if (NULL == r->range) {
2052 (void) 0;
2053 } else if (httpHdrRangeWillBeComplex(r->range)) {
2054 /*
2055 * Some clients break if we return "200 OK" for a Range
2056 * request. We would have to return "200 OK" for a _complex_
2057 * Range request that is also a HIT. Thus, let's prevent HITs
2058 * on complex Range requests
2059 */
2060 debug(33, 3) ("clientProcessRequest2: complex range MISS\n");
2061 http->entry = NULL;
2062 return LOG_TCP_MISS;
2063 } else if (clientCheckRangeForceMiss(e, r->range)) {
2064 debug(33, 3) ("clientProcessRequest2: forcing miss due to range_offset_limit\n");
2065 http->entry = NULL;
2066 return LOG_TCP_MISS;
2067 }
2068 debug(33, 3) ("clientProcessRequest2: default HIT\n");
2069 http->entry = e;
2070 return LOG_TCP_HIT;
2071 }
2072
2073 static void
2074 clientProcessRequest(clientHttpRequest * http)
2075 {
2076 char *url = http->uri;
2077 request_t *r = http->request;
2078 int fd = http->conn->fd;
2079 HttpReply *rep;
2080 http_version_t version;
2081 debug(33, 4) ("clientProcessRequest: %s '%s'\n",
2082 RequestMethodStr[r->method],
2083 url);
2084 if (r->method == METHOD_CONNECT) {
2085 http->log_type = LOG_TCP_MISS;
2086 sslStart(fd, url, r, &http->out.size, &http->al.http.code);
2087 return;
2088 } else if (r->method == METHOD_PURGE) {
2089 clientPurgeRequest(http);
2090 return;
2091 } else if (r->method == METHOD_TRACE) {
2092 if (r->max_forwards == 0) {
2093 http->entry = clientCreateStoreEntry(http, r->method, null_request_flags);
2094 storeReleaseRequest(http->entry);
2095 storeBuffer(http->entry);
2096 rep = httpReplyCreate();
2097 httpBuildVersion(&version, 1, 0);
2098 httpReplySetHeaders(rep, version, HTTP_OK, NULL, "text/plain",
2099 httpRequestPrefixLen(r), 0, squid_curtime);
2100 httpReplySwapOut(rep, http->entry);
2101 httpReplyDestroy(rep);
2102 httpRequestSwapOut(r, http->entry);
2103 storeComplete(http->entry);
2104 return;
2105 }
2106 /* yes, continue */
2107 http->log_type = LOG_TCP_MISS;
2108 } else if (r->content_length >= 0) {
2109 /*
2110 * Need to initialize pump even if content-length: 0
2111 */
2112 http->log_type = LOG_TCP_MISS;
2113 /* XXX oof, POST can be cached! */
2114 pumpInit(fd, r, http->uri);
2115 } else {
2116 http->log_type = clientProcessRequest2(http);
2117 }
2118 debug(33, 4) ("clientProcessRequest: %s for '%s'\n",
2119 log_tags[http->log_type],
2120 http->uri);
2121 http->out.offset = 0;
2122 if (NULL != http->entry) {
2123 storeLockObject(http->entry);
2124 storeCreateMemObject(http->entry, http->uri, http->log_uri);
2125 http->entry->mem_obj->method = r->method;
2126 http->sc = storeClientListAdd(http->entry, http);
2127 #if DELAY_POOLS
2128 delaySetStoreClient(http->sc, delayClient(r));
2129 #endif
2130 storeClientCopy(http->sc, http->entry,
2131 http->out.offset,
2132 http->out.offset,
2133 CLIENT_SOCK_SZ,
2134 memAllocate(MEM_CLIENT_SOCK_BUF),
2135 clientCacheHit,
2136 http);
2137 } else {
2138 /* MISS CASE, http->log_type is already set! */
2139 clientProcessMiss(http);
2140 }
2141 }
2142
2143 /*
2144 * Prepare to fetch the object as it's a cache miss of some kind.
2145 */
2146 static void
2147 clientProcessMiss(clientHttpRequest * http)
2148 {
2149 char *url = http->uri;
2150 request_t *r = http->request;
2151 ErrorState *err = NULL;
2152 debug(33, 4) ("clientProcessMiss: '%s %s'\n",
2153 RequestMethodStr[r->method], url);
2154 /*
2155 * We might have a left-over StoreEntry from a failed cache hit
2156 * or IMS request.
2157 */
2158 if (http->entry) {
2159 if (EBIT_TEST(http->entry->flags, ENTRY_SPECIAL)) {
2160 debug(33, 0) ("clientProcessMiss: miss on a special object (%s).\n", url);
2161 debug(33, 0) ("\tlog_type = %s\n", log_tags[http->log_type]);
2162 storeEntryDump(http->entry, 1);
2163 }
2164 storeUnregister(http->sc, http->entry, http);
2165 http->sc = NULL;
2166 storeUnlockObject(http->entry);
2167 http->entry = NULL;
2168 }
2169 if (clientOnlyIfCached(http)) {
2170 clientProcessOnlyIfCachedMiss(http);
2171 return;
2172 }
2173 /*
2174 * Deny loops when running in accelerator/transproxy mode.
2175 */
2176 if (http->flags.accel && r->flags.loopdetect) {
2177 http->al.http.code = HTTP_FORBIDDEN;
2178 err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
2179 err->request = requestLink(r);
2180 err->src_addr = http->conn->peer.sin_addr;
2181 http->entry = clientCreateStoreEntry(http, r->method, null_request_flags);
2182 errorAppendEntry(http->entry, err);
2183 return;
2184 }
2185 assert(http->out.offset == 0);
2186 http->entry = clientCreateStoreEntry(http, r->method, r->flags);
2187 if (http->redirect.status) {
2188 HttpReply *rep = httpReplyCreate();
2189 #if LOG_TCP_REDIRECTS
2190 http->log_type = LOG_TCP_REDIRECT;
2191 #endif
2192 storeReleaseRequest(http->entry);
2193 httpRedirectReply(rep, http->redirect.status, http->redirect.location);
2194 httpReplySwapOut(rep, http->entry);
2195 httpReplyDestroy(rep);
2196 storeComplete(http->entry);
2197 return;
2198 }
2199 if (http->flags.internal)
2200 r->protocol = PROTO_INTERNAL;
2201 fwdStart(http->conn->fd, http->entry, r);
2202 }
2203
2204 static clientHttpRequest *
2205 parseHttpRequestAbort(ConnStateData * conn, const char *uri)
2206 {
2207 clientHttpRequest *http = memAllocate(MEM_CLIENTHTTPREQUEST);
2208 cbdataAdd(http, memFree, MEM_CLIENTHTTPREQUEST);
2209 http->conn = conn;
2210 http->start = current_time;
2211 http->req_sz = conn->in.offset;
2212 http->uri = xstrdup(uri);
2213 http->log_uri = xstrndup(uri, MAX_URL);
2214 http->range_iter.boundary = StringNull;
2215 dlinkAdd(http, &http->active, &ClientActiveRequests);
2216 return http;
2217 }
2218
2219 /*
2220 * parseHttpRequest()
2221 *
2222 * Returns
2223 * NULL on error or incomplete request
2224 * a clientHttpRequest structure on success
2225 */
2226 static clientHttpRequest *
2227 parseHttpRequest(ConnStateData * conn, method_t * method_p, int *status,
2228 char **prefix_p, size_t * req_line_sz_p)
2229 {
2230 char *inbuf = NULL;
2231 char *mstr = NULL;
2232 char *url = NULL;
2233 char *req_hdr = NULL;
2234 http_version_t http_ver;
2235 char *token = NULL;
2236 char *t = NULL;
2237 char *end;
2238 int free_request = 0;
2239 size_t header_sz; /* size of headers, not including first line */
2240 size_t prefix_sz; /* size of whole request (req-line + headers) */
2241 size_t url_sz;
2242 size_t req_sz;
2243 method_t method;
2244 clientHttpRequest *http = NULL;
2245 #if IPF_TRANSPARENT
2246 struct natlookup natLookup;
2247 static int natfd = -1;
2248 static int siocgnatl_cmd = SIOCGNATL & 0xff;
2249 int x;
2250 #endif
2251
2252 if ((req_sz = headersEnd(conn->in.buf, conn->in.offset)) == 0) {
2253 debug(33, 5) ("Incomplete request, waiting for end of headers\n");
2254 *status = 0;
2255 *prefix_p = NULL;
2256 *method_p = METHOD_NONE;
2257 return NULL;
2258 }
2259 assert(req_sz <= conn->in.offset);
2260 /* Use memcpy, not strdup! */
2261 inbuf = xmalloc(req_sz + 1);
2262 xmemcpy(inbuf, conn->in.buf, req_sz);
2263 *(inbuf + req_sz) = '\0';
2264
2265 /* pre-set these values to make aborting simpler */
2266 *prefix_p = inbuf;
2267 *method_p = METHOD_NONE;
2268 *status = -1;
2269
2270 /* Barf on NULL characters in the headers */
2271 if (strlen(inbuf) != req_sz) {
2272 debug(33, 1) ("parseHttpRequest: Requestheader contains NULL characters\n");
2273 return parseHttpRequestAbort(conn, "error:invalid-request");
2274 }
2275 /* Look for request method */
2276 if ((mstr = strtok(inbuf, "\t ")) == NULL) {
2277 debug(33, 1) ("parseHttpRequest: Can't get request method\n");
2278 return parseHttpRequestAbort(conn, "error:invalid-request-method");
2279 }
2280 method = urlParseMethod(mstr);
2281 if (method == METHOD_NONE) {
2282 debug(33, 1) ("parseHttpRequest: Unsupported method '%s'\n", mstr);
2283 return parseHttpRequestAbort(conn, "error:unsupported-request-method");
2284 }
2285 debug(33, 5) ("parseHttpRequest: Method is '%s'\n", mstr);
2286 *method_p = method;
2287
2288 /* look for URL+HTTP/x.x */
2289 if ((url = strtok(NULL, "\n")) == NULL) {
2290 debug(33, 1) ("parseHttpRequest: Missing URL\n");
2291 return parseHttpRequestAbort(conn, "error:missing-url");
2292 }
2293 while (xisspace(*url))
2294 url++;
2295 t = url + strlen(url);
2296 assert(*t == '\0');
2297 token = NULL;
2298 while (t > url) {
2299 t--;
2300 if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) {
2301 token = t + 1;
2302 break;
2303 }
2304 }
2305 while (t > url && xisspace(*t))
2306 *(t--) = '\0';
2307 debug(33, 5) ("parseHttpRequest: URI is '%s'\n", url);
2308 if (token == NULL) {
2309 debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
2310 #if RELAXED_HTTP_PARSER
2311 httpBuildVersion(&http_ver, 0, 9); /* wild guess */
2312 #else
2313 return parseHttpRequestAbort(conn, "error:missing-http-ident");
2314 #endif
2315 } else {
2316 if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) {
2317 debug(33, 3) ("parseHttpRequest: Invalid HTTP identifier.\n");
2318 return parseHttpRequestAbort(conn, "error: invalid HTTP-ident");
2319 }
2320 debug(33, 6) ("parseHttpRequest: Client HTTP version %d.%d.\n", http_ver.major, http_ver.minor);
2321 }
2322
2323 /*
2324 * Process headers after request line
2325 */
2326 req_hdr = strtok(NULL, null_string);
2327 header_sz = req_sz - (req_hdr - inbuf);
2328 if (0 == header_sz) {
2329 debug(33, 3) ("parseHttpRequest: header_sz == 0\n");
2330 *status = 0;
2331 return NULL;
2332 }
2333 assert(header_sz > 0);
2334 debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
2335 end = req_hdr + header_sz;
2336 debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
2337
2338 prefix_sz = end - inbuf;
2339 *req_line_sz_p = req_hdr - inbuf;
2340 debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
2341 (int) prefix_sz, (int) *req_line_sz_p);
2342 assert(prefix_sz <= conn->in.offset);
2343
2344 /* Ok, all headers are received */
2345 http = memAllocate(MEM_CLIENTHTTPREQUEST);
2346 cbdataAdd(http, memFree, MEM_CLIENTHTTPREQUEST);
2347 http->http_ver = http_ver;
2348 http->conn = conn;
2349 http->start = current_time;
2350 http->req_sz = prefix_sz;
2351 http->range_iter.boundary = StringNull;
2352 *prefix_p = xmalloc(prefix_sz + 1);
2353 xmemcpy(*prefix_p, conn->in.buf, prefix_sz);
2354 *(*prefix_p + prefix_sz) = '\0';
2355 dlinkAdd(http, &http->active, &ClientActiveRequests);
2356
2357 debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n", (*prefix_p) + *req_line_sz_p);
2358 if ((t = strchr(url, '#'))) /* remove HTML anchors */
2359 *t = '\0';
2360
2361 /* handle internal objects */
2362 if (internalCheck(url)) {
2363 /* prepend our name & port */
2364 http->uri = xstrdup(internalLocalUri(NULL, url));
2365 http->flags.internal = 1;
2366 http->flags.accel = 1;
2367 }
2368 /* see if we running in Config2.Accel.on, if so got to convert it to URL */
2369 else if (Config2.Accel.on && *url == '/') {
2370 /* prepend the accel prefix */
2371 if (opt_accel_uses_host && (t = mime_get_header(req_hdr, "Host"))) {
2372 int vport = (int) Config.Accel.port;
2373 char *q;
2374 /* If a Host: header was specified, use it to build the URL
2375 * instead of the one in the Config file. */
2376 /*
2377 * XXX Use of the Host: header here opens a potential
2378 * security hole. There are no checks that the Host: value
2379 * corresponds to one of your servers. It might, for example,
2380 * refer to www.playboy.com. The 'dst' and/or 'dst_domain' ACL
2381 * types should be used to prevent httpd-accelerators
2382 * handling requests for non-local servers */
2383 strtok(t, " /;@");
2384 if ((q = strchr(t, ':'))) {
2385 *q++ = '\0';
2386 vport = atoi(q);
2387 }
2388 url_sz = strlen(url) + 32 + Config.appendDomainLen +
2389 strlen(t);
2390 http->uri = xcalloc(url_sz, 1);
2391 snprintf(http->uri, url_sz, "http://%s:%d%s",
2392 t, vport, url);
2393 } else if (vhost_mode) {
2394 int vport;
2395 /* Put the local socket IP address as the hostname */
2396 url_sz = strlen(url) + 32 + Config.appendDomainLen;
2397 http->uri = xcalloc(url_sz, 1);
2398 if (vport_mode)
2399 vport = (int) ntohs(http->conn->me.sin_port);
2400 else
2401 vport = (int) Config.Accel.port;
2402 #if IPF_TRANSPARENT
2403 natLookup.nl_inport = http->conn->me.sin_port;
2404 natLookup.nl_outport = http->conn->peer.sin_port;
2405 natLookup.nl_inip = http->conn->me.sin_addr;
2406 natLookup.nl_outip = http->conn->peer.sin_addr;
2407 natLookup.nl_flags = IPN_TCP;
2408 if (natfd < 0)
2409 natfd = open(IPL_NAT, O_RDONLY, 0);
2410 if (natfd < 0) {
2411 debug(50, 1) ("parseHttpRequest: NAT open failed: %s\n",
2412 xstrerror());
2413 return parseHttpRequestAbort(conn, "error:nat-open-failed");
2414 }
2415 /*
2416 * IP-Filter changed the type for SIOCGNATL between
2417 * 3.3 and 3.4. It also changed the cmd value for
2418 * SIOCGNATL, so at least we can detect it. We could
2419 * put something in configure and use ifdefs here, but
2420 * this seems simpler.
2421 */
2422 if (63 == siocgnatl_cmd) {
2423 struct natlookup *nlp = &natLookup;
2424 x = ioctl(natfd, SIOCGNATL, &nlp);
2425 } else {
2426 x = ioctl(natfd, SIOCGNATL, &natLookup);
2427 }
2428 if (x < 0) {
2429 if (errno != ESRCH) {
2430 debug(50, 1) ("parseHttpRequest: NAT lookup failed: ioctl(SIOCGNATL)\n");
2431 close(natfd);
2432 natfd = -1;
2433 return parseHttpRequestAbort(conn, "error:nat-lookup-failed");
2434 } else
2435 snprintf(http->uri, url_sz, "http://%s:%d%s",
2436 inet_ntoa(http->conn->me.sin_addr),
2437 vport, url);
2438 } else
2439 snprintf(http->uri, url_sz, "http://%s:%d%s",
2440 inet_ntoa(natLookup.nl_realip),
2441 vport, url);
2442 #else
2443 snprintf(http->uri, url_sz, "http://%s:%d%s",
2444 inet_ntoa(http->conn->me.sin_addr),
2445 vport, url);
2446 #endif
2447 debug(33, 5) ("VHOST REWRITE: '%s'\n", http->uri);
2448 } else {
2449 url_sz = strlen(Config2.Accel.prefix) + strlen(url) +
2450 Config.appendDomainLen + 1;
2451 http->uri = xcalloc(url_sz, 1);
2452 snprintf(http->uri, url_sz, "%s%s", Config2.Accel.prefix, url);
2453 }
2454 http->flags.accel = 1;
2455 } else {
2456 /* URL may be rewritten later, so make extra room */
2457 url_sz = strlen(url) + Config.appendDomainLen + 5;
2458 http->uri = xcalloc(url_sz, 1);
2459 strcpy(http->uri, url);
2460 http->flags.accel = 0;
2461 }
2462 if (!stringHasCntl(http->uri))
2463 http->log_uri = xstrndup(http->uri, MAX_URL);
2464 else
2465 http->log_uri = xstrndup(rfc1738_escape_unescaped(http->uri), MAX_URL);
2466 debug(33, 5) ("parseHttpRequest: Complete request received\n");
2467 if (free_request)
2468 safe_free(url);
2469 xfree(inbuf);
2470 *status = 1;
2471 return http;
2472 }
2473
2474 static int
2475 clientReadDefer(int fdnotused, void *data)
2476 {
2477 ConnStateData *conn = data;
2478 return conn->defer.until > squid_curtime;
2479 }
2480
2481 static void
2482 clientReadRequest(int fd, void *data)
2483 {
2484 ConnStateData *conn = data;
2485 int parser_return_code = 0;
2486 int k;
2487 request_t *request = NULL;
2488 int size;
2489 void *p;
2490 method_t method;
2491 clientHttpRequest *http = NULL;
2492 clientHttpRequest **H = NULL;
2493 char *prefix = NULL;
2494 ErrorState *err = NULL;
2495 fde *F = &fd_table[fd];
2496 int len = conn->in.size - conn->in.offset - 1;
2497 debug(33, 4) ("clientReadRequest: FD %d: reading request...\n", fd);
2498 statCounter.syscalls.sock.reads++;
2499 size = read(fd, conn->in.buf + conn->in.offset, len);
2500 if (size > 0) {
2501 fd_bytes(fd, size, FD_READ);
2502 kb_incr(&statCounter.client_http.kbytes_in, size);
2503 }
2504 /*
2505 * Don't reset the timeout value here. The timeout value will be
2506 * set to Config.Timeout.request by httpAccept() and
2507 * clientWriteComplete(), and should apply to the request as a
2508 * whole, not individual read() calls. Plus, it breaks our
2509 * lame half-close detection
2510 */
2511 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
2512 if (size == 0) {
2513 if (conn->chr == NULL) {
2514 /* no current or pending requests */
2515 comm_close(fd);
2516 return;
2517 } else if (!Config.onoff.half_closed_clients) {
2518 /* admin doesn't want to support half-closed client sockets */
2519 comm_close(fd);
2520 return;
2521 }
2522 /* It might be half-closed, we can't tell */
2523 debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd);
2524 F->flags.socket_eof = 1;
2525 conn->defer.until = squid_curtime + 1;
2526 conn->defer.n++;
2527 fd_note(fd, "half-closed");
2528 return;
2529 } else if (size < 0) {
2530 if (!ignoreErrno(errno)) {
2531 debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd, xstrerror());
2532 comm_close(fd);
2533 return;
2534 } else if (conn->in.offset == 0) {
2535 debug(50, 2) ("clientReadRequest: FD %d: no data to process (%s)\n", fd, xstrerror());
2536 return;
2537 }
2538 /* Continue to process previously read data */
2539 size = 0;
2540 }
2541 conn->in.offset += size;
2542 /* Skip leading (and trailing) whitespace */
2543 while (conn->in.offset > 0) {
2544 int nrequests;
2545 size_t req_line_sz;
2546 while (conn->in.offset > 0 && xisspace(conn->in.buf[0])) {
2547 xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.offset - 1);
2548 conn->in.offset--;
2549 }
2550 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
2551 if (conn->in.offset == 0)
2552 break;
2553 /* Limit the number of concurrent requests to 2 */
2554 for (H = &conn->chr, nrequests = 0; *H; H = &(*H)->next, nrequests++);
2555 if (nrequests >= 2) {
2556 debug(33, 3) ("clientReadRequest: FD %d max concurrent requests reached\n", fd);
2557 debug(33, 5) ("clientReadRequest: FD %d defering new request until one is done\n", fd);
2558 conn->defer.until = squid_curtime + 100; /* Reset when a request is complete */
2559 break;
2560 }
2561 /* Process request */
2562 http = parseHttpRequest(conn,
2563 &method,
2564 &parser_return_code,
2565 &prefix,
2566 &req_line_sz);
2567 if (!http)
2568 safe_free(prefix);
2569 if (http) {
2570 assert(http->req_sz > 0);
2571 conn->in.offset -= http->req_sz;
2572 assert(conn->in.offset >= 0);
2573 debug(33, 5) ("conn->in.offset = %d\n", (int) conn->in.offset);
2574 /*
2575 * If we read past the end of this request, move the remaining
2576 * data to the beginning
2577 */
2578 if (conn->in.offset > 0)
2579 xmemmove(conn->in.buf, conn->in.buf + http->req_sz, conn->in.offset);
2580 /* add to the client request queue */
2581 for (H = &conn->chr; *H; H = &(*H)->next);
2582 *H = http;
2583 conn->nrequests++;
2584 /*
2585 * I wanted to lock 'http' here since its callback data for
2586 * clientLifetimeTimeout(), but there's no logical place to
2587 * cbdataUnlock if the timeout never happens. Maybe its safe
2588 * enough to assume that if the FD is open, and the timeout
2589 * triggers, that 'http' is valid.
2590 */
2591 commSetTimeout(fd, Config.Timeout.lifetime, clientLifetimeTimeout, http);
2592 if (parser_return_code < 0) {
2593 debug(33, 1) ("clientReadRequest: FD %d Invalid Request\n", fd);
2594 err = errorCon(ERR_INVALID_REQ, HTTP_BAD_REQUEST);
2595 err->request_hdrs = xstrdup(conn->in.buf);
2596 http->entry = clientCreateStoreEntry(http, method, null_request_flags);
2597 errorAppendEntry(http->entry, err);
2598 safe_free(prefix);
2599 break;
2600 }
2601 if ((request = urlParse(method, http->uri)) == NULL) {
2602 debug(33, 5) ("Invalid URL: %s\n", http->uri);
2603 err = errorCon(ERR_INVALID_URL, HTTP_BAD_REQUEST);
2604 err->src_addr = conn->peer.sin_addr;
2605 err->url = xstrdup(http->uri);
2606 http->al.http.code = err->http_status;
2607 http->entry = clientCreateStoreEntry(http, method, null_request_flags);
2608 errorAppendEntry(http->entry, err);
2609 safe_free(prefix);
2610 break;
2611 } else {
2612 /* compile headers */
2613 /* we should skip request line! */
2614 if (!httpRequestParseHeader(request, prefix + req_line_sz))
2615 debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
2616 http->uri, prefix);
2617 /* continue anyway? */
2618 }
2619 request->flags.accelerated = http->flags.accel;
2620 if (!http->flags.internal) {
2621 if (internalCheck(strBuf(request->urlpath))) {
2622 if (internalHostnameIs(request->host) &&
2623 request->port == ntohs(Config.Sockaddr.http->s.sin_port)) {
2624 http->flags.internal = 1;
2625 } else if (internalStaticCheck(strBuf(request->urlpath))) {
2626 xstrncpy(request->host, internalHostname(), SQUIDHOSTNAMELEN);
2627 request->port = ntohs(Config.Sockaddr.http->s.sin_port);
2628 http->flags.internal = 1;
2629 }
2630 }
2631 }
2632 /*
2633 * cache the Content-length value in request_t.
2634 */
2635 request->content_length = httpHeaderGetInt(&request->header,
2636 HDR_CONTENT_LENGTH);
2637 request->flags.internal = http->flags.internal;
2638 safe_free(prefix);
2639 safe_free(http->log_uri);
2640 http->log_uri = xstrdup(urlCanonicalClean(request));
2641 request->client_addr = conn->peer.sin_addr;
2642 request->my_addr = conn->me.sin_addr;
2643 request->my_port = ntohs(conn->me.sin_port);
2644 request->http_ver = http->http_ver;
2645 if (!urlCheckRequest(request)) {
2646 err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED);
2647 err->src_addr = conn->peer.sin_addr;
2648 err->request = requestLink(request);
2649 request->flags.proxy_keepalive = 0;
2650 http->al.http.code = err->http_status;
2651 http->entry = clientCreateStoreEntry(http, request->method, null_request_flags);
2652 errorAppendEntry(http->entry, err);
2653 break;
2654 }
2655 if (0 == clientCheckContentLength(request)) {
2656 err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED);
2657 err->src_addr = conn->peer.sin_addr;
2658 err->request = requestLink(request);
2659 http->al.http.code = err->http_status;
2660 http->entry = clientCreateStoreEntry(http, request->method, null_request_flags);
2661 errorAppendEntry(http->entry, err);
2662 break;
2663 }
2664 http->request = requestLink(request);
2665 /*
2666 * We need to set the keepalive flag before doing some
2667 * hacks for POST/PUT requests below. Maybe we could
2668 * set keepalive flag even earlier.
2669 */
2670 clientSetKeepaliveFlag(http);
2671 /*
2672 * break here if the request has a content-length
2673 * because there is a reqeust body following and we
2674 * don't want to parse it as though it was new request.
2675 */
2676 if (request->content_length >= 0) {
2677 int copy_len = XMIN(conn->in.offset, request->content_length);
2678 if (copy_len > 0) {
2679 assert(conn->in.offset >= copy_len);
2680 request->body_sz = copy_len;
2681 request->body = xmalloc(request->body_sz);
2682 xmemcpy(request->body, conn->in.buf, request->body_sz);
2683 conn->in.offset -= copy_len;
2684 if (conn->in.offset)
2685 xmemmove(conn->in.buf, conn->in.buf + copy_len, conn->in.offset);
2686 }
2687 /*
2688 * if we didn't get the full body now, then more will
2689 * be arriving on the client socket. Lets cancel
2690 * the read handler until this request gets forwarded.
2691 */
2692 if (request->body_sz < request->content_length)
2693 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
2694 if (request->content_length < 0)
2695 (void) 0;
2696 else if (clientRequestBodyTooLarge(request->content_length)) {
2697 err = errorCon(ERR_TOO_BIG, HTTP_REQUEST_ENTITY_TOO_LARGE);
2698 err->request = requestLink(request);
2699 http->entry = clientCreateStoreEntry(http,
2700 METHOD_NONE, null_request_flags);
2701 errorAppendEntry(http->entry, err);
2702 break;
2703 }
2704 }
2705 clientAccessCheck(http);
2706 continue; /* while offset > 0 */
2707 } else if (parser_return_code == 0) {
2708 /*
2709 * Partial request received; reschedule until parseHttpRequest()
2710 * is happy with the input
2711 */
2712 k = conn->in.size - 1 - conn->in.offset;
2713 if (k == 0) {
2714 if (conn->in.offset >= Config.maxRequestHeaderSize) {
2715 /* The request is too large to handle */
2716 debug(33, 1) ("Request header is too large (%d bytes)\n",
2717 (int) conn->in.offset);
2718 debug(33, 1) ("Config 'request_header_max_size'= %d bytes.\n",
2719 Config.maxRequestHeaderSize);
2720 err = errorCon(ERR_TOO_BIG, HTTP_REQUEST_ENTITY_TOO_LARGE);
2721 http = parseHttpRequestAbort(conn, "error:request-too-large");
2722 /* add to the client request queue */
2723 for (H = &conn->chr; *H; H = &(*H)->next);
2724 *H = http;
2725 http->entry = clientCreateStoreEntry(http, METHOD_NONE, null_request_flags);
2726 errorAppendEntry(http->entry, err);
2727 return;
2728 }
2729 /* Grow the request memory area to accomodate for a large request */
2730 conn->in.size += CLIENT_REQ_BUF_SZ;
2731 if (conn->in.size == 2 * CLIENT_REQ_BUF_SZ) {
2732 p = conn->in.buf; /* get rid of fixed size Pooled buffer */
2733 conn->in.buf = xcalloc(2, CLIENT_REQ_BUF_SZ);
2734 xmemcpy(conn->in.buf, p, CLIENT_REQ_BUF_SZ);
2735 memFree(p, MEM_CLIENT_REQ_BUF);
2736 } else
2737 conn->in.buf = xrealloc(conn->in.buf, conn->in.size);
2738 /* XXX account conn->in.buf */
2739 debug(33, 3) ("Handling a large request, offset=%d inbufsize=%d\n",
2740 (int) conn->in.offset, conn->in.size);
2741 k = conn->in.size - 1 - conn->in.offset;
2742 }
2743 break;
2744 }
2745 }
2746 }
2747
2748 /* general lifetime handler for HTTP requests */
2749 static void
2750 requestTimeout(int fd, void *data)
2751 {
2752 #if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
2753 ConnStateData *conn = data;
2754 ErrorState *err;
2755 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
2756 if (fd_table[fd].rwstate) {
2757 /*
2758 * Some data has been sent to the client, just close the FD
2759 */
2760 comm_close(fd);
2761 } else if (conn->nrequests) {
2762 /*
2763 * assume its a persistent connection; just close it
2764 */
2765 comm_close(fd);
2766 } else {
2767 /*
2768 * Generate an error
2769 */
2770 err = errorCon(ERR_LIFETIME_EXP, HTTP_REQUEST_TIMEOUT);
2771 err->url = xstrdup("N/A");
2772 /*
2773 * Normally we shouldn't call errorSend() in client_side.c, but
2774 * it should be okay in this case. Presumably if we get here
2775 * this is the first request for the connection, and no data
2776 * has been written yet
2777 */
2778 assert(conn->chr == NULL);
2779 errorSend(fd, err);
2780 /*
2781 * if we don't close() here, we still need a timeout handler!
2782 */
2783 commSetTimeout(fd, 30, requestTimeout, conn);
2784 /*
2785 * Aha, but we don't want a read handler!
2786 */
2787 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
2788 }
2789 #else
2790 /*
2791 * Just close the connection to not confuse browsers
2792 * using persistent connections. Some browsers opens
2793 * an connection and then does not use it until much
2794 * later (presumeably because the request triggering
2795 * the open has already been completed on another
2796 * connection)
2797 */
2798 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
2799 comm_close(fd);
2800 #endif
2801 }
2802
2803 static void
2804 clientLifetimeTimeout(int fd, void *data)
2805 {
2806 clientHttpRequest *http = data;
2807 ConnStateData *conn = http->conn;
2808 debug(33, 1) ("WARNING: Closing client %s connection due to lifetime timeout\n",
2809 inet_ntoa(conn->peer.sin_addr));
2810 debug(33, 1) ("\t%s\n", http->uri);
2811 comm_close(fd);
2812 }
2813
2814 static int
2815 httpAcceptDefer(int fdunused, void *dataunused)
2816 {
2817 static time_t last_warn = 0;
2818 if (fdNFree() >= RESERVED_FD)
2819 return 0;
2820 if (last_warn + 15 < squid_curtime) {
2821 debug(33, 0) ("WARNING! Your cache is running out of filedescriptors\n");
2822 last_warn = squid_curtime;
2823 }
2824 return 1;
2825 }
2826
2827 /* Handle a new connection on HTTP socket. */
2828 void
2829 httpAccept(int sock, void *data)
2830 {
2831 int *N = data;
2832 int fd = -1;
2833 ConnStateData *connState = NULL;
2834 struct sockaddr_in peer;
2835 struct sockaddr_in me;
2836 int max = INCOMING_HTTP_MAX;
2837 #if USE_IDENT
2838 static aclCheck_t identChecklist;
2839 #endif
2840 commSetSelect(sock, COMM_SELECT_READ, httpAccept, NULL, 0);
2841 while (max-- && !httpAcceptDefer(sock, NULL)) {
2842 memset(&peer, '\0', sizeof(struct sockaddr_in));
2843 memset(&me, '\0', sizeof(struct sockaddr_in));
2844 if ((fd = comm_accept(sock, &peer, &me)) < 0) {
2845 if (!ignoreErrno(errno))
2846 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
2847 sock, xstrerror());
2848 break;
2849 }
2850 debug(33, 4) ("httpAccept: FD %d: accepted\n", fd);
2851 connState = memAllocate(MEM_CONNSTATEDATA);
2852 cbdataAdd(connState, memFree, MEM_CONNSTATEDATA);
2853 connState->peer = peer;
2854 connState->log_addr = peer.sin_addr;
2855 connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
2856 connState->me = me;
2857 connState->fd = fd;
2858 connState->in.size = CLIENT_REQ_BUF_SZ;
2859 connState->in.buf = memAllocate(MEM_CLIENT_REQ_BUF);
2860 /* XXX account connState->in.buf */
2861 comm_add_close_handler(fd, connStateFree, connState);
2862 if (Config.onoff.log_fqdn)
2863 fqdncache_gethostbyaddr(peer.sin_addr, FQDN_LOOKUP_IF_MISS);
2864 commSetTimeout(fd, Config.Timeout.request, requestTimeout, connState);
2865 #if USE_IDENT
2866 identChecklist.src_addr = peer.sin_addr;
2867 identChecklist.my_addr = me.sin_addr;
2868 identChecklist.my_port = ntohs(me.sin_port);
2869 if (aclCheckFast(Config.accessList.identLookup, &identChecklist))
2870 identStart(&me, &peer, clientIdentDone, connState);
2871 #endif
2872 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0);
2873 commSetDefer(fd, clientReadDefer, connState);
2874 clientdbEstablished(peer.sin_addr, 1);
2875 assert(N);
2876 (*N)++;
2877 }
2878 }
2879
2880 #define SENDING_BODY 0
2881 #define SENDING_HDRSONLY 1
2882 static int
2883 clientCheckTransferDone(clientHttpRequest * http)
2884 {
2885 int sending = SENDING_BODY;
2886 StoreEntry *entry = http->entry;
2887 MemObject *mem;
2888 http_reply *reply;
2889 int sendlen;
2890 if (entry == NULL)
2891 return 0;
2892 /*
2893 * For now, 'done_copying' is used for special cases like
2894 * Range and HEAD requests.
2895 */
2896 if (http->flags.done_copying)
2897 return 1;
2898 /*
2899 * Handle STORE_OK objects.
2900 * objectLen(entry) will be set proprely.
2901 */
2902 if (entry->store_status == STORE_OK) {
2903 if (http->out.offset >= objectLen(entry))
2904 return 1;
2905 else
2906 return 0;
2907 }
2908 /*
2909 * Now, handle STORE_PENDING objects
2910 */
2911 mem = entry->mem_obj;
2912 assert(mem != NULL);
2913 assert(http->request != NULL);
2914 reply = mem->reply;
2915 if (reply->hdr_sz == 0)
2916 return 0; /* haven't found end of headers yet */
2917 else if (reply->sline.status == HTTP_OK)
2918 sending = SENDING_BODY;
2919 else if (reply->sline.status == HTTP_NO_CONTENT)
2920 sending = SENDING_HDRSONLY;
2921 else if (reply->sline.status == HTTP_NOT_MODIFIED)
2922 sending = SENDING_HDRSONLY;
2923 else if (reply->sline.status < HTTP_OK)
2924 sending = SENDING_HDRSONLY;
2925 else if (http->request->method == METHOD_HEAD)
2926 sending = SENDING_HDRSONLY;
2927 else
2928 sending = SENDING_BODY;
2929 /*
2930 * Figure out how much data we are supposed to send.
2931 * If we are sending a body and we don't have a content-length,
2932 * then we must wait for the object to become STORE_OK.
2933 */
2934 if (sending == SENDING_HDRSONLY)
2935 sendlen = reply->hdr_sz;
2936 else if (reply->content_length < 0)
2937 return 0;
2938 else
2939 sendlen = reply->content_length + reply->hdr_sz;
2940 /*
2941 * Now that we have the expected length, did we send it all?
2942 */
2943 if (http->out.offset < sendlen)
2944 return 0;
2945 else
2946 return 1;
2947 }
2948
2949 static int
2950 clientGotNotEnough(clientHttpRequest * http)
2951 {
2952 int cl = httpReplyBodySize(http->request->method, http->entry->mem_obj->reply);
2953 int hs = http->entry->mem_obj->reply->hdr_sz;
2954 assert(cl >= 0);
2955 if (http->out.offset < cl + hs)
2956 return 1;
2957 return 0;
2958 }
2959
2960 /*
2961 * This function is designed to serve a fairly specific purpose.
2962 * Occasionally our vBNS-connected caches can talk to each other, but not
2963 * the rest of the world. Here we try to detect frequent failures which
2964 * make the cache unusable (e.g. DNS lookup and connect() failures). If
2965 * the failure:success ratio goes above 1.0 then we go into "hit only"
2966 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
2967 * will only fetch HITs from us if they are using the ICP protocol. We
2968 * stay in this mode for 5 minutes.
2969 *
2970 * Duane W., Sept 16, 1996
2971 */
2972
2973 static void
2974 checkFailureRatio(err_type etype, hier_code hcode)
2975 {
2976 static double magic_factor = 100.0;
2977 double n_good;
2978 double n_bad;
2979 if (hcode == HIER_NONE)
2980 return;
2981 n_good = magic_factor / (1.0 + request_failure_ratio);
2982 n_bad = magic_factor - n_good;
2983 switch (etype) {
2984 case ERR_DNS_FAIL:
2985 case ERR_CONNECT_FAIL:
2986 case ERR_READ_ERROR:
2987 n_bad++;
2988 break;
2989 default:
2990 n_good++;
2991 }
2992 request_failure_ratio = n_bad / n_good;
2993 if (hit_only_mode_until > squid_curtime)
2994 return;
2995 if (request_failure_ratio < 1.0)
2996 return;
2997 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio);
2998 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
2999 FAILURE_MODE_TIME / 60);
3000 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
3001 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
3002 }
3003
3004 void
3005 clientHttpConnectionsOpen(void)
3006 {
3007 sockaddr_in_list *s;
3008 int fd;
3009 for (s = Config.Sockaddr.http; s; s = s->next) {
3010 if (MAXHTTPPORTS == NHttpSockets) {
3011 debug(1, 1) ("WARNING: You have too many 'http_port' lines.\n");
3012 debug(1, 1) (" The limit is %d\n", MAXHTTPPORTS);
3013 continue;
3014 }
3015 enter_suid();
3016 fd = comm_open(SOCK_STREAM,
3017 0,
3018 s->s.sin_addr,
3019 ntohs(s->s.sin_port),
3020 COMM_NONBLOCKING,
3021 "HTTP Socket");
3022 leave_suid();
3023 if (fd < 0)
3024 continue;
3025 comm_listen(fd);
3026 commSetSelect(fd, COMM_SELECT_READ, httpAccept, NULL, 0);
3027 /*
3028 * We need to set a defer handler here so that we don't
3029 * peg the CPU with select() when we hit the FD limit.
3030 */
3031 commSetDefer(fd, httpAcceptDefer, NULL);
3032 debug(1, 1) ("Accepting HTTP connections at %s, port %d, FD %d.\n",
3033 inet_ntoa(s->s.sin_addr),
3034 (int) ntohs(s->s.sin_port),
3035 fd);
3036 HttpSockets[NHttpSockets++] = fd;
3037 }
3038 if (NHttpSockets < 1)
3039 fatal("Cannot open HTTP Port");
3040 }
3041
3042 void
3043 clientHttpConnectionsClose(void)
3044 {
3045 int i;
3046 for (i = 0; i < NHttpSockets; i++) {
3047 if (HttpSockets[i] >= 0) {
3048 debug(1, 1) ("FD %d Closing HTTP connection\n", HttpSockets[i]);
3049 comm_close(HttpSockets[i]);
3050 HttpSockets[i] = -1;
3051 }
3052 }
3053 NHttpSockets = 0;
3054 }