]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
binary tree fixes from 1.1.18
[thirdparty/squid.git] / src / http.cc
CommitLineData
da2b3a17 1
30a4f2a8 2/*
e04b118a 3 * $Id: http.cc,v 1.214 1997/10/31 23:37:24 wessels Exp $
30a4f2a8 4 *
5 * DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
6 * AUTHOR: Harvest Derived
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
019dd986 31
32/*
30a4f2a8 33 * Copyright (c) 1994, 1995. All rights reserved.
34 *
35 * The Harvest software was developed by the Internet Research Task
36 * Force Research Group on Resource Discovery (IRTF-RD):
37 *
38 * Mic Bowman of Transarc Corporation.
39 * Peter Danzig of the University of Southern California.
40 * Darren R. Hardy of the University of Colorado at Boulder.
41 * Udi Manber of the University of Arizona.
42 * Michael F. Schwartz of the University of Colorado at Boulder.
43 * Duane Wessels of the University of Colorado at Boulder.
44 *
45 * This copyright notice applies to software in the Harvest
46 * ``src/'' directory only. Users should consult the individual
47 * copyright notices in the ``components/'' subdirectories for
48 * copyright information about other software bundled with the
49 * Harvest source code distribution.
50 *
51 * TERMS OF USE
52 *
53 * The Harvest software may be used and re-distributed without
54 * charge, provided that the software origin and research team are
55 * cited in any use of the system. Most commonly this is
56 * accomplished by including a link to the Harvest Home Page
57 * (http://harvest.cs.colorado.edu/) from the query page of any
58 * Broker you deploy, as well as in the query result pages. These
59 * links are generated automatically by the standard Broker
60 * software distribution.
61 *
62 * The Harvest software is provided ``as is'', without express or
63 * implied warranty, and with no support nor obligation to assist
64 * in its use, correction, modification or enhancement. We assume
65 * no liability with respect to the infringement of copyrights,
66 * trade secrets, or any patents, and are not responsible for
67 * consequential damages. Proper use of the Harvest software is
68 * entirely the responsibility of the user.
69 *
70 * DERIVATIVE WORKS
71 *
72 * Users may make derivative works from the Harvest software, subject
73 * to the following constraints:
74 *
75 * - You must include the above copyright notice and these
76 * accompanying paragraphs in all forms of derivative works,
77 * and any documentation and other materials related to such
78 * distribution and use acknowledge that the software was
79 * developed at the above institutions.
80 *
81 * - You must notify IRTF-RD regarding your distribution of
82 * the derivative work.
83 *
84 * - You must clearly notify users that your are distributing
85 * a modified version and not the original Harvest software.
86 *
87 * - Any derivative product is also subject to these copyright
88 * and use restrictions.
89 *
90 * Note that the Harvest software is NOT in the public domain. We
91 * retain copyright, as specified above.
92 *
93 * HISTORY OF FREE SOFTWARE STATUS
94 *
95 * Originally we required sites to license the software in cases
96 * where they were going to build commercial products/services
97 * around Harvest. In June 1995 we changed this policy. We now
98 * allow people to use the core Harvest software (the code found in
99 * the Harvest ``src/'' directory) for free. We made this change
100 * in the interest of encouraging the widest possible deployment of
101 * the technology. The Harvest software is really a reference
102 * implementation of a set of protocols and formats, some of which
103 * we intend to standardize. We encourage commercial
104 * re-implementations of code complying to this set of standards.
019dd986 105 */
44a47c6e 106
4a83b852 107/*
108 * Anonymizing patch by lutz@as-node.jena.thur.de
de3bdb4c 109 * have a look into http-anon.c to get more informations.
4a83b852 110 */
111
44a47c6e 112#include "squid.h"
090089c4 113
6bf8443a 114static const char *const crlf = "\r\n";
4db43fab 115
6fb52f6c 116typedef enum {
117 SCC_PUBLIC,
118 SCC_PRIVATE,
119 SCC_NOCACHE,
120 SCC_NOSTORE,
121 SCC_NOTRANSFORM,
122 SCC_MUSTREVALIDATE,
123 SCC_PROXYREVALIDATE,
124 SCC_MAXAGE,
125 SCC_ENUM_END
126} http_server_cc_t;
127
6bf8443a 128enum {
6fb52f6c 129 CCC_NOCACHE,
130 CCC_NOSTORE,
131 CCC_MAXAGE,
132 CCC_MAXSTALE,
133 CCC_MINFRESH,
134 CCC_ONLYIFCACHED,
135 CCC_ENUM_END
6bf8443a 136};
137
151a0b6d 138typedef enum {
139 HDR_ACCEPT,
140 HDR_AGE,
141 HDR_CONTENT_LENGTH,
142 HDR_CONTENT_MD5,
143 HDR_CONTENT_TYPE,
144 HDR_DATE,
145 HDR_ETAG,
146 HDR_EXPIRES,
6bf8443a 147 HDR_HOST,
151a0b6d 148 HDR_IMS,
149 HDR_LAST_MODIFIED,
150 HDR_MAX_FORWARDS,
151 HDR_PUBLIC,
152 HDR_RETRY_AFTER,
153 HDR_SET_COOKIE,
154 HDR_UPGRADE,
155 HDR_WARNING,
1294c0fc 156 HDR_PROXY_KEEPALIVE,
151a0b6d 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;
f5b8bbc4 207static void httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int);
208static void httpCacheNegatively(StoreEntry *);
209static void httpMakePrivate(StoreEntry *);
210static void httpMakePublic(StoreEntry *);
211static char *httpStatusString(int status);
bfcaf585 212static STABH httpAbort;
f5b8bbc4 213static HttpStateData *httpBuildState(int, StoreEntry *, request_t *, peer *);
214static int httpSocketOpen(StoreEntry *, request_t *);
215static void httpRestart(HttpStateData *);
f8309b15 216static int httpCachableReply(HttpStateData *);
b8d8561b 217
b177367b 218static void
b716a8ad 219httpStateFree(int fd, void *data)
f5558c95 220{
b177367b 221 HttpStateData *httpState = data;
0d4d4170 222 if (httpState == NULL)
b177367b 223 return;
bfcaf585 224 storeUnregisterAbort(httpState->entry);
f88211e8 225 storeUnlockObject(httpState->entry);
0d4d4170 226 if (httpState->reply_hdr) {
227 put_free_8k_page(httpState->reply_hdr);
228 httpState->reply_hdr = NULL;
229 }
30a4f2a8 230 requestUnlink(httpState->request);
20cc1450 231 requestUnlink(httpState->orig_request);
7dd44885 232 httpState->request = NULL;
233 httpState->orig_request = NULL;
234 cbdataFree(httpState);
f5558c95 235}
236
b8d8561b 237int
75e88d56 238httpCachable(method_t method)
090089c4 239{
090089c4 240 /* GET and HEAD are cachable. Others are not. */
6eb42cae 241 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 242 return 0;
090089c4 243 /* else cachable */
244 return 1;
245}
246
b8d8561b 247static void
5c5783a2 248httpTimeout(int fd, void *data)
090089c4 249{
b177367b 250 HttpStateData *httpState = data;
593c9a75 251 StoreEntry *entry = httpState->entry;
9b312a19 252 ErrorState *err;
a3d5953d 253 debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, entry->url);
8796b9e9 254 assert(entry->store_status == STORE_PENDING);
73a3014d 255 if (entry->mem_obj->inmem_hi == 0) {
fe40a877 256 err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT);
9b312a19 257 err->request = requestLink(httpState->request);
258 errorAppendEntry(entry, err);
259 }
260 storeAbort(entry, 0);
0d4d4170 261 comm_close(fd);
090089c4 262}
263
30a4f2a8 264/* This object can be cached for a long time */
b8d8561b 265static void
266httpMakePublic(StoreEntry * entry)
30a4f2a8 267{
1c481e00 268 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 269 storeSetPublicKey(entry);
270}
271
272/* This object should never be cached at all */
b8d8561b 273static void
274httpMakePrivate(StoreEntry * entry)
30a4f2a8 275{
30a4f2a8 276 storeExpireNow(entry);
d3f89c29 277 BIT_CLR(entry->flag, ENTRY_CACHABLE);
30a4f2a8 278 storeReleaseRequest(entry); /* delete object when not used */
279}
280
281/* This object may be negatively cached */
b8d8561b 282static void
283httpCacheNegatively(StoreEntry * entry)
30a4f2a8 284{
79b5cc5f 285 storeNegativeCache(entry);
1c481e00 286 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 287 storeSetPublicKey(entry);
30a4f2a8 288}
289
290
291/* Build a reply structure from HTTP reply headers */
b8d8561b 292void
48f44632 293httpParseReplyHeaders(const char *buf, struct _http_reply *reply)
30a4f2a8 294{
33b589ff 295 char *headers = get_free_4k_page();
ca85027a 296 char *line;
33b589ff 297 char *end;
30a4f2a8 298 char *s = NULL;
33b589ff 299 char *t;
ca98227c 300 time_t delta;
301 size_t l;
30a4f2a8 302
5b29969a 303 assert(reply != NULL);
f1494beb 304 reply->code = 600;
30a4f2a8 305 ReplyHeaderStats.parsed++;
33b589ff 306 xstrncpy(headers, buf, 4096);
307 end = mime_headers_end(headers);
ca85027a 308 if (end == NULL) {
309 t = headers;
e2ad7f85 310 if (!strncasecmp(t, "HTTP/", 5)) {
311 reply->version = atof(t + 5);
312 if ((t = strchr(t, ' ')))
313 reply->code = atoi(++t);
ca85027a 314 }
e2ad7f85 315 put_free_4k_page(headers);
316 return;
ca85027a 317 }
318 reply->hdr_sz = end - headers;
319 line = get_free_4k_page();
33b589ff 320 for (s = headers; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
321 l = strcspn(s, crlf) + 1;
322 if (l > 4096)
323 l = 4096;
324 xstrncpy(line, s, l);
325 t = line;
a3d5953d 326 debug(11, 3) ("httpParseReplyHeaders: %s\n", t);
33b589ff 327 if (!strncasecmp(t, "HTTP/", 5)) {
e2ad7f85 328 reply->version = atof(t + 5);
33b589ff 329 if ((t = strchr(t, ' ')))
330 reply->code = atoi(++t);
30a4f2a8 331 } else if (!strncasecmp(t, "Content-type:", 13)) {
33b589ff 332 for (t += 13; isspace(*t); t++);
e91b56e5 333 if ((l = strcspn(t, ";\t ")) > 0)
334 *(t + l) = '\0';
33b589ff 335 xstrncpy(reply->content_type, t, HTTP_REPLY_FIELD_SZ);
151a0b6d 336 ReplyHeaderStats.misc[HDR_CONTENT_TYPE]++;
30a4f2a8 337 } else if (!strncasecmp(t, "Content-length:", 15)) {
33b589ff 338 for (t += 15; isspace(*t); t++);
33b589ff 339 reply->content_length = atoi(t);
151a0b6d 340 ReplyHeaderStats.misc[HDR_CONTENT_LENGTH]++;
30a4f2a8 341 } else if (!strncasecmp(t, "Date:", 5)) {
33b589ff 342 for (t += 5; isspace(*t); t++);
343 reply->date = parse_rfc1123(t);
151a0b6d 344 ReplyHeaderStats.misc[HDR_DATE]++;
30a4f2a8 345 } else if (!strncasecmp(t, "Expires:", 8)) {
33b589ff 346 for (t += 8; isspace(*t); t++);
347 reply->expires = parse_rfc1123(t);
348 /*
349 * The HTTP/1.0 specs says that robust implementations
350 * should consider bad or malformed Expires header as
351 * equivalent to "expires immediately."
352 */
353 if (reply->expires == -1)
354 reply->expires = squid_curtime;
151a0b6d 355 ReplyHeaderStats.misc[HDR_EXPIRES]++;
30a4f2a8 356 } else if (!strncasecmp(t, "Last-Modified:", 14)) {
33b589ff 357 for (t += 14; isspace(*t); t++);
358 reply->last_modified = parse_rfc1123(t);
151a0b6d 359 ReplyHeaderStats.misc[HDR_LAST_MODIFIED]++;
360 } else if (!strncasecmp(t, "Accept:", 7)) {
361 ReplyHeaderStats.misc[HDR_ACCEPT]++;
362 } else if (!strncasecmp(t, "Age:", 4)) {
363 ReplyHeaderStats.misc[HDR_AGE]++;
364 } else if (!strncasecmp(t, "Content-MD5:", 12)) {
365 ReplyHeaderStats.misc[HDR_CONTENT_MD5]++;
366 } else if (!strncasecmp(t, "ETag:", 5)) {
367 ReplyHeaderStats.misc[HDR_ETAG]++;
368 } else if (!strncasecmp(t, "Max-Forwards:", 13)) {
369 ReplyHeaderStats.misc[HDR_MAX_FORWARDS]++;
370 } else if (!strncasecmp(t, "Public:", 7)) {
371 ReplyHeaderStats.misc[HDR_PUBLIC]++;
372 } else if (!strncasecmp(t, "Retry-After:", 12)) {
373 ReplyHeaderStats.misc[HDR_RETRY_AFTER]++;
374 } else if (!strncasecmp(t, "Upgrade:", 8)) {
375 ReplyHeaderStats.misc[HDR_UPGRADE]++;
376 } else if (!strncasecmp(t, "Warning:", 8)) {
377 ReplyHeaderStats.misc[HDR_WARNING]++;
caebbe00 378 } else if (!strncasecmp(t, "Cache-Control:", 14)) {
33b589ff 379 for (t += 14; isspace(*t); t++);
4db43fab 380 if (!strncasecmp(t, "public", 6)) {
381 EBIT_SET(reply->cache_control, SCC_PUBLIC);
382 ReplyHeaderStats.cc[SCC_PUBLIC]++;
383 } else if (!strncasecmp(t, "private", 7)) {
384 EBIT_SET(reply->cache_control, SCC_PRIVATE);
385 ReplyHeaderStats.cc[SCC_PRIVATE]++;
386 } else if (!strncasecmp(t, "no-cache", 8)) {
387 EBIT_SET(reply->cache_control, SCC_NOCACHE);
388 ReplyHeaderStats.cc[SCC_NOCACHE]++;
c1764328 389 } else if (!strncasecmp(t, "no-store", 8)) {
390 EBIT_SET(reply->cache_control, SCC_NOSTORE);
391 ReplyHeaderStats.cc[SCC_NOSTORE]++;
392 } else if (!strncasecmp(t, "no-transform", 12)) {
393 EBIT_SET(reply->cache_control, SCC_NOTRANSFORM);
394 ReplyHeaderStats.cc[SCC_NOTRANSFORM]++;
395 } else if (!strncasecmp(t, "must-revalidate", 15)) {
396 EBIT_SET(reply->cache_control, SCC_MUSTREVALIDATE);
397 ReplyHeaderStats.cc[SCC_MUSTREVALIDATE]++;
398 } else if (!strncasecmp(t, "proxy-revalidate", 16)) {
399 EBIT_SET(reply->cache_control, SCC_PROXYREVALIDATE);
400 ReplyHeaderStats.cc[SCC_PROXYREVALIDATE]++;
4db43fab 401 } else if (!strncasecmp(t, "max-age", 7)) {
402 if ((t = strchr(t, '='))) {
ca98227c 403 delta = (time_t) atoi(++t);
404 reply->expires = squid_curtime + delta;
4db43fab 405 EBIT_SET(reply->cache_control, SCC_MAXAGE);
406 ReplyHeaderStats.cc[SCC_MAXAGE]++;
5df61230 407 }
caebbe00 408 }
df3ac7c0 409 } else if (!strncasecmp(t, "Set-Cookie:", 11)) {
410 EBIT_SET(reply->misc_headers, HDR_SET_COOKIE);
58202be7 411 ReplyHeaderStats.misc[HDR_SET_COOKIE]++;
1294c0fc 412 } else if (!strncasecmp(t, "Proxy-Connection:", 17)) {
537eedcb 413 for (t += 17; isspace(*t); t++);
1294c0fc 414 if (!strcasecmp(t, "Keep-Alive"))
415 EBIT_SET(reply->misc_headers, HDR_PROXY_KEEPALIVE);
30a4f2a8 416 }
30a4f2a8 417 }
33b589ff 418 put_free_4k_page(headers);
419 put_free_4k_page(line);
30a4f2a8 420}
421
f8309b15 422static int
423httpCachableReply(HttpStateData * httpState)
c54e9052 424{
f8309b15 425 struct _http_reply *reply = httpState->entry->mem_obj->reply;
426 if (EBIT_TEST(reply->cache_control, SCC_PRIVATE))
427 return 0;
428 if (EBIT_TEST(reply->cache_control, SCC_NOCACHE))
429 return 0;
430 /*
431 * Dealing with cookies is quite a bit more complicated
432 * than this. Ideally we should strip the cookie
433 * header from the reply but still cache the reply body.
434 * More confusion at draft-ietf-http-state-mgmt-05.txt.
435 */
436 if (EBIT_TEST(reply->misc_headers, HDR_SET_COOKIE))
437 return 0;
c54e9052 438 switch (reply->code) {
439 /* Responses that are cacheable */
440 case 200: /* OK */
441 case 203: /* Non-Authoritative Information */
442 case 300: /* Multiple Choices */
443 case 301: /* Moved Permanently */
444 case 410: /* Gone */
1294c0fc 445 /* don't cache objects from peers w/o LMT, Date, or Expires */
c54e9052 446 if (reply->date > -1)
447 return 1;
f8309b15 448 else if (reply->last_modified > -1)
c54e9052 449 return 1;
1294c0fc 450 else if (!httpState->peer)
c54e9052 451 return 1;
f8309b15 452 else if (reply->expires > -1)
c54e9052 453 return 1;
c54e9052 454 else
455 return 0;
456 break;
457 /* Responses that only are cacheable if the server says so */
458 case 302: /* Moved temporarily */
459 if (reply->expires > -1)
460 return 1;
461 else
462 return 0;
463 break;
464 /* Errors can be negatively cached */
465 case 204: /* No Content */
466 case 305: /* Use Proxy (proxy redirect) */
467 case 400: /* Bad Request */
468 case 403: /* Forbidden */
469 case 404: /* Not Found */
470 case 405: /* Method Now Allowed */
471 case 414: /* Request-URI Too Long */
472 case 500: /* Internal Server Error */
473 case 501: /* Not Implemented */
474 case 502: /* Bad Gateway */
475 case 503: /* Service Unavailable */
476 case 504: /* Gateway Timeout */
477 return -1;
478 break;
479 /* Some responses can never be cached */
88738790 480 case 206: /* Partial Content -- Not yet supported */
c54e9052 481 case 303: /* See Other */
482 case 304: /* Not Modified */
483 case 401: /* Unauthorized */
484 case 407: /* Proxy Authentication Required */
485 case 600: /* Squid header parsing error */
486 default: /* Unknown status code */
487 return 0;
488 break;
489 }
490 assert(0);
491}
090089c4 492
b8d8561b 493void
0ee4272b 494httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 495{
496 char *t = NULL;
30a4f2a8 497 StoreEntry *entry = httpState->entry;
d3fb4dea 498 int room;
499 int hdr_len;
33b589ff 500 struct _http_reply *reply = entry->mem_obj->reply;
a3d5953d 501 debug(11, 3) ("httpProcessReplyHeader: key '%s'\n", entry->key);
e924600d 502 if (httpState->reply_hdr == NULL)
30a4f2a8 503 httpState->reply_hdr = get_free_8k_page();
30a4f2a8 504 if (httpState->reply_hdr_state == 0) {
505 hdr_len = strlen(httpState->reply_hdr);
ed85b771 506 room = 8191 - hdr_len;
30a4f2a8 507 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 508 hdr_len += room < size ? room : size;
30a4f2a8 509 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
a3d5953d 510 debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", entry->key);
30a4f2a8 511 httpState->reply_hdr_state += 2;
33b589ff 512 reply->code = 555;
ed85b771 513 return;
d3fb4dea 514 }
d1a43e28 515 t = httpState->reply_hdr + hdr_len;
516 /* headers can be incomplete only if object still arriving */
f86a6a46 517 if (!httpState->eof)
d1a43e28 518 if ((t = mime_headers_end(httpState->reply_hdr)) == NULL)
519 return; /* headers not complete */
2285407f 520 *t = '\0';
30a4f2a8 521 httpState->reply_hdr_state++;
f5558c95 522 }
30a4f2a8 523 if (httpState->reply_hdr_state == 1) {
524 httpState->reply_hdr_state++;
a3d5953d 525 debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 526 httpState->reply_hdr);
527 /* Parse headers into reply structure */
48f44632 528 httpParseReplyHeaders(httpState->reply_hdr, reply);
ca98227c 529 storeTimestampsSet(entry);
30a4f2a8 530 /* Check if object is cacheable or not based on reply code */
a3d5953d 531 debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->code);
f8309b15 532 switch (httpCachableReply(httpState)) {
c54e9052 533 case 1:
534 httpMakePublic(entry);
30a4f2a8 535 break;
c54e9052 536 case 0:
537 httpMakePrivate(entry);
f5558c95 538 break;
c54e9052 539 case -1:
851eeef7 540 httpCacheNegatively(entry);
30a4f2a8 541 break;
c54e9052 542 default:
543 assert(0);
4e38e700 544 break;
f5558c95 545 }
c54e9052 546 if (EBIT_TEST(reply->cache_control, SCC_PROXYREVALIDATE))
547 BIT_SET(entry->flag, ENTRY_REVALIDATE);
1294c0fc 548 if (EBIT_TEST(reply->misc_headers, HDR_PROXY_KEEPALIVE))
549 if (httpState->peer)
550 httpState->peer->stats.n_keepalives_recv++;
f5558c95 551 }
552}
553
603a02fd 554static int
555httpPconnTransferDone(HttpStateData * httpState)
556{
557 /* return 1 if we got the last of the data on a persistent connection */
558 MemObject *mem = httpState->entry->mem_obj;
559 struct _http_reply *reply = mem->reply;
51fdcbd5 560 debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd);
603a02fd 561 if (!BIT_TEST(httpState->flags, HTTP_KEEPALIVE))
562 return 0;
51fdcbd5 563 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
603a02fd 564 reply->content_length);
565 /*
566 * !200 replies maybe don't have content-length, so
567 * if we saw the end of the headers then try being persistent.
568 */
569 if (reply->code != 200)
570 if (httpState->reply_hdr_state > 1)
571 return 1;
572 /*
573 * If there is no content-length, then we probably can't be persistent
574 */
575 if (reply->content_length == 0)
576 return 0;
577 /*
578 * If there is a content_length, see if we've got all of it. If so,
579 * then we can recycle this connection.
580 */
51fdcbd5 581 debug(11, 5) ("httpPconnTransferDone: hdr_sz=%d\n", reply->hdr_sz);
8350fe9b 582 debug(11, 5) ("httpPconnTransferDone: inmem_hi=%d\n",
583 mem->inmem_hi);
584 if (mem->inmem_hi < reply->content_length + reply->hdr_sz)
603a02fd 585 return 0;
586 return 1;
587}
090089c4 588
589/* This will be called when data is ready to be read from fd. Read until
590 * error or connection closed. */
f5558c95 591/* XXX this function is too long! */
b8d8561b 592static void
b177367b 593httpReadReply(int fd, void *data)
090089c4 594{
b177367b 595 HttpStateData *httpState = data;
95d659f0 596 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
bfcaf585 597 StoreEntry *entry = httpState->entry;
603a02fd 598 const request_t *request = httpState->request;
090089c4 599 int len;
30a4f2a8 600 int bin;
090089c4 601 int clen;
602 int off;
9b312a19 603 ErrorState *err;
d89d1fb6 604 if (protoAbortFetch(entry)) {
9b312a19 605 storeAbort(entry, 0);
a3d5953d 606 comm_close(fd);
607 return;
234967c9 608 }
609 /* check if we want to defer reading */
8350fe9b 610 clen = entry->mem_obj->inmem_hi;
611 off = storeLowestMemReaderOffset(entry);
1513873c 612 errno = 0;
30a4f2a8 613 len = read(fd, buf, SQUID_TCP_SO_RCVBUF);
4f92c80c 614 fd_bytes(fd, len, FD_READ);
a3d5953d 615 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 616 if (len > 0) {
4f92c80c 617 commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
4a63c85f 618 IOStats.Http.reads++;
30a4f2a8 619 for (clen = len - 1, bin = 0; clen; bin++)
620 clen >>= 1;
621 IOStats.Http.read_hist[bin]++;
622 }
ba718c8f 623 if (len < 0) {
0a0bf5db 624 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
9b312a19 625 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
090089c4 626 } else {
73a3014d 627 if (clen == 0) {
fe40a877 628 err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 629 err->xerrno = errno;
9b312a19 630 err->request = requestLink(httpState->request);
631 errorAppendEntry(entry, err);
632 }
633 storeAbort(entry, 0);
0d4d4170 634 comm_close(fd);
090089c4 635 }
a3d5953d 636 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
0a0bf5db 637 fd, xstrerror());
8350fe9b 638 } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
b716a8ad 639 if (fd_table[fd].uses > 1) {
640 httpRestart(httpState);
641 } else {
642 httpState->eof = 1;
fe40a877 643 err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 644 err->xerrno = errno;
b716a8ad 645 err->request = requestLink(httpState->request);
646 errorAppendEntry(entry, err);
647 storeAbort(entry, 0);
648 comm_close(fd);
649 }
090089c4 650 } else if (len == 0) {
651 /* Connection closed; retrieval done. */
f86a6a46 652 httpState->eof = 1;
d1a43e28 653 if (httpState->reply_hdr_state < 2)
654 httpProcessReplyHeader(httpState, buf, len);
d1a43e28 655 storeComplete(entry); /* deallocates mem_obj->request */
0d4d4170 656 comm_close(fd);
090089c4 657 } else {
d1a43e28 658 if (httpState->reply_hdr_state < 2)
30a4f2a8 659 httpProcessReplyHeader(httpState, buf, len);
620da955 660 storeAppend(entry, buf, len);
603a02fd 661 if (httpPconnTransferDone(httpState)) {
5b29969a 662 /* yes we have to clear all these! */
8796b9e9 663 commSetDefer(fd, NULL, NULL);
5b29969a 664 commSetTimeout(fd, -1, NULL, NULL);
665 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
603a02fd 666 comm_remove_close_handler(fd, httpStateFree, httpState);
667 storeComplete(entry); /* deallocates mem_obj->request */
8796b9e9 668 pconnPush(fd, request->host, request->port);
603a02fd 669 httpState->fd = -1;
670 httpStateFree(-1, httpState);
671 } else {
672 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
673 }
090089c4 674 }
675}
676
677/* This will be called when request write is complete. Schedule read of
678 * reply. */
b8d8561b 679static void
680httpSendComplete(int fd, char *buf, int size, int errflag, void *data)
090089c4 681{
30a4f2a8 682 HttpStateData *httpState = data;
9b312a19 683 StoreEntry *entry = httpState->entry;
684 ErrorState *err;
a3d5953d 685 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 686 fd, size, errflag);
ea3a2a69 687 if (errflag == COMM_ERR_CLOSING)
688 return;
090089c4 689 if (errflag) {
fe40a877 690 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 691 err->xerrno = errno;
9b312a19 692 err->request = requestLink(httpState->request);
693 errorAppendEntry(entry, err);
694 storeAbort(entry, 0);
0d4d4170 695 comm_close(fd);
090089c4 696 return;
697 } else {
698 /* Schedule read reply. */
b177367b 699 commSetSelect(fd,
019dd986 700 COMM_SELECT_READ,
b177367b 701 httpReadReply,
cd1fb0eb 702 httpState, 0);
70a9dab4 703 commSetDefer(fd, protoCheckDeferRead, entry);
090089c4 704 }
705}
706
6bf8443a 707static void
88738790 708httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int check)
6bf8443a 709{
710 size_t n = *sz + strlen(line) + 2;
711 if (n >= max)
712 return;
88738790 713 if (check) {
17a0a4ee 714 if (Config.onoff.anonymizer == ANONYMIZER_PARANOID) {
88738790 715 if (!httpAnonAllowed(line))
716 return;
17a0a4ee 717 } else if (Config.onoff.anonymizer == ANONYMIZER_STANDARD) {
88738790 718 if (httpAnonDenied(line))
719 return;
720 }
4a83b852 721 }
4a83b852 722 /* allowed header, explicitly known to be not dangerous */
a3d5953d 723 debug(11, 5) ("httpAppendRequestHeader: %s\n", line);
929545fe 724 strcpy(hdr + (*sz), line);
6bf8443a 725 strcat(hdr + (*sz), crlf);
726 *sz = n;
727}
728
1294c0fc 729#define YBUF_SZ (MAX_URL+32)
6bf8443a 730size_t
731httpBuildRequestHeader(request_t * request,
732 request_t * orig_request,
733 StoreEntry * entry,
6bf8443a 734 size_t * in_len,
735 char *hdr_out,
736 size_t out_sz,
603a02fd 737 int cfd,
738 int flags)
6bf8443a 739{
1294c0fc 740 LOCAL_ARRAY(char, ybuf, YBUF_SZ);
6bf8443a 741 char *xbuf = get_free_4k_page();
6bf8443a 742 char *viabuf = get_free_4k_page();
743 char *fwdbuf = get_free_4k_page();
744 char *t = NULL;
745 char *s = NULL;
746 char *end = NULL;
747 size_t len = 0;
748 size_t hdr_len = 0;
6bf8443a 749 size_t l;
750 int hdr_flags = 0;
151a0b6d 751 int cc_flags = 0;
b3b64e58 752 int n;
6bf8443a 753 const char *url = NULL;
2357f74a 754 char *hdr_in = orig_request->headers;
6bf8443a 755
2357f74a 756 assert(hdr_in != NULL);
a3d5953d 757 debug(11, 3) ("httpBuildRequestHeader: INPUT:\n%s\n", hdr_in);
6bf8443a 758 xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096);
759 xstrncpy(viabuf, "Via: ", 4096);
1294c0fc 760 snprintf(ybuf, YBUF_SZ, "%s %s HTTP/1.0",
6bf8443a 761 RequestMethodStr[request->method],
762 *request->urlpath ? request->urlpath : "/");
88738790 763 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
6bf8443a 764 /* Add IMS header */
765 if (entry && entry->lastmod && request->method == METHOD_GET) {
1294c0fc 766 snprintf(ybuf, YBUF_SZ, "If-Modified-Since: %s", mkrfc1123(entry->lastmod));
88738790 767 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
6bf8443a 768 EBIT_SET(hdr_flags, HDR_IMS);
769 }
00c59270 770 end = mime_headers_end(hdr_in);
6bf8443a 771 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
772 hdr_len = t - hdr_in;
6bf8443a 773 l = strcspn(t, crlf) + 1;
774 if (l > 4096)
775 l = 4096;
776 xstrncpy(xbuf, t, l);
a3d5953d 777 debug(11, 5) ("httpBuildRequestHeader: %s\n", xbuf);
6bf8443a 778 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
779 continue;
88738790 780 if (strncasecmp(xbuf, "Proxy-authorization:", 20) == 0)
afe95a7e 781 /* If we're not going to do proxy auth, then it must be passed on */
c1517455 782 if (BIT_TEST(request->flags, REQ_USED_PROXY_AUTH))
88738790 783 continue;
6bf8443a 784 if (strncasecmp(xbuf, "Connection:", 11) == 0)
785 continue;
66f7337b 786 if (strncasecmp(xbuf, "Host:", 5) == 0) {
6bf8443a 787 EBIT_SET(hdr_flags, HDR_HOST);
66f7337b 788 } else if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) {
6bf8443a 789 for (s = xbuf + 14; *s && isspace(*s); s++);
790 if (strncasecmp(s, "Max-age=", 8) == 0)
151a0b6d 791 EBIT_SET(cc_flags, CCC_MAXAGE);
66f7337b 792 } else if (strncasecmp(xbuf, "Via:", 4) == 0) {
6bf8443a 793 for (s = xbuf + 4; *s && isspace(*s); s++);
794 if (strlen(viabuf) + strlen(s) < 4000)
795 strcat(viabuf, s);
796 strcat(viabuf, ", ");
797 continue;
66f7337b 798 } else if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) {
6bf8443a 799 for (s = xbuf + 16; *s && isspace(*s); s++);
800 if (strlen(fwdbuf) + strlen(s) < 4000)
801 strcat(fwdbuf, s);
802 strcat(fwdbuf, ", ");
803 continue;
66f7337b 804 } else if (strncasecmp(xbuf, "If-Modified-Since:", 18) == 0) {
6bf8443a 805 if (EBIT_TEST(hdr_flags, HDR_IMS))
806 continue;
b3b64e58 807 } else if (strncasecmp(xbuf, "Max-Forwards:", 13) == 0) {
808 if (orig_request->method == METHOD_TRACE) {
809 for (s = xbuf + 13; *s && isspace(*s); s++);
810 n = atoi(s);
56878878 811 snprintf(xbuf, 4096, "Max-Forwards: %d", n - 1);
b3b64e58 812 }
66f7337b 813 }
88738790 814 httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512, 1);
815 }
816 hdr_len = t - hdr_in;
817 if (Config.fake_ua && strstr(hdr_out, "User-Agent") == NULL) {
1294c0fc 818 snprintf(ybuf, YBUF_SZ, "User-Agent: %s", Config.fake_ua);
88738790 819 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 0);
6bf8443a 820 }
56878878 821 /* Append Via: */
822 /* snprintf would fail here too */
1294c0fc 823 snprintf(ybuf, YBUF_SZ, "%3.1f %s", orig_request->http_ver, ThisCache);
6bf8443a 824 strcat(viabuf, ybuf);
88738790 825 httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz, 1);
6bf8443a 826 /* Append to X-Forwarded-For: */
a08307eb 827 strcat(fwdbuf, cfd < 0 ? "unknown" : fd_table[cfd].ipaddr);
88738790 828 httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz, 1);
6bf8443a 829 if (!EBIT_TEST(hdr_flags, HDR_HOST)) {
1294c0fc 830 snprintf(ybuf, YBUF_SZ, "Host: %s", orig_request->host);
88738790 831 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
6bf8443a 832 }
151a0b6d 833 if (!EBIT_TEST(cc_flags, CCC_MAXAGE)) {
6bf8443a 834 url = entry ? entry->url : urlCanonical(orig_request, NULL);
1294c0fc 835 snprintf(ybuf, YBUF_SZ, "Cache-control: Max-age=%d", (int) getMaxAge(url));
88738790 836 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
365e5b34 837 if (request->urlpath) {
838 assert(strstr(url, request->urlpath));
839 }
6bf8443a 840 }
603a02fd 841 /* maybe append Connection: Keep-Alive */
842 if (BIT_TEST(flags, HTTP_KEEPALIVE)) {
843 if (BIT_TEST(flags, HTTP_PROXYING)) {
1294c0fc 844 snprintf(ybuf, YBUF_SZ, "Proxy-Connection: Keep-Alive");
603a02fd 845 } else {
1294c0fc 846 snprintf(ybuf, YBUF_SZ, "Connection: Keep-Alive");
603a02fd 847 }
848 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
849 }
88738790 850 httpAppendRequestHeader(hdr_out, null_string, &len, out_sz, 1);
6bf8443a 851 put_free_4k_page(xbuf);
6bf8443a 852 put_free_4k_page(viabuf);
853 put_free_4k_page(fwdbuf);
854 if (in_len)
855 *in_len = hdr_len;
9d9d144b 856 if ((l = strlen(hdr_out)) != len) {
857 debug_trap("httpBuildRequestHeader: size mismatch");
858 len = l;
859 }
a3d5953d 860 debug(11, 3) ("httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out);
6bf8443a 861 return len;
862}
863
090089c4 864/* This will be called when connect completes. Write request. */
b8d8561b 865static void
b177367b 866httpSendRequest(int fd, void *data)
090089c4 867{
b177367b 868 HttpStateData *httpState = data;
090089c4 869 char *buf = NULL;
090089c4 870 int len = 0;
871 int buflen;
30a4f2a8 872 request_t *req = httpState->request;
9864ee44 873 int buftype = 0;
620da955 874 StoreEntry *entry = httpState->entry;
2a26c096 875 int cfd;
1294c0fc 876 peer *p = httpState->peer;
9a1d2653 877 double d;
090089c4 878
a3d5953d 879 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
6bf8443a 880 buflen = strlen(req->urlpath);
62dec5a7 881 if (req->headers)
882 buflen += req->headers_sz + 1;
090089c4 883 buflen += 512; /* lots of extra */
884
78657218 885 if ((req->method == METHOD_POST || req->method == METHOD_PUT)) {
886 debug_trap("httpSendRequest: should not be handling POST/PUT request");
887 return;
090089c4 888 }
30a4f2a8 889 if (buflen < DISK_PAGE_SIZE) {
9864ee44 890 buf = get_free_8k_page();
9864ee44 891 buftype = BUF_TYPE_8K;
6bf8443a 892 buflen = DISK_PAGE_SIZE;
30a4f2a8 893 } else {
9864ee44 894 buf = xcalloc(buflen, 1);
895 buftype = BUF_TYPE_MALLOC;
090089c4 896 }
2a26c096 897 if (!opt_forwarded_for)
6bf8443a 898 cfd = -1;
2a26c096 899 else if (entry->mem_obj == NULL)
6bf8443a 900 cfd = -1;
2a26c096 901 else
382d851a 902 cfd = entry->mem_obj->fd;
1294c0fc 903 if (p != NULL)
603a02fd 904 BIT_SET(httpState->flags, HTTP_PROXYING);
1294c0fc 905 if (req->method == METHOD_GET) {
1294c0fc 906 if (p) {
9a75cc4d 907 d = (double) p->stats.n_keepalives_recv /
908 (double) ++p->stats.n_keepalives_sent;
153cf740 909 if (d > 0.50 || p->stats.n_keepalives_sent < 10)
9a1d2653 910 BIT_SET(httpState->flags, HTTP_KEEPALIVE);
911 } else {
912 BIT_SET(httpState->flags, HTTP_KEEPALIVE);
1294c0fc 913 }
914 }
6bf8443a 915 len = httpBuildRequestHeader(req,
916 httpState->orig_request ? httpState->orig_request : req,
917 entry,
6bf8443a 918 NULL,
919 buf,
920 buflen,
603a02fd 921 cfd,
922 httpState->flags);
a3d5953d 923 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, buf);
30a4f2a8 924 comm_write(fd,
14e59844 925 buf,
926 len,
14e59844 927 httpSendComplete,
9864ee44 928 httpState,
929 buftype == BUF_TYPE_8K ? put_free_8k_page : xfree);
52ecbd09 930#ifdef BREAKS_PCONN_RESTART
20cc1450 931 requestUnlink(httpState->orig_request);
932 httpState->orig_request = NULL;
52ecbd09 933#endif
090089c4 934}
935
603a02fd 936static int
b716a8ad 937httpSocketOpen(StoreEntry * entry, request_t * request)
090089c4 938{
9e4ad609 939 int fd;
9b312a19 940 ErrorState *err;
9e4ad609 941 fd = comm_open(SOCK_STREAM,
16b204c4 942 0,
943 Config.Addrs.tcp_outgoing,
944 0,
945 COMM_NONBLOCKING,
75e88d56 946 entry->url);
603a02fd 947 if (fd < 0) {
948 debug(11, 4) ("httpSocketOpen: Failed because we're out of sockets.\n");
fe40a877 949 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 950 err->xerrno = errno;
603a02fd 951 if (request)
952 err->request = requestLink(request);
9b312a19 953 errorAppendEntry(entry, err);
954 storeAbort(entry, 0);
090089c4 955 }
603a02fd 956 return fd;
957}
958
959static HttpStateData *
960httpBuildState(int fd, StoreEntry * entry, request_t * orig_request, peer * e)
961{
962 HttpStateData *httpState = xcalloc(1, sizeof(HttpStateData));
963 request_t *request;
770f051d 964 storeLockObject(entry);
7dd44885 965 cbdataAdd(httpState);
0a0bf5db 966 httpState->entry = entry;
9e4ad609 967 httpState->fd = fd;
603a02fd 968 if (e) {
969 request = get_free_request_t();
970 request->method = orig_request->method;
971 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
972 request->port = e->http_port;
973 xstrncpy(request->urlpath, entry->url, MAX_URL);
974 httpState->request = requestLink(request);
1294c0fc 975 httpState->peer = e;
603a02fd 976 httpState->orig_request = requestLink(orig_request);
977 BIT_SET(request->flags, REQ_PROXYING);
978 } else {
979 httpState->request = requestLink(orig_request);
980 }
0d4d4170 981 /* register the handler to free HTTP state data when the FD closes */
603a02fd 982 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
e102ebda 983 storeRegisterAbort(entry, httpAbort, httpState);
603a02fd 984 return httpState;
985}
986
987void
988httpStart(request_t * request, StoreEntry * entry, peer * e)
989{
990 HttpStateData *httpState;
991 int fd;
992 debug(11, 3) ("httpStart: \"%s %s\"\n",
993 RequestMethodStr[request->method], entry->url);
994 if (e) {
995 if (e->options & NEIGHBOR_PROXY_ONLY)
603a02fd 996 storeReleaseRequest(entry);
603a02fd 997 if ((fd = pconnPop(e->host, e->http_port)) >= 0) {
51fdcbd5 998 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
603a02fd 999 httpState = httpBuildState(fd, entry, request, e);
b716a8ad 1000 commSetTimeout(httpState->fd,
1001 Config.Timeout.connect,
1002 httpTimeout,
1003 httpState);
603a02fd 1004 httpConnectDone(fd, COMM_OK, httpState);
1005 return;
1006 }
1007 } else {
1008 if ((fd = pconnPop(request->host, request->port)) >= 0) {
51fdcbd5 1009 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
603a02fd 1010 httpState = httpBuildState(fd, entry, request, e);
b716a8ad 1011 commSetTimeout(httpState->fd,
1012 Config.Timeout.connect,
1013 httpTimeout,
1014 httpState);
603a02fd 1015 httpConnectDone(fd, COMM_OK, httpState);
1016 return;
1017 }
1018 }
1019 if ((fd = httpSocketOpen(entry, NULL)) < 0)
1020 return;
1021 httpState = httpBuildState(fd, entry, request, e);
603a02fd 1022 commSetTimeout(httpState->fd,
1023 Config.Timeout.connect,
1024 httpTimeout,
cd1fb0eb 1025 httpState);
edeb28fd 1026 commConnectStart(httpState->fd,
603a02fd 1027 httpState->request->host,
1028 httpState->request->port,
e924600d 1029 httpConnectDone,
1030 httpState);
e5f6c5c2 1031}
1032
b716a8ad 1033static void
1034httpRestart(HttpStateData * httpState)
1035{
1036 /* restart a botched request from a persistent connection */
dd8037ab 1037 debug(11, 2) ("Retrying HTTP request for %s\n", httpState->entry->url);
b716a8ad 1038 if (httpState->fd >= 0) {
1039 comm_remove_close_handler(httpState->fd, httpStateFree, httpState);
1040 comm_close(httpState->fd);
1041 httpState->fd = -1;
1042 }
1043 httpState->fd = httpSocketOpen(httpState->entry, httpState->request);
1044 if (httpState->fd < 0)
1045 return;
1046 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
1047 commSetTimeout(httpState->fd,
1048 Config.Timeout.connect,
1049 httpTimeout,
1050 httpState);
1051 commConnectStart(httpState->fd,
1052 httpState->request->host,
1053 httpState->request->port,
1054 httpConnectDone,
1055 httpState);
1056}
1057
e5f6c5c2 1058static void
1059httpConnectDone(int fd, int status, void *data)
1060{
1061 HttpStateData *httpState = data;
1062 request_t *request = httpState->request;
1063 StoreEntry *entry = httpState->entry;
9b312a19 1064 ErrorState *err;
edeb28fd 1065 if (status == COMM_ERR_DNS) {
a3d5953d 1066 debug(11, 4) ("httpConnectDone: Unknown host: %s\n", request->host);
fe40a877 1067 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
9b312a19 1068 err->dnsserver_msg = xstrdup(dns_error_message);
1069 err->request = requestLink(request);
1070 errorAppendEntry(entry, err);
1071 storeAbort(entry, 0);
edeb28fd 1072 comm_close(fd);
1073 } else if (status != COMM_OK) {
fe40a877 1074 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 1075 err->xerrno = errno;
9b312a19 1076 err->host = xstrdup(request->host);
1077 err->port = request->port;
1078 err->request = requestLink(request);
1079 errorAppendEntry(entry, err);
1080 storeAbort(entry, 0);
1294c0fc 1081 if (httpState->peer)
1082 peerCheckConnectStart(httpState->peer);
e5f6c5c2 1083 comm_close(fd);
1084 } else {
e5f6c5c2 1085 fd_note(fd, entry->url);
b716a8ad 1086 fd_table[fd].uses++;
bfcaf585 1087 commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0);
090089c4 1088 }
090089c4 1089}
1090
b8d8561b 1091void
1092httpReplyHeaderStats(StoreEntry * entry)
30a4f2a8 1093{
6fb52f6c 1094 http_server_cc_t i;
151a0b6d 1095 http_hdr_misc_t j;
30a4f2a8 1096 storeAppendPrintf(entry, open_bracket);
6fb52f6c 1097 storeAppendPrintf(entry, "{HTTP Reply Headers:}\n");
1098 storeAppendPrintf(entry, "{ Headers parsed: %d}\n",
30a4f2a8 1099 ReplyHeaderStats.parsed);
151a0b6d 1100 for (j = HDR_AGE; j < HDR_MISC_END; j++)
1101 storeAppendPrintf(entry, "{%21.21s: %d}\n",
1102 HttpHdrMiscStr[j],
1103 ReplyHeaderStats.misc[j]);
4db43fab 1104 for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++)
884011a1 1105 storeAppendPrintf(entry, "{Cache-Control %s: %d}\n",
6fb52f6c 1106 HttpServerCCStr[i],
1107 ReplyHeaderStats.cc[i]);
30a4f2a8 1108 storeAppendPrintf(entry, close_bracket);
1109}
bfcaf585 1110
1111static void
1112httpAbort(void *data)
1113{
1114 HttpStateData *httpState = data;
dd8037ab 1115 debug(11, 2) ("httpAbort: %s\n", httpState->entry->url);
bfcaf585 1116 comm_close(httpState->fd);
1117}
9b312a19 1118
1119static char *
1120httpStatusString(int status)
1121{
1122 char *p = NULL;
1123 switch (status) {
1124 case 100:
1125 p = "Continue";
1126 break;
1127 case 101:
1128 p = "Switching Protocols";
1129 break;
1130 case 200:
1131 p = "OK";
1132 break;
1133 case 201:
1134 p = "Created";
1135 break;
1136 case 202:
1137 p = "Accepted";
1138 break;
1139 case 203:
1140 p = "Non-Authoritative Information";
1141 break;
1142 case 204:
1143 p = "No Content";
1144 break;
1145 case 205:
1146 p = "Reset Content";
1147 break;
1148 case 206:
1149 p = "Partial Content";
1150 break;
1151 case 300:
1152 p = "Multiple Choices";
1153 break;
1154 case 301:
1155 p = "Moved Permanently";
1156 break;
1157 case 302:
1158 p = "Moved Temporarily";
1159 break;
1160 case 303:
1161 p = "See Other";
1162 break;
1163 case 304:
1164 p = "Not Modified";
1165 break;
1166 case 305:
1167 p = "Use Proxy";
1168 break;
1169 case 400:
1170 p = "Bad Request";
1171 break;
1172 case 401:
1173 p = "Unauthorized";
1174 break;
1175 case 402:
1176 p = "Payment Required";
1177 break;
1178 case 403:
1179 p = "Forbidden";
1180 break;
1181 case 404:
1182 p = "Not Found";
1183 break;
1184 case 405:
1185 p = "Method Not Allowed";
1186 break;
1187 case 406:
1188 p = "Not Acceptable";
1189 break;
1190 case 407:
1191 p = "Proxy Authentication Required";
1192 break;
1193 case 408:
1194 p = "Request Time-out";
1195 break;
1196 case 409:
1197 p = "Conflict";
1198 break;
1199 case 410:
1200 p = "Gone";
1201 break;
1202 case 411:
1203 p = "Length Required";
1204 break;
1205 case 412:
1206 p = "Precondition Failed";
1207 break;
1208 case 413:
1209 p = "Request Entity Too Large";
1210 break;
1211 case 414:
1212 p = "Request-URI Too Large";
1213 break;
1214 case 415:
1215 p = "Unsupported Media Type";
1216 break;
1217 case 500:
1218 p = "Internal Server Error";
1219 break;
1220 case 501:
1221 p = "Not Implemented";
1222 break;
1223 case 502:
1224 p = "Bad Gateway";
1225 break;
1226 case 503:
1227 p = "Service Unavailable";
1228 break;
1229 case 504:
1230 p = "Gateway Time-out";
1231 break;
1232 case 505:
1233 p = "HTTP Version not supported";
1234 break;
1235 default:
1236 p = "Unknown";
1237 debug(11, 0) ("Unknown HTTP status code: %d\n", status);
1238 break;
1239 }
1240 return p;
1241}
1242
1243char *
1244httpReplyHeader(double ver,
1245 http_status status,
1246 char *ctype,
1247 int clen,
1248 time_t lmt,
1249 time_t expires)
1250{
1251 LOCAL_ARRAY(char, buf, HTTP_REPLY_BUF_SZ);
1252 LOCAL_ARRAY(char, float_buf, 64);
1253 int l = 0;
1254 int s = HTTP_REPLY_BUF_SZ;
1255 /* argh, ../lib/snprintf.c doesn't support '%f' */
56878878 1256 snprintf(float_buf, 64, "%3.1f", ver);
9b312a19 1257 assert(strlen(float_buf) == 3);
1258 l += snprintf(buf + l, s - l, "HTTP/%s %d %s\r\n",
1259 float_buf,
1260 (int) status,
1261 httpStatusString(status));
1262 l += snprintf(buf + l, s - l, "Server: Squid/%s\r\n", SQUID_VERSION);
1263 l += snprintf(buf + l, s - l, "Date: %s\r\n", mkrfc1123(squid_curtime));
1264 if (expires >= 0)
1265 l += snprintf(buf + l, s - l, "Expires: %s\r\n", mkrfc1123(expires));
1266 if (lmt)
1267 l += snprintf(buf + l, s - l, "Last-Modified: %s\r\n", mkrfc1123(lmt));
1268 if (clen > 0)
1269 l += snprintf(buf + l, s - l, "Content-Length: %d\r\n", clen);
1270 if (ctype)
1271 l += snprintf(buf + l, s - l, "Content-Type: %s\r\n", ctype);
1272 return buf;
1273}