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