]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
From: Mark Treacy <mark@aone.com.au>
[thirdparty/squid.git] / src / http.cc
CommitLineData
30a4f2a8 1/*
df3ac7c0 2 * $Id: http.cc,v 1.135 1996/12/13 20:33:41 wessels Exp $
30a4f2a8 3 *
4 * DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
5 * AUTHOR: Harvest Derived
6 *
42c04c16 7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 8 * --------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from the
11 * Internet community. Development is led by Duane Wessels of the
12 * National Laboratory for Applied Network Research and funded by
13 * the National Science Foundation.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 *
29 */
019dd986 30
31/*
30a4f2a8 32 * Copyright (c) 1994, 1995. All rights reserved.
33 *
34 * The Harvest software was developed by the Internet Research Task
35 * Force Research Group on Resource Discovery (IRTF-RD):
36 *
37 * Mic Bowman of Transarc Corporation.
38 * Peter Danzig of the University of Southern California.
39 * Darren R. Hardy of the University of Colorado at Boulder.
40 * Udi Manber of the University of Arizona.
41 * Michael F. Schwartz of the University of Colorado at Boulder.
42 * Duane Wessels of the University of Colorado at Boulder.
43 *
44 * This copyright notice applies to software in the Harvest
45 * ``src/'' directory only. Users should consult the individual
46 * copyright notices in the ``components/'' subdirectories for
47 * copyright information about other software bundled with the
48 * Harvest source code distribution.
49 *
50 * TERMS OF USE
51 *
52 * The Harvest software may be used and re-distributed without
53 * charge, provided that the software origin and research team are
54 * cited in any use of the system. Most commonly this is
55 * accomplished by including a link to the Harvest Home Page
56 * (http://harvest.cs.colorado.edu/) from the query page of any
57 * Broker you deploy, as well as in the query result pages. These
58 * links are generated automatically by the standard Broker
59 * software distribution.
60 *
61 * The Harvest software is provided ``as is'', without express or
62 * implied warranty, and with no support nor obligation to assist
63 * in its use, correction, modification or enhancement. We assume
64 * no liability with respect to the infringement of copyrights,
65 * trade secrets, or any patents, and are not responsible for
66 * consequential damages. Proper use of the Harvest software is
67 * entirely the responsibility of the user.
68 *
69 * DERIVATIVE WORKS
70 *
71 * Users may make derivative works from the Harvest software, subject
72 * to the following constraints:
73 *
74 * - You must include the above copyright notice and these
75 * accompanying paragraphs in all forms of derivative works,
76 * and any documentation and other materials related to such
77 * distribution and use acknowledge that the software was
78 * developed at the above institutions.
79 *
80 * - You must notify IRTF-RD regarding your distribution of
81 * the derivative work.
82 *
83 * - You must clearly notify users that your are distributing
84 * a modified version and not the original Harvest software.
85 *
86 * - Any derivative product is also subject to these copyright
87 * and use restrictions.
88 *
89 * Note that the Harvest software is NOT in the public domain. We
90 * retain copyright, as specified above.
91 *
92 * HISTORY OF FREE SOFTWARE STATUS
93 *
94 * Originally we required sites to license the software in cases
95 * where they were going to build commercial products/services
96 * around Harvest. In June 1995 we changed this policy. We now
97 * allow people to use the core Harvest software (the code found in
98 * the Harvest ``src/'' directory) for free. We made this change
99 * in the interest of encouraging the widest possible deployment of
100 * the technology. The Harvest software is really a reference
101 * implementation of a set of protocols and formats, some of which
102 * we intend to standardize. We encourage commercial
103 * re-implementations of code complying to this set of standards.
019dd986 104 */
44a47c6e 105
4a83b852 106/*
107 * Anonymizing patch by lutz@as-node.jena.thur.de
108 * have a look into http.anon.c to get more informations.
109 */
110
44a47c6e 111#include "squid.h"
090089c4 112
234967c9 113#define HTTP_DELETE_GAP (1<<18)
090089c4 114
4db43fab 115static const char *const w_space = " \t\n\r";
6bf8443a 116static const char *const crlf = "\r\n";
4db43fab 117
6fb52f6c 118typedef enum {
119 SCC_PUBLIC,
120 SCC_PRIVATE,
121 SCC_NOCACHE,
122 SCC_NOSTORE,
123 SCC_NOTRANSFORM,
124 SCC_MUSTREVALIDATE,
125 SCC_PROXYREVALIDATE,
126 SCC_MAXAGE,
127 SCC_ENUM_END
128} http_server_cc_t;
129
6bf8443a 130enum {
6fb52f6c 131 CCC_NOCACHE,
132 CCC_NOSTORE,
133 CCC_MAXAGE,
134 CCC_MAXSTALE,
135 CCC_MINFRESH,
136 CCC_ONLYIFCACHED,
137 CCC_ENUM_END
6bf8443a 138};
139
140enum {
141 HDR_IMS,
142 HDR_HOST,
df3ac7c0 143 HDR_MAXAGE,
144 HDR_SET_COOKIE
6bf8443a 145};
6fb52f6c 146
147char *HttpServerCCStr[] =
148{
149 "public",
150 "private",
151 "no-cache",
152 "no-store",
153 "no-transform",
154 "must-revalidate",
155 "proxy-revalidate",
156 "max-age",
157 "NONE"
158};
159
24382924 160static struct {
30a4f2a8 161 int parsed;
162 int date;
163 int lm;
164 int exp;
165 int clen;
166 int ctype;
6fb52f6c 167 int cc[SCC_ENUM_END];
30a4f2a8 168} ReplyHeaderStats;
090089c4 169
b177367b 170static void httpStateFree _PARAMS((int fd, void *));
171static void httpReadReplyTimeout _PARAMS((int fd, void *));
172static void httpLifetimeExpire _PARAMS((int fd, void *));
67508012 173static void httpMakePublic _PARAMS((StoreEntry *));
174static void httpMakePrivate _PARAMS((StoreEntry *));
175static void httpCacheNegatively _PARAMS((StoreEntry *));
b177367b 176static void httpReadReply _PARAMS((int fd, void *));
67508012 177static void httpSendComplete _PARAMS((int fd, char *, int, int, void *));
b177367b 178static void httpSendRequest _PARAMS((int fd, void *));
0ee4272b 179static void httpConnect _PARAMS((int fd, const ipcache_addrs *, void *));
e5f6c5c2 180static void httpConnectDone _PARAMS((int fd, int status, void *data));
6bf8443a 181static void httpAppendRequestHeader _PARAMS((char *hdr, const char *line, size_t * sz, size_t max));
182
b8d8561b 183
b177367b 184static void
185httpStateFree(int fd, void *data)
f5558c95 186{
b177367b 187 HttpStateData *httpState = data;
0d4d4170 188 if (httpState == NULL)
b177367b 189 return;
30a4f2a8 190 storeUnlockObject(httpState->entry);
0d4d4170 191 if (httpState->reply_hdr) {
192 put_free_8k_page(httpState->reply_hdr);
193 httpState->reply_hdr = NULL;
194 }
30a4f2a8 195 requestUnlink(httpState->request);
20cc1450 196 requestUnlink(httpState->orig_request);
0d4d4170 197 xfree(httpState);
f5558c95 198}
199
b8d8561b 200int
0ee4272b 201httpCachable(const char *url, int method)
090089c4 202{
090089c4 203 /* GET and HEAD are cachable. Others are not. */
6eb42cae 204 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 205 return 0;
090089c4 206 /* else cachable */
207 return 1;
208}
209
210/* This will be called when timeout on read. */
b8d8561b 211static void
b177367b 212httpReadReplyTimeout(int fd, void *data)
090089c4 213{
b177367b 214 HttpStateData *httpState = data;
090089c4 215 StoreEntry *entry = NULL;
30a4f2a8 216 entry = httpState->entry;
593c9a75 217 debug(11, 4, "httpReadReplyTimeout: FD %d: '%s'\n", fd, entry->url);
b8de7ebe 218 squid_error_entry(entry, ERR_READ_TIMEOUT, NULL);
b177367b 219 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
0d4d4170 220 comm_close(fd);
090089c4 221}
222
223/* This will be called when socket lifetime is expired. */
b8d8561b 224static void
b177367b 225httpLifetimeExpire(int fd, void *data)
090089c4 226{
b177367b 227 HttpStateData *httpState = data;
593c9a75 228 StoreEntry *entry = httpState->entry;
229 debug(11, 4, "httpLifeTimeExpire: FD %d: '%s'\n", fd, entry->url);
ce49f524 230 squid_error_entry(entry, ERR_LIFETIME_EXP, NULL);
b177367b 231 commSetSelect(fd, COMM_SELECT_READ | COMM_SELECT_WRITE, NULL, NULL, 0);
0d4d4170 232 comm_close(fd);
090089c4 233}
234
30a4f2a8 235/* This object can be cached for a long time */
b8d8561b 236static void
237httpMakePublic(StoreEntry * entry)
30a4f2a8 238{
1c481e00 239 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 240 storeSetPublicKey(entry);
241}
242
243/* This object should never be cached at all */
b8d8561b 244static void
245httpMakePrivate(StoreEntry * entry)
30a4f2a8 246{
30a4f2a8 247 storeExpireNow(entry);
1c481e00 248 BIT_RESET(entry->flag, ENTRY_CACHABLE);
30a4f2a8 249 storeReleaseRequest(entry); /* delete object when not used */
250}
251
252/* This object may be negatively cached */
b8d8561b 253static void
254httpCacheNegatively(StoreEntry * entry)
30a4f2a8 255{
79b5cc5f 256 storeNegativeCache(entry);
1c481e00 257 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 258 storeSetPublicKey(entry);
30a4f2a8 259}
260
261
262/* Build a reply structure from HTTP reply headers */
b8d8561b 263void
48f44632 264httpParseReplyHeaders(const char *buf, struct _http_reply *reply)
30a4f2a8 265{
33b589ff 266 char *headers = get_free_4k_page();
267 char *line = get_free_4k_page();
268 char *end;
30a4f2a8 269 char *s = NULL;
33b589ff 270 char *t;
ca98227c 271 time_t delta;
272 size_t l;
30a4f2a8 273
cb24292d 274 reply->code = 500; /* default to Internal Server Error */
30a4f2a8 275 ReplyHeaderStats.parsed++;
33b589ff 276 xstrncpy(headers, buf, 4096);
277 end = mime_headers_end(headers);
278 if (end)
279 reply->hdr_sz = end - headers;
280 for (s = headers; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
281 l = strcspn(s, crlf) + 1;
282 if (l > 4096)
283 l = 4096;
284 xstrncpy(line, s, l);
285 t = line;
286 debug(11, 3, "httpParseReplyHeaders: %s\n", t);
287 if (!strncasecmp(t, "HTTP/", 5)) {
288 sscanf(t + 5, "%lf", &reply->version);
289 if ((t = strchr(t, ' ')))
290 reply->code = atoi(++t);
30a4f2a8 291 } else if (!strncasecmp(t, "Content-type:", 13)) {
33b589ff 292 for (t += 13; isspace(*t); t++);
e91b56e5 293 if ((l = strcspn(t, ";\t ")) > 0)
294 *(t + l) = '\0';
33b589ff 295 xstrncpy(reply->content_type, t, HTTP_REPLY_FIELD_SZ);
296 ReplyHeaderStats.ctype++;
30a4f2a8 297 } else if (!strncasecmp(t, "Content-length:", 15)) {
33b589ff 298 for (t += 15; isspace(*t); t++);
33b589ff 299 reply->content_length = atoi(t);
300 ReplyHeaderStats.clen++;
30a4f2a8 301 } else if (!strncasecmp(t, "Date:", 5)) {
33b589ff 302 for (t += 5; isspace(*t); t++);
303 reply->date = parse_rfc1123(t);
304 ReplyHeaderStats.date++;
30a4f2a8 305 } else if (!strncasecmp(t, "Expires:", 8)) {
33b589ff 306 for (t += 8; isspace(*t); t++);
307 reply->expires = parse_rfc1123(t);
308 /*
309 * The HTTP/1.0 specs says that robust implementations
310 * should consider bad or malformed Expires header as
311 * equivalent to "expires immediately."
312 */
313 if (reply->expires == -1)
314 reply->expires = squid_curtime;
315 ReplyHeaderStats.exp++;
30a4f2a8 316 } else if (!strncasecmp(t, "Last-Modified:", 14)) {
33b589ff 317 for (t += 14; isspace(*t); t++);
318 reply->last_modified = parse_rfc1123(t);
319 ReplyHeaderStats.lm++;
caebbe00 320 } else if (!strncasecmp(t, "Cache-Control:", 14)) {
33b589ff 321 for (t += 14; isspace(*t); t++);
4db43fab 322 if (!strncasecmp(t, "public", 6)) {
323 EBIT_SET(reply->cache_control, SCC_PUBLIC);
324 ReplyHeaderStats.cc[SCC_PUBLIC]++;
325 } else if (!strncasecmp(t, "private", 7)) {
326 EBIT_SET(reply->cache_control, SCC_PRIVATE);
327 ReplyHeaderStats.cc[SCC_PRIVATE]++;
328 } else if (!strncasecmp(t, "no-cache", 8)) {
329 EBIT_SET(reply->cache_control, SCC_NOCACHE);
330 ReplyHeaderStats.cc[SCC_NOCACHE]++;
c1764328 331 } else if (!strncasecmp(t, "no-store", 8)) {
332 EBIT_SET(reply->cache_control, SCC_NOSTORE);
333 ReplyHeaderStats.cc[SCC_NOSTORE]++;
334 } else if (!strncasecmp(t, "no-transform", 12)) {
335 EBIT_SET(reply->cache_control, SCC_NOTRANSFORM);
336 ReplyHeaderStats.cc[SCC_NOTRANSFORM]++;
337 } else if (!strncasecmp(t, "must-revalidate", 15)) {
338 EBIT_SET(reply->cache_control, SCC_MUSTREVALIDATE);
339 ReplyHeaderStats.cc[SCC_MUSTREVALIDATE]++;
340 } else if (!strncasecmp(t, "proxy-revalidate", 16)) {
341 EBIT_SET(reply->cache_control, SCC_PROXYREVALIDATE);
342 ReplyHeaderStats.cc[SCC_PROXYREVALIDATE]++;
4db43fab 343 } else if (!strncasecmp(t, "max-age", 7)) {
344 if ((t = strchr(t, '='))) {
ca98227c 345 delta = (time_t) atoi(++t);
346 reply->expires = squid_curtime + delta;
4db43fab 347 EBIT_SET(reply->cache_control, SCC_MAXAGE);
348 ReplyHeaderStats.cc[SCC_MAXAGE]++;
5df61230 349 }
caebbe00 350 }
df3ac7c0 351 } else if (!strncasecmp(t, "Set-Cookie:", 11)) {
352 EBIT_SET(reply->misc_headers, HDR_SET_COOKIE);
30a4f2a8 353 }
30a4f2a8 354 }
33b589ff 355 put_free_4k_page(headers);
356 put_free_4k_page(line);
30a4f2a8 357}
358
090089c4 359
b8d8561b 360void
0ee4272b 361httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 362{
363 char *t = NULL;
30a4f2a8 364 StoreEntry *entry = httpState->entry;
d3fb4dea 365 int room;
366 int hdr_len;
33b589ff 367 struct _http_reply *reply = entry->mem_obj->reply;
d3fb4dea 368
ed85b771 369 debug(11, 3, "httpProcessReplyHeader: key '%s'\n", entry->key);
f5558c95 370
30a4f2a8 371 if (httpState->reply_hdr == NULL) {
372 httpState->reply_hdr = get_free_8k_page();
373 memset(httpState->reply_hdr, '\0', 8192);
f5558c95 374 }
30a4f2a8 375 if (httpState->reply_hdr_state == 0) {
376 hdr_len = strlen(httpState->reply_hdr);
ed85b771 377 room = 8191 - hdr_len;
30a4f2a8 378 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 379 hdr_len += room < size ? room : size;
30a4f2a8 380 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
60bf30cb 381 debug(11, 3, "httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", entry->key);
30a4f2a8 382 httpState->reply_hdr_state += 2;
33b589ff 383 reply->code = 555;
ed85b771 384 return;
d3fb4dea 385 }
d1a43e28 386 t = httpState->reply_hdr + hdr_len;
387 /* headers can be incomplete only if object still arriving */
f86a6a46 388 if (!httpState->eof)
d1a43e28 389 if ((t = mime_headers_end(httpState->reply_hdr)) == NULL)
390 return; /* headers not complete */
2285407f 391 *t = '\0';
30a4f2a8 392 httpState->reply_hdr_state++;
f5558c95 393 }
30a4f2a8 394 if (httpState->reply_hdr_state == 1) {
395 httpState->reply_hdr_state++;
019dd986 396 debug(11, 9, "GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 397 httpState->reply_hdr);
398 /* Parse headers into reply structure */
48f44632 399 httpParseReplyHeaders(httpState->reply_hdr, reply);
ca98227c 400 storeTimestampsSet(entry);
30a4f2a8 401 /* Check if object is cacheable or not based on reply code */
cb24292d 402 debug(11, 3, "httpProcessReplyHeader: HTTP CODE: %d\n", reply->code);
2285407f 403 switch (reply->code) {
30a4f2a8 404 /* Responses that are cacheable */
f5558c95 405 case 200: /* OK */
4e38e700 406 case 203: /* Non-Authoritative Information */
407 case 300: /* Multiple Choices */
f5558c95 408 case 301: /* Moved Permanently */
4e38e700 409 case 410: /* Gone */
30a4f2a8 410 /* don't cache objects from neighbors w/o LMT, Date, or Expires */
6fb52f6c 411 if (EBIT_TEST(reply->cache_control, SCC_PRIVATE))
caebbe00 412 httpMakePrivate(entry);
6fb52f6c 413 else if (EBIT_TEST(reply->cache_control, SCC_NOCACHE))
caebbe00 414 httpMakePrivate(entry);
df3ac7c0 415 else if (EBIT_TEST(reply->misc_headers, HDR_SET_COOKIE))
416 httpMakePrivate(entry);
ca98227c 417 else if (reply->date > -1)
30a4f2a8 418 httpMakePublic(entry);
ca98227c 419 else if (reply->last_modified > -1)
30a4f2a8 420 httpMakePublic(entry);
421 else if (!httpState->neighbor)
422 httpMakePublic(entry);
ca98227c 423 else if (reply->expires > -1)
30a4f2a8 424 httpMakePublic(entry);
af00901c 425 else if (entry->mem_obj->request->protocol != PROTO_HTTP)
426 /* XXX Remove this check after a while. DW 8/21/96
427 * We won't keep some FTP objects from neighbors running
428 * 1.0.8 or earlier because their ftpget's don't
429 * add a Date: field */
430 httpMakePublic(entry);
30a4f2a8 431 else
432 httpMakePrivate(entry);
433 break;
434 /* Responses that only are cacheable if the server says so */
435 case 302: /* Moved temporarily */
ca98227c 436 if (reply->expires > -1)
30a4f2a8 437 httpMakePublic(entry);
438 else
439 httpMakePrivate(entry);
f5558c95 440 break;
30a4f2a8 441 /* Errors can be negatively cached */
442 case 204: /* No Content */
443 case 305: /* Use Proxy (proxy redirect) */
444 case 400: /* Bad Request */
445 case 403: /* Forbidden */
446 case 404: /* Not Found */
447 case 405: /* Method Now Allowed */
448 case 414: /* Request-URI Too Long */
449 case 500: /* Internal Server Error */
450 case 501: /* Not Implemented */
451 case 502: /* Bad Gateway */
452 case 503: /* Service Unavailable */
453 case 504: /* Gateway Timeout */
851eeef7 454 httpCacheNegatively(entry);
30a4f2a8 455 break;
456 /* Some responses can never be cached */
457 case 303: /* See Other */
234967c9 458 case 304: /* Not Modified */
4e38e700 459 case 401: /* Unauthorized */
460 case 407: /* Proxy Authentication Required */
30a4f2a8 461 default: /* Unknown status code */
462 httpMakePrivate(entry);
4e38e700 463 break;
f5558c95 464 }
465 }
466}
467
090089c4 468
469/* This will be called when data is ready to be read from fd. Read until
470 * error or connection closed. */
f5558c95 471/* XXX this function is too long! */
b8d8561b 472static void
b177367b 473httpReadReply(int fd, void *data)
090089c4 474{
b177367b 475 HttpStateData *httpState = data;
95d659f0 476 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
090089c4 477 int len;
30a4f2a8 478 int bin;
090089c4 479 int clen;
480 int off;
481 StoreEntry *entry = NULL;
482
30a4f2a8 483 entry = httpState->entry;
234967c9 484 if (entry->flag & DELETE_BEHIND && !storeClientWaiting(entry)) {
485 /* we can terminate connection right now */
486 squid_error_entry(entry, ERR_NO_CLIENTS_BIG_OBJ, NULL);
487 comm_close(fd);
488 return;
489 }
490 /* check if we want to defer reading */
491 clen = entry->mem_obj->e_current_len;
492 off = storeGetLowestReaderOffset(entry);
493 if ((clen - off) > HTTP_DELETE_GAP) {
30a4f2a8 494 if (entry->flag & CLIENT_ABORT_REQUEST) {
495 squid_error_entry(entry, ERR_CLIENT_ABORT, NULL);
496 comm_close(fd);
497 return;
498 }
499 IOStats.Http.reads_deferred++;
234967c9 500 debug(11, 3, "httpReadReply: Read deferred for Object: %s\n",
501 entry->url);
502 debug(11, 3, " Current Gap: %d bytes\n", clen - off);
503 /* reschedule, so it will be automatically reactivated
504 * when Gap is big enough. */
b177367b 505 commSetSelect(fd,
234967c9 506 COMM_SELECT_READ,
b177367b 507 httpReadReply,
508 (void *) httpState, 0);
30a4f2a8 509 /* disable read timeout until we are below the GAP */
b177367b 510 commSetSelect(fd,
234967c9 511 COMM_SELECT_TIMEOUT,
b177367b 512 NULL,
234967c9 513 (void *) NULL,
514 (time_t) 0);
56fa4cad 515 if (!BIT_TEST(entry->flag, READ_DEFERRED)) {
516 comm_set_fd_lifetime(fd, 3600); /* limit during deferring */
517 BIT_SET(entry->flag, READ_DEFERRED);
518 }
234967c9 519 /* dont try reading again for a while */
b6f794d6 520 comm_set_stall(fd, Config.stallDelay);
234967c9 521 return;
56fa4cad 522 } else {
523 BIT_RESET(entry->flag, READ_DEFERRED);
090089c4 524 }
1513873c 525 errno = 0;
30a4f2a8 526 len = read(fd, buf, SQUID_TCP_SO_RCVBUF);
019dd986 527 debug(11, 5, "httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 528 comm_set_fd_lifetime(fd, 86400); /* extend after good read */
529 if (len > 0) {
4a63c85f 530 IOStats.Http.reads++;
30a4f2a8 531 for (clen = len - 1, bin = 0; clen; bin++)
532 clen >>= 1;
533 IOStats.Http.read_hist[bin]++;
534 }
ba718c8f 535 if (len < 0) {
881f7a6c 536 debug(50, 2, "httpReadReply: FD %d: read failure: %s.\n",
090089c4 537 fd, xstrerror());
ba718c8f 538 if (errno == EAGAIN || errno == EWOULDBLOCK) {
1513873c 539 /* reinstall handlers */
6fe6313d 540 /* XXX This may loop forever */
b177367b 541 commSetSelect(fd, COMM_SELECT_READ,
542 httpReadReply, (void *) httpState, 0);
543 commSetSelect(fd, COMM_SELECT_TIMEOUT,
544 httpReadReplyTimeout, (void *) httpState, Config.readTimeout);
090089c4 545 } else {
1c481e00 546 BIT_RESET(entry->flag, ENTRY_CACHABLE);
2daae136 547 storeReleaseRequest(entry);
b8de7ebe 548 squid_error_entry(entry, ERR_READ_ERROR, xstrerror());
0d4d4170 549 comm_close(fd);
090089c4 550 }
ba718c8f 551 } else if (len == 0 && entry->mem_obj->e_current_len == 0) {
f86a6a46 552 httpState->eof = 1;
b8de7ebe 553 squid_error_entry(entry,
ba718c8f 554 ERR_ZERO_SIZE_OBJECT,
555 errno ? xstrerror() : NULL);
0d4d4170 556 comm_close(fd);
090089c4 557 } else if (len == 0) {
558 /* Connection closed; retrieval done. */
f86a6a46 559 httpState->eof = 1;
d1a43e28 560 if (httpState->reply_hdr_state < 2)
561 httpProcessReplyHeader(httpState, buf, len);
562 storeAppend(entry, buf, len); /* invoke handlers! */
563 storeComplete(entry); /* deallocates mem_obj->request */
0d4d4170 564 comm_close(fd);
090089c4 565 } else if (entry->flag & CLIENT_ABORT_REQUEST) {
566 /* append the last bit of info we get */
567 storeAppend(entry, buf, len);
b8de7ebe 568 squid_error_entry(entry, ERR_CLIENT_ABORT, NULL);
0d4d4170 569 comm_close(fd);
090089c4 570 } else {
d1a43e28 571 if (httpState->reply_hdr_state < 2)
30a4f2a8 572 httpProcessReplyHeader(httpState, buf, len);
620da955 573 storeAppend(entry, buf, len);
b177367b 574 commSetSelect(fd,
ba718c8f 575 COMM_SELECT_READ,
b177367b 576 httpReadReply,
577 (void *) httpState, 0);
578 commSetSelect(fd,
ba718c8f 579 COMM_SELECT_TIMEOUT,
b177367b 580 httpReadReplyTimeout,
30a4f2a8 581 (void *) httpState,
b6f794d6 582 Config.readTimeout);
090089c4 583 }
584}
585
586/* This will be called when request write is complete. Schedule read of
587 * reply. */
b8d8561b 588static void
589httpSendComplete(int fd, char *buf, int size, int errflag, void *data)
090089c4 590{
30a4f2a8 591 HttpStateData *httpState = data;
090089c4 592 StoreEntry *entry = NULL;
593
30a4f2a8 594 entry = httpState->entry;
019dd986 595 debug(11, 5, "httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 596 fd, size, errflag);
597
090089c4 598 if (errflag) {
b8de7ebe 599 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
0d4d4170 600 comm_close(fd);
090089c4 601 return;
602 } else {
603 /* Schedule read reply. */
b177367b 604 commSetSelect(fd,
019dd986 605 COMM_SELECT_READ,
b177367b 606 httpReadReply,
607 (void *) httpState, 0);
608 commSetSelect(fd,
019dd986 609 COMM_SELECT_TIMEOUT,
b177367b 610 httpReadReplyTimeout,
30a4f2a8 611 (void *) httpState,
b6f794d6 612 Config.readTimeout);
30a4f2a8 613 comm_set_fd_lifetime(fd, 86400); /* extend lifetime */
090089c4 614 }
615}
616
4a83b852 617#ifdef USE_ANONYMIZER
618#include "http-anon.c"
619#endif
620
6bf8443a 621static void
622httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max)
623{
624 size_t n = *sz + strlen(line) + 2;
625 if (n >= max)
626 return;
4a83b852 627#ifdef USE_ANONYMIZER
628 if (!httpAnonSearchHeaderField(http_anon_allowed_header, line)) {
629 debug(11, 5, "httpAppendRequestHeader: removed for anonymity: <%s>\n",
4e5c19a7 630 line);
4a83b852 631 return;
632 }
633#endif
634 /* allowed header, explicitly known to be not dangerous */
6bf8443a 635 debug(11, 5, "httpAppendRequestHeader: %s\n", line);
929545fe 636 strcpy(hdr + (*sz), line);
6bf8443a 637 strcat(hdr + (*sz), crlf);
638 *sz = n;
639}
640
641size_t
642httpBuildRequestHeader(request_t * request,
643 request_t * orig_request,
644 StoreEntry * entry,
645 char *hdr_in,
646 size_t * in_len,
647 char *hdr_out,
648 size_t out_sz,
649 int cfd)
650{
651 char *xbuf = get_free_4k_page();
872b720f 652 char *ybuf = get_free_8k_page();
6bf8443a 653 char *viabuf = get_free_4k_page();
654 char *fwdbuf = get_free_4k_page();
655 char *t = NULL;
656 char *s = NULL;
657 char *end = NULL;
658 size_t len = 0;
659 size_t hdr_len = 0;
660 size_t in_sz;
661 size_t content_length = 0;
662 size_t l;
663 int hdr_flags = 0;
664 const char *url = NULL;
665
666 debug(11, 3, "httpBuildRequestHeader: INPUT:\n%s\n", hdr_in);
667 xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096);
668 xstrncpy(viabuf, "Via: ", 4096);
669 sprintf(ybuf, "%s %s HTTP/1.0",
670 RequestMethodStr[request->method],
671 *request->urlpath ? request->urlpath : "/");
672 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
673 /* Add IMS header */
674 if (entry && entry->lastmod && request->method == METHOD_GET) {
675 sprintf(ybuf, "If-Modified-Since: %s", mkrfc1123(entry->lastmod));
676 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
677 EBIT_SET(hdr_flags, HDR_IMS);
678 }
00c59270 679 end = mime_headers_end(hdr_in);
6bf8443a 680 in_sz = strlen(hdr_in);
681 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
682 hdr_len = t - hdr_in;
683 if (in_sz - hdr_len <= content_length)
684 break;
685 l = strcspn(t, crlf) + 1;
686 if (l > 4096)
687 l = 4096;
688 xstrncpy(xbuf, t, l);
a7011ca4 689 debug(11, 5, "httpBuildRequestHeader: %s\n", xbuf);
6bf8443a 690 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
691 continue;
692 if (strncasecmp(xbuf, "Connection:", 11) == 0)
693 continue;
694 if (strncasecmp(xbuf, "Host:", 5) == 0)
695 EBIT_SET(hdr_flags, HDR_HOST);
696 if (strncasecmp(xbuf, "Content-length:", 15) == 0) {
697 for (s = xbuf + 15; *s && isspace(*s); s++);
698 content_length = (size_t) atoi(s);
699 }
700 if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) {
701 for (s = xbuf + 14; *s && isspace(*s); s++);
702 if (strncasecmp(s, "Max-age=", 8) == 0)
703 EBIT_SET(hdr_flags, HDR_MAXAGE);
704 }
705 if (strncasecmp(xbuf, "Via:", 4) == 0) {
706 for (s = xbuf + 4; *s && isspace(*s); s++);
707 if (strlen(viabuf) + strlen(s) < 4000)
708 strcat(viabuf, s);
709 strcat(viabuf, ", ");
710 continue;
711 }
712 if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) {
713 for (s = xbuf + 16; *s && isspace(*s); s++);
714 if (strlen(fwdbuf) + strlen(s) < 4000)
715 strcat(fwdbuf, s);
716 strcat(fwdbuf, ", ");
717 continue;
718 }
43f1c650 719 if (strncasecmp(xbuf, "If-Modified-Since:", 18) == 0)
6bf8443a 720 if (EBIT_TEST(hdr_flags, HDR_IMS))
721 continue;
722 httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512);
723 }
724 hdr_len = t - hdr_in;
725 /* Append Via: */
43f1c650 726 sprintf(ybuf, "%3.1f %s", orig_request->http_ver, ThisCache);
6bf8443a 727 strcat(viabuf, ybuf);
728 httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz);
729 /* Append to X-Forwarded-For: */
a08307eb 730 strcat(fwdbuf, cfd < 0 ? "unknown" : fd_table[cfd].ipaddr);
6bf8443a 731 httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz);
732 if (!EBIT_TEST(hdr_flags, HDR_HOST)) {
733 sprintf(ybuf, "Host: %s", orig_request->host);
734 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
735 }
736 if (!EBIT_TEST(hdr_flags, HDR_MAXAGE)) {
737 url = entry ? entry->url : urlCanonical(orig_request, NULL);
738 sprintf(ybuf, "Cache-control: Max-age=%d", (int) getMaxAge(url));
739 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
740 }
741 httpAppendRequestHeader(hdr_out, null_string, &len, out_sz);
742 put_free_4k_page(xbuf);
872b720f 743 put_free_8k_page(ybuf);
6bf8443a 744 put_free_4k_page(viabuf);
745 put_free_4k_page(fwdbuf);
746 if (in_len)
747 *in_len = hdr_len;
9d9d144b 748 if ((l = strlen(hdr_out)) != len) {
749 debug_trap("httpBuildRequestHeader: size mismatch");
750 len = l;
751 }
6bf8443a 752 debug(11, 3, "httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out);
753 return len;
754}
755
090089c4 756/* This will be called when connect completes. Write request. */
b8d8561b 757static void
b177367b 758httpSendRequest(int fd, void *data)
090089c4 759{
b177367b 760 HttpStateData *httpState = data;
090089c4 761 char *buf = NULL;
090089c4 762 int len = 0;
763 int buflen;
30a4f2a8 764 request_t *req = httpState->request;
9864ee44 765 int buftype = 0;
620da955 766 StoreEntry *entry = httpState->entry;
2a26c096 767 int cfd;
090089c4 768
30a4f2a8 769 debug(11, 5, "httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
6bf8443a 770 buflen = strlen(req->urlpath);
30a4f2a8 771 if (httpState->req_hdr)
4768dbe0 772 buflen += httpState->req_hdr_sz + 1;
090089c4 773 buflen += 512; /* lots of extra */
774
78657218 775 if ((req->method == METHOD_POST || req->method == METHOD_PUT)) {
776 debug_trap("httpSendRequest: should not be handling POST/PUT request");
777 return;
090089c4 778 }
30a4f2a8 779 if (buflen < DISK_PAGE_SIZE) {
9864ee44 780 buf = get_free_8k_page();
781 memset(buf, '\0', buflen);
782 buftype = BUF_TYPE_8K;
6bf8443a 783 buflen = DISK_PAGE_SIZE;
30a4f2a8 784 } else {
9864ee44 785 buf = xcalloc(buflen, 1);
786 buftype = BUF_TYPE_MALLOC;
090089c4 787 }
2a26c096 788 if (!opt_forwarded_for)
6bf8443a 789 cfd = -1;
2a26c096 790 else if (entry->mem_obj == NULL)
6bf8443a 791 cfd = -1;
2a26c096 792 else
6bf8443a 793 cfd = storeFirstClientFD(entry->mem_obj);
794 len = httpBuildRequestHeader(req,
795 httpState->orig_request ? httpState->orig_request : req,
796 entry,
797 httpState->req_hdr,
798 NULL,
799 buf,
800 buflen,
801 cfd);
67508012 802 debug(11, 6, "httpSendRequest: FD %d:\n%s\n", fd, buf);
30a4f2a8 803 comm_write(fd,
14e59844 804 buf,
805 len,
806 30,
807 httpSendComplete,
9864ee44 808 httpState,
809 buftype == BUF_TYPE_8K ? put_free_8k_page : xfree);
20cc1450 810 requestUnlink(httpState->orig_request);
811 httpState->orig_request = NULL;
090089c4 812}
813
b8d8561b 814int
20cc1450 815proxyhttpStart(const char *url,
816 request_t * orig_request,
817 StoreEntry * entry,
818 edge * e)
090089c4 819{
f5558c95 820 int sock;
30a4f2a8 821 HttpStateData *httpState = NULL;
7111c86a 822 request_t *request = NULL;
090089c4 823
7111c86a 824 debug(11, 3, "proxyhttpStart: \"%s %s\"\n",
efbb0a25 825 RequestMethodStr[entry->method], url);
019dd986 826 debug(11, 10, "proxyhttpStart: HTTP request header:\n%s\n",
22e4fa85 827 entry->mem_obj->mime_hdr);
090089c4 828
30a4f2a8 829 if (e->options & NEIGHBOR_PROXY_ONLY)
090089c4 830 storeStartDeleteBehind(entry);
831
832 /* Create socket. */
16b204c4 833 sock = comm_open(SOCK_STREAM,
834 0,
835 Config.Addrs.tcp_outgoing,
836 0,
837 COMM_NONBLOCKING,
838 url);
090089c4 839 if (sock == COMM_ERROR) {
019dd986 840 debug(11, 4, "proxyhttpStart: Failed because we're out of sockets.\n");
b8de7ebe 841 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 842 return COMM_ERROR;
843 }
30a4f2a8 844 httpState = xcalloc(1, sizeof(HttpStateData));
845 storeLockObject(httpState->entry = entry, NULL, NULL);
846 httpState->req_hdr = entry->mem_obj->mime_hdr;
4768dbe0 847 httpState->req_hdr_sz = entry->mem_obj->mime_hdr_sz;
30a4f2a8 848 request = get_free_request_t();
849 httpState->request = requestLink(request);
850 httpState->neighbor = e;
20cc1450 851 httpState->orig_request = requestLink(orig_request);
0d4d4170 852 /* register the handler to free HTTP state data when the FD closes */
30a4f2a8 853 comm_add_close_handler(sock,
b177367b 854 httpStateFree,
30a4f2a8 855 (void *) httpState);
784213dc 856 request->method = entry->method;
d5aa0e3b 857 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
30a4f2a8 858 request->port = e->http_port;
d5aa0e3b 859 xstrncpy(request->urlpath, url, MAX_URL);
313c1c28 860 BIT_SET(request->flags, REQ_PROXYING);
a13d9042 861 ipcache_nbgethostbyname(request->host,
862 sock,
b15fe823 863 httpConnect,
a13d9042 864 httpState);
865 return COMM_OK;
866}
867
b15fe823 868static void
fe4e214f 869httpConnect(int fd, const ipcache_addrs * ia, void *data)
a13d9042 870{
871 HttpStateData *httpState = data;
872 request_t *request = httpState->request;
873 StoreEntry *entry = httpState->entry;
e5f6c5c2 874 if (ia == NULL) {
a13d9042 875 debug(11, 4, "httpConnect: Unknown host: %s\n", request->host);
b8de7ebe 876 squid_error_entry(entry, ERR_DNS_FAIL, dns_error_message);
a13d9042 877 comm_close(fd);
b15fe823 878 return;
090089c4 879 }
880 /* Open connection. */
e5f6c5c2 881 httpState->connectState.fd = fd;
882 httpState->connectState.host = request->host;
883 httpState->connectState.port = request->port;
884 httpState->connectState.handler = httpConnectDone;
885 httpState->connectState.data = httpState;
886 comm_nbconnect(fd, &httpState->connectState);
887}
888
889static void
890httpConnectDone(int fd, int status, void *data)
891{
892 HttpStateData *httpState = data;
893 request_t *request = httpState->request;
894 StoreEntry *entry = httpState->entry;
895 edge *e = NULL;
896 if (status != COMM_OK) {
5269d0bd 897 if ((e = httpState->neighbor))
e5f6c5c2 898 e->last_fail_time = squid_curtime;
e5f6c5c2 899 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
900 comm_close(fd);
901 } else {
902 /* Install connection complete handler. */
903 if (opt_no_ipcache)
904 ipcacheInvalidate(request->host);
905 fd_note(fd, entry->url);
b177367b 906 commSetSelect(fd, COMM_SELECT_LIFETIME,
907 httpLifetimeExpire, (void *) httpState, 0);
908 commSetSelect(fd, COMM_SELECT_WRITE,
909 httpSendRequest, (void *) httpState, 0);
9d4b2981 910 if (vizSock > -1)
ef2d27ff 911 vizHackSendPkt(&httpState->connectState.S, 2);
090089c4 912 }
090089c4 913}
914
b8d8561b 915int
20cc1450 916httpStart(char *url,
4768dbe0 917 request_t * request,
918 char *req_hdr,
919 int req_hdr_sz,
920 StoreEntry * entry)
090089c4 921{
922 /* Create state structure. */
a13d9042 923 int sock;
30a4f2a8 924 HttpStateData *httpState = NULL;
090089c4 925
7111c86a 926 debug(11, 3, "httpStart: \"%s %s\"\n",
927 RequestMethodStr[request->method], url);
019dd986 928 debug(11, 10, "httpStart: req_hdr '%s'\n", req_hdr);
090089c4 929
090089c4 930 /* Create socket. */
16b204c4 931 sock = comm_open(SOCK_STREAM,
932 0,
933 Config.Addrs.tcp_outgoing,
934 0,
935 COMM_NONBLOCKING,
936 url);
090089c4 937 if (sock == COMM_ERROR) {
019dd986 938 debug(11, 4, "httpStart: Failed because we're out of sockets.\n");
b8de7ebe 939 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 940 return COMM_ERROR;
941 }
30a4f2a8 942 httpState = xcalloc(1, sizeof(HttpStateData));
943 storeLockObject(httpState->entry = entry, NULL, NULL);
944 httpState->req_hdr = req_hdr;
4768dbe0 945 httpState->req_hdr_sz = req_hdr_sz;
30a4f2a8 946 httpState->request = requestLink(request);
947 comm_add_close_handler(sock,
b177367b 948 httpStateFree,
30a4f2a8 949 (void *) httpState);
a13d9042 950 ipcache_nbgethostbyname(request->host,
951 sock,
952 httpConnect,
953 httpState);
090089c4 954 return COMM_OK;
955}
30a4f2a8 956
b8d8561b 957void
958httpReplyHeaderStats(StoreEntry * entry)
30a4f2a8 959{
6fb52f6c 960 http_server_cc_t i;
30a4f2a8 961 storeAppendPrintf(entry, open_bracket);
6fb52f6c 962 storeAppendPrintf(entry, "{HTTP Reply Headers:}\n");
963 storeAppendPrintf(entry, "{ Headers parsed: %d}\n",
30a4f2a8 964 ReplyHeaderStats.parsed);
6fb52f6c 965 storeAppendPrintf(entry, "{ Date: %d}\n",
30a4f2a8 966 ReplyHeaderStats.date);
6fb52f6c 967 storeAppendPrintf(entry, "{ Last-Modified: %d}\n",
30a4f2a8 968 ReplyHeaderStats.lm);
6fb52f6c 969 storeAppendPrintf(entry, "{ Expires: %d}\n",
30a4f2a8 970 ReplyHeaderStats.exp);
6fb52f6c 971 storeAppendPrintf(entry, "{ Content-Type: %d}\n",
30a4f2a8 972 ReplyHeaderStats.ctype);
6fb52f6c 973 storeAppendPrintf(entry, "{ Content-Length: %d}\n",
30a4f2a8 974 ReplyHeaderStats.clen);
4db43fab 975 for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++)
6fb52f6c 976 storeAppendPrintf(entry, "{Cache-Control %7.7s: %d}\n",
977 HttpServerCCStr[i],
978 ReplyHeaderStats.cc[i]);
30a4f2a8 979 storeAppendPrintf(entry, close_bracket);
980}