]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
update
[thirdparty/squid.git] / src / http.cc
CommitLineData
30a4f2a8 1/*
f88211e8 2 * $Id: http.cc,v 1.168 1997/06/02 19:56:03 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
de3bdb4c 108 * have a look into http-anon.c to get more informations.
4a83b852 109 */
110
44a47c6e 111#include "squid.h"
090089c4 112
234967c9 113#define HTTP_DELETE_GAP (1<<18)
090089c4 114
6bf8443a 115static const char *const crlf = "\r\n";
4db43fab 116
6fb52f6c 117typedef enum {
118 SCC_PUBLIC,
119 SCC_PRIVATE,
120 SCC_NOCACHE,
121 SCC_NOSTORE,
122 SCC_NOTRANSFORM,
123 SCC_MUSTREVALIDATE,
124 SCC_PROXYREVALIDATE,
125 SCC_MAXAGE,
126 SCC_ENUM_END
127} http_server_cc_t;
128
6bf8443a 129enum {
6fb52f6c 130 CCC_NOCACHE,
131 CCC_NOSTORE,
132 CCC_MAXAGE,
133 CCC_MAXSTALE,
134 CCC_MINFRESH,
135 CCC_ONLYIFCACHED,
136 CCC_ENUM_END
6bf8443a 137};
138
151a0b6d 139typedef enum {
140 HDR_ACCEPT,
141 HDR_AGE,
142 HDR_CONTENT_LENGTH,
143 HDR_CONTENT_MD5,
144 HDR_CONTENT_TYPE,
145 HDR_DATE,
146 HDR_ETAG,
147 HDR_EXPIRES,
6bf8443a 148 HDR_HOST,
151a0b6d 149 HDR_IMS,
150 HDR_LAST_MODIFIED,
151 HDR_MAX_FORWARDS,
152 HDR_PUBLIC,
153 HDR_RETRY_AFTER,
154 HDR_SET_COOKIE,
155 HDR_UPGRADE,
156 HDR_WARNING,
157 HDR_MISC_END
158} http_hdr_misc_t;
6fb52f6c 159
e924600d 160static char *HttpServerCCStr[] =
6fb52f6c 161{
162 "public",
163 "private",
164 "no-cache",
165 "no-store",
166 "no-transform",
167 "must-revalidate",
168 "proxy-revalidate",
169 "max-age",
170 "NONE"
171};
172
151a0b6d 173static char *HttpHdrMiscStr[] =
174{
175 "Accept",
176 "Age",
177 "Content-Length",
178 "Content-MD5",
179 "Content-Type",
180 "Date",
181 "Etag",
182 "Expires",
183 "Host",
184 "If-Modified-Since",
185 "Last-Modified",
186 "Max-Forwards",
187 "Public",
188 "Retry-After",
189 "Set-Cookie",
190 "Upgrade",
191 "Warning",
192 "NONE"
193};
194
24382924 195static struct {
30a4f2a8 196 int parsed;
151a0b6d 197 int misc[HDR_MISC_END];
6fb52f6c 198 int cc[SCC_ENUM_END];
30a4f2a8 199} ReplyHeaderStats;
090089c4 200
9e4ad609 201static CNCB httpConnectDone;
202static CWCB httpSendComplete;
9e4ad609 203static PF httpReadReply;
204static PF httpSendRequest;
205static PF httpStateFree;
206static PF httpTimeout;
6bf8443a 207static void httpAppendRequestHeader _PARAMS((char *hdr, const char *line, size_t * sz, size_t max));
9e4ad609 208static void httpCacheNegatively _PARAMS((StoreEntry *));
209static void httpMakePrivate _PARAMS((StoreEntry *));
210static void httpMakePublic _PARAMS((StoreEntry *));
bfcaf585 211static STABH httpAbort;
b8d8561b 212
b177367b 213static void
214httpStateFree(int fd, void *data)
f5558c95 215{
b177367b 216 HttpStateData *httpState = data;
0d4d4170 217 if (httpState == NULL)
b177367b 218 return;
bfcaf585 219 storeUnregisterAbort(httpState->entry);
f88211e8 220 storeUnlockObject(httpState->entry);
0d4d4170 221 if (httpState->reply_hdr) {
222 put_free_8k_page(httpState->reply_hdr);
223 httpState->reply_hdr = NULL;
224 }
30a4f2a8 225 requestUnlink(httpState->request);
20cc1450 226 requestUnlink(httpState->orig_request);
0d4d4170 227 xfree(httpState);
f5558c95 228}
229
b8d8561b 230int
75e88d56 231httpCachable(method_t method)
090089c4 232{
090089c4 233 /* GET and HEAD are cachable. Others are not. */
6eb42cae 234 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 235 return 0;
090089c4 236 /* else cachable */
237 return 1;
238}
239
b8d8561b 240static void
5c5783a2 241httpTimeout(int fd, void *data)
090089c4 242{
b177367b 243 HttpStateData *httpState = data;
593c9a75 244 StoreEntry *entry = httpState->entry;
5c5783a2 245 debug(11, 4, "httpTimeout: FD %d: '%s'\n", fd, entry->url);
246 squid_error_entry(entry, ERR_READ_TIMEOUT, NULL);
0d4d4170 247 comm_close(fd);
090089c4 248}
249
30a4f2a8 250/* This object can be cached for a long time */
b8d8561b 251static void
252httpMakePublic(StoreEntry * entry)
30a4f2a8 253{
1c481e00 254 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 255 storeSetPublicKey(entry);
256}
257
258/* This object should never be cached at all */
b8d8561b 259static void
260httpMakePrivate(StoreEntry * entry)
30a4f2a8 261{
30a4f2a8 262 storeExpireNow(entry);
1c481e00 263 BIT_RESET(entry->flag, ENTRY_CACHABLE);
30a4f2a8 264 storeReleaseRequest(entry); /* delete object when not used */
265}
266
267/* This object may be negatively cached */
b8d8561b 268static void
269httpCacheNegatively(StoreEntry * entry)
30a4f2a8 270{
79b5cc5f 271 storeNegativeCache(entry);
1c481e00 272 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 273 storeSetPublicKey(entry);
30a4f2a8 274}
275
276
277/* Build a reply structure from HTTP reply headers */
b8d8561b 278void
48f44632 279httpParseReplyHeaders(const char *buf, struct _http_reply *reply)
30a4f2a8 280{
33b589ff 281 char *headers = get_free_4k_page();
ca85027a 282 char *line;
33b589ff 283 char *end;
30a4f2a8 284 char *s = NULL;
33b589ff 285 char *t;
ca98227c 286 time_t delta;
287 size_t l;
30a4f2a8 288
f1494beb 289 reply->code = 600;
30a4f2a8 290 ReplyHeaderStats.parsed++;
33b589ff 291 xstrncpy(headers, buf, 4096);
292 end = mime_headers_end(headers);
ca85027a 293 if (end == NULL) {
294 t = headers;
e2ad7f85 295 if (!strncasecmp(t, "HTTP/", 5)) {
296 reply->version = atof(t + 5);
297 if ((t = strchr(t, ' ')))
298 reply->code = atoi(++t);
ca85027a 299 }
e2ad7f85 300 put_free_4k_page(headers);
301 return;
ca85027a 302 }
303 reply->hdr_sz = end - headers;
304 line = get_free_4k_page();
33b589ff 305 for (s = headers; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
306 l = strcspn(s, crlf) + 1;
307 if (l > 4096)
308 l = 4096;
309 xstrncpy(line, s, l);
310 t = line;
311 debug(11, 3, "httpParseReplyHeaders: %s\n", t);
312 if (!strncasecmp(t, "HTTP/", 5)) {
e2ad7f85 313 reply->version = atof(t + 5);
33b589ff 314 if ((t = strchr(t, ' ')))
315 reply->code = atoi(++t);
30a4f2a8 316 } else if (!strncasecmp(t, "Content-type:", 13)) {
33b589ff 317 for (t += 13; isspace(*t); t++);
e91b56e5 318 if ((l = strcspn(t, ";\t ")) > 0)
319 *(t + l) = '\0';
33b589ff 320 xstrncpy(reply->content_type, t, HTTP_REPLY_FIELD_SZ);
151a0b6d 321 ReplyHeaderStats.misc[HDR_CONTENT_TYPE]++;
30a4f2a8 322 } else if (!strncasecmp(t, "Content-length:", 15)) {
33b589ff 323 for (t += 15; isspace(*t); t++);
33b589ff 324 reply->content_length = atoi(t);
151a0b6d 325 ReplyHeaderStats.misc[HDR_CONTENT_LENGTH]++;
30a4f2a8 326 } else if (!strncasecmp(t, "Date:", 5)) {
33b589ff 327 for (t += 5; isspace(*t); t++);
328 reply->date = parse_rfc1123(t);
151a0b6d 329 ReplyHeaderStats.misc[HDR_DATE]++;
30a4f2a8 330 } else if (!strncasecmp(t, "Expires:", 8)) {
33b589ff 331 for (t += 8; isspace(*t); t++);
332 reply->expires = parse_rfc1123(t);
333 /*
334 * The HTTP/1.0 specs says that robust implementations
335 * should consider bad or malformed Expires header as
336 * equivalent to "expires immediately."
337 */
338 if (reply->expires == -1)
339 reply->expires = squid_curtime;
151a0b6d 340 ReplyHeaderStats.misc[HDR_EXPIRES]++;
30a4f2a8 341 } else if (!strncasecmp(t, "Last-Modified:", 14)) {
33b589ff 342 for (t += 14; isspace(*t); t++);
343 reply->last_modified = parse_rfc1123(t);
151a0b6d 344 ReplyHeaderStats.misc[HDR_LAST_MODIFIED]++;
345 } else if (!strncasecmp(t, "Accept:", 7)) {
346 ReplyHeaderStats.misc[HDR_ACCEPT]++;
347 } else if (!strncasecmp(t, "Age:", 4)) {
348 ReplyHeaderStats.misc[HDR_AGE]++;
349 } else if (!strncasecmp(t, "Content-MD5:", 12)) {
350 ReplyHeaderStats.misc[HDR_CONTENT_MD5]++;
351 } else if (!strncasecmp(t, "ETag:", 5)) {
352 ReplyHeaderStats.misc[HDR_ETAG]++;
353 } else if (!strncasecmp(t, "Max-Forwards:", 13)) {
354 ReplyHeaderStats.misc[HDR_MAX_FORWARDS]++;
355 } else if (!strncasecmp(t, "Public:", 7)) {
356 ReplyHeaderStats.misc[HDR_PUBLIC]++;
357 } else if (!strncasecmp(t, "Retry-After:", 12)) {
358 ReplyHeaderStats.misc[HDR_RETRY_AFTER]++;
359 } else if (!strncasecmp(t, "Upgrade:", 8)) {
360 ReplyHeaderStats.misc[HDR_UPGRADE]++;
361 } else if (!strncasecmp(t, "Warning:", 8)) {
362 ReplyHeaderStats.misc[HDR_WARNING]++;
caebbe00 363 } else if (!strncasecmp(t, "Cache-Control:", 14)) {
33b589ff 364 for (t += 14; isspace(*t); t++);
4db43fab 365 if (!strncasecmp(t, "public", 6)) {
366 EBIT_SET(reply->cache_control, SCC_PUBLIC);
367 ReplyHeaderStats.cc[SCC_PUBLIC]++;
368 } else if (!strncasecmp(t, "private", 7)) {
369 EBIT_SET(reply->cache_control, SCC_PRIVATE);
370 ReplyHeaderStats.cc[SCC_PRIVATE]++;
371 } else if (!strncasecmp(t, "no-cache", 8)) {
372 EBIT_SET(reply->cache_control, SCC_NOCACHE);
373 ReplyHeaderStats.cc[SCC_NOCACHE]++;
c1764328 374 } else if (!strncasecmp(t, "no-store", 8)) {
375 EBIT_SET(reply->cache_control, SCC_NOSTORE);
376 ReplyHeaderStats.cc[SCC_NOSTORE]++;
377 } else if (!strncasecmp(t, "no-transform", 12)) {
378 EBIT_SET(reply->cache_control, SCC_NOTRANSFORM);
379 ReplyHeaderStats.cc[SCC_NOTRANSFORM]++;
380 } else if (!strncasecmp(t, "must-revalidate", 15)) {
381 EBIT_SET(reply->cache_control, SCC_MUSTREVALIDATE);
382 ReplyHeaderStats.cc[SCC_MUSTREVALIDATE]++;
383 } else if (!strncasecmp(t, "proxy-revalidate", 16)) {
384 EBIT_SET(reply->cache_control, SCC_PROXYREVALIDATE);
385 ReplyHeaderStats.cc[SCC_PROXYREVALIDATE]++;
4db43fab 386 } else if (!strncasecmp(t, "max-age", 7)) {
387 if ((t = strchr(t, '='))) {
ca98227c 388 delta = (time_t) atoi(++t);
389 reply->expires = squid_curtime + delta;
4db43fab 390 EBIT_SET(reply->cache_control, SCC_MAXAGE);
391 ReplyHeaderStats.cc[SCC_MAXAGE]++;
5df61230 392 }
caebbe00 393 }
df3ac7c0 394 } else if (!strncasecmp(t, "Set-Cookie:", 11)) {
395 EBIT_SET(reply->misc_headers, HDR_SET_COOKIE);
58202be7 396 ReplyHeaderStats.misc[HDR_SET_COOKIE]++;
30a4f2a8 397 }
30a4f2a8 398 }
33b589ff 399 put_free_4k_page(headers);
400 put_free_4k_page(line);
30a4f2a8 401}
402
090089c4 403
b8d8561b 404void
0ee4272b 405httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 406{
407 char *t = NULL;
30a4f2a8 408 StoreEntry *entry = httpState->entry;
d3fb4dea 409 int room;
410 int hdr_len;
33b589ff 411 struct _http_reply *reply = entry->mem_obj->reply;
d3fb4dea 412
ed85b771 413 debug(11, 3, "httpProcessReplyHeader: key '%s'\n", entry->key);
f5558c95 414
e924600d 415 if (httpState->reply_hdr == NULL)
30a4f2a8 416 httpState->reply_hdr = get_free_8k_page();
30a4f2a8 417 if (httpState->reply_hdr_state == 0) {
418 hdr_len = strlen(httpState->reply_hdr);
ed85b771 419 room = 8191 - hdr_len;
30a4f2a8 420 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 421 hdr_len += room < size ? room : size;
30a4f2a8 422 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
60bf30cb 423 debug(11, 3, "httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", entry->key);
30a4f2a8 424 httpState->reply_hdr_state += 2;
33b589ff 425 reply->code = 555;
ed85b771 426 return;
d3fb4dea 427 }
d1a43e28 428 t = httpState->reply_hdr + hdr_len;
429 /* headers can be incomplete only if object still arriving */
f86a6a46 430 if (!httpState->eof)
d1a43e28 431 if ((t = mime_headers_end(httpState->reply_hdr)) == NULL)
432 return; /* headers not complete */
2285407f 433 *t = '\0';
30a4f2a8 434 httpState->reply_hdr_state++;
f5558c95 435 }
30a4f2a8 436 if (httpState->reply_hdr_state == 1) {
437 httpState->reply_hdr_state++;
019dd986 438 debug(11, 9, "GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 439 httpState->reply_hdr);
440 /* Parse headers into reply structure */
48f44632 441 httpParseReplyHeaders(httpState->reply_hdr, reply);
ca98227c 442 storeTimestampsSet(entry);
30a4f2a8 443 /* Check if object is cacheable or not based on reply code */
cb24292d 444 debug(11, 3, "httpProcessReplyHeader: HTTP CODE: %d\n", reply->code);
2285407f 445 switch (reply->code) {
30a4f2a8 446 /* Responses that are cacheable */
f5558c95 447 case 200: /* OK */
4e38e700 448 case 203: /* Non-Authoritative Information */
449 case 300: /* Multiple Choices */
f5558c95 450 case 301: /* Moved Permanently */
4e38e700 451 case 410: /* Gone */
30a4f2a8 452 /* don't cache objects from neighbors w/o LMT, Date, or Expires */
6fb52f6c 453 if (EBIT_TEST(reply->cache_control, SCC_PRIVATE))
caebbe00 454 httpMakePrivate(entry);
6fb52f6c 455 else if (EBIT_TEST(reply->cache_control, SCC_NOCACHE))
caebbe00 456 httpMakePrivate(entry);
e2ad7f85 457 /*
458 * Dealing with cookies is quite a bit more complicated
459 * than this. Ideally we should strip the cookie
460 * header from the reply but still cache the reply body.
461 * More confusion at draft-ietf-http-state-mgmt-05.txt.
462 */
df3ac7c0 463 else if (EBIT_TEST(reply->misc_headers, HDR_SET_COOKIE))
464 httpMakePrivate(entry);
ca98227c 465 else if (reply->date > -1)
30a4f2a8 466 httpMakePublic(entry);
ca98227c 467 else if (reply->last_modified > -1)
30a4f2a8 468 httpMakePublic(entry);
469 else if (!httpState->neighbor)
470 httpMakePublic(entry);
ca98227c 471 else if (reply->expires > -1)
30a4f2a8 472 httpMakePublic(entry);
af00901c 473 else if (entry->mem_obj->request->protocol != PROTO_HTTP)
474 /* XXX Remove this check after a while. DW 8/21/96
475 * We won't keep some FTP objects from neighbors running
476 * 1.0.8 or earlier because their ftpget's don't
477 * add a Date: field */
478 httpMakePublic(entry);
30a4f2a8 479 else
480 httpMakePrivate(entry);
481 break;
482 /* Responses that only are cacheable if the server says so */
483 case 302: /* Moved temporarily */
ca98227c 484 if (reply->expires > -1)
30a4f2a8 485 httpMakePublic(entry);
486 else
487 httpMakePrivate(entry);
f5558c95 488 break;
30a4f2a8 489 /* Errors can be negatively cached */
490 case 204: /* No Content */
491 case 305: /* Use Proxy (proxy redirect) */
492 case 400: /* Bad Request */
493 case 403: /* Forbidden */
494 case 404: /* Not Found */
495 case 405: /* Method Now Allowed */
496 case 414: /* Request-URI Too Long */
497 case 500: /* Internal Server Error */
498 case 501: /* Not Implemented */
499 case 502: /* Bad Gateway */
500 case 503: /* Service Unavailable */
501 case 504: /* Gateway Timeout */
851eeef7 502 httpCacheNegatively(entry);
30a4f2a8 503 break;
504 /* Some responses can never be cached */
505 case 303: /* See Other */
234967c9 506 case 304: /* Not Modified */
4e38e700 507 case 401: /* Unauthorized */
508 case 407: /* Proxy Authentication Required */
f1494beb 509 case 600: /* Squid header parsing error */
30a4f2a8 510 default: /* Unknown status code */
511 httpMakePrivate(entry);
4e38e700 512 break;
f5558c95 513 }
514 }
515}
516
090089c4 517
518/* This will be called when data is ready to be read from fd. Read until
519 * error or connection closed. */
f5558c95 520/* XXX this function is too long! */
b8d8561b 521static void
b177367b 522httpReadReply(int fd, void *data)
090089c4 523{
b177367b 524 HttpStateData *httpState = data;
95d659f0 525 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
bfcaf585 526 StoreEntry *entry = httpState->entry;
090089c4 527 int len;
30a4f2a8 528 int bin;
090089c4 529 int clen;
530 int off;
d89d1fb6 531 if (protoAbortFetch(entry)) {
bfcaf585 532 squid_error_entry(entry, ERR_CLIENT_ABORT, NULL);
533 comm_close(fd);
534 return;
234967c9 535 }
536 /* check if we want to defer reading */
537 clen = entry->mem_obj->e_current_len;
538 off = storeGetLowestReaderOffset(entry);
539 if ((clen - off) > HTTP_DELETE_GAP) {
30a4f2a8 540 IOStats.Http.reads_deferred++;
234967c9 541 debug(11, 3, "httpReadReply: Read deferred for Object: %s\n",
542 entry->url);
543 debug(11, 3, " Current Gap: %d bytes\n", clen - off);
544 /* reschedule, so it will be automatically reactivated
545 * when Gap is big enough. */
b177367b 546 commSetSelect(fd,
234967c9 547 COMM_SELECT_READ,
b177367b 548 httpReadReply,
cd1fb0eb 549 httpState, 0);
30a4f2a8 550 /* disable read timeout until we are below the GAP */
56fa4cad 551 if (!BIT_TEST(entry->flag, READ_DEFERRED)) {
5c5783a2 552 commSetTimeout(fd, Config.Timeout.defer, NULL, NULL);
56fa4cad 553 BIT_SET(entry->flag, READ_DEFERRED);
554 }
234967c9 555 /* dont try reading again for a while */
b6f794d6 556 comm_set_stall(fd, Config.stallDelay);
234967c9 557 return;
56fa4cad 558 } else {
559 BIT_RESET(entry->flag, READ_DEFERRED);
090089c4 560 }
1513873c 561 errno = 0;
30a4f2a8 562 len = read(fd, buf, SQUID_TCP_SO_RCVBUF);
4f92c80c 563 fd_bytes(fd, len, FD_READ);
019dd986 564 debug(11, 5, "httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 565 if (len > 0) {
4f92c80c 566 commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
4a63c85f 567 IOStats.Http.reads++;
30a4f2a8 568 for (clen = len - 1, bin = 0; clen; bin++)
569 clen >>= 1;
570 IOStats.Http.read_hist[bin]++;
571 }
ba718c8f 572 if (len < 0) {
0a0bf5db 573 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
1513873c 574 /* reinstall handlers */
6fe6313d 575 /* XXX This may loop forever */
b177367b 576 commSetSelect(fd, COMM_SELECT_READ,
cd1fb0eb 577 httpReadReply, httpState, 0);
090089c4 578 } else {
1c481e00 579 BIT_RESET(entry->flag, ENTRY_CACHABLE);
2daae136 580 storeReleaseRequest(entry);
b8de7ebe 581 squid_error_entry(entry, ERR_READ_ERROR, xstrerror());
0d4d4170 582 comm_close(fd);
090089c4 583 }
0a0bf5db 584 debug(50, 2, "httpReadReply: FD %d: read failure: %s.\n",
585 fd, xstrerror());
ba718c8f 586 } else if (len == 0 && entry->mem_obj->e_current_len == 0) {
f86a6a46 587 httpState->eof = 1;
b8de7ebe 588 squid_error_entry(entry,
ba718c8f 589 ERR_ZERO_SIZE_OBJECT,
590 errno ? xstrerror() : NULL);
0d4d4170 591 comm_close(fd);
090089c4 592 } else if (len == 0) {
593 /* Connection closed; retrieval done. */
f86a6a46 594 httpState->eof = 1;
d1a43e28 595 if (httpState->reply_hdr_state < 2)
596 httpProcessReplyHeader(httpState, buf, len);
597 storeAppend(entry, buf, len); /* invoke handlers! */
598 storeComplete(entry); /* deallocates mem_obj->request */
0d4d4170 599 comm_close(fd);
090089c4 600 } else {
d1a43e28 601 if (httpState->reply_hdr_state < 2)
30a4f2a8 602 httpProcessReplyHeader(httpState, buf, len);
620da955 603 storeAppend(entry, buf, len);
0a0bf5db 604 commSetSelect(fd,
605 COMM_SELECT_READ,
606 httpReadReply,
cd1fb0eb 607 httpState, 0);
090089c4 608 }
609}
610
611/* This will be called when request write is complete. Schedule read of
612 * reply. */
b8d8561b 613static void
614httpSendComplete(int fd, char *buf, int size, int errflag, void *data)
090089c4 615{
30a4f2a8 616 HttpStateData *httpState = data;
090089c4 617 StoreEntry *entry = NULL;
618
30a4f2a8 619 entry = httpState->entry;
019dd986 620 debug(11, 5, "httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 621 fd, size, errflag);
622
090089c4 623 if (errflag) {
b8de7ebe 624 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
0d4d4170 625 comm_close(fd);
090089c4 626 return;
627 } else {
628 /* Schedule read reply. */
b177367b 629 commSetSelect(fd,
019dd986 630 COMM_SELECT_READ,
b177367b 631 httpReadReply,
cd1fb0eb 632 httpState, 0);
090089c4 633 }
634}
635
6bf8443a 636static void
637httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max)
638{
639 size_t n = *sz + strlen(line) + 2;
640 if (n >= max)
641 return;
fa562c67 642 if (Config.Options.anonymizer == ANONYMIZER_PARANOID) {
bba6fa8f 643 if (!httpAnonAllowed(line))
fa562c67 644 return;
645 } else if (Config.Options.anonymizer == ANONYMIZER_STANDARD) {
bba6fa8f 646 if (httpAnonDenied(line))
fa562c67 647 return;
4a83b852 648 }
4a83b852 649 /* allowed header, explicitly known to be not dangerous */
6bf8443a 650 debug(11, 5, "httpAppendRequestHeader: %s\n", line);
929545fe 651 strcpy(hdr + (*sz), line);
6bf8443a 652 strcat(hdr + (*sz), crlf);
653 *sz = n;
654}
655
656size_t
657httpBuildRequestHeader(request_t * request,
658 request_t * orig_request,
659 StoreEntry * entry,
660 char *hdr_in,
661 size_t * in_len,
662 char *hdr_out,
663 size_t out_sz,
664 int cfd)
665{
429fdbec 666 LOCAL_ARRAY(char, ybuf, MAX_URL + 32);
6bf8443a 667 char *xbuf = get_free_4k_page();
6bf8443a 668 char *viabuf = get_free_4k_page();
669 char *fwdbuf = get_free_4k_page();
670 char *t = NULL;
671 char *s = NULL;
672 char *end = NULL;
673 size_t len = 0;
674 size_t hdr_len = 0;
6bf8443a 675 size_t l;
676 int hdr_flags = 0;
151a0b6d 677 int cc_flags = 0;
b3b64e58 678 int n;
6bf8443a 679 const char *url = NULL;
680
681 debug(11, 3, "httpBuildRequestHeader: INPUT:\n%s\n", hdr_in);
682 xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096);
683 xstrncpy(viabuf, "Via: ", 4096);
684 sprintf(ybuf, "%s %s HTTP/1.0",
685 RequestMethodStr[request->method],
686 *request->urlpath ? request->urlpath : "/");
687 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
688 /* Add IMS header */
689 if (entry && entry->lastmod && request->method == METHOD_GET) {
690 sprintf(ybuf, "If-Modified-Since: %s", mkrfc1123(entry->lastmod));
691 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
692 EBIT_SET(hdr_flags, HDR_IMS);
693 }
00c59270 694 end = mime_headers_end(hdr_in);
6bf8443a 695 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
696 hdr_len = t - hdr_in;
6bf8443a 697 l = strcspn(t, crlf) + 1;
698 if (l > 4096)
699 l = 4096;
700 xstrncpy(xbuf, t, l);
a7011ca4 701 debug(11, 5, "httpBuildRequestHeader: %s\n", xbuf);
6bf8443a 702 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
703 continue;
704 if (strncasecmp(xbuf, "Connection:", 11) == 0)
705 continue;
66f7337b 706 if (strncasecmp(xbuf, "Host:", 5) == 0) {
6bf8443a 707 EBIT_SET(hdr_flags, HDR_HOST);
66f7337b 708 } else if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) {
6bf8443a 709 for (s = xbuf + 14; *s && isspace(*s); s++);
710 if (strncasecmp(s, "Max-age=", 8) == 0)
151a0b6d 711 EBIT_SET(cc_flags, CCC_MAXAGE);
66f7337b 712 } else if (strncasecmp(xbuf, "Via:", 4) == 0) {
6bf8443a 713 for (s = xbuf + 4; *s && isspace(*s); s++);
714 if (strlen(viabuf) + strlen(s) < 4000)
715 strcat(viabuf, s);
716 strcat(viabuf, ", ");
717 continue;
66f7337b 718 } else if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) {
6bf8443a 719 for (s = xbuf + 16; *s && isspace(*s); s++);
720 if (strlen(fwdbuf) + strlen(s) < 4000)
721 strcat(fwdbuf, s);
722 strcat(fwdbuf, ", ");
723 continue;
66f7337b 724 } else if (strncasecmp(xbuf, "If-Modified-Since:", 18) == 0) {
6bf8443a 725 if (EBIT_TEST(hdr_flags, HDR_IMS))
726 continue;
b3b64e58 727 } else if (strncasecmp(xbuf, "Max-Forwards:", 13) == 0) {
728 if (orig_request->method == METHOD_TRACE) {
729 for (s = xbuf + 13; *s && isspace(*s); s++);
730 n = atoi(s);
731 sprintf(xbuf, "Max-Forwards: %d", n - 1);
732 }
66f7337b 733 }
6bf8443a 734 httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512);
735 }
9e5aec44 736 hdr_len = end - hdr_in;
6bf8443a 737 /* Append Via: */
43f1c650 738 sprintf(ybuf, "%3.1f %s", orig_request->http_ver, ThisCache);
6bf8443a 739 strcat(viabuf, ybuf);
740 httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz);
741 /* Append to X-Forwarded-For: */
a08307eb 742 strcat(fwdbuf, cfd < 0 ? "unknown" : fd_table[cfd].ipaddr);
6bf8443a 743 httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz);
744 if (!EBIT_TEST(hdr_flags, HDR_HOST)) {
745 sprintf(ybuf, "Host: %s", orig_request->host);
746 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
747 }
151a0b6d 748 if (!EBIT_TEST(cc_flags, CCC_MAXAGE)) {
6bf8443a 749 url = entry ? entry->url : urlCanonical(orig_request, NULL);
750 sprintf(ybuf, "Cache-control: Max-age=%d", (int) getMaxAge(url));
751 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
752 }
753 httpAppendRequestHeader(hdr_out, null_string, &len, out_sz);
754 put_free_4k_page(xbuf);
6bf8443a 755 put_free_4k_page(viabuf);
756 put_free_4k_page(fwdbuf);
757 if (in_len)
758 *in_len = hdr_len;
9d9d144b 759 if ((l = strlen(hdr_out)) != len) {
760 debug_trap("httpBuildRequestHeader: size mismatch");
761 len = l;
762 }
6bf8443a 763 debug(11, 3, "httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out);
764 return len;
765}
766
090089c4 767/* This will be called when connect completes. Write request. */
b8d8561b 768static void
b177367b 769httpSendRequest(int fd, void *data)
090089c4 770{
b177367b 771 HttpStateData *httpState = data;
090089c4 772 char *buf = NULL;
090089c4 773 int len = 0;
774 int buflen;
30a4f2a8 775 request_t *req = httpState->request;
9864ee44 776 int buftype = 0;
620da955 777 StoreEntry *entry = httpState->entry;
2a26c096 778 int cfd;
090089c4 779
30a4f2a8 780 debug(11, 5, "httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
6bf8443a 781 buflen = strlen(req->urlpath);
62dec5a7 782 if (req->headers)
783 buflen += req->headers_sz + 1;
090089c4 784 buflen += 512; /* lots of extra */
785
78657218 786 if ((req->method == METHOD_POST || req->method == METHOD_PUT)) {
787 debug_trap("httpSendRequest: should not be handling POST/PUT request");
788 return;
090089c4 789 }
30a4f2a8 790 if (buflen < DISK_PAGE_SIZE) {
9864ee44 791 buf = get_free_8k_page();
9864ee44 792 buftype = BUF_TYPE_8K;
6bf8443a 793 buflen = DISK_PAGE_SIZE;
30a4f2a8 794 } else {
9864ee44 795 buf = xcalloc(buflen, 1);
796 buftype = BUF_TYPE_MALLOC;
090089c4 797 }
2a26c096 798 if (!opt_forwarded_for)
6bf8443a 799 cfd = -1;
2a26c096 800 else if (entry->mem_obj == NULL)
6bf8443a 801 cfd = -1;
2a26c096 802 else
382d851a 803 cfd = entry->mem_obj->fd;
6bf8443a 804 len = httpBuildRequestHeader(req,
805 httpState->orig_request ? httpState->orig_request : req,
806 entry,
62dec5a7 807 req->headers,
6bf8443a 808 NULL,
809 buf,
810 buflen,
811 cfd);
67508012 812 debug(11, 6, "httpSendRequest: FD %d:\n%s\n", fd, buf);
30a4f2a8 813 comm_write(fd,
14e59844 814 buf,
815 len,
14e59844 816 httpSendComplete,
9864ee44 817 httpState,
818 buftype == BUF_TYPE_8K ? put_free_8k_page : xfree);
20cc1450 819 requestUnlink(httpState->orig_request);
820 httpState->orig_request = NULL;
090089c4 821}
822
770f051d 823void
75e88d56 824proxyhttpStart(request_t * orig_request,
20cc1450 825 StoreEntry * entry,
deb79f06 826 peer * e)
090089c4 827{
9e4ad609 828 HttpStateData *httpState;
829 request_t *request;
830 int fd;
7111c86a 831 debug(11, 3, "proxyhttpStart: \"%s %s\"\n",
75e88d56 832 RequestMethodStr[orig_request->method], entry->url);
30a4f2a8 833 if (e->options & NEIGHBOR_PROXY_ONLY)
d89d1fb6 834#if DONT_USE_VM
835 storeReleaseRequest(entry);
836#else
090089c4 837 storeStartDeleteBehind(entry);
d89d1fb6 838#endif
090089c4 839 /* Create socket. */
9e4ad609 840 fd = comm_open(SOCK_STREAM,
16b204c4 841 0,
842 Config.Addrs.tcp_outgoing,
843 0,
844 COMM_NONBLOCKING,
75e88d56 845 entry->url);
9e4ad609 846 if (fd == COMM_ERROR) {
019dd986 847 debug(11, 4, "proxyhttpStart: Failed because we're out of sockets.\n");
b8de7ebe 848 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
770f051d 849 return;
090089c4 850 }
770f051d 851 storeLockObject(entry);
30a4f2a8 852 httpState = xcalloc(1, sizeof(HttpStateData));
0a0bf5db 853 httpState->entry = entry;
30a4f2a8 854 request = get_free_request_t();
855 httpState->request = requestLink(request);
856 httpState->neighbor = e;
20cc1450 857 httpState->orig_request = requestLink(orig_request);
9e4ad609 858 httpState->fd = fd;
0d4d4170 859 /* register the handler to free HTTP state data when the FD closes */
429fdbec 860 comm_add_close_handler(httpState->fd,
b177367b 861 httpStateFree,
cd1fb0eb 862 httpState);
2cc3f720 863 request->method = orig_request->method;
d5aa0e3b 864 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
30a4f2a8 865 request->port = e->http_port;
9e4ad609 866 xstrncpy(request->urlpath, entry->url, MAX_URL);
313c1c28 867 BIT_SET(request->flags, REQ_PROXYING);
5c5783a2 868 commSetTimeout(fd, Config.Timeout.connect, httpTimeout, httpState);
edeb28fd 869 commConnectStart(httpState->fd,
e924600d 870 request->host,
871 request->port,
872 httpConnectDone,
873 httpState);
e5f6c5c2 874}
875
876static void
877httpConnectDone(int fd, int status, void *data)
878{
879 HttpStateData *httpState = data;
880 request_t *request = httpState->request;
881 StoreEntry *entry = httpState->entry;
edeb28fd 882 if (status == COMM_ERR_DNS) {
883 debug(11, 4, "httpConnectDone: Unknown host: %s\n", request->host);
884 squid_error_entry(entry, ERR_DNS_FAIL, dns_error_message);
885 comm_close(fd);
886 } else if (status != COMM_OK) {
e5f6c5c2 887 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
e924600d 888 if (httpState->neighbor)
889 peerCheckConnectStart(httpState->neighbor);
e5f6c5c2 890 comm_close(fd);
891 } else {
e5f6c5c2 892 if (opt_no_ipcache)
893 ipcacheInvalidate(request->host);
894 fd_note(fd, entry->url);
bfcaf585 895 storeRegisterAbort(entry, httpAbort, httpState);
896 commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0);
090089c4 897 }
090089c4 898}
899
770f051d 900void
3a1c3e2f 901httpStart(request_t * request, StoreEntry * entry)
090089c4 902{
9e4ad609 903 int fd;
904 HttpStateData *httpState;
7111c86a 905 debug(11, 3, "httpStart: \"%s %s\"\n",
75e88d56 906 RequestMethodStr[request->method], entry->url);
090089c4 907 /* Create socket. */
9e4ad609 908 fd = comm_open(SOCK_STREAM,
16b204c4 909 0,
910 Config.Addrs.tcp_outgoing,
911 0,
912 COMM_NONBLOCKING,
75e88d56 913 entry->url);
9e4ad609 914 if (fd == COMM_ERROR) {
019dd986 915 debug(11, 4, "httpStart: Failed because we're out of sockets.\n");
b8de7ebe 916 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
770f051d 917 return;
090089c4 918 }
770f051d 919 storeLockObject(entry);
30a4f2a8 920 httpState = xcalloc(1, sizeof(HttpStateData));
0a0bf5db 921 httpState->entry = entry;
30a4f2a8 922 httpState->request = requestLink(request);
9e4ad609 923 httpState->fd = fd;
429fdbec 924 comm_add_close_handler(httpState->fd,
b177367b 925 httpStateFree,
cd1fb0eb 926 httpState);
edeb28fd 927 commSetTimeout(fd, Config.Timeout.connect, httpTimeout, httpState);
928 commConnectStart(httpState->fd,
929 request->host,
930 request->port,
931 httpConnectDone,
a13d9042 932 httpState);
090089c4 933}
30a4f2a8 934
b8d8561b 935void
936httpReplyHeaderStats(StoreEntry * entry)
30a4f2a8 937{
6fb52f6c 938 http_server_cc_t i;
151a0b6d 939 http_hdr_misc_t j;
30a4f2a8 940 storeAppendPrintf(entry, open_bracket);
6fb52f6c 941 storeAppendPrintf(entry, "{HTTP Reply Headers:}\n");
942 storeAppendPrintf(entry, "{ Headers parsed: %d}\n",
30a4f2a8 943 ReplyHeaderStats.parsed);
151a0b6d 944 for (j = HDR_AGE; j < HDR_MISC_END; j++)
945 storeAppendPrintf(entry, "{%21.21s: %d}\n",
946 HttpHdrMiscStr[j],
947 ReplyHeaderStats.misc[j]);
4db43fab 948 for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++)
884011a1 949 storeAppendPrintf(entry, "{Cache-Control %s: %d}\n",
6fb52f6c 950 HttpServerCCStr[i],
951 ReplyHeaderStats.cc[i]);
30a4f2a8 952 storeAppendPrintf(entry, close_bracket);
953}
bfcaf585 954
955static void
956httpAbort(void *data)
957{
958 HttpStateData *httpState = data;
959 debug(11, 1, "httpAbort: %s\n", httpState->entry->url);
960 comm_close(httpState->fd);
961}