]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
From: "Andres Kroonmaa" <andre@ml.ee>
[thirdparty/squid.git] / src / http.cc
CommitLineData
30a4f2a8 1/*
929545fe 2 * $Id: http.cc,v 1.124 1996/12/03 17:00:11 wessels Exp $
30a4f2a8 3 *
4 * DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
5 * AUTHOR: Harvest Derived
6 *
42c04c16 7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 8 * --------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from the
11 * Internet community. Development is led by Duane Wessels of the
12 * National Laboratory for Applied Network Research and funded by
13 * the National Science Foundation.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 *
29 */
019dd986 30
31/*
30a4f2a8 32 * Copyright (c) 1994, 1995. All rights reserved.
33 *
34 * The Harvest software was developed by the Internet Research Task
35 * Force Research Group on Resource Discovery (IRTF-RD):
36 *
37 * Mic Bowman of Transarc Corporation.
38 * Peter Danzig of the University of Southern California.
39 * Darren R. Hardy of the University of Colorado at Boulder.
40 * Udi Manber of the University of Arizona.
41 * Michael F. Schwartz of the University of Colorado at Boulder.
42 * Duane Wessels of the University of Colorado at Boulder.
43 *
44 * This copyright notice applies to software in the Harvest
45 * ``src/'' directory only. Users should consult the individual
46 * copyright notices in the ``components/'' subdirectories for
47 * copyright information about other software bundled with the
48 * Harvest source code distribution.
49 *
50 * TERMS OF USE
51 *
52 * The Harvest software may be used and re-distributed without
53 * charge, provided that the software origin and research team are
54 * cited in any use of the system. Most commonly this is
55 * accomplished by including a link to the Harvest Home Page
56 * (http://harvest.cs.colorado.edu/) from the query page of any
57 * Broker you deploy, as well as in the query result pages. These
58 * links are generated automatically by the standard Broker
59 * software distribution.
60 *
61 * The Harvest software is provided ``as is'', without express or
62 * implied warranty, and with no support nor obligation to assist
63 * in its use, correction, modification or enhancement. We assume
64 * no liability with respect to the infringement of copyrights,
65 * trade secrets, or any patents, and are not responsible for
66 * consequential damages. Proper use of the Harvest software is
67 * entirely the responsibility of the user.
68 *
69 * DERIVATIVE WORKS
70 *
71 * Users may make derivative works from the Harvest software, subject
72 * to the following constraints:
73 *
74 * - You must include the above copyright notice and these
75 * accompanying paragraphs in all forms of derivative works,
76 * and any documentation and other materials related to such
77 * distribution and use acknowledge that the software was
78 * developed at the above institutions.
79 *
80 * - You must notify IRTF-RD regarding your distribution of
81 * the derivative work.
82 *
83 * - You must clearly notify users that your are distributing
84 * a modified version and not the original Harvest software.
85 *
86 * - Any derivative product is also subject to these copyright
87 * and use restrictions.
88 *
89 * Note that the Harvest software is NOT in the public domain. We
90 * retain copyright, as specified above.
91 *
92 * HISTORY OF FREE SOFTWARE STATUS
93 *
94 * Originally we required sites to license the software in cases
95 * where they were going to build commercial products/services
96 * around Harvest. In June 1995 we changed this policy. We now
97 * allow people to use the core Harvest software (the code found in
98 * the Harvest ``src/'' directory) for free. We made this change
99 * in the interest of encouraging the widest possible deployment of
100 * the technology. The Harvest software is really a reference
101 * implementation of a set of protocols and formats, some of which
102 * we intend to standardize. We encourage commercial
103 * re-implementations of code complying to this set of standards.
019dd986 104 */
44a47c6e 105
106#include "squid.h"
090089c4 107
234967c9 108#define HTTP_DELETE_GAP (1<<18)
090089c4 109
4db43fab 110static const char *const w_space = " \t\n\r";
6bf8443a 111static const char *const crlf = "\r\n";
4db43fab 112
6fb52f6c 113typedef enum {
114 SCC_PUBLIC,
115 SCC_PRIVATE,
116 SCC_NOCACHE,
117 SCC_NOSTORE,
118 SCC_NOTRANSFORM,
119 SCC_MUSTREVALIDATE,
120 SCC_PROXYREVALIDATE,
121 SCC_MAXAGE,
122 SCC_ENUM_END
123} http_server_cc_t;
124
6bf8443a 125enum {
6fb52f6c 126 CCC_NOCACHE,
127 CCC_NOSTORE,
128 CCC_MAXAGE,
129 CCC_MAXSTALE,
130 CCC_MINFRESH,
131 CCC_ONLYIFCACHED,
132 CCC_ENUM_END
6bf8443a 133};
134
135enum {
136 HDR_IMS,
137 HDR_HOST,
138 HDR_MAXAGE
139};
6fb52f6c 140
141char *HttpServerCCStr[] =
142{
143 "public",
144 "private",
145 "no-cache",
146 "no-store",
147 "no-transform",
148 "must-revalidate",
149 "proxy-revalidate",
150 "max-age",
151 "NONE"
152};
153
24382924 154static struct {
30a4f2a8 155 int parsed;
156 int date;
157 int lm;
158 int exp;
159 int clen;
160 int ctype;
6fb52f6c 161 int cc[SCC_ENUM_END];
30a4f2a8 162} ReplyHeaderStats;
090089c4 163
b177367b 164static void httpStateFree _PARAMS((int fd, void *));
165static void httpReadReplyTimeout _PARAMS((int fd, void *));
166static void httpLifetimeExpire _PARAMS((int fd, void *));
67508012 167static void httpMakePublic _PARAMS((StoreEntry *));
168static void httpMakePrivate _PARAMS((StoreEntry *));
169static void httpCacheNegatively _PARAMS((StoreEntry *));
b177367b 170static void httpReadReply _PARAMS((int fd, void *));
67508012 171static void httpSendComplete _PARAMS((int fd, char *, int, int, void *));
b177367b 172static void httpSendRequest _PARAMS((int fd, void *));
0ee4272b 173static void httpConnect _PARAMS((int fd, const ipcache_addrs *, void *));
e5f6c5c2 174static void httpConnectDone _PARAMS((int fd, int status, void *data));
6bf8443a 175static void httpAppendRequestHeader _PARAMS((char *hdr, const char *line, size_t * sz, size_t max));
176
b8d8561b 177
b177367b 178static void
179httpStateFree(int fd, void *data)
f5558c95 180{
b177367b 181 HttpStateData *httpState = data;
0d4d4170 182 if (httpState == NULL)
b177367b 183 return;
30a4f2a8 184 storeUnlockObject(httpState->entry);
0d4d4170 185 if (httpState->reply_hdr) {
186 put_free_8k_page(httpState->reply_hdr);
187 httpState->reply_hdr = NULL;
188 }
30a4f2a8 189 requestUnlink(httpState->request);
20cc1450 190 requestUnlink(httpState->orig_request);
0d4d4170 191 xfree(httpState);
f5558c95 192}
193
b8d8561b 194int
0ee4272b 195httpCachable(const char *url, int method)
090089c4 196{
090089c4 197 /* GET and HEAD are cachable. Others are not. */
6eb42cae 198 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 199 return 0;
090089c4 200 /* else cachable */
201 return 1;
202}
203
204/* This will be called when timeout on read. */
b8d8561b 205static void
b177367b 206httpReadReplyTimeout(int fd, void *data)
090089c4 207{
b177367b 208 HttpStateData *httpState = data;
090089c4 209 StoreEntry *entry = NULL;
30a4f2a8 210 entry = httpState->entry;
593c9a75 211 debug(11, 4, "httpReadReplyTimeout: FD %d: '%s'\n", fd, entry->url);
b8de7ebe 212 squid_error_entry(entry, ERR_READ_TIMEOUT, NULL);
b177367b 213 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
0d4d4170 214 comm_close(fd);
090089c4 215}
216
217/* This will be called when socket lifetime is expired. */
b8d8561b 218static void
b177367b 219httpLifetimeExpire(int fd, void *data)
090089c4 220{
b177367b 221 HttpStateData *httpState = data;
593c9a75 222 StoreEntry *entry = httpState->entry;
223 debug(11, 4, "httpLifeTimeExpire: FD %d: '%s'\n", fd, entry->url);
ce49f524 224 squid_error_entry(entry, ERR_LIFETIME_EXP, NULL);
b177367b 225 commSetSelect(fd, COMM_SELECT_READ | COMM_SELECT_WRITE, NULL, NULL, 0);
0d4d4170 226 comm_close(fd);
090089c4 227}
228
30a4f2a8 229/* This object can be cached for a long time */
b8d8561b 230static void
231httpMakePublic(StoreEntry * entry)
30a4f2a8 232{
1c481e00 233 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 234 storeSetPublicKey(entry);
235}
236
237/* This object should never be cached at all */
b8d8561b 238static void
239httpMakePrivate(StoreEntry * entry)
30a4f2a8 240{
30a4f2a8 241 storeExpireNow(entry);
1c481e00 242 BIT_RESET(entry->flag, ENTRY_CACHABLE);
30a4f2a8 243 storeReleaseRequest(entry); /* delete object when not used */
244}
245
246/* This object may be negatively cached */
b8d8561b 247static void
248httpCacheNegatively(StoreEntry * entry)
30a4f2a8 249{
79b5cc5f 250 storeNegativeCache(entry);
1c481e00 251 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 252 storeSetPublicKey(entry);
30a4f2a8 253}
254
255
256/* Build a reply structure from HTTP reply headers */
b8d8561b 257void
48f44632 258httpParseReplyHeaders(const char *buf, struct _http_reply *reply)
30a4f2a8 259{
260 char *headers = NULL;
261 char *t = NULL;
262 char *s = NULL;
6fb52f6c 263 int delta;
30a4f2a8 264
265 ReplyHeaderStats.parsed++;
266 headers = xstrdup(buf);
267 t = strtok(headers, "\n");
268 while (t) {
269 s = t + strlen(t);
270 while (*s == '\r')
271 *s-- = '\0';
272 if (!strncasecmp(t, "HTTP", 4)) {
273 sscanf(t + 1, "%lf", &reply->version);
274 if ((t = strchr(t, ' '))) {
275 t++;
276 reply->code = atoi(t);
277 }
278 } else if (!strncasecmp(t, "Content-type:", 13)) {
279 if ((t = strchr(t, ' '))) {
280 t++;
f2052513 281 xstrncpy(reply->content_type, t, HTTP_REPLY_FIELD_SZ);
30a4f2a8 282 ReplyHeaderStats.ctype++;
283 }
284 } else if (!strncasecmp(t, "Content-length:", 15)) {
285 if ((t = strchr(t, ' '))) {
286 t++;
287 reply->content_length = atoi(t);
288 ReplyHeaderStats.clen++;
289 }
290 } else if (!strncasecmp(t, "Date:", 5)) {
291 if ((t = strchr(t, ' '))) {
292 t++;
f2052513 293 xstrncpy(reply->date, t, HTTP_REPLY_FIELD_SZ);
30a4f2a8 294 ReplyHeaderStats.date++;
295 }
296 } else if (!strncasecmp(t, "Expires:", 8)) {
297 if ((t = strchr(t, ' '))) {
298 t++;
f2052513 299 xstrncpy(reply->expires, t, HTTP_REPLY_FIELD_SZ);
30a4f2a8 300 ReplyHeaderStats.exp++;
301 }
302 } else if (!strncasecmp(t, "Last-Modified:", 14)) {
303 if ((t = strchr(t, ' '))) {
304 t++;
f2052513 305 xstrncpy(reply->last_modified, t, HTTP_REPLY_FIELD_SZ);
30a4f2a8 306 ReplyHeaderStats.lm++;
307 }
caebbe00 308 } else if (!strncasecmp(t, "Cache-Control:", 14)) {
4db43fab 309 t += 14;
310 while (*t == ' ' || *t == '\t')
311 t++;
312 if (!strncasecmp(t, "public", 6)) {
313 EBIT_SET(reply->cache_control, SCC_PUBLIC);
314 ReplyHeaderStats.cc[SCC_PUBLIC]++;
315 } else if (!strncasecmp(t, "private", 7)) {
316 EBIT_SET(reply->cache_control, SCC_PRIVATE);
317 ReplyHeaderStats.cc[SCC_PRIVATE]++;
318 } else if (!strncasecmp(t, "no-cache", 8)) {
319 EBIT_SET(reply->cache_control, SCC_NOCACHE);
320 ReplyHeaderStats.cc[SCC_NOCACHE]++;
c1764328 321 } else if (!strncasecmp(t, "no-store", 8)) {
322 EBIT_SET(reply->cache_control, SCC_NOSTORE);
323 ReplyHeaderStats.cc[SCC_NOSTORE]++;
324 } else if (!strncasecmp(t, "no-transform", 12)) {
325 EBIT_SET(reply->cache_control, SCC_NOTRANSFORM);
326 ReplyHeaderStats.cc[SCC_NOTRANSFORM]++;
327 } else if (!strncasecmp(t, "must-revalidate", 15)) {
328 EBIT_SET(reply->cache_control, SCC_MUSTREVALIDATE);
329 ReplyHeaderStats.cc[SCC_MUSTREVALIDATE]++;
330 } else if (!strncasecmp(t, "proxy-revalidate", 16)) {
331 EBIT_SET(reply->cache_control, SCC_PROXYREVALIDATE);
332 ReplyHeaderStats.cc[SCC_PROXYREVALIDATE]++;
4db43fab 333 } else if (!strncasecmp(t, "max-age", 7)) {
334 if ((t = strchr(t, '='))) {
335 delta = atoi(++t);
336 EBIT_SET(reply->cache_control, SCC_MAXAGE);
337 ReplyHeaderStats.cc[SCC_MAXAGE]++;
338 strcpy(reply->expires, mkrfc1123(squid_curtime + delta));
5df61230 339 }
caebbe00 340 }
30a4f2a8 341 }
342 t = strtok(NULL, "\n");
343 }
1c481e00 344#if LOG_TIMESTAMPS
345 fprintf(timestamp_log, "T %9d D %9d L %9d E %9d\n",
346 squid_curtime,
cf49df4b 347 parse_rfc1123(reply->date),
348 parse_rfc1123(reply->last_modified),
349 parse_rfc1123(reply->expires));
1c481e00 350#endif /* LOG_TIMESTAMPS */
30a4f2a8 351 safe_free(headers);
352}
353
090089c4 354
b8d8561b 355void
0ee4272b 356httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 357{
358 char *t = NULL;
30a4f2a8 359 StoreEntry *entry = httpState->entry;
d3fb4dea 360 int room;
361 int hdr_len;
2285407f 362 struct _http_reply *reply = NULL;
d3fb4dea 363
ed85b771 364 debug(11, 3, "httpProcessReplyHeader: key '%s'\n", entry->key);
f5558c95 365
30a4f2a8 366 if (httpState->reply_hdr == NULL) {
367 httpState->reply_hdr = get_free_8k_page();
368 memset(httpState->reply_hdr, '\0', 8192);
f5558c95 369 }
30a4f2a8 370 if (httpState->reply_hdr_state == 0) {
371 hdr_len = strlen(httpState->reply_hdr);
ed85b771 372 room = 8191 - hdr_len;
30a4f2a8 373 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 374 hdr_len += room < size ? room : size;
30a4f2a8 375 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
60bf30cb 376 debug(11, 3, "httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", entry->key);
30a4f2a8 377 httpState->reply_hdr_state += 2;
b15fe823 378 entry->mem_obj->reply->code = 555;
ed85b771 379 return;
d3fb4dea 380 }
d1a43e28 381 t = httpState->reply_hdr + hdr_len;
382 /* headers can be incomplete only if object still arriving */
f86a6a46 383 if (!httpState->eof)
d1a43e28 384 if ((t = mime_headers_end(httpState->reply_hdr)) == NULL)
385 return; /* headers not complete */
2285407f 386 *t = '\0';
387 reply = entry->mem_obj->reply;
30a4f2a8 388 reply->hdr_sz = t - httpState->reply_hdr;
2285407f 389 debug(11, 7, "httpProcessReplyHeader: hdr_sz = %d\n", reply->hdr_sz);
30a4f2a8 390 httpState->reply_hdr_state++;
f5558c95 391 }
30a4f2a8 392 if (httpState->reply_hdr_state == 1) {
393 httpState->reply_hdr_state++;
019dd986 394 debug(11, 9, "GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 395 httpState->reply_hdr);
396 /* Parse headers into reply structure */
48f44632 397 httpParseReplyHeaders(httpState->reply_hdr, reply);
38792624 398 timestampsSet(entry);
30a4f2a8 399 /* Check if object is cacheable or not based on reply code */
2285407f 400 if (reply->code)
8e352276 401 debug(11, 3, "httpProcessReplyHeader: HTTP CODE: %d\n", reply->code);
2285407f 402 switch (reply->code) {
30a4f2a8 403 /* Responses that are cacheable */
f5558c95 404 case 200: /* OK */
4e38e700 405 case 203: /* Non-Authoritative Information */
406 case 300: /* Multiple Choices */
f5558c95 407 case 301: /* Moved Permanently */
4e38e700 408 case 410: /* Gone */
30a4f2a8 409 /* don't cache objects from neighbors w/o LMT, Date, or Expires */
6fb52f6c 410 if (EBIT_TEST(reply->cache_control, SCC_PRIVATE))
caebbe00 411 httpMakePrivate(entry);
6fb52f6c 412 else if (EBIT_TEST(reply->cache_control, SCC_NOCACHE))
caebbe00 413 httpMakePrivate(entry);
414 else if (*reply->date)
30a4f2a8 415 httpMakePublic(entry);
416 else if (*reply->last_modified)
417 httpMakePublic(entry);
418 else if (!httpState->neighbor)
419 httpMakePublic(entry);
420 else if (*reply->expires)
421 httpMakePublic(entry);
af00901c 422 else if (entry->mem_obj->request->protocol != PROTO_HTTP)
423 /* XXX Remove this check after a while. DW 8/21/96
424 * We won't keep some FTP objects from neighbors running
425 * 1.0.8 or earlier because their ftpget's don't
426 * add a Date: field */
427 httpMakePublic(entry);
30a4f2a8 428 else
429 httpMakePrivate(entry);
430 break;
431 /* Responses that only are cacheable if the server says so */
432 case 302: /* Moved temporarily */
433 if (*reply->expires)
434 httpMakePublic(entry);
435 else
436 httpMakePrivate(entry);
f5558c95 437 break;
30a4f2a8 438 /* Errors can be negatively cached */
439 case 204: /* No Content */
440 case 305: /* Use Proxy (proxy redirect) */
441 case 400: /* Bad Request */
442 case 403: /* Forbidden */
443 case 404: /* Not Found */
444 case 405: /* Method Now Allowed */
445 case 414: /* Request-URI Too Long */
446 case 500: /* Internal Server Error */
447 case 501: /* Not Implemented */
448 case 502: /* Bad Gateway */
449 case 503: /* Service Unavailable */
450 case 504: /* Gateway Timeout */
851eeef7 451 httpCacheNegatively(entry);
30a4f2a8 452 break;
453 /* Some responses can never be cached */
454 case 303: /* See Other */
234967c9 455 case 304: /* Not Modified */
4e38e700 456 case 401: /* Unauthorized */
457 case 407: /* Proxy Authentication Required */
30a4f2a8 458 default: /* Unknown status code */
459 httpMakePrivate(entry);
4e38e700 460 break;
f5558c95 461 }
462 }
463}
464
090089c4 465
466/* This will be called when data is ready to be read from fd. Read until
467 * error or connection closed. */
f5558c95 468/* XXX this function is too long! */
b8d8561b 469static void
b177367b 470httpReadReply(int fd, void *data)
090089c4 471{
b177367b 472 HttpStateData *httpState = data;
95d659f0 473 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
090089c4 474 int len;
30a4f2a8 475 int bin;
090089c4 476 int clen;
477 int off;
478 StoreEntry *entry = NULL;
479
30a4f2a8 480 entry = httpState->entry;
234967c9 481 if (entry->flag & DELETE_BEHIND && !storeClientWaiting(entry)) {
482 /* we can terminate connection right now */
483 squid_error_entry(entry, ERR_NO_CLIENTS_BIG_OBJ, NULL);
484 comm_close(fd);
485 return;
486 }
487 /* check if we want to defer reading */
488 clen = entry->mem_obj->e_current_len;
489 off = storeGetLowestReaderOffset(entry);
490 if ((clen - off) > HTTP_DELETE_GAP) {
30a4f2a8 491 if (entry->flag & CLIENT_ABORT_REQUEST) {
492 squid_error_entry(entry, ERR_CLIENT_ABORT, NULL);
493 comm_close(fd);
494 return;
495 }
496 IOStats.Http.reads_deferred++;
234967c9 497 debug(11, 3, "httpReadReply: Read deferred for Object: %s\n",
498 entry->url);
499 debug(11, 3, " Current Gap: %d bytes\n", clen - off);
500 /* reschedule, so it will be automatically reactivated
501 * when Gap is big enough. */
b177367b 502 commSetSelect(fd,
234967c9 503 COMM_SELECT_READ,
b177367b 504 httpReadReply,
505 (void *) httpState, 0);
30a4f2a8 506 /* disable read timeout until we are below the GAP */
b177367b 507 commSetSelect(fd,
234967c9 508 COMM_SELECT_TIMEOUT,
b177367b 509 NULL,
234967c9 510 (void *) NULL,
511 (time_t) 0);
56fa4cad 512 if (!BIT_TEST(entry->flag, READ_DEFERRED)) {
513 comm_set_fd_lifetime(fd, 3600); /* limit during deferring */
514 BIT_SET(entry->flag, READ_DEFERRED);
515 }
234967c9 516 /* dont try reading again for a while */
b6f794d6 517 comm_set_stall(fd, Config.stallDelay);
234967c9 518 return;
56fa4cad 519 } else {
520 BIT_RESET(entry->flag, READ_DEFERRED);
090089c4 521 }
1513873c 522 errno = 0;
30a4f2a8 523 len = read(fd, buf, SQUID_TCP_SO_RCVBUF);
019dd986 524 debug(11, 5, "httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 525 comm_set_fd_lifetime(fd, 86400); /* extend after good read */
526 if (len > 0) {
4a63c85f 527 IOStats.Http.reads++;
30a4f2a8 528 for (clen = len - 1, bin = 0; clen; bin++)
529 clen >>= 1;
530 IOStats.Http.read_hist[bin]++;
531 }
ba718c8f 532 if (len < 0) {
881f7a6c 533 debug(50, 2, "httpReadReply: FD %d: read failure: %s.\n",
090089c4 534 fd, xstrerror());
ba718c8f 535 if (errno == EAGAIN || errno == EWOULDBLOCK) {
1513873c 536 /* reinstall handlers */
6fe6313d 537 /* XXX This may loop forever */
b177367b 538 commSetSelect(fd, COMM_SELECT_READ,
539 httpReadReply, (void *) httpState, 0);
540 commSetSelect(fd, COMM_SELECT_TIMEOUT,
541 httpReadReplyTimeout, (void *) httpState, Config.readTimeout);
090089c4 542 } else {
1c481e00 543 BIT_RESET(entry->flag, ENTRY_CACHABLE);
2daae136 544 storeReleaseRequest(entry);
b8de7ebe 545 squid_error_entry(entry, ERR_READ_ERROR, xstrerror());
0d4d4170 546 comm_close(fd);
090089c4 547 }
ba718c8f 548 } else if (len == 0 && entry->mem_obj->e_current_len == 0) {
f86a6a46 549 httpState->eof = 1;
b8de7ebe 550 squid_error_entry(entry,
ba718c8f 551 ERR_ZERO_SIZE_OBJECT,
552 errno ? xstrerror() : NULL);
0d4d4170 553 comm_close(fd);
090089c4 554 } else if (len == 0) {
555 /* Connection closed; retrieval done. */
f86a6a46 556 httpState->eof = 1;
d1a43e28 557 if (httpState->reply_hdr_state < 2)
558 httpProcessReplyHeader(httpState, buf, len);
559 storeAppend(entry, buf, len); /* invoke handlers! */
560 storeComplete(entry); /* deallocates mem_obj->request */
0d4d4170 561 comm_close(fd);
090089c4 562 } else if (entry->flag & CLIENT_ABORT_REQUEST) {
563 /* append the last bit of info we get */
564 storeAppend(entry, buf, len);
b8de7ebe 565 squid_error_entry(entry, ERR_CLIENT_ABORT, NULL);
0d4d4170 566 comm_close(fd);
090089c4 567 } else {
d1a43e28 568 if (httpState->reply_hdr_state < 2)
30a4f2a8 569 httpProcessReplyHeader(httpState, buf, len);
620da955 570 storeAppend(entry, buf, len);
b177367b 571 commSetSelect(fd,
ba718c8f 572 COMM_SELECT_READ,
b177367b 573 httpReadReply,
574 (void *) httpState, 0);
575 commSetSelect(fd,
ba718c8f 576 COMM_SELECT_TIMEOUT,
b177367b 577 httpReadReplyTimeout,
30a4f2a8 578 (void *) httpState,
b6f794d6 579 Config.readTimeout);
090089c4 580 }
581}
582
583/* This will be called when request write is complete. Schedule read of
584 * reply. */
b8d8561b 585static void
586httpSendComplete(int fd, char *buf, int size, int errflag, void *data)
090089c4 587{
30a4f2a8 588 HttpStateData *httpState = data;
090089c4 589 StoreEntry *entry = NULL;
590
30a4f2a8 591 entry = httpState->entry;
019dd986 592 debug(11, 5, "httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 593 fd, size, errflag);
594
090089c4 595 if (errflag) {
b8de7ebe 596 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
0d4d4170 597 comm_close(fd);
090089c4 598 return;
599 } else {
600 /* Schedule read reply. */
b177367b 601 commSetSelect(fd,
019dd986 602 COMM_SELECT_READ,
b177367b 603 httpReadReply,
604 (void *) httpState, 0);
605 commSetSelect(fd,
019dd986 606 COMM_SELECT_TIMEOUT,
b177367b 607 httpReadReplyTimeout,
30a4f2a8 608 (void *) httpState,
b6f794d6 609 Config.readTimeout);
30a4f2a8 610 comm_set_fd_lifetime(fd, 86400); /* extend lifetime */
090089c4 611 }
612}
613
6bf8443a 614static void
615httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max)
616{
617 size_t n = *sz + strlen(line) + 2;
618 if (n >= max)
619 return;
620 debug(11, 5, "httpAppendRequestHeader: %s\n", line);
929545fe 621 strcpy(hdr + (*sz), line);
6bf8443a 622 strcat(hdr + (*sz), crlf);
623 *sz = n;
624}
625
626size_t
627httpBuildRequestHeader(request_t * request,
628 request_t * orig_request,
629 StoreEntry * entry,
630 char *hdr_in,
631 size_t * in_len,
632 char *hdr_out,
633 size_t out_sz,
634 int cfd)
635{
636 char *xbuf = get_free_4k_page();
637 char *ybuf = get_free_4k_page();
638 char *viabuf = get_free_4k_page();
639 char *fwdbuf = get_free_4k_page();
640 char *t = NULL;
641 char *s = NULL;
642 char *end = NULL;
643 size_t len = 0;
644 size_t hdr_len = 0;
645 size_t in_sz;
646 size_t content_length = 0;
647 size_t l;
648 int hdr_flags = 0;
649 const char *url = NULL;
650
651 debug(11, 3, "httpBuildRequestHeader: INPUT:\n%s\n", hdr_in);
652 xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096);
653 xstrncpy(viabuf, "Via: ", 4096);
654 sprintf(ybuf, "%s %s HTTP/1.0",
655 RequestMethodStr[request->method],
656 *request->urlpath ? request->urlpath : "/");
657 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
658 /* Add IMS header */
659 if (entry && entry->lastmod && request->method == METHOD_GET) {
660 sprintf(ybuf, "If-Modified-Since: %s", mkrfc1123(entry->lastmod));
661 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
662 EBIT_SET(hdr_flags, HDR_IMS);
663 }
00c59270 664 end = mime_headers_end(hdr_in);
6bf8443a 665 in_sz = strlen(hdr_in);
666 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
667 hdr_len = t - hdr_in;
668 if (in_sz - hdr_len <= content_length)
669 break;
670 l = strcspn(t, crlf) + 1;
671 if (l > 4096)
672 l = 4096;
673 xstrncpy(xbuf, t, l);
674 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
675 continue;
676 if (strncasecmp(xbuf, "Connection:", 11) == 0)
677 continue;
678 if (strncasecmp(xbuf, "Host:", 5) == 0)
679 EBIT_SET(hdr_flags, HDR_HOST);
680 if (strncasecmp(xbuf, "Content-length:", 15) == 0) {
681 for (s = xbuf + 15; *s && isspace(*s); s++);
682 content_length = (size_t) atoi(s);
683 }
684 if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) {
685 for (s = xbuf + 14; *s && isspace(*s); s++);
686 if (strncasecmp(s, "Max-age=", 8) == 0)
687 EBIT_SET(hdr_flags, HDR_MAXAGE);
688 }
689 if (strncasecmp(xbuf, "Via:", 4) == 0) {
690 for (s = xbuf + 4; *s && isspace(*s); s++);
691 if (strlen(viabuf) + strlen(s) < 4000)
692 strcat(viabuf, s);
693 strcat(viabuf, ", ");
694 continue;
695 }
696 if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) {
697 for (s = xbuf + 16; *s && isspace(*s); s++);
698 if (strlen(fwdbuf) + strlen(s) < 4000)
699 strcat(fwdbuf, s);
700 strcat(fwdbuf, ", ");
701 continue;
702 }
703 if (strncasecmp(xbuf, "If-Modified-Since:", 18))
704 if (EBIT_TEST(hdr_flags, HDR_IMS))
705 continue;
706 httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512);
707 }
708 hdr_len = t - hdr_in;
709 /* Append Via: */
710 sprintf(ybuf, "%3.1f %s:%d (Squid/%s)",
711 orig_request->http_ver,
712 getMyHostname(),
713 (int) Config.Port.http,
714 SQUID_VERSION);
715 strcat(viabuf, ybuf);
716 httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz);
717 /* Append to X-Forwarded-For: */
718 if (cfd >= 0)
719 strcat(fwdbuf, fd_table[cfd].ipaddr);
720 httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz);
721 if (!EBIT_TEST(hdr_flags, HDR_HOST)) {
722 sprintf(ybuf, "Host: %s", orig_request->host);
723 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
724 }
725 if (!EBIT_TEST(hdr_flags, HDR_MAXAGE)) {
726 url = entry ? entry->url : urlCanonical(orig_request, NULL);
727 sprintf(ybuf, "Cache-control: Max-age=%d", (int) getMaxAge(url));
728 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
729 }
730 httpAppendRequestHeader(hdr_out, null_string, &len, out_sz);
731 put_free_4k_page(xbuf);
732 put_free_4k_page(ybuf);
733 put_free_4k_page(viabuf);
734 put_free_4k_page(fwdbuf);
735 if (in_len)
736 *in_len = hdr_len;
9d9d144b 737 if ((l = strlen(hdr_out)) != len) {
738 debug_trap("httpBuildRequestHeader: size mismatch");
739 len = l;
740 }
6bf8443a 741 debug(11, 3, "httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out);
742 return len;
743}
744
090089c4 745/* This will be called when connect completes. Write request. */
b8d8561b 746static void
b177367b 747httpSendRequest(int fd, void *data)
090089c4 748{
b177367b 749 HttpStateData *httpState = data;
090089c4 750 char *buf = NULL;
090089c4 751 int len = 0;
752 int buflen;
30a4f2a8 753 request_t *req = httpState->request;
9864ee44 754 int buftype = 0;
620da955 755 StoreEntry *entry = httpState->entry;
2a26c096 756 int cfd;
090089c4 757
30a4f2a8 758 debug(11, 5, "httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
6bf8443a 759 buflen = strlen(req->urlpath);
30a4f2a8 760 if (httpState->req_hdr)
4768dbe0 761 buflen += httpState->req_hdr_sz + 1;
090089c4 762 buflen += 512; /* lots of extra */
763
78657218 764 if ((req->method == METHOD_POST || req->method == METHOD_PUT)) {
765 debug_trap("httpSendRequest: should not be handling POST/PUT request");
766 return;
090089c4 767 }
30a4f2a8 768 if (buflen < DISK_PAGE_SIZE) {
9864ee44 769 buf = get_free_8k_page();
770 memset(buf, '\0', buflen);
771 buftype = BUF_TYPE_8K;
6bf8443a 772 buflen = DISK_PAGE_SIZE;
30a4f2a8 773 } else {
9864ee44 774 buf = xcalloc(buflen, 1);
775 buftype = BUF_TYPE_MALLOC;
090089c4 776 }
2a26c096 777 if (!opt_forwarded_for)
6bf8443a 778 cfd = -1;
2a26c096 779 else if (entry->mem_obj == NULL)
6bf8443a 780 cfd = -1;
2a26c096 781 else
6bf8443a 782 cfd = storeFirstClientFD(entry->mem_obj);
783 len = httpBuildRequestHeader(req,
784 httpState->orig_request ? httpState->orig_request : req,
785 entry,
786 httpState->req_hdr,
787 NULL,
788 buf,
789 buflen,
790 cfd);
67508012 791 debug(11, 6, "httpSendRequest: FD %d:\n%s\n", fd, buf);
30a4f2a8 792 comm_write(fd,
14e59844 793 buf,
794 len,
795 30,
796 httpSendComplete,
9864ee44 797 httpState,
798 buftype == BUF_TYPE_8K ? put_free_8k_page : xfree);
20cc1450 799 requestUnlink(httpState->orig_request);
800 httpState->orig_request = NULL;
090089c4 801}
802
b8d8561b 803int
20cc1450 804proxyhttpStart(const char *url,
805 request_t * orig_request,
806 StoreEntry * entry,
807 edge * e)
090089c4 808{
f5558c95 809 int sock;
30a4f2a8 810 HttpStateData *httpState = NULL;
7111c86a 811 request_t *request = NULL;
090089c4 812
7111c86a 813 debug(11, 3, "proxyhttpStart: \"%s %s\"\n",
efbb0a25 814 RequestMethodStr[entry->method], url);
019dd986 815 debug(11, 10, "proxyhttpStart: HTTP request header:\n%s\n",
22e4fa85 816 entry->mem_obj->mime_hdr);
090089c4 817
30a4f2a8 818 if (e->options & NEIGHBOR_PROXY_ONLY)
090089c4 819 storeStartDeleteBehind(entry);
820
821 /* Create socket. */
16b204c4 822 sock = comm_open(SOCK_STREAM,
823 0,
824 Config.Addrs.tcp_outgoing,
825 0,
826 COMM_NONBLOCKING,
827 url);
090089c4 828 if (sock == COMM_ERROR) {
019dd986 829 debug(11, 4, "proxyhttpStart: Failed because we're out of sockets.\n");
b8de7ebe 830 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 831 return COMM_ERROR;
832 }
30a4f2a8 833 httpState = xcalloc(1, sizeof(HttpStateData));
834 storeLockObject(httpState->entry = entry, NULL, NULL);
835 httpState->req_hdr = entry->mem_obj->mime_hdr;
4768dbe0 836 httpState->req_hdr_sz = entry->mem_obj->mime_hdr_sz;
30a4f2a8 837 request = get_free_request_t();
838 httpState->request = requestLink(request);
839 httpState->neighbor = e;
20cc1450 840 httpState->orig_request = requestLink(orig_request);
0d4d4170 841 /* register the handler to free HTTP state data when the FD closes */
30a4f2a8 842 comm_add_close_handler(sock,
b177367b 843 httpStateFree,
30a4f2a8 844 (void *) httpState);
784213dc 845 request->method = entry->method;
d5aa0e3b 846 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
30a4f2a8 847 request->port = e->http_port;
d5aa0e3b 848 xstrncpy(request->urlpath, url, MAX_URL);
313c1c28 849 BIT_SET(request->flags, REQ_PROXYING);
a13d9042 850 ipcache_nbgethostbyname(request->host,
851 sock,
b15fe823 852 httpConnect,
a13d9042 853 httpState);
854 return COMM_OK;
855}
856
b15fe823 857static void
fe4e214f 858httpConnect(int fd, const ipcache_addrs * ia, void *data)
a13d9042 859{
860 HttpStateData *httpState = data;
861 request_t *request = httpState->request;
862 StoreEntry *entry = httpState->entry;
e5f6c5c2 863 if (ia == NULL) {
a13d9042 864 debug(11, 4, "httpConnect: Unknown host: %s\n", request->host);
b8de7ebe 865 squid_error_entry(entry, ERR_DNS_FAIL, dns_error_message);
a13d9042 866 comm_close(fd);
b15fe823 867 return;
090089c4 868 }
869 /* Open connection. */
e5f6c5c2 870 httpState->connectState.fd = fd;
871 httpState->connectState.host = request->host;
872 httpState->connectState.port = request->port;
873 httpState->connectState.handler = httpConnectDone;
874 httpState->connectState.data = httpState;
875 comm_nbconnect(fd, &httpState->connectState);
876}
877
878static void
879httpConnectDone(int fd, int status, void *data)
880{
881 HttpStateData *httpState = data;
882 request_t *request = httpState->request;
883 StoreEntry *entry = httpState->entry;
884 edge *e = NULL;
885 if (status != COMM_OK) {
5269d0bd 886 if ((e = httpState->neighbor))
e5f6c5c2 887 e->last_fail_time = squid_curtime;
e5f6c5c2 888 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
889 comm_close(fd);
890 } else {
891 /* Install connection complete handler. */
892 if (opt_no_ipcache)
893 ipcacheInvalidate(request->host);
894 fd_note(fd, entry->url);
b177367b 895 commSetSelect(fd, COMM_SELECT_LIFETIME,
896 httpLifetimeExpire, (void *) httpState, 0);
897 commSetSelect(fd, COMM_SELECT_WRITE,
898 httpSendRequest, (void *) httpState, 0);
9d4b2981 899 if (vizSock > -1)
ef2d27ff 900 vizHackSendPkt(&httpState->connectState.S, 2);
090089c4 901 }
090089c4 902}
903
b8d8561b 904int
20cc1450 905httpStart(char *url,
4768dbe0 906 request_t * request,
907 char *req_hdr,
908 int req_hdr_sz,
909 StoreEntry * entry)
090089c4 910{
911 /* Create state structure. */
a13d9042 912 int sock;
30a4f2a8 913 HttpStateData *httpState = NULL;
090089c4 914
7111c86a 915 debug(11, 3, "httpStart: \"%s %s\"\n",
916 RequestMethodStr[request->method], url);
019dd986 917 debug(11, 10, "httpStart: req_hdr '%s'\n", req_hdr);
090089c4 918
090089c4 919 /* Create socket. */
16b204c4 920 sock = comm_open(SOCK_STREAM,
921 0,
922 Config.Addrs.tcp_outgoing,
923 0,
924 COMM_NONBLOCKING,
925 url);
090089c4 926 if (sock == COMM_ERROR) {
019dd986 927 debug(11, 4, "httpStart: Failed because we're out of sockets.\n");
b8de7ebe 928 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 929 return COMM_ERROR;
930 }
30a4f2a8 931 httpState = xcalloc(1, sizeof(HttpStateData));
932 storeLockObject(httpState->entry = entry, NULL, NULL);
933 httpState->req_hdr = req_hdr;
4768dbe0 934 httpState->req_hdr_sz = req_hdr_sz;
30a4f2a8 935 httpState->request = requestLink(request);
936 comm_add_close_handler(sock,
b177367b 937 httpStateFree,
30a4f2a8 938 (void *) httpState);
a13d9042 939 ipcache_nbgethostbyname(request->host,
940 sock,
941 httpConnect,
942 httpState);
090089c4 943 return COMM_OK;
944}
30a4f2a8 945
b8d8561b 946void
947httpReplyHeaderStats(StoreEntry * entry)
30a4f2a8 948{
6fb52f6c 949 http_server_cc_t i;
30a4f2a8 950 storeAppendPrintf(entry, open_bracket);
6fb52f6c 951 storeAppendPrintf(entry, "{HTTP Reply Headers:}\n");
952 storeAppendPrintf(entry, "{ Headers parsed: %d}\n",
30a4f2a8 953 ReplyHeaderStats.parsed);
6fb52f6c 954 storeAppendPrintf(entry, "{ Date: %d}\n",
30a4f2a8 955 ReplyHeaderStats.date);
6fb52f6c 956 storeAppendPrintf(entry, "{ Last-Modified: %d}\n",
30a4f2a8 957 ReplyHeaderStats.lm);
6fb52f6c 958 storeAppendPrintf(entry, "{ Expires: %d}\n",
30a4f2a8 959 ReplyHeaderStats.exp);
6fb52f6c 960 storeAppendPrintf(entry, "{ Content-Type: %d}\n",
30a4f2a8 961 ReplyHeaderStats.ctype);
6fb52f6c 962 storeAppendPrintf(entry, "{ Content-Length: %d}\n",
30a4f2a8 963 ReplyHeaderStats.clen);
4db43fab 964 for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++)
6fb52f6c 965 storeAppendPrintf(entry, "{Cache-Control %7.7s: %d}\n",
966 HttpServerCCStr[i],
967 ReplyHeaderStats.cc[i]);
30a4f2a8 968 storeAppendPrintf(entry, close_bracket);
969}