]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
Stew Forster:
[thirdparty/squid.git] / src / client_side.cc
CommitLineData
3c66d057 1
dd11e0b7 2/*
69c95dd3 3 * $Id: client_side.cc,v 1.306 1998/05/14 16:33:48 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);
8e092300 131 return r->cache_control &&
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{
543 /* rewrite */
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
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);
764 if (strListIsMember(&s, ThisCache, ',')) {
77ed547a 765 if (!http->flags.accel) {
93f9abd0 766 debug(33, 1) ("WARNING: Forwarding loop detected for '%s'\n",
23d92c64 767 http->uri);
99edd1c3 768 debug(33, 1) ("--> %s\n", strBuf(s));
7a2f978b 769 }
79a15e0a 770 EBIT_SET(request->flags, REQ_LOOPDETECT);
7a2f978b 771 }
d21f1c54 772#if FORW_VIA_DB
99edd1c3 773 fvdbCountVia(strBuf(s));
d21f1c54 774#endif
99edd1c3 775 stringClean(&s);
d21f1c54 776 }
7a2f978b 777#if USE_USERAGENT_LOG
99edd1c3 778 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT)))
779 logUserAgent(fqdnFromAddr(http->conn->peer.sin_addr), str);
d21f1c54 780#endif
781#if FORW_VIA_DB
99edd1c3 782 if (httpHeaderHas(req_hdr, HDR_X_FORWARDED_FOR)) {
783 String s = httpHeaderGetList(req_hdr, HDR_X_FORWARDED_FOR);
784 fvdbCountForw(strBuf(s));
785 stringClean(&s);
786 }
7a2f978b 787#endif
99edd1c3 788#if OLD_CODE
4e41e277 789 if ((t = mime_get_header_field(request_hdr, "Cache-control", "max-age="))) {
790 request->max_age = atoi(t + 8);
791 } else {
792 request->max_age = -1;
793 }
794 if ((t = mime_get_header_field(request_hdr, "Cache-control", "only-if-cached"))) {
795 EBIT_SET(request->flags, REQ_CC_ONLY_IF_CACHED);
7a2f978b 796 }
99edd1c3 797#else
798 request->cache_control = httpHeaderGetCc(req_hdr);
799#endif
7a2f978b 800 if (request->method == METHOD_TRACE) {
99edd1c3 801#if OLD_CODE
7a2f978b 802 if ((t = mime_get_header(request_hdr, "Max-Forwards")))
803 request->max_forwards = atoi(t);
99edd1c3 804#else
805 request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS);
806#endif
7a2f978b 807 }
50ddd7a4 808 if (clientCachable(http))
809 EBIT_SET(request->flags, REQ_CACHABLE);
810 if (clientHierarchical(http))
811 EBIT_SET(request->flags, REQ_HIERARCHICAL);
99edd1c3 812 debug(33, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n",
50ddd7a4 813 EBIT_TEST(request->flags, REQ_NOCACHE) ? "SET" : "NOT SET");
99edd1c3 814 debug(33, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n",
50ddd7a4 815 EBIT_TEST(request->flags, REQ_CACHABLE) ? "SET" : "NOT SET");
99edd1c3 816 debug(33, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n",
50ddd7a4 817 EBIT_TEST(request->flags, REQ_HIERARCHICAL) ? "SET" : "NOT SET");
7a2f978b 818}
819
31be8b80 820static int
efb9218c 821clientCheckContentLength(request_t * r)
31be8b80 822{
99edd1c3 823#if OLD_CODE
31be8b80 824 char *t;
825 int len;
826 /*
efb9218c 827 * We only require a content-length for "upload" methods
31be8b80 828 */
efb9218c 829 if (0 == pumpMethod(r->method))
31be8b80 830 return 1;
31be8b80 831 t = mime_get_header(r->headers, "Content-Length");
efb9218c 832 if (NULL == t)
31be8b80 833 return 0;
834 len = atoi(t);
835 if (len < 0)
836 return 0;
837 return 1;
99edd1c3 838#else
839 /* We only require a content-length for "upload" methods */
840 return !pumpMethod(r->method) ||
841 httpHeaderGetInt(&r->header, HDR_CONTENT_LENGTH) >= 0;
842#endif
31be8b80 843}
844
7a2f978b 845static int
50ddd7a4 846clientCachable(clientHttpRequest * http)
7a2f978b 847{
23d92c64 848 const char *url = http->uri;
7a2f978b 849 request_t *req = http->request;
850 method_t method = req->method;
bd05e3e3 851 aclCheck_t ch;
852 memset(&ch, '\0', sizeof(ch));
853 /*
854 * Hopefully, nobody really wants 'no_cache' by client's IP
855 * address, but if they do, this should work if they use IP
856 * addresses in their ACLs, or if the client's address is in
857 * the FQDN cache.
858 *
859 * This may not work yet for 'dst' and 'dst_domain' ACLs.
860 */
861 ch.src_addr = http->conn->peer.sin_addr;
862 ch.request = http->request;
79a51871 863 /*
864 * aclCheckFast returns 1 for ALLOW and 0 for DENY. The default
865 * is ALLOW, so we require 'no_cache DENY foo' in squid.conf
866 * to indicate uncachable objects.
867 */
95291419 868 if (!aclCheckFast(Config.accessList.noCache, &ch))
4b4cd312 869 return 0;
7a2f978b 870 if (req->protocol == PROTO_HTTP)
871 return httpCachable(method);
872 /* FTP is always cachable */
873 if (req->protocol == PROTO_GOPHER)
874 return gopherCachable(url);
875 if (req->protocol == PROTO_WAIS)
876 return 0;
877 if (method == METHOD_CONNECT)
878 return 0;
879 if (method == METHOD_TRACE)
880 return 0;
881 if (req->protocol == PROTO_CACHEOBJ)
882 return 0;
883 return 1;
884}
885
886/* Return true if we can query our neighbors for this object */
887static int
50ddd7a4 888clientHierarchical(clientHttpRequest * http)
7a2f978b 889{
23d92c64 890 const char *url = http->uri;
7a2f978b 891 request_t *request = http->request;
892 method_t method = request->method;
893 const wordlist *p = NULL;
894
895 /* IMS needs a private key, so we can use the hierarchy for IMS only
896 * if our neighbors support private keys */
79a15e0a 897 if (EBIT_TEST(request->flags, REQ_IMS) && !neighbors_do_private_keys)
7a2f978b 898 return 0;
79a15e0a 899 if (EBIT_TEST(request->flags, REQ_AUTH))
7a2f978b 900 return 0;
901 if (method == METHOD_TRACE)
902 return 1;
903 if (method != METHOD_GET)
904 return 0;
905 /* scan hierarchy_stoplist */
906 for (p = Config.hierarchy_stoplist; p; p = p->next)
907 if (strstr(url, p->key))
908 return 0;
79a15e0a 909 if (EBIT_TEST(request->flags, REQ_LOOPDETECT))
7a2f978b 910 return 0;
911 if (request->protocol == PROTO_HTTP)
912 return httpCachable(method);
913 if (request->protocol == PROTO_GOPHER)
914 return gopherCachable(url);
915 if (request->protocol == PROTO_WAIS)
916 return 0;
917 if (request->protocol == PROTO_CACHEOBJ)
918 return 0;
919 return 1;
920}
921
cf50a0af 922int
7a2f978b 923isTcpHit(log_type code)
924{
925 /* this should be a bitmap for better optimization */
926 if (code == LOG_TCP_HIT)
927 return 1;
928 if (code == LOG_TCP_IMS_HIT)
929 return 1;
930 if (code == LOG_TCP_REFRESH_FAIL_HIT)
931 return 1;
932 if (code == LOG_TCP_REFRESH_HIT)
933 return 1;
934 if (code == LOG_TCP_NEGATIVE_HIT)
935 return 1;
936 if (code == LOG_TCP_MEM_HIT)
937 return 1;
938 return 0;
939}
940
941static void
942clientAppendReplyHeader(char *hdr, const char *line, size_t * sz, size_t max)
943{
944 size_t n = *sz + strlen(line) + 2;
945 if (n >= max)
946 return;
947 strcpy(hdr + (*sz), line);
948 strcat(hdr + (*sz), crlf);
949 *sz = n;
950}
951
adba4a64 952/* this entire function has to be rewriten using new interfaces @?@ @?@ */
7a2f978b 953size_t
954clientBuildReplyHeader(clientHttpRequest * http,
955 char *hdr_in,
2334c194 956 size_t hdr_in_sz,
7a2f978b 957 size_t * in_len,
958 char *hdr_out,
959 size_t out_sz)
960{
ab013258 961 LOCAL_ARRAY(char, no_forward, 1024);
7a2f978b 962 char *xbuf;
963 char *ybuf;
964 char *t = NULL;
2334c194 965 char *end;
7a2f978b 966 size_t len = 0;
967 size_t hdr_len = 0;
968 size_t l;
efb9218c 969 if (0 != strncmp(hdr_in, "HTTP/", 5))
970 return 0;
2334c194 971 hdr_len = headersEnd(hdr_in, hdr_in_sz);
8e8731ad 972 if (0 == hdr_len) {
93f9abd0 973 debug(33, 3) ("clientBuildReplyHeader: DIDN'T FIND END-OF-HEADERS\n");
7a2f978b 974 return 0;
975 }
7021844c 976 xbuf = memAllocate(MEM_4K_BUF);
977 ybuf = memAllocate(MEM_4K_BUF);
2334c194 978 end = hdr_in + hdr_len;
7a2f978b 979 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
7a2f978b 980 l = strcspn(t, crlf) + 1;
8febe7df 981 /* Wow, we might find a NULL before 'end' */
982 if (1 == l)
983 break;
7a2f978b 984 xstrncpy(xbuf, t, l > 4096 ? 4096 : l);
ae1acd0b 985 /* enforce 1.0 reply version, this hack will be rewritten */
2334c194 986 if (t == hdr_in && !strncasecmp(xbuf, "HTTP/", 5) && l > 8 &&
2ac76861 987 (isspace(xbuf[8]) || isspace(xbuf[9])))
988 xmemmove(xbuf + 5, "1.0 ", 4);
901e234d 989#if DONT_FILTER_THESE
990 /*
99edd1c3 991 * but you might want to if you run Squid as an HTTP accelerator
901e234d 992 */
7a2f978b 993 if (strncasecmp(xbuf, "Accept-Ranges:", 14) == 0)
994 continue;
995 if (strncasecmp(xbuf, "Etag:", 5) == 0)
996 continue;
997#endif
998 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
999 continue;
ab013258 1000 if (strncasecmp(xbuf, "Connection:", 11) == 0) {
1001 handleConnectionHeader(0, no_forward, &xbuf[11]);
7a2f978b 1002 continue;
ab013258 1003 }
99edd1c3 1004 if (strncasecmp(xbuf, "keep-alive:", 11) == 0)
7a2f978b 1005 continue;
1006 if (strncasecmp(xbuf, "Set-Cookie:", 11) == 0)
1007 if (isTcpHit(http->log_type))
1008 continue;
ab013258 1009 if (!handleConnectionHeader(1, no_forward, xbuf))
1010 clientAppendReplyHeader(hdr_out, xbuf, &len, out_sz - 512);
7a2f978b 1011 }
7a2f978b 1012 /* Append X-Cache: */
02941a5d 1013 snprintf(ybuf, 4096, "X-Cache: %s from %s",
1014 isTcpHit(http->log_type) ? "HIT" : "MISS",
1015 getMyHostname());
7a2f978b 1016 clientAppendReplyHeader(hdr_out, ybuf, &len, out_sz);
6cfa8966 1017#if USE_CACHE_DIGESTS
57b1ff70 1018 /* Append X-Cache-Lookup: -- temporary hack, to be removed @?@ @?@ */
1019 snprintf(ybuf, 4096, "X-Cache-Lookup: %s from %s:%d",
1020 http->lookup_type ? http->lookup_type : "NONE",
1021 getMyHostname(), Config.Port.http->i);
1022 clientAppendReplyHeader(hdr_out, ybuf, &len, out_sz);
1023#endif
7a2f978b 1024 /* Append Proxy-Connection: */
79a15e0a 1025 if (EBIT_TEST(http->request->flags, REQ_PROXY_KEEPALIVE)) {
99edd1c3 1026 snprintf(ybuf, 4096, "Proxy-Connection: keep-alive");
7a2f978b 1027 clientAppendReplyHeader(hdr_out, ybuf, &len, out_sz);
1028 }
1029 clientAppendReplyHeader(hdr_out, null_string, &len, out_sz);
1030 if (in_len)
1031 *in_len = hdr_len;
1032 if ((l = strlen(hdr_out)) != len) {
1033 debug_trap("clientBuildReplyHeader: size mismatch");
1034 len = l;
1035 }
93f9abd0 1036 debug(33, 3) ("clientBuildReplyHeader: OUTPUT:\n%s\n", hdr_out);
3f6c0fb2 1037 memFree(MEM_4K_BUF, xbuf);
1038 memFree(MEM_4K_BUF, ybuf);
7a2f978b 1039 return len;
1040}
1041
4c323c5f 1042static void
7a2f978b 1043clientCacheHit(void *data, char *buf, ssize_t size)
1044{
1045 clientHttpRequest *http = data;
fdfc0d63 1046 StoreEntry *e;
93f9abd0 1047 debug(33, 3) ("clientCacheHit: %s, %d bytes\n", http->uri, (int) size);
7a2f978b 1048 if (size >= 0) {
1049 clientSendMoreData(data, buf, size);
1050 } else if (http->entry == NULL) {
93f9abd0 1051 debug(33, 3) ("clientCacheHit: request aborted\n");
7a2f978b 1052 } else {
1053 /* swap in failure */
93f9abd0 1054 debug(33, 3) ("clientCacheHit: swapin failure for %s\n", http->uri);
7a2f978b 1055 http->log_type = LOG_TCP_SWAPFAIL_MISS;
fdfc0d63 1056 if ((e = http->entry)) {
1057 http->entry = NULL;
1058 storeUnregister(e, http);
fdfc0d63 1059 storeUnlockObject(e);
1060 }
fb63215a 1061 clientProcessMiss(http);
7a2f978b 1062 }
1063}
1064
4c323c5f 1065static void
7a2f978b 1066clientSendMoreData(void *data, char *buf, ssize_t size)
1067{
1068 clientHttpRequest *http = data;
1069 StoreEntry *entry = http->entry;
1070 ConnStateData *conn = http->conn;
1071 int fd = conn->fd;
7a2f978b 1072 size_t hdrlen;
1073 size_t l = 0;
1074 size_t writelen;
1075 char *newbuf;
3f6c0fb2 1076 FREE *freefunc = memFree4K;
93f9abd0 1077 debug(33, 5) ("clientSendMoreData: %s, %d bytes\n", http->uri, (int) size);
7a2f978b 1078 assert(size <= SM_PAGE_SIZE);
40defb17 1079 assert(http->request != NULL);
93f9abd0 1080 debug(33, 5) ("clientSendMoreData: FD %d '%s', out.offset=%d \n",
5f6ac48b 1081 fd, storeUrl(entry), (int) http->out.offset);
7a2f978b 1082 if (conn->chr != http) {
1083 /* there is another object in progress, defer this one */
a43b3862 1084 debug(0, 0) ("clientSendMoreData: Deferring %s\n", storeUrl(entry));
7a2f978b 1085 freefunc(buf);
1086 return;
fdfc0d63 1087 } else if (entry && entry->store_status == STORE_ABORTED) {
d6e928c9 1088 /* call clientWriteComplete so the client socket gets closed */
1089 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
7a2f978b 1090 freefunc(buf);
1091 return;
1092 } else if (size < 0) {
d6e928c9 1093 /* call clientWriteComplete so the client socket gets closed */
1094 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
7a2f978b 1095 freefunc(buf);
1096 return;
1097 } else if (size == 0) {
d6e928c9 1098 /* call clientWriteComplete so the client socket gets closed */
1099 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
7a2f978b 1100 freefunc(buf);
1101 return;
1102 }
1103 writelen = size;
40defb17 1104 if (http->out.offset == 0) {
7a2f978b 1105 if (Config.onoff.log_mime_hdrs) {
2334c194 1106 size_t k;
1107 if ((k = headersEnd(buf, size))) {
7a2f978b 1108 safe_free(http->al.headers.reply);
1afe05c5 1109 http->al.headers.reply = xcalloc(k + 1, 1);
2334c194 1110 xstrncpy(http->al.headers.reply, buf, k);
7a2f978b 1111 }
1112 }
7021844c 1113 newbuf = memAllocate(MEM_8K_BUF);
7a2f978b 1114 hdrlen = 0;
2334c194 1115 l = clientBuildReplyHeader(http, buf, size, &hdrlen, newbuf, 8192);
7a2f978b 1116 if (l != 0) {
1117 writelen = l + size - hdrlen;
1118 assert(writelen <= 8192);
1119 /*
1120 * l is the length of the new headers in newbuf
1121 * hdrlen is the length of the old headers in buf
1122 * size - hdrlen is the amount of body in buf
1123 */
93f9abd0 1124 debug(33, 3) ("clientSendMoreData: Appending %d bytes after headers\n",
7a2f978b 1125 (int) (size - hdrlen));
6cf028ab 1126 if (((size - hdrlen) + l) > 8192) {
0e473d70 1127 debug(0, 0) ("Size, hdrlen, l %d, %d, %d\n", size, hdrlen, l);
6cf028ab 1128 return;
1129 }
7a2f978b 1130 xmemcpy(newbuf + l, buf + hdrlen, size - hdrlen);
1131 /* replace buf with newbuf */
1132 freefunc(buf);
1133 buf = newbuf;
3f6c0fb2 1134 freefunc = memFree8K;
7a2f978b 1135 newbuf = NULL;
1136 } else {
3f6c0fb2 1137 memFree(MEM_8K_BUF, newbuf);
7a2f978b 1138 newbuf = NULL;
1139 if (size < SM_PAGE_SIZE && entry->store_status == STORE_PENDING) {
1140 /* wait for more to arrive */
1141 storeClientCopy(entry,
1142 http->out.offset + size,
1143 http->out.offset,
1144 SM_PAGE_SIZE,
1145 buf,
1146 clientSendMoreData,
1147 http);
1148 return;
1149 }
1150 }
1151 }
1152 http->out.offset += size;
40defb17 1153 /*
1154 * ick, this is gross
1155 */
7a2f978b 1156 if (http->request->method == METHOD_HEAD) {
2334c194 1157 size_t k;
1158 if ((k = headersEnd(buf, size))) {
1159 writelen = k;
7a2f978b 1160 /* force end */
f68b5d81 1161 if (entry->store_status == STORE_PENDING)
c24fca87 1162 http->out.offset = entry->mem_obj->inmem_hi;
f68b5d81 1163 else
07304bf9 1164 http->out.offset = objectLen(entry);
7a2f978b 1165 }
1166 }
1167 comm_write(fd, buf, writelen, clientWriteComplete, http, freefunc);
1168}
1169
1a92a1e2 1170static
1171void
1172clientKeepaliveNextRequest(clientHttpRequest * http)
1173{
1174 ConnStateData *conn = http->conn;
1175 StoreEntry *entry;
1176 conn->defer.until = 0; /* Kick it to read a new request */
1177 httpRequestFree(http);
1178 if ((http = conn->chr) != NULL) {
1179 debug(33, 1) ("clientKeepaliveNextRequest: FD %d Sending next\n",
1180 conn->fd);
1181 entry = http->entry;
1182 if (0 == storeClientCopyPending(entry, http)) {
1183 if (entry->store_status == STORE_ABORTED)
1184 debug(33, 0) ("clientWriteComplete: entry->swap_status == STORE_ABORTED\n");
1185 storeClientCopy(entry,
1186 http->out.offset,
1187 http->out.offset,
1188 SM_PAGE_SIZE,
1189 memAllocate(MEM_4K_BUF),
1190 clientSendMoreData,
1191 http);
1192 }
1193 } else {
1194 debug(33, 5) ("clientWriteComplete: FD %d reading next request\n",
1195 conn->fd);
1196 fd_note(conn->fd, "Reading next request");
1197 /*
1198 * Set the timeout BEFORE calling clientReadRequest().
1199 */
1200 commSetTimeout(conn->fd, 15, requestTimeout, conn);
1201 clientReadRequest(conn->fd, conn); /* Read next request */
1202 /*
1203 * Note, the FD may be closed at this point.
1204 */
1205 }
1206}
1207
fc5d6f7f 1208static void
79a15e0a 1209clientWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
7a2f978b 1210{
1211 clientHttpRequest *http = data;
7a2f978b 1212 StoreEntry *entry = http->entry;
b34ed725 1213 int done;
7a2f978b 1214 http->out.size += size;
93f9abd0 1215 debug(33, 5) ("clientWriteComplete: FD %d, sz %d, err %d, off %d, len %d\n",
5f6ac48b 1216 fd, size, errflag, (int) http->out.offset, objectLen(entry));
d8b83480 1217 if (size > 0) {
1218 kb_incr(&Counter.client_http.kbytes_out, size);
1219 if (isTcpHit(http->log_type))
1220 kb_incr(&Counter.client_http.hit_kbytes_out, size);
1221 }
7a2f978b 1222 if (errflag) {
36547bcf 1223#if DONT_DO_THIS
1224 /*
1225 * Not sure why this CheckQuickAbort() would be needed here.
1226 * We also call CheckQuickAbort() in httpRequestFree(), which
1227 * gets called as a comm_close handler. We need to be careful
1228 * that CheckQuickAbort() gets called only ONCE, and AFTER
1229 * storeUnregister() has been called. [DW/1.2.beta18]
1230 */
7a2f978b 1231 CheckQuickAbort(http);
36547bcf 1232#endif
7a2f978b 1233 comm_close(fd);
fdfc0d63 1234 } else if (NULL == entry) {
1235 comm_close(fd); /* yuk */
7a2f978b 1236 } else if (entry->store_status == STORE_ABORTED) {
7a2f978b 1237 comm_close(fd);
a200bbd2 1238 } else if ((done = clientCheckTransferDone(http)) != 0 || size == 0) {
93f9abd0 1239 debug(33, 5) ("clientWriteComplete: FD %d transfer is DONE\n", fd);
7a2f978b 1240 /* We're finished case */
038eb4ed 1241 if (http->entry->mem_obj->reply->content_length < 0 || !done ||
b34ed725 1242 EBIT_TEST(entry->flag, ENTRY_BAD_LENGTH)) {
1243 /*
1244 * Client connection closed due to unknown or invalid
1245 * content length. Persistent connection is not possible.
1246 * This catches most cases, but probably not all.
1247 */
7a2f978b 1248 comm_close(fd);
79a15e0a 1249 } else if (EBIT_TEST(http->request->flags, REQ_PROXY_KEEPALIVE)) {
93f9abd0 1250 debug(33, 5) ("clientWriteComplete: FD %d Keeping Alive\n", fd);
1a92a1e2 1251 clientKeepaliveNextRequest(http);
7a2f978b 1252 } else {
1253 comm_close(fd);
1254 }
1255 } else {
1256 /* More data will be coming from primary server; register with
1257 * storage manager. */
0e473d70 1258 if (entry->store_status == STORE_ABORTED)
6cf028ab 1259 debug(33, 0) ("clientWriteComplete 2: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1260 storeClientCopy(entry,
1261 http->out.offset,
1262 http->out.offset,
1263 SM_PAGE_SIZE,
7021844c 1264 memAllocate(MEM_4K_BUF),
7a2f978b 1265 clientSendMoreData,
1266 http);
1267 }
1268}
1269
2920225f 1270/* called when clientGetHeadersFor*IMS completes */
1271static void
99edd1c3 1272clientFinishIMS(clientHttpRequest *http)
2920225f 1273{
1274 StoreEntry *entry = http->entry;
1275 MemBuf mb;
1276
1277 http->log_type = LOG_TCP_IMS_HIT;
1278 entry->refcount++;
1279 /* All headers are available, double check if object is modified or not */
1280 if (modifiedSince(entry, http->request)) {
1281 debug(33, 4) ("clientFinishIMS: Modified '%s'\n",
1282 storeUrl(entry));
1283 if (entry->store_status == STORE_ABORTED)
1284 debug(33, 0) ("clientFinishIMS: entry->swap_status == STORE_ABORTED\n");
1285 storeClientCopy(entry,
1286 http->out.offset,
1287 http->out.offset,
1288 SM_PAGE_SIZE,
1289 memAllocate(MEM_4K_BUF),
1290 clientSendMoreData,
1291 http);
1292 return;
1293 }
1294 debug(33, 4) ("clientFinishIMS: Not modified '%s'\n",
1295 storeUrl(entry));
1296 /*
1297 * Create the Not-Modified reply from the existing entry,
1298 * Then make a new entry to hold the reply to be written
1299 * to the client.
1300 */
1301 mb = httpPacked304Reply(entry->mem_obj->reply);
1302 storeUnregister(entry, http);
1303 storeUnlockObject(entry);
1304 http->entry = clientCreateStoreEntry(http, http->request->method, 0);
1305 httpReplyParse(http->entry->mem_obj->reply, mb.buf);
1306 storeAppend(http->entry, mb.buf, mb.size);
1307 memBufClean(&mb);
1308 storeComplete(http->entry);
1309}
1310
7a2f978b 1311static void
1b02b5be 1312clientGetHeadersForIMS(void *data, char *buf, ssize_t size)
7a2f978b 1313{
1314 clientHttpRequest *http = data;
7a2f978b 1315 StoreEntry *entry = http->entry;
dd83ba69 1316 MemObject *mem;
93f9abd0 1317 debug(33, 3) ("clientGetHeadersForIMS: %s, %d bytes\n",
1318 http->uri, (int) size);
7a2f978b 1319 assert(size <= SM_PAGE_SIZE);
3f6c0fb2 1320 memFree(MEM_4K_BUF, buf);
1b02b5be 1321 buf = NULL;
6cf028ab 1322 if (size < 0 || entry->store_status == STORE_ABORTED) {
8d4f9bc8 1323 /*
1324 * There are different reasons why we might have size < 0. One
1325 * being that we failed to open a swapfile. Another being that
1326 * the request was cancelled from the client-side. If the client
1327 * cancelled the request, then http->entry will be NULL.
1328 */
1329 if (entry != NULL) {
1330 debug(33, 1) ("clientGetHeadersForIMS: storeClientCopy failed for '%s'\n",
1331 storeKeyText(entry->key));
1332 clientProcessMiss(http);
1333 }
7a2f978b 1334 return;
1335 }
dd83ba69 1336 mem = entry->mem_obj;
cb69b4c7 1337 if (mem->reply->sline.status == 0) {
7a2f978b 1338 if (entry->mem_status == IN_MEMORY) {
fb63215a 1339 clientProcessMiss(http);
7a2f978b 1340 return;
1341 }
93f9abd0 1342 if (size == SM_PAGE_SIZE && http->out.offset == 0) {
1343 /*
1344 * We can't get any more headers than this, so bail
1345 */
1346 debug(33, 1) ("clientGetHeadersForIMS: failed, forcing cache miss\n");
1347 clientProcessMiss(http);
1348 return;
1349 }
1350 debug(33, 3) ("clientGetHeadersForIMS: waiting for HTTP reply headers\n");
7a2f978b 1351 /* All headers are not yet available, wait for more data */
0e473d70 1352 if (entry->store_status == STORE_ABORTED)
6cf028ab 1353 debug(33, 0) ("clientGetHeadersForIMS: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1354 storeClientCopy(entry,
1355 http->out.offset + size,
1356 http->out.offset,
1357 SM_PAGE_SIZE,
7021844c 1358 memAllocate(MEM_4K_BUF),
1b02b5be 1359 clientGetHeadersForIMS,
7a2f978b 1360 http);
1361 return;
1362 }
1363 /* All headers are available, check if object is modified or not */
1364 /* ---------------------------------------------------------------
cb69b4c7 1365 * Removed check for reply->sline.status != 200 because of a potential
7a2f978b 1366 * problem with ICP. We will return a HIT for any public, cached
1367 * object. This includes other responses like 301, 410, as coded in
1368 * http.c. It is Bad(tm) to return UDP_HIT and then, if the reply
50ddd7a4 1369 * code is not 200, hand off to clientProcessMiss(), which may disallow
7a2f978b 1370 * the request based on 'miss_access' rules. Alternatively, we might
1371 * consider requiring returning UDP_HIT only for 200's. This
1372 * problably means an entry->flag bit, which would be lost during
1373 * restart because the flags aren't preserved across restarts.
1374 * --DW 3/11/96.
1375 * ---------------------------------------------------------------- */
1376#ifdef CHECK_REPLY_CODE_NOTEQUAL_200
1377 /* Only objects with statuscode==200 can be "Not modified" */
cb69b4c7 1378 if (mem->reply->sline.status != 200) {
93f9abd0 1379 debug(33, 4) ("clientGetHeadersForIMS: Reply code %d!=200\n",
cb69b4c7 1380 mem->reply->sline.status);
fb63215a 1381 clientProcessMiss(http);
7a2f978b 1382 return;
1383 }
7a2f978b 1384#endif
2920225f 1385 clientFinishIMS(http);
1386}
1387
1388/*
99edd1c3 1389 * client sent an IMS request for ENTRY_SPECIAL;
1390 * mimic clientGetHeadersForIMS(), but call clientCacheHit()
1391 * if something goes wrong;
1392 * note: clientGetHeadersForIMS frees "buf" earlier than we do
2920225f 1393 */
1394static void
1395clientGetHeadersForSpecialIMS(void *data, char *buf, ssize_t size)
1396{
1397 clientHttpRequest *http = data;
1398 StoreEntry *entry = http->entry;
1399 debug(33, 3) ("clientGetHeadersForSpecialIMS: %s, %d bytes\n",
1400 http->uri, (int) size);
1401 assert(size <= SM_PAGE_SIZE);
1402 if (size < 0 || entry->store_status == STORE_ABORTED) {
1403 clientCacheHit(data, buf, size);
1404 return;
1405 }
1406 if (entry->mem_obj->reply->sline.status == 0) {
1407 if (entry->mem_status == IN_MEMORY) {
1408 clientCacheHit(data, buf, size);
1409 return;
1410 }
1411 if (size == SM_PAGE_SIZE && http->out.offset == 0) {
1412 clientCacheHit(data, buf, size);
1413 return;
1414 }
1415 debug(33, 3) ("clientGetHeadersForSpecialIMS: waiting for HTTP reply headers\n");
1416 /* All headers are not yet available, wait for more data */
0e473d70 1417 if (entry->store_status == STORE_ABORTED)
2920225f 1418 debug(33, 0) ("clientGetHeadersForSpecialIMS: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1419 storeClientCopy(entry,
2920225f 1420 http->out.offset + size,
7a2f978b 1421 http->out.offset,
1422 SM_PAGE_SIZE,
2920225f 1423 buf,
1424 clientGetHeadersForSpecialIMS,
7a2f978b 1425 http);
1426 return;
1427 }
2920225f 1428 memFree(MEM_4K_BUF, buf);
1429 clientFinishIMS(http);
7a2f978b 1430}
1431
038eb4ed 1432/*
99edd1c3 1433 * client issued a request with an only-if-cached cache-control directive;
038eb4ed 1434 * we did not find a cached object that can be returned without
99edd1c3 1435 * contacting other servers;
038eb4ed 1436 * respond with a 504 (Gateway Timeout) as suggested in [RFC 2068]
1437 */
1438static void
1439clientProcessOnlyIfCachedMiss(clientHttpRequest * http)
1440{
1441 char *url = http->uri;
1442 request_t *r = http->request;
1443 ErrorState *err = NULL;
1444 debug(33, 4) ("clientProcessOnlyIfCachedMiss: '%s %s'\n",
1445 RequestMethodStr[r->method], url);
038eb4ed 1446 http->al.http.code = HTTP_GATEWAY_TIMEOUT;
1447 err = errorCon(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT);
1448 err->request = requestLink(r);
1449 err->src_addr = http->conn->peer.sin_addr;
1450 http->entry = clientCreateStoreEntry(http, r->method, 0);
1451 errorAppendEntry(http->entry, err);
1452}
1453
50ddd7a4 1454static log_type
1455clientProcessRequest2(clientHttpRequest * http)
1456{
1457 const request_t *r = http->request;
23d92c64 1458 const cache_key *key = storeKeyPublic(http->uri, r->method);
50ddd7a4 1459 StoreEntry *e;
57b1ff70 1460 e = http->entry = storeGet(key);
6cfa8966 1461#if USE_CACHE_DIGESTS
57b1ff70 1462 http->lookup_type = e ? "HIT" : "MISS";
1ca847dd 1463#endif
57b1ff70 1464 if (!e) {
50ddd7a4 1465 /* this object isn't in the cache */
1466 return LOG_TCP_MISS;
1467 } else if (EBIT_TEST(e->flag, ENTRY_SPECIAL)) {
12784378 1468 /* ideally, special entries should be processed later,
1469 * so we can use std processing routines for IMS and such */
57b1ff70 1470 if (EBIT_TEST(r->flags, REQ_IMS) && e->lastmod <= r->ims)
12784378 1471 return LOG_TCP_IMS_HIT;
17b6e784 1472 else if (e->mem_status == IN_MEMORY)
50ddd7a4 1473 return LOG_TCP_MEM_HIT;
1474 else
1475 return LOG_TCP_HIT;
1476 } else if (!storeEntryValidToSend(e)) {
1477 storeRelease(e);
1478 http->entry = NULL;
1479 return LOG_TCP_MISS;
1480 } else if (EBIT_TEST(r->flags, REQ_NOCACHE)) {
1481 /* NOCACHE should always eject a negative cached object */
1482 if (EBIT_TEST(e->flag, ENTRY_NEGCACHED))
1483 storeRelease(e);
1484 /* NOCACHE+IMS should not eject a valid object */
1485 else if (EBIT_TEST(r->flags, REQ_IMS))
1486 (void) 0;
1487 /* Request-Range should not eject a valid object */
1488 else if (EBIT_TEST(r->flags, REQ_RANGE))
1489 (void) 0;
1490 else
1491 storeRelease(e);
1492 ipcacheReleaseInvalid(r->host);
1493 http->entry = NULL;
ee1679df 1494 return LOG_TCP_CLIENT_REFRESH_MISS;
50ddd7a4 1495 } else if (checkNegativeHit(e)) {
1496 return LOG_TCP_NEGATIVE_HIT;
1497 } else if (refreshCheck(e, r, 0)) {
1498 /* The object is in the cache, but it needs to be validated. Use
1499 * LOG_TCP_REFRESH_MISS for the time being, maybe change it to
1b02b5be 1500 * _HIT later in clientHandleIMSReply() */
50ddd7a4 1501 if (r->protocol == PROTO_HTTP)
1502 return LOG_TCP_REFRESH_MISS;
1503 else
1504 return LOG_TCP_MISS; /* XXX zoinks */
1505 } else if (EBIT_TEST(r->flags, REQ_IMS)) {
1506 /* User-initiated IMS request for something we think is valid */
1507 return LOG_TCP_IMS_MISS;
1508 } else if (e->mem_status == IN_MEMORY) {
1509 return LOG_TCP_MEM_HIT;
1510 } else {
1511 return LOG_TCP_HIT;
1512 }
1513}
1514
4c323c5f 1515static void
fb63215a 1516clientProcessRequest(clientHttpRequest * http)
7a2f978b 1517{
23d92c64 1518 char *url = http->uri;
7a2f978b 1519 StoreEntry *entry = NULL;
50ddd7a4 1520 request_t *r = http->request;
fb63215a 1521 int fd = http->conn->fd;
1bea1d83 1522 HttpReply *rep;
93f9abd0 1523 debug(33, 4) ("clientProcessRequest: %s '%s'\n",
50ddd7a4 1524 RequestMethodStr[r->method],
7a2f978b 1525 url);
fb63215a 1526 if (r->method == METHOD_CONNECT) {
7a2f978b 1527 http->log_type = LOG_TCP_MISS;
ea0efb98 1528 sslStart(fd, url, r, &http->out.size);
1529 return;
fb63215a 1530 } else if (r->method == METHOD_PURGE) {
ea0efb98 1531 clientPurgeRequest(http);
1532 return;
fb63215a 1533 } else if (r->method == METHOD_TRACE) {
50ddd7a4 1534 if (r->max_forwards == 0) {
b17b22eb 1535 http->entry = clientCreateStoreEntry(http, r->method, 0);
1536 storeReleaseRequest(http->entry);
1537 storeBuffer(http->entry);
1bea1d83 1538 rep = httpReplyCreate();
31be8b80 1539 httpReplySetHeaders(rep, 1.0, HTTP_OK, NULL, "text/plain",
99edd1c3 1540 r->prefix_sz, 0, squid_curtime);
1bea1d83 1541 httpReplySwapOut(rep, http->entry);
1542 httpReplyDestroy(rep);
99edd1c3 1543 storeAppend(http->entry, r->prefix, r->prefix_sz);
b17b22eb 1544 storeComplete(http->entry);
7a2f978b 1545 return;
1546 }
1547 /* yes, continue */
6addbd81 1548 http->log_type = LOG_TCP_MISS;
efb9218c 1549 } else if (pumpMethod(r->method)) {
fb63215a 1550 http->log_type = LOG_TCP_MISS;
31be8b80 1551 /* XXX oof, POST can be cached! */
54220df8 1552 pumpInit(fd, r, http->uri);
31be8b80 1553 } else {
efb9218c 1554 http->log_type = clientProcessRequest2(http);
7a2f978b 1555 }
93f9abd0 1556 debug(33, 4) ("clientProcessRequest: %s for '%s'\n",
7a2f978b 1557 log_tags[http->log_type],
23d92c64 1558 http->uri);
50ddd7a4 1559 if ((entry = http->entry) != NULL) {
7a2f978b 1560 storeLockObject(entry);
23d92c64 1561 storeCreateMemObject(entry, http->uri, http->log_uri);
7a2f978b 1562 storeClientListAdd(entry, http);
1563 }
7a2f978b 1564 http->out.offset = 0;
1565 switch (http->log_type) {
1566 case LOG_TCP_HIT:
1567 case LOG_TCP_NEGATIVE_HIT:
1568 case LOG_TCP_MEM_HIT:
1569 entry->refcount++; /* HIT CASE */
0e473d70 1570 if (entry->store_status == STORE_ABORTED)
6cf028ab 1571 debug(33, 0) ("clientProcessRequest: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1572 storeClientCopy(entry,
1573 http->out.offset,
1574 http->out.offset,
1575 SM_PAGE_SIZE,
7021844c 1576 memAllocate(MEM_4K_BUF),
7a2f978b 1577 clientCacheHit,
1578 http);
038eb4ed 1579 return;
2920225f 1580 case LOG_TCP_IMS_HIT:
7a2f978b 1581 case LOG_TCP_IMS_MISS:
0e473d70 1582 if (entry->store_status == STORE_ABORTED)
6cf028ab 1583 debug(33, 0) ("clientProcessRequest 2: entry->swap_status == STORE_ABORTED\n");
7a2f978b 1584 storeClientCopy(entry,
1585 http->out.offset,
1586 http->out.offset,
1587 SM_PAGE_SIZE,
7021844c 1588 memAllocate(MEM_4K_BUF),
99edd1c3 1589 (http->log_type == LOG_TCP_IMS_MISS) ?
1590 clientGetHeadersForIMS : clientGetHeadersForSpecialIMS,
7a2f978b 1591 http);
1592 break;
1593 case LOG_TCP_REFRESH_MISS:
fb63215a 1594 clientProcessExpired(http);
7a2f978b 1595 break;
1596 default:
fb63215a 1597 clientProcessMiss(http);
7a2f978b 1598 break;
1599 }
1600}
1601
1602/*
1603 * Prepare to fetch the object as it's a cache miss of some kind.
1604 */
1605static void
fb63215a 1606clientProcessMiss(clientHttpRequest * http)
7a2f978b 1607{
23d92c64 1608 char *url = http->uri;
f44af445 1609 request_t *r = http->request;
7a2f978b 1610 aclCheck_t ch;
1611 int answer;
1612 ErrorState *err = NULL;
93f9abd0 1613 debug(33, 4) ("clientProcessMiss: '%s %s'\n",
f44af445 1614 RequestMethodStr[r->method], url);
99edd1c3 1615 debug(33, 10) ("clientProcessMiss: prefix:\n%s\n", r->prefix);
79e0dc20 1616 /*
1617 * We might have a left-over StoreEntry from a failed cache hit
1618 * or IMS request.
1619 */
1620 if (http->entry) {
12784378 1621 if (EBIT_TEST(http->entry->flag, ENTRY_SPECIAL))
1622 debug(33, 0) ("clientProcessMiss: miss on a special object (%s).\n", url);
79e0dc20 1623 storeUnregister(http->entry, http);
1624 storeUnlockObject(http->entry);
1625 http->entry = NULL;
1626 }
7c1d4010 1627 if (clientOnlyIfCached(http)) {
1628 clientProcessOnlyIfCachedMiss(http);
1629 return;
1630 }
79e0dc20 1631 /*
1632 * Check if this host is allowed to fetch MISSES from us (miss_access)
1633 */
7a2f978b 1634 memset(&ch, '\0', sizeof(aclCheck_t));
1635 ch.src_addr = http->conn->peer.sin_addr;
f44af445 1636 ch.request = r;
7a2f978b 1637 answer = aclCheckFast(Config.accessList.miss, &ch);
1da5651f 1638 if (answer == 0) {
7a2f978b 1639 http->al.http.code = HTTP_FORBIDDEN;
bdb3c273 1640 err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN);
f44af445 1641 err->request = requestLink(r);
7a2f978b 1642 err->src_addr = http->conn->peer.sin_addr;
f44af445 1643 http->entry = clientCreateStoreEntry(http, r->method, 0);
79e0dc20 1644 errorAppendEntry(http->entry, err);
7a2f978b 1645 return;
1646 }
79e0dc20 1647 assert(http->out.offset == 0);
f44af445 1648 http->entry = clientCreateStoreEntry(http, r->method, r->flags);
79e0dc20 1649 http->entry->refcount++;
1da5651f 1650 if (http->flags.internal)
1651 r->protocol = PROTO_INTERNAL;
f44af445 1652 protoDispatch(http->conn->fd, http->entry, r);
7a2f978b 1653}
1654
99edd1c3 1655static clientHttpRequest *
1656parseHttpRequestAbort(ConnStateData * conn, const char *uri)
1657{
1658 clientHttpRequest *http = xcalloc(1, sizeof(clientHttpRequest));
1659 cbdataAdd(http, MEM_NONE);
1660 http->conn = conn;
1661 http->start = current_time;
1662 http->req_sz = conn->in.offset;
1663 http->uri = xstrdup(uri);
1664 http->log_uri = xstrdup(uri);
1665 return http;
1666}
1667
7a2f978b 1668/*
1669 * parseHttpRequest()
1670 *
1671 * Returns
1672 * NULL on error or incomplete request
1673 * a clientHttpRequest structure on success
1674 */
1675static clientHttpRequest *
1676parseHttpRequest(ConnStateData * conn, method_t * method_p, int *status,
99edd1c3 1677 char **prefix_p, size_t *req_line_sz_p)
7a2f978b 1678{
1679 char *inbuf = NULL;
1680 char *mstr = NULL;
1681 char *url = NULL;
1682 char *req_hdr = NULL;
7a2f978b 1683 float http_ver;
1684 char *token = NULL;
1685 char *t = NULL;
2334c194 1686 char *end;
7a2f978b 1687 int free_request = 0;
1688 size_t header_sz; /* size of headers, not including first line */
99edd1c3 1689 size_t prefix_sz; /* size of whole request (req-line + headers)*/
7a2f978b 1690 size_t url_sz;
1691 method_t method;
1692 clientHttpRequest *http = NULL;
1693
1694 /* Make sure a complete line has been received */
99edd1c3 1695 if ((t = strchr(conn->in.buf, '\n')) == NULL) {
93f9abd0 1696 debug(33, 5) ("Incomplete request line, waiting for more data\n");
7a2f978b 1697 *status = 0;
1698 return NULL;
1699 }
99edd1c3 1700 *req_line_sz_p = t - conn->in.buf;
7a2f978b 1701 /* Use xmalloc/xmemcpy instead of xstrdup because inbuf might
1702 * contain NULL bytes; especially for POST data */
1703 inbuf = xmalloc(conn->in.offset + 1);
1704 xmemcpy(inbuf, conn->in.buf, conn->in.offset);
1705 *(inbuf + conn->in.offset) = '\0';
1706
99edd1c3 1707 /* pre-set these values to make aborting simpler */
1708 *prefix_p = inbuf;
1709 *method_p = METHOD_NONE;
1710 *status = -1;
1711
7a2f978b 1712 /* Look for request method */
1713 if ((mstr = strtok(inbuf, "\t ")) == NULL) {
93f9abd0 1714 debug(33, 1) ("parseHttpRequest: Can't get request method\n");
99edd1c3 1715 return parseHttpRequestAbort(conn, "error:invalid-request-method");
7a2f978b 1716 }
1717 method = urlParseMethod(mstr);
1718 if (method == METHOD_NONE) {
93f9abd0 1719 debug(33, 1) ("parseHttpRequest: Unsupported method '%s'\n", mstr);
99edd1c3 1720 return parseHttpRequestAbort(conn, "error:unsupported-request-method");
7a2f978b 1721 }
93f9abd0 1722 debug(33, 5) ("parseHttpRequest: Method is '%s'\n", mstr);
3775d53c 1723 *method_p = method;
7a2f978b 1724
1725 /* look for URL */
1726 if ((url = strtok(NULL, "\r\n\t ")) == NULL) {
93f9abd0 1727 debug(33, 1) ("parseHttpRequest: Missing URL\n");
99edd1c3 1728 return parseHttpRequestAbort(conn, "error:missing-url");
7a2f978b 1729 }
93f9abd0 1730 debug(33, 5) ("parseHttpRequest: Request is '%s'\n", url);
7a2f978b 1731
1732 token = strtok(NULL, null_string);
1733 for (t = token; t && *t && *t != '\n' && *t != '\r'; t++);
46c7dbf0 1734 if (t == NULL || *t == '\0' || t == token || strncmp(token, "HTTP/", 5)) {
93f9abd0 1735 debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
46052490 1736#if RELAXED_HTTP_PARSER
1737 http_ver = (float) 0.9; /* wild guess */
1738#else
99edd1c3 1739 return parseHttpRequestAbort(conn, "error:missing-http-ident");
7518fce5 1740#endif
99edd1c3 1741 } else {
7518fce5 1742 http_ver = (float) atof(token + 5);
99edd1c3 1743 }
7a2f978b 1744
1745 /* Check if headers are received */
754b9ce9 1746 req_hdr = t;
8d9810cd 1747 header_sz = headersEnd(req_hdr, conn->in.offset - (req_hdr - inbuf));
2334c194 1748 if (0 == header_sz) {
1a92a1e2 1749 debug(33, 3) ("parseHttpRequest: header_sz == 0\n");
7a2f978b 1750 xfree(inbuf);
1751 *status = 0;
1752 return NULL;
1753 }
754b9ce9 1754 /*
99edd1c3 1755 * Skip whitespace at the end of the first line, up to the
754b9ce9 1756 * first newline.
1757 */
1758 while (isspace(*req_hdr)) {
1759 header_sz--;
1760 if (*(req_hdr++) == '\n')
1761 break;
1762 }
1763 assert(header_sz > 0);
1764 debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
2334c194 1765 end = req_hdr + header_sz;
04990e2d 1766 debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
99edd1c3 1767
1768#if UNREACHABLE_CODE
1769 if (end <= req_hdr) {
1770 /* Invalid request */
1771 debug(33, 3) ("parseHttpRequest: No request headers?\n");
1772 return parseHttpRequestAbort(conn, "error:no-request-headers");
1773 }
1774#endif
1775 prefix_sz = end - inbuf;
1776 *req_line_sz_p = req_hdr - inbuf;
1777 debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
1778 (int) prefix_sz, (int) *req_line_sz_p);
1779 assert(prefix_sz <= conn->in.offset);
7a2f978b 1780
1781 /* Ok, all headers are received */
1782 http = xcalloc(1, sizeof(clientHttpRequest));
3f6c0fb2 1783 cbdataAdd(http, MEM_NONE);
7a2f978b 1784 http->http_ver = http_ver;
1785 http->conn = conn;
1786 http->start = current_time;
99edd1c3 1787 http->req_sz = prefix_sz;
1788 *prefix_p = xmalloc(prefix_sz + 1);
1789 xmemcpy(*prefix_p, conn->in.buf, prefix_sz);
1790 *(*prefix_p + prefix_sz) = '\0';
7a2f978b 1791
99edd1c3 1792 debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n", (*prefix_p)+*req_line_sz_p);
23d92c64 1793 /* Assign http->uri */
7a2f978b 1794 if ((t = strchr(url, '\n'))) /* remove NL */
1795 *t = '\0';
1796 if ((t = strchr(url, '\r'))) /* remove CR */
1797 *t = '\0';
1798 if ((t = strchr(url, '#'))) /* remove HTML anchors */
1799 *t = '\0';
1800
4162ee3b 1801 /* handle internal objects */
1da5651f 1802 if (internalCheck(url)) {
4162ee3b 1803 /* prepend our name & port */
1da5651f 1804 http->uri = xstrdup(internalLocalUri(NULL, url));
77ed547a 1805 http->flags.internal = 1;
4162ee3b 1806 }
7a2f978b 1807 /* see if we running in Config2.Accel.on, if so got to convert it to URL */
4162ee3b 1808 else if (Config2.Accel.on && *url == '/') {
7a2f978b 1809 /* prepend the accel prefix */
dbfed404 1810 if (opt_accel_uses_host && (t = mime_get_header(req_hdr, "Host"))) {
7a2f978b 1811 /* If a Host: header was specified, use it to build the URL
1812 * instead of the one in the Config file. */
1813 /*
1814 * XXX Use of the Host: header here opens a potential
1815 * security hole. There are no checks that the Host: value
1816 * corresponds to one of your servers. It might, for example,
1817 * refer to www.playboy.com. The 'dst' and/or 'dst_domain' ACL
1818 * types should be used to prevent httpd-accelerators
1819 * handling requests for non-local servers */
1820 strtok(t, " :/;@");
12fc602c 1821 url_sz = strlen(url) + 32 + Config.appendDomainLen +
1822 strlen(t);
23d92c64 1823 http->uri = xcalloc(url_sz, 1);
1824 snprintf(http->uri, url_sz, "http://%s:%d%s",
7a2f978b 1825 t, (int) Config.Accel.port, url);
dbfed404 1826 } else if (vhost_mode) {
1827 /* Put the local socket IP address as the hostname */
1828 url_sz = strlen(url) + 32 + Config.appendDomainLen;
1829 http->uri = xcalloc(url_sz, 1);
1830 snprintf(http->uri, url_sz, "http://%s:%d%s",
1831 inet_ntoa(http->conn->me.sin_addr),
1832 (int) Config.Accel.port,
1833 url);
93f9abd0 1834 debug(33, 5) ("VHOST REWRITE: '%s'\n", http->uri);
7a2f978b 1835 } else {
1836 url_sz = strlen(Config2.Accel.prefix) + strlen(url) +
1837 Config.appendDomainLen + 1;
23d92c64 1838 http->uri = xcalloc(url_sz, 1);
1839 snprintf(http->uri, url_sz, "%s%s", Config2.Accel.prefix, url);
7a2f978b 1840 }
77ed547a 1841 http->flags.accel = 1;
7a2f978b 1842 } else {
1843 /* URL may be rewritten later, so make extra room */
1844 url_sz = strlen(url) + Config.appendDomainLen + 5;
23d92c64 1845 http->uri = xcalloc(url_sz, 1);
1846 strcpy(http->uri, url);
77ed547a 1847 http->flags.accel = 0;
7a2f978b 1848 }
23d92c64 1849 http->log_uri = xstrdup(http->uri);
93f9abd0 1850 debug(33, 5) ("parseHttpRequest: Complete request received\n");
7a2f978b 1851 if (free_request)
1852 safe_free(url);
1853 xfree(inbuf);
7a2f978b 1854 *status = 1;
1855 return http;
1856}
1857
1858static int
79d39a72 1859clientReadDefer(int fdnotused, void *data)
7a2f978b 1860{
1861 ConnStateData *conn = data;
1862 return conn->defer.until > squid_curtime;
1863}
1864
1865static void
1866clientReadRequest(int fd, void *data)
1867{
1868 ConnStateData *conn = data;
1869 int parser_return_code = 0;
1870 int k;
1871 request_t *request = NULL;
7a2f978b 1872 int size;
7a2f978b 1873 method_t method;
1874 clientHttpRequest *http = NULL;
1875 clientHttpRequest **H = NULL;
99edd1c3 1876 char *prefix;
7a2f978b 1877 ErrorState *err = NULL;
1878 fde *F = &fd_table[fd];
44db45e8 1879 int len = conn->in.size - conn->in.offset - 1;
93f9abd0 1880 debug(33, 4) ("clientReadRequest: FD %d: reading request...\n", fd);
7a2f978b 1881 size = read(fd, conn->in.buf + conn->in.offset, len);
d8b83480 1882 if (size > 0) {
1883 fd_bytes(fd, size, FD_READ);
1884 kb_incr(&Counter.client_http.kbytes_in, size);
1885 }
44db45e8 1886 /*
1887 * Don't reset the timeout value here. The timeout value will be
1888 * set to Config.Timeout.request by httpAccept() and
1889 * clientWriteComplete(), and should apply to the request as a
1890 * whole, not individual read() calls. Plus, it breaks our
1891 * lame half-close detection
1892 */
1893 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
7a2f978b 1894 if (size == 0) {
1895 if (conn->chr == NULL) {
1896 /* no current or pending requests */
1897 comm_close(fd);
1898 return;
1899 }
1900 /* It might be half-closed, we can't tell */
93f9abd0 1901 debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd);
79a15e0a 1902 EBIT_SET(F->flags, FD_SOCKET_EOF);
7a2f978b 1903 conn->defer.until = squid_curtime + 1;
1904 conn->defer.n++;
44db45e8 1905 fd_note(fd, "half-closed");
7a2f978b 1906 return;
1907 } else if (size < 0) {
6d3caf23 1908 if (!ignoreErrno(errno)) {
7a2f978b 1909 debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd, xstrerror());
1910 comm_close(fd);
ebf4efff 1911 return;
47130615 1912 } else if (conn->in.offset == 0) {
7c1d4010 1913 debug(50, 2) ("clientReadRequest: FD %d: no data to process (%s)\n", fd, xstrerror());
ebf4efff 1914 return;
7a2f978b 1915 }
ebf4efff 1916 /* Continue to process previously read data */
47130615 1917 size = 0;
7a2f978b 1918 }
1919 conn->in.offset += size;
5ede6c8f 1920 /* Skip leading (and trailing) whitespace */
1921 while (conn->in.offset > 0 && isspace(conn->in.buf[0])) {
1922 xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.offset - 1);
1923 conn->in.offset--;
1924 }
7a2f978b 1925 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
7a2f978b 1926 while (conn->in.offset > 0) {
ebf4efff 1927 int nrequests;
cff0c749 1928 size_t req_line_sz;
ebf4efff 1929 /* Limit the number of concurrent requests to 2 */
1930 for (H = &conn->chr, nrequests = 0; *H; H = &(*H)->next, nrequests++);
1931 if (nrequests >= 2) {
93f9abd0 1932 debug(33, 2) ("clientReadRequest: FD %d max concurrent requests reached\n", fd);
1933 debug(33, 5) ("clientReadRequest: FD %d defering new request until one is done\n", fd);
47130615 1934 conn->defer.until = squid_curtime + 100; /* Reset when a request is complete */
ebf4efff 1935 break;
1936 }
1937 /* Process request */
7a2f978b 1938 http = parseHttpRequest(conn,
1939 &method,
1940 &parser_return_code,
99edd1c3 1941 &prefix,
1942 &req_line_sz);
7a2f978b 1943 if (http) {
1944 assert(http->req_sz > 0);
1945 conn->in.offset -= http->req_sz;
1946 assert(conn->in.offset >= 0);
6fa92aa2 1947 /*
1948 * If we read past the end of this request, move the remaining
1949 * data to the beginning
1950 */
1951 if (conn->in.offset > 0)
dbfed404 1952 xmemmove(conn->in.buf, conn->in.buf + http->req_sz, conn->in.offset);
ebf4efff 1953 /* add to the client request queue */
7a2f978b 1954 for (H = &conn->chr; *H; H = &(*H)->next);
1955 *H = http;
1956 conn->nrequests++;
1957 commSetTimeout(fd, Config.Timeout.lifetime, NULL, NULL);
3775d53c 1958 if (parser_return_code < 0) {
93f9abd0 1959 debug(33, 1) ("clientReadRequest: FD %d Invalid Request\n", fd);
3775d53c 1960 err = errorCon(ERR_INVALID_REQ, HTTP_BAD_REQUEST);
1961 err->request_hdrs = xstrdup(conn->in.buf);
1962 http->entry = clientCreateStoreEntry(http, method, 0);
1963 errorAppendEntry(http->entry, err);
1964 break;
1965 }
23d92c64 1966 if ((request = urlParse(method, http->uri)) == NULL) {
93f9abd0 1967 debug(33, 5) ("Invalid URL: %s\n", http->uri);
7a2f978b 1968 err = errorCon(ERR_INVALID_URL, HTTP_BAD_REQUEST);
1969 err->src_addr = conn->peer.sin_addr;
23d92c64 1970 err->url = xstrdup(http->uri);
7a2f978b 1971 http->al.http.code = err->http_status;
f44af445 1972 http->entry = clientCreateStoreEntry(http, method, 0);
79e0dc20 1973 errorAppendEntry(http->entry, err);
99edd1c3 1974 safe_free(prefix);
7a2f978b 1975 break;
99edd1c3 1976 } else {
1977 /* compile headers */
1978 /* we should skip request line! */
1979 if (!httpRequestParseHeader(request, prefix+req_line_sz))
1980 debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
1981 http->uri, prefix);
1982 /* continue anyway? */
7a2f978b 1983 }
77ed547a 1984 if (!http->flags.internal)
7c81606a 1985 if (internalCheck(strBuf(request->urlpath)))
5a1bc610 1986 if (0 == strcasecmp(request->host, getMyHostname()))
4b4cd312 1987 if (request->port == Config.Port.http->i)
1988 http->flags.internal = 1;
23d92c64 1989 safe_free(http->log_uri);
1990 http->log_uri = xstrdup(urlCanonicalClean(request));
7a2f978b 1991 request->client_addr = conn->peer.sin_addr;
1992 request->http_ver = http->http_ver;
99edd1c3 1993 request->prefix = prefix;
1994 request->prefix_sz = http->req_sz;
7a2f978b 1995 if (!urlCheckRequest(request)) {
1996 err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED);
1997 err->src_addr = conn->peer.sin_addr;
7a2f978b 1998 err->request = requestLink(request);
1999 http->al.http.code = err->http_status;
f44af445 2000 http->entry = clientCreateStoreEntry(http, request->method, 0);
79e0dc20 2001 errorAppendEntry(http->entry, err);
3775d53c 2002 break;
7a2f978b 2003 }
31be8b80 2004 if (0 == clientCheckContentLength(request)) {
2005 err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED);
2006 err->src_addr = conn->peer.sin_addr;
2007 err->request = requestLink(request);
2008 http->al.http.code = err->http_status;
2009 http->entry = clientCreateStoreEntry(http, request->method, 0);
2010 errorAppendEntry(http->entry, err);
2011 break;
2012 }
7a2f978b 2013 http->request = requestLink(request);
cf26e54c 2014 clientAccessCheck(http);
3775d53c 2015 /*
2016 * break here for NON-GET because most likely there is a
7a2f978b 2017 * reqeust body following and we don't want to parse it
3775d53c 2018 * as though it was new request
2019 */
7a2f978b 2020 if (request->method != METHOD_GET) {
2021 if (conn->in.offset) {
2022 request->body_sz = conn->in.offset;
2023 request->body = xmalloc(request->body_sz);
2024 xmemcpy(request->body, conn->in.buf, request->body_sz);
2025 conn->in.offset = 0;
2026 }
2027 break;
2028 }
7a2f978b 2029 continue; /* while offset > 0 */
2030 } else if (parser_return_code == 0) {
2031 /*
2032 * Partial request received; reschedule until parseHttpRequest()
2033 * is happy with the input
2034 */
2035 k = conn->in.size - 1 - conn->in.offset;
2036 if (k == 0) {
2037 if (conn->in.offset >= Config.maxRequestSize) {
2038 /* The request is too large to handle */
93f9abd0 2039 debug(33, 0) ("Request won't fit in buffer.\n");
2040 debug(33, 0) ("Config 'request_size'= %d bytes.\n",
7a2f978b 2041 Config.maxRequestSize);
93f9abd0 2042 debug(33, 0) ("This request = %d bytes.\n",
5f6ac48b 2043 (int) conn->in.offset);
7a2f978b 2044 err = errorCon(ERR_INVALID_REQ, HTTP_REQUEST_ENTITY_TOO_LARGE);
f44af445 2045 http->entry = clientCreateStoreEntry(http, request->method, 0);
79e0dc20 2046 errorAppendEntry(http->entry, err);
7a2f978b 2047 return;
2048 }
2049 /* Grow the request memory area to accomodate for a large request */
2050 conn->in.size += REQUEST_BUF_SIZE;
2051 conn->in.buf = xrealloc(conn->in.buf, conn->in.size);
59c4d35b 2052 /* XXX account conn->in.buf */
93f9abd0 2053 debug(33, 2) ("Handling a large request, offset=%d inbufsize=%d\n",
5f6ac48b 2054 (int) conn->in.offset, conn->in.size);
7a2f978b 2055 k = conn->in.size - 1 - conn->in.offset;
2056 }
7a2f978b 2057 break;
7a2f978b 2058 }
2059 }
2060}
2061
2062/* general lifetime handler for HTTP requests */
2063static void
2064requestTimeout(int fd, void *data)
2065{
2066 ConnStateData *conn = data;
2067 ErrorState *err;
93f9abd0 2068 debug(33, 2) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
7a2f978b 2069 if (fd_table[fd].rwstate) {
79e0dc20 2070 /*
2071 * Some data has been sent to the client, just close the FD
2072 */
7a2f978b 2073 comm_close(fd);
2074 } else if (conn->nrequests) {
79e0dc20 2075 /*
2076 * assume its a persistent connection; just close it
2077 */
7a2f978b 2078 comm_close(fd);
2079 } else {
79e0dc20 2080 /*
2081 * Generate an error
2082 */
7a2f978b 2083 err = errorCon(ERR_LIFETIME_EXP, HTTP_REQUEST_TIMEOUT);
7a2f978b 2084 err->url = xstrdup("N/A");
79e0dc20 2085 /*
2086 * Normally we shouldn't call errorSend() in client_side.c, but
2087 * it should be okay in this case. Presumably if we get here
2088 * this is the first request for the connection, and no data
2089 * has been written yet
2090 */
2091 assert(conn->chr == NULL);
7a2f978b 2092 errorSend(fd, err);
79e0dc20 2093 /*
2094 * if we don't close() here, we still need a timeout handler!
2095 */
7a2f978b 2096 commSetTimeout(fd, 30, requestTimeout, conn);
2097 }
2098}
2099
2100int
79d39a72 2101httpAcceptDefer(int fdnotused, void *notused)
7a2f978b 2102{
22f5d1ca 2103 return fdNFree() < RESERVED_FD;
7a2f978b 2104}
2105
bf8e5903 2106/* Handle a new connection on HTTP socket. */
7a2f978b 2107void
2108httpAccept(int sock, void *notused)
2109{
2110 int fd = -1;
2111 ConnStateData *connState = NULL;
2112 struct sockaddr_in peer;
2113 struct sockaddr_in me;
2114 memset(&peer, '\0', sizeof(struct sockaddr_in));
2115 memset(&me, '\0', sizeof(struct sockaddr_in));
2116 commSetSelect(sock, COMM_SELECT_READ, httpAccept, NULL, 0);
2117 if ((fd = comm_accept(sock, &peer, &me)) < 0) {
2118 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
2119 sock, xstrerror());
2120 return;
2121 }
93f9abd0 2122 debug(33, 4) ("httpAccept: FD %d: accepted\n", fd);
7a2f978b 2123 connState = xcalloc(1, sizeof(ConnStateData));
2124 connState->peer = peer;
2125 connState->log_addr = peer.sin_addr;
2126 connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
2127 connState->me = me;
2128 connState->fd = fd;
2129 connState->ident.fd = -1;
2130 connState->in.size = REQUEST_BUF_SIZE;
2131 connState->in.buf = xcalloc(connState->in.size, 1);
3f6c0fb2 2132 cbdataAdd(connState, MEM_NONE);
59c4d35b 2133 /* XXX account connState->in.buf */
7a2f978b 2134 comm_add_close_handler(fd, connStateFree, connState);
2135 if (Config.onoff.log_fqdn)
2136 fqdncache_gethostbyaddr(peer.sin_addr, FQDN_LOOKUP_IF_MISS);
2137 commSetTimeout(fd, Config.Timeout.request, requestTimeout, connState);
2138 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0);
2139 commSetDefer(fd, clientReadDefer, connState);
2140}
2141
7a2f978b 2142/* return 1 if the request should be aborted */
2143static int
2144CheckQuickAbort2(const clientHttpRequest * http)
2145{
4efca7da 2146 int curlen;
2147 int minlen;
2148 int expectlen;
7a2f978b 2149
79a15e0a 2150 if (!EBIT_TEST(http->request->flags, REQ_CACHABLE))
7a2f978b 2151 return 1;
79a15e0a 2152 if (EBIT_TEST(http->entry->flag, KEY_PRIVATE))
7a2f978b 2153 return 1;
2154 if (http->entry->mem_obj == NULL)
2155 return 1;
038eb4ed 2156 expectlen = http->entry->mem_obj->reply->content_length;
4efca7da 2157 curlen = (int) http->entry->mem_obj->inmem_hi;
2158 minlen = (int) Config.quickAbort.min;
7a2f978b 2159 if (minlen < 0)
2160 /* disabled */
2161 return 0;
2162 if (curlen > expectlen)
2163 /* bad content length */
2164 return 1;
2165 if ((expectlen - curlen) < minlen)
2166 /* only little more left */
2167 return 0;
2168 if ((expectlen - curlen) > Config.quickAbort.max)
2169 /* too much left to go */
2170 return 1;
4efca7da 2171 if (expectlen < 128)
2172 /* avoid FPE */
2173 return 0;
2174 if ((curlen / (expectlen / QUICK_ABORT_100PCT)) > Config.quickAbort.pct)
7a2f978b 2175 /* past point of no return */
2176 return 0;
2177 return 1;
2178}
2179
2180
2181static void
2182CheckQuickAbort(clientHttpRequest * http)
2183{
2184 StoreEntry *entry = http->entry;
2185 /* Note, set entry here because http->entry might get changed (for IMS
2186 * requests) during the storeAbort() call */
2187 if (entry == NULL)
2188 return;
36547bcf 2189 if (storePendingNClients(entry) > 0)
7a2f978b 2190 return;
2191 if (entry->store_status != STORE_PENDING)
2192 return;
2193 if (CheckQuickAbort2(http) == 0)
2194 return;
93f9abd0 2195 debug(33, 3) ("CheckQuickAbort: ABORTING %s\n", storeUrl(entry));
7a2f978b 2196 storeAbort(entry, 1);
2197}
2198
978e455f 2199#define SENDING_BODY 0
2200#define SENDING_HDRSONLY 1
7a2f978b 2201static int
1b02b5be 2202clientCheckTransferDone(clientHttpRequest * http)
7a2f978b 2203{
978e455f 2204 int sending = SENDING_BODY;
7a2f978b 2205 StoreEntry *entry = http->entry;
978e455f 2206 MemObject *mem;
2207 http_reply *reply;
2208 int sendlen;
7a2f978b 2209 if (entry == NULL)
2210 return 0;
978e455f 2211 /*
2212 * Handle STORE_OK and STORE_ABORTED objects.
07304bf9 2213 * objectLen(entry) will be set proprely.
978e455f 2214 */
2215 if (entry->store_status != STORE_PENDING) {
07304bf9 2216 if (http->out.offset >= objectLen(entry))
7a2f978b 2217 return 1;
513f05a6 2218 else
2219 return 0;
2220 }
978e455f 2221 /*
2222 * Now, handle STORE_PENDING objects
2223 */
2224 mem = entry->mem_obj;
2225 assert(mem != NULL);
2226 assert(http->request != NULL);
2227 reply = mem->reply;
2228 if (reply->hdr_sz == 0)
b34ed725 2229 return 0; /* haven't found end of headers yet */
cb69b4c7 2230 else if (reply->sline.status == HTTP_OK)
b34ed725 2231 sending = SENDING_BODY;
cb69b4c7 2232 else if (reply->sline.status == HTTP_NO_CONTENT)
b34ed725 2233 sending = SENDING_HDRSONLY;
cb69b4c7 2234 else if (reply->sline.status == HTTP_NOT_MODIFIED)
b34ed725 2235 sending = SENDING_HDRSONLY;
cb69b4c7 2236 else if (reply->sline.status < HTTP_OK)
a3c60429 2237 sending = SENDING_HDRSONLY;
978e455f 2238 else if (http->request->method == METHOD_HEAD)
b34ed725 2239 sending = SENDING_HDRSONLY;
978e455f 2240 else
2241 sending = SENDING_BODY;
2242 /*
2243 * Figure out how much data we are supposed to send.
2244 * If we are sending a body and we don't have a content-length,
2245 * then we must wait for the object to become STORE_OK or
2246 * STORE_ABORTED.
2247 */
2248 if (sending == SENDING_HDRSONLY)
2249 sendlen = reply->hdr_sz;
038eb4ed 2250 else if (reply->content_length < 0)
978e455f 2251 return 0;
2252 else
038eb4ed 2253 sendlen = reply->content_length + reply->hdr_sz;
978e455f 2254 /*
2255 * Now that we have the expected length, did we send it all?
2256 */
2257 if (http->out.offset < sendlen)
2258 return 0;
2259 else
b34ed725 2260 return 1;
7a2f978b 2261}
2262
7a2f978b 2263/*
2264 * This function is designed to serve a fairly specific purpose.
2265 * Occasionally our vBNS-connected caches can talk to each other, but not
2266 * the rest of the world. Here we try to detect frequent failures which
2267 * make the cache unusable (e.g. DNS lookup and connect() failures). If
2268 * the failure:success ratio goes above 1.0 then we go into "hit only"
2269 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
2270 * will only fetch HITs from us if they are using the ICP protocol. We
2271 * stay in this mode for 5 minutes.
2272 *
2273 * Duane W., Sept 16, 1996
2274 */
2275
2276static void
88aad2e5 2277checkFailureRatio(err_type etype, hier_code hcode)
7a2f978b 2278{
88aad2e5 2279 static double magic_factor = 100.0;
7a2f978b 2280 double n_good;
2281 double n_bad;
2282 if (hcode == HIER_NONE)
2283 return;
88aad2e5 2284 n_good = magic_factor / (1.0 + request_failure_ratio);
7a2f978b 2285 n_bad = magic_factor - n_good;
88aad2e5 2286 switch (etype) {
7a2f978b 2287 case ERR_DNS_FAIL:
2288 case ERR_CONNECT_FAIL:
2289 case ERR_READ_ERROR:
2290 n_bad++;
2291 break;
2292 default:
2293 n_good++;
2294 }
88aad2e5 2295 request_failure_ratio = n_bad / n_good;
7a2f978b 2296 if (hit_only_mode_until > squid_curtime)
2297 return;
88aad2e5 2298 if (request_failure_ratio < 1.0)
7a2f978b 2299 return;
93f9abd0 2300 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio);
2301 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
7a2f978b 2302 FAILURE_MODE_TIME / 60);
2303 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
88aad2e5 2304 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
7a2f978b 2305}
15df8349 2306
2307void
2308clientHttpConnectionsOpen(void)
2309{
2310 ushortlist *u;
2311 int fd;
15df8349 2312 for (u = Config.Port.http; u; u = u->next) {
8daca701 2313 enter_suid();
2314 fd = comm_open(SOCK_STREAM,
2315 0,
2316 Config.Addrs.tcp_incoming,
2317 u->i,
2318 COMM_NONBLOCKING,
2319 "HTTP Socket");
2320 leave_suid();
2321 if (fd < 0)
2322 continue;
2323 comm_listen(fd);
2324 commSetSelect(fd, COMM_SELECT_READ, httpAccept, NULL, 0);
2325 commSetDefer(fd, httpAcceptDefer, NULL);
2326 debug(1, 1) ("Accepting HTTP connections on port %d, FD %d.\n",
2327 (int) u->i, fd);
2328 HttpSockets[NHttpSockets++] = fd;
15df8349 2329 }
2330 if (NHttpSockets < 1)
8daca701 2331 fatal("Cannot open HTTP Port");
15df8349 2332}
c0fbae16 2333
ab013258 2334int
2335handleConnectionHeader(int flag, char *where, char *what)
2336{
2337 char *t, *p, *wh;
2338 int i;
2339 LOCAL_ARRAY(char, mbuf, 256);
2340
2341 if (flag) { /* lookup mode */
2342 if (where[0] == '\0' || what[0] == '\0')
2343 return 0;
2344 p = xstrdup(what);
2345 t = strtok(p, ":");
2346 if (t == NULL)
2347 return 0;
2348 debug(20, 3) ("handleConnectionHeader: %s\n AND %s (%p)\n", where, t, p);
2349 i = strstr(where, t) ? 1 : 0;
2350 xfree(p);
2351 return (i);
2352 }
2353 where[0] = '\0';
2354 wh = xstrdup(what);
2355 t = strtok(wh, ",");
2356 while (t != NULL) {
2357
2358#ifdef BE_PARANOID
2359 static char no_conn[] = "Expires:Host:Content-length:Content-type:";
2360
2361 if (handleConnectionHeader(1, no_conn, t)) {
2362 debug(1, 1) ("handleConnectionHeader: problematic header %s\n", t);
2363 t = strtok(NULL, ",\n");
2364 continue;
2365 }
2366#endif
2367 if ((p = strchr(t, ':')))
2368 xstrncpy(mbuf, t, p - t + 1);
2369 else
2370 snprintf(mbuf, 256, "%s:", t);
2371 strcat(where, mbuf);
2372 t = strtok(NULL, ",\n");
2373 }
2374 debug(20, 3) ("handleConnectionHeader: we have %s\n", where);
2375 xfree(wh);
2376 return 1;
2377}
2378
c0fbae16 2379void
2380clientHttpConnectionsClose(void)
2381{
2382 int i;
2383 for (i = 0; i < NHttpSockets; i++) {
2384 if (HttpSockets[i] >= 0) {
2385 debug(1, 1) ("FD %d Closing HTTP connection\n", HttpSockets[i]);
2386 comm_close(HttpSockets[i]);
2387 HttpSockets[i] = -1;
2388 }
2389 }
2390 NHttpSockets = 0;
2391}