]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
fix poll() arg types
[thirdparty/squid.git] / src / http.cc
CommitLineData
30a4f2a8 1/*
66f7337b 2 * $Id: http.cc,v 1.136 1996/12/13 22:10:30 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;
6bf8443a 661 size_t l;
662 int hdr_flags = 0;
663 const char *url = NULL;
664
665 debug(11, 3, "httpBuildRequestHeader: INPUT:\n%s\n", hdr_in);
666 xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096);
667 xstrncpy(viabuf, "Via: ", 4096);
668 sprintf(ybuf, "%s %s HTTP/1.0",
669 RequestMethodStr[request->method],
670 *request->urlpath ? request->urlpath : "/");
671 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
672 /* Add IMS header */
673 if (entry && entry->lastmod && request->method == METHOD_GET) {
674 sprintf(ybuf, "If-Modified-Since: %s", mkrfc1123(entry->lastmod));
675 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
676 EBIT_SET(hdr_flags, HDR_IMS);
677 }
00c59270 678 end = mime_headers_end(hdr_in);
6bf8443a 679 in_sz = strlen(hdr_in);
680 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
681 hdr_len = t - hdr_in;
6bf8443a 682 l = strcspn(t, crlf) + 1;
683 if (l > 4096)
684 l = 4096;
685 xstrncpy(xbuf, t, l);
a7011ca4 686 debug(11, 5, "httpBuildRequestHeader: %s\n", xbuf);
6bf8443a 687 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
688 continue;
689 if (strncasecmp(xbuf, "Connection:", 11) == 0)
690 continue;
66f7337b 691 if (strncasecmp(xbuf, "Host:", 5) == 0) {
6bf8443a 692 EBIT_SET(hdr_flags, HDR_HOST);
66f7337b 693 } else if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) {
6bf8443a 694 for (s = xbuf + 14; *s && isspace(*s); s++);
695 if (strncasecmp(s, "Max-age=", 8) == 0)
696 EBIT_SET(hdr_flags, HDR_MAXAGE);
66f7337b 697 } else if (strncasecmp(xbuf, "Via:", 4) == 0) {
6bf8443a 698 for (s = xbuf + 4; *s && isspace(*s); s++);
699 if (strlen(viabuf) + strlen(s) < 4000)
700 strcat(viabuf, s);
701 strcat(viabuf, ", ");
702 continue;
66f7337b 703 } else if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) {
6bf8443a 704 for (s = xbuf + 16; *s && isspace(*s); s++);
705 if (strlen(fwdbuf) + strlen(s) < 4000)
706 strcat(fwdbuf, s);
707 strcat(fwdbuf, ", ");
708 continue;
66f7337b 709 } else if (strncasecmp(xbuf, "If-Modified-Since:", 18) == 0) {
6bf8443a 710 if (EBIT_TEST(hdr_flags, HDR_IMS))
711 continue;
66f7337b 712 }
6bf8443a 713 httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512);
714 }
715 hdr_len = t - hdr_in;
716 /* Append Via: */
43f1c650 717 sprintf(ybuf, "%3.1f %s", orig_request->http_ver, ThisCache);
6bf8443a 718 strcat(viabuf, ybuf);
719 httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz);
720 /* Append to X-Forwarded-For: */
a08307eb 721 strcat(fwdbuf, cfd < 0 ? "unknown" : fd_table[cfd].ipaddr);
6bf8443a 722 httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz);
723 if (!EBIT_TEST(hdr_flags, HDR_HOST)) {
724 sprintf(ybuf, "Host: %s", orig_request->host);
725 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
726 }
727 if (!EBIT_TEST(hdr_flags, HDR_MAXAGE)) {
728 url = entry ? entry->url : urlCanonical(orig_request, NULL);
729 sprintf(ybuf, "Cache-control: Max-age=%d", (int) getMaxAge(url));
730 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
731 }
732 httpAppendRequestHeader(hdr_out, null_string, &len, out_sz);
733 put_free_4k_page(xbuf);
872b720f 734 put_free_8k_page(ybuf);
6bf8443a 735 put_free_4k_page(viabuf);
736 put_free_4k_page(fwdbuf);
737 if (in_len)
738 *in_len = hdr_len;
9d9d144b 739 if ((l = strlen(hdr_out)) != len) {
740 debug_trap("httpBuildRequestHeader: size mismatch");
741 len = l;
742 }
6bf8443a 743 debug(11, 3, "httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out);
744 return len;
745}
746
090089c4 747/* This will be called when connect completes. Write request. */
b8d8561b 748static void
b177367b 749httpSendRequest(int fd, void *data)
090089c4 750{
b177367b 751 HttpStateData *httpState = data;
090089c4 752 char *buf = NULL;
090089c4 753 int len = 0;
754 int buflen;
30a4f2a8 755 request_t *req = httpState->request;
9864ee44 756 int buftype = 0;
620da955 757 StoreEntry *entry = httpState->entry;
2a26c096 758 int cfd;
090089c4 759
30a4f2a8 760 debug(11, 5, "httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
6bf8443a 761 buflen = strlen(req->urlpath);
30a4f2a8 762 if (httpState->req_hdr)
4768dbe0 763 buflen += httpState->req_hdr_sz + 1;
090089c4 764 buflen += 512; /* lots of extra */
765
78657218 766 if ((req->method == METHOD_POST || req->method == METHOD_PUT)) {
767 debug_trap("httpSendRequest: should not be handling POST/PUT request");
768 return;
090089c4 769 }
30a4f2a8 770 if (buflen < DISK_PAGE_SIZE) {
9864ee44 771 buf = get_free_8k_page();
772 memset(buf, '\0', buflen);
773 buftype = BUF_TYPE_8K;
6bf8443a 774 buflen = DISK_PAGE_SIZE;
30a4f2a8 775 } else {
9864ee44 776 buf = xcalloc(buflen, 1);
777 buftype = BUF_TYPE_MALLOC;
090089c4 778 }
2a26c096 779 if (!opt_forwarded_for)
6bf8443a 780 cfd = -1;
2a26c096 781 else if (entry->mem_obj == NULL)
6bf8443a 782 cfd = -1;
2a26c096 783 else
6bf8443a 784 cfd = storeFirstClientFD(entry->mem_obj);
785 len = httpBuildRequestHeader(req,
786 httpState->orig_request ? httpState->orig_request : req,
787 entry,
788 httpState->req_hdr,
789 NULL,
790 buf,
791 buflen,
792 cfd);
67508012 793 debug(11, 6, "httpSendRequest: FD %d:\n%s\n", fd, buf);
30a4f2a8 794 comm_write(fd,
14e59844 795 buf,
796 len,
797 30,
798 httpSendComplete,
9864ee44 799 httpState,
800 buftype == BUF_TYPE_8K ? put_free_8k_page : xfree);
20cc1450 801 requestUnlink(httpState->orig_request);
802 httpState->orig_request = NULL;
090089c4 803}
804
b8d8561b 805int
20cc1450 806proxyhttpStart(const char *url,
807 request_t * orig_request,
808 StoreEntry * entry,
809 edge * e)
090089c4 810{
f5558c95 811 int sock;
30a4f2a8 812 HttpStateData *httpState = NULL;
7111c86a 813 request_t *request = NULL;
090089c4 814
7111c86a 815 debug(11, 3, "proxyhttpStart: \"%s %s\"\n",
efbb0a25 816 RequestMethodStr[entry->method], url);
019dd986 817 debug(11, 10, "proxyhttpStart: HTTP request header:\n%s\n",
22e4fa85 818 entry->mem_obj->mime_hdr);
090089c4 819
30a4f2a8 820 if (e->options & NEIGHBOR_PROXY_ONLY)
090089c4 821 storeStartDeleteBehind(entry);
822
823 /* Create socket. */
16b204c4 824 sock = comm_open(SOCK_STREAM,
825 0,
826 Config.Addrs.tcp_outgoing,
827 0,
828 COMM_NONBLOCKING,
829 url);
090089c4 830 if (sock == COMM_ERROR) {
019dd986 831 debug(11, 4, "proxyhttpStart: Failed because we're out of sockets.\n");
b8de7ebe 832 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 833 return COMM_ERROR;
834 }
30a4f2a8 835 httpState = xcalloc(1, sizeof(HttpStateData));
836 storeLockObject(httpState->entry = entry, NULL, NULL);
837 httpState->req_hdr = entry->mem_obj->mime_hdr;
4768dbe0 838 httpState->req_hdr_sz = entry->mem_obj->mime_hdr_sz;
30a4f2a8 839 request = get_free_request_t();
840 httpState->request = requestLink(request);
841 httpState->neighbor = e;
20cc1450 842 httpState->orig_request = requestLink(orig_request);
0d4d4170 843 /* register the handler to free HTTP state data when the FD closes */
30a4f2a8 844 comm_add_close_handler(sock,
b177367b 845 httpStateFree,
30a4f2a8 846 (void *) httpState);
784213dc 847 request->method = entry->method;
d5aa0e3b 848 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
30a4f2a8 849 request->port = e->http_port;
d5aa0e3b 850 xstrncpy(request->urlpath, url, MAX_URL);
313c1c28 851 BIT_SET(request->flags, REQ_PROXYING);
a13d9042 852 ipcache_nbgethostbyname(request->host,
853 sock,
b15fe823 854 httpConnect,
a13d9042 855 httpState);
856 return COMM_OK;
857}
858
b15fe823 859static void
fe4e214f 860httpConnect(int fd, const ipcache_addrs * ia, void *data)
a13d9042 861{
862 HttpStateData *httpState = data;
863 request_t *request = httpState->request;
864 StoreEntry *entry = httpState->entry;
e5f6c5c2 865 if (ia == NULL) {
a13d9042 866 debug(11, 4, "httpConnect: Unknown host: %s\n", request->host);
b8de7ebe 867 squid_error_entry(entry, ERR_DNS_FAIL, dns_error_message);
a13d9042 868 comm_close(fd);
b15fe823 869 return;
090089c4 870 }
871 /* Open connection. */
e5f6c5c2 872 httpState->connectState.fd = fd;
873 httpState->connectState.host = request->host;
874 httpState->connectState.port = request->port;
875 httpState->connectState.handler = httpConnectDone;
876 httpState->connectState.data = httpState;
877 comm_nbconnect(fd, &httpState->connectState);
878}
879
880static void
881httpConnectDone(int fd, int status, void *data)
882{
883 HttpStateData *httpState = data;
884 request_t *request = httpState->request;
885 StoreEntry *entry = httpState->entry;
886 edge *e = NULL;
887 if (status != COMM_OK) {
5269d0bd 888 if ((e = httpState->neighbor))
e5f6c5c2 889 e->last_fail_time = squid_curtime;
e5f6c5c2 890 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
891 comm_close(fd);
892 } else {
893 /* Install connection complete handler. */
894 if (opt_no_ipcache)
895 ipcacheInvalidate(request->host);
896 fd_note(fd, entry->url);
b177367b 897 commSetSelect(fd, COMM_SELECT_LIFETIME,
898 httpLifetimeExpire, (void *) httpState, 0);
899 commSetSelect(fd, COMM_SELECT_WRITE,
900 httpSendRequest, (void *) httpState, 0);
9d4b2981 901 if (vizSock > -1)
ef2d27ff 902 vizHackSendPkt(&httpState->connectState.S, 2);
090089c4 903 }
090089c4 904}
905
b8d8561b 906int
20cc1450 907httpStart(char *url,
4768dbe0 908 request_t * request,
909 char *req_hdr,
910 int req_hdr_sz,
911 StoreEntry * entry)
090089c4 912{
913 /* Create state structure. */
a13d9042 914 int sock;
30a4f2a8 915 HttpStateData *httpState = NULL;
090089c4 916
7111c86a 917 debug(11, 3, "httpStart: \"%s %s\"\n",
918 RequestMethodStr[request->method], url);
019dd986 919 debug(11, 10, "httpStart: req_hdr '%s'\n", req_hdr);
090089c4 920
090089c4 921 /* Create socket. */
16b204c4 922 sock = comm_open(SOCK_STREAM,
923 0,
924 Config.Addrs.tcp_outgoing,
925 0,
926 COMM_NONBLOCKING,
927 url);
090089c4 928 if (sock == COMM_ERROR) {
019dd986 929 debug(11, 4, "httpStart: Failed because we're out of sockets.\n");
b8de7ebe 930 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 931 return COMM_ERROR;
932 }
30a4f2a8 933 httpState = xcalloc(1, sizeof(HttpStateData));
934 storeLockObject(httpState->entry = entry, NULL, NULL);
935 httpState->req_hdr = req_hdr;
4768dbe0 936 httpState->req_hdr_sz = req_hdr_sz;
30a4f2a8 937 httpState->request = requestLink(request);
938 comm_add_close_handler(sock,
b177367b 939 httpStateFree,
30a4f2a8 940 (void *) httpState);
a13d9042 941 ipcache_nbgethostbyname(request->host,
942 sock,
943 httpConnect,
944 httpState);
090089c4 945 return COMM_OK;
946}
30a4f2a8 947
b8d8561b 948void
949httpReplyHeaderStats(StoreEntry * entry)
30a4f2a8 950{
6fb52f6c 951 http_server_cc_t i;
30a4f2a8 952 storeAppendPrintf(entry, open_bracket);
6fb52f6c 953 storeAppendPrintf(entry, "{HTTP Reply Headers:}\n");
954 storeAppendPrintf(entry, "{ Headers parsed: %d}\n",
30a4f2a8 955 ReplyHeaderStats.parsed);
6fb52f6c 956 storeAppendPrintf(entry, "{ Date: %d}\n",
30a4f2a8 957 ReplyHeaderStats.date);
6fb52f6c 958 storeAppendPrintf(entry, "{ Last-Modified: %d}\n",
30a4f2a8 959 ReplyHeaderStats.lm);
6fb52f6c 960 storeAppendPrintf(entry, "{ Expires: %d}\n",
30a4f2a8 961 ReplyHeaderStats.exp);
6fb52f6c 962 storeAppendPrintf(entry, "{ Content-Type: %d}\n",
30a4f2a8 963 ReplyHeaderStats.ctype);
6fb52f6c 964 storeAppendPrintf(entry, "{ Content-Length: %d}\n",
30a4f2a8 965 ReplyHeaderStats.clen);
4db43fab 966 for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++)
6fb52f6c 967 storeAppendPrintf(entry, "{Cache-Control %7.7s: %d}\n",
968 HttpServerCCStr[i],
969 ReplyHeaderStats.cc[i]);
30a4f2a8 970 storeAppendPrintf(entry, close_bracket);
971}