]>
Commit | Line | Data |
---|---|---|
30a4f2a8 | 1 | /* |
b6c0e933 | 2 | * $Id: http.cc,v 1.152 1997/02/28 21:33:37 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 | |
4a83b852 | 106 | /* |
107 | * Anonymizing patch by lutz@as-node.jena.thur.de | |
de3bdb4c | 108 | * have a look into http-anon.c to get more informations. |
4a83b852 | 109 | */ |
110 | ||
44a47c6e | 111 | #include "squid.h" |
090089c4 | 112 | |
234967c9 | 113 | #define HTTP_DELETE_GAP (1<<18) |
090089c4 | 114 | |
4db43fab | 115 | static const char *const w_space = " \t\n\r"; |
6bf8443a | 116 | static const char *const crlf = "\r\n"; |
4db43fab | 117 | |
6fb52f6c | 118 | typedef enum { |
119 | SCC_PUBLIC, | |
120 | SCC_PRIVATE, | |
121 | SCC_NOCACHE, | |
122 | SCC_NOSTORE, | |
123 | SCC_NOTRANSFORM, | |
124 | SCC_MUSTREVALIDATE, | |
125 | SCC_PROXYREVALIDATE, | |
126 | SCC_MAXAGE, | |
127 | SCC_ENUM_END | |
128 | } http_server_cc_t; | |
129 | ||
6bf8443a | 130 | enum { |
6fb52f6c | 131 | CCC_NOCACHE, |
132 | CCC_NOSTORE, | |
133 | CCC_MAXAGE, | |
134 | CCC_MAXSTALE, | |
135 | CCC_MINFRESH, | |
136 | CCC_ONLYIFCACHED, | |
137 | CCC_ENUM_END | |
6bf8443a | 138 | }; |
139 | ||
151a0b6d | 140 | typedef enum { |
141 | HDR_ACCEPT, | |
142 | HDR_AGE, | |
143 | HDR_CONTENT_LENGTH, | |
144 | HDR_CONTENT_MD5, | |
145 | HDR_CONTENT_TYPE, | |
146 | HDR_DATE, | |
147 | HDR_ETAG, | |
148 | HDR_EXPIRES, | |
6bf8443a | 149 | HDR_HOST, |
151a0b6d | 150 | HDR_IMS, |
151 | HDR_LAST_MODIFIED, | |
152 | HDR_MAX_FORWARDS, | |
153 | HDR_PUBLIC, | |
154 | HDR_RETRY_AFTER, | |
155 | HDR_SET_COOKIE, | |
156 | HDR_UPGRADE, | |
157 | HDR_WARNING, | |
158 | HDR_MISC_END | |
159 | } http_hdr_misc_t; | |
6fb52f6c | 160 | |
0a0bf5db | 161 | typedef struct proxy_ctrl_t { |
162 | int sock; | |
0a0bf5db | 163 | request_t *orig_request; |
164 | StoreEntry *entry; | |
165 | peer *e; | |
166 | } proxy_ctrl_t; | |
167 | ||
168 | typedef struct http_ctrl_t { | |
169 | int sock; | |
170 | request_t *request; | |
171 | char *req_hdr; | |
172 | int req_hdr_sz; | |
173 | StoreEntry *entry; | |
174 | } http_ctrl_t; | |
175 | ||
6fb52f6c | 176 | char *HttpServerCCStr[] = |
177 | { | |
178 | "public", | |
179 | "private", | |
180 | "no-cache", | |
181 | "no-store", | |
182 | "no-transform", | |
183 | "must-revalidate", | |
184 | "proxy-revalidate", | |
185 | "max-age", | |
186 | "NONE" | |
187 | }; | |
188 | ||
151a0b6d | 189 | static char *HttpHdrMiscStr[] = |
190 | { | |
191 | "Accept", | |
192 | "Age", | |
193 | "Content-Length", | |
194 | "Content-MD5", | |
195 | "Content-Type", | |
196 | "Date", | |
197 | "Etag", | |
198 | "Expires", | |
199 | "Host", | |
200 | "If-Modified-Since", | |
201 | "Last-Modified", | |
202 | "Max-Forwards", | |
203 | "Public", | |
204 | "Retry-After", | |
205 | "Set-Cookie", | |
206 | "Upgrade", | |
207 | "Warning", | |
208 | "NONE" | |
209 | }; | |
210 | ||
24382924 | 211 | static struct { |
30a4f2a8 | 212 | int parsed; |
151a0b6d | 213 | int misc[HDR_MISC_END]; |
6fb52f6c | 214 | int cc[SCC_ENUM_END]; |
30a4f2a8 | 215 | } ReplyHeaderStats; |
090089c4 | 216 | |
b177367b | 217 | static void httpStateFree _PARAMS((int fd, void *)); |
218 | static void httpReadReplyTimeout _PARAMS((int fd, void *)); | |
219 | static void httpLifetimeExpire _PARAMS((int fd, void *)); | |
67508012 | 220 | static void httpMakePublic _PARAMS((StoreEntry *)); |
221 | static void httpMakePrivate _PARAMS((StoreEntry *)); | |
222 | static void httpCacheNegatively _PARAMS((StoreEntry *)); | |
b177367b | 223 | static void httpReadReply _PARAMS((int fd, void *)); |
67508012 | 224 | static void httpSendComplete _PARAMS((int fd, char *, int, int, void *)); |
0a0bf5db | 225 | static void proxyhttpStartComplete _PARAMS((void *, int)); |
226 | static void httpStartComplete _PARAMS((void *, int)); | |
b177367b | 227 | static void httpSendRequest _PARAMS((int fd, void *)); |
0ee4272b | 228 | static void httpConnect _PARAMS((int fd, const ipcache_addrs *, void *)); |
e5f6c5c2 | 229 | static void httpConnectDone _PARAMS((int fd, int status, void *data)); |
6bf8443a | 230 | static void httpAppendRequestHeader _PARAMS((char *hdr, const char *line, size_t * sz, size_t max)); |
231 | ||
b8d8561b | 232 | |
b177367b | 233 | static void |
234 | httpStateFree(int fd, void *data) | |
f5558c95 | 235 | { |
b177367b | 236 | HttpStateData *httpState = data; |
0d4d4170 | 237 | if (httpState == NULL) |
b177367b | 238 | return; |
30a4f2a8 | 239 | storeUnlockObject(httpState->entry); |
0d4d4170 | 240 | if (httpState->reply_hdr) { |
241 | put_free_8k_page(httpState->reply_hdr); | |
242 | httpState->reply_hdr = NULL; | |
243 | } | |
30a4f2a8 | 244 | requestUnlink(httpState->request); |
20cc1450 | 245 | requestUnlink(httpState->orig_request); |
0d4d4170 | 246 | xfree(httpState); |
f5558c95 | 247 | } |
248 | ||
b8d8561b | 249 | int |
75e88d56 | 250 | httpCachable(method_t method) |
090089c4 | 251 | { |
090089c4 | 252 | /* GET and HEAD are cachable. Others are not. */ |
6eb42cae | 253 | if (method != METHOD_GET && method != METHOD_HEAD) |
090089c4 | 254 | return 0; |
090089c4 | 255 | /* else cachable */ |
256 | return 1; | |
257 | } | |
258 | ||
259 | /* This will be called when timeout on read. */ | |
b8d8561b | 260 | static void |
b177367b | 261 | httpReadReplyTimeout(int fd, void *data) |
090089c4 | 262 | { |
b177367b | 263 | HttpStateData *httpState = data; |
090089c4 | 264 | StoreEntry *entry = NULL; |
30a4f2a8 | 265 | entry = httpState->entry; |
593c9a75 | 266 | debug(11, 4, "httpReadReplyTimeout: FD %d: '%s'\n", fd, entry->url); |
b8de7ebe | 267 | squid_error_entry(entry, ERR_READ_TIMEOUT, NULL); |
b177367b | 268 | commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); |
0d4d4170 | 269 | comm_close(fd); |
090089c4 | 270 | } |
271 | ||
272 | /* This will be called when socket lifetime is expired. */ | |
b8d8561b | 273 | static void |
b177367b | 274 | httpLifetimeExpire(int fd, void *data) |
090089c4 | 275 | { |
b177367b | 276 | HttpStateData *httpState = data; |
593c9a75 | 277 | StoreEntry *entry = httpState->entry; |
278 | debug(11, 4, "httpLifeTimeExpire: FD %d: '%s'\n", fd, entry->url); | |
ce49f524 | 279 | squid_error_entry(entry, ERR_LIFETIME_EXP, NULL); |
b177367b | 280 | commSetSelect(fd, COMM_SELECT_READ | COMM_SELECT_WRITE, NULL, NULL, 0); |
0d4d4170 | 281 | comm_close(fd); |
090089c4 | 282 | } |
283 | ||
30a4f2a8 | 284 | /* This object can be cached for a long time */ |
b8d8561b | 285 | static void |
286 | httpMakePublic(StoreEntry * entry) | |
30a4f2a8 | 287 | { |
1c481e00 | 288 | if (BIT_TEST(entry->flag, ENTRY_CACHABLE)) |
30a4f2a8 | 289 | storeSetPublicKey(entry); |
290 | } | |
291 | ||
292 | /* This object should never be cached at all */ | |
b8d8561b | 293 | static void |
294 | httpMakePrivate(StoreEntry * entry) | |
30a4f2a8 | 295 | { |
30a4f2a8 | 296 | storeExpireNow(entry); |
1c481e00 | 297 | BIT_RESET(entry->flag, ENTRY_CACHABLE); |
30a4f2a8 | 298 | storeReleaseRequest(entry); /* delete object when not used */ |
299 | } | |
300 | ||
301 | /* This object may be negatively cached */ | |
b8d8561b | 302 | static void |
303 | httpCacheNegatively(StoreEntry * entry) | |
30a4f2a8 | 304 | { |
79b5cc5f | 305 | storeNegativeCache(entry); |
1c481e00 | 306 | if (BIT_TEST(entry->flag, ENTRY_CACHABLE)) |
30a4f2a8 | 307 | storeSetPublicKey(entry); |
30a4f2a8 | 308 | } |
309 | ||
310 | ||
311 | /* Build a reply structure from HTTP reply headers */ | |
b8d8561b | 312 | void |
48f44632 | 313 | httpParseReplyHeaders(const char *buf, struct _http_reply *reply) |
30a4f2a8 | 314 | { |
33b589ff | 315 | char *headers = get_free_4k_page(); |
ca85027a | 316 | char *line; |
33b589ff | 317 | char *end; |
30a4f2a8 | 318 | char *s = NULL; |
33b589ff | 319 | char *t; |
ca98227c | 320 | time_t delta; |
321 | size_t l; | |
30a4f2a8 | 322 | |
f1494beb | 323 | reply->code = 600; |
30a4f2a8 | 324 | ReplyHeaderStats.parsed++; |
33b589ff | 325 | xstrncpy(headers, buf, 4096); |
326 | end = mime_headers_end(headers); | |
ca85027a | 327 | if (end == NULL) { |
328 | t = headers; | |
e2ad7f85 | 329 | if (!strncasecmp(t, "HTTP/", 5)) { |
330 | reply->version = atof(t + 5); | |
331 | if ((t = strchr(t, ' '))) | |
332 | reply->code = atoi(++t); | |
ca85027a | 333 | } |
e2ad7f85 | 334 | put_free_4k_page(headers); |
335 | return; | |
ca85027a | 336 | } |
337 | reply->hdr_sz = end - headers; | |
338 | line = get_free_4k_page(); | |
33b589ff | 339 | for (s = headers; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) { |
340 | l = strcspn(s, crlf) + 1; | |
341 | if (l > 4096) | |
342 | l = 4096; | |
343 | xstrncpy(line, s, l); | |
344 | t = line; | |
345 | debug(11, 3, "httpParseReplyHeaders: %s\n", t); | |
346 | if (!strncasecmp(t, "HTTP/", 5)) { | |
e2ad7f85 | 347 | reply->version = atof(t + 5); |
33b589ff | 348 | if ((t = strchr(t, ' '))) |
349 | reply->code = atoi(++t); | |
30a4f2a8 | 350 | } else if (!strncasecmp(t, "Content-type:", 13)) { |
33b589ff | 351 | for (t += 13; isspace(*t); t++); |
e91b56e5 | 352 | if ((l = strcspn(t, ";\t ")) > 0) |
353 | *(t + l) = '\0'; | |
33b589ff | 354 | xstrncpy(reply->content_type, t, HTTP_REPLY_FIELD_SZ); |
151a0b6d | 355 | ReplyHeaderStats.misc[HDR_CONTENT_TYPE]++; |
30a4f2a8 | 356 | } else if (!strncasecmp(t, "Content-length:", 15)) { |
33b589ff | 357 | for (t += 15; isspace(*t); t++); |
33b589ff | 358 | reply->content_length = atoi(t); |
151a0b6d | 359 | ReplyHeaderStats.misc[HDR_CONTENT_LENGTH]++; |
30a4f2a8 | 360 | } else if (!strncasecmp(t, "Date:", 5)) { |
33b589ff | 361 | for (t += 5; isspace(*t); t++); |
362 | reply->date = parse_rfc1123(t); | |
151a0b6d | 363 | ReplyHeaderStats.misc[HDR_DATE]++; |
30a4f2a8 | 364 | } else if (!strncasecmp(t, "Expires:", 8)) { |
33b589ff | 365 | for (t += 8; isspace(*t); t++); |
366 | reply->expires = parse_rfc1123(t); | |
367 | /* | |
368 | * The HTTP/1.0 specs says that robust implementations | |
369 | * should consider bad or malformed Expires header as | |
370 | * equivalent to "expires immediately." | |
371 | */ | |
372 | if (reply->expires == -1) | |
373 | reply->expires = squid_curtime; | |
151a0b6d | 374 | ReplyHeaderStats.misc[HDR_EXPIRES]++; |
30a4f2a8 | 375 | } else if (!strncasecmp(t, "Last-Modified:", 14)) { |
33b589ff | 376 | for (t += 14; isspace(*t); t++); |
377 | reply->last_modified = parse_rfc1123(t); | |
151a0b6d | 378 | ReplyHeaderStats.misc[HDR_LAST_MODIFIED]++; |
379 | } else if (!strncasecmp(t, "Accept:", 7)) { | |
380 | ReplyHeaderStats.misc[HDR_ACCEPT]++; | |
381 | } else if (!strncasecmp(t, "Age:", 4)) { | |
382 | ReplyHeaderStats.misc[HDR_AGE]++; | |
383 | } else if (!strncasecmp(t, "Content-MD5:", 12)) { | |
384 | ReplyHeaderStats.misc[HDR_CONTENT_MD5]++; | |
385 | } else if (!strncasecmp(t, "ETag:", 5)) { | |
386 | ReplyHeaderStats.misc[HDR_ETAG]++; | |
387 | } else if (!strncasecmp(t, "Max-Forwards:", 13)) { | |
388 | ReplyHeaderStats.misc[HDR_MAX_FORWARDS]++; | |
389 | } else if (!strncasecmp(t, "Public:", 7)) { | |
390 | ReplyHeaderStats.misc[HDR_PUBLIC]++; | |
391 | } else if (!strncasecmp(t, "Retry-After:", 12)) { | |
392 | ReplyHeaderStats.misc[HDR_RETRY_AFTER]++; | |
393 | } else if (!strncasecmp(t, "Upgrade:", 8)) { | |
394 | ReplyHeaderStats.misc[HDR_UPGRADE]++; | |
395 | } else if (!strncasecmp(t, "Warning:", 8)) { | |
396 | ReplyHeaderStats.misc[HDR_WARNING]++; | |
caebbe00 | 397 | } else if (!strncasecmp(t, "Cache-Control:", 14)) { |
33b589ff | 398 | for (t += 14; isspace(*t); t++); |
4db43fab | 399 | if (!strncasecmp(t, "public", 6)) { |
400 | EBIT_SET(reply->cache_control, SCC_PUBLIC); | |
401 | ReplyHeaderStats.cc[SCC_PUBLIC]++; | |
402 | } else if (!strncasecmp(t, "private", 7)) { | |
403 | EBIT_SET(reply->cache_control, SCC_PRIVATE); | |
404 | ReplyHeaderStats.cc[SCC_PRIVATE]++; | |
405 | } else if (!strncasecmp(t, "no-cache", 8)) { | |
406 | EBIT_SET(reply->cache_control, SCC_NOCACHE); | |
407 | ReplyHeaderStats.cc[SCC_NOCACHE]++; | |
c1764328 | 408 | } else if (!strncasecmp(t, "no-store", 8)) { |
409 | EBIT_SET(reply->cache_control, SCC_NOSTORE); | |
410 | ReplyHeaderStats.cc[SCC_NOSTORE]++; | |
411 | } else if (!strncasecmp(t, "no-transform", 12)) { | |
412 | EBIT_SET(reply->cache_control, SCC_NOTRANSFORM); | |
413 | ReplyHeaderStats.cc[SCC_NOTRANSFORM]++; | |
414 | } else if (!strncasecmp(t, "must-revalidate", 15)) { | |
415 | EBIT_SET(reply->cache_control, SCC_MUSTREVALIDATE); | |
416 | ReplyHeaderStats.cc[SCC_MUSTREVALIDATE]++; | |
417 | } else if (!strncasecmp(t, "proxy-revalidate", 16)) { | |
418 | EBIT_SET(reply->cache_control, SCC_PROXYREVALIDATE); | |
419 | ReplyHeaderStats.cc[SCC_PROXYREVALIDATE]++; | |
4db43fab | 420 | } else if (!strncasecmp(t, "max-age", 7)) { |
421 | if ((t = strchr(t, '='))) { | |
ca98227c | 422 | delta = (time_t) atoi(++t); |
423 | reply->expires = squid_curtime + delta; | |
4db43fab | 424 | EBIT_SET(reply->cache_control, SCC_MAXAGE); |
425 | ReplyHeaderStats.cc[SCC_MAXAGE]++; | |
5df61230 | 426 | } |
caebbe00 | 427 | } |
df3ac7c0 | 428 | } else if (!strncasecmp(t, "Set-Cookie:", 11)) { |
429 | EBIT_SET(reply->misc_headers, HDR_SET_COOKIE); | |
58202be7 | 430 | ReplyHeaderStats.misc[HDR_SET_COOKIE]++; |
30a4f2a8 | 431 | } |
30a4f2a8 | 432 | } |
33b589ff | 433 | put_free_4k_page(headers); |
434 | put_free_4k_page(line); | |
30a4f2a8 | 435 | } |
436 | ||
090089c4 | 437 | |
b8d8561b | 438 | void |
0ee4272b | 439 | httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) |
f5558c95 | 440 | { |
441 | char *t = NULL; | |
30a4f2a8 | 442 | StoreEntry *entry = httpState->entry; |
d3fb4dea | 443 | int room; |
444 | int hdr_len; | |
33b589ff | 445 | struct _http_reply *reply = entry->mem_obj->reply; |
d3fb4dea | 446 | |
ed85b771 | 447 | debug(11, 3, "httpProcessReplyHeader: key '%s'\n", entry->key); |
f5558c95 | 448 | |
30a4f2a8 | 449 | if (httpState->reply_hdr == NULL) { |
450 | httpState->reply_hdr = get_free_8k_page(); | |
451 | memset(httpState->reply_hdr, '\0', 8192); | |
f5558c95 | 452 | } |
30a4f2a8 | 453 | if (httpState->reply_hdr_state == 0) { |
454 | hdr_len = strlen(httpState->reply_hdr); | |
ed85b771 | 455 | room = 8191 - hdr_len; |
30a4f2a8 | 456 | strncat(httpState->reply_hdr, buf, room < size ? room : size); |
d3fb4dea | 457 | hdr_len += room < size ? room : size; |
30a4f2a8 | 458 | if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) { |
60bf30cb | 459 | debug(11, 3, "httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", entry->key); |
30a4f2a8 | 460 | httpState->reply_hdr_state += 2; |
33b589ff | 461 | reply->code = 555; |
ed85b771 | 462 | return; |
d3fb4dea | 463 | } |
d1a43e28 | 464 | t = httpState->reply_hdr + hdr_len; |
465 | /* headers can be incomplete only if object still arriving */ | |
f86a6a46 | 466 | if (!httpState->eof) |
d1a43e28 | 467 | if ((t = mime_headers_end(httpState->reply_hdr)) == NULL) |
468 | return; /* headers not complete */ | |
2285407f | 469 | *t = '\0'; |
30a4f2a8 | 470 | httpState->reply_hdr_state++; |
f5558c95 | 471 | } |
30a4f2a8 | 472 | if (httpState->reply_hdr_state == 1) { |
473 | httpState->reply_hdr_state++; | |
019dd986 | 474 | debug(11, 9, "GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", |
30a4f2a8 | 475 | httpState->reply_hdr); |
476 | /* Parse headers into reply structure */ | |
48f44632 | 477 | httpParseReplyHeaders(httpState->reply_hdr, reply); |
ca98227c | 478 | storeTimestampsSet(entry); |
30a4f2a8 | 479 | /* Check if object is cacheable or not based on reply code */ |
cb24292d | 480 | debug(11, 3, "httpProcessReplyHeader: HTTP CODE: %d\n", reply->code); |
2285407f | 481 | switch (reply->code) { |
30a4f2a8 | 482 | /* Responses that are cacheable */ |
f5558c95 | 483 | case 200: /* OK */ |
4e38e700 | 484 | case 203: /* Non-Authoritative Information */ |
485 | case 300: /* Multiple Choices */ | |
f5558c95 | 486 | case 301: /* Moved Permanently */ |
4e38e700 | 487 | case 410: /* Gone */ |
30a4f2a8 | 488 | /* don't cache objects from neighbors w/o LMT, Date, or Expires */ |
6fb52f6c | 489 | if (EBIT_TEST(reply->cache_control, SCC_PRIVATE)) |
caebbe00 | 490 | httpMakePrivate(entry); |
6fb52f6c | 491 | else if (EBIT_TEST(reply->cache_control, SCC_NOCACHE)) |
caebbe00 | 492 | httpMakePrivate(entry); |
e2ad7f85 | 493 | /* |
494 | * Dealing with cookies is quite a bit more complicated | |
495 | * than this. Ideally we should strip the cookie | |
496 | * header from the reply but still cache the reply body. | |
497 | * More confusion at draft-ietf-http-state-mgmt-05.txt. | |
498 | */ | |
df3ac7c0 | 499 | else if (EBIT_TEST(reply->misc_headers, HDR_SET_COOKIE)) |
500 | httpMakePrivate(entry); | |
ca98227c | 501 | else if (reply->date > -1) |
30a4f2a8 | 502 | httpMakePublic(entry); |
ca98227c | 503 | else if (reply->last_modified > -1) |
30a4f2a8 | 504 | httpMakePublic(entry); |
505 | else if (!httpState->neighbor) | |
506 | httpMakePublic(entry); | |
ca98227c | 507 | else if (reply->expires > -1) |
30a4f2a8 | 508 | httpMakePublic(entry); |
af00901c | 509 | else if (entry->mem_obj->request->protocol != PROTO_HTTP) |
510 | /* XXX Remove this check after a while. DW 8/21/96 | |
511 | * We won't keep some FTP objects from neighbors running | |
512 | * 1.0.8 or earlier because their ftpget's don't | |
513 | * add a Date: field */ | |
514 | httpMakePublic(entry); | |
30a4f2a8 | 515 | else |
516 | httpMakePrivate(entry); | |
517 | break; | |
518 | /* Responses that only are cacheable if the server says so */ | |
519 | case 302: /* Moved temporarily */ | |
ca98227c | 520 | if (reply->expires > -1) |
30a4f2a8 | 521 | httpMakePublic(entry); |
522 | else | |
523 | httpMakePrivate(entry); | |
f5558c95 | 524 | break; |
30a4f2a8 | 525 | /* Errors can be negatively cached */ |
526 | case 204: /* No Content */ | |
527 | case 305: /* Use Proxy (proxy redirect) */ | |
528 | case 400: /* Bad Request */ | |
529 | case 403: /* Forbidden */ | |
530 | case 404: /* Not Found */ | |
531 | case 405: /* Method Now Allowed */ | |
532 | case 414: /* Request-URI Too Long */ | |
533 | case 500: /* Internal Server Error */ | |
534 | case 501: /* Not Implemented */ | |
535 | case 502: /* Bad Gateway */ | |
536 | case 503: /* Service Unavailable */ | |
537 | case 504: /* Gateway Timeout */ | |
851eeef7 | 538 | httpCacheNegatively(entry); |
30a4f2a8 | 539 | break; |
540 | /* Some responses can never be cached */ | |
541 | case 303: /* See Other */ | |
234967c9 | 542 | case 304: /* Not Modified */ |
4e38e700 | 543 | case 401: /* Unauthorized */ |
544 | case 407: /* Proxy Authentication Required */ | |
f1494beb | 545 | case 600: /* Squid header parsing error */ |
30a4f2a8 | 546 | default: /* Unknown status code */ |
547 | httpMakePrivate(entry); | |
4e38e700 | 548 | break; |
f5558c95 | 549 | } |
550 | } | |
551 | } | |
552 | ||
090089c4 | 553 | |
554 | /* This will be called when data is ready to be read from fd. Read until | |
555 | * error or connection closed. */ | |
f5558c95 | 556 | /* XXX this function is too long! */ |
b8d8561b | 557 | static void |
b177367b | 558 | httpReadReply(int fd, void *data) |
090089c4 | 559 | { |
b177367b | 560 | HttpStateData *httpState = data; |
95d659f0 | 561 | LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF); |
090089c4 | 562 | int len; |
30a4f2a8 | 563 | int bin; |
090089c4 | 564 | int clen; |
565 | int off; | |
566 | StoreEntry *entry = NULL; | |
567 | ||
30a4f2a8 | 568 | entry = httpState->entry; |
234967c9 | 569 | if (entry->flag & DELETE_BEHIND && !storeClientWaiting(entry)) { |
570 | /* we can terminate connection right now */ | |
571 | squid_error_entry(entry, ERR_NO_CLIENTS_BIG_OBJ, NULL); | |
572 | comm_close(fd); | |
573 | return; | |
574 | } | |
575 | /* check if we want to defer reading */ | |
576 | clen = entry->mem_obj->e_current_len; | |
577 | off = storeGetLowestReaderOffset(entry); | |
578 | if ((clen - off) > HTTP_DELETE_GAP) { | |
30a4f2a8 | 579 | if (entry->flag & CLIENT_ABORT_REQUEST) { |
580 | squid_error_entry(entry, ERR_CLIENT_ABORT, NULL); | |
581 | comm_close(fd); | |
582 | return; | |
583 | } | |
584 | IOStats.Http.reads_deferred++; | |
234967c9 | 585 | debug(11, 3, "httpReadReply: Read deferred for Object: %s\n", |
586 | entry->url); | |
587 | debug(11, 3, " Current Gap: %d bytes\n", clen - off); | |
588 | /* reschedule, so it will be automatically reactivated | |
589 | * when Gap is big enough. */ | |
b177367b | 590 | commSetSelect(fd, |
234967c9 | 591 | COMM_SELECT_READ, |
b177367b | 592 | httpReadReply, |
593 | (void *) httpState, 0); | |
30a4f2a8 | 594 | /* disable read timeout until we are below the GAP */ |
b177367b | 595 | commSetSelect(fd, |
234967c9 | 596 | COMM_SELECT_TIMEOUT, |
b177367b | 597 | NULL, |
234967c9 | 598 | (void *) NULL, |
599 | (time_t) 0); | |
56fa4cad | 600 | if (!BIT_TEST(entry->flag, READ_DEFERRED)) { |
601 | comm_set_fd_lifetime(fd, 3600); /* limit during deferring */ | |
602 | BIT_SET(entry->flag, READ_DEFERRED); | |
603 | } | |
234967c9 | 604 | /* dont try reading again for a while */ |
b6f794d6 | 605 | comm_set_stall(fd, Config.stallDelay); |
234967c9 | 606 | return; |
56fa4cad | 607 | } else { |
608 | BIT_RESET(entry->flag, READ_DEFERRED); | |
090089c4 | 609 | } |
1513873c | 610 | errno = 0; |
30a4f2a8 | 611 | len = read(fd, buf, SQUID_TCP_SO_RCVBUF); |
019dd986 | 612 | debug(11, 5, "httpReadReply: FD %d: len %d.\n", fd, len); |
30a4f2a8 | 613 | comm_set_fd_lifetime(fd, 86400); /* extend after good read */ |
614 | if (len > 0) { | |
4a63c85f | 615 | IOStats.Http.reads++; |
30a4f2a8 | 616 | for (clen = len - 1, bin = 0; clen; bin++) |
617 | clen >>= 1; | |
618 | IOStats.Http.read_hist[bin]++; | |
619 | } | |
ba718c8f | 620 | if (len < 0) { |
0a0bf5db | 621 | if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { |
1513873c | 622 | /* reinstall handlers */ |
6fe6313d | 623 | /* XXX This may loop forever */ |
b177367b | 624 | commSetSelect(fd, COMM_SELECT_READ, |
625 | httpReadReply, (void *) httpState, 0); | |
626 | commSetSelect(fd, COMM_SELECT_TIMEOUT, | |
627 | httpReadReplyTimeout, (void *) httpState, Config.readTimeout); | |
090089c4 | 628 | } else { |
1c481e00 | 629 | BIT_RESET(entry->flag, ENTRY_CACHABLE); |
2daae136 | 630 | storeReleaseRequest(entry); |
b8de7ebe | 631 | squid_error_entry(entry, ERR_READ_ERROR, xstrerror()); |
0d4d4170 | 632 | comm_close(fd); |
090089c4 | 633 | } |
0a0bf5db | 634 | debug(50, 2, "httpReadReply: FD %d: read failure: %s.\n", |
635 | fd, xstrerror()); | |
ba718c8f | 636 | } else if (len == 0 && entry->mem_obj->e_current_len == 0) { |
f86a6a46 | 637 | httpState->eof = 1; |
b8de7ebe | 638 | squid_error_entry(entry, |
ba718c8f | 639 | ERR_ZERO_SIZE_OBJECT, |
640 | errno ? xstrerror() : NULL); | |
0d4d4170 | 641 | comm_close(fd); |
090089c4 | 642 | } else if (len == 0) { |
643 | /* Connection closed; retrieval done. */ | |
f86a6a46 | 644 | httpState->eof = 1; |
d1a43e28 | 645 | if (httpState->reply_hdr_state < 2) |
646 | httpProcessReplyHeader(httpState, buf, len); | |
647 | storeAppend(entry, buf, len); /* invoke handlers! */ | |
648 | storeComplete(entry); /* deallocates mem_obj->request */ | |
0d4d4170 | 649 | comm_close(fd); |
090089c4 | 650 | } else if (entry->flag & CLIENT_ABORT_REQUEST) { |
651 | /* append the last bit of info we get */ | |
652 | storeAppend(entry, buf, len); | |
b8de7ebe | 653 | squid_error_entry(entry, ERR_CLIENT_ABORT, NULL); |
0d4d4170 | 654 | comm_close(fd); |
090089c4 | 655 | } else { |
d1a43e28 | 656 | if (httpState->reply_hdr_state < 2) |
30a4f2a8 | 657 | httpProcessReplyHeader(httpState, buf, len); |
620da955 | 658 | storeAppend(entry, buf, len); |
b177367b | 659 | commSetSelect(fd, |
ba718c8f | 660 | COMM_SELECT_TIMEOUT, |
b177367b | 661 | httpReadReplyTimeout, |
30a4f2a8 | 662 | (void *) httpState, |
b6f794d6 | 663 | Config.readTimeout); |
0a0bf5db | 664 | commSetSelect(fd, |
665 | COMM_SELECT_READ, | |
666 | httpReadReply, | |
667 | (void *) httpState, 0); | |
090089c4 | 668 | } |
669 | } | |
670 | ||
671 | /* This will be called when request write is complete. Schedule read of | |
672 | * reply. */ | |
b8d8561b | 673 | static void |
674 | httpSendComplete(int fd, char *buf, int size, int errflag, void *data) | |
090089c4 | 675 | { |
30a4f2a8 | 676 | HttpStateData *httpState = data; |
090089c4 | 677 | StoreEntry *entry = NULL; |
678 | ||
30a4f2a8 | 679 | entry = httpState->entry; |
019dd986 | 680 | debug(11, 5, "httpSendComplete: FD %d: size %d: errflag %d.\n", |
090089c4 | 681 | fd, size, errflag); |
682 | ||
090089c4 | 683 | if (errflag) { |
b8de7ebe | 684 | squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror()); |
0d4d4170 | 685 | comm_close(fd); |
090089c4 | 686 | return; |
687 | } else { | |
688 | /* Schedule read reply. */ | |
b177367b | 689 | commSetSelect(fd, |
019dd986 | 690 | COMM_SELECT_READ, |
b177367b | 691 | httpReadReply, |
692 | (void *) httpState, 0); | |
693 | commSetSelect(fd, | |
019dd986 | 694 | COMM_SELECT_TIMEOUT, |
b177367b | 695 | httpReadReplyTimeout, |
30a4f2a8 | 696 | (void *) httpState, |
b6f794d6 | 697 | Config.readTimeout); |
30a4f2a8 | 698 | comm_set_fd_lifetime(fd, 86400); /* extend lifetime */ |
090089c4 | 699 | } |
700 | } | |
701 | ||
6bf8443a | 702 | static void |
703 | httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max) | |
704 | { | |
705 | size_t n = *sz + strlen(line) + 2; | |
706 | if (n >= max) | |
707 | return; | |
fa562c67 | 708 | if (Config.Options.anonymizer == ANONYMIZER_PARANOID) { |
bba6fa8f | 709 | if (!httpAnonAllowed(line)) |
fa562c67 | 710 | return; |
711 | } else if (Config.Options.anonymizer == ANONYMIZER_STANDARD) { | |
bba6fa8f | 712 | if (httpAnonDenied(line)) |
fa562c67 | 713 | return; |
4a83b852 | 714 | } |
4a83b852 | 715 | /* allowed header, explicitly known to be not dangerous */ |
6bf8443a | 716 | debug(11, 5, "httpAppendRequestHeader: %s\n", line); |
929545fe | 717 | strcpy(hdr + (*sz), line); |
6bf8443a | 718 | strcat(hdr + (*sz), crlf); |
719 | *sz = n; | |
720 | } | |
721 | ||
722 | size_t | |
723 | httpBuildRequestHeader(request_t * request, | |
724 | request_t * orig_request, | |
725 | StoreEntry * entry, | |
726 | char *hdr_in, | |
727 | size_t * in_len, | |
728 | char *hdr_out, | |
729 | size_t out_sz, | |
730 | int cfd) | |
731 | { | |
732 | char *xbuf = get_free_4k_page(); | |
872b720f | 733 | char *ybuf = get_free_8k_page(); |
6bf8443a | 734 | char *viabuf = get_free_4k_page(); |
735 | char *fwdbuf = get_free_4k_page(); | |
736 | char *t = NULL; | |
737 | char *s = NULL; | |
738 | char *end = NULL; | |
739 | size_t len = 0; | |
740 | size_t hdr_len = 0; | |
741 | size_t in_sz; | |
6bf8443a | 742 | size_t l; |
743 | int hdr_flags = 0; | |
151a0b6d | 744 | int cc_flags = 0; |
b3b64e58 | 745 | int n; |
6bf8443a | 746 | const char *url = NULL; |
747 | ||
748 | debug(11, 3, "httpBuildRequestHeader: INPUT:\n%s\n", hdr_in); | |
749 | xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096); | |
750 | xstrncpy(viabuf, "Via: ", 4096); | |
751 | sprintf(ybuf, "%s %s HTTP/1.0", | |
752 | RequestMethodStr[request->method], | |
753 | *request->urlpath ? request->urlpath : "/"); | |
754 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz); | |
755 | /* Add IMS header */ | |
756 | if (entry && entry->lastmod && request->method == METHOD_GET) { | |
757 | sprintf(ybuf, "If-Modified-Since: %s", mkrfc1123(entry->lastmod)); | |
758 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz); | |
759 | EBIT_SET(hdr_flags, HDR_IMS); | |
760 | } | |
00c59270 | 761 | end = mime_headers_end(hdr_in); |
6bf8443a | 762 | in_sz = strlen(hdr_in); |
763 | for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) { | |
764 | hdr_len = t - hdr_in; | |
6bf8443a | 765 | l = strcspn(t, crlf) + 1; |
766 | if (l > 4096) | |
767 | l = 4096; | |
768 | xstrncpy(xbuf, t, l); | |
a7011ca4 | 769 | debug(11, 5, "httpBuildRequestHeader: %s\n", xbuf); |
6bf8443a | 770 | if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0) |
771 | continue; | |
772 | if (strncasecmp(xbuf, "Connection:", 11) == 0) | |
773 | continue; | |
66f7337b | 774 | if (strncasecmp(xbuf, "Host:", 5) == 0) { |
6bf8443a | 775 | EBIT_SET(hdr_flags, HDR_HOST); |
66f7337b | 776 | } else if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) { |
6bf8443a | 777 | for (s = xbuf + 14; *s && isspace(*s); s++); |
778 | if (strncasecmp(s, "Max-age=", 8) == 0) | |
151a0b6d | 779 | EBIT_SET(cc_flags, CCC_MAXAGE); |
66f7337b | 780 | } else if (strncasecmp(xbuf, "Via:", 4) == 0) { |
6bf8443a | 781 | for (s = xbuf + 4; *s && isspace(*s); s++); |
782 | if (strlen(viabuf) + strlen(s) < 4000) | |
783 | strcat(viabuf, s); | |
784 | strcat(viabuf, ", "); | |
785 | continue; | |
66f7337b | 786 | } else if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) { |
6bf8443a | 787 | for (s = xbuf + 16; *s && isspace(*s); s++); |
788 | if (strlen(fwdbuf) + strlen(s) < 4000) | |
789 | strcat(fwdbuf, s); | |
790 | strcat(fwdbuf, ", "); | |
791 | continue; | |
66f7337b | 792 | } else if (strncasecmp(xbuf, "If-Modified-Since:", 18) == 0) { |
6bf8443a | 793 | if (EBIT_TEST(hdr_flags, HDR_IMS)) |
794 | continue; | |
b3b64e58 | 795 | } else if (strncasecmp(xbuf, "Max-Forwards:", 13) == 0) { |
796 | if (orig_request->method == METHOD_TRACE) { | |
797 | for (s = xbuf + 13; *s && isspace(*s); s++); | |
798 | n = atoi(s); | |
799 | sprintf(xbuf, "Max-Forwards: %d", n - 1); | |
800 | } | |
66f7337b | 801 | } |
6bf8443a | 802 | httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512); |
803 | } | |
804 | hdr_len = t - hdr_in; | |
805 | /* Append Via: */ | |
43f1c650 | 806 | sprintf(ybuf, "%3.1f %s", orig_request->http_ver, ThisCache); |
6bf8443a | 807 | strcat(viabuf, ybuf); |
808 | httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz); | |
809 | /* Append to X-Forwarded-For: */ | |
a08307eb | 810 | strcat(fwdbuf, cfd < 0 ? "unknown" : fd_table[cfd].ipaddr); |
6bf8443a | 811 | httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz); |
812 | if (!EBIT_TEST(hdr_flags, HDR_HOST)) { | |
813 | sprintf(ybuf, "Host: %s", orig_request->host); | |
814 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz); | |
815 | } | |
151a0b6d | 816 | if (!EBIT_TEST(cc_flags, CCC_MAXAGE)) { |
6bf8443a | 817 | url = entry ? entry->url : urlCanonical(orig_request, NULL); |
818 | sprintf(ybuf, "Cache-control: Max-age=%d", (int) getMaxAge(url)); | |
819 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz); | |
820 | } | |
821 | httpAppendRequestHeader(hdr_out, null_string, &len, out_sz); | |
822 | put_free_4k_page(xbuf); | |
872b720f | 823 | put_free_8k_page(ybuf); |
6bf8443a | 824 | put_free_4k_page(viabuf); |
825 | put_free_4k_page(fwdbuf); | |
826 | if (in_len) | |
827 | *in_len = hdr_len; | |
9d9d144b | 828 | if ((l = strlen(hdr_out)) != len) { |
829 | debug_trap("httpBuildRequestHeader: size mismatch"); | |
830 | len = l; | |
831 | } | |
6bf8443a | 832 | debug(11, 3, "httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out); |
833 | return len; | |
834 | } | |
835 | ||
090089c4 | 836 | /* This will be called when connect completes. Write request. */ |
b8d8561b | 837 | static void |
b177367b | 838 | httpSendRequest(int fd, void *data) |
090089c4 | 839 | { |
b177367b | 840 | HttpStateData *httpState = data; |
090089c4 | 841 | char *buf = NULL; |
090089c4 | 842 | int len = 0; |
843 | int buflen; | |
30a4f2a8 | 844 | request_t *req = httpState->request; |
9864ee44 | 845 | int buftype = 0; |
620da955 | 846 | StoreEntry *entry = httpState->entry; |
2a26c096 | 847 | int cfd; |
090089c4 | 848 | |
30a4f2a8 | 849 | debug(11, 5, "httpSendRequest: FD %d: httpState %p.\n", fd, httpState); |
6bf8443a | 850 | buflen = strlen(req->urlpath); |
30a4f2a8 | 851 | if (httpState->req_hdr) |
4768dbe0 | 852 | buflen += httpState->req_hdr_sz + 1; |
090089c4 | 853 | buflen += 512; /* lots of extra */ |
854 | ||
78657218 | 855 | if ((req->method == METHOD_POST || req->method == METHOD_PUT)) { |
856 | debug_trap("httpSendRequest: should not be handling POST/PUT request"); | |
857 | return; | |
090089c4 | 858 | } |
30a4f2a8 | 859 | if (buflen < DISK_PAGE_SIZE) { |
9864ee44 | 860 | buf = get_free_8k_page(); |
861 | memset(buf, '\0', buflen); | |
862 | buftype = BUF_TYPE_8K; | |
6bf8443a | 863 | buflen = DISK_PAGE_SIZE; |
30a4f2a8 | 864 | } else { |
9864ee44 | 865 | buf = xcalloc(buflen, 1); |
866 | buftype = BUF_TYPE_MALLOC; | |
090089c4 | 867 | } |
2a26c096 | 868 | if (!opt_forwarded_for) |
6bf8443a | 869 | cfd = -1; |
2a26c096 | 870 | else if (entry->mem_obj == NULL) |
6bf8443a | 871 | cfd = -1; |
2a26c096 | 872 | else |
6bf8443a | 873 | cfd = storeFirstClientFD(entry->mem_obj); |
874 | len = httpBuildRequestHeader(req, | |
875 | httpState->orig_request ? httpState->orig_request : req, | |
876 | entry, | |
877 | httpState->req_hdr, | |
878 | NULL, | |
879 | buf, | |
880 | buflen, | |
881 | cfd); | |
67508012 | 882 | debug(11, 6, "httpSendRequest: FD %d:\n%s\n", fd, buf); |
30a4f2a8 | 883 | comm_write(fd, |
14e59844 | 884 | buf, |
885 | len, | |
886 | 30, | |
887 | httpSendComplete, | |
9864ee44 | 888 | httpState, |
889 | buftype == BUF_TYPE_8K ? put_free_8k_page : xfree); | |
20cc1450 | 890 | requestUnlink(httpState->orig_request); |
891 | httpState->orig_request = NULL; | |
090089c4 | 892 | } |
893 | ||
b8d8561b | 894 | int |
75e88d56 | 895 | proxyhttpStart(request_t * orig_request, |
20cc1450 | 896 | StoreEntry * entry, |
deb79f06 | 897 | peer * e) |
090089c4 | 898 | { |
0a0bf5db | 899 | proxy_ctrl_t *ctrlp; |
f5558c95 | 900 | int sock; |
7111c86a | 901 | debug(11, 3, "proxyhttpStart: \"%s %s\"\n", |
75e88d56 | 902 | RequestMethodStr[orig_request->method], entry->url); |
019dd986 | 903 | debug(11, 10, "proxyhttpStart: HTTP request header:\n%s\n", |
22e4fa85 | 904 | entry->mem_obj->mime_hdr); |
30a4f2a8 | 905 | if (e->options & NEIGHBOR_PROXY_ONLY) |
090089c4 | 906 | storeStartDeleteBehind(entry); |
090089c4 | 907 | /* Create socket. */ |
16b204c4 | 908 | sock = comm_open(SOCK_STREAM, |
909 | 0, | |
910 | Config.Addrs.tcp_outgoing, | |
911 | 0, | |
912 | COMM_NONBLOCKING, | |
75e88d56 | 913 | entry->url); |
090089c4 | 914 | if (sock == COMM_ERROR) { |
019dd986 | 915 | debug(11, 4, "proxyhttpStart: Failed because we're out of sockets.\n"); |
b8de7ebe | 916 | squid_error_entry(entry, ERR_NO_FDS, xstrerror()); |
090089c4 | 917 | return COMM_ERROR; |
918 | } | |
0a0bf5db | 919 | ctrlp = xmalloc(sizeof(proxy_ctrl_t)); |
920 | ctrlp->sock = sock; | |
0a0bf5db | 921 | ctrlp->orig_request = orig_request; |
922 | ctrlp->entry = entry; | |
923 | ctrlp->e = e; | |
924 | storeLockObject(entry, proxyhttpStartComplete, ctrlp); | |
925 | return COMM_OK; | |
926 | } | |
927 | ||
928 | ||
929 | static void | |
930 | proxyhttpStartComplete(void *data, int status) | |
931 | { | |
75e88d56 | 932 | proxy_ctrl_t *ctrlp = data; |
933 | int sock = ctrlp->sock; | |
934 | request_t *orig_request = ctrlp->orig_request; | |
935 | StoreEntry *entry = ctrlp->entry; | |
936 | peer *e = ctrlp->e; | |
937 | char *url = entry->url; | |
0a0bf5db | 938 | HttpStateData *httpState = NULL; |
939 | request_t *request = NULL; | |
0a0bf5db | 940 | xfree(ctrlp); |
30a4f2a8 | 941 | httpState = xcalloc(1, sizeof(HttpStateData)); |
0a0bf5db | 942 | httpState->entry = entry; |
30a4f2a8 | 943 | httpState->req_hdr = entry->mem_obj->mime_hdr; |
4768dbe0 | 944 | httpState->req_hdr_sz = entry->mem_obj->mime_hdr_sz; |
30a4f2a8 | 945 | request = get_free_request_t(); |
946 | httpState->request = requestLink(request); | |
947 | httpState->neighbor = e; | |
20cc1450 | 948 | httpState->orig_request = requestLink(orig_request); |
0d4d4170 | 949 | /* register the handler to free HTTP state data when the FD closes */ |
30a4f2a8 | 950 | comm_add_close_handler(sock, |
b177367b | 951 | httpStateFree, |
30a4f2a8 | 952 | (void *) httpState); |
2cc3f720 | 953 | request->method = orig_request->method; |
d5aa0e3b | 954 | xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN); |
30a4f2a8 | 955 | request->port = e->http_port; |
d5aa0e3b | 956 | xstrncpy(request->urlpath, url, MAX_URL); |
313c1c28 | 957 | BIT_SET(request->flags, REQ_PROXYING); |
a13d9042 | 958 | ipcache_nbgethostbyname(request->host, |
959 | sock, | |
b15fe823 | 960 | httpConnect, |
a13d9042 | 961 | httpState); |
0a0bf5db | 962 | return; |
a13d9042 | 963 | } |
964 | ||
b15fe823 | 965 | static void |
fe4e214f | 966 | httpConnect(int fd, const ipcache_addrs * ia, void *data) |
a13d9042 | 967 | { |
968 | HttpStateData *httpState = data; | |
969 | request_t *request = httpState->request; | |
970 | StoreEntry *entry = httpState->entry; | |
e5f6c5c2 | 971 | if (ia == NULL) { |
a13d9042 | 972 | debug(11, 4, "httpConnect: Unknown host: %s\n", request->host); |
b8de7ebe | 973 | squid_error_entry(entry, ERR_DNS_FAIL, dns_error_message); |
a13d9042 | 974 | comm_close(fd); |
b15fe823 | 975 | return; |
090089c4 | 976 | } |
977 | /* Open connection. */ | |
e5f6c5c2 | 978 | httpState->connectState.fd = fd; |
979 | httpState->connectState.host = request->host; | |
980 | httpState->connectState.port = request->port; | |
981 | httpState->connectState.handler = httpConnectDone; | |
982 | httpState->connectState.data = httpState; | |
983 | comm_nbconnect(fd, &httpState->connectState); | |
984 | } | |
985 | ||
986 | static void | |
987 | httpConnectDone(int fd, int status, void *data) | |
988 | { | |
989 | HttpStateData *httpState = data; | |
990 | request_t *request = httpState->request; | |
991 | StoreEntry *entry = httpState->entry; | |
deb79f06 | 992 | peer *e = NULL; |
e5f6c5c2 | 993 | if (status != COMM_OK) { |
5269d0bd | 994 | if ((e = httpState->neighbor)) |
e5f6c5c2 | 995 | e->last_fail_time = squid_curtime; |
e5f6c5c2 | 996 | squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror()); |
997 | comm_close(fd); | |
998 | } else { | |
999 | /* Install connection complete handler. */ | |
1000 | if (opt_no_ipcache) | |
1001 | ipcacheInvalidate(request->host); | |
1002 | fd_note(fd, entry->url); | |
b177367b | 1003 | commSetSelect(fd, COMM_SELECT_LIFETIME, |
1004 | httpLifetimeExpire, (void *) httpState, 0); | |
1005 | commSetSelect(fd, COMM_SELECT_WRITE, | |
1006 | httpSendRequest, (void *) httpState, 0); | |
9d4b2981 | 1007 | if (vizSock > -1) |
ef2d27ff | 1008 | vizHackSendPkt(&httpState->connectState.S, 2); |
090089c4 | 1009 | } |
090089c4 | 1010 | } |
1011 | ||
b8d8561b | 1012 | int |
75e88d56 | 1013 | httpStart(request_t * request, |
4768dbe0 | 1014 | char *req_hdr, |
1015 | int req_hdr_sz, | |
1016 | StoreEntry * entry) | |
090089c4 | 1017 | { |
0a0bf5db | 1018 | http_ctrl_t *ctrlp; |
a13d9042 | 1019 | int sock; |
7111c86a | 1020 | debug(11, 3, "httpStart: \"%s %s\"\n", |
75e88d56 | 1021 | RequestMethodStr[request->method], entry->url); |
019dd986 | 1022 | debug(11, 10, "httpStart: req_hdr '%s'\n", req_hdr); |
090089c4 | 1023 | /* Create socket. */ |
16b204c4 | 1024 | sock = comm_open(SOCK_STREAM, |
1025 | 0, | |
1026 | Config.Addrs.tcp_outgoing, | |
1027 | 0, | |
1028 | COMM_NONBLOCKING, | |
75e88d56 | 1029 | entry->url); |
090089c4 | 1030 | if (sock == COMM_ERROR) { |
019dd986 | 1031 | debug(11, 4, "httpStart: Failed because we're out of sockets.\n"); |
b8de7ebe | 1032 | squid_error_entry(entry, ERR_NO_FDS, xstrerror()); |
090089c4 | 1033 | return COMM_ERROR; |
1034 | } | |
0a0bf5db | 1035 | ctrlp = xmalloc(sizeof(http_ctrl_t)); |
1036 | ctrlp->sock = sock; | |
1037 | ctrlp->request = request; | |
1038 | ctrlp->req_hdr = req_hdr; | |
1039 | ctrlp->req_hdr_sz = req_hdr_sz; | |
1040 | ctrlp->entry = entry; | |
1041 | storeLockObject(entry, httpStartComplete, ctrlp); | |
1042 | return COMM_OK; | |
1043 | } | |
1044 | ||
1045 | ||
1046 | static void | |
1047 | httpStartComplete(void *data, int status) | |
1048 | { | |
1049 | http_ctrl_t *ctrlp = (http_ctrl_t *) data; | |
1050 | HttpStateData *httpState = NULL; | |
1051 | int sock; | |
1052 | request_t *request; | |
1053 | char *req_hdr; | |
1054 | int req_hdr_sz; | |
1055 | StoreEntry *entry; | |
1056 | ||
1057 | sock = ctrlp->sock; | |
1058 | request = ctrlp->request; | |
1059 | req_hdr = ctrlp->req_hdr; | |
1060 | req_hdr_sz = ctrlp->req_hdr_sz; | |
1061 | entry = ctrlp->entry; | |
1062 | xfree(ctrlp); | |
1063 | ||
30a4f2a8 | 1064 | httpState = xcalloc(1, sizeof(HttpStateData)); |
0a0bf5db | 1065 | httpState->entry = entry; |
1066 | ||
30a4f2a8 | 1067 | httpState->req_hdr = req_hdr; |
4768dbe0 | 1068 | httpState->req_hdr_sz = req_hdr_sz; |
30a4f2a8 | 1069 | httpState->request = requestLink(request); |
1070 | comm_add_close_handler(sock, | |
b177367b | 1071 | httpStateFree, |
30a4f2a8 | 1072 | (void *) httpState); |
a13d9042 | 1073 | ipcache_nbgethostbyname(request->host, |
1074 | sock, | |
1075 | httpConnect, | |
1076 | httpState); | |
090089c4 | 1077 | } |
30a4f2a8 | 1078 | |
b8d8561b | 1079 | void |
1080 | httpReplyHeaderStats(StoreEntry * entry) | |
30a4f2a8 | 1081 | { |
6fb52f6c | 1082 | http_server_cc_t i; |
151a0b6d | 1083 | http_hdr_misc_t j; |
30a4f2a8 | 1084 | storeAppendPrintf(entry, open_bracket); |
6fb52f6c | 1085 | storeAppendPrintf(entry, "{HTTP Reply Headers:}\n"); |
1086 | storeAppendPrintf(entry, "{ Headers parsed: %d}\n", | |
30a4f2a8 | 1087 | ReplyHeaderStats.parsed); |
151a0b6d | 1088 | for (j = HDR_AGE; j < HDR_MISC_END; j++) |
1089 | storeAppendPrintf(entry, "{%21.21s: %d}\n", | |
1090 | HttpHdrMiscStr[j], | |
1091 | ReplyHeaderStats.misc[j]); | |
4db43fab | 1092 | for (i = SCC_PUBLIC; i < SCC_ENUM_END; i++) |
884011a1 | 1093 | storeAppendPrintf(entry, "{Cache-Control %s: %d}\n", |
6fb52f6c | 1094 | HttpServerCCStr[i], |
1095 | ReplyHeaderStats.cc[i]); | |
30a4f2a8 | 1096 | storeAppendPrintf(entry, close_bracket); |
1097 | } |