]>
Commit | Line | Data |
---|---|---|
30a4f2a8 | 1 | /* |
929545fe | 2 | * $Id: http.cc,v 1.124 1996/12/03 17:00:11 wessels Exp $ |
30a4f2a8 | 3 | * |
4 | * DEBUG: section 11 Hypertext Transfer Protocol (HTTP) | |
5 | * AUTHOR: Harvest Derived | |
6 | * | |
42c04c16 | 7 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
30a4f2a8 | 8 | * -------------------------------------------------------- |
9 | * | |
10 | * Squid is the result of efforts by numerous individuals from the | |
11 | * Internet community. Development is led by Duane Wessels of the | |
12 | * National Laboratory for Applied Network Research and funded by | |
13 | * the National Science Foundation. | |
14 | * | |
15 | * This program is free software; you can redistribute it and/or modify | |
16 | * it under the terms of the GNU General Public License as published by | |
17 | * the Free Software Foundation; either version 2 of the License, or | |
18 | * (at your option) any later version. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | * GNU General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU General Public License | |
26 | * along with this program; if not, write to the Free Software | |
27 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
28 | * | |
29 | */ | |
019dd986 | 30 | |
31 | /* | |
30a4f2a8 | 32 | * Copyright (c) 1994, 1995. All rights reserved. |
33 | * | |
34 | * The Harvest software was developed by the Internet Research Task | |
35 | * Force Research Group on Resource Discovery (IRTF-RD): | |
36 | * | |
37 | * Mic Bowman of Transarc Corporation. | |
38 | * Peter Danzig of the University of Southern California. | |
39 | * Darren R. Hardy of the University of Colorado at Boulder. | |
40 | * Udi Manber of the University of Arizona. | |
41 | * Michael F. Schwartz of the University of Colorado at Boulder. | |
42 | * Duane Wessels of the University of Colorado at Boulder. | |
43 | * | |
44 | * This copyright notice applies to software in the Harvest | |
45 | * ``src/'' directory only. Users should consult the individual | |
46 | * copyright notices in the ``components/'' subdirectories for | |
47 | * copyright information about other software bundled with the | |
48 | * Harvest source code distribution. | |
49 | * | |
50 | * TERMS OF USE | |
51 | * | |
52 | * The Harvest software may be used and re-distributed without | |
53 | * charge, provided that the software origin and research team are | |
54 | * cited in any use of the system. Most commonly this is | |
55 | * accomplished by including a link to the Harvest Home Page | |
56 | * (http://harvest.cs.colorado.edu/) from the query page of any | |
57 | * Broker you deploy, as well as in the query result pages. These | |
58 | * links are generated automatically by the standard Broker | |
59 | * software distribution. | |
60 | * | |
61 | * The Harvest software is provided ``as is'', without express or | |
62 | * implied warranty, and with no support nor obligation to assist | |
63 | * in its use, correction, modification or enhancement. We assume | |
64 | * no liability with respect to the infringement of copyrights, | |
65 | * trade secrets, or any patents, and are not responsible for | |
66 | * consequential damages. Proper use of the Harvest software is | |
67 | * entirely the responsibility of the user. | |
68 | * | |
69 | * DERIVATIVE WORKS | |
70 | * | |
71 | * Users may make derivative works from the Harvest software, subject | |
72 | * to the following constraints: | |
73 | * | |
74 | * - You must include the above copyright notice and these | |
75 | * accompanying paragraphs in all forms of derivative works, | |
76 | * and any documentation and other materials related to such | |
77 | * distribution and use acknowledge that the software was | |
78 | * developed at the above institutions. | |
79 | * | |
80 | * - You must notify IRTF-RD regarding your distribution of | |
81 | * the derivative work. | |
82 | * | |
83 | * - You must clearly notify users that your are distributing | |
84 | * a modified version and not the original Harvest software. | |
85 | * | |
86 | * - Any derivative product is also subject to these copyright | |
87 | * and use restrictions. | |
88 | * | |
89 | * Note that the Harvest software is NOT in the public domain. We | |
90 | * retain copyright, as specified above. | |
91 | * | |
92 | * HISTORY OF FREE SOFTWARE STATUS | |
93 | * | |
94 | * Originally we required sites to license the software in cases | |
95 | * where they were going to build commercial products/services | |
96 | * around Harvest. In June 1995 we changed this policy. We now | |
97 | * allow people to use the core Harvest software (the code found in | |
98 | * the Harvest ``src/'' directory) for free. We made this change | |
99 | * in the interest of encouraging the widest possible deployment of | |
100 | * the technology. The Harvest software is really a reference | |
101 | * implementation of a set of protocols and formats, some of which | |
102 | * we intend to standardize. We encourage commercial | |
103 | * re-implementations of code complying to this set of standards. | |
019dd986 | 104 | */ |
44a47c6e | 105 | |
106 | #include "squid.h" | |
090089c4 | 107 | |
234967c9 | 108 | #define HTTP_DELETE_GAP (1<<18) |
090089c4 | 109 | |
4db43fab | 110 | static const char *const w_space = " \t\n\r"; |
6bf8443a | 111 | static const char *const crlf = "\r\n"; |
4db43fab | 112 | |
6fb52f6c | 113 | typedef 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 | 125 | enum { |
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 | ||
135 | enum { | |
136 | HDR_IMS, | |
137 | HDR_HOST, | |
138 | HDR_MAXAGE | |
139 | }; | |
6fb52f6c | 140 | |
141 | char *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 | 154 | static 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 | 164 | static void httpStateFree _PARAMS((int fd, void *)); |
165 | static void httpReadReplyTimeout _PARAMS((int fd, void *)); | |
166 | static void httpLifetimeExpire _PARAMS((int fd, void *)); | |
67508012 | 167 | static void httpMakePublic _PARAMS((StoreEntry *)); |
168 | static void httpMakePrivate _PARAMS((StoreEntry *)); | |
169 | static void httpCacheNegatively _PARAMS((StoreEntry *)); | |
b177367b | 170 | static void httpReadReply _PARAMS((int fd, void *)); |
67508012 | 171 | static void httpSendComplete _PARAMS((int fd, char *, int, int, void *)); |
b177367b | 172 | static void httpSendRequest _PARAMS((int fd, void *)); |
0ee4272b | 173 | static void httpConnect _PARAMS((int fd, const ipcache_addrs *, void *)); |
e5f6c5c2 | 174 | static void httpConnectDone _PARAMS((int fd, int status, void *data)); |
6bf8443a | 175 | static void httpAppendRequestHeader _PARAMS((char *hdr, const char *line, size_t * sz, size_t max)); |
176 | ||
b8d8561b | 177 | |
b177367b | 178 | static void |
179 | httpStateFree(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 | 194 | int |
0ee4272b | 195 | httpCachable(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 | 205 | static void |
b177367b | 206 | httpReadReplyTimeout(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 | 218 | static void |
b177367b | 219 | httpLifetimeExpire(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 | 230 | static void |
231 | httpMakePublic(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 | 238 | static void |
239 | httpMakePrivate(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 | 247 | static void |
248 | httpCacheNegatively(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 | 257 | void |
48f44632 | 258 | httpParseReplyHeaders(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 | 355 | void |
0ee4272b | 356 | httpProcessReplyHeader(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 | 469 | static void |
b177367b | 470 | httpReadReply(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 | 585 | static void |
586 | httpSendComplete(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 | 614 | static void |
615 | httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max) | |
616 | { | |
617 | size_t n = *sz + strlen(line) + 2; | |
618 | if (n >= max) | |
619 | return; | |
620 | debug(11, 5, "httpAppendRequestHeader: %s\n", line); | |
929545fe | 621 | strcpy(hdr + (*sz), line); |
6bf8443a | 622 | strcat(hdr + (*sz), crlf); |
623 | *sz = n; | |
624 | } | |
625 | ||
626 | size_t | |
627 | httpBuildRequestHeader(request_t * request, | |
628 | request_t * orig_request, | |
629 | StoreEntry * entry, | |
630 | char *hdr_in, | |
631 | size_t * in_len, | |
632 | char *hdr_out, | |
633 | size_t out_sz, | |
634 | int cfd) | |
635 | { | |
636 | char *xbuf = get_free_4k_page(); | |
637 | char *ybuf = get_free_4k_page(); | |
638 | char *viabuf = get_free_4k_page(); | |
639 | char *fwdbuf = get_free_4k_page(); | |
640 | char *t = NULL; | |
641 | char *s = NULL; | |
642 | char *end = NULL; | |
643 | size_t len = 0; | |
644 | size_t hdr_len = 0; | |
645 | size_t in_sz; | |
646 | size_t content_length = 0; | |
647 | size_t l; | |
648 | int hdr_flags = 0; | |
649 | const char *url = NULL; | |
650 | ||
651 | debug(11, 3, "httpBuildRequestHeader: INPUT:\n%s\n", hdr_in); | |
652 | xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096); | |
653 | xstrncpy(viabuf, "Via: ", 4096); | |
654 | sprintf(ybuf, "%s %s HTTP/1.0", | |
655 | RequestMethodStr[request->method], | |
656 | *request->urlpath ? request->urlpath : "/"); | |
657 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz); | |
658 | /* Add IMS header */ | |
659 | if (entry && entry->lastmod && request->method == METHOD_GET) { | |
660 | sprintf(ybuf, "If-Modified-Since: %s", mkrfc1123(entry->lastmod)); | |
661 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz); | |
662 | EBIT_SET(hdr_flags, HDR_IMS); | |
663 | } | |
00c59270 | 664 | end = mime_headers_end(hdr_in); |
6bf8443a | 665 | in_sz = strlen(hdr_in); |
666 | for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) { | |
667 | hdr_len = t - hdr_in; | |
668 | if (in_sz - hdr_len <= content_length) | |
669 | break; | |
670 | l = strcspn(t, crlf) + 1; | |
671 | if (l > 4096) | |
672 | l = 4096; | |
673 | xstrncpy(xbuf, t, l); | |
674 | if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0) | |
675 | continue; | |
676 | if (strncasecmp(xbuf, "Connection:", 11) == 0) | |
677 | continue; | |
678 | if (strncasecmp(xbuf, "Host:", 5) == 0) | |
679 | EBIT_SET(hdr_flags, HDR_HOST); | |
680 | if (strncasecmp(xbuf, "Content-length:", 15) == 0) { | |
681 | for (s = xbuf + 15; *s && isspace(*s); s++); | |
682 | content_length = (size_t) atoi(s); | |
683 | } | |
684 | if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) { | |
685 | for (s = xbuf + 14; *s && isspace(*s); s++); | |
686 | if (strncasecmp(s, "Max-age=", 8) == 0) | |
687 | EBIT_SET(hdr_flags, HDR_MAXAGE); | |
688 | } | |
689 | if (strncasecmp(xbuf, "Via:", 4) == 0) { | |
690 | for (s = xbuf + 4; *s && isspace(*s); s++); | |
691 | if (strlen(viabuf) + strlen(s) < 4000) | |
692 | strcat(viabuf, s); | |
693 | strcat(viabuf, ", "); | |
694 | continue; | |
695 | } | |
696 | if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) { | |
697 | for (s = xbuf + 16; *s && isspace(*s); s++); | |
698 | if (strlen(fwdbuf) + strlen(s) < 4000) | |
699 | strcat(fwdbuf, s); | |
700 | strcat(fwdbuf, ", "); | |
701 | continue; | |
702 | } | |
703 | if (strncasecmp(xbuf, "If-Modified-Since:", 18)) | |
704 | if (EBIT_TEST(hdr_flags, HDR_IMS)) | |
705 | continue; | |
706 | httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512); | |
707 | } | |
708 | hdr_len = t - hdr_in; | |
709 | /* Append Via: */ | |
710 | sprintf(ybuf, "%3.1f %s:%d (Squid/%s)", | |
711 | orig_request->http_ver, | |
712 | getMyHostname(), | |
713 | (int) Config.Port.http, | |
714 | SQUID_VERSION); | |
715 | strcat(viabuf, ybuf); | |
716 | httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz); | |
717 | /* Append to X-Forwarded-For: */ | |
718 | if (cfd >= 0) | |
719 | strcat(fwdbuf, fd_table[cfd].ipaddr); | |
720 | httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz); | |
721 | if (!EBIT_TEST(hdr_flags, HDR_HOST)) { | |
722 | sprintf(ybuf, "Host: %s", orig_request->host); | |
723 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz); | |
724 | } | |
725 | if (!EBIT_TEST(hdr_flags, HDR_MAXAGE)) { | |
726 | url = entry ? entry->url : urlCanonical(orig_request, NULL); | |
727 | sprintf(ybuf, "Cache-control: Max-age=%d", (int) getMaxAge(url)); | |
728 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz); | |
729 | } | |
730 | httpAppendRequestHeader(hdr_out, null_string, &len, out_sz); | |
731 | put_free_4k_page(xbuf); | |
732 | put_free_4k_page(ybuf); | |
733 | put_free_4k_page(viabuf); | |
734 | put_free_4k_page(fwdbuf); | |
735 | if (in_len) | |
736 | *in_len = hdr_len; | |
9d9d144b | 737 | if ((l = strlen(hdr_out)) != len) { |
738 | debug_trap("httpBuildRequestHeader: size mismatch"); | |
739 | len = l; | |
740 | } | |
6bf8443a | 741 | debug(11, 3, "httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out); |
742 | return len; | |
743 | } | |
744 | ||
090089c4 | 745 | /* This will be called when connect completes. Write request. */ |
b8d8561b | 746 | static void |
b177367b | 747 | httpSendRequest(int fd, void *data) |
090089c4 | 748 | { |
b177367b | 749 | HttpStateData *httpState = data; |
090089c4 | 750 | char *buf = NULL; |
090089c4 | 751 | int len = 0; |
752 | int buflen; | |
30a4f2a8 | 753 | request_t *req = httpState->request; |
9864ee44 | 754 | int buftype = 0; |
620da955 | 755 | StoreEntry *entry = httpState->entry; |
2a26c096 | 756 | int cfd; |
090089c4 | 757 | |
30a4f2a8 | 758 | debug(11, 5, "httpSendRequest: FD %d: httpState %p.\n", fd, httpState); |
6bf8443a | 759 | buflen = strlen(req->urlpath); |
30a4f2a8 | 760 | if (httpState->req_hdr) |
4768dbe0 | 761 | buflen += httpState->req_hdr_sz + 1; |
090089c4 | 762 | buflen += 512; /* lots of extra */ |
763 | ||
78657218 | 764 | if ((req->method == METHOD_POST || req->method == METHOD_PUT)) { |
765 | debug_trap("httpSendRequest: should not be handling POST/PUT request"); | |
766 | return; | |
090089c4 | 767 | } |
30a4f2a8 | 768 | if (buflen < DISK_PAGE_SIZE) { |
9864ee44 | 769 | buf = get_free_8k_page(); |
770 | memset(buf, '\0', buflen); | |
771 | buftype = BUF_TYPE_8K; | |
6bf8443a | 772 | buflen = DISK_PAGE_SIZE; |
30a4f2a8 | 773 | } else { |
9864ee44 | 774 | buf = xcalloc(buflen, 1); |
775 | buftype = BUF_TYPE_MALLOC; | |
090089c4 | 776 | } |
2a26c096 | 777 | if (!opt_forwarded_for) |
6bf8443a | 778 | cfd = -1; |
2a26c096 | 779 | else if (entry->mem_obj == NULL) |
6bf8443a | 780 | cfd = -1; |
2a26c096 | 781 | else |
6bf8443a | 782 | cfd = storeFirstClientFD(entry->mem_obj); |
783 | len = httpBuildRequestHeader(req, | |
784 | httpState->orig_request ? httpState->orig_request : req, | |
785 | entry, | |
786 | httpState->req_hdr, | |
787 | NULL, | |
788 | buf, | |
789 | buflen, | |
790 | cfd); | |
67508012 | 791 | debug(11, 6, "httpSendRequest: FD %d:\n%s\n", fd, buf); |
30a4f2a8 | 792 | comm_write(fd, |
14e59844 | 793 | buf, |
794 | len, | |
795 | 30, | |
796 | httpSendComplete, | |
9864ee44 | 797 | httpState, |
798 | buftype == BUF_TYPE_8K ? put_free_8k_page : xfree); | |
20cc1450 | 799 | requestUnlink(httpState->orig_request); |
800 | httpState->orig_request = NULL; | |
090089c4 | 801 | } |
802 | ||
b8d8561b | 803 | int |
20cc1450 | 804 | proxyhttpStart(const char *url, |
805 | request_t * orig_request, | |
806 | StoreEntry * entry, | |
807 | edge * e) | |
090089c4 | 808 | { |
f5558c95 | 809 | int sock; |
30a4f2a8 | 810 | HttpStateData *httpState = NULL; |
7111c86a | 811 | request_t *request = NULL; |
090089c4 | 812 | |
7111c86a | 813 | debug(11, 3, "proxyhttpStart: \"%s %s\"\n", |
efbb0a25 | 814 | RequestMethodStr[entry->method], url); |
019dd986 | 815 | debug(11, 10, "proxyhttpStart: HTTP request header:\n%s\n", |
22e4fa85 | 816 | entry->mem_obj->mime_hdr); |
090089c4 | 817 | |
30a4f2a8 | 818 | if (e->options & NEIGHBOR_PROXY_ONLY) |
090089c4 | 819 | storeStartDeleteBehind(entry); |
820 | ||
821 | /* Create socket. */ | |
16b204c4 | 822 | sock = comm_open(SOCK_STREAM, |
823 | 0, | |
824 | Config.Addrs.tcp_outgoing, | |
825 | 0, | |
826 | COMM_NONBLOCKING, | |
827 | url); | |
090089c4 | 828 | if (sock == COMM_ERROR) { |
019dd986 | 829 | debug(11, 4, "proxyhttpStart: Failed because we're out of sockets.\n"); |
b8de7ebe | 830 | squid_error_entry(entry, ERR_NO_FDS, xstrerror()); |
090089c4 | 831 | return COMM_ERROR; |
832 | } | |
30a4f2a8 | 833 | httpState = xcalloc(1, sizeof(HttpStateData)); |
834 | storeLockObject(httpState->entry = entry, NULL, NULL); | |
835 | httpState->req_hdr = entry->mem_obj->mime_hdr; | |
4768dbe0 | 836 | httpState->req_hdr_sz = entry->mem_obj->mime_hdr_sz; |
30a4f2a8 | 837 | request = get_free_request_t(); |
838 | httpState->request = requestLink(request); | |
839 | httpState->neighbor = e; | |
20cc1450 | 840 | httpState->orig_request = requestLink(orig_request); |
0d4d4170 | 841 | /* register the handler to free HTTP state data when the FD closes */ |
30a4f2a8 | 842 | comm_add_close_handler(sock, |
b177367b | 843 | httpStateFree, |
30a4f2a8 | 844 | (void *) httpState); |
784213dc | 845 | request->method = entry->method; |
d5aa0e3b | 846 | xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN); |
30a4f2a8 | 847 | request->port = e->http_port; |
d5aa0e3b | 848 | xstrncpy(request->urlpath, url, MAX_URL); |
313c1c28 | 849 | BIT_SET(request->flags, REQ_PROXYING); |
a13d9042 | 850 | ipcache_nbgethostbyname(request->host, |
851 | sock, | |
b15fe823 | 852 | httpConnect, |
a13d9042 | 853 | httpState); |
854 | return COMM_OK; | |
855 | } | |
856 | ||
b15fe823 | 857 | static void |
fe4e214f | 858 | httpConnect(int fd, const ipcache_addrs * ia, void *data) |
a13d9042 | 859 | { |
860 | HttpStateData *httpState = data; | |
861 | request_t *request = httpState->request; | |
862 | StoreEntry *entry = httpState->entry; | |
e5f6c5c2 | 863 | if (ia == NULL) { |
a13d9042 | 864 | debug(11, 4, "httpConnect: Unknown host: %s\n", request->host); |
b8de7ebe | 865 | squid_error_entry(entry, ERR_DNS_FAIL, dns_error_message); |
a13d9042 | 866 | comm_close(fd); |
b15fe823 | 867 | return; |
090089c4 | 868 | } |
869 | /* Open connection. */ | |
e5f6c5c2 | 870 | httpState->connectState.fd = fd; |
871 | httpState->connectState.host = request->host; | |
872 | httpState->connectState.port = request->port; | |
873 | httpState->connectState.handler = httpConnectDone; | |
874 | httpState->connectState.data = httpState; | |
875 | comm_nbconnect(fd, &httpState->connectState); | |
876 | } | |
877 | ||
878 | static void | |
879 | httpConnectDone(int fd, int status, void *data) | |
880 | { | |
881 | HttpStateData *httpState = data; | |
882 | request_t *request = httpState->request; | |
883 | StoreEntry *entry = httpState->entry; | |
884 | edge *e = NULL; | |
885 | if (status != COMM_OK) { | |
5269d0bd | 886 | if ((e = httpState->neighbor)) |
e5f6c5c2 | 887 | e->last_fail_time = squid_curtime; |
e5f6c5c2 | 888 | squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror()); |
889 | comm_close(fd); | |
890 | } else { | |
891 | /* Install connection complete handler. */ | |
892 | if (opt_no_ipcache) | |
893 | ipcacheInvalidate(request->host); | |
894 | fd_note(fd, entry->url); | |
b177367b | 895 | commSetSelect(fd, COMM_SELECT_LIFETIME, |
896 | httpLifetimeExpire, (void *) httpState, 0); | |
897 | commSetSelect(fd, COMM_SELECT_WRITE, | |
898 | httpSendRequest, (void *) httpState, 0); | |
9d4b2981 | 899 | if (vizSock > -1) |
ef2d27ff | 900 | vizHackSendPkt(&httpState->connectState.S, 2); |
090089c4 | 901 | } |
090089c4 | 902 | } |
903 | ||
b8d8561b | 904 | int |
20cc1450 | 905 | httpStart(char *url, |
4768dbe0 | 906 | request_t * request, |
907 | char *req_hdr, | |
908 | int req_hdr_sz, | |
909 | StoreEntry * entry) | |
090089c4 | 910 | { |
911 | /* Create state structure. */ | |
a13d9042 | 912 | int sock; |
30a4f2a8 | 913 | HttpStateData *httpState = NULL; |
090089c4 | 914 | |
7111c86a | 915 | debug(11, 3, "httpStart: \"%s %s\"\n", |
916 | RequestMethodStr[request->method], url); | |
019dd986 | 917 | debug(11, 10, "httpStart: req_hdr '%s'\n", req_hdr); |
090089c4 | 918 | |
090089c4 | 919 | /* Create socket. */ |
16b204c4 | 920 | sock = comm_open(SOCK_STREAM, |
921 | 0, | |
922 | Config.Addrs.tcp_outgoing, | |
923 | 0, | |
924 | COMM_NONBLOCKING, | |
925 | url); | |
090089c4 | 926 | if (sock == COMM_ERROR) { |
019dd986 | 927 | debug(11, 4, "httpStart: Failed because we're out of sockets.\n"); |
b8de7ebe | 928 | squid_error_entry(entry, ERR_NO_FDS, xstrerror()); |
090089c4 | 929 | return COMM_ERROR; |
930 | } | |
30a4f2a8 | 931 | httpState = xcalloc(1, sizeof(HttpStateData)); |
932 | storeLockObject(httpState->entry = entry, NULL, NULL); | |
933 | httpState->req_hdr = req_hdr; | |
4768dbe0 | 934 | httpState->req_hdr_sz = req_hdr_sz; |
30a4f2a8 | 935 | httpState->request = requestLink(request); |
936 | comm_add_close_handler(sock, | |
b177367b | 937 | httpStateFree, |
30a4f2a8 | 938 | (void *) httpState); |
a13d9042 | 939 | ipcache_nbgethostbyname(request->host, |
940 | sock, | |
941 | httpConnect, | |
942 | httpState); | |
090089c4 | 943 | return COMM_OK; |
944 | } | |
30a4f2a8 | 945 | |
b8d8561b | 946 | void |
947 | httpReplyHeaderStats(StoreEntry * entry) | |
30a4f2a8 | 948 | { |
6fb52f6c | 949 | http_server_cc_t i; |
30a4f2a8 | 950 | storeAppendPrintf(entry, open_bracket); |
6fb52f6c | 951 | storeAppendPrintf(entry, "{HTTP Reply Headers:}\n"); |
952 | storeAppendPrintf(entry, "{ Headers parsed: %d}\n", | |
30a4f2a8 | 953 | ReplyHeaderStats.parsed); |
6fb52f6c | 954 | storeAppendPrintf(entry, "{ Date: %d}\n", |
30a4f2a8 | 955 | ReplyHeaderStats.date); |
6fb52f6c | 956 | storeAppendPrintf(entry, "{ Last-Modified: %d}\n", |
30a4f2a8 | 957 | ReplyHeaderStats.lm); |
6fb52f6c | 958 | storeAppendPrintf(entry, "{ Expires: %d}\n", |
30a4f2a8 | 959 | ReplyHeaderStats.exp); |
6fb52f6c | 960 | storeAppendPrintf(entry, "{ Content-Type: %d}\n", |
30a4f2a8 | 961 | ReplyHeaderStats.ctype); |
6fb52f6c | 962 | storeAppendPrintf(entry, "{ Content-Length: %d}\n", |
30a4f2a8 | 963 | ReplyHeaderStats.clen); |
4db43fab | 964 | for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++) |
6fb52f6c | 965 | storeAppendPrintf(entry, "{Cache-Control %7.7s: %d}\n", |
966 | HttpServerCCStr[i], | |
967 | ReplyHeaderStats.cc[i]); | |
30a4f2a8 | 968 | storeAppendPrintf(entry, close_bracket); |
969 | } |