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