]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
added optional leakfinder code
[thirdparty/squid.git] / src / http.cc
CommitLineData
da2b3a17 1
30a4f2a8 2/*
db1cd23c 3 * $Id: http.cc,v 1.333 1998/12/05 00:54:28 wessels Exp $
30a4f2a8 4 *
5 * DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
6 * AUTHOR: Harvest Derived
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
e25c139f 13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
30a4f2a8 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
30a4f2a8 34 */
019dd986 35
4a83b852 36/*
37 * Anonymizing patch by lutz@as-node.jena.thur.de
de3bdb4c 38 * have a look into http-anon.c to get more informations.
4a83b852 39 */
40
44a47c6e 41#include "squid.h"
090089c4 42
6bf8443a 43static const char *const crlf = "\r\n";
4db43fab 44
9e4ad609 45static CNCB httpConnectDone;
46static CWCB httpSendComplete;
54220df8 47static CWCB httpSendRequestEntry;
48
9e4ad609 49static PF httpReadReply;
50static PF httpSendRequest;
51static PF httpStateFree;
52static PF httpTimeout;
f5b8bbc4 53static void httpCacheNegatively(StoreEntry *);
54static void httpMakePrivate(StoreEntry *);
55static void httpMakePublic(StoreEntry *);
f8309b15 56static int httpCachableReply(HttpStateData *);
f9cece6e 57static void httpMaybeRemovePublic(StoreEntry *, http_status);
b8d8561b 58
b177367b 59static void
79d39a72 60httpStateFree(int fdnotused, void *data)
f5558c95 61{
b177367b 62 HttpStateData *httpState = data;
0d4d4170 63 if (httpState == NULL)
b177367b 64 return;
f88211e8 65 storeUnlockObject(httpState->entry);
0d4d4170 66 if (httpState->reply_hdr) {
db1cd23c 67 memFree(httpState->reply_hdr, MEM_8K_BUF);
0d4d4170 68 httpState->reply_hdr = NULL;
69 }
30a4f2a8 70 requestUnlink(httpState->request);
20cc1450 71 requestUnlink(httpState->orig_request);
7dd44885 72 httpState->request = NULL;
73 httpState->orig_request = NULL;
74 cbdataFree(httpState);
f5558c95 75}
76
b8d8561b 77int
75e88d56 78httpCachable(method_t method)
090089c4 79{
090089c4 80 /* GET and HEAD are cachable. Others are not. */
6eb42cae 81 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 82 return 0;
090089c4 83 /* else cachable */
84 return 1;
85}
86
b8d8561b 87static void
5c5783a2 88httpTimeout(int fd, void *data)
090089c4 89{
b177367b 90 HttpStateData *httpState = data;
593c9a75 91 StoreEntry *entry = httpState->entry;
9fb13bb6 92 debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
8796b9e9 93 assert(entry->store_status == STORE_PENDING);
73a3014d 94 if (entry->mem_obj->inmem_hi == 0) {
db1cd23c 95 fwdFail(httpState->fwd, ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT, 0);
b50179a6 96 } else {
b34ed725 97 storeAbort(entry, 0);
9b312a19 98 }
0d4d4170 99 comm_close(fd);
090089c4 100}
101
30a4f2a8 102/* This object can be cached for a long time */
b8d8561b 103static void
104httpMakePublic(StoreEntry * entry)
30a4f2a8 105{
d46a87a8 106 if (EBIT_TEST(entry->flags, ENTRY_CACHABLE))
30a4f2a8 107 storeSetPublicKey(entry);
108}
109
110/* This object should never be cached at all */
b8d8561b 111static void
112httpMakePrivate(StoreEntry * entry)
30a4f2a8 113{
30a4f2a8 114 storeExpireNow(entry);
30a4f2a8 115 storeReleaseRequest(entry); /* delete object when not used */
f3e570e9 116 /* storeReleaseRequest clears ENTRY_CACHABLE flag */
30a4f2a8 117}
118
119/* This object may be negatively cached */
b8d8561b 120static void
121httpCacheNegatively(StoreEntry * entry)
30a4f2a8 122{
79b5cc5f 123 storeNegativeCache(entry);
d46a87a8 124 if (EBIT_TEST(entry->flags, ENTRY_CACHABLE))
30a4f2a8 125 storeSetPublicKey(entry);
30a4f2a8 126}
127
f9cece6e 128static void
129httpMaybeRemovePublic(StoreEntry * e, http_status status)
130{
131 int remove = 0;
f9cece6e 132 StoreEntry *pe;
d46a87a8 133 if (!EBIT_TEST(e->flags, KEY_PRIVATE))
9dc1202d 134 return;
f9cece6e 135 switch (status) {
136 case HTTP_OK:
137 case HTTP_NON_AUTHORITATIVE_INFORMATION:
138 case HTTP_MULTIPLE_CHOICES:
139 case HTTP_MOVED_PERMANENTLY:
140 case HTTP_MOVED_TEMPORARILY:
141 case HTTP_FORBIDDEN:
142 case HTTP_NOT_FOUND:
143 case HTTP_METHOD_NOT_ALLOWED:
144 case HTTP_GONE:
145 remove = 1;
146 break;
147#if WORK_IN_PROGRESS
c8fd0193 148 case HTTP_UNAUTHORIZED:
149 remove = 1;
f9cece6e 150 break;
151#endif
152 default:
153 remove = 0;
154 break;
155 }
156 if (!remove)
157 return;
158 assert(e->mem_obj);
08e5d64f 159 if ((pe = storeGetPublic(e->mem_obj->url, e->mem_obj->method)) != NULL) {
0856d155 160 assert(e != pe);
161 storeRelease(pe);
162 }
163 if (e->mem_obj->method == METHOD_GET) {
164 /* A fresh GET should eject old HEAD objects */
08e5d64f 165 if ((pe = storeGetPublic(e->mem_obj->url, METHOD_HEAD)) != NULL) {
0856d155 166 assert(e != pe);
167 storeRelease(pe);
168 }
169 }
f9cece6e 170}
171
f8309b15 172static int
173httpCachableReply(HttpStateData * httpState)
c54e9052 174{
d8b249ef 175 HttpReply *rep = httpState->entry->mem_obj->reply;
176 HttpHeader *hdr = &rep->header;
177 const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0;
c68e9c6b 178 const char *v;
7faf2bdb 179 if (EBIT_TEST(cc_mask, CC_PRIVATE))
f8309b15 180 return 0;
7faf2bdb 181 if (EBIT_TEST(cc_mask, CC_NO_CACHE))
f8309b15 182 return 0;
ed2f05a1 183 if (EBIT_TEST(cc_mask, CC_NO_STORE))
184 return 0;
92695e5e 185 if (httpState->request->flags.auth) {
a6dfe2d9 186 /*
187 * Responses to requests with authorization may be cached
68aefb7d 188 * only if a Cache-Control: public reply header is present.
a6dfe2d9 189 * RFC 2068, sec 14.9.4
190 */
191 if (!EBIT_TEST(cc_mask, CC_PUBLIC))
fee0cebb 192 return 0;
a6dfe2d9 193 }
f8309b15 194 /*
02fe0fbc 195 * We don't properly deal with Vary features yet, so we can't
196 * cache these
f8309b15 197 */
783e4699 198 if (httpHeaderHas(hdr, HDR_VARY))
199 return 0;
c68e9c6b 200 /* Pragma: no-cache in _replies_ is not documented in HTTP,
201 * but servers like "Active Imaging Webcast/2.0" sure do use it */
202 if (httpHeaderHas(hdr, HDR_PRAGMA)) {
203 String s = httpHeaderGetList(hdr, HDR_PRAGMA);
204 const int no_cache = strListIsMember(&s, "no-cache", ',');
205 stringClean(&s);
206 if (no_cache)
207 return 0;
208 }
209 /*
210 * The "multipart/x-mixed-replace" content type is used for
211 * continuous push replies. These are generally dynamic and
212 * probably should not be cachable
213 */
214 if ((v = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE)))
215 if (!strncasecmp(v, "multipart/x-mixed-replace", 25))
216 return 0;
cb69b4c7 217 switch (httpState->entry->mem_obj->reply->sline.status) {
c54e9052 218 /* Responses that are cacheable */
19a04dac 219 case HTTP_OK:
220 case HTTP_NON_AUTHORITATIVE_INFORMATION:
221 case HTTP_MULTIPLE_CHOICES:
222 case HTTP_MOVED_PERMANENTLY:
223 case HTTP_GONE:
1294c0fc 224 /* don't cache objects from peers w/o LMT, Date, or Expires */
cb69b4c7 225 /* check that is it enough to check headers @?@ */
d8b249ef 226 if (rep->date > -1)
c54e9052 227 return 1;
d8b249ef 228 else if (rep->last_modified > -1)
c54e9052 229 return 1;
1294c0fc 230 else if (!httpState->peer)
c54e9052 231 return 1;
d8b249ef 232 /* @?@ (here and 302): invalid expires header compiles to squid_curtime */
233 else if (rep->expires > -1)
c54e9052 234 return 1;
c54e9052 235 else
236 return 0;
79d39a72 237 /* NOTREACHED */
c54e9052 238 break;
239 /* Responses that only are cacheable if the server says so */
19a04dac 240 case HTTP_MOVED_TEMPORARILY:
d8b249ef 241 if (rep->expires > -1)
c54e9052 242 return 1;
243 else
244 return 0;
79d39a72 245 /* NOTREACHED */
c54e9052 246 break;
247 /* Errors can be negatively cached */
19a04dac 248 case HTTP_NO_CONTENT:
249 case HTTP_USE_PROXY:
250 case HTTP_BAD_REQUEST:
251 case HTTP_FORBIDDEN:
252 case HTTP_NOT_FOUND:
253 case HTTP_METHOD_NOT_ALLOWED:
254 case HTTP_REQUEST_URI_TOO_LARGE:
255 case HTTP_INTERNAL_SERVER_ERROR:
256 case HTTP_NOT_IMPLEMENTED:
257 case HTTP_BAD_GATEWAY:
258 case HTTP_SERVICE_UNAVAILABLE:
259 case HTTP_GATEWAY_TIMEOUT:
c54e9052 260 return -1;
79d39a72 261 /* NOTREACHED */
c54e9052 262 break;
263 /* Some responses can never be cached */
0cdcddb9 264 case HTTP_PARTIAL_CONTENT: /* Not yet supported */
19a04dac 265 case HTTP_SEE_OTHER:
266 case HTTP_NOT_MODIFIED:
267 case HTTP_UNAUTHORIZED:
268 case HTTP_PROXY_AUTHENTICATION_REQUIRED:
0cdcddb9 269 case HTTP_INVALID_HEADER: /* Squid header parsing error */
c54e9052 270 default: /* Unknown status code */
271 return 0;
79d39a72 272 /* NOTREACHED */
c54e9052 273 break;
274 }
79d39a72 275 /* NOTREACHED */
c54e9052 276}
090089c4 277
cb69b4c7 278/* rewrite this later using new interfaces @?@ */
b8d8561b 279void
0ee4272b 280httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 281{
282 char *t = NULL;
30a4f2a8 283 StoreEntry *entry = httpState->entry;
d3fb4dea 284 int room;
285 int hdr_len;
cb69b4c7 286 HttpReply *reply = entry->mem_obj->reply;
b6cfb65c 287 debug(11, 3) ("httpProcessReplyHeader: key '%s'\n",
288 storeKeyText(entry->key));
e924600d 289 if (httpState->reply_hdr == NULL)
7021844c 290 httpState->reply_hdr = memAllocate(MEM_8K_BUF);
30a4f2a8 291 if (httpState->reply_hdr_state == 0) {
292 hdr_len = strlen(httpState->reply_hdr);
ed85b771 293 room = 8191 - hdr_len;
30a4f2a8 294 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 295 hdr_len += room < size ? room : size;
30a4f2a8 296 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
84fa351c 297 debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr);
30a4f2a8 298 httpState->reply_hdr_state += 2;
728da2ee 299 reply->sline.status = HTTP_INVALID_HEADER;
ed85b771 300 return;
d3fb4dea 301 }
d1a43e28 302 t = httpState->reply_hdr + hdr_len;
303 /* headers can be incomplete only if object still arriving */
2334c194 304 if (!httpState->eof) {
305 size_t k = headersEnd(httpState->reply_hdr, 8192);
306 if (0 == k)
d1a43e28 307 return; /* headers not complete */
2334c194 308 t = httpState->reply_hdr + k;
309 }
2285407f 310 *t = '\0';
30a4f2a8 311 httpState->reply_hdr_state++;
f5558c95 312 }
30a4f2a8 313 if (httpState->reply_hdr_state == 1) {
123abbe1 314 const Ctx ctx = ctx_enter(entry->mem_obj->url);
30a4f2a8 315 httpState->reply_hdr_state++;
a3d5953d 316 debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 317 httpState->reply_hdr);
318 /* Parse headers into reply structure */
2246b732 319 /* what happens if we fail to parse here? */
ee1679df 320 httpReplyParse(reply, httpState->reply_hdr); /* httpState->eof); */
ca98227c 321 storeTimestampsSet(entry);
30a4f2a8 322 /* Check if object is cacheable or not based on reply code */
cb69b4c7 323 debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status);
9f239bed 324 if (neighbors_do_private_keys)
325 httpMaybeRemovePublic(entry, reply->sline.status);
f8309b15 326 switch (httpCachableReply(httpState)) {
c54e9052 327 case 1:
328 httpMakePublic(entry);
30a4f2a8 329 break;
c54e9052 330 case 0:
331 httpMakePrivate(entry);
f5558c95 332 break;
c54e9052 333 case -1:
851eeef7 334 httpCacheNegatively(entry);
30a4f2a8 335 break;
c54e9052 336 default:
337 assert(0);
4e38e700 338 break;
f5558c95 339 }
0336304c 340 if (reply->cache_control) {
341 if (EBIT_TEST(reply->cache_control->mask, CC_PROXY_REVALIDATE))
d46a87a8 342 EBIT_SET(entry->flags, ENTRY_REVALIDATE);
308e4a84 343 else if (EBIT_TEST(reply->cache_control->mask, CC_MUST_REVALIDATE))
d46a87a8 344 EBIT_SET(entry->flags, ENTRY_REVALIDATE);
0336304c 345 }
b515fc11 346 if (httpState->flags.keepalive)
9a47da71 347 if (httpState->peer)
348 httpState->peer->stats.n_keepalives_sent++;
9f5a2895 349 if (reply->keep_alive)
1294c0fc 350 if (httpState->peer)
351 httpState->peer->stats.n_keepalives_recv++;
123abbe1 352 ctx_exit(ctx);
fc4d5b17 353 if (reply->date > -1 && !httpState->peer) {
354 int skew = abs(reply->date - squid_curtime);
e1c811de 355 if (skew > 86400)
356 debug(11, 3) ("%s's clock is skewed by %d seconds!\n",
7d47d8e6 357 httpState->request->host, skew);
fc4d5b17 358 }
f5558c95 359 }
360}
361
603a02fd 362static int
363httpPconnTransferDone(HttpStateData * httpState)
364{
365 /* return 1 if we got the last of the data on a persistent connection */
366 MemObject *mem = httpState->entry->mem_obj;
cb69b4c7 367 HttpReply *reply = mem->reply;
51fdcbd5 368 debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd);
978e455f 369 /*
99edd1c3 370 * If we didn't send a keep-alive request header, then this
978e455f 371 * can not be a persistent connection.
372 */
b515fc11 373 if (!httpState->flags.keepalive)
603a02fd 374 return 0;
9f5a2895 375 /*
376 * What does the reply have to say about keep-alive?
377 */
378 if (!reply->keep_alive)
379 return 0;
51fdcbd5 380 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
d8b249ef 381 reply->content_length);
603a02fd 382 /*
978e455f 383 * Deal with gross HTTP stuff
384 * - If we haven't seen the end of the reply headers, we can't
385 * be persistent.
0856d155 386 * - For HEAD requests we're done.
978e455f 387 * - For "200 OK" check the content-length in the next block.
978e455f 388 * - For "204 No Content" (even with content-length) we're done.
389 * - For "304 Not Modified" (even with content-length) we're done.
a3c60429 390 * - 1XX replies never have a body; we're done.
a3c60429 391 * - For all other replies, check content length in next block.
603a02fd 392 */
978e455f 393 if (httpState->reply_hdr_state < 2)
394 return 0;
0856d155 395 else if (httpState->request->method == METHOD_HEAD)
396 return 1;
cb69b4c7 397 else if (reply->sline.status == HTTP_OK)
a3c60429 398 (void) 0; /* common case, continue */
cb69b4c7 399 else if (reply->sline.status == HTTP_NO_CONTENT)
978e455f 400 return 1;
cb69b4c7 401 else if (reply->sline.status == HTTP_NOT_MODIFIED)
978e455f 402 return 1;
cb69b4c7 403 else if (reply->sline.status < HTTP_OK)
a3c60429 404 return 1;
603a02fd 405 /*
a3c60429 406 * If there is no content-length, then we can't be
978e455f 407 * persistent. If there is a content length, then we must
408 * wait until we've seen the end of the body.
603a02fd 409 */
d8b249ef 410 if (reply->content_length < 0)
603a02fd 411 return 0;
d8b249ef 412 else if (mem->inmem_hi < reply->content_length + reply->hdr_sz)
603a02fd 413 return 0;
978e455f 414 else
b34ed725 415 return 1;
603a02fd 416}
090089c4 417
418/* This will be called when data is ready to be read from fd. Read until
419 * error or connection closed. */
f5558c95 420/* XXX this function is too long! */
b8d8561b 421static void
b177367b 422httpReadReply(int fd, void *data)
090089c4 423{
b177367b 424 HttpStateData *httpState = data;
95d659f0 425 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
bfcaf585 426 StoreEntry *entry = httpState->entry;
603a02fd 427 const request_t *request = httpState->request;
090089c4 428 int len;
30a4f2a8 429 int bin;
090089c4 430 int clen;
447e176b 431 size_t read_sz;
432#if DELAY_POOLS
433 delay_id delay_id = delayMostBytesAllowed(entry->mem_obj);
434#endif
234967c9 435 /* check if we want to defer reading */
1513873c 436 errno = 0;
447e176b 437 read_sz = SQUID_TCP_SO_RCVBUF;
438#if DELAY_POOLS
56e64999 439 read_sz = delayBytesWanted(delay_id, 1, read_sz);
447e176b 440#endif
886f2785 441 Counter.syscalls.sock.reads++;
447e176b 442 len = read(fd, buf, read_sz);
a3d5953d 443 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 444 if (len > 0) {
ee1679df 445 fd_bytes(fd, len, FD_READ);
447e176b 446#if DELAY_POOLS
447 delayBytesIn(delay_id, len);
448#endif
a0f32775 449 kb_incr(&Counter.server.all.kbytes_in, len);
450 kb_incr(&Counter.server.http.kbytes_in, len);
4f92c80c 451 commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
4a63c85f 452 IOStats.Http.reads++;
30a4f2a8 453 for (clen = len - 1, bin = 0; clen; bin++)
454 clen >>= 1;
455 IOStats.Http.read_hist[bin]++;
456 }
5ede6c8f 457 if (!httpState->reply_hdr && len > 0) {
458 /* Skip whitespace */
459 while (len > 0 && isspace(*buf))
460 xmemmove(buf, buf + 1, len--);
461 if (len == 0) {
462 /* Continue to read... */
463 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
464 return;
465 }
466 }
ba718c8f 467 if (len < 0) {
55cb44f1 468 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
469 fd, xstrerror());
b224ea98 470 if (ignoreErrno(errno)) {
9b312a19 471 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
910169e5 472 } else if (entry->mem_obj->inmem_hi == 0) {
db1cd23c 473 fwdFail(httpState->fwd, ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR, errno);
1afe05c5 474 comm_close(fd);
090089c4 475 } else {
55cb44f1 476 storeAbort(entry, 0);
0d4d4170 477 comm_close(fd);
090089c4 478 }
8350fe9b 479 } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
db1cd23c 480 fwdFail(httpState->fwd, ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE, errno);
910169e5 481 httpState->eof = 1;
482 comm_close(fd);
090089c4 483 } else if (len == 0) {
484 /* Connection closed; retrieval done. */
f86a6a46 485 httpState->eof = 1;
d1a43e28 486 if (httpState->reply_hdr_state < 2)
b34ed725 487 /*
488 * Yes Henrik, there is a point to doing this. When we
489 * called httpProcessReplyHeader() before, we didn't find
490 * the end of headers, but now we are definately at EOF, so
491 * we want to process the reply headers.
492 */
d1a43e28 493 httpProcessReplyHeader(httpState, buf, len);
db1cd23c 494 fwdComplete(httpState->fwd);
0d4d4170 495 comm_close(fd);
090089c4 496 } else {
7e3e1d01 497 if (httpState->reply_hdr_state < 2) {
30a4f2a8 498 httpProcessReplyHeader(httpState, buf, len);
db1cd23c 499 if (httpState->reply_hdr_state == 2) {
500 http_status s = entry->mem_obj->reply->sline.status;
501 /* If its "successful" reply, allow the client
502 * to get it
503 */
504 if (s >= 200 && s < 300)
505 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
506 }
7e3e1d01 507 }
620da955 508 storeAppend(entry, buf, len);
9d66d521 509#ifdef OPTIMISTIC_IO
510 if (entry->store_status == STORE_ABORTED) {
511 /*
512 * the above storeAppend() call could ABORT this entry,
513 * in that case, the server FD should already be closed.
514 * there's nothing for us to do.
515 */
516 (void) 0;
517 } else
518#endif
603a02fd 519 if (httpPconnTransferDone(httpState)) {
5b29969a 520 /* yes we have to clear all these! */
8796b9e9 521 commSetDefer(fd, NULL, NULL);
5b29969a 522 commSetTimeout(fd, -1, NULL, NULL);
523 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
603a02fd 524 comm_remove_close_handler(fd, httpStateFree, httpState);
db1cd23c 525 fwdComplete(httpState->fwd);
526 /* call fwdComplete BEFORE fwdUnregister or else fwdUnregister
52f0d243 527 * will storeAbort */
db1cd23c 528 fwdUnregister(fd, httpState->fwd);
8796b9e9 529 pconnPush(fd, request->host, request->port);
603a02fd 530 httpState->fd = -1;
531 httpStateFree(-1, httpState);
532 } else {
9f5a2895 533 /* Wait for EOF condition */
603a02fd 534 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
535 }
090089c4 536 }
537}
538
539/* This will be called when request write is complete. Schedule read of
540 * reply. */
b8d8561b 541static void
79a15e0a 542httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
090089c4 543{
30a4f2a8 544 HttpStateData *httpState = data;
9b312a19 545 StoreEntry *entry = httpState->entry;
546 ErrorState *err;
a3d5953d 547 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 548 fd, size, errflag);
ee1679df 549 if (size > 0) {
550 fd_bytes(fd, size, FD_WRITE);
a0f32775 551 kb_incr(&Counter.server.all.kbytes_out, size);
399e85ea 552 kb_incr(&Counter.server.http.kbytes_out, size);
ee1679df 553 }
ea3a2a69 554 if (errflag == COMM_ERR_CLOSING)
555 return;
090089c4 556 if (errflag) {
fe40a877 557 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 558 err->xerrno = errno;
79a15e0a 559 err->request = requestLink(httpState->orig_request);
9b312a19 560 errorAppendEntry(entry, err);
0d4d4170 561 comm_close(fd);
090089c4 562 return;
563 } else {
564 /* Schedule read reply. */
b177367b 565 commSetSelect(fd,
019dd986 566 COMM_SELECT_READ,
b177367b 567 httpReadReply,
cd1fb0eb 568 httpState, 0);
41462d93 569 commSetDefer(fd, fwdCheckDeferRead, entry);
090089c4 570 }
571}
572
99edd1c3 573/*
574 * build request headers and append them to a given MemBuf
575 * used by httpBuildRequestPrefix()
576 * note: calls httpHeaderInit(), the caller is responsible for Clean()-ing
577 */
e1e72f06 578void
6bf8443a 579httpBuildRequestHeader(request_t * request,
580 request_t * orig_request,
581 StoreEntry * entry,
5999b776 582 HttpHeader * hdr_out,
603a02fd 583 int cfd,
b515fc11 584 http_state_flags flags)
6bf8443a 585{
99edd1c3 586 /* building buffer for complex strings */
5999b776 587#define BBUF_SZ (MAX_URL+32)
99edd1c3 588 LOCAL_ARRAY(char, bbuf, BBUF_SZ);
589 String strConnection = StringNull;
590 const HttpHeader *hdr_in = &orig_request->header;
5d679edb 591 int we_do_ranges;
99edd1c3 592 const HttpHeaderEntry *e;
593 HttpHeaderPos pos = HttpHeaderInitPos;
2246b732 594 httpHeaderInit(hdr_out, hoRequest);
99edd1c3 595 /* append our IMS header */
e17dc75c 596 if (entry && entry->lastmod > -1 && request->method == METHOD_GET)
99edd1c3 597 httpHeaderPutTime(hdr_out, HDR_IF_MODIFIED_SINCE, entry->lastmod);
598
5d679edb 599 /* decide if we want to do Ranges ourselves
600 * (and fetch the whole object now)
601 * We want to handle Ranges ourselves iff
602 * - we can actually parse client Range specs
603 * - the specs are expected to be simple enough (e.g. no out-of-order ranges)
604 * - reply will be cachable
db1cd23c 605 * (If the reply will be uncachable we have to throw it away after
5d679edb 606 * serving this request, so it is better to forward ranges to
607 * the server and fetch only the requested content)
608 */
609 we_do_ranges =
c68e9c6b 610 orig_request->range && orig_request->flags.cachable && !httpHdrRangeWillBeComplex(orig_request->range) && (Config.rangeOffsetLimit == -1 || httpHdrRangeFirstOffset(orig_request->range) <= Config.rangeOffsetLimit);
5d679edb 611 debug(11, 8) ("httpBuildRequestHeader: range specs: %p, cachable: %d; we_do_ranges: %d\n",
612 orig_request->range, orig_request->flags.cachable, we_do_ranges);
137ee196 613
99edd1c3 614 strConnection = httpHeaderGetList(hdr_in, HDR_CONNECTION);
615 while ((e = httpHeaderGetEntry(hdr_in, &pos))) {
616 debug(11, 5) ("httpBuildRequestHeader: %s: %s\n",
617 strBuf(e->name), strBuf(e->value));
618 if (!httpRequestHdrAllowed(e, &strConnection))
6bf8443a 619 continue;
99edd1c3 620 switch (e->id) {
621 case HDR_PROXY_AUTHORIZATION:
c68e9c6b 622 /* If we're not doing proxy auth, then it must be passed on */
92695e5e 623 if (!request->flags.used_proxy_auth)
99edd1c3 624 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
625 break;
c68e9c6b 626 case HDR_AUTHORIZATION:
627 /* If we're not doing www auth, then it must be passed on */
628 if (!request->flags.accelerated || !request->flags.used_proxy_auth)
629 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
630 else
631 request->flags.auth = 0; /* We have used the authentication */
632 break;
99edd1c3 633 case HDR_HOST:
77ed547a 634 /* Don't use client's Host: header for redirected requests */
c68e9c6b 635 if (!request->flags.redirected || !Config.onoff.redir_rewrites_host)
99edd1c3 636 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
637 break;
638 case HDR_IF_MODIFIED_SINCE:
639 /* append unless we added our own;
640 * note: at most one client's ims header can pass through */
641 if (!httpHeaderHas(hdr_out, HDR_IF_MODIFIED_SINCE))
642 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
643 break;
644 case HDR_MAX_FORWARDS:
b3b64e58 645 if (orig_request->method == METHOD_TRACE) {
99edd1c3 646 /* sacrificing efficiency over clarity, etc. */
647 const int hops = httpHeaderGetInt(hdr_in, HDR_MAX_FORWARDS);
648 if (hops > 0)
5999b776 649 httpHeaderPutInt(hdr_out, HDR_MAX_FORWARDS, hops - 1);
b3b64e58 650 }
99edd1c3 651 break;
137ee196 652 case HDR_RANGE:
a9771e51 653 case HDR_IF_RANGE:
5d679edb 654 case HDR_REQUEST_RANGE:
655 if (!we_do_ranges)
137ee196 656 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
657 break;
99edd1c3 658 case HDR_PROXY_CONNECTION:
659 case HDR_CONNECTION:
660 case HDR_VIA:
661 case HDR_X_FORWARDED_FOR:
662 case HDR_CACHE_CONTROL:
663 /* append these after the loop if needed */
664 break;
665 default:
666 /* pass on all other header fields */
667 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
66f7337b 668 }
88738790 669 }
99edd1c3 670
671 /* append fake user agent if configured and
672 * the real one is not supplied by the client */
673 if (Config.fake_ua && !httpHeaderHas(hdr_out, HDR_USER_AGENT))
674 httpHeaderPutStr(hdr_out, HDR_USER_AGENT, Config.fake_ua);
675
676 /* append Via */
677 {
678 String strVia = httpHeaderGetList(hdr_in, HDR_VIA);
679 snprintf(bbuf, BBUF_SZ, "%3.1f %s", orig_request->http_ver, ThisCache);
680 strListAdd(&strVia, bbuf, ',');
681 httpHeaderPutStr(hdr_out, HDR_VIA, strBuf(strVia));
682 stringClean(&strVia);
6bf8443a 683 }
99edd1c3 684 /* append X-Forwarded-For */
685 {
686 String strFwd = httpHeaderGetList(hdr_in, HDR_X_FORWARDED_FOR);
687 strListAdd(&strFwd, (cfd < 0 ? "unknown" : fd_table[cfd].ipaddr), ',');
688 httpHeaderPutStr(hdr_out, HDR_X_FORWARDED_FOR, strBuf(strFwd));
689 stringClean(&strFwd);
690 }
691 /* append Host if not there already */
692 if (!httpHeaderHas(hdr_out, HDR_HOST)) {
693 /* use port# only if not default */
694 if (orig_request->port == urlDefaultPort(orig_request->protocol)) {
695 httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->host);
696 } else {
2246b732 697 httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d",
99edd1c3 698 orig_request->host, (int) orig_request->port);
99edd1c3 699 }
6bf8443a 700 }
c68e9c6b 701 /* append Authorization if known in URL, not in header and going direct */
702 if (!httpHeaderHas(hdr_out, HDR_AUTHORIZATION)) {
703 if (!request->flags.proxying && *request->login) {
704 httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s",
705 base64_encode(request->login));
706 }
707 }
708 /* append Proxy-Authorization if configured for peer, and proxying */
709 if (!httpHeaderHas(hdr_out, HDR_PROXY_AUTHORIZATION)) {
710 if (request->flags.proxying && request->peer_login) {
711 httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s",
712 base64_encode(request->peer_login));
713 }
714 }
99edd1c3 715 /* append Cache-Control, add max-age if not there already */
716 {
717 HttpHdrCc *cc = httpHeaderGetCc(hdr_in);
718 if (!cc)
719 cc = httpHdrCcCreate();
720 if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) {
9b5d1d21 721 const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request);
99edd1c3 722 httpHdrCcSetMaxAge(cc, getMaxAge(url));
723 if (strLen(request->urlpath))
724 assert(strstr(url, strBuf(request->urlpath)));
725 }
db1cd23c 726 if (flags.only_if_cached)
727 EBIT_SET(cc->mask, CC_ONLY_IF_CACHED);
99edd1c3 728 httpHeaderPutCc(hdr_out, cc);
729 httpHdrCcDestroy(cc);
6bf8443a 730 }
99edd1c3 731 /* maybe append Connection: keep-alive */
b515fc11 732 if (flags.keepalive) {
733 if (flags.proxying) {
99edd1c3 734 httpHeaderPutStr(hdr_out, HDR_PROXY_CONNECTION, "keep-alive");
603a02fd 735 } else {
99edd1c3 736 httpHeaderPutStr(hdr_out, HDR_CONNECTION, "keep-alive");
603a02fd 737 }
603a02fd 738 }
99edd1c3 739 stringClean(&strConnection);
740}
741
742/* build request prefix and append it to a given MemBuf;
743 * return the length of the prefix */
744size_t
745httpBuildRequestPrefix(request_t * request,
746 request_t * orig_request,
747 StoreEntry * entry,
5999b776 748 MemBuf * mb,
99edd1c3 749 int cfd,
b515fc11 750 http_state_flags flags)
99edd1c3 751{
752 const int offset = mb->size;
753 memBufPrintf(mb, "%s %s HTTP/1.0\r\n",
754 RequestMethodStr[request->method],
755 strLen(request->urlpath) ? strBuf(request->urlpath) : "/");
756 /* build and pack headers */
757 {
758 HttpHeader hdr;
759 Packer p;
760 httpBuildRequestHeader(request, orig_request, entry, &hdr, cfd, flags);
761 packerToMemInit(&p, mb);
762 httpHeaderPackInto(&hdr, &p);
763 httpHeaderClean(&hdr);
764 packerClean(&p);
9d9d144b 765 }
99edd1c3 766 /* append header terminator */
767 memBufAppend(mb, "\r\n", 2);
768 return mb->size - offset;
6bf8443a 769}
090089c4 770/* This will be called when connect completes. Write request. */
b8d8561b 771static void
b177367b 772httpSendRequest(int fd, void *data)
090089c4 773{
b177367b 774 HttpStateData *httpState = data;
99edd1c3 775 MemBuf mb;
30a4f2a8 776 request_t *req = httpState->request;
620da955 777 StoreEntry *entry = httpState->entry;
2a26c096 778 int cfd;
1294c0fc 779 peer *p = httpState->peer;
901e234d 780 CWCB *sendHeaderDone;
090089c4 781
a3d5953d 782 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
090089c4 783
efb9218c 784 if (pumpMethod(req->method))
7db8b16d 785 sendHeaderDone = httpSendRequestEntry;
786 else
787 sendHeaderDone = httpSendComplete;
54220df8 788
2a26c096 789 if (!opt_forwarded_for)
6bf8443a 790 cfd = -1;
2a26c096 791 else if (entry->mem_obj == NULL)
6bf8443a 792 cfd = -1;
2a26c096 793 else
382d851a 794 cfd = entry->mem_obj->fd;
b0a1e5bf 795 assert(-1 == cfd || FD_SOCKET == fd_table[cfd].type);
1294c0fc 796 if (p != NULL)
b515fc11 797 httpState->flags.proxying = 1;
efb9218c 798 /*
99edd1c3 799 * Is keep-alive okay for all request methods?
efb9218c 800 */
801 if (p == NULL)
b515fc11 802 httpState->flags.keepalive = 1;
efb9218c 803 else if (p->stats.n_keepalives_sent < 10)
b515fc11 804 httpState->flags.keepalive = 1;
efb9218c 805 else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50)
b515fc11 806 httpState->flags.keepalive = 1;
db1cd23c 807 if (httpState->peer)
808 if (neighborType(httpState->peer, httpState->request) == PEER_SIBLING)
809 httpState->flags.only_if_cached = 1;
99edd1c3 810 memBufDefInit(&mb);
811 httpBuildRequestPrefix(req,
79a15e0a 812 httpState->orig_request,
6bf8443a 813 entry,
99edd1c3 814 &mb,
603a02fd 815 cfd,
816 httpState->flags);
99edd1c3 817 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, mb.buf);
818 comm_write_mbuf(fd, mb, sendHeaderDone, httpState);
090089c4 819}
910169e5 820void
db1cd23c 821httpStart(FwdState * fwd)
603a02fd 822{
db1cd23c 823 int fd = fwd->server_fd;
cb87dab6 824 HttpStateData *httpState = memAllocate(MEM_HTTP_STATE_DATA);
910169e5 825 request_t *proxy_req;
db1cd23c 826 request_t *orig_req = fwd->request;
910169e5 827 debug(11, 3) ("httpStart: \"%s %s\"\n",
828 RequestMethodStr[orig_req->method],
db1cd23c 829 storeUrl(fwd->entry));
830 cbdataAdd(httpState, memFree, MEM_HTTP_STATE_DATA);
831 storeLockObject(fwd->entry);
832 httpState->fwd = fwd;
833 httpState->entry = fwd->entry;
9e4ad609 834 httpState->fd = fd;
db1cd23c 835 EBIT_SET(httpState->entry->flags, ENTRY_FWD_HDR_WAIT);
836 if (fwd->servers)
837 httpState->peer = fwd->servers->peer; /* might be NULL */
910169e5 838 if (httpState->peer) {
839 proxy_req = requestCreate(orig_req->method,
840 PROTO_NONE, storeUrl(httpState->entry));
841 xstrncpy(proxy_req->host, httpState->peer->host, SQUIDHOSTNAMELEN);
842 proxy_req->port = httpState->peer->http_port;
23e8446b 843 proxy_req->flags = orig_req->flags;
c68e9c6b 844 proxy_req->peer_login = httpState->peer->login;
910169e5 845 httpState->request = requestLink(proxy_req);
910169e5 846 httpState->orig_request = requestLink(orig_req);
92695e5e 847 proxy_req->flags.proxying = 1;
910169e5 848 /*
849 * This NEIGHBOR_PROXY_ONLY check probably shouldn't be here.
850 * We might end up getting the object from somewhere else if,
851 * for example, the request to this neighbor fails.
852 */
cd196bc8 853 if (httpState->peer->options.proxy_only)
910169e5 854 storeReleaseRequest(httpState->entry);
95e36d02 855#if DELAY_POOLS
cd196bc8 856 if (httpState->peer->options.no_delay) {
447e176b 857 proxy_req->delay_id = 0;
95e36d02 858 } else {
447e176b 859 proxy_req->delay_id = orig_req->delay_id;
95e36d02 860 }
861#endif
603a02fd 862 } else {
910169e5 863 httpState->request = requestLink(orig_req);
864 httpState->orig_request = requestLink(orig_req);
603a02fd 865 }
910169e5 866 /*
867 * register the handler to free HTTP state data when the FD closes
868 */
869 comm_add_close_handler(fd, httpStateFree, httpState);
a0f32775 870 Counter.server.all.requests++;
871 Counter.server.http.requests++;
41462d93 872 httpConnectDone(fd, COMM_OK, httpState);
e5f6c5c2 873}
874
875static void
876httpConnectDone(int fd, int status, void *data)
877{
878 HttpStateData *httpState = data;
879 request_t *request = httpState->request;
880 StoreEntry *entry = httpState->entry;
9b312a19 881 ErrorState *err;
edeb28fd 882 if (status == COMM_ERR_DNS) {
a3d5953d 883 debug(11, 4) ("httpConnectDone: Unknown host: %s\n", request->host);
fe40a877 884 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
9b312a19 885 err->dnsserver_msg = xstrdup(dns_error_message);
79a15e0a 886 err->request = requestLink(httpState->orig_request);
9b312a19 887 errorAppendEntry(entry, err);
edeb28fd 888 comm_close(fd);
889 } else if (status != COMM_OK) {
fe40a877 890 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 891 err->xerrno = errno;
9b312a19 892 err->host = xstrdup(request->host);
893 err->port = request->port;
79a15e0a 894 err->request = requestLink(httpState->orig_request);
9b312a19 895 errorAppendEntry(entry, err);
1294c0fc 896 if (httpState->peer)
897 peerCheckConnectStart(httpState->peer);
e5f6c5c2 898 comm_close(fd);
899 } else {
bfcaf585 900 commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0);
86cf9987 901 commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState);
090089c4 902 }
090089c4 903}
904
54220df8 905static void
7db8b16d 906httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data)
54220df8 907{
908 HttpStateData *httpState = data;
909 StoreEntry *entry = httpState->entry;
910 ErrorState *err;
911 debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
7db8b16d 912 fd, size, errflag);
54220df8 913 if (size > 0) {
7db8b16d 914 fd_bytes(fd, size, FD_WRITE);
54220df8 915 kb_incr(&Counter.server.all.kbytes_out, size);
916 kb_incr(&Counter.server.http.kbytes_out, size);
917 }
918 if (errflag == COMM_ERR_CLOSING)
7db8b16d 919 return;
54220df8 920 if (errflag) {
7db8b16d 921 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
922 err->xerrno = errno;
923 err->request = requestLink(httpState->orig_request);
924 errorAppendEntry(entry, err);
925 comm_close(fd);
926 return;
54220df8 927 }
7db8b16d 928 pumpStart(fd, entry, httpState->orig_request, httpSendComplete, httpState);
54220df8 929}