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