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