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