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