]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
report # on-disk objects
[thirdparty/squid.git] / src / http.cc
CommitLineData
da2b3a17 1
30a4f2a8 2/*
e1e72f06 3 * $Id: http.cc,v 1.273 1998/05/21 22:01:08 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/
30a4f2a8 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 */
019dd986 31
32/*
30a4f2a8 33 * Copyright (c) 1994, 1995. All rights reserved.
34 *
35 * The Harvest software was developed by the Internet Research Task
36 * Force Research Group on Resource Discovery (IRTF-RD):
37 *
38 * Mic Bowman of Transarc Corporation.
39 * Peter Danzig of the University of Southern California.
40 * Darren R. Hardy of the University of Colorado at Boulder.
41 * Udi Manber of the University of Arizona.
42 * Michael F. Schwartz of the University of Colorado at Boulder.
43 * Duane Wessels of the University of Colorado at Boulder.
44 *
45 * This copyright notice applies to software in the Harvest
46 * ``src/'' directory only. Users should consult the individual
47 * copyright notices in the ``components/'' subdirectories for
48 * copyright information about other software bundled with the
49 * Harvest source code distribution.
50 *
51 * TERMS OF USE
52 *
53 * The Harvest software may be used and re-distributed without
54 * charge, provided that the software origin and research team are
55 * cited in any use of the system. Most commonly this is
56 * accomplished by including a link to the Harvest Home Page
57 * (http://harvest.cs.colorado.edu/) from the query page of any
58 * Broker you deploy, as well as in the query result pages. These
59 * links are generated automatically by the standard Broker
60 * software distribution.
61 *
62 * The Harvest software is provided ``as is'', without express or
63 * implied warranty, and with no support nor obligation to assist
64 * in its use, correction, modification or enhancement. We assume
65 * no liability with respect to the infringement of copyrights,
66 * trade secrets, or any patents, and are not responsible for
67 * consequential damages. Proper use of the Harvest software is
68 * entirely the responsibility of the user.
69 *
70 * DERIVATIVE WORKS
71 *
72 * Users may make derivative works from the Harvest software, subject
73 * to the following constraints:
74 *
75 * - You must include the above copyright notice and these
76 * accompanying paragraphs in all forms of derivative works,
77 * and any documentation and other materials related to such
78 * distribution and use acknowledge that the software was
79 * developed at the above institutions.
80 *
81 * - You must notify IRTF-RD regarding your distribution of
82 * the derivative work.
83 *
84 * - You must clearly notify users that your are distributing
85 * a modified version and not the original Harvest software.
86 *
87 * - Any derivative product is also subject to these copyright
88 * and use restrictions.
89 *
90 * Note that the Harvest software is NOT in the public domain. We
91 * retain copyright, as specified above.
92 *
93 * HISTORY OF FREE SOFTWARE STATUS
94 *
95 * Originally we required sites to license the software in cases
96 * where they were going to build commercial products/services
97 * around Harvest. In June 1995 we changed this policy. We now
98 * allow people to use the core Harvest software (the code found in
99 * the Harvest ``src/'' directory) for free. We made this change
100 * in the interest of encouraging the widest possible deployment of
101 * the technology. The Harvest software is really a reference
102 * implementation of a set of protocols and formats, some of which
103 * we intend to standardize. We encourage commercial
104 * re-implementations of code complying to this set of standards.
019dd986 105 */
44a47c6e 106
4a83b852 107/*
108 * Anonymizing patch by lutz@as-node.jena.thur.de
de3bdb4c 109 * have a look into http-anon.c to get more informations.
4a83b852 110 */
111
44a47c6e 112#include "squid.h"
090089c4 113
6bf8443a 114static const char *const crlf = "\r\n";
4db43fab 115
6bf8443a 116enum {
6fb52f6c 117 CCC_NOCACHE,
118 CCC_NOSTORE,
119 CCC_MAXAGE,
120 CCC_MAXSTALE,
121 CCC_MINFRESH,
122 CCC_ONLYIFCACHED,
123 CCC_ENUM_END
6bf8443a 124};
125
9e4ad609 126static CNCB httpConnectDone;
127static CWCB httpSendComplete;
54220df8 128static CWCB httpSendRequestEntry;
129
9e4ad609 130static PF httpReadReply;
131static PF httpSendRequest;
132static PF httpStateFree;
133static PF httpTimeout;
99edd1c3 134#if OLD_CODE
f5b8bbc4 135static void httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int);
99edd1c3 136#endif
f5b8bbc4 137static void httpCacheNegatively(StoreEntry *);
138static void httpMakePrivate(StoreEntry *);
139static void httpMakePublic(StoreEntry *);
bfcaf585 140static STABH httpAbort;
f5b8bbc4 141static HttpStateData *httpBuildState(int, StoreEntry *, request_t *, peer *);
142static int httpSocketOpen(StoreEntry *, request_t *);
143static void httpRestart(HttpStateData *);
efb9218c 144static int httpTryRestart(HttpStateData *);
f8309b15 145static int httpCachableReply(HttpStateData *);
b8d8561b 146
b177367b 147static void
79d39a72 148httpStateFree(int fdnotused, void *data)
f5558c95 149{
b177367b 150 HttpStateData *httpState = data;
0d4d4170 151 if (httpState == NULL)
b177367b 152 return;
bfcaf585 153 storeUnregisterAbort(httpState->entry);
ddb6142d 154 assert(httpState->entry->store_status != STORE_PENDING);
f88211e8 155 storeUnlockObject(httpState->entry);
0d4d4170 156 if (httpState->reply_hdr) {
3f6c0fb2 157 memFree(MEM_8K_BUF, httpState->reply_hdr);
0d4d4170 158 httpState->reply_hdr = NULL;
159 }
30a4f2a8 160 requestUnlink(httpState->request);
20cc1450 161 requestUnlink(httpState->orig_request);
7dd44885 162 httpState->request = NULL;
163 httpState->orig_request = NULL;
164 cbdataFree(httpState);
f5558c95 165}
166
b8d8561b 167int
75e88d56 168httpCachable(method_t method)
090089c4 169{
090089c4 170 /* GET and HEAD are cachable. Others are not. */
6eb42cae 171 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 172 return 0;
090089c4 173 /* else cachable */
174 return 1;
175}
176
b8d8561b 177static void
5c5783a2 178httpTimeout(int fd, void *data)
090089c4 179{
b177367b 180 HttpStateData *httpState = data;
593c9a75 181 StoreEntry *entry = httpState->entry;
9b312a19 182 ErrorState *err;
9fb13bb6 183 debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
8796b9e9 184 assert(entry->store_status == STORE_PENDING);
73a3014d 185 if (entry->mem_obj->inmem_hi == 0) {
fe40a877 186 err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT);
79a15e0a 187 err->request = requestLink(httpState->orig_request);
9b312a19 188 errorAppendEntry(entry, err);
b50179a6 189 } else {
b34ed725 190 storeAbort(entry, 0);
9b312a19 191 }
0d4d4170 192 comm_close(fd);
090089c4 193}
194
30a4f2a8 195/* This object can be cached for a long time */
b8d8561b 196static void
197httpMakePublic(StoreEntry * entry)
30a4f2a8 198{
79a15e0a 199 if (EBIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 200 storeSetPublicKey(entry);
201}
202
203/* This object should never be cached at all */
b8d8561b 204static void
205httpMakePrivate(StoreEntry * entry)
30a4f2a8 206{
30a4f2a8 207 storeExpireNow(entry);
79a15e0a 208 EBIT_CLR(entry->flag, ENTRY_CACHABLE);
30a4f2a8 209 storeReleaseRequest(entry); /* delete object when not used */
210}
211
212/* This object may be negatively cached */
b8d8561b 213static void
214httpCacheNegatively(StoreEntry * entry)
30a4f2a8 215{
79b5cc5f 216 storeNegativeCache(entry);
79a15e0a 217 if (EBIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 218 storeSetPublicKey(entry);
30a4f2a8 219}
220
f8309b15 221static int
222httpCachableReply(HttpStateData * httpState)
c54e9052 223{
d8b249ef 224 HttpReply *rep = httpState->entry->mem_obj->reply;
225 HttpHeader *hdr = &rep->header;
226 const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0;
7faf2bdb 227 if (EBIT_TEST(cc_mask, CC_PRIVATE))
f8309b15 228 return 0;
7faf2bdb 229 if (EBIT_TEST(cc_mask, CC_NO_CACHE))
f8309b15 230 return 0;
79a15e0a 231 if (EBIT_TEST(httpState->request->flags, REQ_AUTH))
7faf2bdb 232 if (!EBIT_TEST(cc_mask, CC_PROXY_REVALIDATE))
fee0cebb 233 return 0;
f8309b15 234 /*
235 * Dealing with cookies is quite a bit more complicated
236 * than this. Ideally we should strip the cookie
237 * header from the reply but still cache the reply body.
238 * More confusion at draft-ietf-http-state-mgmt-05.txt.
239 */
cb69b4c7 240 /* With new headers the above stripping should be easy to do? @?@ */
241 if (httpHeaderHas(hdr, HDR_SET_COOKIE))
f8309b15 242 return 0;
783e4699 243 if (httpHeaderHas(hdr, HDR_VARY))
244 return 0;
cb69b4c7 245 switch (httpState->entry->mem_obj->reply->sline.status) {
c54e9052 246 /* Responses that are cacheable */
247 case 200: /* OK */
248 case 203: /* Non-Authoritative Information */
249 case 300: /* Multiple Choices */
250 case 301: /* Moved Permanently */
251 case 410: /* Gone */
1294c0fc 252 /* don't cache objects from peers w/o LMT, Date, or Expires */
cb69b4c7 253 /* check that is it enough to check headers @?@ */
d8b249ef 254 if (rep->date > -1)
c54e9052 255 return 1;
d8b249ef 256 else if (rep->last_modified > -1)
c54e9052 257 return 1;
1294c0fc 258 else if (!httpState->peer)
c54e9052 259 return 1;
d8b249ef 260 /* @?@ (here and 302): invalid expires header compiles to squid_curtime */
261 else if (rep->expires > -1)
c54e9052 262 return 1;
c54e9052 263 else
264 return 0;
79d39a72 265 /* NOTREACHED */
c54e9052 266 break;
267 /* Responses that only are cacheable if the server says so */
268 case 302: /* Moved temporarily */
d8b249ef 269 if (rep->expires > -1)
c54e9052 270 return 1;
271 else
272 return 0;
79d39a72 273 /* NOTREACHED */
c54e9052 274 break;
cb69b4c7 275/* @?@ should we replace these magic numbers with http_status enums? */
c54e9052 276 /* Errors can be negatively cached */
277 case 204: /* No Content */
278 case 305: /* Use Proxy (proxy redirect) */
279 case 400: /* Bad Request */
280 case 403: /* Forbidden */
281 case 404: /* Not Found */
282 case 405: /* Method Now Allowed */
283 case 414: /* Request-URI Too Long */
284 case 500: /* Internal Server Error */
285 case 501: /* Not Implemented */
286 case 502: /* Bad Gateway */
287 case 503: /* Service Unavailable */
288 case 504: /* Gateway Timeout */
289 return -1;
79d39a72 290 /* NOTREACHED */
c54e9052 291 break;
292 /* Some responses can never be cached */
88738790 293 case 206: /* Partial Content -- Not yet supported */
c54e9052 294 case 303: /* See Other */
295 case 304: /* Not Modified */
296 case 401: /* Unauthorized */
297 case 407: /* Proxy Authentication Required */
298 case 600: /* Squid header parsing error */
299 default: /* Unknown status code */
300 return 0;
79d39a72 301 /* NOTREACHED */
c54e9052 302 break;
303 }
79d39a72 304 /* NOTREACHED */
c54e9052 305}
090089c4 306
cb69b4c7 307/* rewrite this later using new interfaces @?@ */
b8d8561b 308void
0ee4272b 309httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 310{
311 char *t = NULL;
30a4f2a8 312 StoreEntry *entry = httpState->entry;
d3fb4dea 313 int room;
314 int hdr_len;
cb69b4c7 315 HttpReply *reply = entry->mem_obj->reply;
b6cfb65c 316 debug(11, 3) ("httpProcessReplyHeader: key '%s'\n",
317 storeKeyText(entry->key));
e924600d 318 if (httpState->reply_hdr == NULL)
7021844c 319 httpState->reply_hdr = memAllocate(MEM_8K_BUF);
30a4f2a8 320 if (httpState->reply_hdr_state == 0) {
321 hdr_len = strlen(httpState->reply_hdr);
ed85b771 322 room = 8191 - hdr_len;
30a4f2a8 323 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 324 hdr_len += room < size ? room : size;
30a4f2a8 325 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
84fa351c 326 debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr);
30a4f2a8 327 httpState->reply_hdr_state += 2;
cb69b4c7 328 reply->sline.status = 555;
ed85b771 329 return;
d3fb4dea 330 }
d1a43e28 331 t = httpState->reply_hdr + hdr_len;
332 /* headers can be incomplete only if object still arriving */
2334c194 333 if (!httpState->eof) {
334 size_t k = headersEnd(httpState->reply_hdr, 8192);
335 if (0 == k)
d1a43e28 336 return; /* headers not complete */
2334c194 337 t = httpState->reply_hdr + k;
338 }
2285407f 339 *t = '\0';
30a4f2a8 340 httpState->reply_hdr_state++;
f5558c95 341 }
30a4f2a8 342 if (httpState->reply_hdr_state == 1) {
123abbe1 343 const Ctx ctx = ctx_enter(entry->mem_obj->url);
30a4f2a8 344 httpState->reply_hdr_state++;
a3d5953d 345 debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 346 httpState->reply_hdr);
347 /* Parse headers into reply structure */
2334c194 348 /* Old code never parsed headers if headersEnd failed, was it intentional ? @?@ @?@ */
cb69b4c7 349 /* what happens if we fail to parse here? @?@ @?@ */
ee1679df 350 httpReplyParse(reply, httpState->reply_hdr); /* httpState->eof); */
ca98227c 351 storeTimestampsSet(entry);
30a4f2a8 352 /* Check if object is cacheable or not based on reply code */
cb69b4c7 353 debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status);
f8309b15 354 switch (httpCachableReply(httpState)) {
c54e9052 355 case 1:
356 httpMakePublic(entry);
30a4f2a8 357 break;
c54e9052 358 case 0:
359 httpMakePrivate(entry);
f5558c95 360 break;
c54e9052 361 case -1:
851eeef7 362 httpCacheNegatively(entry);
30a4f2a8 363 break;
c54e9052 364 default:
365 assert(0);
4e38e700 366 break;
f5558c95 367 }
d8b249ef 368 if (reply->cache_control && EBIT_TEST(reply->cache_control->mask, CC_PROXY_REVALIDATE))
79a15e0a 369 EBIT_SET(entry->flag, ENTRY_REVALIDATE);
9a47da71 370 if (EBIT_TEST(httpState->flags, HTTP_KEEPALIVE))
371 if (httpState->peer)
372 httpState->peer->stats.n_keepalives_sent++;
9f5a2895 373 if (reply->keep_alive)
1294c0fc 374 if (httpState->peer)
375 httpState->peer->stats.n_keepalives_recv++;
123abbe1 376 ctx_exit(ctx);
f5558c95 377 }
378}
379
603a02fd 380static int
381httpPconnTransferDone(HttpStateData * httpState)
382{
383 /* return 1 if we got the last of the data on a persistent connection */
384 MemObject *mem = httpState->entry->mem_obj;
cb69b4c7 385 HttpReply *reply = mem->reply;
51fdcbd5 386 debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd);
978e455f 387 /*
99edd1c3 388 * If we didn't send a keep-alive request header, then this
978e455f 389 * can not be a persistent connection.
390 */
79a15e0a 391 if (!EBIT_TEST(httpState->flags, HTTP_KEEPALIVE))
603a02fd 392 return 0;
9f5a2895 393 /*
394 * What does the reply have to say about keep-alive?
395 */
396 if (!reply->keep_alive)
397 return 0;
51fdcbd5 398 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
d8b249ef 399 reply->content_length);
603a02fd 400 /*
978e455f 401 * Deal with gross HTTP stuff
402 * - If we haven't seen the end of the reply headers, we can't
403 * be persistent.
404 * - For "200 OK" check the content-length in the next block.
978e455f 405 * - For "204 No Content" (even with content-length) we're done.
406 * - For "304 Not Modified" (even with content-length) we're done.
a3c60429 407 * - 1XX replies never have a body; we're done.
978e455f 408 * - For HEAD requests with content-length we're done.
a3c60429 409 * - For all other replies, check content length in next block.
603a02fd 410 */
978e455f 411 if (httpState->reply_hdr_state < 2)
412 return 0;
cb69b4c7 413 else if (reply->sline.status == HTTP_OK)
a3c60429 414 (void) 0; /* common case, continue */
cb69b4c7 415 else if (reply->sline.status == HTTP_NO_CONTENT)
978e455f 416 return 1;
cb69b4c7 417 else if (reply->sline.status == HTTP_NOT_MODIFIED)
978e455f 418 return 1;
cb69b4c7 419 else if (reply->sline.status < HTTP_OK)
a3c60429 420 return 1;
978e455f 421 else if (httpState->request->method == METHOD_HEAD)
422 return 1;
603a02fd 423 /*
a3c60429 424 * If there is no content-length, then we can't be
978e455f 425 * persistent. If there is a content length, then we must
426 * wait until we've seen the end of the body.
603a02fd 427 */
d8b249ef 428 if (reply->content_length < 0)
603a02fd 429 return 0;
d8b249ef 430 else if (mem->inmem_hi < reply->content_length + reply->hdr_sz)
603a02fd 431 return 0;
978e455f 432 else
b34ed725 433 return 1;
603a02fd 434}
090089c4 435
436/* This will be called when data is ready to be read from fd. Read until
437 * error or connection closed. */
f5558c95 438/* XXX this function is too long! */
b8d8561b 439static void
b177367b 440httpReadReply(int fd, void *data)
090089c4 441{
b177367b 442 HttpStateData *httpState = data;
95d659f0 443 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
bfcaf585 444 StoreEntry *entry = httpState->entry;
603a02fd 445 const request_t *request = httpState->request;
090089c4 446 int len;
30a4f2a8 447 int bin;
090089c4 448 int clen;
9b312a19 449 ErrorState *err;
d89d1fb6 450 if (protoAbortFetch(entry)) {
9b312a19 451 storeAbort(entry, 0);
a3d5953d 452 comm_close(fd);
453 return;
234967c9 454 }
455 /* check if we want to defer reading */
8350fe9b 456 clen = entry->mem_obj->inmem_hi;
1513873c 457 errno = 0;
30a4f2a8 458 len = read(fd, buf, SQUID_TCP_SO_RCVBUF);
a3d5953d 459 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 460 if (len > 0) {
ee1679df 461 fd_bytes(fd, len, FD_READ);
a0f32775 462 kb_incr(&Counter.server.all.kbytes_in, len);
463 kb_incr(&Counter.server.http.kbytes_in, len);
4f92c80c 464 commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
4a63c85f 465 IOStats.Http.reads++;
30a4f2a8 466 for (clen = len - 1, bin = 0; clen; bin++)
467 clen >>= 1;
468 IOStats.Http.read_hist[bin]++;
469 }
5ede6c8f 470 if (!httpState->reply_hdr && len > 0) {
471 /* Skip whitespace */
472 while (len > 0 && isspace(*buf))
473 xmemmove(buf, buf + 1, len--);
474 if (len == 0) {
475 /* Continue to read... */
476 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
477 return;
478 }
479 }
ba718c8f 480 if (len < 0) {
55cb44f1 481 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
482 fd, xstrerror());
b224ea98 483 if (ignoreErrno(errno)) {
9b312a19 484 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
55cb44f1 485 } else if (entry->mem_obj->inmem_hi == 0 && httpTryRestart(httpState)) {
486 httpRestart(httpState);
487 } else if (clen == 0) {
488 err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
489 err->xerrno = errno;
490 err->request = requestLink(httpState->orig_request);
491 errorAppendEntry(entry, err);
1afe05c5 492 comm_close(fd);
090089c4 493 } else {
55cb44f1 494 storeAbort(entry, 0);
0d4d4170 495 comm_close(fd);
090089c4 496 }
8350fe9b 497 } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
efb9218c 498 if (httpTryRestart(httpState)) {
b716a8ad 499 httpRestart(httpState);
500 } else {
501 httpState->eof = 1;
fe40a877 502 err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 503 err->xerrno = errno;
79a15e0a 504 err->request = requestLink(httpState->orig_request);
b716a8ad 505 errorAppendEntry(entry, err);
b716a8ad 506 comm_close(fd);
507 }
090089c4 508 } else if (len == 0) {
509 /* Connection closed; retrieval done. */
f86a6a46 510 httpState->eof = 1;
d1a43e28 511 if (httpState->reply_hdr_state < 2)
b34ed725 512 /*
513 * Yes Henrik, there is a point to doing this. When we
514 * called httpProcessReplyHeader() before, we didn't find
515 * the end of headers, but now we are definately at EOF, so
516 * we want to process the reply headers.
517 */
d1a43e28 518 httpProcessReplyHeader(httpState, buf, len);
d1a43e28 519 storeComplete(entry); /* deallocates mem_obj->request */
0d4d4170 520 comm_close(fd);
090089c4 521 } else {
d1a43e28 522 if (httpState->reply_hdr_state < 2)
30a4f2a8 523 httpProcessReplyHeader(httpState, buf, len);
620da955 524 storeAppend(entry, buf, len);
603a02fd 525 if (httpPconnTransferDone(httpState)) {
5b29969a 526 /* yes we have to clear all these! */
8796b9e9 527 commSetDefer(fd, NULL, NULL);
5b29969a 528 commSetTimeout(fd, -1, NULL, NULL);
529 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
603a02fd 530 comm_remove_close_handler(fd, httpStateFree, httpState);
531 storeComplete(entry); /* deallocates mem_obj->request */
8796b9e9 532 pconnPush(fd, request->host, request->port);
603a02fd 533 httpState->fd = -1;
534 httpStateFree(-1, httpState);
535 } else {
9f5a2895 536 /* Wait for EOF condition */
603a02fd 537 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
538 }
090089c4 539 }
540}
541
542/* This will be called when request write is complete. Schedule read of
543 * reply. */
b8d8561b 544static void
79a15e0a 545httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
090089c4 546{
30a4f2a8 547 HttpStateData *httpState = data;
9b312a19 548 StoreEntry *entry = httpState->entry;
549 ErrorState *err;
a3d5953d 550 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 551 fd, size, errflag);
ee1679df 552 if (size > 0) {
553 fd_bytes(fd, size, FD_WRITE);
a0f32775 554 kb_incr(&Counter.server.all.kbytes_out, size);
399e85ea 555 kb_incr(&Counter.server.http.kbytes_out, size);
ee1679df 556 }
ea3a2a69 557 if (errflag == COMM_ERR_CLOSING)
558 return;
090089c4 559 if (errflag) {
fe40a877 560 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 561 err->xerrno = errno;
79a15e0a 562 err->request = requestLink(httpState->orig_request);
9b312a19 563 errorAppendEntry(entry, err);
0d4d4170 564 comm_close(fd);
090089c4 565 return;
566 } else {
567 /* Schedule read reply. */
b177367b 568 commSetSelect(fd,
019dd986 569 COMM_SELECT_READ,
b177367b 570 httpReadReply,
cd1fb0eb 571 httpState, 0);
70a9dab4 572 commSetDefer(fd, protoCheckDeferRead, entry);
090089c4 573 }
574}
575
99edd1c3 576#if OLD_CODE
6bf8443a 577static void
88738790 578httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int check)
6bf8443a 579{
580 size_t n = *sz + strlen(line) + 2;
581 if (n >= max)
582 return;
88738790 583 if (check) {
17a0a4ee 584 if (Config.onoff.anonymizer == ANONYMIZER_PARANOID) {
88738790 585 if (!httpAnonAllowed(line))
586 return;
17a0a4ee 587 } else if (Config.onoff.anonymizer == ANONYMIZER_STANDARD) {
88738790 588 if (httpAnonDenied(line))
589 return;
590 }
4a83b852 591 }
4a83b852 592 /* allowed header, explicitly known to be not dangerous */
a3d5953d 593 debug(11, 5) ("httpAppendRequestHeader: %s\n", line);
929545fe 594 strcpy(hdr + (*sz), line);
6bf8443a 595 strcat(hdr + (*sz), crlf);
596 *sz = n;
597}
99edd1c3 598#endif
6bf8443a 599
99edd1c3 600/*
601 * build request headers and append them to a given MemBuf
602 * used by httpBuildRequestPrefix()
603 * note: calls httpHeaderInit(), the caller is responsible for Clean()-ing
604 */
e1e72f06 605void
6bf8443a 606httpBuildRequestHeader(request_t * request,
607 request_t * orig_request,
608 StoreEntry * entry,
99edd1c3 609 HttpHeader *hdr_out,
603a02fd 610 int cfd,
611 int flags)
6bf8443a 612{
99edd1c3 613 /* building buffer for complex strings */
614 #define BBUF_SZ (MAX_URL+32)
615 LOCAL_ARRAY(char, bbuf, BBUF_SZ);
616 String strConnection = StringNull;
617 const HttpHeader *hdr_in = &orig_request->header;
618 const HttpHeaderEntry *e;
619 HttpHeaderPos pos = HttpHeaderInitPos;
620
621 assert(orig_request->prefix != NULL);
622 debug(11, 3) ("httpBuildRequestHeader:\n%s", orig_request->prefix);
623 httpHeaderInit(hdr_out);
624
625 /* append our IMS header */
626 if (entry && entry->lastmod && request->method == METHOD_GET)
627 httpHeaderPutTime(hdr_out, HDR_IF_MODIFIED_SINCE, entry->lastmod);
628
629 strConnection = httpHeaderGetList(hdr_in, HDR_CONNECTION);
630 while ((e = httpHeaderGetEntry(hdr_in, &pos))) {
631 debug(11, 5) ("httpBuildRequestHeader: %s: %s\n",
632 strBuf(e->name), strBuf(e->value));
633 if (!httpRequestHdrAllowed(e, &strConnection))
6bf8443a 634 continue;
99edd1c3 635 switch (e->id) {
636 case HDR_PROXY_AUTHORIZATION:
afe95a7e 637 /* If we're not going to do proxy auth, then it must be passed on */
99edd1c3 638 if (!EBIT_TEST(request->flags, REQ_USED_PROXY_AUTH))
639 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
640 break;
641 case HDR_HOST:
77ed547a 642 /* Don't use client's Host: header for redirected requests */
99edd1c3 643 if (!EBIT_TEST(request->flags, REQ_REDIRECTED))
644 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
645 break;
646 case HDR_IF_MODIFIED_SINCE:
647 /* append unless we added our own;
648 * note: at most one client's ims header can pass through */
649 if (!httpHeaderHas(hdr_out, HDR_IF_MODIFIED_SINCE))
650 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
651 break;
652 case HDR_MAX_FORWARDS:
b3b64e58 653 if (orig_request->method == METHOD_TRACE) {
99edd1c3 654 /* sacrificing efficiency over clarity, etc. */
655 const int hops = httpHeaderGetInt(hdr_in, HDR_MAX_FORWARDS);
656 if (hops > 0)
657 httpHeaderPutInt(hdr_out, HDR_MAX_FORWARDS, hops-1);
b3b64e58 658 }
99edd1c3 659 break;
660 case HDR_PROXY_CONNECTION:
661 case HDR_CONNECTION:
662 case HDR_VIA:
663 case HDR_X_FORWARDED_FOR:
664 case HDR_CACHE_CONTROL:
665 /* append these after the loop if needed */
666 break;
667 default:
668 /* pass on all other header fields */
669 httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
66f7337b 670 }
88738790 671 }
99edd1c3 672
673 /* append fake user agent if configured and
674 * the real one is not supplied by the client */
675 if (Config.fake_ua && !httpHeaderHas(hdr_out, HDR_USER_AGENT))
676 httpHeaderPutStr(hdr_out, HDR_USER_AGENT, Config.fake_ua);
677
678 /* append Via */
679 {
680 String strVia = httpHeaderGetList(hdr_in, HDR_VIA);
681 snprintf(bbuf, BBUF_SZ, "%3.1f %s", orig_request->http_ver, ThisCache);
682 strListAdd(&strVia, bbuf, ',');
683 httpHeaderPutStr(hdr_out, HDR_VIA, strBuf(strVia));
684 stringClean(&strVia);
6bf8443a 685 }
99edd1c3 686 /* append X-Forwarded-For */
687 {
688 String strFwd = httpHeaderGetList(hdr_in, HDR_X_FORWARDED_FOR);
689 strListAdd(&strFwd, (cfd < 0 ? "unknown" : fd_table[cfd].ipaddr), ',');
690 httpHeaderPutStr(hdr_out, HDR_X_FORWARDED_FOR, strBuf(strFwd));
691 stringClean(&strFwd);
692 }
693 /* append Host if not there already */
694 if (!httpHeaderHas(hdr_out, HDR_HOST)) {
695 /* use port# only if not default */
696 if (orig_request->port == urlDefaultPort(orig_request->protocol)) {
697 httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->host);
698 } else {
699 snprintf(bbuf, BBUF_SZ, "%s:%d",
700 orig_request->host, (int) orig_request->port);
701 httpHeaderPutStr(hdr_out, HDR_HOST, bbuf);
702 }
6bf8443a 703 }
99edd1c3 704 /* append Cache-Control, add max-age if not there already */
705 {
706 HttpHdrCc *cc = httpHeaderGetCc(hdr_in);
707 if (!cc)
708 cc = httpHdrCcCreate();
709 if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) {
710 const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request, NULL);
711 httpHdrCcSetMaxAge(cc, getMaxAge(url));
712 if (strLen(request->urlpath))
713 assert(strstr(url, strBuf(request->urlpath)));
714 }
715 httpHeaderPutCc(hdr_out, cc);
716 httpHdrCcDestroy(cc);
6bf8443a 717 }
99edd1c3 718 /* maybe append Connection: keep-alive */
79a15e0a 719 if (EBIT_TEST(flags, HTTP_KEEPALIVE)) {
720 if (EBIT_TEST(flags, HTTP_PROXYING)) {
99edd1c3 721 httpHeaderPutStr(hdr_out, HDR_PROXY_CONNECTION, "keep-alive");
603a02fd 722 } else {
99edd1c3 723 httpHeaderPutStr(hdr_out, HDR_CONNECTION, "keep-alive");
603a02fd 724 }
603a02fd 725 }
99edd1c3 726 stringClean(&strConnection);
727}
728
729/* build request prefix and append it to a given MemBuf;
730 * return the length of the prefix */
731size_t
732httpBuildRequestPrefix(request_t * request,
733 request_t * orig_request,
734 StoreEntry * entry,
735 MemBuf *mb,
736 int cfd,
737 int flags)
738{
739 const int offset = mb->size;
740 memBufPrintf(mb, "%s %s HTTP/1.0\r\n",
741 RequestMethodStr[request->method],
742 strLen(request->urlpath) ? strBuf(request->urlpath) : "/");
743 /* build and pack headers */
744 {
745 HttpHeader hdr;
746 Packer p;
747 httpBuildRequestHeader(request, orig_request, entry, &hdr, cfd, flags);
748 packerToMemInit(&p, mb);
749 httpHeaderPackInto(&hdr, &p);
750 httpHeaderClean(&hdr);
751 packerClean(&p);
9d9d144b 752 }
99edd1c3 753 /* append header terminator */
754 memBufAppend(mb, "\r\n", 2);
755 return mb->size - offset;
6bf8443a 756}
757
090089c4 758/* This will be called when connect completes. Write request. */
b8d8561b 759static void
b177367b 760httpSendRequest(int fd, void *data)
090089c4 761{
b177367b 762 HttpStateData *httpState = data;
99edd1c3 763 MemBuf mb;
30a4f2a8 764 request_t *req = httpState->request;
620da955 765 StoreEntry *entry = httpState->entry;
2a26c096 766 int cfd;
1294c0fc 767 peer *p = httpState->peer;
901e234d 768 CWCB *sendHeaderDone;
090089c4 769
a3d5953d 770 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
090089c4 771
efb9218c 772 if (pumpMethod(req->method))
7db8b16d 773 sendHeaderDone = httpSendRequestEntry;
774 else
775 sendHeaderDone = httpSendComplete;
54220df8 776
2a26c096 777 if (!opt_forwarded_for)
6bf8443a 778 cfd = -1;
2a26c096 779 else if (entry->mem_obj == NULL)
6bf8443a 780 cfd = -1;
2a26c096 781 else
382d851a 782 cfd = entry->mem_obj->fd;
b0a1e5bf 783 assert(-1 == cfd || FD_SOCKET == fd_table[cfd].type);
1294c0fc 784 if (p != NULL)
79a15e0a 785 EBIT_SET(httpState->flags, HTTP_PROXYING);
efb9218c 786 /*
99edd1c3 787 * Is keep-alive okay for all request methods?
efb9218c 788 */
789 if (p == NULL)
790 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
791 else if (p->stats.n_keepalives_sent < 10)
792 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
793 else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50)
794 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
99edd1c3 795 memBufDefInit(&mb);
796 httpBuildRequestPrefix(req,
79a15e0a 797 httpState->orig_request,
6bf8443a 798 entry,
99edd1c3 799 &mb,
603a02fd 800 cfd,
801 httpState->flags);
99edd1c3 802 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, mb.buf);
803 comm_write_mbuf(fd, mb, sendHeaderDone, httpState);
090089c4 804}
805
603a02fd 806static int
b716a8ad 807httpSocketOpen(StoreEntry * entry, request_t * request)
090089c4 808{
9e4ad609 809 int fd;
9b312a19 810 ErrorState *err;
9e4ad609 811 fd = comm_open(SOCK_STREAM,
16b204c4 812 0,
813 Config.Addrs.tcp_outgoing,
814 0,
815 COMM_NONBLOCKING,
9fb13bb6 816 storeUrl(entry));
603a02fd 817 if (fd < 0) {
79a15e0a 818 debug(50, 4) ("httpSocketOpen: %s\n", xstrerror());
fe40a877 819 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 820 err->xerrno = errno;
79a15e0a 821 err->request = requestLink(request);
9b312a19 822 errorAppendEntry(entry, err);
090089c4 823 }
603a02fd 824 return fd;
825}
826
827static HttpStateData *
828httpBuildState(int fd, StoreEntry * entry, request_t * orig_request, peer * e)
829{
cb87dab6 830 HttpStateData *httpState = memAllocate(MEM_HTTP_STATE_DATA);
603a02fd 831 request_t *request;
770f051d 832 storeLockObject(entry);
cb87dab6 833 cbdataAdd(httpState, MEM_HTTP_STATE_DATA);
0a0bf5db 834 httpState->entry = entry;
9e4ad609 835 httpState->fd = fd;
603a02fd 836 if (e) {
99edd1c3 837 request = requestCreate(
838 orig_request->method, PROTO_NONE, storeUrl(entry));
603a02fd 839 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
840 request->port = e->http_port;
603a02fd 841 httpState->request = requestLink(request);
1294c0fc 842 httpState->peer = e;
603a02fd 843 httpState->orig_request = requestLink(orig_request);
79a15e0a 844 EBIT_SET(request->flags, REQ_PROXYING);
603a02fd 845 } else {
846 httpState->request = requestLink(orig_request);
79a15e0a 847 httpState->orig_request = requestLink(orig_request);
603a02fd 848 }
0d4d4170 849 /* register the handler to free HTTP state data when the FD closes */
603a02fd 850 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
e102ebda 851 storeRegisterAbort(entry, httpAbort, httpState);
603a02fd 852 return httpState;
853}
854
855void
856httpStart(request_t * request, StoreEntry * entry, peer * e)
857{
858 HttpStateData *httpState;
859 int fd;
860 debug(11, 3) ("httpStart: \"%s %s\"\n",
9fb13bb6 861 RequestMethodStr[request->method], storeUrl(entry));
a0f32775 862 Counter.server.all.requests++;
863 Counter.server.http.requests++;
603a02fd 864 if (e) {
a369131d 865 if (EBIT_TEST(e->options, NEIGHBOR_PROXY_ONLY))
603a02fd 866 storeReleaseRequest(entry);
603a02fd 867 if ((fd = pconnPop(e->host, e->http_port)) >= 0) {
51fdcbd5 868 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
603a02fd 869 httpState = httpBuildState(fd, entry, request, e);
b716a8ad 870 commSetTimeout(httpState->fd,
871 Config.Timeout.connect,
872 httpTimeout,
873 httpState);
603a02fd 874 httpConnectDone(fd, COMM_OK, httpState);
875 return;
876 }
877 } else {
878 if ((fd = pconnPop(request->host, request->port)) >= 0) {
51fdcbd5 879 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
603a02fd 880 httpState = httpBuildState(fd, entry, request, e);
b716a8ad 881 commSetTimeout(httpState->fd,
882 Config.Timeout.connect,
883 httpTimeout,
884 httpState);
603a02fd 885 httpConnectDone(fd, COMM_OK, httpState);
886 return;
887 }
888 }
79a15e0a 889 if ((fd = httpSocketOpen(entry, request)) < 0)
603a02fd 890 return;
891 httpState = httpBuildState(fd, entry, request, e);
603a02fd 892 commSetTimeout(httpState->fd,
893 Config.Timeout.connect,
894 httpTimeout,
cd1fb0eb 895 httpState);
edeb28fd 896 commConnectStart(httpState->fd,
603a02fd 897 httpState->request->host,
898 httpState->request->port,
e924600d 899 httpConnectDone,
900 httpState);
e5f6c5c2 901}
902
efb9218c 903static int
904httpTryRestart(HttpStateData * httpState)
905{
906 /*
907 * We only retry the request if it looks like it was
908 * on a persistent/pipelined connection
909 */
910 if (fd_table[httpState->fd].uses < 2)
911 return 0;
912 if (pumpMethod(httpState->orig_request->method))
913 if (0 == pumpRestart(httpState->orig_request))
914 return 0;
915 return 1;
916}
917
b716a8ad 918static void
919httpRestart(HttpStateData * httpState)
920{
921 /* restart a botched request from a persistent connection */
9fb13bb6 922 debug(11, 2) ("Retrying HTTP request for %s\n", storeUrl(httpState->entry));
901e234d 923 if (pumpMethod(httpState->orig_request->method)) {
cca378c3 924 debug(11, 3) ("Potential Coredump: httpRestart %s %s\n",
efb9218c 925 RequestMethodStr[httpState->orig_request->method],
926 storeUrl(httpState->entry));
e371ed5c 927 }
b716a8ad 928 if (httpState->fd >= 0) {
929 comm_remove_close_handler(httpState->fd, httpStateFree, httpState);
930 comm_close(httpState->fd);
931 httpState->fd = -1;
932 }
79a15e0a 933 httpState->fd = httpSocketOpen(httpState->entry, httpState->orig_request);
b716a8ad 934 if (httpState->fd < 0)
935 return;
936 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
937 commSetTimeout(httpState->fd,
938 Config.Timeout.connect,
939 httpTimeout,
940 httpState);
941 commConnectStart(httpState->fd,
942 httpState->request->host,
943 httpState->request->port,
944 httpConnectDone,
945 httpState);
946}
947
e5f6c5c2 948static void
949httpConnectDone(int fd, int status, void *data)
950{
951 HttpStateData *httpState = data;
952 request_t *request = httpState->request;
953 StoreEntry *entry = httpState->entry;
9b312a19 954 ErrorState *err;
edeb28fd 955 if (status == COMM_ERR_DNS) {
a3d5953d 956 debug(11, 4) ("httpConnectDone: Unknown host: %s\n", request->host);
fe40a877 957 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
9b312a19 958 err->dnsserver_msg = xstrdup(dns_error_message);
79a15e0a 959 err->request = requestLink(httpState->orig_request);
9b312a19 960 errorAppendEntry(entry, err);
edeb28fd 961 comm_close(fd);
962 } else if (status != COMM_OK) {
fe40a877 963 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 964 err->xerrno = errno;
9b312a19 965 err->host = xstrdup(request->host);
966 err->port = request->port;
79a15e0a 967 err->request = requestLink(httpState->orig_request);
9b312a19 968 errorAppendEntry(entry, err);
1294c0fc 969 if (httpState->peer)
970 peerCheckConnectStart(httpState->peer);
e5f6c5c2 971 comm_close(fd);
972 } else {
9fb13bb6 973 fd_note(fd, storeUrl(entry));
b716a8ad 974 fd_table[fd].uses++;
bfcaf585 975 commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0);
86cf9987 976 commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState);
090089c4 977 }
090089c4 978}
979
bfcaf585 980static void
981httpAbort(void *data)
982{
983 HttpStateData *httpState = data;
9fb13bb6 984 debug(11, 2) ("httpAbort: %s\n", storeUrl(httpState->entry));
bfcaf585 985 comm_close(httpState->fd);
986}
9b312a19 987
54220df8 988static void
7db8b16d 989httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data)
54220df8 990{
991 HttpStateData *httpState = data;
992 StoreEntry *entry = httpState->entry;
993 ErrorState *err;
994 debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
7db8b16d 995 fd, size, errflag);
54220df8 996 if (size > 0) {
7db8b16d 997 fd_bytes(fd, size, FD_WRITE);
54220df8 998 kb_incr(&Counter.server.all.kbytes_out, size);
999 kb_incr(&Counter.server.http.kbytes_out, size);
1000 }
1001 if (errflag == COMM_ERR_CLOSING)
7db8b16d 1002 return;
54220df8 1003 if (errflag) {
7db8b16d 1004 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
1005 err->xerrno = errno;
1006 err->request = requestLink(httpState->orig_request);
1007 errorAppendEntry(entry, err);
1008 comm_close(fd);
1009 return;
54220df8 1010 }
7db8b16d 1011 pumpStart(fd, entry, httpState->orig_request, httpSendComplete, httpState);
54220df8 1012}