]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
fix KILL_PARENT debug()
[thirdparty/squid.git] / src / http.cc
CommitLineData
da2b3a17 1
30a4f2a8 2/*
4e3f29eb 3 * $Id: http.cc,v 1.208 1997/10/30 03:31:22 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,
156 HDR_MISC_END
157} http_hdr_misc_t;
6fb52f6c 158
e924600d 159static char *HttpServerCCStr[] =
6fb52f6c 160{
161 "public",
162 "private",
163 "no-cache",
164 "no-store",
165 "no-transform",
166 "must-revalidate",
167 "proxy-revalidate",
168 "max-age",
169 "NONE"
170};
171
151a0b6d 172static char *HttpHdrMiscStr[] =
173{
174 "Accept",
175 "Age",
176 "Content-Length",
177 "Content-MD5",
178 "Content-Type",
179 "Date",
180 "Etag",
181 "Expires",
182 "Host",
183 "If-Modified-Since",
184 "Last-Modified",
185 "Max-Forwards",
186 "Public",
187 "Retry-After",
188 "Set-Cookie",
189 "Upgrade",
190 "Warning",
191 "NONE"
192};
193
24382924 194static struct {
30a4f2a8 195 int parsed;
151a0b6d 196 int misc[HDR_MISC_END];
6fb52f6c 197 int cc[SCC_ENUM_END];
30a4f2a8 198} ReplyHeaderStats;
090089c4 199
9e4ad609 200static CNCB httpConnectDone;
201static CWCB httpSendComplete;
9e4ad609 202static PF httpReadReply;
203static PF httpSendRequest;
204static PF httpStateFree;
205static PF httpTimeout;
f5b8bbc4 206static void httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int);
207static void httpCacheNegatively(StoreEntry *);
208static void httpMakePrivate(StoreEntry *);
209static void httpMakePublic(StoreEntry *);
210static char *httpStatusString(int status);
bfcaf585 211static STABH httpAbort;
f5b8bbc4 212static HttpStateData *httpBuildState(int, StoreEntry *, request_t *, peer *);
213static int httpSocketOpen(StoreEntry *, request_t *);
214static void httpRestart(HttpStateData *);
f8309b15 215static int httpCachableReply(HttpStateData *);
b8d8561b 216
b177367b 217static void
b716a8ad 218httpStateFree(int fd, void *data)
f5558c95 219{
b177367b 220 HttpStateData *httpState = data;
0d4d4170 221 if (httpState == NULL)
b177367b 222 return;
bfcaf585 223 storeUnregisterAbort(httpState->entry);
f88211e8 224 storeUnlockObject(httpState->entry);
0d4d4170 225 if (httpState->reply_hdr) {
226 put_free_8k_page(httpState->reply_hdr);
227 httpState->reply_hdr = NULL;
228 }
30a4f2a8 229 requestUnlink(httpState->request);
20cc1450 230 requestUnlink(httpState->orig_request);
7dd44885 231 httpState->request = NULL;
232 httpState->orig_request = NULL;
233 cbdataFree(httpState);
f5558c95 234}
235
b8d8561b 236int
75e88d56 237httpCachable(method_t method)
090089c4 238{
090089c4 239 /* GET and HEAD are cachable. Others are not. */
6eb42cae 240 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 241 return 0;
090089c4 242 /* else cachable */
243 return 1;
244}
245
b8d8561b 246static void
5c5783a2 247httpTimeout(int fd, void *data)
090089c4 248{
b177367b 249 HttpStateData *httpState = data;
593c9a75 250 StoreEntry *entry = httpState->entry;
9b312a19 251 ErrorState *err;
a3d5953d 252 debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, entry->url);
8796b9e9 253 assert(entry->store_status == STORE_PENDING);
73a3014d 254 if (entry->mem_obj->inmem_hi == 0) {
fe40a877 255 err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT);
9b312a19 256 err->request = requestLink(httpState->request);
257 errorAppendEntry(entry, err);
258 }
259 storeAbort(entry, 0);
0d4d4170 260 comm_close(fd);
090089c4 261}
262
30a4f2a8 263/* This object can be cached for a long time */
b8d8561b 264static void
265httpMakePublic(StoreEntry * entry)
30a4f2a8 266{
1c481e00 267 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 268 storeSetPublicKey(entry);
269}
270
271/* This object should never be cached at all */
b8d8561b 272static void
273httpMakePrivate(StoreEntry * entry)
30a4f2a8 274{
30a4f2a8 275 storeExpireNow(entry);
d3f89c29 276 BIT_CLR(entry->flag, ENTRY_CACHABLE);
30a4f2a8 277 storeReleaseRequest(entry); /* delete object when not used */
278}
279
280/* This object may be negatively cached */
b8d8561b 281static void
282httpCacheNegatively(StoreEntry * entry)
30a4f2a8 283{
79b5cc5f 284 storeNegativeCache(entry);
1c481e00 285 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 286 storeSetPublicKey(entry);
30a4f2a8 287}
288
289
290/* Build a reply structure from HTTP reply headers */
b8d8561b 291void
48f44632 292httpParseReplyHeaders(const char *buf, struct _http_reply *reply)
30a4f2a8 293{
33b589ff 294 char *headers = get_free_4k_page();
ca85027a 295 char *line;
33b589ff 296 char *end;
30a4f2a8 297 char *s = NULL;
33b589ff 298 char *t;
ca98227c 299 time_t delta;
300 size_t l;
30a4f2a8 301
5b29969a 302 assert(reply != NULL);
f1494beb 303 reply->code = 600;
30a4f2a8 304 ReplyHeaderStats.parsed++;
33b589ff 305 xstrncpy(headers, buf, 4096);
306 end = mime_headers_end(headers);
ca85027a 307 if (end == NULL) {
308 t = headers;
e2ad7f85 309 if (!strncasecmp(t, "HTTP/", 5)) {
310 reply->version = atof(t + 5);
311 if ((t = strchr(t, ' ')))
312 reply->code = atoi(++t);
ca85027a 313 }
e2ad7f85 314 put_free_4k_page(headers);
315 return;
ca85027a 316 }
317 reply->hdr_sz = end - headers;
318 line = get_free_4k_page();
33b589ff 319 for (s = headers; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
320 l = strcspn(s, crlf) + 1;
321 if (l > 4096)
322 l = 4096;
323 xstrncpy(line, s, l);
324 t = line;
a3d5953d 325 debug(11, 3) ("httpParseReplyHeaders: %s\n", t);
33b589ff 326 if (!strncasecmp(t, "HTTP/", 5)) {
e2ad7f85 327 reply->version = atof(t + 5);
33b589ff 328 if ((t = strchr(t, ' ')))
329 reply->code = atoi(++t);
30a4f2a8 330 } else if (!strncasecmp(t, "Content-type:", 13)) {
33b589ff 331 for (t += 13; isspace(*t); t++);
e91b56e5 332 if ((l = strcspn(t, ";\t ")) > 0)
333 *(t + l) = '\0';
33b589ff 334 xstrncpy(reply->content_type, t, HTTP_REPLY_FIELD_SZ);
151a0b6d 335 ReplyHeaderStats.misc[HDR_CONTENT_TYPE]++;
30a4f2a8 336 } else if (!strncasecmp(t, "Content-length:", 15)) {
33b589ff 337 for (t += 15; isspace(*t); t++);
33b589ff 338 reply->content_length = atoi(t);
151a0b6d 339 ReplyHeaderStats.misc[HDR_CONTENT_LENGTH]++;
30a4f2a8 340 } else if (!strncasecmp(t, "Date:", 5)) {
33b589ff 341 for (t += 5; isspace(*t); t++);
342 reply->date = parse_rfc1123(t);
151a0b6d 343 ReplyHeaderStats.misc[HDR_DATE]++;
30a4f2a8 344 } else if (!strncasecmp(t, "Expires:", 8)) {
33b589ff 345 for (t += 8; isspace(*t); t++);
346 reply->expires = parse_rfc1123(t);
347 /*
348 * The HTTP/1.0 specs says that robust implementations
349 * should consider bad or malformed Expires header as
350 * equivalent to "expires immediately."
351 */
352 if (reply->expires == -1)
353 reply->expires = squid_curtime;
151a0b6d 354 ReplyHeaderStats.misc[HDR_EXPIRES]++;
30a4f2a8 355 } else if (!strncasecmp(t, "Last-Modified:", 14)) {
33b589ff 356 for (t += 14; isspace(*t); t++);
357 reply->last_modified = parse_rfc1123(t);
151a0b6d 358 ReplyHeaderStats.misc[HDR_LAST_MODIFIED]++;
359 } else if (!strncasecmp(t, "Accept:", 7)) {
360 ReplyHeaderStats.misc[HDR_ACCEPT]++;
361 } else if (!strncasecmp(t, "Age:", 4)) {
362 ReplyHeaderStats.misc[HDR_AGE]++;
363 } else if (!strncasecmp(t, "Content-MD5:", 12)) {
364 ReplyHeaderStats.misc[HDR_CONTENT_MD5]++;
365 } else if (!strncasecmp(t, "ETag:", 5)) {
366 ReplyHeaderStats.misc[HDR_ETAG]++;
367 } else if (!strncasecmp(t, "Max-Forwards:", 13)) {
368 ReplyHeaderStats.misc[HDR_MAX_FORWARDS]++;
369 } else if (!strncasecmp(t, "Public:", 7)) {
370 ReplyHeaderStats.misc[HDR_PUBLIC]++;
371 } else if (!strncasecmp(t, "Retry-After:", 12)) {
372 ReplyHeaderStats.misc[HDR_RETRY_AFTER]++;
373 } else if (!strncasecmp(t, "Upgrade:", 8)) {
374 ReplyHeaderStats.misc[HDR_UPGRADE]++;
375 } else if (!strncasecmp(t, "Warning:", 8)) {
376 ReplyHeaderStats.misc[HDR_WARNING]++;
caebbe00 377 } else if (!strncasecmp(t, "Cache-Control:", 14)) {
33b589ff 378 for (t += 14; isspace(*t); t++);
4db43fab 379 if (!strncasecmp(t, "public", 6)) {
380 EBIT_SET(reply->cache_control, SCC_PUBLIC);
381 ReplyHeaderStats.cc[SCC_PUBLIC]++;
382 } else if (!strncasecmp(t, "private", 7)) {
383 EBIT_SET(reply->cache_control, SCC_PRIVATE);
384 ReplyHeaderStats.cc[SCC_PRIVATE]++;
385 } else if (!strncasecmp(t, "no-cache", 8)) {
386 EBIT_SET(reply->cache_control, SCC_NOCACHE);
387 ReplyHeaderStats.cc[SCC_NOCACHE]++;
c1764328 388 } else if (!strncasecmp(t, "no-store", 8)) {
389 EBIT_SET(reply->cache_control, SCC_NOSTORE);
390 ReplyHeaderStats.cc[SCC_NOSTORE]++;
391 } else if (!strncasecmp(t, "no-transform", 12)) {
392 EBIT_SET(reply->cache_control, SCC_NOTRANSFORM);
393 ReplyHeaderStats.cc[SCC_NOTRANSFORM]++;
394 } else if (!strncasecmp(t, "must-revalidate", 15)) {
395 EBIT_SET(reply->cache_control, SCC_MUSTREVALIDATE);
396 ReplyHeaderStats.cc[SCC_MUSTREVALIDATE]++;
397 } else if (!strncasecmp(t, "proxy-revalidate", 16)) {
398 EBIT_SET(reply->cache_control, SCC_PROXYREVALIDATE);
399 ReplyHeaderStats.cc[SCC_PROXYREVALIDATE]++;
4db43fab 400 } else if (!strncasecmp(t, "max-age", 7)) {
401 if ((t = strchr(t, '='))) {
ca98227c 402 delta = (time_t) atoi(++t);
403 reply->expires = squid_curtime + delta;
4db43fab 404 EBIT_SET(reply->cache_control, SCC_MAXAGE);
405 ReplyHeaderStats.cc[SCC_MAXAGE]++;
5df61230 406 }
caebbe00 407 }
df3ac7c0 408 } else if (!strncasecmp(t, "Set-Cookie:", 11)) {
409 EBIT_SET(reply->misc_headers, HDR_SET_COOKIE);
58202be7 410 ReplyHeaderStats.misc[HDR_SET_COOKIE]++;
30a4f2a8 411 }
30a4f2a8 412 }
33b589ff 413 put_free_4k_page(headers);
414 put_free_4k_page(line);
30a4f2a8 415}
416
f8309b15 417static int
418httpCachableReply(HttpStateData * httpState)
c54e9052 419{
f8309b15 420 struct _http_reply *reply = httpState->entry->mem_obj->reply;
421 if (EBIT_TEST(reply->cache_control, SCC_PRIVATE))
422 return 0;
423 if (EBIT_TEST(reply->cache_control, SCC_NOCACHE))
424 return 0;
425 /*
426 * Dealing with cookies is quite a bit more complicated
427 * than this. Ideally we should strip the cookie
428 * header from the reply but still cache the reply body.
429 * More confusion at draft-ietf-http-state-mgmt-05.txt.
430 */
431 if (EBIT_TEST(reply->misc_headers, HDR_SET_COOKIE))
432 return 0;
c54e9052 433 switch (reply->code) {
434 /* Responses that are cacheable */
435 case 200: /* OK */
436 case 203: /* Non-Authoritative Information */
437 case 300: /* Multiple Choices */
438 case 301: /* Moved Permanently */
439 case 410: /* Gone */
440 /* don't cache objects from neighbors w/o LMT, Date, or Expires */
c54e9052 441 if (reply->date > -1)
442 return 1;
f8309b15 443 else if (reply->last_modified > -1)
c54e9052 444 return 1;
f8309b15 445 else if (!httpState->neighbor)
c54e9052 446 return 1;
f8309b15 447 else if (reply->expires > -1)
c54e9052 448 return 1;
c54e9052 449 else
450 return 0;
451 break;
452 /* Responses that only are cacheable if the server says so */
453 case 302: /* Moved temporarily */
454 if (reply->expires > -1)
455 return 1;
456 else
457 return 0;
458 break;
459 /* Errors can be negatively cached */
460 case 204: /* No Content */
461 case 305: /* Use Proxy (proxy redirect) */
462 case 400: /* Bad Request */
463 case 403: /* Forbidden */
464 case 404: /* Not Found */
465 case 405: /* Method Now Allowed */
466 case 414: /* Request-URI Too Long */
467 case 500: /* Internal Server Error */
468 case 501: /* Not Implemented */
469 case 502: /* Bad Gateway */
470 case 503: /* Service Unavailable */
471 case 504: /* Gateway Timeout */
472 return -1;
473 break;
474 /* Some responses can never be cached */
88738790 475 case 206: /* Partial Content -- Not yet supported */
c54e9052 476 case 303: /* See Other */
477 case 304: /* Not Modified */
478 case 401: /* Unauthorized */
479 case 407: /* Proxy Authentication Required */
480 case 600: /* Squid header parsing error */
481 default: /* Unknown status code */
482 return 0;
483 break;
484 }
485 assert(0);
486}
090089c4 487
b8d8561b 488void
0ee4272b 489httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 490{
491 char *t = NULL;
30a4f2a8 492 StoreEntry *entry = httpState->entry;
d3fb4dea 493 int room;
494 int hdr_len;
33b589ff 495 struct _http_reply *reply = entry->mem_obj->reply;
a3d5953d 496 debug(11, 3) ("httpProcessReplyHeader: key '%s'\n", entry->key);
e924600d 497 if (httpState->reply_hdr == NULL)
30a4f2a8 498 httpState->reply_hdr = get_free_8k_page();
30a4f2a8 499 if (httpState->reply_hdr_state == 0) {
500 hdr_len = strlen(httpState->reply_hdr);
ed85b771 501 room = 8191 - hdr_len;
30a4f2a8 502 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 503 hdr_len += room < size ? room : size;
30a4f2a8 504 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
a3d5953d 505 debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", entry->key);
30a4f2a8 506 httpState->reply_hdr_state += 2;
33b589ff 507 reply->code = 555;
ed85b771 508 return;
d3fb4dea 509 }
d1a43e28 510 t = httpState->reply_hdr + hdr_len;
511 /* headers can be incomplete only if object still arriving */
f86a6a46 512 if (!httpState->eof)
d1a43e28 513 if ((t = mime_headers_end(httpState->reply_hdr)) == NULL)
514 return; /* headers not complete */
2285407f 515 *t = '\0';
30a4f2a8 516 httpState->reply_hdr_state++;
f5558c95 517 }
30a4f2a8 518 if (httpState->reply_hdr_state == 1) {
519 httpState->reply_hdr_state++;
a3d5953d 520 debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 521 httpState->reply_hdr);
522 /* Parse headers into reply structure */
48f44632 523 httpParseReplyHeaders(httpState->reply_hdr, reply);
ca98227c 524 storeTimestampsSet(entry);
30a4f2a8 525 /* Check if object is cacheable or not based on reply code */
a3d5953d 526 debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->code);
f8309b15 527 switch (httpCachableReply(httpState)) {
c54e9052 528 case 1:
529 httpMakePublic(entry);
30a4f2a8 530 break;
c54e9052 531 case 0:
532 httpMakePrivate(entry);
f5558c95 533 break;
c54e9052 534 case -1:
851eeef7 535 httpCacheNegatively(entry);
30a4f2a8 536 break;
c54e9052 537 default:
538 assert(0);
4e38e700 539 break;
f5558c95 540 }
c54e9052 541 if (EBIT_TEST(reply->cache_control, SCC_PROXYREVALIDATE))
542 BIT_SET(entry->flag, ENTRY_REVALIDATE);
f5558c95 543 }
544}
545
603a02fd 546static int
547httpPconnTransferDone(HttpStateData * httpState)
548{
549 /* return 1 if we got the last of the data on a persistent connection */
550 MemObject *mem = httpState->entry->mem_obj;
551 struct _http_reply *reply = mem->reply;
51fdcbd5 552 debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd);
603a02fd 553 if (!BIT_TEST(httpState->flags, HTTP_KEEPALIVE))
554 return 0;
51fdcbd5 555 debug(11, 5) ("httpPconnTransferDone: content_length=%d\n",
603a02fd 556 reply->content_length);
557 /*
558 * !200 replies maybe don't have content-length, so
559 * if we saw the end of the headers then try being persistent.
560 */
561 if (reply->code != 200)
562 if (httpState->reply_hdr_state > 1)
563 return 1;
564 /*
565 * If there is no content-length, then we probably can't be persistent
566 */
567 if (reply->content_length == 0)
568 return 0;
569 /*
570 * If there is a content_length, see if we've got all of it. If so,
571 * then we can recycle this connection.
572 */
51fdcbd5 573 debug(11, 5) ("httpPconnTransferDone: hdr_sz=%d\n", reply->hdr_sz);
8350fe9b 574 debug(11, 5) ("httpPconnTransferDone: inmem_hi=%d\n",
575 mem->inmem_hi);
576 if (mem->inmem_hi < reply->content_length + reply->hdr_sz)
603a02fd 577 return 0;
578 return 1;
579}
090089c4 580
581/* This will be called when data is ready to be read from fd. Read until
582 * error or connection closed. */
f5558c95 583/* XXX this function is too long! */
b8d8561b 584static void
b177367b 585httpReadReply(int fd, void *data)
090089c4 586{
b177367b 587 HttpStateData *httpState = data;
95d659f0 588 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
bfcaf585 589 StoreEntry *entry = httpState->entry;
603a02fd 590 const request_t *request = httpState->request;
090089c4 591 int len;
30a4f2a8 592 int bin;
090089c4 593 int clen;
594 int off;
9b312a19 595 ErrorState *err;
d89d1fb6 596 if (protoAbortFetch(entry)) {
9b312a19 597 storeAbort(entry, 0);
a3d5953d 598 comm_close(fd);
599 return;
234967c9 600 }
601 /* check if we want to defer reading */
8350fe9b 602 clen = entry->mem_obj->inmem_hi;
603 off = storeLowestMemReaderOffset(entry);
1513873c 604 errno = 0;
30a4f2a8 605 len = read(fd, buf, SQUID_TCP_SO_RCVBUF);
4f92c80c 606 fd_bytes(fd, len, FD_READ);
a3d5953d 607 debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 608 if (len > 0) {
4f92c80c 609 commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
4a63c85f 610 IOStats.Http.reads++;
30a4f2a8 611 for (clen = len - 1, bin = 0; clen; bin++)
612 clen >>= 1;
613 IOStats.Http.read_hist[bin]++;
614 }
ba718c8f 615 if (len < 0) {
0a0bf5db 616 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
9b312a19 617 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
090089c4 618 } else {
73a3014d 619 if (clen == 0) {
fe40a877 620 err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 621 err->xerrno = errno;
9b312a19 622 err->request = requestLink(httpState->request);
623 errorAppendEntry(entry, err);
624 }
625 storeAbort(entry, 0);
0d4d4170 626 comm_close(fd);
090089c4 627 }
a3d5953d 628 debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n",
0a0bf5db 629 fd, xstrerror());
8350fe9b 630 } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
b716a8ad 631 if (fd_table[fd].uses > 1) {
632 httpRestart(httpState);
633 } else {
634 httpState->eof = 1;
fe40a877 635 err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 636 err->xerrno = errno;
b716a8ad 637 err->request = requestLink(httpState->request);
638 errorAppendEntry(entry, err);
639 storeAbort(entry, 0);
640 comm_close(fd);
641 }
090089c4 642 } else if (len == 0) {
643 /* Connection closed; retrieval done. */
f86a6a46 644 httpState->eof = 1;
d1a43e28 645 if (httpState->reply_hdr_state < 2)
646 httpProcessReplyHeader(httpState, buf, len);
d1a43e28 647 storeComplete(entry); /* deallocates mem_obj->request */
0d4d4170 648 comm_close(fd);
090089c4 649 } else {
d1a43e28 650 if (httpState->reply_hdr_state < 2)
30a4f2a8 651 httpProcessReplyHeader(httpState, buf, len);
620da955 652 storeAppend(entry, buf, len);
603a02fd 653 if (httpPconnTransferDone(httpState)) {
5b29969a 654 /* yes we have to clear all these! */
8796b9e9 655 commSetDefer(fd, NULL, NULL);
5b29969a 656 commSetTimeout(fd, -1, NULL, NULL);
657 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
603a02fd 658 comm_remove_close_handler(fd, httpStateFree, httpState);
659 storeComplete(entry); /* deallocates mem_obj->request */
8796b9e9 660 pconnPush(fd, request->host, request->port);
603a02fd 661 httpState->fd = -1;
662 httpStateFree(-1, httpState);
663 } else {
664 commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0);
665 }
090089c4 666 }
667}
668
669/* This will be called when request write is complete. Schedule read of
670 * reply. */
b8d8561b 671static void
672httpSendComplete(int fd, char *buf, int size, int errflag, void *data)
090089c4 673{
30a4f2a8 674 HttpStateData *httpState = data;
9b312a19 675 StoreEntry *entry = httpState->entry;
676 ErrorState *err;
a3d5953d 677 debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 678 fd, size, errflag);
ea3a2a69 679 if (errflag == COMM_ERR_CLOSING)
680 return;
090089c4 681 if (errflag) {
fe40a877 682 err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 683 err->xerrno = errno;
9b312a19 684 err->request = requestLink(httpState->request);
685 errorAppendEntry(entry, err);
686 storeAbort(entry, 0);
0d4d4170 687 comm_close(fd);
090089c4 688 return;
689 } else {
690 /* Schedule read reply. */
b177367b 691 commSetSelect(fd,
019dd986 692 COMM_SELECT_READ,
b177367b 693 httpReadReply,
cd1fb0eb 694 httpState, 0);
70a9dab4 695 commSetDefer(fd, protoCheckDeferRead, entry);
090089c4 696 }
697}
698
6bf8443a 699static void
88738790 700httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int check)
6bf8443a 701{
702 size_t n = *sz + strlen(line) + 2;
703 if (n >= max)
704 return;
88738790 705 if (check) {
17a0a4ee 706 if (Config.onoff.anonymizer == ANONYMIZER_PARANOID) {
88738790 707 if (!httpAnonAllowed(line))
708 return;
17a0a4ee 709 } else if (Config.onoff.anonymizer == ANONYMIZER_STANDARD) {
88738790 710 if (httpAnonDenied(line))
711 return;
712 }
4a83b852 713 }
4a83b852 714 /* allowed header, explicitly known to be not dangerous */
a3d5953d 715 debug(11, 5) ("httpAppendRequestHeader: %s\n", line);
929545fe 716 strcpy(hdr + (*sz), line);
6bf8443a 717 strcat(hdr + (*sz), crlf);
718 *sz = n;
719}
720
721size_t
722httpBuildRequestHeader(request_t * request,
723 request_t * orig_request,
724 StoreEntry * entry,
6bf8443a 725 size_t * in_len,
726 char *hdr_out,
727 size_t out_sz,
603a02fd 728 int cfd,
729 int flags)
6bf8443a 730{
429fdbec 731 LOCAL_ARRAY(char, ybuf, MAX_URL + 32);
6bf8443a 732 char *xbuf = get_free_4k_page();
6bf8443a 733 char *viabuf = get_free_4k_page();
734 char *fwdbuf = get_free_4k_page();
735 char *t = NULL;
736 char *s = NULL;
737 char *end = NULL;
738 size_t len = 0;
739 size_t hdr_len = 0;
6bf8443a 740 size_t l;
741 int hdr_flags = 0;
151a0b6d 742 int cc_flags = 0;
b3b64e58 743 int n;
6bf8443a 744 const char *url = NULL;
2357f74a 745 char *hdr_in = orig_request->headers;
6bf8443a 746
2357f74a 747 assert(hdr_in != NULL);
a3d5953d 748 debug(11, 3) ("httpBuildRequestHeader: INPUT:\n%s\n", hdr_in);
6bf8443a 749 xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096);
750 xstrncpy(viabuf, "Via: ", 4096);
042461c3 751 snprintf(ybuf, MAX_URL + 32, "%s %s HTTP/1.0",
6bf8443a 752 RequestMethodStr[request->method],
753 *request->urlpath ? request->urlpath : "/");
88738790 754 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
6bf8443a 755 /* Add IMS header */
756 if (entry && entry->lastmod && request->method == METHOD_GET) {
042461c3 757 snprintf(ybuf, MAX_URL + 32, "If-Modified-Since: %s", mkrfc1123(entry->lastmod));
88738790 758 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
6bf8443a 759 EBIT_SET(hdr_flags, HDR_IMS);
760 }
00c59270 761 end = mime_headers_end(hdr_in);
6bf8443a 762 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
763 hdr_len = t - hdr_in;
6bf8443a 764 l = strcspn(t, crlf) + 1;
765 if (l > 4096)
766 l = 4096;
767 xstrncpy(xbuf, t, l);
a3d5953d 768 debug(11, 5) ("httpBuildRequestHeader: %s\n", xbuf);
6bf8443a 769 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
770 continue;
88738790 771 if (strncasecmp(xbuf, "Proxy-authorization:", 20) == 0)
afe95a7e 772 /* If we're not going to do proxy auth, then it must be passed on */
c1517455 773 if (BIT_TEST(request->flags, REQ_USED_PROXY_AUTH))
88738790 774 continue;
6bf8443a 775 if (strncasecmp(xbuf, "Connection:", 11) == 0)
776 continue;
66f7337b 777 if (strncasecmp(xbuf, "Host:", 5) == 0) {
6bf8443a 778 EBIT_SET(hdr_flags, HDR_HOST);
66f7337b 779 } else if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) {
6bf8443a 780 for (s = xbuf + 14; *s && isspace(*s); s++);
781 if (strncasecmp(s, "Max-age=", 8) == 0)
151a0b6d 782 EBIT_SET(cc_flags, CCC_MAXAGE);
66f7337b 783 } else if (strncasecmp(xbuf, "Via:", 4) == 0) {
6bf8443a 784 for (s = xbuf + 4; *s && isspace(*s); s++);
785 if (strlen(viabuf) + strlen(s) < 4000)
786 strcat(viabuf, s);
787 strcat(viabuf, ", ");
788 continue;
66f7337b 789 } else if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) {
6bf8443a 790 for (s = xbuf + 16; *s && isspace(*s); s++);
791 if (strlen(fwdbuf) + strlen(s) < 4000)
792 strcat(fwdbuf, s);
793 strcat(fwdbuf, ", ");
794 continue;
66f7337b 795 } else if (strncasecmp(xbuf, "If-Modified-Since:", 18) == 0) {
6bf8443a 796 if (EBIT_TEST(hdr_flags, HDR_IMS))
797 continue;
b3b64e58 798 } else if (strncasecmp(xbuf, "Max-Forwards:", 13) == 0) {
799 if (orig_request->method == METHOD_TRACE) {
800 for (s = xbuf + 13; *s && isspace(*s); s++);
801 n = atoi(s);
56878878 802 snprintf(xbuf, 4096, "Max-Forwards: %d", n - 1);
b3b64e58 803 }
66f7337b 804 }
88738790 805 httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512, 1);
806 }
807 hdr_len = t - hdr_in;
808 if (Config.fake_ua && strstr(hdr_out, "User-Agent") == NULL) {
56878878 809 snprintf(ybuf, MAX_URL + 32, "User-Agent: %s", Config.fake_ua);
88738790 810 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 0);
6bf8443a 811 }
56878878 812 /* Append Via: */
813 /* snprintf would fail here too */
814 snprintf(ybuf, MAX_URL + 32, "%3.1f %s", orig_request->http_ver, ThisCache);
6bf8443a 815 strcat(viabuf, ybuf);
88738790 816 httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz, 1);
6bf8443a 817 /* Append to X-Forwarded-For: */
a08307eb 818 strcat(fwdbuf, cfd < 0 ? "unknown" : fd_table[cfd].ipaddr);
88738790 819 httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz, 1);
6bf8443a 820 if (!EBIT_TEST(hdr_flags, HDR_HOST)) {
56878878 821 snprintf(ybuf, MAX_URL + 32, "Host: %s", orig_request->host);
88738790 822 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
6bf8443a 823 }
151a0b6d 824 if (!EBIT_TEST(cc_flags, CCC_MAXAGE)) {
6bf8443a 825 url = entry ? entry->url : urlCanonical(orig_request, NULL);
56878878 826 snprintf(ybuf, MAX_URL + 32, "Cache-control: Max-age=%d", (int) getMaxAge(url));
88738790 827 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
365e5b34 828 if (request->urlpath) {
829 assert(strstr(url, request->urlpath));
830 }
6bf8443a 831 }
603a02fd 832 /* maybe append Connection: Keep-Alive */
833 if (BIT_TEST(flags, HTTP_KEEPALIVE)) {
834 if (BIT_TEST(flags, HTTP_PROXYING)) {
56878878 835 snprintf(ybuf, MAX_URL + 32, "Proxy-Connection: Keep-Alive");
603a02fd 836 } else {
56878878 837 snprintf(ybuf, MAX_URL + 32, "Connection: Keep-Alive");
603a02fd 838 }
839 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1);
840 }
88738790 841 httpAppendRequestHeader(hdr_out, null_string, &len, out_sz, 1);
6bf8443a 842 put_free_4k_page(xbuf);
6bf8443a 843 put_free_4k_page(viabuf);
844 put_free_4k_page(fwdbuf);
845 if (in_len)
846 *in_len = hdr_len;
9d9d144b 847 if ((l = strlen(hdr_out)) != len) {
848 debug_trap("httpBuildRequestHeader: size mismatch");
849 len = l;
850 }
a3d5953d 851 debug(11, 3) ("httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out);
6bf8443a 852 return len;
853}
854
090089c4 855/* This will be called when connect completes. Write request. */
b8d8561b 856static void
b177367b 857httpSendRequest(int fd, void *data)
090089c4 858{
b177367b 859 HttpStateData *httpState = data;
090089c4 860 char *buf = NULL;
090089c4 861 int len = 0;
862 int buflen;
30a4f2a8 863 request_t *req = httpState->request;
9864ee44 864 int buftype = 0;
620da955 865 StoreEntry *entry = httpState->entry;
2a26c096 866 int cfd;
090089c4 867
a3d5953d 868 debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
6bf8443a 869 buflen = strlen(req->urlpath);
62dec5a7 870 if (req->headers)
871 buflen += req->headers_sz + 1;
090089c4 872 buflen += 512; /* lots of extra */
873
78657218 874 if ((req->method == METHOD_POST || req->method == METHOD_PUT)) {
875 debug_trap("httpSendRequest: should not be handling POST/PUT request");
876 return;
090089c4 877 }
30a4f2a8 878 if (buflen < DISK_PAGE_SIZE) {
9864ee44 879 buf = get_free_8k_page();
9864ee44 880 buftype = BUF_TYPE_8K;
6bf8443a 881 buflen = DISK_PAGE_SIZE;
30a4f2a8 882 } else {
9864ee44 883 buf = xcalloc(buflen, 1);
884 buftype = BUF_TYPE_MALLOC;
090089c4 885 }
2a26c096 886 if (!opt_forwarded_for)
6bf8443a 887 cfd = -1;
2a26c096 888 else if (entry->mem_obj == NULL)
6bf8443a 889 cfd = -1;
2a26c096 890 else
382d851a 891 cfd = entry->mem_obj->fd;
603a02fd 892 if (httpState->neighbor != NULL)
893 BIT_SET(httpState->flags, HTTP_PROXYING);
894 if (req->method == METHOD_GET)
895 BIT_SET(httpState->flags, HTTP_KEEPALIVE);
6bf8443a 896 len = httpBuildRequestHeader(req,
897 httpState->orig_request ? httpState->orig_request : req,
898 entry,
6bf8443a 899 NULL,
900 buf,
901 buflen,
603a02fd 902 cfd,
903 httpState->flags);
a3d5953d 904 debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, buf);
30a4f2a8 905 comm_write(fd,
14e59844 906 buf,
907 len,
14e59844 908 httpSendComplete,
9864ee44 909 httpState,
910 buftype == BUF_TYPE_8K ? put_free_8k_page : xfree);
52ecbd09 911#ifdef BREAKS_PCONN_RESTART
20cc1450 912 requestUnlink(httpState->orig_request);
913 httpState->orig_request = NULL;
52ecbd09 914#endif
090089c4 915}
916
603a02fd 917static int
b716a8ad 918httpSocketOpen(StoreEntry * entry, request_t * request)
090089c4 919{
9e4ad609 920 int fd;
9b312a19 921 ErrorState *err;
9e4ad609 922 fd = comm_open(SOCK_STREAM,
16b204c4 923 0,
924 Config.Addrs.tcp_outgoing,
925 0,
926 COMM_NONBLOCKING,
75e88d56 927 entry->url);
603a02fd 928 if (fd < 0) {
929 debug(11, 4) ("httpSocketOpen: Failed because we're out of sockets.\n");
fe40a877 930 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
c45ed9ad 931 err->xerrno = errno;
603a02fd 932 if (request)
933 err->request = requestLink(request);
9b312a19 934 errorAppendEntry(entry, err);
935 storeAbort(entry, 0);
090089c4 936 }
603a02fd 937 return fd;
938}
939
940static HttpStateData *
941httpBuildState(int fd, StoreEntry * entry, request_t * orig_request, peer * e)
942{
943 HttpStateData *httpState = xcalloc(1, sizeof(HttpStateData));
944 request_t *request;
770f051d 945 storeLockObject(entry);
7dd44885 946 cbdataAdd(httpState);
0a0bf5db 947 httpState->entry = entry;
9e4ad609 948 httpState->fd = fd;
603a02fd 949 if (e) {
950 request = get_free_request_t();
951 request->method = orig_request->method;
952 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
953 request->port = e->http_port;
954 xstrncpy(request->urlpath, entry->url, MAX_URL);
955 httpState->request = requestLink(request);
956 httpState->neighbor = e;
957 httpState->orig_request = requestLink(orig_request);
958 BIT_SET(request->flags, REQ_PROXYING);
959 } else {
960 httpState->request = requestLink(orig_request);
961 }
0d4d4170 962 /* register the handler to free HTTP state data when the FD closes */
603a02fd 963 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
e102ebda 964 storeRegisterAbort(entry, httpAbort, httpState);
603a02fd 965 return httpState;
966}
967
968void
969httpStart(request_t * request, StoreEntry * entry, peer * e)
970{
971 HttpStateData *httpState;
972 int fd;
973 debug(11, 3) ("httpStart: \"%s %s\"\n",
974 RequestMethodStr[request->method], entry->url);
975 if (e) {
976 if (e->options & NEIGHBOR_PROXY_ONLY)
603a02fd 977 storeReleaseRequest(entry);
603a02fd 978 if ((fd = pconnPop(e->host, e->http_port)) >= 0) {
51fdcbd5 979 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
603a02fd 980 httpState = httpBuildState(fd, entry, request, e);
b716a8ad 981 commSetTimeout(httpState->fd,
982 Config.Timeout.connect,
983 httpTimeout,
984 httpState);
603a02fd 985 httpConnectDone(fd, COMM_OK, httpState);
986 return;
987 }
988 } else {
989 if ((fd = pconnPop(request->host, request->port)) >= 0) {
51fdcbd5 990 debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd);
603a02fd 991 httpState = httpBuildState(fd, entry, request, e);
b716a8ad 992 commSetTimeout(httpState->fd,
993 Config.Timeout.connect,
994 httpTimeout,
995 httpState);
603a02fd 996 httpConnectDone(fd, COMM_OK, httpState);
997 return;
998 }
999 }
1000 if ((fd = httpSocketOpen(entry, NULL)) < 0)
1001 return;
1002 httpState = httpBuildState(fd, entry, request, e);
603a02fd 1003 commSetTimeout(httpState->fd,
1004 Config.Timeout.connect,
1005 httpTimeout,
cd1fb0eb 1006 httpState);
edeb28fd 1007 commConnectStart(httpState->fd,
603a02fd 1008 httpState->request->host,
1009 httpState->request->port,
e924600d 1010 httpConnectDone,
1011 httpState);
e5f6c5c2 1012}
1013
b716a8ad 1014static void
1015httpRestart(HttpStateData * httpState)
1016{
1017 /* restart a botched request from a persistent connection */
dd8037ab 1018 debug(11, 2) ("Retrying HTTP request for %s\n", httpState->entry->url);
b716a8ad 1019 if (httpState->fd >= 0) {
1020 comm_remove_close_handler(httpState->fd, httpStateFree, httpState);
1021 comm_close(httpState->fd);
1022 httpState->fd = -1;
1023 }
1024 httpState->fd = httpSocketOpen(httpState->entry, httpState->request);
1025 if (httpState->fd < 0)
1026 return;
1027 comm_add_close_handler(httpState->fd, httpStateFree, httpState);
1028 commSetTimeout(httpState->fd,
1029 Config.Timeout.connect,
1030 httpTimeout,
1031 httpState);
1032 commConnectStart(httpState->fd,
1033 httpState->request->host,
1034 httpState->request->port,
1035 httpConnectDone,
1036 httpState);
1037}
1038
e5f6c5c2 1039static void
1040httpConnectDone(int fd, int status, void *data)
1041{
1042 HttpStateData *httpState = data;
1043 request_t *request = httpState->request;
1044 StoreEntry *entry = httpState->entry;
9b312a19 1045 ErrorState *err;
edeb28fd 1046 if (status == COMM_ERR_DNS) {
a3d5953d 1047 debug(11, 4) ("httpConnectDone: Unknown host: %s\n", request->host);
fe40a877 1048 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
9b312a19 1049 err->dnsserver_msg = xstrdup(dns_error_message);
1050 err->request = requestLink(request);
1051 errorAppendEntry(entry, err);
1052 storeAbort(entry, 0);
edeb28fd 1053 comm_close(fd);
1054 } else if (status != COMM_OK) {
fe40a877 1055 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
c45ed9ad 1056 err->xerrno = errno;
9b312a19 1057 err->host = xstrdup(request->host);
1058 err->port = request->port;
1059 err->request = requestLink(request);
1060 errorAppendEntry(entry, err);
1061 storeAbort(entry, 0);
e924600d 1062 if (httpState->neighbor)
1063 peerCheckConnectStart(httpState->neighbor);
e5f6c5c2 1064 comm_close(fd);
1065 } else {
e5f6c5c2 1066 fd_note(fd, entry->url);
b716a8ad 1067 fd_table[fd].uses++;
bfcaf585 1068 commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0);
090089c4 1069 }
090089c4 1070}
1071
b8d8561b 1072void
1073httpReplyHeaderStats(StoreEntry * entry)
30a4f2a8 1074{
6fb52f6c 1075 http_server_cc_t i;
151a0b6d 1076 http_hdr_misc_t j;
30a4f2a8 1077 storeAppendPrintf(entry, open_bracket);
6fb52f6c 1078 storeAppendPrintf(entry, "{HTTP Reply Headers:}\n");
1079 storeAppendPrintf(entry, "{ Headers parsed: %d}\n",
30a4f2a8 1080 ReplyHeaderStats.parsed);
151a0b6d 1081 for (j = HDR_AGE; j < HDR_MISC_END; j++)
1082 storeAppendPrintf(entry, "{%21.21s: %d}\n",
1083 HttpHdrMiscStr[j],
1084 ReplyHeaderStats.misc[j]);
4db43fab 1085 for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++)
884011a1 1086 storeAppendPrintf(entry, "{Cache-Control %s: %d}\n",
6fb52f6c 1087 HttpServerCCStr[i],
1088 ReplyHeaderStats.cc[i]);
30a4f2a8 1089 storeAppendPrintf(entry, close_bracket);
1090}
bfcaf585 1091
1092static void
1093httpAbort(void *data)
1094{
1095 HttpStateData *httpState = data;
dd8037ab 1096 debug(11, 2) ("httpAbort: %s\n", httpState->entry->url);
bfcaf585 1097 comm_close(httpState->fd);
1098}
9b312a19 1099
1100static char *
1101httpStatusString(int status)
1102{
1103 char *p = NULL;
1104 switch (status) {
1105 case 100:
1106 p = "Continue";
1107 break;
1108 case 101:
1109 p = "Switching Protocols";
1110 break;
1111 case 200:
1112 p = "OK";
1113 break;
1114 case 201:
1115 p = "Created";
1116 break;
1117 case 202:
1118 p = "Accepted";
1119 break;
1120 case 203:
1121 p = "Non-Authoritative Information";
1122 break;
1123 case 204:
1124 p = "No Content";
1125 break;
1126 case 205:
1127 p = "Reset Content";
1128 break;
1129 case 206:
1130 p = "Partial Content";
1131 break;
1132 case 300:
1133 p = "Multiple Choices";
1134 break;
1135 case 301:
1136 p = "Moved Permanently";
1137 break;
1138 case 302:
1139 p = "Moved Temporarily";
1140 break;
1141 case 303:
1142 p = "See Other";
1143 break;
1144 case 304:
1145 p = "Not Modified";
1146 break;
1147 case 305:
1148 p = "Use Proxy";
1149 break;
1150 case 400:
1151 p = "Bad Request";
1152 break;
1153 case 401:
1154 p = "Unauthorized";
1155 break;
1156 case 402:
1157 p = "Payment Required";
1158 break;
1159 case 403:
1160 p = "Forbidden";
1161 break;
1162 case 404:
1163 p = "Not Found";
1164 break;
1165 case 405:
1166 p = "Method Not Allowed";
1167 break;
1168 case 406:
1169 p = "Not Acceptable";
1170 break;
1171 case 407:
1172 p = "Proxy Authentication Required";
1173 break;
1174 case 408:
1175 p = "Request Time-out";
1176 break;
1177 case 409:
1178 p = "Conflict";
1179 break;
1180 case 410:
1181 p = "Gone";
1182 break;
1183 case 411:
1184 p = "Length Required";
1185 break;
1186 case 412:
1187 p = "Precondition Failed";
1188 break;
1189 case 413:
1190 p = "Request Entity Too Large";
1191 break;
1192 case 414:
1193 p = "Request-URI Too Large";
1194 break;
1195 case 415:
1196 p = "Unsupported Media Type";
1197 break;
1198 case 500:
1199 p = "Internal Server Error";
1200 break;
1201 case 501:
1202 p = "Not Implemented";
1203 break;
1204 case 502:
1205 p = "Bad Gateway";
1206 break;
1207 case 503:
1208 p = "Service Unavailable";
1209 break;
1210 case 504:
1211 p = "Gateway Time-out";
1212 break;
1213 case 505:
1214 p = "HTTP Version not supported";
1215 break;
1216 default:
1217 p = "Unknown";
1218 debug(11, 0) ("Unknown HTTP status code: %d\n", status);
1219 break;
1220 }
1221 return p;
1222}
1223
1224char *
1225httpReplyHeader(double ver,
1226 http_status status,
1227 char *ctype,
1228 int clen,
1229 time_t lmt,
1230 time_t expires)
1231{
1232 LOCAL_ARRAY(char, buf, HTTP_REPLY_BUF_SZ);
1233 LOCAL_ARRAY(char, float_buf, 64);
1234 int l = 0;
1235 int s = HTTP_REPLY_BUF_SZ;
1236 /* argh, ../lib/snprintf.c doesn't support '%f' */
56878878 1237 snprintf(float_buf, 64, "%3.1f", ver);
9b312a19 1238 assert(strlen(float_buf) == 3);
1239 l += snprintf(buf + l, s - l, "HTTP/%s %d %s\r\n",
1240 float_buf,
1241 (int) status,
1242 httpStatusString(status));
1243 l += snprintf(buf + l, s - l, "Server: Squid/%s\r\n", SQUID_VERSION);
1244 l += snprintf(buf + l, s - l, "Date: %s\r\n", mkrfc1123(squid_curtime));
1245 if (expires >= 0)
1246 l += snprintf(buf + l, s - l, "Expires: %s\r\n", mkrfc1123(expires));
1247 if (lmt)
1248 l += snprintf(buf + l, s - l, "Last-Modified: %s\r\n", mkrfc1123(lmt));
1249 if (clen > 0)
1250 l += snprintf(buf + l, s - l, "Content-Length: %d\r\n", clen);
1251 if (ctype)
1252 l += snprintf(buf + l, s - l, "Content-Type: %s\r\n", ctype);
1253 return buf;
1254}