]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
multicast viz hack stuff
[thirdparty/squid.git] / src / http.cc
CommitLineData
30a4f2a8 1/*
9d4b2981 2 * $Id: http.cc,v 1.121 1996/11/30 23:09:52 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);
621 strcat(hdr + (*sz), line);
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 }
664 if ((end = mime_headers_end(hdr_in)) == NULL)
665 return 0;
666 in_sz = strlen(hdr_in);
667 for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
668 hdr_len = t - hdr_in;
669 if (in_sz - hdr_len <= content_length)
670 break;
671 l = strcspn(t, crlf) + 1;
672 if (l > 4096)
673 l = 4096;
674 xstrncpy(xbuf, t, l);
675 if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0)
676 continue;
677 if (strncasecmp(xbuf, "Connection:", 11) == 0)
678 continue;
679 if (strncasecmp(xbuf, "Host:", 5) == 0)
680 EBIT_SET(hdr_flags, HDR_HOST);
681 if (strncasecmp(xbuf, "Content-length:", 15) == 0) {
682 for (s = xbuf + 15; *s && isspace(*s); s++);
683 content_length = (size_t) atoi(s);
684 }
685 if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) {
686 for (s = xbuf + 14; *s && isspace(*s); s++);
687 if (strncasecmp(s, "Max-age=", 8) == 0)
688 EBIT_SET(hdr_flags, HDR_MAXAGE);
689 }
690 if (strncasecmp(xbuf, "Via:", 4) == 0) {
691 for (s = xbuf + 4; *s && isspace(*s); s++);
692 if (strlen(viabuf) + strlen(s) < 4000)
693 strcat(viabuf, s);
694 strcat(viabuf, ", ");
695 continue;
696 }
697 if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) {
698 for (s = xbuf + 16; *s && isspace(*s); s++);
699 if (strlen(fwdbuf) + strlen(s) < 4000)
700 strcat(fwdbuf, s);
701 strcat(fwdbuf, ", ");
702 continue;
703 }
704 if (strncasecmp(xbuf, "If-Modified-Since:", 18))
705 if (EBIT_TEST(hdr_flags, HDR_IMS))
706 continue;
707 httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512);
708 }
709 hdr_len = t - hdr_in;
710 /* Append Via: */
711 sprintf(ybuf, "%3.1f %s:%d (Squid/%s)",
712 orig_request->http_ver,
713 getMyHostname(),
714 (int) Config.Port.http,
715 SQUID_VERSION);
716 strcat(viabuf, ybuf);
717 httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz);
718 /* Append to X-Forwarded-For: */
719 if (cfd >= 0)
720 strcat(fwdbuf, fd_table[cfd].ipaddr);
721 httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz);
722 if (!EBIT_TEST(hdr_flags, HDR_HOST)) {
723 sprintf(ybuf, "Host: %s", orig_request->host);
724 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
725 }
726 if (!EBIT_TEST(hdr_flags, HDR_MAXAGE)) {
727 url = entry ? entry->url : urlCanonical(orig_request, NULL);
728 sprintf(ybuf, "Cache-control: Max-age=%d", (int) getMaxAge(url));
729 httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz);
730 }
731 httpAppendRequestHeader(hdr_out, null_string, &len, out_sz);
732 put_free_4k_page(xbuf);
733 put_free_4k_page(ybuf);
734 put_free_4k_page(viabuf);
735 put_free_4k_page(fwdbuf);
736 if (in_len)
737 *in_len = hdr_len;
738 debug(11, 3, "httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out);
739 return len;
740}
741
090089c4 742/* This will be called when connect completes. Write request. */
b8d8561b 743static void
b177367b 744httpSendRequest(int fd, void *data)
090089c4 745{
b177367b 746 HttpStateData *httpState = data;
090089c4 747 char *buf = NULL;
090089c4 748 int len = 0;
749 int buflen;
30a4f2a8 750 request_t *req = httpState->request;
9864ee44 751 int buftype = 0;
620da955 752 StoreEntry *entry = httpState->entry;
2a26c096 753 int cfd;
090089c4 754
30a4f2a8 755 debug(11, 5, "httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
6bf8443a 756 buflen = strlen(req->urlpath);
30a4f2a8 757 if (httpState->req_hdr)
4768dbe0 758 buflen += httpState->req_hdr_sz + 1;
090089c4 759 buflen += 512; /* lots of extra */
760
78657218 761 if ((req->method == METHOD_POST || req->method == METHOD_PUT)) {
762 debug_trap("httpSendRequest: should not be handling POST/PUT request");
763 return;
090089c4 764 }
30a4f2a8 765 if (buflen < DISK_PAGE_SIZE) {
9864ee44 766 buf = get_free_8k_page();
767 memset(buf, '\0', buflen);
768 buftype = BUF_TYPE_8K;
6bf8443a 769 buflen = DISK_PAGE_SIZE;
30a4f2a8 770 } else {
9864ee44 771 buf = xcalloc(buflen, 1);
772 buftype = BUF_TYPE_MALLOC;
090089c4 773 }
2a26c096 774 if (!opt_forwarded_for)
6bf8443a 775 cfd = -1;
2a26c096 776 else if (entry->mem_obj == NULL)
6bf8443a 777 cfd = -1;
2a26c096 778 else
6bf8443a 779 cfd = storeFirstClientFD(entry->mem_obj);
780 len = httpBuildRequestHeader(req,
781 httpState->orig_request ? httpState->orig_request : req,
782 entry,
783 httpState->req_hdr,
784 NULL,
785 buf,
786 buflen,
787 cfd);
67508012 788 debug(11, 6, "httpSendRequest: FD %d:\n%s\n", fd, buf);
30a4f2a8 789 comm_write(fd,
14e59844 790 buf,
791 len,
792 30,
793 httpSendComplete,
9864ee44 794 httpState,
795 buftype == BUF_TYPE_8K ? put_free_8k_page : xfree);
20cc1450 796 requestUnlink(httpState->orig_request);
797 httpState->orig_request = NULL;
090089c4 798}
799
b8d8561b 800int
20cc1450 801proxyhttpStart(const char *url,
802 request_t * orig_request,
803 StoreEntry * entry,
804 edge * e)
090089c4 805{
f5558c95 806 int sock;
30a4f2a8 807 HttpStateData *httpState = NULL;
7111c86a 808 request_t *request = NULL;
090089c4 809
7111c86a 810 debug(11, 3, "proxyhttpStart: \"%s %s\"\n",
efbb0a25 811 RequestMethodStr[entry->method], url);
019dd986 812 debug(11, 10, "proxyhttpStart: HTTP request header:\n%s\n",
22e4fa85 813 entry->mem_obj->mime_hdr);
090089c4 814
30a4f2a8 815 if (e->options & NEIGHBOR_PROXY_ONLY)
090089c4 816 storeStartDeleteBehind(entry);
817
818 /* Create socket. */
16b204c4 819 sock = comm_open(SOCK_STREAM,
820 0,
821 Config.Addrs.tcp_outgoing,
822 0,
823 COMM_NONBLOCKING,
824 url);
090089c4 825 if (sock == COMM_ERROR) {
019dd986 826 debug(11, 4, "proxyhttpStart: Failed because we're out of sockets.\n");
b8de7ebe 827 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 828 return COMM_ERROR;
829 }
30a4f2a8 830 httpState = xcalloc(1, sizeof(HttpStateData));
831 storeLockObject(httpState->entry = entry, NULL, NULL);
832 httpState->req_hdr = entry->mem_obj->mime_hdr;
4768dbe0 833 httpState->req_hdr_sz = entry->mem_obj->mime_hdr_sz;
30a4f2a8 834 request = get_free_request_t();
835 httpState->request = requestLink(request);
836 httpState->neighbor = e;
20cc1450 837 httpState->orig_request = requestLink(orig_request);
0d4d4170 838 /* register the handler to free HTTP state data when the FD closes */
30a4f2a8 839 comm_add_close_handler(sock,
b177367b 840 httpStateFree,
30a4f2a8 841 (void *) httpState);
784213dc 842 request->method = entry->method;
d5aa0e3b 843 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
30a4f2a8 844 request->port = e->http_port;
d5aa0e3b 845 xstrncpy(request->urlpath, url, MAX_URL);
313c1c28 846 BIT_SET(request->flags, REQ_PROXYING);
a13d9042 847 ipcache_nbgethostbyname(request->host,
848 sock,
b15fe823 849 httpConnect,
a13d9042 850 httpState);
851 return COMM_OK;
852}
853
b15fe823 854static void
fe4e214f 855httpConnect(int fd, const ipcache_addrs * ia, void *data)
a13d9042 856{
857 HttpStateData *httpState = data;
858 request_t *request = httpState->request;
859 StoreEntry *entry = httpState->entry;
e5f6c5c2 860 if (ia == NULL) {
a13d9042 861 debug(11, 4, "httpConnect: Unknown host: %s\n", request->host);
b8de7ebe 862 squid_error_entry(entry, ERR_DNS_FAIL, dns_error_message);
a13d9042 863 comm_close(fd);
b15fe823 864 return;
090089c4 865 }
866 /* Open connection. */
e5f6c5c2 867 httpState->connectState.fd = fd;
868 httpState->connectState.host = request->host;
869 httpState->connectState.port = request->port;
870 httpState->connectState.handler = httpConnectDone;
871 httpState->connectState.data = httpState;
872 comm_nbconnect(fd, &httpState->connectState);
873}
874
875static void
876httpConnectDone(int fd, int status, void *data)
877{
878 HttpStateData *httpState = data;
879 request_t *request = httpState->request;
880 StoreEntry *entry = httpState->entry;
881 edge *e = NULL;
882 if (status != COMM_OK) {
5269d0bd 883 if ((e = httpState->neighbor))
e5f6c5c2 884 e->last_fail_time = squid_curtime;
e5f6c5c2 885 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
886 comm_close(fd);
887 } else {
888 /* Install connection complete handler. */
889 if (opt_no_ipcache)
890 ipcacheInvalidate(request->host);
891 fd_note(fd, entry->url);
b177367b 892 commSetSelect(fd, COMM_SELECT_LIFETIME,
893 httpLifetimeExpire, (void *) httpState, 0);
894 commSetSelect(fd, COMM_SELECT_WRITE,
895 httpSendRequest, (void *) httpState, 0);
9d4b2981 896 if (vizSock > -1)
ef2d27ff 897 vizHackSendPkt(&httpState->connectState.S, 2);
090089c4 898 }
090089c4 899}
900
b8d8561b 901int
20cc1450 902httpStart(char *url,
4768dbe0 903 request_t * request,
904 char *req_hdr,
905 int req_hdr_sz,
906 StoreEntry * entry)
090089c4 907{
908 /* Create state structure. */
a13d9042 909 int sock;
30a4f2a8 910 HttpStateData *httpState = NULL;
090089c4 911
7111c86a 912 debug(11, 3, "httpStart: \"%s %s\"\n",
913 RequestMethodStr[request->method], url);
019dd986 914 debug(11, 10, "httpStart: req_hdr '%s'\n", req_hdr);
090089c4 915
090089c4 916 /* Create socket. */
16b204c4 917 sock = comm_open(SOCK_STREAM,
918 0,
919 Config.Addrs.tcp_outgoing,
920 0,
921 COMM_NONBLOCKING,
922 url);
090089c4 923 if (sock == COMM_ERROR) {
019dd986 924 debug(11, 4, "httpStart: Failed because we're out of sockets.\n");
b8de7ebe 925 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 926 return COMM_ERROR;
927 }
30a4f2a8 928 httpState = xcalloc(1, sizeof(HttpStateData));
929 storeLockObject(httpState->entry = entry, NULL, NULL);
930 httpState->req_hdr = req_hdr;
4768dbe0 931 httpState->req_hdr_sz = req_hdr_sz;
30a4f2a8 932 httpState->request = requestLink(request);
933 comm_add_close_handler(sock,
b177367b 934 httpStateFree,
30a4f2a8 935 (void *) httpState);
a13d9042 936 ipcache_nbgethostbyname(request->host,
937 sock,
938 httpConnect,
939 httpState);
090089c4 940 return COMM_OK;
941}
30a4f2a8 942
b8d8561b 943void
944httpReplyHeaderStats(StoreEntry * entry)
30a4f2a8 945{
6fb52f6c 946 http_server_cc_t i;
30a4f2a8 947 storeAppendPrintf(entry, open_bracket);
6fb52f6c 948 storeAppendPrintf(entry, "{HTTP Reply Headers:}\n");
949 storeAppendPrintf(entry, "{ Headers parsed: %d}\n",
30a4f2a8 950 ReplyHeaderStats.parsed);
6fb52f6c 951 storeAppendPrintf(entry, "{ Date: %d}\n",
30a4f2a8 952 ReplyHeaderStats.date);
6fb52f6c 953 storeAppendPrintf(entry, "{ Last-Modified: %d}\n",
30a4f2a8 954 ReplyHeaderStats.lm);
6fb52f6c 955 storeAppendPrintf(entry, "{ Expires: %d}\n",
30a4f2a8 956 ReplyHeaderStats.exp);
6fb52f6c 957 storeAppendPrintf(entry, "{ Content-Type: %d}\n",
30a4f2a8 958 ReplyHeaderStats.ctype);
6fb52f6c 959 storeAppendPrintf(entry, "{ Content-Length: %d}\n",
30a4f2a8 960 ReplyHeaderStats.clen);
4db43fab 961 for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++)
6fb52f6c 962 storeAppendPrintf(entry, "{Cache-Control %7.7s: %d}\n",
963 HttpServerCCStr[i],
964 ReplyHeaderStats.cc[i]);
30a4f2a8 965 storeAppendPrintf(entry, close_bracket);
966}