]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
fixed debug statement
[thirdparty/squid.git] / src / http.cc
CommitLineData
da2b3a17 1
30a4f2a8 2/*
901e234d 3 * $Id: http.cc,v 1.256 1998/03/17 04:00:13 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;
f5b8bbc4 134static void httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int);
135static void httpCacheNegatively(StoreEntry *);
136static void httpMakePrivate(StoreEntry *);
137static void httpMakePublic(StoreEntry *);
bfcaf585 138static STABH httpAbort;
f5b8bbc4 139static HttpStateData *httpBuildState(int, StoreEntry *, request_t *, peer *);
140static int httpSocketOpen(StoreEntry *, request_t *);
141static void httpRestart(HttpStateData *);
efb9218c 142static int httpTryRestart(HttpStateData *);
f8309b15 143static int httpCachableReply(HttpStateData *);
b8d8561b 144
b177367b 145static void
79d39a72 146httpStateFree(int fdnotused, void *data)
f5558c95 147{
b177367b 148 HttpStateData *httpState = data;
0d4d4170 149 if (httpState == NULL)
b177367b 150 return;
bfcaf585 151 storeUnregisterAbort(httpState->entry);
ddb6142d 152 assert(httpState->entry->store_status != STORE_PENDING);
f88211e8 153 storeUnlockObject(httpState->entry);
0d4d4170 154 if (httpState->reply_hdr) {
3f6c0fb2 155 memFree(MEM_8K_BUF, httpState->reply_hdr);
0d4d4170 156 httpState->reply_hdr = NULL;
157 }
30a4f2a8 158 requestUnlink(httpState->request);
20cc1450 159 requestUnlink(httpState->orig_request);
7dd44885 160 httpState->request = NULL;
161 httpState->orig_request = NULL;
162 cbdataFree(httpState);
f5558c95 163}
164
b8d8561b 165int
75e88d56 166httpCachable(method_t method)
090089c4 167{
090089c4 168 /* GET and HEAD are cachable. Others are not. */
6eb42cae 169 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 170 return 0;
090089c4 171 /* else cachable */
172 return 1;
173}
174
b8d8561b 175static void
5c5783a2 176httpTimeout(int fd, void *data)
090089c4 177{
b177367b 178 HttpStateData *httpState = data;
593c9a75 179 StoreEntry *entry = httpState->entry;
9b312a19 180 ErrorState *err;
9fb13bb6 181 debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
8796b9e9 182 assert(entry->store_status == STORE_PENDING);
73a3014d 183 if (entry->mem_obj->inmem_hi == 0) {
fe40a877 184 err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT);
79a15e0a 185 err->request = requestLink(httpState->orig_request);
9b312a19 186 errorAppendEntry(entry, err);
b50179a6 187 } else {
b34ed725 188 storeAbort(entry, 0);
9b312a19 189 }
0d4d4170 190 comm_close(fd);
090089c4 191}
192
30a4f2a8 193/* This object can be cached for a long time */
b8d8561b 194static void
195httpMakePublic(StoreEntry * entry)
30a4f2a8 196{
79a15e0a 197 if (EBIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 198 storeSetPublicKey(entry);
199}
200
201/* This object should never be cached at all */
b8d8561b 202static void
203httpMakePrivate(StoreEntry * entry)
30a4f2a8 204{
30a4f2a8 205 storeExpireNow(entry);
79a15e0a 206 EBIT_CLR(entry->flag, ENTRY_CACHABLE);
30a4f2a8 207 storeReleaseRequest(entry); /* delete object when not used */
208}
209
210/* This object may be negatively cached */
b8d8561b 211static void
212httpCacheNegatively(StoreEntry * entry)
30a4f2a8 213{
79b5cc5f 214 storeNegativeCache(entry);
79a15e0a 215 if (EBIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 216 storeSetPublicKey(entry);
30a4f2a8 217}
218
f8309b15 219static int
220httpCachableReply(HttpStateData * httpState)
c54e9052 221{
cb69b4c7 222 HttpHeader *hdr = &httpState->entry->mem_obj->reply->hdr;
7faf2bdb 223 const HttpHdrCc *cc = httpHeaderGetCc(hdr);
224 const int cc_mask = (cc) ? cc->mask : 0;
225 if (EBIT_TEST(cc_mask, CC_PRIVATE))
f8309b15 226 return 0;
7faf2bdb 227 if (EBIT_TEST(cc_mask, CC_NO_CACHE))
f8309b15 228 return 0;
79a15e0a 229 if (EBIT_TEST(httpState->request->flags, REQ_AUTH))
7faf2bdb 230 if (!EBIT_TEST(cc_mask, CC_PROXY_REVALIDATE))
fee0cebb 231 return 0;
f8309b15 232 /*
233 * Dealing with cookies is quite a bit more complicated
234 * than this. Ideally we should strip the cookie
235 * header from the reply but still cache the reply body.
236 * More confusion at draft-ietf-http-state-mgmt-05.txt.
237 */
cb69b4c7 238 /* With new headers the above stripping should be easy to do? @?@ */
239 if (httpHeaderHas(hdr, HDR_SET_COOKIE))
f8309b15 240 return 0;
cb69b4c7 241 switch (httpState->entry->mem_obj->reply->sline.status) {
c54e9052 242 /* Responses that are cacheable */
243 case 200: /* OK */
244 case 203: /* Non-Authoritative Information */
245 case 300: /* Multiple Choices */
246 case 301: /* Moved Permanently */
247 case 410: /* Gone */
1294c0fc 248 /* don't cache objects from peers w/o LMT, Date, or Expires */
cb69b4c7 249 /* check that is it enough to check headers @?@ */
250 if (httpHeaderHas(hdr, HDR_DATE))
c54e9052 251 return 1;
cb69b4c7 252 else if (httpHeaderHas(hdr, HDR_LAST_MODIFIED))
c54e9052 253 return 1;
1294c0fc 254 else if (!httpState->peer)
c54e9052 255 return 1;
cb69b4c7 256 else if (httpHeaderHas(hdr, HDR_EXPIRES))
c54e9052 257 return 1;
c54e9052 258 else
259 return 0;
79d39a72 260 /* NOTREACHED */
c54e9052 261 break;
262 /* Responses that only are cacheable if the server says so */
263 case 302: /* Moved temporarily */
cb69b4c7 264 if (httpHeaderHas(hdr, HDR_EXPIRES))
c54e9052 265 return 1;
266 else
267 return 0;
79d39a72 268 /* NOTREACHED */
c54e9052 269 break;
cb69b4c7 270/* @?@ should we replace these magic numbers with http_status enums? */
c54e9052 271 /* Errors can be negatively cached */
272 case 204: /* No Content */
273 case 305: /* Use Proxy (proxy redirect) */
274 case 400: /* Bad Request */
275 case 403: /* Forbidden */
276 case 404: /* Not Found */
277 case 405: /* Method Now Allowed */
278 case 414: /* Request-URI Too Long */
279 case 500: /* Internal Server Error */
280 case 501: /* Not Implemented */
281 case 502: /* Bad Gateway */
282 case 503: /* Service Unavailable */
283 case 504: /* Gateway Timeout */
284 return -1;
79d39a72 285 /* NOTREACHED */
c54e9052 286 break;
287 /* Some responses can never be cached */
88738790 288 case 206: /* Partial Content -- Not yet supported */
c54e9052 289 case 303: /* See Other */
290 case 304: /* Not Modified */
291 case 401: /* Unauthorized */
292 case 407: /* Proxy Authentication Required */
293 case 600: /* Squid header parsing error */
294 default: /* Unknown status code */
295 return 0;
79d39a72 296 /* NOTREACHED */
c54e9052 297 break;
298 }
79d39a72 299 /* NOTREACHED */
c54e9052 300}
090089c4 301
cb69b4c7 302/* rewrite this later using new interfaces @?@ */
b8d8561b 303void
0ee4272b 304httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 305{
306 char *t = NULL;
30a4f2a8 307 StoreEntry *entry = httpState->entry;
d3fb4dea 308 int room;
309 int hdr_len;
cb69b4c7 310 HttpReply *reply = entry->mem_obj->reply;
b6cfb65c 311 debug(11, 3) ("httpProcessReplyHeader: key '%s'\n",
312 storeKeyText(entry->key));
e924600d 313 if (httpState->reply_hdr == NULL)
7021844c 314 httpState->reply_hdr = memAllocate(MEM_8K_BUF);
30a4f2a8 315 if (httpState->reply_hdr_state == 0) {
316 hdr_len = strlen(httpState->reply_hdr);
ed85b771 317 room = 8191 - hdr_len;
30a4f2a8 318 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 319 hdr_len += room < size ? room : size;
30a4f2a8 320 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
84fa351c 321 debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr);
30a4f2a8 322 httpState->reply_hdr_state += 2;
cb69b4c7 323 reply->sline.status = 555;
ed85b771 324 return;
d3fb4dea 325 }
d1a43e28 326 t = httpState->reply_hdr + hdr_len;
327 /* headers can be incomplete only if object still arriving */
f86a6a46 328 if (!httpState->eof)
d1a43e28 329 if ((t = mime_headers_end(httpState->reply_hdr)) == NULL)
330 return; /* headers not complete */
2285407f 331 *t = '\0';
30a4f2a8 332 httpState->reply_hdr_state++;
f5558c95 333 }
30a4f2a8 334 if (httpState->reply_hdr_state == 1) {
123abbe1 335 const Ctx ctx = ctx_enter(entry->mem_obj->url);
30a4f2a8 336 httpState->reply_hdr_state++;
a3d5953d 337 debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 338 httpState->reply_hdr);
339 /* Parse headers into reply structure */
cb69b4c7 340 /* Old code never parsed headers if mime_headers_end failed, was it intentional ? @?@ @?@ */
341 /* what happens if we fail to parse here? @?@ @?@ */
ee1679df 342 httpReplyParse(reply, httpState->reply_hdr); /* httpState->eof); */
ca98227c 343 storeTimestampsSet(entry);
30a4f2a8 344 /* Check if object is cacheable or not based on reply code */
cb69b4c7 345 debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status);
f8309b15 346 switch (httpCachableReply(httpState)) {
c54e9052 347 case 1:
348 httpMakePublic(entry);
30a4f2a8 349 break;
c54e9052 350 case 0:
351 httpMakePrivate(entry);
f5558c95 352 break;
c54e9052 353 case -1:
851eeef7 354 httpCacheNegatively(entry);
30a4f2a8 355 break;
c54e9052 356 default:
357 assert(0);
4e38e700 358 break;
f5558c95 359 }
7faf2bdb 360 if (httpReplyHasCc(reply, CC_PROXY_REVALIDATE))
79a15e0a 361 EBIT_SET(entry->flag, ENTRY_REVALIDATE);
9a47da71 362 if (EBIT_TEST(httpState->flags, HTTP_KEEPALIVE))
363 if (httpState->peer)
364 httpState->peer->stats.n_keepalives_sent++;
cb69b4c7 365 if (httpHeaderHas(&reply->hdr, HDR_PROXY_KEEPALIVE))
1294c0fc 366 if (httpState->peer)
367 httpState->peer->stats.n_keepalives_recv++;
123abbe1 368 ctx_exit(ctx);
f5558c95 369 }
370}
371
603a02fd 372static int
373httpPconnTransferDone(HttpStateData * httpState)
374{
375 /* return 1 if we got the last of the data on a persistent connection */
376 MemObject *mem = httpState->entry->mem_obj;
cb69b4c7 377 HttpReply *reply = mem->reply;
51fdcbd5 378 debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd);
978e455f 379 /*
380 * If we didn't send a Keepalive request header, then this
381 * can not be a persistent connection.
382 */
79a15e0a 383 if (!EBIT_TEST(httpState->flags, HTTP_KEEPALIVE))
603a02fd 384 return 0;
51fdcbd5 385 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
cb69b4c7 386 httpReplyContentLen(reply));
603a02fd 387 /*
978e455f 388 * Deal with gross HTTP stuff
389 * - If we haven't seen the end of the reply headers, we can't
390 * be persistent.
391 * - For "200 OK" check the content-length in the next block.
978e455f 392 * - For "204 No Content" (even with content-length) we're done.
393 * - For "304 Not Modified" (even with content-length) we're done.
a3c60429 394 * - 1XX replies never have a body; we're done.
978e455f 395 * - For HEAD requests with content-length we're done.
a3c60429 396 * - For all other replies, check content length in next block.
603a02fd 397 */
978e455f 398 if (httpState->reply_hdr_state < 2)
399 return 0;
cb69b4c7 400 else if (reply->sline.status == HTTP_OK)
a3c60429 401 (void) 0; /* common case, continue */
cb69b4c7 402 else if (reply->sline.status == HTTP_NO_CONTENT)
978e455f 403 return 1;
cb69b4c7 404 else if (reply->sline.status == HTTP_NOT_MODIFIED)
978e455f 405 return 1;
cb69b4c7 406 else if (reply->sline.status < HTTP_OK)
a3c60429 407 return 1;
978e455f 408 else if (httpState->request->method == METHOD_HEAD)
409 return 1;
603a02fd 410 /*
a3c60429 411 * If there is no content-length, then we can't be
978e455f 412 * persistent. If there is a content length, then we must
413 * wait until we've seen the end of the body.
603a02fd 414 */
cb69b4c7 415 if (httpReplyContentLen(reply) < 0)
603a02fd 416 return 0;
cb69b4c7 417 else if (mem->inmem_hi < httpReplyContentLen(reply) + reply->hdr_sz)
603a02fd 418 return 0;
978e455f 419 else
b34ed725 420 return 1;
603a02fd 421}
090089c4 422
423/* This will be called when data is ready to be read from fd. Read until
424 * error or connection closed. */
f5558c95 425/* XXX this function is too long! */
b8d8561b 426static void
b177367b 427httpReadReply(int fd, void *data)
090089c4 428{
b177367b 429 HttpStateData *httpState = data;
95d659f0 430 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
bfcaf585 431 StoreEntry *entry = httpState->entry;
603a02fd 432 const request_t *request = httpState->request;
090089c4 433 int len;
30a4f2a8 434 int bin;
090089c4 435 int clen;
9b312a19 436 ErrorState *err;
d89d1fb6 437 if (protoAbortFetch(entry)) {
9b312a19 438 storeAbort(entry, 0);
a3d5953d 439 comm_close(fd);
440 return;
234967c9 441 }
442 /* check if we want to defer reading */
8350fe9b 443 clen = entry->mem_obj->inmem_hi;
1513873c 444 errno = 0;
30a4f2a8 445 len = read(fd, buf, SQUID_TCP_SO_RCVBUF);
a3d5953d 446 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 447 if (len > 0) {
ee1679df 448 fd_bytes(fd, len, FD_READ);
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 }
ba718c8f 457 if (len < 0) {
b224ea98 458 if (ignoreErrno(errno)) {
9b312a19 459 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
090089c4 460 } else {
73a3014d 461 if (clen == 0) {
fe40a877 462 err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 463 err->xerrno = errno;
79a15e0a 464 err->request = requestLink(httpState->orig_request);
9b312a19 465 errorAppendEntry(entry, err);
b50179a6 466 } else {
b34ed725 467 storeAbort(entry, 0);
9b312a19 468 }
0d4d4170 469 comm_close(fd);
090089c4 470 }
a3d5953d 471 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
0a0bf5db 472 fd, xstrerror());
8350fe9b 473 } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
efb9218c 474 if (httpTryRestart(httpState)) {
b716a8ad 475 httpRestart(httpState);
476 } else {
477 httpState->eof = 1;
fe40a877 478 err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 479 err->xerrno = errno;
79a15e0a 480 err->request = requestLink(httpState->orig_request);
b716a8ad 481 errorAppendEntry(entry, err);
b716a8ad 482 comm_close(fd);
483 }
090089c4 484 } else if (len == 0) {
485 /* Connection closed; retrieval done. */
f86a6a46 486 httpState->eof = 1;
d1a43e28 487 if (httpState->reply_hdr_state < 2)
b34ed725 488 /*
489 * Yes Henrik, there is a point to doing this. When we
490 * called httpProcessReplyHeader() before, we didn't find
491 * the end of headers, but now we are definately at EOF, so
492 * we want to process the reply headers.
493 */
d1a43e28 494 httpProcessReplyHeader(httpState, buf, len);
d1a43e28 495 storeComplete(entry); /* deallocates mem_obj->request */
0d4d4170 496 comm_close(fd);
090089c4 497 } else {
d1a43e28 498 if (httpState->reply_hdr_state < 2)
30a4f2a8 499 httpProcessReplyHeader(httpState, buf, len);
620da955 500 storeAppend(entry, buf, len);
603a02fd 501 if (httpPconnTransferDone(httpState)) {
5b29969a 502 /* yes we have to clear all these! */
8796b9e9 503 commSetDefer(fd, NULL, NULL);
5b29969a 504 commSetTimeout(fd, -1, NULL, NULL);
505 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
603a02fd 506 comm_remove_close_handler(fd, httpStateFree, httpState);
507 storeComplete(entry); /* deallocates mem_obj->request */
8796b9e9 508 pconnPush(fd, request->host, request->port);
603a02fd 509 httpState->fd = -1;
510 httpStateFree(-1, httpState);
511 } else {
512 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
513 }
090089c4 514 }
515}
516
517/* This will be called when request write is complete. Schedule read of
518 * reply. */
b8d8561b 519static void
79a15e0a 520httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
090089c4 521{
30a4f2a8 522 HttpStateData *httpState = data;
9b312a19 523 StoreEntry *entry = httpState->entry;
524 ErrorState *err;
a3d5953d 525 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 526 fd, size, errflag);
ee1679df 527 if (size > 0) {
528 fd_bytes(fd, size, FD_WRITE);
a0f32775 529 kb_incr(&Counter.server.all.kbytes_out, size);
399e85ea 530 kb_incr(&Counter.server.http.kbytes_out, size);
ee1679df 531 }
ea3a2a69 532 if (errflag == COMM_ERR_CLOSING)
533 return;
090089c4 534 if (errflag) {
fe40a877 535 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 536 err->xerrno = errno;
79a15e0a 537 err->request = requestLink(httpState->orig_request);
9b312a19 538 errorAppendEntry(entry, err);
0d4d4170 539 comm_close(fd);
090089c4 540 return;
541 } else {
542 /* Schedule read reply. */
b177367b 543 commSetSelect(fd,
019dd986 544 COMM_SELECT_READ,
b177367b 545 httpReadReply,
cd1fb0eb 546 httpState, 0);
70a9dab4 547 commSetDefer(fd, protoCheckDeferRead, entry);
090089c4 548 }
549}
550
6bf8443a 551static void
88738790 552httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int check)
6bf8443a 553{
554 size_t n = *sz + strlen(line) + 2;
555 if (n >= max)
556 return;
88738790 557 if (check) {
17a0a4ee 558 if (Config.onoff.anonymizer == ANONYMIZER_PARANOID) {
88738790 559 if (!httpAnonAllowed(line))
560 return;
17a0a4ee 561 } else if (Config.onoff.anonymizer == ANONYMIZER_STANDARD) {
88738790 562 if (httpAnonDenied(line))
563 return;
564 }
4a83b852 565 }
4a83b852 566 /* allowed header, explicitly known to be not dangerous */
a3d5953d 567 debug(11, 5) ("httpAppendRequestHeader: %s\n", line);
929545fe 568 strcpy(hdr + (*sz), line);
6bf8443a 569 strcat(hdr + (*sz), crlf);
570 *sz = n;
571}
572
1294c0fc 573#define YBUF_SZ (MAX_URL+32)
6bf8443a 574size_t
575httpBuildRequestHeader(request_t * request,
576 request_t * orig_request,
577 StoreEntry * entry,
6bf8443a 578 size_t * in_len,
579 char *hdr_out,
580 size_t out_sz,
603a02fd 581 int cfd,
582 int flags)
6bf8443a 583{
1294c0fc 584 LOCAL_ARRAY(char, ybuf, YBUF_SZ);
ab013258 585 LOCAL_ARRAY(char, no_forward, 1024);
7021844c 586 char *xbuf = memAllocate(MEM_4K_BUF);
587 char *viabuf = memAllocate(MEM_4K_BUF);
588 char *fwdbuf = memAllocate(MEM_4K_BUF);
6bf8443a 589 char *t = NULL;
590 char *s = NULL;
591 char *end = NULL;
592 size_t len = 0;
593 size_t hdr_len = 0;
6bf8443a 594 size_t l;
595 int hdr_flags = 0;
151a0b6d 596 int cc_flags = 0;
b3b64e58 597 int n;
6bf8443a 598 const char *url = NULL;
2357f74a 599 char *hdr_in = orig_request->headers;
6bf8443a 600
2357f74a 601 assert(hdr_in != NULL);
a3d5953d 602 debug(11, 3) ("httpBuildRequestHeader: INPUT:\n%s\n", hdr_in);
6bf8443a 603 xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096);
604 xstrncpy(viabuf, "Via: ", 4096);
1294c0fc 605 snprintf(ybuf, YBUF_SZ, "%s %s HTTP/1.0",
6bf8443a 606 RequestMethodStr[request->method],
02922e76 607 strLen(request->urlpath) ? strBuf(request->urlpath) : "/");
88738790 608 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
6bf8443a 609 /* Add IMS header */
610 if (entry && entry->lastmod && request->method == METHOD_GET) {
1294c0fc 611 snprintf(ybuf, YBUF_SZ, "If-Modified-Since: %s", mkrfc1123(entry->lastmod));
88738790 612 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
6bf8443a 613 EBIT_SET(hdr_flags, HDR_IMS);
614 }
00c59270 615 end = mime_headers_end(hdr_in);
6bf8443a 616 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
617 hdr_len = t - hdr_in;
6bf8443a 618 l = strcspn(t, crlf) + 1;
619 if (l > 4096)
620 l = 4096;
621 xstrncpy(xbuf, t, l);
a3d5953d 622 debug(11, 5) ("httpBuildRequestHeader: %s\n", xbuf);
6bf8443a 623 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
624 continue;
88738790 625 if (strncasecmp(xbuf, "Proxy-authorization:", 20) == 0)
afe95a7e 626 /* If we're not going to do proxy auth, then it must be passed on */
79a15e0a 627 if (EBIT_TEST(request->flags, REQ_USED_PROXY_AUTH))
88738790 628 continue;
ab013258 629 if (strncasecmp(xbuf, "Connection:", 11) == 0) {
067bea91 630 handleConnectionHeader(0, no_forward, &xbuf[11]);
6bf8443a 631 continue;
067bea91 632 }
66f7337b 633 if (strncasecmp(xbuf, "Host:", 5) == 0) {
6bf8443a 634 EBIT_SET(hdr_flags, HDR_HOST);
66f7337b 635 } else if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) {
6bf8443a 636 for (s = xbuf + 14; *s && isspace(*s); s++);
637 if (strncasecmp(s, "Max-age=", 8) == 0)
151a0b6d 638 EBIT_SET(cc_flags, CCC_MAXAGE);
66f7337b 639 } else if (strncasecmp(xbuf, "Via:", 4) == 0) {
6bf8443a 640 for (s = xbuf + 4; *s && isspace(*s); s++);
69e81830 641 if ((int) strlen(viabuf) + (int) strlen(s) < 4000)
6bf8443a 642 strcat(viabuf, s);
643 strcat(viabuf, ", ");
644 continue;
66f7337b 645 } else if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) {
6bf8443a 646 for (s = xbuf + 16; *s && isspace(*s); s++);
69e81830 647 if ((int) strlen(fwdbuf) + (int) strlen(s) < 4000)
6bf8443a 648 strcat(fwdbuf, s);
649 strcat(fwdbuf, ", ");
650 continue;
66f7337b 651 } else if (strncasecmp(xbuf, "If-Modified-Since:", 18) == 0) {
6bf8443a 652 if (EBIT_TEST(hdr_flags, HDR_IMS))
653 continue;
b3b64e58 654 } else if (strncasecmp(xbuf, "Max-Forwards:", 13) == 0) {
655 if (orig_request->method == METHOD_TRACE) {
656 for (s = xbuf + 13; *s && isspace(*s); s++);
657 n = atoi(s);
56878878 658 snprintf(xbuf, 4096, "Max-Forwards: %d", n - 1);
b3b64e58 659 }
66f7337b 660 }
067bea91 661 if (!handleConnectionHeader(1, no_forward, xbuf))
ab013258 662 httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512, 1);
88738790 663 }
664 hdr_len = t - hdr_in;
665 if (Config.fake_ua && strstr(hdr_out, "User-Agent") == NULL) {
1294c0fc 666 snprintf(ybuf, YBUF_SZ, "User-Agent: %s", Config.fake_ua);
88738790 667 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 0);
6bf8443a 668 }
56878878 669 /* Append Via: */
670 /* snprintf would fail here too */
1294c0fc 671 snprintf(ybuf, YBUF_SZ, "%3.1f %s", orig_request->http_ver, ThisCache);
6bf8443a 672 strcat(viabuf, ybuf);
88738790 673 httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz, 1);
6bf8443a 674 /* Append to X-Forwarded-For: */
a08307eb 675 strcat(fwdbuf, cfd < 0 ? "unknown" : fd_table[cfd].ipaddr);
88738790 676 httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz, 1);
6bf8443a 677 if (!EBIT_TEST(hdr_flags, HDR_HOST)) {
1294c0fc 678 snprintf(ybuf, YBUF_SZ, "Host: %s", orig_request->host);
88738790 679 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
6bf8443a 680 }
151a0b6d 681 if (!EBIT_TEST(cc_flags, CCC_MAXAGE)) {
9fb13bb6 682 url = entry ? storeUrl(entry) : urlCanonical(orig_request, NULL);
1294c0fc 683 snprintf(ybuf, YBUF_SZ, "Cache-control: Max-age=%d", (int) getMaxAge(url));
88738790 684 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
02922e76 685 if (strLen(request->urlpath))
686 assert(strstr(url, strBuf(request->urlpath)));
6bf8443a 687 }
603a02fd 688 /* maybe append Connection: Keep-Alive */
79a15e0a 689 if (EBIT_TEST(flags, HTTP_KEEPALIVE)) {
690 if (EBIT_TEST(flags, HTTP_PROXYING)) {
1294c0fc 691 snprintf(ybuf, YBUF_SZ, "Proxy-Connection: Keep-Alive");
603a02fd 692 } else {
1294c0fc 693 snprintf(ybuf, YBUF_SZ, "Connection: Keep-Alive");
603a02fd 694 }
695 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
696 }
88738790 697 httpAppendRequestHeader(hdr_out, null_string, &len, out_sz, 1);
3f6c0fb2 698 memFree(MEM_4K_BUF, xbuf);
699 memFree(MEM_4K_BUF, viabuf);
700 memFree(MEM_4K_BUF, fwdbuf);
6bf8443a 701 if (in_len)
702 *in_len = hdr_len;
9d9d144b 703 if ((l = strlen(hdr_out)) != len) {
704 debug_trap("httpBuildRequestHeader: size mismatch");
705 len = l;
706 }
a3d5953d 707 debug(11, 3) ("httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out);
6bf8443a 708 return len;
709}
710
090089c4 711/* This will be called when connect completes. Write request. */
b8d8561b 712static void
b177367b 713httpSendRequest(int fd, void *data)
090089c4 714{
b177367b 715 HttpStateData *httpState = data;
090089c4 716 char *buf = NULL;
090089c4 717 int len = 0;
718 int buflen;
30a4f2a8 719 request_t *req = httpState->request;
9864ee44 720 int buftype = 0;
620da955 721 StoreEntry *entry = httpState->entry;
2a26c096 722 int cfd;
1294c0fc 723 peer *p = httpState->peer;
901e234d 724 CWCB *sendHeaderDone;
090089c4 725
a3d5953d 726 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
02922e76 727 buflen = strLen(req->urlpath);
62dec5a7 728 if (req->headers)
729 buflen += req->headers_sz + 1;
090089c4 730 buflen += 512; /* lots of extra */
731
efb9218c 732 if (pumpMethod(req->method))
7db8b16d 733 sendHeaderDone = httpSendRequestEntry;
734 else
735 sendHeaderDone = httpSendComplete;
54220df8 736
30a4f2a8 737 if (buflen < DISK_PAGE_SIZE) {
7021844c 738 buf = memAllocate(MEM_8K_BUF);
9864ee44 739 buftype = BUF_TYPE_8K;
6bf8443a 740 buflen = DISK_PAGE_SIZE;
30a4f2a8 741 } else {
9864ee44 742 buf = xcalloc(buflen, 1);
743 buftype = BUF_TYPE_MALLOC;
090089c4 744 }
2a26c096 745 if (!opt_forwarded_for)
6bf8443a 746 cfd = -1;
2a26c096 747 else if (entry->mem_obj == NULL)
6bf8443a 748 cfd = -1;
2a26c096 749 else
382d851a 750 cfd = entry->mem_obj->fd;
1294c0fc 751 if (p != NULL)
79a15e0a 752 EBIT_SET(httpState->flags, HTTP_PROXYING);
efb9218c 753 /*
754 * Is Keepalive okay for all request methods?
755 */
756 if (p == NULL)
757 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
758 else if (p->stats.n_keepalives_sent < 10)
759 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
760 else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50)
761 EBIT_SET(httpState->flags, HTTP_KEEPALIVE);
6bf8443a 762 len = httpBuildRequestHeader(req,
79a15e0a 763 httpState->orig_request,
6bf8443a 764 entry,
6bf8443a 765 NULL,
766 buf,
767 buflen,
603a02fd 768 cfd,
769 httpState->flags);
a3d5953d 770 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, buf);
30a4f2a8 771 comm_write(fd,
14e59844 772 buf,
773 len,
54220df8 774 sendHeaderDone,
9864ee44 775 httpState,
3f6c0fb2 776 buftype == BUF_TYPE_8K ? memFree8K : xfree);
090089c4 777}
778
603a02fd 779static int
b716a8ad 780httpSocketOpen(StoreEntry * entry, request_t * request)
090089c4 781{
9e4ad609 782 int fd;
9b312a19 783 ErrorState *err;
9e4ad609 784 fd = comm_open(SOCK_STREAM,
16b204c4 785 0,
786 Config.Addrs.tcp_outgoing,
787 0,
788 COMM_NONBLOCKING,
9fb13bb6 789 storeUrl(entry));
603a02fd 790 if (fd < 0) {
79a15e0a 791 debug(50, 4) ("httpSocketOpen: %s\n", xstrerror());
fe40a877 792 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 793 err->xerrno = errno;
79a15e0a 794 err->request = requestLink(request);
9b312a19 795 errorAppendEntry(entry, err);
090089c4 796 }
603a02fd 797 return fd;
798}
799
800static HttpStateData *
801httpBuildState(int fd, StoreEntry * entry, request_t * orig_request, peer * e)
802{
cb87dab6 803 HttpStateData *httpState = memAllocate(MEM_HTTP_STATE_DATA);
603a02fd 804 request_t *request;
770f051d 805 storeLockObject(entry);
cb87dab6 806 cbdataAdd(httpState, MEM_HTTP_STATE_DATA);
0a0bf5db 807 httpState->entry = entry;
9e4ad609 808 httpState->fd = fd;
603a02fd 809 if (e) {
7021844c 810 request = memAllocate(MEM_REQUEST_T);
603a02fd 811 request->method = orig_request->method;
812 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
813 request->port = e->http_port;
02922e76 814 stringReset(&request->urlpath, storeUrl(entry));
603a02fd 815 httpState->request = requestLink(request);
1294c0fc 816 httpState->peer = e;
603a02fd 817 httpState->orig_request = requestLink(orig_request);
79a15e0a 818 EBIT_SET(request->flags, REQ_PROXYING);
603a02fd 819 } else {
820 httpState->request = requestLink(orig_request);
79a15e0a 821 httpState->orig_request = requestLink(orig_request);
603a02fd 822 }
0d4d4170 823 /* register the handler to free HTTP state data when the FD closes */
603a02fd 824 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
e102ebda 825 storeRegisterAbort(entry, httpAbort, httpState);
603a02fd 826 return httpState;
827}
828
829void
830httpStart(request_t * request, StoreEntry * entry, peer * e)
831{
832 HttpStateData *httpState;
833 int fd;
834 debug(11, 3) ("httpStart: \"%s %s\"\n",
9fb13bb6 835 RequestMethodStr[request->method], storeUrl(entry));
a0f32775 836 Counter.server.all.requests++;
837 Counter.server.http.requests++;
603a02fd 838 if (e) {
a369131d 839 if (EBIT_TEST(e->options, NEIGHBOR_PROXY_ONLY))
603a02fd 840 storeReleaseRequest(entry);
603a02fd 841 if ((fd = pconnPop(e->host, e->http_port)) >= 0) {
51fdcbd5 842 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
603a02fd 843 httpState = httpBuildState(fd, entry, request, e);
b716a8ad 844 commSetTimeout(httpState->fd,
845 Config.Timeout.connect,
846 httpTimeout,
847 httpState);
603a02fd 848 httpConnectDone(fd, COMM_OK, httpState);
849 return;
850 }
851 } else {
852 if ((fd = pconnPop(request->host, request->port)) >= 0) {
51fdcbd5 853 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
603a02fd 854 httpState = httpBuildState(fd, entry, request, e);
b716a8ad 855 commSetTimeout(httpState->fd,
856 Config.Timeout.connect,
857 httpTimeout,
858 httpState);
603a02fd 859 httpConnectDone(fd, COMM_OK, httpState);
860 return;
861 }
862 }
79a15e0a 863 if ((fd = httpSocketOpen(entry, request)) < 0)
603a02fd 864 return;
865 httpState = httpBuildState(fd, entry, request, e);
603a02fd 866 commSetTimeout(httpState->fd,
867 Config.Timeout.connect,
868 httpTimeout,
cd1fb0eb 869 httpState);
edeb28fd 870 commConnectStart(httpState->fd,
603a02fd 871 httpState->request->host,
872 httpState->request->port,
e924600d 873 httpConnectDone,
874 httpState);
e5f6c5c2 875}
876
efb9218c 877static int
878httpTryRestart(HttpStateData * httpState)
879{
880 /*
881 * We only retry the request if it looks like it was
882 * on a persistent/pipelined connection
883 */
884 if (fd_table[httpState->fd].uses < 2)
885 return 0;
886 if (pumpMethod(httpState->orig_request->method))
887 if (0 == pumpRestart(httpState->orig_request))
888 return 0;
889 return 1;
890}
891
b716a8ad 892static void
893httpRestart(HttpStateData * httpState)
894{
895 /* restart a botched request from a persistent connection */
9fb13bb6 896 debug(11, 2) ("Retrying HTTP request for %s\n", storeUrl(httpState->entry));
901e234d 897 if (pumpMethod(httpState->orig_request->method)) {
efb9218c 898 debug(11, 1) ("Potential Coredump: httpRestart %s %s\n",
899 RequestMethodStr[httpState->orig_request->method],
900 storeUrl(httpState->entry));
e371ed5c 901 }
b716a8ad 902 if (httpState->fd >= 0) {
903 comm_remove_close_handler(httpState->fd, httpStateFree, httpState);
904 comm_close(httpState->fd);
905 httpState->fd = -1;
906 }
79a15e0a 907 httpState->fd = httpSocketOpen(httpState->entry, httpState->orig_request);
b716a8ad 908 if (httpState->fd < 0)
909 return;
910 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
911 commSetTimeout(httpState->fd,
912 Config.Timeout.connect,
913 httpTimeout,
914 httpState);
915 commConnectStart(httpState->fd,
916 httpState->request->host,
917 httpState->request->port,
918 httpConnectDone,
919 httpState);
920}
921
e5f6c5c2 922static void
923httpConnectDone(int fd, int status, void *data)
924{
925 HttpStateData *httpState = data;
926 request_t *request = httpState->request;
927 StoreEntry *entry = httpState->entry;
9b312a19 928 ErrorState *err;
edeb28fd 929 if (status == COMM_ERR_DNS) {
a3d5953d 930 debug(11, 4) ("httpConnectDone: Unknown host: %s\n", request->host);
fe40a877 931 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
9b312a19 932 err->dnsserver_msg = xstrdup(dns_error_message);
79a15e0a 933 err->request = requestLink(httpState->orig_request);
9b312a19 934 errorAppendEntry(entry, err);
edeb28fd 935 comm_close(fd);
936 } else if (status != COMM_OK) {
fe40a877 937 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 938 err->xerrno = errno;
9b312a19 939 err->host = xstrdup(request->host);
940 err->port = request->port;
79a15e0a 941 err->request = requestLink(httpState->orig_request);
9b312a19 942 errorAppendEntry(entry, err);
1294c0fc 943 if (httpState->peer)
944 peerCheckConnectStart(httpState->peer);
e5f6c5c2 945 comm_close(fd);
946 } else {
9fb13bb6 947 fd_note(fd, storeUrl(entry));
b716a8ad 948 fd_table[fd].uses++;
bfcaf585 949 commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0);
090089c4 950 }
090089c4 951}
952
bfcaf585 953static void
954httpAbort(void *data)
955{
956 HttpStateData *httpState = data;
9fb13bb6 957 debug(11, 2) ("httpAbort: %s\n", storeUrl(httpState->entry));
bfcaf585 958 comm_close(httpState->fd);
959}
9b312a19 960
54220df8 961static void
7db8b16d 962httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data)
54220df8 963{
964 HttpStateData *httpState = data;
965 StoreEntry *entry = httpState->entry;
966 ErrorState *err;
967 debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
7db8b16d 968 fd, size, errflag);
54220df8 969 if (size > 0) {
7db8b16d 970 fd_bytes(fd, size, FD_WRITE);
54220df8 971 kb_incr(&Counter.server.all.kbytes_out, size);
972 kb_incr(&Counter.server.http.kbytes_out, size);
973 }
974 if (errflag == COMM_ERR_CLOSING)
7db8b16d 975 return;
54220df8 976 if (errflag) {
7db8b16d 977 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
978 err->xerrno = errno;
979 err->request = requestLink(httpState->orig_request);
980 errorAppendEntry(entry, err);
981 comm_close(fd);
982 return;
54220df8 983 }
7db8b16d 984 pumpStart(fd, entry, httpState->orig_request, httpSendComplete, httpState);
54220df8 985}