]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
force direct for REQ_LOOPDETECT flag
[thirdparty/squid.git] / src / client_side.cc
CommitLineData
3c66d057 1
dd11e0b7 2/*
e2525655 3 * $Id: client_side.cc,v 1.315 1998/05/27 18:35:22 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/
dd11e0b7 9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
f88bb09c 31
32#include "squid.h"
33
5492ad1d 34#if LINGERING_CLOSE
35#define comm_close comm_lingering_close
36#endif
37
7a2f978b 38static const char *const crlf = "\r\n";
cb69b4c7 39static const char *const proxy_auth_challenge =
ee1679df 40"Basic realm=\"Squid proxy-caching web server\"";
7a2f978b 41
42#define REQUEST_BUF_SIZE 4096
43#define FAILURE_MODE_TIME 300
44
45/* Local functions */
46
fc5d6f7f 47static CWCB clientWriteComplete;
7a2f978b 48static PF clientReadRequest;
49static PF connStateFree;
50static PF requestTimeout;
4b4cd312 51static void clientFinishIMS(clientHttpRequest * http);
1b02b5be 52static STCB clientGetHeadersForIMS;
2920225f 53static STCB clientGetHeadersForSpecialIMS;
7a2f978b 54static int CheckQuickAbort2(const clientHttpRequest *);
1b02b5be 55static int clientCheckTransferDone(clientHttpRequest *);
7a2f978b 56static void CheckQuickAbort(clientHttpRequest *);
88aad2e5 57static void checkFailureRatio(err_type, hier_code);
fb63215a 58static void clientProcessMiss(clientHttpRequest *);
7a2f978b 59static void clientAppendReplyHeader(char *, const char *, size_t *, size_t);
2334c194 60size_t clientBuildReplyHeader(clientHttpRequest *, char *, size_t, size_t *, char *, size_t);
99edd1c3 61static clientHttpRequest *parseHttpRequestAbort(ConnStateData * conn, const char *uri);
7a2f978b 62static clientHttpRequest *parseHttpRequest(ConnStateData *, method_t *, int *, char **, size_t *);
582b6456 63static RH clientRedirectDone;
1b02b5be 64static STCB clientHandleIMSReply;
f5b8bbc4 65static int clientGetsOldEntry(StoreEntry * new, StoreEntry * old, request_t * request);
66static int checkAccelOnly(clientHttpRequest *);
7c1d4010 67static int clientOnlyIfCached(clientHttpRequest * http);
7a2f978b 68static STCB clientSendMoreData;
69static STCB clientCacheHit;
99edd1c3 70static void clientInterpretRequestHeaders(clientHttpRequest *);
fb63215a 71static void clientProcessRequest(clientHttpRequest *);
72static void clientProcessExpired(void *data);
038eb4ed 73static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http);
cb69b4c7 74static HttpReply *clientConstructProxyAuthReply(clientHttpRequest * http);
50ddd7a4 75static int clientCachable(clientHttpRequest * http);
76static int clientHierarchical(clientHttpRequest * http);
efb9218c 77static int clientCheckContentLength(request_t * r);
ea6f43cd 78
38d7734b 79static int
382d851a 80checkAccelOnly(clientHttpRequest * http)
38d7734b 81{
82 /* return TRUE if someone makes a proxy request to us and
83 * we are in httpd-accel only mode */
f1dc9b30 84 if (!Config2.Accel.on)
38d7734b 85 return 0;
17a0a4ee 86 if (Config.onoff.accel_with_proxy)
38d7734b 87 return 0;
382d851a 88 if (http->request->protocol == PROTO_CACHEOBJ)
38d7734b 89 return 0;
77ed547a 90 if (http->flags.accel)
38d7734b 91 return 0;
92 return 1;
93}
94
b8d8561b 95void
382d851a 96clientAccessCheck(void *data)
f88bb09c 97{
382d851a 98 clientHttpRequest *http = data;
99 ConnStateData *conn = http->conn;
99edd1c3 100 const char *browser;
17a0a4ee 101 if (Config.onoff.ident_lookup && conn->ident.state == IDENT_NONE) {
425ad52e 102 identStart(-1, conn, clientAccessCheck, http);
ba1d2afa 103 return;
104 }
382d851a 105 if (checkAccelOnly(http)) {
106 clientAccessCheckDone(0, http);
75e88d56 107 return;
f88bb09c 108 }
99edd1c3 109#if OLD_CODE
f182d1c5 110 browser = mime_get_header(http->request->headers, "User-Agent");
99edd1c3 111#else
112 browser = httpHeaderGetStr(&http->request->header, HDR_USER_AGENT);
113#endif
f1dc9b30 114 http->acl_checklist = aclChecklistCreate(Config.accessList.http,
382d851a 115 http->request,
116 conn->peer.sin_addr,
75e88d56 117 browser,
382d851a 118 conn->ident.ident);
119 aclNBCheck(http->acl_checklist, clientAccessCheckDone, http);
f88bb09c 120}
121
7c1d4010 122/*
123 * returns true if client specified that the object must come from the cache
124 * witout contacting origin server
125 */
126static int
127clientOnlyIfCached(clientHttpRequest * http)
128{
129 const request_t *r = http->request;
130 assert(r);
ea285003 131 return r->cache_control &&
8e092300 132 EBIT_TEST(r->cache_control->mask, CC_ONLY_IF_CACHED);
7c1d4010 133}
134
cb69b4c7 135static HttpReply *
ee1679df 136clientConstructProxyAuthReply(clientHttpRequest * http)
cb69b4c7 137{
138 ErrorState *err = errorCon(ERR_CACHE_ACCESS_DENIED, HTTP_PROXY_AUTHENTICATION_REQUIRED);
139 HttpReply *rep;
140 err->request = requestLink(http->request);
141 rep = errorBuildReply(err);
142 errorStateFree(err);
143 /* add Authenticate header */
038eb4ed 144 httpHeaderPutStr(&rep->header, HDR_PROXY_AUTHENTICATE, proxy_auth_challenge);
cb69b4c7 145 return rep;
146}
fc5d6f7f 147
23d92c64 148StoreEntry *
f44af445 149clientCreateStoreEntry(clientHttpRequest * h, method_t m, int flags)
79e0dc20 150{
151 StoreEntry *e;
40defb17 152 /*
153 * For erroneous requests, we might not have a h->request,
154 * so make a fake one.
155 */
99edd1c3 156 if (h->request == NULL)
157 h->request = requestLink(requestCreate(m, PROTO_NONE, NULL));
23d92c64 158 e = storeCreateEntry(h->uri, h->log_uri, flags, m);
79e0dc20 159 storeClientListAdd(e, h);
7021844c 160 storeClientCopy(e, 0, 0, 4096, memAllocate(MEM_4K_BUF), clientSendMoreData, h);
79e0dc20 161 return e;
162}
163
164
b8d8561b 165void
75e88d56 166clientAccessCheckDone(int answer, void *data)
f88bb09c 167{
382d851a 168 clientHttpRequest *http = data;
02922e76 169 int page_id = -1;
9b312a19 170 ErrorState *err = NULL;
1bea1d83 171 HttpReply *rep;
23d92c64 172 debug(33, 5) ("clientAccessCheckDone: '%s' answer=%d\n", http->uri, answer);
382d851a 173 http->acl_checklist = NULL;
fc5d6f7f 174 if (answer == ACCESS_ALLOWED) {
23d92c64 175 urlCanonical(http->request, http->uri);
ce66013b 176 assert(http->redirect_state == REDIRECT_NONE);
382d851a 177 http->redirect_state = REDIRECT_PENDING;
178 redirectStart(http, clientRedirectDone, http);
fc5d6f7f 179 } else if (answer == ACCESS_REQ_PROXY_AUTH) {
180 http->al.http.code = HTTP_PROXY_AUTHENTICATION_REQUIRED;
181 http->log_type = LOG_TCP_DENIED;
f44af445 182 http->entry = clientCreateStoreEntry(http, http->request->method, 0);
1bea1d83 183 /* create appropreate response */
184 rep = clientConstructProxyAuthReply(http);
185 httpReplySwapOut(rep, http->entry);
186 /* do not need it anymore */
187 httpReplyDestroy(rep);
f88bb09c 188 } else {
23d92c64 189 debug(33, 5) ("Access Denied: %s\n", http->uri);
901e234d 190 debug(33, 5) ("AclMatchedName = %s\n",
6f47fbc7 191 AclMatchedName ? AclMatchedName : "<null>");
79a15e0a 192 http->log_type = LOG_TCP_DENIED;
f44af445 193 http->entry = clientCreateStoreEntry(http, http->request->method, 0);
02922e76 194 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
195 /* NOTE: don't use HTTP_UNAUTHORIZED because then the
196 * stupid browser wants us to authenticate */
197 err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
198 err->request = requestLink(http->request);
199 err->src_addr = http->conn->peer.sin_addr;
200 if (page_id > 0)
201 err->page_id = page_id;
202 errorAppendEntry(http->entry, err);
f88bb09c 203 }
204}
205
b8d8561b 206static void
207clientRedirectDone(void *data, char *result)
f88bb09c 208{
382d851a 209 clientHttpRequest *http = data;
88738790 210 size_t l;
c0cdaf99 211 request_t *new_request = NULL;
382d851a 212 request_t *old_request = http->request;
23d92c64 213 debug(33, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri,
f88bb09c 214 result ? result : "NULL");
ce66013b 215 assert(http->redirect_state == REDIRECT_PENDING);
382d851a 216 http->redirect_state = REDIRECT_DONE;
77ed547a 217 if (result && strcmp(result, http->uri))
20cc1450 218 new_request = urlParse(old_request->method, result);
c0cdaf99 219 if (new_request) {
23d92c64 220 safe_free(http->uri);
88738790 221 /* need to malloc because the URL returned by the redirector might
222 * not be big enough to append the local domain
223 * -- David Lamkin drl@net-tel.co.uk */
1da5651f 224 l = strlen(result) + Config.appendDomainLen + 64;
23d92c64 225 http->uri = xcalloc(l, 1);
226 xstrncpy(http->uri, result, l);
20cc1450 227 new_request->http_ver = old_request->http_ver;
99edd1c3 228#if OLD_CODE
16b9d3df 229 new_request->headers = xstrdup(old_request->headers);
2357f74a 230 new_request->headers_sz = old_request->headers_sz;
99edd1c3 231#else
232 new_request->prefix = xstrdup(old_request->prefix);
233 new_request->prefix_sz = old_request->prefix_sz;
234 httpHeaderUpdate(&new_request->header, &old_request->header);
235#endif
77ed547a 236 new_request->client_addr = old_request->client_addr;
237 EBIT_SET(new_request->flags, REQ_REDIRECTED);
6cf028ab 238 if (old_request->body) {
239 new_request->body = xmalloc(old_request->body_sz);
240 xmemcpy(new_request->body, old_request->body, old_request->body_sz);
241 new_request->body_sz = old_request->body_sz;
242 }
20cc1450 243 requestUnlink(old_request);
382d851a 244 http->request = requestLink(new_request);
23d92c64 245 urlCanonical(http->request, http->uri);
f88bb09c 246 }
99edd1c3 247 clientInterpretRequestHeaders(http);
23d92c64 248 fd_note(http->conn->fd, http->uri);
fb63215a 249 clientProcessRequest(http);
e81957b7 250}
251
fb63215a 252static void
253clientProcessExpired(void *data)
620da955 254{
382d851a 255 clientHttpRequest *http = data;
23d92c64 256 char *url = http->uri;
620da955 257 StoreEntry *entry = NULL;
23d92c64 258 debug(33, 3) ("clientProcessExpired: '%s'\n", http->uri);
7c1d4010 259 /*
260 * check if we are allowed to contact other servers
261 * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return
6106c6fc 262 * a stale entry *if* it matches client requirements
7c1d4010 263 */
264 if (clientOnlyIfCached(http)) {
265 clientProcessOnlyIfCachedMiss(http);
266 return;
267 }
79a15e0a 268 EBIT_SET(http->request->flags, REQ_REFRESH);
382d851a 269 http->old_entry = http->entry;
620da955 270 entry = storeCreateEntry(url,
23d92c64 271 http->log_uri,
382d851a 272 http->request->flags,
273 http->request->method);
620da955 274 /* NOTE, don't call storeLockObject(), storeCreateEntry() does it */
fe96bbe6 275 storeClientListAdd(entry, http);
276 storeClientListAdd(http->old_entry, http);
382d851a 277 entry->lastmod = http->old_entry->lastmod;
50ddd7a4 278 debug(33, 5) ("clientProcessExpired: setting lmt = %d\n",
5f6ac48b 279 (int) entry->lastmod);
d1a43e28 280 entry->refcount++; /* EXPIRED CASE */
382d851a 281 http->entry = entry;
282 http->out.offset = 0;
fb63215a 283 protoDispatch(http->conn->fd, http->entry, http->request);
f990cccc 284 /* Register with storage manager to receive updates when data comes in. */
0e473d70 285 if (entry->store_status == STORE_ABORTED)
6cf028ab 286 debug(33, 0) ("clientProcessExpired: entry->swap_status == STORE_ABORTED\n");
d89d1fb6 287 storeClientCopy(entry,
fe96bbe6 288 http->out.offset,
d89d1fb6 289 http->out.offset,
290 4096,
7021844c 291 memAllocate(MEM_4K_BUF),
1b02b5be 292 clientHandleIMSReply,
d89d1fb6 293 http);
620da955 294}
295
91f4d519 296static int
297clientGetsOldEntry(StoreEntry * new_entry, StoreEntry * old_entry, request_t * request)
298{
71a8a965 299 const http_status status = new_entry->mem_obj->reply->sline.status;
300 if (0 == status) {
301 debug(33, 5) ("clientGetsOldEntry: YES, broken HTTP reply\n");
302 return 1;
303 }
91f4d519 304 /* If the reply is anything but "Not Modified" then
305 * we must forward it to the client */
71a8a965 306 if (HTTP_NOT_MODIFIED != status) {
cb69b4c7 307 debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", status);
91f4d519 308 return 0;
309 }
310 /* If the client did not send IMS in the request, then it
311 * must get the old object, not this "Not Modified" reply */
79a15e0a 312 if (!EBIT_TEST(request->flags, REQ_IMS)) {
a3d5953d 313 debug(33, 5) ("clientGetsOldEntry: YES, no client IMS\n");
91f4d519 314 return 1;
315 }
316 /* If the client IMS time is prior to the entry LASTMOD time we
317 * need to send the old object */
318 if (modifiedSince(old_entry, request)) {
5f6ac48b 319 debug(33, 5) ("clientGetsOldEntry: YES, modified since %d\n",
1afe05c5 320 (int) request->ims);
91f4d519 321 return 1;
322 }
a3d5953d 323 debug(33, 5) ("clientGetsOldEntry: NO, new one is fine\n");
91f4d519 324 return 0;
325}
326
327
52d4522b 328static void
1b02b5be 329clientHandleIMSReply(void *data, char *buf, ssize_t size)
620da955 330{
382d851a 331 clientHttpRequest *http = data;
382d851a 332 StoreEntry *entry = http->entry;
620da955 333 MemObject *mem = entry->mem_obj;
9fb13bb6 334 const char *url = storeUrl(entry);
e92d33a5 335 int unlink_request = 0;
76cff1d4 336 StoreEntry *oldentry;
71a8a965 337 const http_status status = mem->reply->sline.status;
93f9abd0 338 debug(33, 3) ("clientHandleIMSReply: %s, %d bytes\n", url, (int) size);
3f6c0fb2 339 memFree(MEM_4K_BUF, buf);
76a5501f 340 buf = NULL;
620da955 341 /* unregister this handler */
070d29eb 342 if (size < 0 || entry->store_status == STORE_ABORTED) {
1b02b5be 343 debug(33, 3) ("clientHandleIMSReply: ABORTED '%s'\n", url);
c54e9052 344 /* We have an existing entry, but failed to validate it */
accc6d47 345 /* Its okay to send the old one anyway */
346 http->log_type = LOG_TCP_REFRESH_FAIL_HIT;
347 storeUnregister(entry, http);
348 storeUnlockObject(entry);
349 entry = http->entry = http->old_entry;
350 entry->refcount++;
71a8a965 351 } else if (STORE_PENDING == entry->store_status && 0 == status) {
1b02b5be 352 debug(33, 3) ("clientHandleIMSReply: Incomplete headers for '%s'\n", url);
4455552a 353 if (size >= 4096) {
354 /* will not get any bigger than that */
355 debug(33, 3) ("clientHandleIMSReply: Reply is too large '%s', using old entry\n", url);
878570db 356 /* use old entry, this repeats the code abovez */
4455552a 357 http->log_type = LOG_TCP_REFRESH_FAIL_HIT;
358 storeUnregister(entry, http);
743eebfa 359 storeUnlockObject(entry);
4455552a 360 entry = http->entry = http->old_entry;
361 entry->refcount++;
878570db 362 /* continue */
4455552a 363 } else {
4455552a 364 storeClientCopy(entry,
365 http->out.offset + size,
366 http->out.offset,
367 4096,
368 memAllocate(MEM_4K_BUF),
369 clientHandleIMSReply,
370 http);
878570db 371 return;
4455552a 372 }
382d851a 373 } else if (clientGetsOldEntry(entry, http->old_entry, http->request)) {
620da955 374 /* We initiated the IMS request, the client is not expecting
76cff1d4 375 * 304, so put the good one back. First, make sure the old entry
376 * headers have been loaded from disk. */
382d851a 377 oldentry = http->old_entry;
382d851a 378 http->log_type = LOG_TCP_REFRESH_HIT;
d89d1fb6 379 if (oldentry->mem_obj->request == NULL) {
380 oldentry->mem_obj->request = requestLink(mem->request);
381 unlink_request = 1;
e92d33a5 382 }
64763c37 383 /* Don't memcpy() the whole reply structure here. For example,
384 * www.thegist.com (Netscape/1.13) returns a content-length for
385 * 304's which seems to be the length of the 304 HEADERS!!! and
386 * not the body they refer to. */
71a8a965 387 httpReplyUpdateOnNotModified(oldentry->mem_obj->reply, mem->reply);
d89d1fb6 388 storeTimestampsSet(oldentry);
382d851a 389 storeUnregister(entry, http);
6d54aea8 390 storeUnlockObject(entry);
382d851a 391 entry = http->entry = oldentry;
41fad779 392 entry->timestamp = squid_curtime;
657266fe 393 if (unlink_request) {
e92d33a5 394 requestUnlink(entry->mem_obj->request);
657266fe 395 entry->mem_obj->request = NULL;
396 }
620da955 397 } else {
398 /* the client can handle this reply, whatever it is */
382d851a 399 http->log_type = LOG_TCP_REFRESH_MISS;
71a8a965 400 if (HTTP_NOT_MODIFIED == mem->reply->sline.status) {
382d851a 401 http->old_entry->timestamp = squid_curtime;
402 http->old_entry->refcount++;
403 http->log_type = LOG_TCP_REFRESH_HIT;
d1a43e28 404 }
382d851a 405 storeUnregister(http->old_entry, http);
406 storeUnlockObject(http->old_entry);
620da955 407 }
382d851a 408 http->old_entry = NULL; /* done with old_entry */
0e473d70 409 if (entry->store_status == STORE_ABORTED) {
6cf028ab 410 debug(33, 0) ("clientHandleIMSReply: IMS swapin failed on aborted object\n");
411 http->log_type = LOG_TCP_SWAPFAIL_MISS;
412 clientProcessMiss(http);
413 return;
414 }
fdfc0d63 415 /*
416 * use clientCacheHit() here as the callback because we might
417 * be swapping in from disk, and the file might not really be
418 * there
419 */
d89d1fb6 420 storeClientCopy(entry,
fe96bbe6 421 http->out.offset,
d89d1fb6 422 http->out.offset,
423 4096,
7021844c 424 memAllocate(MEM_4K_BUF),
e8479da9 425 clientCacheHit,
d89d1fb6 426 http);
620da955 427}
91f4d519 428
429int
304d07cb 430modifiedSince(StoreEntry * entry, request_t * request)
91f4d519 431{
432 int object_length;
433 MemObject *mem = entry->mem_obj;
9fb13bb6 434 debug(33, 3) ("modifiedSince: '%s'\n", storeUrl(entry));
91f4d519 435 if (entry->lastmod < 0)
436 return 1;
437 /* Find size of the object */
038eb4ed 438 object_length = mem->reply->content_length;
cb69b4c7 439 if (object_length < 0)
07304bf9 440 object_length = contentLen(entry);
91f4d519 441 if (entry->lastmod > request->ims) {
a3d5953d 442 debug(33, 3) ("--> YES: entry newer than client\n");
91f4d519 443 return 1;
444 } else if (entry->lastmod < request->ims) {
a3d5953d 445 debug(33, 3) ("--> NO: entry older than client\n");
91f4d519 446 return 0;
447 } else if (request->imslen < 0) {
a3d5953d 448 debug(33, 3) ("--> NO: same LMT, no client length\n");
91f4d519 449 return 0;
450 } else if (request->imslen == object_length) {
a3d5953d 451 debug(33, 3) ("--> NO: same LMT, same length\n");
91f4d519 452 return 0;
453 } else {
a3d5953d 454 debug(33, 3) ("--> YES: same LMT, different length\n");
91f4d519 455 return 1;
456 }
457}
b3b64e58 458
99edd1c3 459#if UNUSED_CODE
b3b64e58 460char *
382d851a 461clientConstructTraceEcho(clientHttpRequest * http)
b3b64e58 462{
463 LOCAL_ARRAY(char, line, 256);
464 LOCAL_ARRAY(char, buf, 8192);
465 size_t len;
466 memset(buf, '\0', 8192);
042461c3 467 snprintf(buf, 8192, "HTTP/1.0 200 OK\r\n");
56878878 468 snprintf(line, 256, "Date: %s\r\n", mkrfc1123(squid_curtime));
b3b64e58 469 strcat(buf, line);
042461c3 470 snprintf(line, 256, "Server: Squid/%s\r\n", SQUID_VERSION);
b3b64e58 471 strcat(buf, line);
042461c3 472 snprintf(line, 256, "Content-Type: message/http\r\n");
b3b64e58 473 strcat(buf, line);
474 strcat(buf, "\r\n");
475 len = strlen(buf);
382d851a 476 httpBuildRequestHeader(http->request,
477 http->request,
b3b64e58 478 NULL, /* entry */
b3b64e58 479 NULL, /* in_len */
480 buf + len,
481 8192 - len,
603a02fd 482 http->conn->fd,
483 0); /* flags */
382d851a 484 http->log_type = LOG_TCP_MISS;
4869e8a1 485 http->http_code = HTTP_OK;
b3b64e58 486 return buf;
487}
99edd1c3 488#endif /* UNUSED_CODE */
a90eae18 489
490void
382d851a 491clientPurgeRequest(clientHttpRequest * http)
a90eae18 492{
a90eae18 493 StoreEntry *entry;
9b312a19 494 ErrorState *err = NULL;
9fb13bb6 495 const cache_key *k;
8d6da567 496 HttpReply *r;
9b312a19 497 debug(33, 3) ("Config.onoff.enable_purge = %d\n", Config.onoff.enable_purge);
17a0a4ee 498 if (!Config.onoff.enable_purge) {
79a15e0a 499 http->log_type = LOG_TCP_DENIED;
fe40a877 500 err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
9b312a19 501 err->request = requestLink(http->request);
502 err->src_addr = http->conn->peer.sin_addr;
f44af445 503 http->entry = clientCreateStoreEntry(http, http->request->method, 0);
79e0dc20 504 errorAppendEntry(http->entry, err);
a90eae18 505 return;
506 }
382d851a 507 http->log_type = LOG_TCP_MISS;
23d92c64 508 k = storeKeyPublic(http->uri, METHOD_GET);
9fb13bb6 509 if ((entry = storeGet(k)) == NULL) {
9b312a19 510 http->http_code = HTTP_NOT_FOUND;
a90eae18 511 } else {
512 storeRelease(entry);
9b312a19 513 http->http_code = HTTP_OK;
a90eae18 514 }
afa9f1cd 515 debug(33, 4) ("clientGetHeadersForIMS: Not modified '%s'\n",
516 storeUrl(entry));
517 /*
518 * Make a new entry to hold the reply to be written
519 * to the client.
520 */
afa9f1cd 521 http->entry = clientCreateStoreEntry(http, http->request->method, 0);
8d6da567 522 httpReplyReset(r = http->entry->mem_obj->reply);
523 httpReplySetHeaders(r, 1.0, http->http_code, NULL, NULL, 0, 0, -1);
524 httpReplySwapOut(r, http->entry);
afa9f1cd 525 storeComplete(http->entry);
a90eae18 526}
e33ba616 527
528int
ea3a2a69 529checkNegativeHit(StoreEntry * e)
e33ba616 530{
79a15e0a 531 if (!EBIT_TEST(e->flag, ENTRY_NEGCACHED))
ea3a2a69 532 return 0;
533 if (e->expires <= squid_curtime)
534 return 0;
535 if (e->store_status != STORE_OK)
536 return 0;
537 return 1;
e33ba616 538}
7a2f978b 539
69c95dd3 540static void
541updateCDJunkStats()
542{
ea285003 543 /* rewrite */
69c95dd3 544}
545
a7c05555 546void
0e473d70 547clientUpdateCounters(clientHttpRequest * http)
a7c05555 548{
ee1679df 549 int svc_time = tvSubMsec(http->start, current_time);
550 icp_ping_data *i;
39edba21 551 HierarchyLogEntry *H;
0e473d70 552 Counter.client_http.requests++;
d8b83480 553 if (isTcpHit(http->log_type))
0e473d70 554 Counter.client_http.hits++;
0e473d70 555 if (http->request->err_type != ERR_NONE)
556 Counter.client_http.errors++;
12cf1be2 557 statHistCount(&Counter.client_http.all_svc_time, svc_time);
ee1679df 558 /*
559 * The idea here is not to be complete, but to get service times
560 * for only well-defined types. For example, we don't include
561 * LOG_TCP_REFRESH_FAIL_HIT because its not really a cache hit
562 * (we *tried* to validate it, but failed).
563 */
564 switch (http->log_type) {
565 case LOG_TCP_IMS_HIT:
12cf1be2 566 statHistCount(&Counter.client_http.nm_svc_time, svc_time);
ee1679df 567 break;
568 case LOG_TCP_HIT:
569 case LOG_TCP_MEM_HIT:
12cf1be2 570 statHistCount(&Counter.client_http.hit_svc_time, svc_time);
ee1679df 571 break;
572 case LOG_TCP_MISS:
573 case LOG_TCP_CLIENT_REFRESH_MISS:
12cf1be2 574 statHistCount(&Counter.client_http.miss_svc_time, svc_time);
ee1679df 575 break;
576 default:
577 /* make compiler warnings go away */
578 break;
579 }
39edba21 580 H = &http->request->hier;
69c95dd3 581 switch (H->alg) {
582 case PEER_SA_DIGEST:
17b6e784 583 Counter.cd.times_used++;
69c95dd3 584 break;
585 case PEER_SA_ICP:
586 Counter.icp.times_used++;
587 i = &H->icp;
588 if (0 != i->stop.tv_sec && 0 != i->start.tv_sec)
589 statHistCount(&Counter.icp.query_svc_time,
590 tvSubUsec(i->start, i->stop));
591 if (i->timeout)
592 Counter.icp.query_timeouts++;
593 break;
594 case PEER_SA_NETDB:
595 Counter.netdb.times_used++;
596 break;
597 default:
598 break;
17b6e784 599 }
a7c05555 600}
601
7a2f978b 602static void
603httpRequestFree(void *data)
604{
605 clientHttpRequest *http = data;
606 clientHttpRequest **H;
607 ConnStateData *conn = http->conn;
608 StoreEntry *entry = http->entry;
609 request_t *request = http->request;
610 MemObject *mem = NULL;
93f9abd0 611 debug(33, 3) ("httpRequestFree: %s\n", storeUrl(entry));
1b02b5be 612 if (!clientCheckTransferDone(http)) {
7a2f978b 613 if (entry)
614 storeUnregister(entry, http); /* unregister BEFORE abort */
615 CheckQuickAbort(http);
616 entry = http->entry; /* reset, IMS might have changed it */
617 if (entry && entry->ping_status == PING_WAITING)
618 storeReleaseRequest(entry);
79d39a72 619 protoUnregister(entry, request);
7a2f978b 620 }
621 assert(http->log_type < LOG_TYPE_MAX);
622 if (entry)
623 mem = entry->mem_obj;
624 if (http->out.size || http->log_type) {
625 http->al.icp.opcode = 0;
ef65d6ca 626 http->al.url = http->log_uri;
627 debug(33, 9) ("httpRequestFree: al.url='%s'\n", http->al.url);
7a2f978b 628 if (mem) {
cb69b4c7 629 http->al.http.code = mem->reply->sline.status;
038eb4ed 630 http->al.http.content_type = strBuf(mem->reply->content_type);
7a2f978b 631 }
632 http->al.cache.caddr = conn->log_addr;
633 http->al.cache.size = http->out.size;
634 http->al.cache.code = http->log_type;
635 http->al.cache.msec = tvSubMsec(http->start, current_time);
636 http->al.cache.ident = conn->ident.ident;
637 if (request) {
638 http->al.http.method = request->method;
99edd1c3 639 http->al.headers.request = request->prefix;
7a2f978b 640 http->al.hier = request->hier;
641 }
642 accessLogLog(&http->al);
a7c05555 643 clientUpdateCounters(http);
6f47fbc7 644 clientdbUpdate(conn->peer.sin_addr, http->log_type, PROTO_HTTP, http->out.size);
7a2f978b 645 }
646 if (http->redirect_state == REDIRECT_PENDING)
23d92c64 647 redirectUnregister(http->uri, http);
7a2f978b 648 if (http->acl_checklist)
649 aclChecklistFree(http->acl_checklist);
88aad2e5 650 if (request)
c24fca87 651 checkFailureRatio(request->err_type, http->al.hier.code);
23d92c64 652 safe_free(http->uri);
653 safe_free(http->log_uri);
7a2f978b 654 safe_free(http->al.headers.reply);
655 if (entry) {
656 http->entry = NULL;
657 storeUnregister(entry, http);
658 storeUnlockObject(entry);
659 }
660 /* old_entry might still be set if we didn't yet get the reply
1b02b5be 661 * code in clientHandleIMSReply() */
7a2f978b 662 if (http->old_entry) {
663 storeUnregister(http->old_entry, http);
664 storeUnlockObject(http->old_entry);
665 http->old_entry = NULL;
666 }
667 requestUnlink(http->request);
668 assert(http != http->next);
669 assert(http->conn->chr != NULL);
670 H = &http->conn->chr;
671 while (*H) {
672 if (*H == http)
673 break;
674 H = &(*H)->next;
675 }
676 assert(*H != NULL);
677 *H = http->next;
678 http->next = NULL;
679 cbdataFree(http);
680}
681
682/* This is a handler normally called by comm_close() */
683static void
684connStateFree(int fd, void *data)
685{
686 ConnStateData *connState = data;
687 clientHttpRequest *http;
93f9abd0 688 debug(33, 3) ("connStateFree: FD %d\n", fd);
7a2f978b 689 assert(connState != NULL);
79d39a72 690 while ((http = connState->chr) != NULL) {
7a2f978b 691 assert(http->conn == connState);
692 assert(connState->chr != connState->chr->next);
693 httpRequestFree(http);
694 }
695 if (connState->ident.fd > -1)
696 comm_close(connState->ident.fd);
697 safe_free(connState->in.buf);
59c4d35b 698 /* XXX account connState->in.buf */
7a2f978b 699 pconnHistCount(0, connState->nrequests);
700 cbdataFree(connState);
701}
702
703static void
99edd1c3 704clientInterpretRequestHeaders(clientHttpRequest * http)
7a2f978b 705{
706 request_t *request = http->request;
99edd1c3 707#if OLD_CODE
7a2f978b 708 char *request_hdr = request->headers;
99edd1c3 709 const char *t = NULL;
710#else
711 const HttpHeader *req_hdr = &request->header;
712#if USE_USERAGENT_LOG
713 const char *str;
714#endif
715#endif
716#if OLD_CODE
7a2f978b 717 request->ims = -2;
718 request->imslen = -1;
99edd1c3 719 if ((t = httpHeaderGetStr(req_hdr, HDR_IF_MODIFIED_SINCE))) {
79a15e0a 720 EBIT_SET(request->flags, REQ_IMS);
7a2f978b 721 request->ims = parse_rfc1123(t);
99edd1c3 722 /*
723 * "length=..." is not in the HTTP/1.1 specs. Any real proof that we
724 * should hornor it? Send complains to rousskov@nlanr.net
725 */
7a2f978b 726 while ((t = strchr(t, ';'))) {
727 for (t++; isspace(*t); t++);
728 if (strncasecmp(t, "length=", 7) == 0)
729 request->imslen = atoi(t + 7);
730 }
731 }
99edd1c3 732#else
733 request->imslen = -1;
734 request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE);
735 if (request->ims > 0)
736 EBIT_SET(request->flags, REQ_IMS);
737#endif
738 if (httpHeaderHas(req_hdr, HDR_PRAGMA)) {
739 String s = httpHeaderGetList(req_hdr, HDR_PRAGMA);
740 if (strListIsMember(&s, "no-cache", ','))
741 EBIT_SET(request->flags, REQ_NOCACHE);
742 stringClean(&s);
7a2f978b 743 }
99edd1c3 744 if (httpHeaderHas(req_hdr, HDR_RANGE)) {
79a15e0a 745 EBIT_SET(request->flags, REQ_NOCACHE);
746 EBIT_SET(request->flags, REQ_RANGE);
99edd1c3 747 /* Request-Range: deleted, not in the specs. Does it exist? */
7a2f978b 748 }
99edd1c3 749 if (httpHeaderHas(req_hdr, HDR_AUTHORIZATION))
79a15e0a 750 EBIT_SET(request->flags, REQ_AUTH);
7a2f978b 751 if (request->login[0] != '\0')
79a15e0a 752 EBIT_SET(request->flags, REQ_AUTH);
99edd1c3 753#if OLD_CODE
ea285003 754 if ((t = httpHeaderGetStr(req_hdr, HDR_PROXY_CONNECTION))) {
7a2f978b 755 if (!strcasecmp(t, "Keep-Alive"))
79a15e0a 756 EBIT_SET(request->flags, REQ_PROXY_KEEPALIVE);
7a2f978b 757 }
99edd1c3 758#else
759 if (httpMsgIsPersistent(request->http_ver, req_hdr))
760 EBIT_SET(request->flags, REQ_PROXY_KEEPALIVE);
761#endif
762 if (httpHeaderHas(req_hdr, HDR_VIA)) {
763 String s = httpHeaderGetList(req_hdr, HDR_VIA);
edf3ffdc 764 /* ThisCache cannot be a member of Via header, "1.0 ThisCache" can */
765 if (strListIsSubstr(&s, ThisCache, ',')) {
9330543e 766 debug(33, 1) ("WARNING: Forwarding loop detected for '%s'\n",
767 http->uri);
768 debug(33, 1) ("--> %s\n", strBuf(s));
79a15e0a 769 EBIT_SET(request->flags, REQ_LOOPDETECT);
7a2f978b 770 }
d21f1c54 771#if FORW_VIA_DB
ea285003 772 fvdbCountVia(strBuf(s));
d21f1c54 773#endif
99edd1c3 774 stringClean(&s);
d21f1c54 775 }
7a2f978b 776#if USE_USERAGENT_LOG
99edd1c3 777 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT)))
778 logUserAgent(fqdnFromAddr(http->conn->peer.sin_addr), str);
d21f1c54 779#endif
780#if FORW_VIA_DB
99edd1c3 781 if (httpHeaderHas(req_hdr, HDR_X_FORWARDED_FOR)) {
782 String s = httpHeaderGetList(req_hdr, HDR_X_FORWARDED_FOR);
783 fvdbCountForw(strBuf(s));
784 stringClean(&s);
785 }
7a2f978b 786#endif
99edd1c3 787#if OLD_CODE
4e41e277 788 if ((t = mime_get_header_field(request_hdr, "Cache-control", "max-age="))) {
789 request->max_age = atoi(t + 8);
790 } else {
791 request->max_age = -1;
792 }
793 if ((t = mime_get_header_field(request_hdr, "Cache-control", "only-if-cached"))) {
794 EBIT_SET(request->flags, REQ_CC_ONLY_IF_CACHED);
7a2f978b 795 }
99edd1c3 796#else
797 request->cache_control = httpHeaderGetCc(req_hdr);
798#endif
7a2f978b 799 if (request->method == METHOD_TRACE) {
99edd1c3 800#if OLD_CODE
7a2f978b 801 if ((t = mime_get_header(request_hdr, "Max-Forwards")))
802 request->max_forwards = atoi(t);
99edd1c3 803#else
804 request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS);
805#endif
7a2f978b 806 }
50ddd7a4 807 if (clientCachable(http))
808 EBIT_SET(request->flags, REQ_CACHABLE);
809 if (clientHierarchical(http))
810 EBIT_SET(request->flags, REQ_HIERARCHICAL);
99edd1c3 811 debug(33, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n",
50ddd7a4 812 EBIT_TEST(request->flags, REQ_NOCACHE) ? "SET" : "NOT SET");
99edd1c3 813 debug(33, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n",
50ddd7a4 814 EBIT_TEST(request->flags, REQ_CACHABLE) ? "SET" : "NOT SET");
99edd1c3 815 debug(33, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n",
50ddd7a4 816 EBIT_TEST(request->flags, REQ_HIERARCHICAL) ? "SET" : "NOT SET");
7a2f978b 817}
818
31be8b80 819static int
efb9218c 820clientCheckContentLength(request_t * r)
31be8b80 821{
99edd1c3 822#if OLD_CODE
31be8b80 823 char *t;
824 int len;
825 /*
efb9218c 826 * We only require a content-length for "upload" methods
31be8b80 827 */
efb9218c 828 if (0 == pumpMethod(r->method))
31be8b80 829 return 1;
31be8b80 830 t = mime_get_header(r->headers, "Content-Length");
efb9218c 831 if (NULL == t)
31be8b80 832 return 0;
833 len = atoi(t);
834 if (len < 0)
835 return 0;
836 return 1;
99edd1c3 837#else
838 /* We only require a content-length for "upload" methods */
ea285003 839 return !pumpMethod(r->method) ||
99edd1c3 840 httpHeaderGetInt(&r->header, HDR_CONTENT_LENGTH) >= 0;
841#endif
31be8b80 842}
843
7a2f978b 844static int
50ddd7a4 845clientCachable(clientHttpRequest * http)
7a2f978b 846{
23d92c64 847 const char *url = http->uri;
7a2f978b 848 request_t *req = http->request;
849 method_t method = req->method;
bd05e3e3 850 aclCheck_t ch;
851 memset(&ch, '\0', sizeof(ch));
852 /*
853 * Hopefully, nobody really wants 'no_cache' by client's IP
854 * address, but if they do, this should work if they use IP
855 * addresses in their ACLs, or if the client's address is in
856 * the FQDN cache.
857 *
858 * This may not work yet for 'dst' and 'dst_domain' ACLs.
859 */
860 ch.src_addr = http->conn->peer.sin_addr;
861 ch.request = http->request;
79a51871 862 /*
863 * aclCheckFast returns 1 for ALLOW and 0 for DENY. The default
864 * is ALLOW, so we require 'no_cache DENY foo' in squid.conf
865 * to indicate uncachable objects.
866 */
95291419 867 if (!aclCheckFast(Config.accessList.noCache, &ch))
4b4cd312 868 return 0;
7a2f978b 869 if (req->protocol == PROTO_HTTP)
870 return httpCachable(method);
871 /* FTP is always cachable */
872 if (req->protocol == PROTO_GOPHER)
873 return gopherCachable(url);
874 if (req->protocol == PROTO_WAIS)
875 return 0;
876 if (method == METHOD_CONNECT)
877 return 0;
878 if (method == METHOD_TRACE)
879 return 0;
880 if (req->protocol == PROTO_CACHEOBJ)
881 return 0;
882 return 1;
883}
884
885/* Return true if we can query our neighbors for this object */
886static int
50ddd7a4 887clientHierarchical(clientHttpRequest * http)
7a2f978b 888{
23d92c64 889 const char *url = http->uri;
7a2f978b 890 request_t *request = http->request;
891 method_t method = request->method;
892 const wordlist *p = NULL;
893
894 /* IMS needs a private key, so we can use the hierarchy for IMS only
895 * if our neighbors support private keys */
79a15e0a 896 if (EBIT_TEST(request->flags, REQ_IMS) && !neighbors_do_private_keys)
7a2f978b 897 return 0;
79a15e0a 898 if (EBIT_TEST(request->flags, REQ_AUTH))
7a2f978b 899 return 0;
900 if (method == METHOD_TRACE)
901 return 1;
902 if (method != METHOD_GET)
903 return 0;
904 /* scan hierarchy_stoplist */
905 for (p = Config.hierarchy_stoplist; p; p = p->next)
906 if (strstr(url, p->key))
907 return 0;
79a15e0a 908 if (EBIT_TEST(request->flags, REQ_LOOPDETECT))
7a2f978b 909 return 0;
910 if (request->protocol == PROTO_HTTP)
911 return httpCachable(method);
912 if (request->protocol == PROTO_GOPHER)
913 return gopherCachable(url);
914 if (request->protocol == PROTO_WAIS)
915 return 0;
916 if (request->protocol == PROTO_CACHEOBJ)
917 return 0;
918 return 1;
919}
920
cf50a0af 921int
7a2f978b 922isTcpHit(log_type code)
923{
924 /* this should be a bitmap for better optimization */
925 if (code == LOG_TCP_HIT)
926 return 1;
927 if (code == LOG_TCP_IMS_HIT)
928 return 1;
929 if (code == LOG_TCP_REFRESH_FAIL_HIT)
930 return 1;
931 if (code == LOG_TCP_REFRESH_HIT)
932 return 1;
933 if (code == LOG_TCP_NEGATIVE_HIT)
934 return 1;
935 if (code == LOG_TCP_MEM_HIT)
936 return 1;
937 return 0;
938}
939
940static void
941clientAppendReplyHeader(char *hdr, const char *line, size_t * sz, size_t max)
942{
943 size_t n = *sz + strlen(line) + 2;
944 if (n >= max)
945 return;
946 strcpy(hdr + (*sz), line);
947 strcat(hdr + (*sz), crlf);
948 *sz = n;
949}
950
adba4a64 951/* this entire function has to be rewriten using new interfaces @?@ @?@ */
7a2f978b 952size_t
953clientBuildReplyHeader(clientHttpRequest * http,
954 char *hdr_in,
2334c194 955 size_t hdr_in_sz,
7a2f978b 956 size_t * in_len,
957 char *hdr_out,
958 size_t out_sz)
959{
ab013258 960 LOCAL_ARRAY(char, no_forward, 1024);
7a2f978b 961 char *xbuf;
962 char *ybuf;
963 char *t = NULL;
2334c194 964 char *end;
7a2f978b 965 size_t len = 0;
966 size_t hdr_len = 0;
967 size_t l;
efb9218c 968 if (0 != strncmp(hdr_in, "HTTP/", 5))
969 return 0;
2334c194 970 hdr_len = headersEnd(hdr_in, hdr_in_sz);
8e8731ad 971 if (0 == hdr_len) {
93f9abd0 972 debug(33, 3) ("clientBuildReplyHeader: DIDN'T FIND END-OF-HEADERS\n");
7a2f978b 973 return 0;
974 }
7021844c 975 xbuf = memAllocate(MEM_4K_BUF);
976 ybuf = memAllocate(MEM_4K_BUF);
2334c194 977 end = hdr_in + hdr_len;
7a2f978b 978 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
7a2f978b 979 l = strcspn(t, crlf) + 1;
8febe7df 980 /* Wow, we might find a NULL before 'end' */
981 if (1 == l)
982 break;
7a2f978b 983 xstrncpy(xbuf, t, l > 4096 ? 4096 : l);
ae1acd0b 984 /* enforce 1.0 reply version, this hack will be rewritten */
2334c194 985 if (t == hdr_in && !strncasecmp(xbuf, "HTTP/", 5) && l > 8 &&
2ac76861 986 (isspace(xbuf[8]) || isspace(xbuf[9])))
987 xmemmove(xbuf + 5, "1.0 ", 4);
901e234d 988#if DONT_FILTER_THESE
989 /*
99edd1c3 990 * but you might want to if you run Squid as an HTTP accelerator
901e234d 991 */
7a2f978b 992 if (strncasecmp(xbuf, "Accept-Ranges:", 14) == 0)
993 continue;
994 if (strncasecmp(xbuf, "Etag:", 5) == 0)
995 continue;
996#endif
997 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
998 continue;
ab013258 999 if (strncasecmp(xbuf, "Connection:", 11) == 0) {
1000 handleConnectionHeader(0, no_forward, &xbuf[11]);
7a2f978b 1001 continue;
ab013258 1002 }
99edd1c3 1003 if (strncasecmp(xbuf, "keep-alive:", 11) == 0)
7a2f978b 1004 continue;
1005 if (strncasecmp(xbuf, "Set-Cookie:", 11) == 0)
1006 if (isTcpHit(http->log_type))
1007 continue;
ab013258 1008 if (!handleConnectionHeader(1, no_forward, xbuf))
1009 clientAppendReplyHeader(hdr_out, xbuf, &len, out_sz - 512);
7a2f978b 1010 }
7a2f978b 1011 /* Append X-Cache: */
02941a5d 1012 snprintf(ybuf, 4096, "X-Cache: %s from %s",
1013 isTcpHit(http->log_type) ? "HIT" : "MISS",
1014 getMyHostname());
7a2f978b 1015 clientAppendReplyHeader(hdr_out, ybuf, &len, out_sz);
6cfa8966 1016#if USE_CACHE_DIGESTS
57b1ff70 1017 /* Append X-Cache-Lookup: -- temporary hack, to be removed @?@ @?@ */
1018 snprintf(ybuf, 4096, "X-Cache-Lookup: %s from %s:%d",
1019 http->lookup_type ? http->lookup_type : "NONE",
1020 getMyHostname(), Config.Port.http->i);
1021 clientAppendReplyHeader(hdr_out, ybuf, &len, out_sz);
1022#endif
7a2f978b 1023 /* Append Proxy-Connection: */
79a15e0a 1024 if (EBIT_TEST(http->request->flags, REQ_PROXY_KEEPALIVE)) {
99edd1c3 1025 snprintf(ybuf, 4096, "Proxy-Connection: keep-alive");
7a2f978b 1026 clientAppendReplyHeader(hdr_out, ybuf, &len, out_sz);
1027 }
1028 clientAppendReplyHeader(hdr_out, null_string, &len, out_sz);
1029 if (in_len)
1030 *in_len = hdr_len;
1031 if ((l = strlen(hdr_out)) != len) {
1032 debug_trap("clientBuildReplyHeader: size mismatch");
1033 len = l;
1034 }
93f9abd0 1035 debug(33, 3) ("clientBuildReplyHeader: OUTPUT:\n%s\n", hdr_out);
3f6c0fb2 1036 memFree(MEM_4K_BUF, xbuf);
1037 memFree(MEM_4K_BUF, ybuf);
7a2f978b 1038 return len;
1039}
1040
4c323c5f 1041static void
7a2f978b 1042clientCacheHit(void *data, char *buf, ssize_t size)
1043{
1044 clientHttpRequest *http = data;
fdfc0d63 1045 StoreEntry *e;
93f9abd0 1046 debug(33, 3) ("clientCacheHit: %s, %d bytes\n", http->uri, (int) size);
7a2f978b 1047 if (size >= 0) {
1048 clientSendMoreData(data, buf, size);
1049 } else if (http->entry == NULL) {
93f9abd0 1050 debug(33, 3) ("clientCacheHit: request aborted\n");
7a2f978b 1051 } else {
1052 /* swap in failure */
93f9abd0 1053 debug(33, 3) ("clientCacheHit: swapin failure for %s\n", http->uri);
7a2f978b 1054 http->log_type = LOG_TCP_SWAPFAIL_MISS;
fdfc0d63 1055 if ((e = http->entry)) {
1056 http->entry = NULL;
1057 storeUnregister(e, http);
fdfc0d63 1058 storeUnlockObject(e);
1059 }
fb63215a 1060 clientProcessMiss(http);
7a2f978b 1061 }
1062}
1063
4c323c5f 1064static void
7a2f978b 1065clientSendMoreData(void *data, char *buf, ssize_t size)
1066{
1067 clientHttpRequest *http = data;
1068 StoreEntry *entry = http->entry;
1069 ConnStateData *conn = http->conn;
1070 int fd = conn->fd;
7a2f978b 1071 size_t hdrlen;
1072 size_t l = 0;
1073 size_t writelen;
1074 char *newbuf;
3f6c0fb2 1075 FREE *freefunc = memFree4K;
93f9abd0 1076 debug(33, 5) ("clientSendMoreData: %s, %d bytes\n", http->uri, (int) size);
7a2f978b 1077 assert(size <= SM_PAGE_SIZE);
40defb17 1078 assert(http->request != NULL);
93f9abd0 1079 debug(33, 5) ("clientSendMoreData: FD %d '%s', out.offset=%d \n",
5f6ac48b 1080 fd, storeUrl(entry), (int) http->out.offset);
7a2f978b 1081 if (conn->chr != http) {
1082 /* there is another object in progress, defer this one */
a43b3862 1083 debug(0, 0) ("clientSendMoreData: Deferring %s\n", storeUrl(entry));
7a2f978b 1084 freefunc(buf);
1085 return;
fdfc0d63 1086 } else if (entry && entry->store_status == STORE_ABORTED) {
d6e928c9 1087 /* call clientWriteComplete so the client socket gets closed */
1088 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
7a2f978b 1089 freefunc(buf);
1090 return;
1091 } else if (size < 0) {
d6e928c9 1092 /* call clientWriteComplete so the client socket gets closed */
1093 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
7a2f978b 1094 freefunc(buf);
1095 return;
1096 } else if (size == 0) {
d6e928c9 1097 /* call clientWriteComplete so the client socket gets closed */
1098 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
7a2f978b 1099 freefunc(buf);
1100 return;
1101 }
1102 writelen = size;
40defb17 1103 if (http->out.offset == 0) {
7a2f978b 1104 if (Config.onoff.log_mime_hdrs) {
2334c194 1105 size_t k;
1106 if ((k = headersEnd(buf, size))) {
7a2f978b 1107 safe_free(http->al.headers.reply);
1afe05c5 1108 http->al.headers.reply = xcalloc(k + 1, 1);
2334c194 1109 xstrncpy(http->al.headers.reply, buf, k);
7a2f978b 1110 }
1111 }
7021844c 1112 newbuf = memAllocate(MEM_8K_BUF);
7a2f978b 1113 hdrlen = 0;
2334c194 1114 l = clientBuildReplyHeader(http, buf, size, &hdrlen, newbuf, 8192);
7a2f978b 1115 if (l != 0) {
1116 writelen = l + size - hdrlen;
1117 assert(writelen <= 8192);
1118 /*
1119 * l is the length of the new headers in newbuf
1120 * hdrlen is the length of the old headers in buf
1121 * size - hdrlen is the amount of body in buf
1122 */
93f9abd0 1123 debug(33, 3) ("clientSendMoreData: Appending %d bytes after headers\n",
7a2f978b 1124 (int) (size - hdrlen));
6cf028ab 1125 if (((size - hdrlen) + l) > 8192) {
0e473d70 1126 debug(0, 0) ("Size, hdrlen, l %d, %d, %d\n", size, hdrlen, l);
6cf028ab 1127 return;
1128 }
7a2f978b 1129 xmemcpy(newbuf + l, buf + hdrlen, size - hdrlen);
1130 /* replace buf with newbuf */
1131 freefunc(buf);
1132 buf = newbuf;
3f6c0fb2 1133 freefunc = memFree8K;
7a2f978b 1134 newbuf = NULL;
1135 } else {
3f6c0fb2 1136 memFree(MEM_8K_BUF, newbuf);
7a2f978b 1137 newbuf = NULL;
1138 if (size < SM_PAGE_SIZE && entry->store_status == STORE_PENDING) {
1139 /* wait for more to arrive */
1140 storeClientCopy(entry,
1141 http->out.offset + size,
1142 http->out.offset,
1143 SM_PAGE_SIZE,
1144 buf,
1145 clientSendMoreData,
1146 http);
1147 return;
1148 }
1149 }
1150 }
1151 http->out.offset += size;
40defb17 1152 /*
1153 * ick, this is gross
1154 */
7a2f978b 1155 if (http->request->method == METHOD_HEAD) {
2334c194 1156 size_t k;
1157 if ((k = headersEnd(buf, size))) {
1158 writelen = k;
7a2f978b 1159 /* force end */
f68b5d81 1160 if (entry->store_status == STORE_PENDING)
c24fca87 1161 http->out.offset = entry->mem_obj->inmem_hi;
f68b5d81 1162 else
07304bf9 1163 http->out.offset = objectLen(entry);
7a2f978b 1164 }
1165 }
1166 comm_write(fd, buf, writelen, clientWriteComplete, http, freefunc);
1167}
1168
1a92a1e2 1169static
1170void
1171clientKeepaliveNextRequest(clientHttpRequest * http)
1172{
1173 ConnStateData *conn = http->conn;
1174 StoreEntry *entry;
1175 conn->defer.until = 0; /* Kick it to read a new request */
1176 httpRequestFree(http);
1177 if ((http = conn->chr) != NULL) {
1178 debug(33, 1) ("clientKeepaliveNextRequest: FD %d Sending next\n",
1179 conn->fd);
1180 entry = http->entry;
1181 if (0 == storeClientCopyPending(entry, http)) {
1182 if (entry->store_status == STORE_ABORTED)
1183 debug(33, 0) ("clientWriteComplete: entry->swap_status == STORE_ABORTED\n");
1184 storeClientCopy(entry,
1185 http->out.offset,
1186 http->out.offset,
1187 SM_PAGE_SIZE,
1188 memAllocate(MEM_4K_BUF),
1189 clientSendMoreData,
1190 http);
1191 }
1192 } else {
1193 debug(33, 5) ("clientWriteComplete: FD %d reading next request\n",
1194 conn->fd);
1195 fd_note(conn->fd, "Reading next request");
1196 /*
1197 * Set the timeout BEFORE calling clientReadRequest().
1198 */
1199 commSetTimeout(conn->fd, 15, requestTimeout, conn);
1200 clientReadRequest(conn->fd, conn); /* Read next request */
1201 /*
1202 * Note, the FD may be closed at this point.
1203 */
1204 }
1205}
1206
fc5d6f7f 1207static void
79a15e0a 1208clientWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
7a2f978b 1209{
1210 clientHttpRequest *http = data;
7a2f978b 1211 StoreEntry *entry = http->entry;
b34ed725 1212 int done;
7a2f978b 1213 http->out.size += size;
93f9abd0 1214 debug(33, 5) ("clientWriteComplete: FD %d, sz %d, err %d, off %d, len %d\n",
5f6ac48b 1215 fd, size, errflag, (int) http->out.offset, objectLen(entry));
d8b83480 1216 if (size > 0) {
ea285003 1217 kb_incr(&Counter.client_http.kbytes_out, size);
1218 if (isTcpHit(http->log_type))
d8b83480 1219 kb_incr(&Counter.client_http.hit_kbytes_out, size);
1220 }
7a2f978b 1221 if (errflag) {
36547bcf 1222#if DONT_DO_THIS
1223 /*
1224 * Not sure why this CheckQuickAbort() would be needed here.
1225 * We also call CheckQuickAbort() in httpRequestFree(), which
1226 * gets called as a comm_close handler. We need to be careful
1227 * that CheckQuickAbort() gets called only ONCE, and AFTER
1228 * storeUnregister() has been called. [DW/1.2.beta18]
1229 */
7a2f978b 1230 CheckQuickAbort(http);
36547bcf 1231#endif
7a2f978b 1232 comm_close(fd);
fdfc0d63 1233 } else if (NULL == entry) {
1234 comm_close(fd); /* yuk */
7a2f978b 1235 } else if (entry->store_status == STORE_ABORTED) {
7a2f978b 1236 comm_close(fd);
a200bbd2 1237 } else if ((done = clientCheckTransferDone(http)) != 0 || size == 0) {
93f9abd0 1238 debug(33, 5) ("clientWriteComplete: FD %d transfer is DONE\n", fd);
7a2f978b 1239 /* We're finished case */
038eb4ed 1240 if (http->entry->mem_obj->reply->content_length < 0 || !done ||
b34ed725 1241 EBIT_TEST(entry->flag, ENTRY_BAD_LENGTH)) {
1242 /*
1243 * Client connection closed due to unknown or invalid
1244 * content length. Persistent connection is not possible.
1245 * This catches most cases, but probably not all.
1246 */
7a2f978b 1247 comm_close(fd);
79a15e0a 1248 } else if (EBIT_TEST(http->request->flags, REQ_PROXY_KEEPALIVE)) {
93f9abd0 1249 debug(33, 5) ("clientWriteComplete: FD %d Keeping Alive\n", fd);
1a92a1e2 1250 clientKeepaliveNextRequest(http);
7a2f978b 1251 } else {
1252 comm_close(fd);
1253 }
1254 } else {
1255 /* More data will be coming from primary server; register with
1256 * storage manager. */
0e473d70 1257 if (entry->store_status == STORE_ABORTED)
6cf028ab 1258 debug(33, 0) ("clientWriteComplete 2: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1259 storeClientCopy(entry,
1260 http->out.offset,
1261 http->out.offset,
1262 SM_PAGE_SIZE,
7021844c 1263 memAllocate(MEM_4K_BUF),
7a2f978b 1264 clientSendMoreData,
1265 http);
1266 }
1267}
1268
2920225f 1269/* called when clientGetHeadersFor*IMS completes */
1270static void
ea285003 1271clientFinishIMS(clientHttpRequest * http)
2920225f 1272{
1273 StoreEntry *entry = http->entry;
1274 MemBuf mb;
1275
1276 http->log_type = LOG_TCP_IMS_HIT;
1277 entry->refcount++;
1278 /* All headers are available, double check if object is modified or not */
1279 if (modifiedSince(entry, http->request)) {
1280 debug(33, 4) ("clientFinishIMS: Modified '%s'\n",
1281 storeUrl(entry));
1282 if (entry->store_status == STORE_ABORTED)
1283 debug(33, 0) ("clientFinishIMS: entry->swap_status == STORE_ABORTED\n");
1284 storeClientCopy(entry,
1285 http->out.offset,
1286 http->out.offset,
1287 SM_PAGE_SIZE,
1288 memAllocate(MEM_4K_BUF),
1289 clientSendMoreData,
1290 http);
1291 return;
1292 }
1293 debug(33, 4) ("clientFinishIMS: Not modified '%s'\n",
1294 storeUrl(entry));
1295 /*
1296 * Create the Not-Modified reply from the existing entry,
1297 * Then make a new entry to hold the reply to be written
1298 * to the client.
1299 */
1300 mb = httpPacked304Reply(entry->mem_obj->reply);
1301 storeUnregister(entry, http);
1302 storeUnlockObject(entry);
1303 http->entry = clientCreateStoreEntry(http, http->request->method, 0);
1304 httpReplyParse(http->entry->mem_obj->reply, mb.buf);
1305 storeAppend(http->entry, mb.buf, mb.size);
1306 memBufClean(&mb);
1307 storeComplete(http->entry);
1308}
1309
7a2f978b 1310static void
1b02b5be 1311clientGetHeadersForIMS(void *data, char *buf, ssize_t size)
7a2f978b 1312{
1313 clientHttpRequest *http = data;
7a2f978b 1314 StoreEntry *entry = http->entry;
dd83ba69 1315 MemObject *mem;
93f9abd0 1316 debug(33, 3) ("clientGetHeadersForIMS: %s, %d bytes\n",
1317 http->uri, (int) size);
7a2f978b 1318 assert(size <= SM_PAGE_SIZE);
3f6c0fb2 1319 memFree(MEM_4K_BUF, buf);
1b02b5be 1320 buf = NULL;
6cf028ab 1321 if (size < 0 || entry->store_status == STORE_ABORTED) {
8d4f9bc8 1322 /*
1323 * There are different reasons why we might have size < 0. One
1324 * being that we failed to open a swapfile. Another being that
1325 * the request was cancelled from the client-side. If the client
1326 * cancelled the request, then http->entry will be NULL.
1327 */
1328 if (entry != NULL) {
1329 debug(33, 1) ("clientGetHeadersForIMS: storeClientCopy failed for '%s'\n",
1330 storeKeyText(entry->key));
1331 clientProcessMiss(http);
1332 }
7a2f978b 1333 return;
1334 }
dd83ba69 1335 mem = entry->mem_obj;
cb69b4c7 1336 if (mem->reply->sline.status == 0) {
7a2f978b 1337 if (entry->mem_status == IN_MEMORY) {
fb63215a 1338 clientProcessMiss(http);
7a2f978b 1339 return;
1340 }
93f9abd0 1341 if (size == SM_PAGE_SIZE && http->out.offset == 0) {
1342 /*
1343 * We can't get any more headers than this, so bail
1344 */
1345 debug(33, 1) ("clientGetHeadersForIMS: failed, forcing cache miss\n");
1346 clientProcessMiss(http);
1347 return;
1348 }
1349 debug(33, 3) ("clientGetHeadersForIMS: waiting for HTTP reply headers\n");
7a2f978b 1350 /* All headers are not yet available, wait for more data */
0e473d70 1351 if (entry->store_status == STORE_ABORTED)
6cf028ab 1352 debug(33, 0) ("clientGetHeadersForIMS: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1353 storeClientCopy(entry,
1354 http->out.offset + size,
1355 http->out.offset,
1356 SM_PAGE_SIZE,
7021844c 1357 memAllocate(MEM_4K_BUF),
1b02b5be 1358 clientGetHeadersForIMS,
7a2f978b 1359 http);
1360 return;
1361 }
1362 /* All headers are available, check if object is modified or not */
1363 /* ---------------------------------------------------------------
cb69b4c7 1364 * Removed check for reply->sline.status != 200 because of a potential
7a2f978b 1365 * problem with ICP. We will return a HIT for any public, cached
1366 * object. This includes other responses like 301, 410, as coded in
1367 * http.c. It is Bad(tm) to return UDP_HIT and then, if the reply
50ddd7a4 1368 * code is not 200, hand off to clientProcessMiss(), which may disallow
7a2f978b 1369 * the request based on 'miss_access' rules. Alternatively, we might
1370 * consider requiring returning UDP_HIT only for 200's. This
1371 * problably means an entry->flag bit, which would be lost during
1372 * restart because the flags aren't preserved across restarts.
1373 * --DW 3/11/96.
1374 * ---------------------------------------------------------------- */
1375#ifdef CHECK_REPLY_CODE_NOTEQUAL_200
1376 /* Only objects with statuscode==200 can be "Not modified" */
cb69b4c7 1377 if (mem->reply->sline.status != 200) {
93f9abd0 1378 debug(33, 4) ("clientGetHeadersForIMS: Reply code %d!=200\n",
cb69b4c7 1379 mem->reply->sline.status);
fb63215a 1380 clientProcessMiss(http);
7a2f978b 1381 return;
1382 }
7a2f978b 1383#endif
2920225f 1384 clientFinishIMS(http);
1385}
1386
1387/*
99edd1c3 1388 * client sent an IMS request for ENTRY_SPECIAL;
1389 * mimic clientGetHeadersForIMS(), but call clientCacheHit()
1390 * if something goes wrong;
1391 * note: clientGetHeadersForIMS frees "buf" earlier than we do
2920225f 1392 */
1393static void
1394clientGetHeadersForSpecialIMS(void *data, char *buf, ssize_t size)
1395{
1396 clientHttpRequest *http = data;
1397 StoreEntry *entry = http->entry;
1398 debug(33, 3) ("clientGetHeadersForSpecialIMS: %s, %d bytes\n",
1399 http->uri, (int) size);
1400 assert(size <= SM_PAGE_SIZE);
1401 if (size < 0 || entry->store_status == STORE_ABORTED) {
1402 clientCacheHit(data, buf, size);
1403 return;
1404 }
1405 if (entry->mem_obj->reply->sline.status == 0) {
1406 if (entry->mem_status == IN_MEMORY) {
1407 clientCacheHit(data, buf, size);
1408 return;
1409 }
1410 if (size == SM_PAGE_SIZE && http->out.offset == 0) {
1411 clientCacheHit(data, buf, size);
1412 return;
1413 }
1414 debug(33, 3) ("clientGetHeadersForSpecialIMS: waiting for HTTP reply headers\n");
1415 /* All headers are not yet available, wait for more data */
0e473d70 1416 if (entry->store_status == STORE_ABORTED)
2920225f 1417 debug(33, 0) ("clientGetHeadersForSpecialIMS: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1418 storeClientCopy(entry,
2920225f 1419 http->out.offset + size,
7a2f978b 1420 http->out.offset,
1421 SM_PAGE_SIZE,
2920225f 1422 buf,
1423 clientGetHeadersForSpecialIMS,
7a2f978b 1424 http);
1425 return;
1426 }
2920225f 1427 memFree(MEM_4K_BUF, buf);
1428 clientFinishIMS(http);
7a2f978b 1429}
1430
038eb4ed 1431/*
99edd1c3 1432 * client issued a request with an only-if-cached cache-control directive;
038eb4ed 1433 * we did not find a cached object that can be returned without
99edd1c3 1434 * contacting other servers;
038eb4ed 1435 * respond with a 504 (Gateway Timeout) as suggested in [RFC 2068]
1436 */
1437static void
1438clientProcessOnlyIfCachedMiss(clientHttpRequest * http)
1439{
1440 char *url = http->uri;
1441 request_t *r = http->request;
1442 ErrorState *err = NULL;
1443 debug(33, 4) ("clientProcessOnlyIfCachedMiss: '%s %s'\n",
1444 RequestMethodStr[r->method], url);
038eb4ed 1445 http->al.http.code = HTTP_GATEWAY_TIMEOUT;
1446 err = errorCon(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT);
1447 err->request = requestLink(r);
1448 err->src_addr = http->conn->peer.sin_addr;
1449 http->entry = clientCreateStoreEntry(http, r->method, 0);
1450 errorAppendEntry(http->entry, err);
1451}
1452
50ddd7a4 1453static log_type
1454clientProcessRequest2(clientHttpRequest * http)
1455{
1456 const request_t *r = http->request;
23d92c64 1457 const cache_key *key = storeKeyPublic(http->uri, r->method);
50ddd7a4 1458 StoreEntry *e;
57b1ff70 1459 e = http->entry = storeGet(key);
6cfa8966 1460#if USE_CACHE_DIGESTS
57b1ff70 1461 http->lookup_type = e ? "HIT" : "MISS";
1ca847dd 1462#endif
57b1ff70 1463 if (!e) {
50ddd7a4 1464 /* this object isn't in the cache */
1465 return LOG_TCP_MISS;
1466 } else if (EBIT_TEST(e->flag, ENTRY_SPECIAL)) {
12784378 1467 /* ideally, special entries should be processed later,
1468 * so we can use std processing routines for IMS and such */
57b1ff70 1469 if (EBIT_TEST(r->flags, REQ_IMS) && e->lastmod <= r->ims)
12784378 1470 return LOG_TCP_IMS_HIT;
17b6e784 1471 else if (e->mem_status == IN_MEMORY)
50ddd7a4 1472 return LOG_TCP_MEM_HIT;
1473 else
1474 return LOG_TCP_HIT;
1475 } else if (!storeEntryValidToSend(e)) {
1476 storeRelease(e);
1477 http->entry = NULL;
1478 return LOG_TCP_MISS;
1479 } else if (EBIT_TEST(r->flags, REQ_NOCACHE)) {
1480 /* NOCACHE should always eject a negative cached object */
1481 if (EBIT_TEST(e->flag, ENTRY_NEGCACHED))
1482 storeRelease(e);
1483 /* NOCACHE+IMS should not eject a valid object */
1484 else if (EBIT_TEST(r->flags, REQ_IMS))
1485 (void) 0;
1486 /* Request-Range should not eject a valid object */
1487 else if (EBIT_TEST(r->flags, REQ_RANGE))
1488 (void) 0;
1489 else
1490 storeRelease(e);
1491 ipcacheReleaseInvalid(r->host);
1492 http->entry = NULL;
ee1679df 1493 return LOG_TCP_CLIENT_REFRESH_MISS;
50ddd7a4 1494 } else if (checkNegativeHit(e)) {
1495 return LOG_TCP_NEGATIVE_HIT;
1496 } else if (refreshCheck(e, r, 0)) {
1497 /* The object is in the cache, but it needs to be validated. Use
1498 * LOG_TCP_REFRESH_MISS for the time being, maybe change it to
1b02b5be 1499 * _HIT later in clientHandleIMSReply() */
50ddd7a4 1500 if (r->protocol == PROTO_HTTP)
1501 return LOG_TCP_REFRESH_MISS;
1502 else
1503 return LOG_TCP_MISS; /* XXX zoinks */
1504 } else if (EBIT_TEST(r->flags, REQ_IMS)) {
1505 /* User-initiated IMS request for something we think is valid */
1506 return LOG_TCP_IMS_MISS;
1507 } else if (e->mem_status == IN_MEMORY) {
1508 return LOG_TCP_MEM_HIT;
1509 } else {
1510 return LOG_TCP_HIT;
1511 }
1512}
1513
4c323c5f 1514static void
fb63215a 1515clientProcessRequest(clientHttpRequest * http)
7a2f978b 1516{
23d92c64 1517 char *url = http->uri;
7a2f978b 1518 StoreEntry *entry = NULL;
50ddd7a4 1519 request_t *r = http->request;
fb63215a 1520 int fd = http->conn->fd;
1bea1d83 1521 HttpReply *rep;
93f9abd0 1522 debug(33, 4) ("clientProcessRequest: %s '%s'\n",
50ddd7a4 1523 RequestMethodStr[r->method],
7a2f978b 1524 url);
fb63215a 1525 if (r->method == METHOD_CONNECT) {
7a2f978b 1526 http->log_type = LOG_TCP_MISS;
ea0efb98 1527 sslStart(fd, url, r, &http->out.size);
1528 return;
fb63215a 1529 } else if (r->method == METHOD_PURGE) {
ea0efb98 1530 clientPurgeRequest(http);
1531 return;
fb63215a 1532 } else if (r->method == METHOD_TRACE) {
50ddd7a4 1533 if (r->max_forwards == 0) {
b17b22eb 1534 http->entry = clientCreateStoreEntry(http, r->method, 0);
1535 storeReleaseRequest(http->entry);
1536 storeBuffer(http->entry);
1bea1d83 1537 rep = httpReplyCreate();
31be8b80 1538 httpReplySetHeaders(rep, 1.0, HTTP_OK, NULL, "text/plain",
99edd1c3 1539 r->prefix_sz, 0, squid_curtime);
1bea1d83 1540 httpReplySwapOut(rep, http->entry);
1541 httpReplyDestroy(rep);
99edd1c3 1542 storeAppend(http->entry, r->prefix, r->prefix_sz);
b17b22eb 1543 storeComplete(http->entry);
7a2f978b 1544 return;
1545 }
1546 /* yes, continue */
6addbd81 1547 http->log_type = LOG_TCP_MISS;
efb9218c 1548 } else if (pumpMethod(r->method)) {
fb63215a 1549 http->log_type = LOG_TCP_MISS;
31be8b80 1550 /* XXX oof, POST can be cached! */
54220df8 1551 pumpInit(fd, r, http->uri);
31be8b80 1552 } else {
efb9218c 1553 http->log_type = clientProcessRequest2(http);
7a2f978b 1554 }
93f9abd0 1555 debug(33, 4) ("clientProcessRequest: %s for '%s'\n",
7a2f978b 1556 log_tags[http->log_type],
23d92c64 1557 http->uri);
50ddd7a4 1558 if ((entry = http->entry) != NULL) {
7a2f978b 1559 storeLockObject(entry);
23d92c64 1560 storeCreateMemObject(entry, http->uri, http->log_uri);
7a2f978b 1561 storeClientListAdd(entry, http);
1562 }
7a2f978b 1563 http->out.offset = 0;
1564 switch (http->log_type) {
1565 case LOG_TCP_HIT:
1566 case LOG_TCP_NEGATIVE_HIT:
1567 case LOG_TCP_MEM_HIT:
1568 entry->refcount++; /* HIT CASE */
0e473d70 1569 if (entry->store_status == STORE_ABORTED)
6cf028ab 1570 debug(33, 0) ("clientProcessRequest: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1571 storeClientCopy(entry,
1572 http->out.offset,
1573 http->out.offset,
1574 SM_PAGE_SIZE,
7021844c 1575 memAllocate(MEM_4K_BUF),
7a2f978b 1576 clientCacheHit,
1577 http);
038eb4ed 1578 return;
2920225f 1579 case LOG_TCP_IMS_HIT:
7a2f978b 1580 case LOG_TCP_IMS_MISS:
0e473d70 1581 if (entry->store_status == STORE_ABORTED)
6cf028ab 1582 debug(33, 0) ("clientProcessRequest 2: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1583 storeClientCopy(entry,
1584 http->out.offset,
1585 http->out.offset,
1586 SM_PAGE_SIZE,
7021844c 1587 memAllocate(MEM_4K_BUF),
ea285003 1588 (http->log_type == LOG_TCP_IMS_MISS) ?
1589 clientGetHeadersForIMS : clientGetHeadersForSpecialIMS,
7a2f978b 1590 http);
1591 break;
1592 case LOG_TCP_REFRESH_MISS:
fb63215a 1593 clientProcessExpired(http);
7a2f978b 1594 break;
1595 default:
fb63215a 1596 clientProcessMiss(http);
7a2f978b 1597 break;
1598 }
1599}
1600
1601/*
1602 * Prepare to fetch the object as it's a cache miss of some kind.
1603 */
1604static void
fb63215a 1605clientProcessMiss(clientHttpRequest * http)
7a2f978b 1606{
23d92c64 1607 char *url = http->uri;
f44af445 1608 request_t *r = http->request;
7a2f978b 1609 aclCheck_t ch;
1610 int answer;
1611 ErrorState *err = NULL;
93f9abd0 1612 debug(33, 4) ("clientProcessMiss: '%s %s'\n",
f44af445 1613 RequestMethodStr[r->method], url);
99edd1c3 1614 debug(33, 10) ("clientProcessMiss: prefix:\n%s\n", r->prefix);
79e0dc20 1615 /*
1616 * We might have a left-over StoreEntry from a failed cache hit
1617 * or IMS request.
1618 */
1619 if (http->entry) {
12784378 1620 if (EBIT_TEST(http->entry->flag, ENTRY_SPECIAL))
1621 debug(33, 0) ("clientProcessMiss: miss on a special object (%s).\n", url);
79e0dc20 1622 storeUnregister(http->entry, http);
1623 storeUnlockObject(http->entry);
1624 http->entry = NULL;
1625 }
7c1d4010 1626 if (clientOnlyIfCached(http)) {
1627 clientProcessOnlyIfCachedMiss(http);
1628 return;
1629 }
9330543e 1630 /*
1631 * Deny loops when running in accelerator/transproxy mode.
1632 */
1633 if (http->flags.accel && EBIT_TEST(r->flags, REQ_LOOPDETECT)) {
1634 http->al.http.code = HTTP_FORBIDDEN;
1635 err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
1636 err->request = requestLink(r);
1637 err->src_addr = http->conn->peer.sin_addr;
1638 http->entry = clientCreateStoreEntry(http, r->method, 0);
1639 errorAppendEntry(http->entry, err);
1640 return;
1641 }
79e0dc20 1642 /*
1643 * Check if this host is allowed to fetch MISSES from us (miss_access)
1644 */
7a2f978b 1645 memset(&ch, '\0', sizeof(aclCheck_t));
1646 ch.src_addr = http->conn->peer.sin_addr;
f44af445 1647 ch.request = r;
7a2f978b 1648 answer = aclCheckFast(Config.accessList.miss, &ch);
1da5651f 1649 if (answer == 0) {
7a2f978b 1650 http->al.http.code = HTTP_FORBIDDEN;
bdb3c273 1651 err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN);
f44af445 1652 err->request = requestLink(r);
7a2f978b 1653 err->src_addr = http->conn->peer.sin_addr;
f44af445 1654 http->entry = clientCreateStoreEntry(http, r->method, 0);
79e0dc20 1655 errorAppendEntry(http->entry, err);
7a2f978b 1656 return;
1657 }
79e0dc20 1658 assert(http->out.offset == 0);
f44af445 1659 http->entry = clientCreateStoreEntry(http, r->method, r->flags);
79e0dc20 1660 http->entry->refcount++;
1da5651f 1661 if (http->flags.internal)
1662 r->protocol = PROTO_INTERNAL;
f44af445 1663 protoDispatch(http->conn->fd, http->entry, r);
7a2f978b 1664}
1665
99edd1c3 1666static clientHttpRequest *
1667parseHttpRequestAbort(ConnStateData * conn, const char *uri)
1668{
1669 clientHttpRequest *http = xcalloc(1, sizeof(clientHttpRequest));
1670 cbdataAdd(http, MEM_NONE);
1671 http->conn = conn;
1672 http->start = current_time;
1673 http->req_sz = conn->in.offset;
1674 http->uri = xstrdup(uri);
1675 http->log_uri = xstrdup(uri);
1676 return http;
1677}
1678
7a2f978b 1679/*
1680 * parseHttpRequest()
1681 *
1682 * Returns
1683 * NULL on error or incomplete request
1684 * a clientHttpRequest structure on success
1685 */
1686static clientHttpRequest *
1687parseHttpRequest(ConnStateData * conn, method_t * method_p, int *status,
ea285003 1688 char **prefix_p, size_t * req_line_sz_p)
7a2f978b 1689{
1690 char *inbuf = NULL;
1691 char *mstr = NULL;
1692 char *url = NULL;
1693 char *req_hdr = NULL;
7a2f978b 1694 float http_ver;
1695 char *token = NULL;
1696 char *t = NULL;
2334c194 1697 char *end;
7a2f978b 1698 int free_request = 0;
1699 size_t header_sz; /* size of headers, not including first line */
ea285003 1700 size_t prefix_sz; /* size of whole request (req-line + headers) */
7a2f978b 1701 size_t url_sz;
1702 method_t method;
1703 clientHttpRequest *http = NULL;
1704
1705 /* Make sure a complete line has been received */
99edd1c3 1706 if ((t = strchr(conn->in.buf, '\n')) == NULL) {
93f9abd0 1707 debug(33, 5) ("Incomplete request line, waiting for more data\n");
7a2f978b 1708 *status = 0;
1709 return NULL;
1710 }
99edd1c3 1711 *req_line_sz_p = t - conn->in.buf;
7a2f978b 1712 /* Use xmalloc/xmemcpy instead of xstrdup because inbuf might
1713 * contain NULL bytes; especially for POST data */
1714 inbuf = xmalloc(conn->in.offset + 1);
1715 xmemcpy(inbuf, conn->in.buf, conn->in.offset);
1716 *(inbuf + conn->in.offset) = '\0';
1717
99edd1c3 1718 /* pre-set these values to make aborting simpler */
1719 *prefix_p = inbuf;
1720 *method_p = METHOD_NONE;
1721 *status = -1;
1722
7a2f978b 1723 /* Look for request method */
1724 if ((mstr = strtok(inbuf, "\t ")) == NULL) {
93f9abd0 1725 debug(33, 1) ("parseHttpRequest: Can't get request method\n");
99edd1c3 1726 return parseHttpRequestAbort(conn, "error:invalid-request-method");
7a2f978b 1727 }
1728 method = urlParseMethod(mstr);
1729 if (method == METHOD_NONE) {
93f9abd0 1730 debug(33, 1) ("parseHttpRequest: Unsupported method '%s'\n", mstr);
99edd1c3 1731 return parseHttpRequestAbort(conn, "error:unsupported-request-method");
7a2f978b 1732 }
93f9abd0 1733 debug(33, 5) ("parseHttpRequest: Method is '%s'\n", mstr);
3775d53c 1734 *method_p = method;
7a2f978b 1735
1736 /* look for URL */
1737 if ((url = strtok(NULL, "\r\n\t ")) == NULL) {
93f9abd0 1738 debug(33, 1) ("parseHttpRequest: Missing URL\n");
99edd1c3 1739 return parseHttpRequestAbort(conn, "error:missing-url");
7a2f978b 1740 }
93f9abd0 1741 debug(33, 5) ("parseHttpRequest: Request is '%s'\n", url);
7a2f978b 1742
1743 token = strtok(NULL, null_string);
1744 for (t = token; t && *t && *t != '\n' && *t != '\r'; t++);
46c7dbf0 1745 if (t == NULL || *t == '\0' || t == token || strncmp(token, "HTTP/", 5)) {
93f9abd0 1746 debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
46052490 1747#if RELAXED_HTTP_PARSER
1748 http_ver = (float) 0.9; /* wild guess */
1749#else
99edd1c3 1750 return parseHttpRequestAbort(conn, "error:missing-http-ident");
7518fce5 1751#endif
99edd1c3 1752 } else {
7518fce5 1753 http_ver = (float) atof(token + 5);
99edd1c3 1754 }
7a2f978b 1755
1756 /* Check if headers are received */
754b9ce9 1757 req_hdr = t;
8d9810cd 1758 header_sz = headersEnd(req_hdr, conn->in.offset - (req_hdr - inbuf));
2334c194 1759 if (0 == header_sz) {
1a92a1e2 1760 debug(33, 3) ("parseHttpRequest: header_sz == 0\n");
7a2f978b 1761 xfree(inbuf);
1762 *status = 0;
1763 return NULL;
1764 }
754b9ce9 1765 /*
99edd1c3 1766 * Skip whitespace at the end of the first line, up to the
754b9ce9 1767 * first newline.
1768 */
1769 while (isspace(*req_hdr)) {
1770 header_sz--;
1771 if (*(req_hdr++) == '\n')
1772 break;
1773 }
1774 assert(header_sz > 0);
1775 debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
2334c194 1776 end = req_hdr + header_sz;
04990e2d 1777 debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
99edd1c3 1778
1779#if UNREACHABLE_CODE
1780 if (end <= req_hdr) {
1781 /* Invalid request */
1782 debug(33, 3) ("parseHttpRequest: No request headers?\n");
1783 return parseHttpRequestAbort(conn, "error:no-request-headers");
1784 }
1785#endif
1786 prefix_sz = end - inbuf;
1787 *req_line_sz_p = req_hdr - inbuf;
ea285003 1788 debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
99edd1c3 1789 (int) prefix_sz, (int) *req_line_sz_p);
1790 assert(prefix_sz <= conn->in.offset);
7a2f978b 1791
1792 /* Ok, all headers are received */
1793 http = xcalloc(1, sizeof(clientHttpRequest));
3f6c0fb2 1794 cbdataAdd(http, MEM_NONE);
7a2f978b 1795 http->http_ver = http_ver;
1796 http->conn = conn;
1797 http->start = current_time;
99edd1c3 1798 http->req_sz = prefix_sz;
1799 *prefix_p = xmalloc(prefix_sz + 1);
1800 xmemcpy(*prefix_p, conn->in.buf, prefix_sz);
1801 *(*prefix_p + prefix_sz) = '\0';
7a2f978b 1802
ea285003 1803 debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n", (*prefix_p) + *req_line_sz_p);
23d92c64 1804 /* Assign http->uri */
7a2f978b 1805 if ((t = strchr(url, '\n'))) /* remove NL */
1806 *t = '\0';
1807 if ((t = strchr(url, '\r'))) /* remove CR */
1808 *t = '\0';
1809 if ((t = strchr(url, '#'))) /* remove HTML anchors */
1810 *t = '\0';
1811
4162ee3b 1812 /* handle internal objects */
1da5651f 1813 if (internalCheck(url)) {
4162ee3b 1814 /* prepend our name & port */
1da5651f 1815 http->uri = xstrdup(internalLocalUri(NULL, url));
77ed547a 1816 http->flags.internal = 1;
4162ee3b 1817 }
7a2f978b 1818 /* see if we running in Config2.Accel.on, if so got to convert it to URL */
4162ee3b 1819 else if (Config2.Accel.on && *url == '/') {
7a2f978b 1820 /* prepend the accel prefix */
dbfed404 1821 if (opt_accel_uses_host && (t = mime_get_header(req_hdr, "Host"))) {
7a2f978b 1822 /* If a Host: header was specified, use it to build the URL
1823 * instead of the one in the Config file. */
1824 /*
1825 * XXX Use of the Host: header here opens a potential
1826 * security hole. There are no checks that the Host: value
1827 * corresponds to one of your servers. It might, for example,
1828 * refer to www.playboy.com. The 'dst' and/or 'dst_domain' ACL
1829 * types should be used to prevent httpd-accelerators
1830 * handling requests for non-local servers */
1831 strtok(t, " :/;@");
12fc602c 1832 url_sz = strlen(url) + 32 + Config.appendDomainLen +
1833 strlen(t);
23d92c64 1834 http->uri = xcalloc(url_sz, 1);
1835 snprintf(http->uri, url_sz, "http://%s:%d%s",
7a2f978b 1836 t, (int) Config.Accel.port, url);
dbfed404 1837 } else if (vhost_mode) {
1838 /* Put the local socket IP address as the hostname */
1839 url_sz = strlen(url) + 32 + Config.appendDomainLen;
1840 http->uri = xcalloc(url_sz, 1);
1841 snprintf(http->uri, url_sz, "http://%s:%d%s",
1842 inet_ntoa(http->conn->me.sin_addr),
1843 (int) Config.Accel.port,
1844 url);
93f9abd0 1845 debug(33, 5) ("VHOST REWRITE: '%s'\n", http->uri);
7a2f978b 1846 } else {
1847 url_sz = strlen(Config2.Accel.prefix) + strlen(url) +
1848 Config.appendDomainLen + 1;
23d92c64 1849 http->uri = xcalloc(url_sz, 1);
1850 snprintf(http->uri, url_sz, "%s%s", Config2.Accel.prefix, url);
7a2f978b 1851 }
77ed547a 1852 http->flags.accel = 1;
7a2f978b 1853 } else {
1854 /* URL may be rewritten later, so make extra room */
1855 url_sz = strlen(url) + Config.appendDomainLen + 5;
23d92c64 1856 http->uri = xcalloc(url_sz, 1);
1857 strcpy(http->uri, url);
77ed547a 1858 http->flags.accel = 0;
7a2f978b 1859 }
23d92c64 1860 http->log_uri = xstrdup(http->uri);
93f9abd0 1861 debug(33, 5) ("parseHttpRequest: Complete request received\n");
7a2f978b 1862 if (free_request)
1863 safe_free(url);
1864 xfree(inbuf);
7a2f978b 1865 *status = 1;
1866 return http;
1867}
1868
1869static int
79d39a72 1870clientReadDefer(int fdnotused, void *data)
7a2f978b 1871{
1872 ConnStateData *conn = data;
1873 return conn->defer.until > squid_curtime;
1874}
1875
1876static void
1877clientReadRequest(int fd, void *data)
1878{
1879 ConnStateData *conn = data;
1880 int parser_return_code = 0;
1881 int k;
1882 request_t *request = NULL;
7a2f978b 1883 int size;
7a2f978b 1884 method_t method;
1885 clientHttpRequest *http = NULL;
1886 clientHttpRequest **H = NULL;
99edd1c3 1887 char *prefix;
7a2f978b 1888 ErrorState *err = NULL;
1889 fde *F = &fd_table[fd];
44db45e8 1890 int len = conn->in.size - conn->in.offset - 1;
93f9abd0 1891 debug(33, 4) ("clientReadRequest: FD %d: reading request...\n", fd);
7a2f978b 1892 size = read(fd, conn->in.buf + conn->in.offset, len);
d8b83480 1893 if (size > 0) {
ea285003 1894 fd_bytes(fd, size, FD_READ);
1895 kb_incr(&Counter.client_http.kbytes_in, size);
d8b83480 1896 }
44db45e8 1897 /*
1898 * Don't reset the timeout value here. The timeout value will be
1899 * set to Config.Timeout.request by httpAccept() and
1900 * clientWriteComplete(), and should apply to the request as a
1901 * whole, not individual read() calls. Plus, it breaks our
1902 * lame half-close detection
1903 */
1904 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
7a2f978b 1905 if (size == 0) {
1906 if (conn->chr == NULL) {
1907 /* no current or pending requests */
1908 comm_close(fd);
1909 return;
ea285003 1910 } else if (!Config.onoff.half_closed_clients) {
1911 /* admin doesn't want to support half-closed client sockets */
1912 comm_close(fd);
1913 return;
7a2f978b 1914 }
1915 /* It might be half-closed, we can't tell */
93f9abd0 1916 debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd);
58a6c186 1917 F->flags.socket_eof = 1;
7a2f978b 1918 conn->defer.until = squid_curtime + 1;
1919 conn->defer.n++;
44db45e8 1920 fd_note(fd, "half-closed");
7a2f978b 1921 return;
1922 } else if (size < 0) {
6d3caf23 1923 if (!ignoreErrno(errno)) {
7a2f978b 1924 debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd, xstrerror());
1925 comm_close(fd);
ebf4efff 1926 return;
47130615 1927 } else if (conn->in.offset == 0) {
7c1d4010 1928 debug(50, 2) ("clientReadRequest: FD %d: no data to process (%s)\n", fd, xstrerror());
ebf4efff 1929 return;
7a2f978b 1930 }
ebf4efff 1931 /* Continue to process previously read data */
47130615 1932 size = 0;
7a2f978b 1933 }
1934 conn->in.offset += size;
5ede6c8f 1935 /* Skip leading (and trailing) whitespace */
1936 while (conn->in.offset > 0 && isspace(conn->in.buf[0])) {
1937 xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.offset - 1);
1938 conn->in.offset--;
1939 }
7a2f978b 1940 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
7a2f978b 1941 while (conn->in.offset > 0) {
ebf4efff 1942 int nrequests;
cff0c749 1943 size_t req_line_sz;
ebf4efff 1944 /* Limit the number of concurrent requests to 2 */
1945 for (H = &conn->chr, nrequests = 0; *H; H = &(*H)->next, nrequests++);
1946 if (nrequests >= 2) {
93f9abd0 1947 debug(33, 2) ("clientReadRequest: FD %d max concurrent requests reached\n", fd);
1948 debug(33, 5) ("clientReadRequest: FD %d defering new request until one is done\n", fd);
47130615 1949 conn->defer.until = squid_curtime + 100; /* Reset when a request is complete */
ebf4efff 1950 break;
1951 }
1952 /* Process request */
7a2f978b 1953 http = parseHttpRequest(conn,
1954 &method,
1955 &parser_return_code,
99edd1c3 1956 &prefix,
1957 &req_line_sz);
7a2f978b 1958 if (http) {
1959 assert(http->req_sz > 0);
1960 conn->in.offset -= http->req_sz;
1961 assert(conn->in.offset >= 0);
6fa92aa2 1962 /*
1963 * If we read past the end of this request, move the remaining
1964 * data to the beginning
1965 */
1966 if (conn->in.offset > 0)
dbfed404 1967 xmemmove(conn->in.buf, conn->in.buf + http->req_sz, conn->in.offset);
ebf4efff 1968 /* add to the client request queue */
7a2f978b 1969 for (H = &conn->chr; *H; H = &(*H)->next);
1970 *H = http;
1971 conn->nrequests++;
1972 commSetTimeout(fd, Config.Timeout.lifetime, NULL, NULL);
3775d53c 1973 if (parser_return_code < 0) {
93f9abd0 1974 debug(33, 1) ("clientReadRequest: FD %d Invalid Request\n", fd);
3775d53c 1975 err = errorCon(ERR_INVALID_REQ, HTTP_BAD_REQUEST);
1976 err->request_hdrs = xstrdup(conn->in.buf);
1977 http->entry = clientCreateStoreEntry(http, method, 0);
1978 errorAppendEntry(http->entry, err);
1979 break;
1980 }
23d92c64 1981 if ((request = urlParse(method, http->uri)) == NULL) {
93f9abd0 1982 debug(33, 5) ("Invalid URL: %s\n", http->uri);
7a2f978b 1983 err = errorCon(ERR_INVALID_URL, HTTP_BAD_REQUEST);
1984 err->src_addr = conn->peer.sin_addr;
23d92c64 1985 err->url = xstrdup(http->uri);
7a2f978b 1986 http->al.http.code = err->http_status;
f44af445 1987 http->entry = clientCreateStoreEntry(http, method, 0);
79e0dc20 1988 errorAppendEntry(http->entry, err);
99edd1c3 1989 safe_free(prefix);
7a2f978b 1990 break;
99edd1c3 1991 } else {
1992 /* compile headers */
1993 /* we should skip request line! */
ea285003 1994 if (!httpRequestParseHeader(request, prefix + req_line_sz))
99edd1c3 1995 debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
1996 http->uri, prefix);
1997 /* continue anyway? */
7a2f978b 1998 }
ba26bca4 1999 if (!http->flags.internal) {
2000 if (internalCheck(strBuf(request->urlpath))) {
2001 if (0 == strcasecmp(request->host, getMyHostname())) {
4b4cd312 2002 if (request->port == Config.Port.http->i)
2003 http->flags.internal = 1;
ba26bca4 2004 } else if (internalStaticCheck(strBuf(request->urlpath))) {
5999b776 2005 xstrncpy(request->host, getMyHostname(), SQUIDHOSTNAMELEN);
2006 request->port = Config.Port.http->i;
2007 http->flags.internal = 1;
ba26bca4 2008 }
2009 }
2010 }
23d92c64 2011 safe_free(http->log_uri);
2012 http->log_uri = xstrdup(urlCanonicalClean(request));
7a2f978b 2013 request->client_addr = conn->peer.sin_addr;
2014 request->http_ver = http->http_ver;
99edd1c3 2015 request->prefix = prefix;
2016 request->prefix_sz = http->req_sz;
7a2f978b 2017 if (!urlCheckRequest(request)) {
2018 err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED);
2019 err->src_addr = conn->peer.sin_addr;
7a2f978b 2020 err->request = requestLink(request);
2021 http->al.http.code = err->http_status;
f44af445 2022 http->entry = clientCreateStoreEntry(http, request->method, 0);
79e0dc20 2023 errorAppendEntry(http->entry, err);
3775d53c 2024 break;
7a2f978b 2025 }
31be8b80 2026 if (0 == clientCheckContentLength(request)) {
2027 err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED);
2028 err->src_addr = conn->peer.sin_addr;
2029 err->request = requestLink(request);
2030 http->al.http.code = err->http_status;
2031 http->entry = clientCreateStoreEntry(http, request->method, 0);
2032 errorAppendEntry(http->entry, err);
2033 break;
2034 }
7a2f978b 2035 http->request = requestLink(request);
cf26e54c 2036 clientAccessCheck(http);
3775d53c 2037 /*
2038 * break here for NON-GET because most likely there is a
7a2f978b 2039 * reqeust body following and we don't want to parse it
3775d53c 2040 * as though it was new request
2041 */
7a2f978b 2042 if (request->method != METHOD_GET) {
2043 if (conn->in.offset) {
2044 request->body_sz = conn->in.offset;
2045 request->body = xmalloc(request->body_sz);
2046 xmemcpy(request->body, conn->in.buf, request->body_sz);
2047 conn->in.offset = 0;
2048 }
2049 break;
2050 }
7a2f978b 2051 continue; /* while offset > 0 */
2052 } else if (parser_return_code == 0) {
2053 /*
2054 * Partial request received; reschedule until parseHttpRequest()
2055 * is happy with the input
2056 */
2057 k = conn->in.size - 1 - conn->in.offset;
2058 if (k == 0) {
2059 if (conn->in.offset >= Config.maxRequestSize) {
2060 /* The request is too large to handle */
93f9abd0 2061 debug(33, 0) ("Request won't fit in buffer.\n");
2062 debug(33, 0) ("Config 'request_size'= %d bytes.\n",
7a2f978b 2063 Config.maxRequestSize);
93f9abd0 2064 debug(33, 0) ("This request = %d bytes.\n",
5f6ac48b 2065 (int) conn->in.offset);
7a2f978b 2066 err = errorCon(ERR_INVALID_REQ, HTTP_REQUEST_ENTITY_TOO_LARGE);
f44af445 2067 http->entry = clientCreateStoreEntry(http, request->method, 0);
79e0dc20 2068 errorAppendEntry(http->entry, err);
7a2f978b 2069 return;
2070 }
2071 /* Grow the request memory area to accomodate for a large request */
2072 conn->in.size += REQUEST_BUF_SIZE;
2073 conn->in.buf = xrealloc(conn->in.buf, conn->in.size);
59c4d35b 2074 /* XXX account conn->in.buf */
93f9abd0 2075 debug(33, 2) ("Handling a large request, offset=%d inbufsize=%d\n",
5f6ac48b 2076 (int) conn->in.offset, conn->in.size);
7a2f978b 2077 k = conn->in.size - 1 - conn->in.offset;
2078 }
7a2f978b 2079 break;
7a2f978b 2080 }
2081 }
2082}
2083
2084/* general lifetime handler for HTTP requests */
2085static void
2086requestTimeout(int fd, void *data)
2087{
2088 ConnStateData *conn = data;
2089 ErrorState *err;
93f9abd0 2090 debug(33, 2) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
7a2f978b 2091 if (fd_table[fd].rwstate) {
79e0dc20 2092 /*
2093 * Some data has been sent to the client, just close the FD
2094 */
7a2f978b 2095 comm_close(fd);
2096 } else if (conn->nrequests) {
79e0dc20 2097 /*
2098 * assume its a persistent connection; just close it
2099 */
7a2f978b 2100 comm_close(fd);
2101 } else {
79e0dc20 2102 /*
2103 * Generate an error
2104 */
7a2f978b 2105 err = errorCon(ERR_LIFETIME_EXP, HTTP_REQUEST_TIMEOUT);
7a2f978b 2106 err->url = xstrdup("N/A");
79e0dc20 2107 /*
2108 * Normally we shouldn't call errorSend() in client_side.c, but
2109 * it should be okay in this case. Presumably if we get here
2110 * this is the first request for the connection, and no data
2111 * has been written yet
2112 */
2113 assert(conn->chr == NULL);
7a2f978b 2114 errorSend(fd, err);
79e0dc20 2115 /*
2116 * if we don't close() here, we still need a timeout handler!
2117 */
7a2f978b 2118 commSetTimeout(fd, 30, requestTimeout, conn);
2119 }
2120}
2121
2122int
79d39a72 2123httpAcceptDefer(int fdnotused, void *notused)
7a2f978b 2124{
3d6629c6 2125 static time_t last_warn = 0;
2126 if (fdNFree() >= RESERVED_FD)
2127 return 0;
2128 if (last_warn + 15 < squid_curtime) {
2129 debug(33, 0) ("WARNING! Your cache is running out of filedescriptors\n");
2130 last_warn = squid_curtime;
2131 }
2132 return 1;
7a2f978b 2133}
2134
bf8e5903 2135/* Handle a new connection on HTTP socket. */
7a2f978b 2136void
2137httpAccept(int sock, void *notused)
2138{
2139 int fd = -1;
2140 ConnStateData *connState = NULL;
2141 struct sockaddr_in peer;
2142 struct sockaddr_in me;
e2525655 2143 int max = 10;
2144 while (max-- && !httpAcceptDefer(sock, notused)) {
2145 memset(&peer, '\0', sizeof(struct sockaddr_in));
2146 memset(&me, '\0', sizeof(struct sockaddr_in));
2147 commSetSelect(sock, COMM_SELECT_READ, httpAccept, NULL, 0);
2148 if ((fd = comm_accept(sock, &peer, &me)) < 0) {
2149 if (!ignoreErrno(errno))
2150 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
2151 sock, xstrerror());
2152 break;
2153 }
2154 debug(33, 4) ("httpAccept: FD %d: accepted\n", fd);
2155 connState = xcalloc(1, sizeof(ConnStateData));
2156 connState->peer = peer;
2157 connState->log_addr = peer.sin_addr;
2158 connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
2159 connState->me = me;
2160 connState->fd = fd;
2161 connState->ident.fd = -1;
2162 connState->in.size = REQUEST_BUF_SIZE;
2163 connState->in.buf = xcalloc(connState->in.size, 1);
2164 cbdataAdd(connState, MEM_NONE);
2165 /* XXX account connState->in.buf */
2166 comm_add_close_handler(fd, connStateFree, connState);
2167 if (Config.onoff.log_fqdn)
2168 fqdncache_gethostbyaddr(peer.sin_addr, FQDN_LOOKUP_IF_MISS);
2169 commSetTimeout(fd, Config.Timeout.request, requestTimeout, connState);
2170 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0);
2171 commSetDefer(fd, clientReadDefer, connState);
7a2f978b 2172 }
7a2f978b 2173}
2174
7a2f978b 2175/* return 1 if the request should be aborted */
2176static int
2177CheckQuickAbort2(const clientHttpRequest * http)
2178{
4efca7da 2179 int curlen;
2180 int minlen;
2181 int expectlen;
7a2f978b 2182
79a15e0a 2183 if (!EBIT_TEST(http->request->flags, REQ_CACHABLE))
7a2f978b 2184 return 1;
79a15e0a 2185 if (EBIT_TEST(http->entry->flag, KEY_PRIVATE))
7a2f978b 2186 return 1;
2187 if (http->entry->mem_obj == NULL)
2188 return 1;
038eb4ed 2189 expectlen = http->entry->mem_obj->reply->content_length;
4efca7da 2190 curlen = (int) http->entry->mem_obj->inmem_hi;
2191 minlen = (int) Config.quickAbort.min;
7a2f978b 2192 if (minlen < 0)
2193 /* disabled */
2194 return 0;
2195 if (curlen > expectlen)
2196 /* bad content length */
2197 return 1;
2198 if ((expectlen - curlen) < minlen)
2199 /* only little more left */
2200 return 0;
2201 if ((expectlen - curlen) > Config.quickAbort.max)
2202 /* too much left to go */
2203 return 1;
37d0359b 2204 if (expectlen < 100)
4efca7da 2205 /* avoid FPE */
2206 return 0;
37d0359b 2207 if ((curlen / (expectlen / 100)) > Config.quickAbort.pct)
7a2f978b 2208 /* past point of no return */
2209 return 0;
2210 return 1;
2211}
2212
2213
2214static void
2215CheckQuickAbort(clientHttpRequest * http)
2216{
2217 StoreEntry *entry = http->entry;
2218 /* Note, set entry here because http->entry might get changed (for IMS
2219 * requests) during the storeAbort() call */
2220 if (entry == NULL)
2221 return;
36547bcf 2222 if (storePendingNClients(entry) > 0)
7a2f978b 2223 return;
2224 if (entry->store_status != STORE_PENDING)
2225 return;
2226 if (CheckQuickAbort2(http) == 0)
2227 return;
93f9abd0 2228 debug(33, 3) ("CheckQuickAbort: ABORTING %s\n", storeUrl(entry));
7a2f978b 2229 storeAbort(entry, 1);
2230}
2231
978e455f 2232#define SENDING_BODY 0
2233#define SENDING_HDRSONLY 1
7a2f978b 2234static int
1b02b5be 2235clientCheckTransferDone(clientHttpRequest * http)
7a2f978b 2236{
978e455f 2237 int sending = SENDING_BODY;
7a2f978b 2238 StoreEntry *entry = http->entry;
978e455f 2239 MemObject *mem;
2240 http_reply *reply;
2241 int sendlen;
7a2f978b 2242 if (entry == NULL)
2243 return 0;
978e455f 2244 /*
2245 * Handle STORE_OK and STORE_ABORTED objects.
07304bf9 2246 * objectLen(entry) will be set proprely.
978e455f 2247 */
2248 if (entry->store_status != STORE_PENDING) {
07304bf9 2249 if (http->out.offset >= objectLen(entry))
7a2f978b 2250 return 1;
513f05a6 2251 else
2252 return 0;
2253 }
978e455f 2254 /*
2255 * Now, handle STORE_PENDING objects
2256 */
2257 mem = entry->mem_obj;
2258 assert(mem != NULL);
2259 assert(http->request != NULL);
2260 reply = mem->reply;
2261 if (reply->hdr_sz == 0)
b34ed725 2262 return 0; /* haven't found end of headers yet */
cb69b4c7 2263 else if (reply->sline.status == HTTP_OK)
b34ed725 2264 sending = SENDING_BODY;
cb69b4c7 2265 else if (reply->sline.status == HTTP_NO_CONTENT)
b34ed725 2266 sending = SENDING_HDRSONLY;
cb69b4c7 2267 else if (reply->sline.status == HTTP_NOT_MODIFIED)
b34ed725 2268 sending = SENDING_HDRSONLY;
cb69b4c7 2269 else if (reply->sline.status < HTTP_OK)
a3c60429 2270 sending = SENDING_HDRSONLY;
978e455f 2271 else if (http->request->method == METHOD_HEAD)
b34ed725 2272 sending = SENDING_HDRSONLY;
978e455f 2273 else
2274 sending = SENDING_BODY;
2275 /*
2276 * Figure out how much data we are supposed to send.
2277 * If we are sending a body and we don't have a content-length,
2278 * then we must wait for the object to become STORE_OK or
2279 * STORE_ABORTED.
2280 */
2281 if (sending == SENDING_HDRSONLY)
2282 sendlen = reply->hdr_sz;
038eb4ed 2283 else if (reply->content_length < 0)
978e455f 2284 return 0;
2285 else
038eb4ed 2286 sendlen = reply->content_length + reply->hdr_sz;
978e455f 2287 /*
2288 * Now that we have the expected length, did we send it all?
2289 */
2290 if (http->out.offset < sendlen)
2291 return 0;
2292 else
b34ed725 2293 return 1;
7a2f978b 2294}
2295
7a2f978b 2296/*
2297 * This function is designed to serve a fairly specific purpose.
2298 * Occasionally our vBNS-connected caches can talk to each other, but not
2299 * the rest of the world. Here we try to detect frequent failures which
2300 * make the cache unusable (e.g. DNS lookup and connect() failures). If
2301 * the failure:success ratio goes above 1.0 then we go into "hit only"
2302 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
2303 * will only fetch HITs from us if they are using the ICP protocol. We
2304 * stay in this mode for 5 minutes.
2305 *
2306 * Duane W., Sept 16, 1996
2307 */
2308
2309static void
88aad2e5 2310checkFailureRatio(err_type etype, hier_code hcode)
7a2f978b 2311{
88aad2e5 2312 static double magic_factor = 100.0;
7a2f978b 2313 double n_good;
2314 double n_bad;
2315 if (hcode == HIER_NONE)
2316 return;
88aad2e5 2317 n_good = magic_factor / (1.0 + request_failure_ratio);
7a2f978b 2318 n_bad = magic_factor - n_good;
88aad2e5 2319 switch (etype) {
7a2f978b 2320 case ERR_DNS_FAIL:
2321 case ERR_CONNECT_FAIL:
2322 case ERR_READ_ERROR:
2323 n_bad++;
2324 break;
2325 default:
2326 n_good++;
2327 }
88aad2e5 2328 request_failure_ratio = n_bad / n_good;
7a2f978b 2329 if (hit_only_mode_until > squid_curtime)
2330 return;
88aad2e5 2331 if (request_failure_ratio < 1.0)
7a2f978b 2332 return;
93f9abd0 2333 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio);
2334 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
7a2f978b 2335 FAILURE_MODE_TIME / 60);
2336 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
88aad2e5 2337 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
7a2f978b 2338}
15df8349 2339
2340void
2341clientHttpConnectionsOpen(void)
2342{
2343 ushortlist *u;
2344 int fd;
15df8349 2345 for (u = Config.Port.http; u; u = u->next) {
8daca701 2346 enter_suid();
2347 fd = comm_open(SOCK_STREAM,
2348 0,
2349 Config.Addrs.tcp_incoming,
2350 u->i,
2351 COMM_NONBLOCKING,
2352 "HTTP Socket");
2353 leave_suid();
2354 if (fd < 0)
2355 continue;
2356 comm_listen(fd);
2357 commSetSelect(fd, COMM_SELECT_READ, httpAccept, NULL, 0);
2358 commSetDefer(fd, httpAcceptDefer, NULL);
2359 debug(1, 1) ("Accepting HTTP connections on port %d, FD %d.\n",
2360 (int) u->i, fd);
2361 HttpSockets[NHttpSockets++] = fd;
15df8349 2362 }
2363 if (NHttpSockets < 1)
8daca701 2364 fatal("Cannot open HTTP Port");
15df8349 2365}
c0fbae16 2366
ab013258 2367int
2368handleConnectionHeader(int flag, char *where, char *what)
2369{
2370 char *t, *p, *wh;
2371 int i;
2372 LOCAL_ARRAY(char, mbuf, 256);
2373
2374 if (flag) { /* lookup mode */
2375 if (where[0] == '\0' || what[0] == '\0')
2376 return 0;
2377 p = xstrdup(what);
2378 t = strtok(p, ":");
2379 if (t == NULL)
2380 return 0;
2381 debug(20, 3) ("handleConnectionHeader: %s\n AND %s (%p)\n", where, t, p);
2382 i = strstr(where, t) ? 1 : 0;
2383 xfree(p);
2384 return (i);
2385 }
2386 where[0] = '\0';
2387 wh = xstrdup(what);
2388 t = strtok(wh, ",");
2389 while (t != NULL) {
2390
2391#ifdef BE_PARANOID
2392 static char no_conn[] = "Expires:Host:Content-length:Content-type:";
2393
2394 if (handleConnectionHeader(1, no_conn, t)) {
2395 debug(1, 1) ("handleConnectionHeader: problematic header %s\n", t);
2396 t = strtok(NULL, ",\n");
2397 continue;
2398 }
2399#endif
2400 if ((p = strchr(t, ':')))
2401 xstrncpy(mbuf, t, p - t + 1);
2402 else
2403 snprintf(mbuf, 256, "%s:", t);
2404 strcat(where, mbuf);
2405 t = strtok(NULL, ",\n");
2406 }
2407 debug(20, 3) ("handleConnectionHeader: we have %s\n", where);
2408 xfree(wh);
2409 return 1;
2410}
2411
c0fbae16 2412void
2413clientHttpConnectionsClose(void)
2414{
2415 int i;
2416 for (i = 0; i < NHttpSockets; i++) {
2417 if (HttpSockets[i] >= 0) {
2418 debug(1, 1) ("FD %d Closing HTTP connection\n", HttpSockets[i]);
2419 comm_close(HttpSockets[i]);
2420 HttpSockets[i] = -1;
2421 }
2422 }
2423 NHttpSockets = 0;
2424}