]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
Check for if we should delete behind when above LOW water mark.
[thirdparty/squid.git] / src / http.cc
CommitLineData
30a4f2a8 1/*
0bdcddea 2 * $Id: http.cc,v 1.119 1996/11/28 07:31:49 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";
111
6fb52f6c 112typedef enum {
113 SCC_PUBLIC,
114 SCC_PRIVATE,
115 SCC_NOCACHE,
116 SCC_NOSTORE,
117 SCC_NOTRANSFORM,
118 SCC_MUSTREVALIDATE,
119 SCC_PROXYREVALIDATE,
120 SCC_MAXAGE,
121 SCC_ENUM_END
122} http_server_cc_t;
123
124typedef enum {
125 CCC_NOCACHE,
126 CCC_NOSTORE,
127 CCC_MAXAGE,
128 CCC_MAXSTALE,
129 CCC_MINFRESH,
130 CCC_ONLYIFCACHED,
131 CCC_ENUM_END
132} http_client_cc_t;
133
134char *HttpServerCCStr[] =
135{
136 "public",
137 "private",
138 "no-cache",
139 "no-store",
140 "no-transform",
141 "must-revalidate",
142 "proxy-revalidate",
143 "max-age",
144 "NONE"
145};
146
24382924 147static struct {
30a4f2a8 148 int parsed;
149 int date;
150 int lm;
151 int exp;
152 int clen;
153 int ctype;
6fb52f6c 154 int cc[SCC_ENUM_END];
30a4f2a8 155} ReplyHeaderStats;
090089c4 156
b177367b 157static void httpStateFree _PARAMS((int fd, void *));
158static void httpReadReplyTimeout _PARAMS((int fd, void *));
159static void httpLifetimeExpire _PARAMS((int fd, void *));
67508012 160static void httpMakePublic _PARAMS((StoreEntry *));
161static void httpMakePrivate _PARAMS((StoreEntry *));
162static void httpCacheNegatively _PARAMS((StoreEntry *));
b177367b 163static void httpReadReply _PARAMS((int fd, void *));
67508012 164static void httpSendComplete _PARAMS((int fd, char *, int, int, void *));
b177367b 165static void httpSendRequest _PARAMS((int fd, void *));
0ee4272b 166static void httpConnect _PARAMS((int fd, const ipcache_addrs *, void *));
e5f6c5c2 167static void httpConnectDone _PARAMS((int fd, int status, void *data));
b8d8561b 168
b177367b 169static void
170httpStateFree(int fd, void *data)
f5558c95 171{
b177367b 172 HttpStateData *httpState = data;
0d4d4170 173 if (httpState == NULL)
b177367b 174 return;
30a4f2a8 175 storeUnlockObject(httpState->entry);
0d4d4170 176 if (httpState->reply_hdr) {
177 put_free_8k_page(httpState->reply_hdr);
178 httpState->reply_hdr = NULL;
179 }
30a4f2a8 180 requestUnlink(httpState->request);
20cc1450 181 requestUnlink(httpState->orig_request);
0d4d4170 182 xfree(httpState);
f5558c95 183}
184
b8d8561b 185int
0ee4272b 186httpCachable(const char *url, int method)
090089c4 187{
090089c4 188 /* GET and HEAD are cachable. Others are not. */
6eb42cae 189 if (method != METHOD_GET && method != METHOD_HEAD)
090089c4 190 return 0;
090089c4 191 /* else cachable */
192 return 1;
193}
194
195/* This will be called when timeout on read. */
b8d8561b 196static void
b177367b 197httpReadReplyTimeout(int fd, void *data)
090089c4 198{
b177367b 199 HttpStateData *httpState = data;
090089c4 200 StoreEntry *entry = NULL;
30a4f2a8 201 entry = httpState->entry;
593c9a75 202 debug(11, 4, "httpReadReplyTimeout: FD %d: '%s'\n", fd, entry->url);
b8de7ebe 203 squid_error_entry(entry, ERR_READ_TIMEOUT, NULL);
b177367b 204 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
0d4d4170 205 comm_close(fd);
090089c4 206}
207
208/* This will be called when socket lifetime is expired. */
b8d8561b 209static void
b177367b 210httpLifetimeExpire(int fd, void *data)
090089c4 211{
b177367b 212 HttpStateData *httpState = data;
593c9a75 213 StoreEntry *entry = httpState->entry;
214 debug(11, 4, "httpLifeTimeExpire: FD %d: '%s'\n", fd, entry->url);
ce49f524 215 squid_error_entry(entry, ERR_LIFETIME_EXP, NULL);
b177367b 216 commSetSelect(fd, COMM_SELECT_READ | COMM_SELECT_WRITE, NULL, NULL, 0);
0d4d4170 217 comm_close(fd);
090089c4 218}
219
30a4f2a8 220/* This object can be cached for a long time */
b8d8561b 221static void
222httpMakePublic(StoreEntry * entry)
30a4f2a8 223{
1c481e00 224 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 225 storeSetPublicKey(entry);
226}
227
228/* This object should never be cached at all */
b8d8561b 229static void
230httpMakePrivate(StoreEntry * entry)
30a4f2a8 231{
30a4f2a8 232 storeExpireNow(entry);
1c481e00 233 BIT_RESET(entry->flag, ENTRY_CACHABLE);
30a4f2a8 234 storeReleaseRequest(entry); /* delete object when not used */
235}
236
237/* This object may be negatively cached */
b8d8561b 238static void
239httpCacheNegatively(StoreEntry * entry)
30a4f2a8 240{
79b5cc5f 241 storeNegativeCache(entry);
1c481e00 242 if (BIT_TEST(entry->flag, ENTRY_CACHABLE))
30a4f2a8 243 storeSetPublicKey(entry);
30a4f2a8 244}
245
246
247/* Build a reply structure from HTTP reply headers */
b8d8561b 248void
48f44632 249httpParseReplyHeaders(const char *buf, struct _http_reply *reply)
30a4f2a8 250{
251 char *headers = NULL;
252 char *t = NULL;
253 char *s = NULL;
6fb52f6c 254 int delta;
30a4f2a8 255
256 ReplyHeaderStats.parsed++;
257 headers = xstrdup(buf);
258 t = strtok(headers, "\n");
259 while (t) {
260 s = t + strlen(t);
261 while (*s == '\r')
262 *s-- = '\0';
263 if (!strncasecmp(t, "HTTP", 4)) {
264 sscanf(t + 1, "%lf", &reply->version);
265 if ((t = strchr(t, ' '))) {
266 t++;
267 reply->code = atoi(t);
268 }
269 } else if (!strncasecmp(t, "Content-type:", 13)) {
270 if ((t = strchr(t, ' '))) {
271 t++;
f2052513 272 xstrncpy(reply->content_type, t, HTTP_REPLY_FIELD_SZ);
30a4f2a8 273 ReplyHeaderStats.ctype++;
274 }
275 } else if (!strncasecmp(t, "Content-length:", 15)) {
276 if ((t = strchr(t, ' '))) {
277 t++;
278 reply->content_length = atoi(t);
279 ReplyHeaderStats.clen++;
280 }
281 } else if (!strncasecmp(t, "Date:", 5)) {
282 if ((t = strchr(t, ' '))) {
283 t++;
f2052513 284 xstrncpy(reply->date, t, HTTP_REPLY_FIELD_SZ);
30a4f2a8 285 ReplyHeaderStats.date++;
286 }
287 } else if (!strncasecmp(t, "Expires:", 8)) {
288 if ((t = strchr(t, ' '))) {
289 t++;
f2052513 290 xstrncpy(reply->expires, t, HTTP_REPLY_FIELD_SZ);
30a4f2a8 291 ReplyHeaderStats.exp++;
292 }
293 } else if (!strncasecmp(t, "Last-Modified:", 14)) {
294 if ((t = strchr(t, ' '))) {
295 t++;
f2052513 296 xstrncpy(reply->last_modified, t, HTTP_REPLY_FIELD_SZ);
30a4f2a8 297 ReplyHeaderStats.lm++;
298 }
caebbe00 299 } else if (!strncasecmp(t, "Cache-Control:", 14)) {
4db43fab 300 t += 14;
301 while (*t == ' ' || *t == '\t')
302 t++;
303 if (!strncasecmp(t, "public", 6)) {
304 EBIT_SET(reply->cache_control, SCC_PUBLIC);
305 ReplyHeaderStats.cc[SCC_PUBLIC]++;
306 } else if (!strncasecmp(t, "private", 7)) {
307 EBIT_SET(reply->cache_control, SCC_PRIVATE);
308 ReplyHeaderStats.cc[SCC_PRIVATE]++;
309 } else if (!strncasecmp(t, "no-cache", 8)) {
310 EBIT_SET(reply->cache_control, SCC_NOCACHE);
311 ReplyHeaderStats.cc[SCC_NOCACHE]++;
c1764328 312 } else if (!strncasecmp(t, "no-store", 8)) {
313 EBIT_SET(reply->cache_control, SCC_NOSTORE);
314 ReplyHeaderStats.cc[SCC_NOSTORE]++;
315 } else if (!strncasecmp(t, "no-transform", 12)) {
316 EBIT_SET(reply->cache_control, SCC_NOTRANSFORM);
317 ReplyHeaderStats.cc[SCC_NOTRANSFORM]++;
318 } else if (!strncasecmp(t, "must-revalidate", 15)) {
319 EBIT_SET(reply->cache_control, SCC_MUSTREVALIDATE);
320 ReplyHeaderStats.cc[SCC_MUSTREVALIDATE]++;
321 } else if (!strncasecmp(t, "proxy-revalidate", 16)) {
322 EBIT_SET(reply->cache_control, SCC_PROXYREVALIDATE);
323 ReplyHeaderStats.cc[SCC_PROXYREVALIDATE]++;
4db43fab 324 } else if (!strncasecmp(t, "max-age", 7)) {
325 if ((t = strchr(t, '='))) {
326 delta = atoi(++t);
327 EBIT_SET(reply->cache_control, SCC_MAXAGE);
328 ReplyHeaderStats.cc[SCC_MAXAGE]++;
329 strcpy(reply->expires, mkrfc1123(squid_curtime + delta));
5df61230 330 }
caebbe00 331 }
30a4f2a8 332 }
333 t = strtok(NULL, "\n");
334 }
1c481e00 335#if LOG_TIMESTAMPS
336 fprintf(timestamp_log, "T %9d D %9d L %9d E %9d\n",
337 squid_curtime,
cf49df4b 338 parse_rfc1123(reply->date),
339 parse_rfc1123(reply->last_modified),
340 parse_rfc1123(reply->expires));
1c481e00 341#endif /* LOG_TIMESTAMPS */
30a4f2a8 342 safe_free(headers);
343}
344
090089c4 345
b8d8561b 346void
0ee4272b 347httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size)
f5558c95 348{
349 char *t = NULL;
30a4f2a8 350 StoreEntry *entry = httpState->entry;
d3fb4dea 351 int room;
352 int hdr_len;
2285407f 353 struct _http_reply *reply = NULL;
d3fb4dea 354
ed85b771 355 debug(11, 3, "httpProcessReplyHeader: key '%s'\n", entry->key);
f5558c95 356
30a4f2a8 357 if (httpState->reply_hdr == NULL) {
358 httpState->reply_hdr = get_free_8k_page();
359 memset(httpState->reply_hdr, '\0', 8192);
f5558c95 360 }
30a4f2a8 361 if (httpState->reply_hdr_state == 0) {
362 hdr_len = strlen(httpState->reply_hdr);
ed85b771 363 room = 8191 - hdr_len;
30a4f2a8 364 strncat(httpState->reply_hdr, buf, room < size ? room : size);
d3fb4dea 365 hdr_len += room < size ? room : size;
30a4f2a8 366 if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) {
60bf30cb 367 debug(11, 3, "httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", entry->key);
30a4f2a8 368 httpState->reply_hdr_state += 2;
b15fe823 369 entry->mem_obj->reply->code = 555;
ed85b771 370 return;
d3fb4dea 371 }
d1a43e28 372 t = httpState->reply_hdr + hdr_len;
373 /* headers can be incomplete only if object still arriving */
f86a6a46 374 if (!httpState->eof)
d1a43e28 375 if ((t = mime_headers_end(httpState->reply_hdr)) == NULL)
376 return; /* headers not complete */
2285407f 377 *t = '\0';
378 reply = entry->mem_obj->reply;
30a4f2a8 379 reply->hdr_sz = t - httpState->reply_hdr;
2285407f 380 debug(11, 7, "httpProcessReplyHeader: hdr_sz = %d\n", reply->hdr_sz);
30a4f2a8 381 httpState->reply_hdr_state++;
f5558c95 382 }
30a4f2a8 383 if (httpState->reply_hdr_state == 1) {
384 httpState->reply_hdr_state++;
019dd986 385 debug(11, 9, "GOT HTTP REPLY HDR:\n---------\n%s\n----------\n",
30a4f2a8 386 httpState->reply_hdr);
387 /* Parse headers into reply structure */
48f44632 388 httpParseReplyHeaders(httpState->reply_hdr, reply);
38792624 389 timestampsSet(entry);
30a4f2a8 390 /* Check if object is cacheable or not based on reply code */
2285407f 391 if (reply->code)
8e352276 392 debug(11, 3, "httpProcessReplyHeader: HTTP CODE: %d\n", reply->code);
2285407f 393 switch (reply->code) {
30a4f2a8 394 /* Responses that are cacheable */
f5558c95 395 case 200: /* OK */
4e38e700 396 case 203: /* Non-Authoritative Information */
397 case 300: /* Multiple Choices */
f5558c95 398 case 301: /* Moved Permanently */
4e38e700 399 case 410: /* Gone */
30a4f2a8 400 /* don't cache objects from neighbors w/o LMT, Date, or Expires */
6fb52f6c 401 if (EBIT_TEST(reply->cache_control, SCC_PRIVATE))
caebbe00 402 httpMakePrivate(entry);
6fb52f6c 403 else if (EBIT_TEST(reply->cache_control, SCC_NOCACHE))
caebbe00 404 httpMakePrivate(entry);
405 else if (*reply->date)
30a4f2a8 406 httpMakePublic(entry);
407 else if (*reply->last_modified)
408 httpMakePublic(entry);
409 else if (!httpState->neighbor)
410 httpMakePublic(entry);
411 else if (*reply->expires)
412 httpMakePublic(entry);
af00901c 413 else if (entry->mem_obj->request->protocol != PROTO_HTTP)
414 /* XXX Remove this check after a while. DW 8/21/96
415 * We won't keep some FTP objects from neighbors running
416 * 1.0.8 or earlier because their ftpget's don't
417 * add a Date: field */
418 httpMakePublic(entry);
30a4f2a8 419 else
420 httpMakePrivate(entry);
421 break;
422 /* Responses that only are cacheable if the server says so */
423 case 302: /* Moved temporarily */
424 if (*reply->expires)
425 httpMakePublic(entry);
426 else
427 httpMakePrivate(entry);
f5558c95 428 break;
30a4f2a8 429 /* Errors can be negatively cached */
430 case 204: /* No Content */
431 case 305: /* Use Proxy (proxy redirect) */
432 case 400: /* Bad Request */
433 case 403: /* Forbidden */
434 case 404: /* Not Found */
435 case 405: /* Method Now Allowed */
436 case 414: /* Request-URI Too Long */
437 case 500: /* Internal Server Error */
438 case 501: /* Not Implemented */
439 case 502: /* Bad Gateway */
440 case 503: /* Service Unavailable */
441 case 504: /* Gateway Timeout */
851eeef7 442 httpCacheNegatively(entry);
30a4f2a8 443 break;
444 /* Some responses can never be cached */
445 case 303: /* See Other */
234967c9 446 case 304: /* Not Modified */
4e38e700 447 case 401: /* Unauthorized */
448 case 407: /* Proxy Authentication Required */
30a4f2a8 449 default: /* Unknown status code */
450 httpMakePrivate(entry);
4e38e700 451 break;
f5558c95 452 }
453 }
454}
455
090089c4 456
457/* This will be called when data is ready to be read from fd. Read until
458 * error or connection closed. */
f5558c95 459/* XXX this function is too long! */
b8d8561b 460static void
b177367b 461httpReadReply(int fd, void *data)
090089c4 462{
b177367b 463 HttpStateData *httpState = data;
95d659f0 464 LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
090089c4 465 int len;
30a4f2a8 466 int bin;
090089c4 467 int clen;
468 int off;
469 StoreEntry *entry = NULL;
470
30a4f2a8 471 entry = httpState->entry;
234967c9 472 if (entry->flag & DELETE_BEHIND && !storeClientWaiting(entry)) {
473 /* we can terminate connection right now */
474 squid_error_entry(entry, ERR_NO_CLIENTS_BIG_OBJ, NULL);
475 comm_close(fd);
476 return;
477 }
478 /* check if we want to defer reading */
479 clen = entry->mem_obj->e_current_len;
480 off = storeGetLowestReaderOffset(entry);
481 if ((clen - off) > HTTP_DELETE_GAP) {
30a4f2a8 482 if (entry->flag & CLIENT_ABORT_REQUEST) {
483 squid_error_entry(entry, ERR_CLIENT_ABORT, NULL);
484 comm_close(fd);
485 return;
486 }
487 IOStats.Http.reads_deferred++;
234967c9 488 debug(11, 3, "httpReadReply: Read deferred for Object: %s\n",
489 entry->url);
490 debug(11, 3, " Current Gap: %d bytes\n", clen - off);
491 /* reschedule, so it will be automatically reactivated
492 * when Gap is big enough. */
b177367b 493 commSetSelect(fd,
234967c9 494 COMM_SELECT_READ,
b177367b 495 httpReadReply,
496 (void *) httpState, 0);
30a4f2a8 497 /* disable read timeout until we are below the GAP */
b177367b 498 commSetSelect(fd,
234967c9 499 COMM_SELECT_TIMEOUT,
b177367b 500 NULL,
234967c9 501 (void *) NULL,
502 (time_t) 0);
56fa4cad 503 if (!BIT_TEST(entry->flag, READ_DEFERRED)) {
504 comm_set_fd_lifetime(fd, 3600); /* limit during deferring */
505 BIT_SET(entry->flag, READ_DEFERRED);
506 }
234967c9 507 /* dont try reading again for a while */
b6f794d6 508 comm_set_stall(fd, Config.stallDelay);
234967c9 509 return;
56fa4cad 510 } else {
511 BIT_RESET(entry->flag, READ_DEFERRED);
090089c4 512 }
1513873c 513 errno = 0;
30a4f2a8 514 len = read(fd, buf, SQUID_TCP_SO_RCVBUF);
019dd986 515 debug(11, 5, "httpReadReply: FD %d: len %d.\n", fd, len);
30a4f2a8 516 comm_set_fd_lifetime(fd, 86400); /* extend after good read */
517 if (len > 0) {
4a63c85f 518 IOStats.Http.reads++;
30a4f2a8 519 for (clen = len - 1, bin = 0; clen; bin++)
520 clen >>= 1;
521 IOStats.Http.read_hist[bin]++;
522 }
ba718c8f 523 if (len < 0) {
881f7a6c 524 debug(50, 2, "httpReadReply: FD %d: read failure: %s.\n",
090089c4 525 fd, xstrerror());
ba718c8f 526 if (errno == EAGAIN || errno == EWOULDBLOCK) {
1513873c 527 /* reinstall handlers */
6fe6313d 528 /* XXX This may loop forever */
b177367b 529 commSetSelect(fd, COMM_SELECT_READ,
530 httpReadReply, (void *) httpState, 0);
531 commSetSelect(fd, COMM_SELECT_TIMEOUT,
532 httpReadReplyTimeout, (void *) httpState, Config.readTimeout);
090089c4 533 } else {
1c481e00 534 BIT_RESET(entry->flag, ENTRY_CACHABLE);
2daae136 535 storeReleaseRequest(entry);
b8de7ebe 536 squid_error_entry(entry, ERR_READ_ERROR, xstrerror());
0d4d4170 537 comm_close(fd);
090089c4 538 }
ba718c8f 539 } else if (len == 0 && entry->mem_obj->e_current_len == 0) {
f86a6a46 540 httpState->eof = 1;
b8de7ebe 541 squid_error_entry(entry,
ba718c8f 542 ERR_ZERO_SIZE_OBJECT,
543 errno ? xstrerror() : NULL);
0d4d4170 544 comm_close(fd);
090089c4 545 } else if (len == 0) {
546 /* Connection closed; retrieval done. */
f86a6a46 547 httpState->eof = 1;
d1a43e28 548 if (httpState->reply_hdr_state < 2)
549 httpProcessReplyHeader(httpState, buf, len);
550 storeAppend(entry, buf, len); /* invoke handlers! */
551 storeComplete(entry); /* deallocates mem_obj->request */
0d4d4170 552 comm_close(fd);
090089c4 553 } else if (entry->flag & CLIENT_ABORT_REQUEST) {
554 /* append the last bit of info we get */
555 storeAppend(entry, buf, len);
b8de7ebe 556 squid_error_entry(entry, ERR_CLIENT_ABORT, NULL);
0d4d4170 557 comm_close(fd);
090089c4 558 } else {
d1a43e28 559 if (httpState->reply_hdr_state < 2)
30a4f2a8 560 httpProcessReplyHeader(httpState, buf, len);
620da955 561 storeAppend(entry, buf, len);
b177367b 562 commSetSelect(fd,
ba718c8f 563 COMM_SELECT_READ,
b177367b 564 httpReadReply,
565 (void *) httpState, 0);
566 commSetSelect(fd,
ba718c8f 567 COMM_SELECT_TIMEOUT,
b177367b 568 httpReadReplyTimeout,
30a4f2a8 569 (void *) httpState,
b6f794d6 570 Config.readTimeout);
090089c4 571 }
572}
573
574/* This will be called when request write is complete. Schedule read of
575 * reply. */
b8d8561b 576static void
577httpSendComplete(int fd, char *buf, int size, int errflag, void *data)
090089c4 578{
30a4f2a8 579 HttpStateData *httpState = data;
090089c4 580 StoreEntry *entry = NULL;
581
30a4f2a8 582 entry = httpState->entry;
019dd986 583 debug(11, 5, "httpSendComplete: FD %d: size %d: errflag %d.\n",
090089c4 584 fd, size, errflag);
585
090089c4 586 if (errflag) {
b8de7ebe 587 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
0d4d4170 588 comm_close(fd);
090089c4 589 return;
590 } else {
591 /* Schedule read reply. */
b177367b 592 commSetSelect(fd,
019dd986 593 COMM_SELECT_READ,
b177367b 594 httpReadReply,
595 (void *) httpState, 0);
596 commSetSelect(fd,
019dd986 597 COMM_SELECT_TIMEOUT,
b177367b 598 httpReadReplyTimeout,
30a4f2a8 599 (void *) httpState,
b6f794d6 600 Config.readTimeout);
30a4f2a8 601 comm_set_fd_lifetime(fd, 86400); /* extend lifetime */
090089c4 602 }
603}
604
605/* This will be called when connect completes. Write request. */
b8d8561b 606static void
b177367b 607httpSendRequest(int fd, void *data)
090089c4 608{
b177367b 609 HttpStateData *httpState = data;
090089c4 610 char *xbuf = NULL;
611 char *ybuf = NULL;
612 char *buf = NULL;
20cc1450 613 char *viabuf = NULL;
2a26c096 614 char *fwdbuf = NULL;
090089c4 615 char *t = NULL;
fcf47a5f 616 char *s = NULL;
0ee4272b 617 const char *const crlf = "\r\n";
090089c4 618 int len = 0;
619 int buflen;
30a4f2a8 620 request_t *req = httpState->request;
20cc1450 621 request_t *orig_req;
0ee4272b 622 const char *Method = RequestMethodStr[req->method];
9864ee44 623 int buftype = 0;
620da955 624 StoreEntry *entry = httpState->entry;
2a26c096 625 int hdr_flags = 0;
626 int cfd;
090089c4 627
30a4f2a8 628 debug(11, 5, "httpSendRequest: FD %d: httpState %p.\n", fd, httpState);
20cc1450 629 orig_req = httpState->orig_request ? httpState->orig_request : req;
7111c86a 630 buflen = strlen(Method) + strlen(req->urlpath);
30a4f2a8 631 if (httpState->req_hdr)
4768dbe0 632 buflen += httpState->req_hdr_sz + 1;
090089c4 633 buflen += 512; /* lots of extra */
634
78657218 635 if ((req->method == METHOD_POST || req->method == METHOD_PUT)) {
636 debug_trap("httpSendRequest: should not be handling POST/PUT request");
637 return;
090089c4 638 }
30a4f2a8 639 if (buflen < DISK_PAGE_SIZE) {
9864ee44 640 buf = get_free_8k_page();
641 memset(buf, '\0', buflen);
642 buftype = BUF_TYPE_8K;
30a4f2a8 643 } else {
9864ee44 644 buf = xcalloc(buflen, 1);
645 buftype = BUF_TYPE_MALLOC;
090089c4 646 }
090089c4 647
30a4f2a8 648 sprintf(buf, "%s %s HTTP/1.0\r\n",
649 Method,
650 *req->urlpath ? req->urlpath : "/");
090089c4 651 len = strlen(buf);
90050993 652 /* Add IMS header */
653 if (entry->lastmod && req->method == METHOD_GET) {
654 debug(11, 3, "httpSendRequest: Adding IMS: %s\r\n",
655 mkrfc1123(entry->lastmod));
656 ybuf = get_free_4k_page();
657 sprintf(ybuf, "If-Modified-Since: %s\r\n", mkrfc1123(entry->lastmod));
658 strcat(buf, ybuf);
659 len += strlen(ybuf);
660 put_free_4k_page(ybuf);
fcf47a5f 661 ybuf = NULL;
2a26c096 662 EBIT_SET(hdr_flags, HDR_IMS);
90050993 663 }
30a4f2a8 664 if (httpState->req_hdr) { /* we have to parse the request header */
665 xbuf = xstrdup(httpState->req_hdr);
090089c4 666 for (t = strtok(xbuf, crlf); t; t = strtok(NULL, crlf)) {
8c5a561b 667 if (strncasecmp(t, "Proxy-Connection:", 17) == 0)
668 continue;
620da955 669 if (strncasecmp(t, "Connection:", 11) == 0)
af00901c 670 continue;
f78dec53 671 if (strncasecmp(t, "Host:", 5) == 0)
2a26c096 672 EBIT_SET(hdr_flags, HDR_HOST);
fcf47a5f 673 if (strncasecmp(t, "Cache-Control:", 14) == 0) {
674 for (s = t + 14; *s && isspace(*s); s++);
675 if (strncasecmp(s, "Max-age=", 8) == 0)
2a26c096 676 EBIT_SET(hdr_flags, HDR_MAXAGE);
fcf47a5f 677 }
20cc1450 678 if (strncasecmp(t, "Via:", 4) == 0) {
679 viabuf = get_free_4k_page();
680 xstrncpy(viabuf, t, 4096);
681 strcat(viabuf, ", ");
682 continue;
683 }
2a26c096 684 if (strncasecmp(t, "X-Forwarded-For:", 16) == 0) {
685 fwdbuf = get_free_4k_page();
686 xstrncpy(fwdbuf, t, 4096);
687 strcat(fwdbuf, ", ");
90050993 688 continue;
2a26c096 689 }
690 if (EBIT_TEST(hdr_flags, HDR_IMS))
0bdcddea 691 if (!strncasecmp(t, "If-Modified-Since:", 18))
2a26c096 692 continue;
090089c4 693 if (len + (int) strlen(t) > buflen - 10)
694 continue;
695 strcat(buf, t);
696 strcat(buf, crlf);
697 len += strlen(t) + 2;
698 }
699 xfree(xbuf);
090089c4 700 }
61f7f59b 701 /* Add Via: header */
20cc1450 702 if (viabuf == NULL) {
703 viabuf = get_free_4k_page();
704 strcpy(viabuf, "Via: ");
705 }
706 ybuf = get_free_4k_page();
707 sprintf(ybuf, "%3.1f %s:%d (Squid/%s)\r\n",
708 orig_req->http_ver,
709 getMyHostname(),
710 (int) Config.Port.http,
711 SQUID_VERSION);
712 strcat(viabuf, ybuf);
713 strcat(buf, viabuf);
714 len += strlen(viabuf);
d9eec504 715 put_free_4k_page(viabuf);
716 put_free_4k_page(ybuf);
717 viabuf = ybuf = NULL;
20cc1450 718
2a26c096 719 /* Append to X-Forwarded-For: */
720 if (fwdbuf == NULL) {
0bdcddea 721 fwdbuf = get_free_4k_page();
722 strcpy(fwdbuf, "X-Forwarded-For: ");
2a26c096 723 }
724 ybuf = get_free_4k_page();
725 if (!opt_forwarded_for)
726 strcat(fwdbuf, "unknown");
727 else if (entry->mem_obj == NULL)
728 strcat(fwdbuf, "unknown");
729 else if ((cfd = storeFirstClientFD(entry->mem_obj)) < 0)
730 strcat(fwdbuf, "unknown");
731 else
732 strcat(fwdbuf, fd_table[cfd].ipaddr);
733 strcat(fwdbuf, ybuf);
734 len += strlen(fwdbuf);
735 put_free_4k_page(fwdbuf);
736 put_free_4k_page(ybuf);
737 fwdbuf = ybuf = NULL;
738
739 if (EBIT_TEST(hdr_flags, HDR_HOST)) {
f78dec53 740 ybuf = get_free_4k_page();
20cc1450 741 sprintf(ybuf, "Host: %s\r\n", orig_req->host);
f78dec53 742 strcat(buf, ybuf);
743 len += strlen(ybuf);
744 put_free_4k_page(ybuf);
745 }
2a26c096 746 if (!EBIT_TEST(hdr_flags, HDR_MAXAGE)) {
48f44632 747 ybuf = get_free_4k_page();
748 sprintf(ybuf, "Cache-control: Max-age=%d\r\n",
749 (int) getMaxAge(entry->url));
750 strcat(buf, ybuf);
751 len += strlen(ybuf);
752 put_free_4k_page(ybuf);
753 }
090089c4 754 strcat(buf, crlf);
755 len += 2;
67508012 756 debug(11, 6, "httpSendRequest: FD %d:\n%s\n", fd, buf);
30a4f2a8 757 comm_write(fd,
14e59844 758 buf,
759 len,
760 30,
761 httpSendComplete,
9864ee44 762 httpState,
763 buftype == BUF_TYPE_8K ? put_free_8k_page : xfree);
20cc1450 764 requestUnlink(httpState->orig_request);
765 httpState->orig_request = NULL;
090089c4 766}
767
b8d8561b 768int
20cc1450 769proxyhttpStart(const char *url,
770 request_t * orig_request,
771 StoreEntry * entry,
772 edge * e)
090089c4 773{
f5558c95 774 int sock;
30a4f2a8 775 HttpStateData *httpState = NULL;
7111c86a 776 request_t *request = NULL;
090089c4 777
7111c86a 778 debug(11, 3, "proxyhttpStart: \"%s %s\"\n",
efbb0a25 779 RequestMethodStr[entry->method], url);
019dd986 780 debug(11, 10, "proxyhttpStart: HTTP request header:\n%s\n",
22e4fa85 781 entry->mem_obj->mime_hdr);
090089c4 782
30a4f2a8 783 if (e->options & NEIGHBOR_PROXY_ONLY)
090089c4 784 storeStartDeleteBehind(entry);
785
786 /* Create socket. */
16b204c4 787 sock = comm_open(SOCK_STREAM,
788 0,
789 Config.Addrs.tcp_outgoing,
790 0,
791 COMM_NONBLOCKING,
792 url);
090089c4 793 if (sock == COMM_ERROR) {
019dd986 794 debug(11, 4, "proxyhttpStart: Failed because we're out of sockets.\n");
b8de7ebe 795 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 796 return COMM_ERROR;
797 }
30a4f2a8 798 httpState = xcalloc(1, sizeof(HttpStateData));
799 storeLockObject(httpState->entry = entry, NULL, NULL);
800 httpState->req_hdr = entry->mem_obj->mime_hdr;
4768dbe0 801 httpState->req_hdr_sz = entry->mem_obj->mime_hdr_sz;
30a4f2a8 802 request = get_free_request_t();
803 httpState->request = requestLink(request);
804 httpState->neighbor = e;
20cc1450 805 httpState->orig_request = requestLink(orig_request);
0d4d4170 806 /* register the handler to free HTTP state data when the FD closes */
30a4f2a8 807 comm_add_close_handler(sock,
b177367b 808 httpStateFree,
30a4f2a8 809 (void *) httpState);
784213dc 810 request->method = entry->method;
d5aa0e3b 811 xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN);
30a4f2a8 812 request->port = e->http_port;
d5aa0e3b 813 xstrncpy(request->urlpath, url, MAX_URL);
313c1c28 814 BIT_SET(request->flags, REQ_PROXYING);
a13d9042 815 ipcache_nbgethostbyname(request->host,
816 sock,
b15fe823 817 httpConnect,
a13d9042 818 httpState);
819 return COMM_OK;
820}
821
b15fe823 822static void
fe4e214f 823httpConnect(int fd, const ipcache_addrs * ia, void *data)
a13d9042 824{
825 HttpStateData *httpState = data;
826 request_t *request = httpState->request;
827 StoreEntry *entry = httpState->entry;
e5f6c5c2 828 if (ia == NULL) {
a13d9042 829 debug(11, 4, "httpConnect: Unknown host: %s\n", request->host);
b8de7ebe 830 squid_error_entry(entry, ERR_DNS_FAIL, dns_error_message);
a13d9042 831 comm_close(fd);
b15fe823 832 return;
090089c4 833 }
834 /* Open connection. */
e5f6c5c2 835 httpState->connectState.fd = fd;
836 httpState->connectState.host = request->host;
837 httpState->connectState.port = request->port;
838 httpState->connectState.handler = httpConnectDone;
839 httpState->connectState.data = httpState;
840 comm_nbconnect(fd, &httpState->connectState);
841}
842
843static void
844httpConnectDone(int fd, int status, void *data)
845{
846 HttpStateData *httpState = data;
847 request_t *request = httpState->request;
848 StoreEntry *entry = httpState->entry;
849 edge *e = NULL;
850 if (status != COMM_OK) {
5269d0bd 851 if ((e = httpState->neighbor))
e5f6c5c2 852 e->last_fail_time = squid_curtime;
e5f6c5c2 853 squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
854 comm_close(fd);
855 } else {
856 /* Install connection complete handler. */
857 if (opt_no_ipcache)
858 ipcacheInvalidate(request->host);
859 fd_note(fd, entry->url);
b177367b 860 commSetSelect(fd, COMM_SELECT_LIFETIME,
861 httpLifetimeExpire, (void *) httpState, 0);
862 commSetSelect(fd, COMM_SELECT_WRITE,
863 httpSendRequest, (void *) httpState, 0);
ef2d27ff 864 if (Config.vizHackAddr.sin_port)
865 vizHackSendPkt(&httpState->connectState.S, 2);
090089c4 866 }
090089c4 867}
868
b8d8561b 869int
20cc1450 870httpStart(char *url,
4768dbe0 871 request_t * request,
872 char *req_hdr,
873 int req_hdr_sz,
874 StoreEntry * entry)
090089c4 875{
876 /* Create state structure. */
a13d9042 877 int sock;
30a4f2a8 878 HttpStateData *httpState = NULL;
090089c4 879
7111c86a 880 debug(11, 3, "httpStart: \"%s %s\"\n",
881 RequestMethodStr[request->method], url);
019dd986 882 debug(11, 10, "httpStart: req_hdr '%s'\n", req_hdr);
090089c4 883
090089c4 884 /* Create socket. */
16b204c4 885 sock = comm_open(SOCK_STREAM,
886 0,
887 Config.Addrs.tcp_outgoing,
888 0,
889 COMM_NONBLOCKING,
890 url);
090089c4 891 if (sock == COMM_ERROR) {
019dd986 892 debug(11, 4, "httpStart: Failed because we're out of sockets.\n");
b8de7ebe 893 squid_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 894 return COMM_ERROR;
895 }
30a4f2a8 896 httpState = xcalloc(1, sizeof(HttpStateData));
897 storeLockObject(httpState->entry = entry, NULL, NULL);
898 httpState->req_hdr = req_hdr;
4768dbe0 899 httpState->req_hdr_sz = req_hdr_sz;
30a4f2a8 900 httpState->request = requestLink(request);
901 comm_add_close_handler(sock,
b177367b 902 httpStateFree,
30a4f2a8 903 (void *) httpState);
a13d9042 904 ipcache_nbgethostbyname(request->host,
905 sock,
906 httpConnect,
907 httpState);
090089c4 908 return COMM_OK;
909}
30a4f2a8 910
b8d8561b 911void
912httpReplyHeaderStats(StoreEntry * entry)
30a4f2a8 913{
6fb52f6c 914 http_server_cc_t i;
30a4f2a8 915 storeAppendPrintf(entry, open_bracket);
6fb52f6c 916 storeAppendPrintf(entry, "{HTTP Reply Headers:}\n");
917 storeAppendPrintf(entry, "{ Headers parsed: %d}\n",
30a4f2a8 918 ReplyHeaderStats.parsed);
6fb52f6c 919 storeAppendPrintf(entry, "{ Date: %d}\n",
30a4f2a8 920 ReplyHeaderStats.date);
6fb52f6c 921 storeAppendPrintf(entry, "{ Last-Modified: %d}\n",
30a4f2a8 922 ReplyHeaderStats.lm);
6fb52f6c 923 storeAppendPrintf(entry, "{ Expires: %d}\n",
30a4f2a8 924 ReplyHeaderStats.exp);
6fb52f6c 925 storeAppendPrintf(entry, "{ Content-Type: %d}\n",
30a4f2a8 926 ReplyHeaderStats.ctype);
6fb52f6c 927 storeAppendPrintf(entry, "{ Content-Length: %d}\n",
30a4f2a8 928 ReplyHeaderStats.clen);
4db43fab 929 for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++)
6fb52f6c 930 storeAppendPrintf(entry, "{Cache-Control %7.7s: %d}\n",
931 HttpServerCCStr[i],
932 ReplyHeaderStats.cc[i]);
30a4f2a8 933 storeAppendPrintf(entry, close_bracket);
934}