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