]>
Commit | Line | Data |
---|---|---|
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 | 110 | static const char *const w_space = " \t\n\r"; |
111 | ||
6fb52f6c | 112 | typedef 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 | ||
124 | typedef 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 | ||
134 | char *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 | 147 | static 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 | 157 | static void httpStateFree _PARAMS((int fd, void *)); |
158 | static void httpReadReplyTimeout _PARAMS((int fd, void *)); | |
159 | static void httpLifetimeExpire _PARAMS((int fd, void *)); | |
67508012 | 160 | static void httpMakePublic _PARAMS((StoreEntry *)); |
161 | static void httpMakePrivate _PARAMS((StoreEntry *)); | |
162 | static void httpCacheNegatively _PARAMS((StoreEntry *)); | |
b177367b | 163 | static void httpReadReply _PARAMS((int fd, void *)); |
67508012 | 164 | static void httpSendComplete _PARAMS((int fd, char *, int, int, void *)); |
b177367b | 165 | static void httpSendRequest _PARAMS((int fd, void *)); |
0ee4272b | 166 | static void httpConnect _PARAMS((int fd, const ipcache_addrs *, void *)); |
e5f6c5c2 | 167 | static void httpConnectDone _PARAMS((int fd, int status, void *data)); |
b8d8561b | 168 | |
b177367b | 169 | static void |
170 | httpStateFree(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 | 185 | int |
0ee4272b | 186 | httpCachable(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 | 196 | static void |
b177367b | 197 | httpReadReplyTimeout(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 | 209 | static void |
b177367b | 210 | httpLifetimeExpire(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 | 221 | static void |
222 | httpMakePublic(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 | 229 | static void |
230 | httpMakePrivate(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 | 238 | static void |
239 | httpCacheNegatively(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 | 248 | void |
48f44632 | 249 | httpParseReplyHeaders(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 | 346 | void |
0ee4272b | 347 | httpProcessReplyHeader(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 | 460 | static void |
b177367b | 461 | httpReadReply(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 | 576 | static void |
577 | httpSendComplete(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 | 606 | static void |
b177367b | 607 | httpSendRequest(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 | 768 | int |
20cc1450 | 769 | proxyhttpStart(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 | 822 | static void |
fe4e214f | 823 | httpConnect(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 | ||
843 | static void | |
844 | httpConnectDone(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 | 869 | int |
20cc1450 | 870 | httpStart(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 | 911 | void |
912 | httpReplyHeaderStats(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 | } |