]>
Commit | Line | Data |
---|---|---|
da2b3a17 | 1 | |
30a4f2a8 | 2 | /* |
2262fa23 | 3 | * $Id: http.cc,v 1.266 1998/04/27 19:52:48 wessels Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 11 Hypertext Transfer Protocol (HTTP) | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
30a4f2a8 | 9 | * -------------------------------------------------------- |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
019dd986 | 31 | |
32 | /* | |
30a4f2a8 | 33 | * Copyright (c) 1994, 1995. All rights reserved. |
34 | * | |
35 | * The Harvest software was developed by the Internet Research Task | |
36 | * Force Research Group on Resource Discovery (IRTF-RD): | |
37 | * | |
38 | * Mic Bowman of Transarc Corporation. | |
39 | * Peter Danzig of the University of Southern California. | |
40 | * Darren R. Hardy of the University of Colorado at Boulder. | |
41 | * Udi Manber of the University of Arizona. | |
42 | * Michael F. Schwartz of the University of Colorado at Boulder. | |
43 | * Duane Wessels of the University of Colorado at Boulder. | |
44 | * | |
45 | * This copyright notice applies to software in the Harvest | |
46 | * ``src/'' directory only. Users should consult the individual | |
47 | * copyright notices in the ``components/'' subdirectories for | |
48 | * copyright information about other software bundled with the | |
49 | * Harvest source code distribution. | |
50 | * | |
51 | * TERMS OF USE | |
52 | * | |
53 | * The Harvest software may be used and re-distributed without | |
54 | * charge, provided that the software origin and research team are | |
55 | * cited in any use of the system. Most commonly this is | |
56 | * accomplished by including a link to the Harvest Home Page | |
57 | * (http://harvest.cs.colorado.edu/) from the query page of any | |
58 | * Broker you deploy, as well as in the query result pages. These | |
59 | * links are generated automatically by the standard Broker | |
60 | * software distribution. | |
61 | * | |
62 | * The Harvest software is provided ``as is'', without express or | |
63 | * implied warranty, and with no support nor obligation to assist | |
64 | * in its use, correction, modification or enhancement. We assume | |
65 | * no liability with respect to the infringement of copyrights, | |
66 | * trade secrets, or any patents, and are not responsible for | |
67 | * consequential damages. Proper use of the Harvest software is | |
68 | * entirely the responsibility of the user. | |
69 | * | |
70 | * DERIVATIVE WORKS | |
71 | * | |
72 | * Users may make derivative works from the Harvest software, subject | |
73 | * to the following constraints: | |
74 | * | |
75 | * - You must include the above copyright notice and these | |
76 | * accompanying paragraphs in all forms of derivative works, | |
77 | * and any documentation and other materials related to such | |
78 | * distribution and use acknowledge that the software was | |
79 | * developed at the above institutions. | |
80 | * | |
81 | * - You must notify IRTF-RD regarding your distribution of | |
82 | * the derivative work. | |
83 | * | |
84 | * - You must clearly notify users that your are distributing | |
85 | * a modified version and not the original Harvest software. | |
86 | * | |
87 | * - Any derivative product is also subject to these copyright | |
88 | * and use restrictions. | |
89 | * | |
90 | * Note that the Harvest software is NOT in the public domain. We | |
91 | * retain copyright, as specified above. | |
92 | * | |
93 | * HISTORY OF FREE SOFTWARE STATUS | |
94 | * | |
95 | * Originally we required sites to license the software in cases | |
96 | * where they were going to build commercial products/services | |
97 | * around Harvest. In June 1995 we changed this policy. We now | |
98 | * allow people to use the core Harvest software (the code found in | |
99 | * the Harvest ``src/'' directory) for free. We made this change | |
100 | * in the interest of encouraging the widest possible deployment of | |
101 | * the technology. The Harvest software is really a reference | |
102 | * implementation of a set of protocols and formats, some of which | |
103 | * we intend to standardize. We encourage commercial | |
104 | * re-implementations of code complying to this set of standards. | |
019dd986 | 105 | */ |
44a47c6e | 106 | |
4a83b852 | 107 | /* |
108 | * Anonymizing patch by lutz@as-node.jena.thur.de | |
de3bdb4c | 109 | * have a look into http-anon.c to get more informations. |
4a83b852 | 110 | */ |
111 | ||
44a47c6e | 112 | #include "squid.h" |
090089c4 | 113 | |
6bf8443a | 114 | static const char *const crlf = "\r\n"; |
4db43fab | 115 | |
6bf8443a | 116 | enum { |
6fb52f6c | 117 | CCC_NOCACHE, |
118 | CCC_NOSTORE, | |
119 | CCC_MAXAGE, | |
120 | CCC_MAXSTALE, | |
121 | CCC_MINFRESH, | |
122 | CCC_ONLYIFCACHED, | |
123 | CCC_ENUM_END | |
6bf8443a | 124 | }; |
125 | ||
9e4ad609 | 126 | static CNCB httpConnectDone; |
127 | static CWCB httpSendComplete; | |
54220df8 | 128 | static CWCB httpSendRequestEntry; |
129 | ||
9e4ad609 | 130 | static PF httpReadReply; |
131 | static PF httpSendRequest; | |
132 | static PF httpStateFree; | |
133 | static PF httpTimeout; | |
f5b8bbc4 | 134 | static void httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int); |
135 | static void httpCacheNegatively(StoreEntry *); | |
136 | static void httpMakePrivate(StoreEntry *); | |
137 | static void httpMakePublic(StoreEntry *); | |
bfcaf585 | 138 | static STABH httpAbort; |
f5b8bbc4 | 139 | static HttpStateData *httpBuildState(int, StoreEntry *, request_t *, peer *); |
140 | static int httpSocketOpen(StoreEntry *, request_t *); | |
141 | static void httpRestart(HttpStateData *); | |
efb9218c | 142 | static int httpTryRestart(HttpStateData *); |
f8309b15 | 143 | static int httpCachableReply(HttpStateData *); |
b8d8561b | 144 | |
b177367b | 145 | static void |
79d39a72 | 146 | httpStateFree(int fdnotused, void *data) |
f5558c95 | 147 | { |
b177367b | 148 | HttpStateData *httpState = data; |
0d4d4170 | 149 | if (httpState == NULL) |
b177367b | 150 | return; |
bfcaf585 | 151 | storeUnregisterAbort(httpState->entry); |
ddb6142d | 152 | assert(httpState->entry->store_status != STORE_PENDING); |
f88211e8 | 153 | storeUnlockObject(httpState->entry); |
0d4d4170 | 154 | if (httpState->reply_hdr) { |
3f6c0fb2 | 155 | memFree(MEM_8K_BUF, httpState->reply_hdr); |
0d4d4170 | 156 | httpState->reply_hdr = NULL; |
157 | } | |
30a4f2a8 | 158 | requestUnlink(httpState->request); |
20cc1450 | 159 | requestUnlink(httpState->orig_request); |
7dd44885 | 160 | httpState->request = NULL; |
161 | httpState->orig_request = NULL; | |
162 | cbdataFree(httpState); | |
f5558c95 | 163 | } |
164 | ||
b8d8561b | 165 | int |
75e88d56 | 166 | httpCachable(method_t method) |
090089c4 | 167 | { |
090089c4 | 168 | /* GET and HEAD are cachable. Others are not. */ |
6eb42cae | 169 | if (method != METHOD_GET && method != METHOD_HEAD) |
090089c4 | 170 | return 0; |
090089c4 | 171 | /* else cachable */ |
172 | return 1; | |
173 | } | |
174 | ||
b8d8561b | 175 | static void |
5c5783a2 | 176 | httpTimeout(int fd, void *data) |
090089c4 | 177 | { |
b177367b | 178 | HttpStateData *httpState = data; |
593c9a75 | 179 | StoreEntry *entry = httpState->entry; |
9b312a19 | 180 | ErrorState *err; |
9fb13bb6 | 181 | debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry)); |
8796b9e9 | 182 | assert(entry->store_status == STORE_PENDING); |
73a3014d | 183 | if (entry->mem_obj->inmem_hi == 0) { |
fe40a877 | 184 | err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT); |
79a15e0a | 185 | err->request = requestLink(httpState->orig_request); |
9b312a19 | 186 | errorAppendEntry(entry, err); |
b50179a6 | 187 | } else { |
b34ed725 | 188 | storeAbort(entry, 0); |
9b312a19 | 189 | } |
0d4d4170 | 190 | comm_close(fd); |
090089c4 | 191 | } |
192 | ||
30a4f2a8 | 193 | /* This object can be cached for a long time */ |
b8d8561b | 194 | static void |
195 | httpMakePublic(StoreEntry * entry) | |
30a4f2a8 | 196 | { |
79a15e0a | 197 | if (EBIT_TEST(entry->flag, ENTRY_CACHABLE)) |
30a4f2a8 | 198 | storeSetPublicKey(entry); |
199 | } | |
200 | ||
201 | /* This object should never be cached at all */ | |
b8d8561b | 202 | static void |
203 | httpMakePrivate(StoreEntry * entry) | |
30a4f2a8 | 204 | { |
30a4f2a8 | 205 | storeExpireNow(entry); |
79a15e0a | 206 | EBIT_CLR(entry->flag, ENTRY_CACHABLE); |
30a4f2a8 | 207 | storeReleaseRequest(entry); /* delete object when not used */ |
208 | } | |
209 | ||
210 | /* This object may be negatively cached */ | |
b8d8561b | 211 | static void |
212 | httpCacheNegatively(StoreEntry * entry) | |
30a4f2a8 | 213 | { |
79b5cc5f | 214 | storeNegativeCache(entry); |
79a15e0a | 215 | if (EBIT_TEST(entry->flag, ENTRY_CACHABLE)) |
30a4f2a8 | 216 | storeSetPublicKey(entry); |
30a4f2a8 | 217 | } |
218 | ||
f8309b15 | 219 | static int |
220 | httpCachableReply(HttpStateData * httpState) | |
c54e9052 | 221 | { |
d8b249ef | 222 | HttpReply *rep = httpState->entry->mem_obj->reply; |
223 | HttpHeader *hdr = &rep->header; | |
224 | const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0; | |
7faf2bdb | 225 | if (EBIT_TEST(cc_mask, CC_PRIVATE)) |
f8309b15 | 226 | return 0; |
7faf2bdb | 227 | if (EBIT_TEST(cc_mask, CC_NO_CACHE)) |
f8309b15 | 228 | return 0; |
79a15e0a | 229 | if (EBIT_TEST(httpState->request->flags, REQ_AUTH)) |
7faf2bdb | 230 | if (!EBIT_TEST(cc_mask, CC_PROXY_REVALIDATE)) |
fee0cebb | 231 | return 0; |
f8309b15 | 232 | /* |
233 | * Dealing with cookies is quite a bit more complicated | |
234 | * than this. Ideally we should strip the cookie | |
235 | * header from the reply but still cache the reply body. | |
236 | * More confusion at draft-ietf-http-state-mgmt-05.txt. | |
237 | */ | |
cb69b4c7 | 238 | /* With new headers the above stripping should be easy to do? @?@ */ |
239 | if (httpHeaderHas(hdr, HDR_SET_COOKIE)) | |
f8309b15 | 240 | return 0; |
cb69b4c7 | 241 | switch (httpState->entry->mem_obj->reply->sline.status) { |
c54e9052 | 242 | /* Responses that are cacheable */ |
243 | case 200: /* OK */ | |
244 | case 203: /* Non-Authoritative Information */ | |
245 | case 300: /* Multiple Choices */ | |
246 | case 301: /* Moved Permanently */ | |
247 | case 410: /* Gone */ | |
1294c0fc | 248 | /* don't cache objects from peers w/o LMT, Date, or Expires */ |
cb69b4c7 | 249 | /* check that is it enough to check headers @?@ */ |
d8b249ef | 250 | if (rep->date > -1) |
c54e9052 | 251 | return 1; |
d8b249ef | 252 | else if (rep->last_modified > -1) |
c54e9052 | 253 | return 1; |
1294c0fc | 254 | else if (!httpState->peer) |
c54e9052 | 255 | return 1; |
d8b249ef | 256 | /* @?@ (here and 302): invalid expires header compiles to squid_curtime */ |
257 | else if (rep->expires > -1) | |
c54e9052 | 258 | return 1; |
c54e9052 | 259 | else |
260 | return 0; | |
79d39a72 | 261 | /* NOTREACHED */ |
c54e9052 | 262 | break; |
263 | /* Responses that only are cacheable if the server says so */ | |
264 | case 302: /* Moved temporarily */ | |
d8b249ef | 265 | if (rep->expires > -1) |
c54e9052 | 266 | return 1; |
267 | else | |
268 | return 0; | |
79d39a72 | 269 | /* NOTREACHED */ |
c54e9052 | 270 | break; |
cb69b4c7 | 271 | /* @?@ should we replace these magic numbers with http_status enums? */ |
c54e9052 | 272 | /* Errors can be negatively cached */ |
273 | case 204: /* No Content */ | |
274 | case 305: /* Use Proxy (proxy redirect) */ | |
275 | case 400: /* Bad Request */ | |
276 | case 403: /* Forbidden */ | |
277 | case 404: /* Not Found */ | |
278 | case 405: /* Method Now Allowed */ | |
279 | case 414: /* Request-URI Too Long */ | |
280 | case 500: /* Internal Server Error */ | |
281 | case 501: /* Not Implemented */ | |
282 | case 502: /* Bad Gateway */ | |
283 | case 503: /* Service Unavailable */ | |
284 | case 504: /* Gateway Timeout */ | |
285 | return -1; | |
79d39a72 | 286 | /* NOTREACHED */ |
c54e9052 | 287 | break; |
288 | /* Some responses can never be cached */ | |
88738790 | 289 | case 206: /* Partial Content -- Not yet supported */ |
c54e9052 | 290 | case 303: /* See Other */ |
291 | case 304: /* Not Modified */ | |
292 | case 401: /* Unauthorized */ | |
293 | case 407: /* Proxy Authentication Required */ | |
294 | case 600: /* Squid header parsing error */ | |
295 | default: /* Unknown status code */ | |
296 | return 0; | |
79d39a72 | 297 | /* NOTREACHED */ |
c54e9052 | 298 | break; |
299 | } | |
79d39a72 | 300 | /* NOTREACHED */ |
c54e9052 | 301 | } |
090089c4 | 302 | |
cb69b4c7 | 303 | /* rewrite this later using new interfaces @?@ */ |
b8d8561b | 304 | void |
0ee4272b | 305 | httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) |
f5558c95 | 306 | { |
307 | char *t = NULL; | |
30a4f2a8 | 308 | StoreEntry *entry = httpState->entry; |
d3fb4dea | 309 | int room; |
310 | int hdr_len; | |
cb69b4c7 | 311 | HttpReply *reply = entry->mem_obj->reply; |
b6cfb65c | 312 | debug(11, 3) ("httpProcessReplyHeader: key '%s'\n", |
313 | storeKeyText(entry->key)); | |
e924600d | 314 | if (httpState->reply_hdr == NULL) |
7021844c | 315 | httpState->reply_hdr = memAllocate(MEM_8K_BUF); |
30a4f2a8 | 316 | if (httpState->reply_hdr_state == 0) { |
317 | hdr_len = strlen(httpState->reply_hdr); | |
ed85b771 | 318 | room = 8191 - hdr_len; |
30a4f2a8 | 319 | strncat(httpState->reply_hdr, buf, room < size ? room : size); |
d3fb4dea | 320 | hdr_len += room < size ? room : size; |
30a4f2a8 | 321 | if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) { |
84fa351c | 322 | debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr); |
30a4f2a8 | 323 | httpState->reply_hdr_state += 2; |
cb69b4c7 | 324 | reply->sline.status = 555; |
ed85b771 | 325 | return; |
d3fb4dea | 326 | } |
d1a43e28 | 327 | t = httpState->reply_hdr + hdr_len; |
328 | /* headers can be incomplete only if object still arriving */ | |
2334c194 | 329 | if (!httpState->eof) { |
330 | size_t k = headersEnd(httpState->reply_hdr, 8192); | |
331 | if (0 == k) | |
d1a43e28 | 332 | return; /* headers not complete */ |
2334c194 | 333 | t = httpState->reply_hdr + k; |
334 | } | |
2285407f | 335 | *t = '\0'; |
30a4f2a8 | 336 | httpState->reply_hdr_state++; |
f5558c95 | 337 | } |
30a4f2a8 | 338 | if (httpState->reply_hdr_state == 1) { |
123abbe1 | 339 | const Ctx ctx = ctx_enter(entry->mem_obj->url); |
30a4f2a8 | 340 | httpState->reply_hdr_state++; |
a3d5953d | 341 | debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", |
30a4f2a8 | 342 | httpState->reply_hdr); |
343 | /* Parse headers into reply structure */ | |
2334c194 | 344 | /* Old code never parsed headers if headersEnd failed, was it intentional ? @?@ @?@ */ |
cb69b4c7 | 345 | /* what happens if we fail to parse here? @?@ @?@ */ |
ee1679df | 346 | httpReplyParse(reply, httpState->reply_hdr); /* httpState->eof); */ |
ca98227c | 347 | storeTimestampsSet(entry); |
30a4f2a8 | 348 | /* Check if object is cacheable or not based on reply code */ |
cb69b4c7 | 349 | debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status); |
f8309b15 | 350 | switch (httpCachableReply(httpState)) { |
c54e9052 | 351 | case 1: |
352 | httpMakePublic(entry); | |
30a4f2a8 | 353 | break; |
c54e9052 | 354 | case 0: |
355 | httpMakePrivate(entry); | |
f5558c95 | 356 | break; |
c54e9052 | 357 | case -1: |
851eeef7 | 358 | httpCacheNegatively(entry); |
30a4f2a8 | 359 | break; |
c54e9052 | 360 | default: |
361 | assert(0); | |
4e38e700 | 362 | break; |
f5558c95 | 363 | } |
d8b249ef | 364 | if (reply->cache_control && EBIT_TEST(reply->cache_control->mask, CC_PROXY_REVALIDATE)) |
79a15e0a | 365 | EBIT_SET(entry->flag, ENTRY_REVALIDATE); |
9a47da71 | 366 | if (EBIT_TEST(httpState->flags, HTTP_KEEPALIVE)) |
367 | if (httpState->peer) | |
368 | httpState->peer->stats.n_keepalives_sent++; | |
a90ba3db | 369 | if (reply->proxy_keep_alive) |
1294c0fc | 370 | if (httpState->peer) |
371 | httpState->peer->stats.n_keepalives_recv++; | |
123abbe1 | 372 | ctx_exit(ctx); |
f5558c95 | 373 | } |
374 | } | |
375 | ||
603a02fd | 376 | static int |
377 | httpPconnTransferDone(HttpStateData * httpState) | |
378 | { | |
379 | /* return 1 if we got the last of the data on a persistent connection */ | |
380 | MemObject *mem = httpState->entry->mem_obj; | |
cb69b4c7 | 381 | HttpReply *reply = mem->reply; |
51fdcbd5 | 382 | debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); |
978e455f | 383 | /* |
384 | * If we didn't send a Keepalive request header, then this | |
385 | * can not be a persistent connection. | |
386 | */ | |
79a15e0a | 387 | if (!EBIT_TEST(httpState->flags, HTTP_KEEPALIVE)) |
603a02fd | 388 | return 0; |
51fdcbd5 | 389 | debug(11, 5) ("httpPconnTransferDone: content_length=%d\n", |
d8b249ef | 390 | reply->content_length); |
603a02fd | 391 | /* |
978e455f | 392 | * Deal with gross HTTP stuff |
393 | * - If we haven't seen the end of the reply headers, we can't | |
394 | * be persistent. | |
395 | * - For "200 OK" check the content-length in the next block. | |
978e455f | 396 | * - For "204 No Content" (even with content-length) we're done. |
397 | * - For "304 Not Modified" (even with content-length) we're done. | |
a3c60429 | 398 | * - 1XX replies never have a body; we're done. |
978e455f | 399 | * - For HEAD requests with content-length we're done. |
a3c60429 | 400 | * - For all other replies, check content length in next block. |
603a02fd | 401 | */ |
978e455f | 402 | if (httpState->reply_hdr_state < 2) |
403 | return 0; | |
cb69b4c7 | 404 | else if (reply->sline.status == HTTP_OK) |
a3c60429 | 405 | (void) 0; /* common case, continue */ |
cb69b4c7 | 406 | else if (reply->sline.status == HTTP_NO_CONTENT) |
978e455f | 407 | return 1; |
cb69b4c7 | 408 | else if (reply->sline.status == HTTP_NOT_MODIFIED) |
978e455f | 409 | return 1; |
cb69b4c7 | 410 | else if (reply->sline.status < HTTP_OK) |
a3c60429 | 411 | return 1; |
978e455f | 412 | else if (httpState->request->method == METHOD_HEAD) |
413 | return 1; | |
603a02fd | 414 | /* |
a3c60429 | 415 | * If there is no content-length, then we can't be |
978e455f | 416 | * persistent. If there is a content length, then we must |
417 | * wait until we've seen the end of the body. | |
603a02fd | 418 | */ |
d8b249ef | 419 | if (reply->content_length < 0) |
603a02fd | 420 | return 0; |
d8b249ef | 421 | else if (mem->inmem_hi < reply->content_length + reply->hdr_sz) |
603a02fd | 422 | return 0; |
978e455f | 423 | else |
b34ed725 | 424 | return 1; |
603a02fd | 425 | } |
090089c4 | 426 | |
427 | /* This will be called when data is ready to be read from fd. Read until | |
428 | * error or connection closed. */ | |
f5558c95 | 429 | /* XXX this function is too long! */ |
b8d8561b | 430 | static void |
b177367b | 431 | httpReadReply(int fd, void *data) |
090089c4 | 432 | { |
b177367b | 433 | HttpStateData *httpState = data; |
95d659f0 | 434 | LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF); |
bfcaf585 | 435 | StoreEntry *entry = httpState->entry; |
603a02fd | 436 | const request_t *request = httpState->request; |
090089c4 | 437 | int len; |
30a4f2a8 | 438 | int bin; |
090089c4 | 439 | int clen; |
9b312a19 | 440 | ErrorState *err; |
d89d1fb6 | 441 | if (protoAbortFetch(entry)) { |
9b312a19 | 442 | storeAbort(entry, 0); |
a3d5953d | 443 | comm_close(fd); |
444 | return; | |
234967c9 | 445 | } |
446 | /* check if we want to defer reading */ | |
8350fe9b | 447 | clen = entry->mem_obj->inmem_hi; |
1513873c | 448 | errno = 0; |
30a4f2a8 | 449 | len = read(fd, buf, SQUID_TCP_SO_RCVBUF); |
a3d5953d | 450 | debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len); |
30a4f2a8 | 451 | if (len > 0) { |
ee1679df | 452 | fd_bytes(fd, len, FD_READ); |
a0f32775 | 453 | kb_incr(&Counter.server.all.kbytes_in, len); |
454 | kb_incr(&Counter.server.http.kbytes_in, len); | |
4f92c80c | 455 | commSetTimeout(fd, Config.Timeout.read, NULL, NULL); |
4a63c85f | 456 | IOStats.Http.reads++; |
30a4f2a8 | 457 | for (clen = len - 1, bin = 0; clen; bin++) |
458 | clen >>= 1; | |
459 | IOStats.Http.read_hist[bin]++; | |
460 | } | |
ba718c8f | 461 | if (len < 0) { |
55cb44f1 | 462 | debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n", |
463 | fd, xstrerror()); | |
b224ea98 | 464 | if (ignoreErrno(errno)) { |
9b312a19 | 465 | commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); |
55cb44f1 | 466 | } else if (entry->mem_obj->inmem_hi == 0 && httpTryRestart(httpState)) { |
467 | httpRestart(httpState); | |
468 | } else if (clen == 0) { | |
469 | err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR); | |
470 | err->xerrno = errno; | |
471 | err->request = requestLink(httpState->orig_request); | |
472 | errorAppendEntry(entry, err); | |
1afe05c5 | 473 | comm_close(fd); |
090089c4 | 474 | } else { |
55cb44f1 | 475 | storeAbort(entry, 0); |
0d4d4170 | 476 | comm_close(fd); |
090089c4 | 477 | } |
8350fe9b | 478 | } else if (len == 0 && entry->mem_obj->inmem_hi == 0) { |
efb9218c | 479 | if (httpTryRestart(httpState)) { |
b716a8ad | 480 | httpRestart(httpState); |
481 | } else { | |
482 | httpState->eof = 1; | |
fe40a877 | 483 | err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE); |
c45ed9ad | 484 | err->xerrno = errno; |
79a15e0a | 485 | err->request = requestLink(httpState->orig_request); |
b716a8ad | 486 | errorAppendEntry(entry, err); |
b716a8ad | 487 | comm_close(fd); |
488 | } | |
090089c4 | 489 | } else if (len == 0) { |
490 | /* Connection closed; retrieval done. */ | |
f86a6a46 | 491 | httpState->eof = 1; |
d1a43e28 | 492 | if (httpState->reply_hdr_state < 2) |
b34ed725 | 493 | /* |
494 | * Yes Henrik, there is a point to doing this. When we | |
495 | * called httpProcessReplyHeader() before, we didn't find | |
496 | * the end of headers, but now we are definately at EOF, so | |
497 | * we want to process the reply headers. | |
498 | */ | |
d1a43e28 | 499 | httpProcessReplyHeader(httpState, buf, len); |
d1a43e28 | 500 | storeComplete(entry); /* deallocates mem_obj->request */ |
0d4d4170 | 501 | comm_close(fd); |
090089c4 | 502 | } else { |
d1a43e28 | 503 | if (httpState->reply_hdr_state < 2) |
30a4f2a8 | 504 | httpProcessReplyHeader(httpState, buf, len); |
620da955 | 505 | storeAppend(entry, buf, len); |
603a02fd | 506 | if (httpPconnTransferDone(httpState)) { |
5b29969a | 507 | /* yes we have to clear all these! */ |
8796b9e9 | 508 | commSetDefer(fd, NULL, NULL); |
5b29969a | 509 | commSetTimeout(fd, -1, NULL, NULL); |
510 | commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); | |
603a02fd | 511 | comm_remove_close_handler(fd, httpStateFree, httpState); |
512 | storeComplete(entry); /* deallocates mem_obj->request */ | |
8796b9e9 | 513 | pconnPush(fd, request->host, request->port); |
603a02fd | 514 | httpState->fd = -1; |
515 | httpStateFree(-1, httpState); | |
516 | } else { | |
517 | commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); | |
518 | } | |
090089c4 | 519 | } |
520 | } | |
521 | ||
522 | /* This will be called when request write is complete. Schedule read of | |
523 | * reply. */ | |
b8d8561b | 524 | static void |
79a15e0a | 525 | httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) |
090089c4 | 526 | { |
30a4f2a8 | 527 | HttpStateData *httpState = data; |
9b312a19 | 528 | StoreEntry *entry = httpState->entry; |
529 | ErrorState *err; | |
a3d5953d | 530 | debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n", |
090089c4 | 531 | fd, size, errflag); |
ee1679df | 532 | if (size > 0) { |
533 | fd_bytes(fd, size, FD_WRITE); | |
a0f32775 | 534 | kb_incr(&Counter.server.all.kbytes_out, size); |
399e85ea | 535 | kb_incr(&Counter.server.http.kbytes_out, size); |
ee1679df | 536 | } |
ea3a2a69 | 537 | if (errflag == COMM_ERR_CLOSING) |
538 | return; | |
090089c4 | 539 | if (errflag) { |
fe40a877 | 540 | err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); |
c45ed9ad | 541 | err->xerrno = errno; |
79a15e0a | 542 | err->request = requestLink(httpState->orig_request); |
9b312a19 | 543 | errorAppendEntry(entry, err); |
0d4d4170 | 544 | comm_close(fd); |
090089c4 | 545 | return; |
546 | } else { | |
547 | /* Schedule read reply. */ | |
b177367b | 548 | commSetSelect(fd, |
019dd986 | 549 | COMM_SELECT_READ, |
b177367b | 550 | httpReadReply, |
cd1fb0eb | 551 | httpState, 0); |
70a9dab4 | 552 | commSetDefer(fd, protoCheckDeferRead, entry); |
090089c4 | 553 | } |
554 | } | |
555 | ||
6bf8443a | 556 | static void |
88738790 | 557 | httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int check) |
6bf8443a | 558 | { |
559 | size_t n = *sz + strlen(line) + 2; | |
560 | if (n >= max) | |
561 | return; | |
88738790 | 562 | if (check) { |
17a0a4ee | 563 | if (Config.onoff.anonymizer == ANONYMIZER_PARANOID) { |
88738790 | 564 | if (!httpAnonAllowed(line)) |
565 | return; | |
17a0a4ee | 566 | } else if (Config.onoff.anonymizer == ANONYMIZER_STANDARD) { |
88738790 | 567 | if (httpAnonDenied(line)) |
568 | return; | |
569 | } | |
4a83b852 | 570 | } |
4a83b852 | 571 | /* allowed header, explicitly known to be not dangerous */ |
a3d5953d | 572 | debug(11, 5) ("httpAppendRequestHeader: %s\n", line); |
929545fe | 573 | strcpy(hdr + (*sz), line); |
6bf8443a | 574 | strcat(hdr + (*sz), crlf); |
575 | *sz = n; | |
576 | } | |
577 | ||
1294c0fc | 578 | #define YBUF_SZ (MAX_URL+32) |
6bf8443a | 579 | size_t |
580 | httpBuildRequestHeader(request_t * request, | |
581 | request_t * orig_request, | |
582 | StoreEntry * entry, | |
6bf8443a | 583 | size_t * in_len, |
584 | char *hdr_out, | |
585 | size_t out_sz, | |
603a02fd | 586 | int cfd, |
587 | int flags) | |
6bf8443a | 588 | { |
1294c0fc | 589 | LOCAL_ARRAY(char, ybuf, YBUF_SZ); |
ab013258 | 590 | LOCAL_ARRAY(char, no_forward, 1024); |
7021844c | 591 | char *xbuf = memAllocate(MEM_4K_BUF); |
592 | char *viabuf = memAllocate(MEM_4K_BUF); | |
593 | char *fwdbuf = memAllocate(MEM_4K_BUF); | |
6bf8443a | 594 | char *t = NULL; |
595 | char *s = NULL; | |
596 | char *end = NULL; | |
597 | size_t len = 0; | |
598 | size_t hdr_len = 0; | |
6bf8443a | 599 | size_t l; |
600 | int hdr_flags = 0; | |
151a0b6d | 601 | int cc_flags = 0; |
b3b64e58 | 602 | int n; |
6bf8443a | 603 | const char *url = NULL; |
2334c194 | 604 | char *hdr_in; |
6bf8443a | 605 | |
2334c194 | 606 | assert(orig_request->headers != NULL); |
607 | hdr_in = orig_request->headers; | |
a3d5953d | 608 | debug(11, 3) ("httpBuildRequestHeader: INPUT:\n%s\n", hdr_in); |
6bf8443a | 609 | xstrncpy(fwdbuf, "X-Forwarded-For: ", 4096); |
610 | xstrncpy(viabuf, "Via: ", 4096); | |
1294c0fc | 611 | snprintf(ybuf, YBUF_SZ, "%s %s HTTP/1.0", |
6bf8443a | 612 | RequestMethodStr[request->method], |
02922e76 | 613 | strLen(request->urlpath) ? strBuf(request->urlpath) : "/"); |
88738790 | 614 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1); |
6bf8443a | 615 | /* Add IMS header */ |
616 | if (entry && entry->lastmod && request->method == METHOD_GET) { | |
1294c0fc | 617 | snprintf(ybuf, YBUF_SZ, "If-Modified-Since: %s", mkrfc1123(entry->lastmod)); |
88738790 | 618 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1); |
6bf8443a | 619 | EBIT_SET(hdr_flags, HDR_IMS); |
620 | } | |
2334c194 | 621 | assert(orig_request->headers_sz > 0); |
622 | end = orig_request->headers + orig_request->headers_sz; | |
6bf8443a | 623 | for (t = hdr_in; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) { |
624 | hdr_len = t - hdr_in; | |
6bf8443a | 625 | l = strcspn(t, crlf) + 1; |
626 | if (l > 4096) | |
627 | l = 4096; | |
8febe7df | 628 | /* We might find a NULL before 'end' */ |
629 | if (1 == l) | |
1afe05c5 | 630 | break; |
6bf8443a | 631 | xstrncpy(xbuf, t, l); |
a3d5953d | 632 | debug(11, 5) ("httpBuildRequestHeader: %s\n", xbuf); |
6bf8443a | 633 | if (strncasecmp(xbuf, "Proxy-Connection:", 17) == 0) |
634 | continue; | |
88738790 | 635 | if (strncasecmp(xbuf, "Proxy-authorization:", 20) == 0) |
afe95a7e | 636 | /* If we're not going to do proxy auth, then it must be passed on */ |
79a15e0a | 637 | if (EBIT_TEST(request->flags, REQ_USED_PROXY_AUTH)) |
88738790 | 638 | continue; |
ab013258 | 639 | if (strncasecmp(xbuf, "Connection:", 11) == 0) { |
067bea91 | 640 | handleConnectionHeader(0, no_forward, &xbuf[11]); |
6bf8443a | 641 | continue; |
067bea91 | 642 | } |
66f7337b | 643 | if (strncasecmp(xbuf, "Host:", 5) == 0) { |
77ed547a | 644 | /* Don't use client's Host: header for redirected requests */ |
645 | if (EBIT_TEST(request->flags, REQ_REDIRECTED)) | |
646 | continue; | |
6bf8443a | 647 | EBIT_SET(hdr_flags, HDR_HOST); |
66f7337b | 648 | } else if (strncasecmp(xbuf, "Cache-Control:", 14) == 0) { |
6bf8443a | 649 | for (s = xbuf + 14; *s && isspace(*s); s++); |
650 | if (strncasecmp(s, "Max-age=", 8) == 0) | |
151a0b6d | 651 | EBIT_SET(cc_flags, CCC_MAXAGE); |
66f7337b | 652 | } else if (strncasecmp(xbuf, "Via:", 4) == 0) { |
6bf8443a | 653 | for (s = xbuf + 4; *s && isspace(*s); s++); |
69e81830 | 654 | if ((int) strlen(viabuf) + (int) strlen(s) < 4000) |
6bf8443a | 655 | strcat(viabuf, s); |
656 | strcat(viabuf, ", "); | |
657 | continue; | |
66f7337b | 658 | } else if (strncasecmp(xbuf, "X-Forwarded-For:", 16) == 0) { |
6bf8443a | 659 | for (s = xbuf + 16; *s && isspace(*s); s++); |
69e81830 | 660 | if ((int) strlen(fwdbuf) + (int) strlen(s) < 4000) |
6bf8443a | 661 | strcat(fwdbuf, s); |
662 | strcat(fwdbuf, ", "); | |
663 | continue; | |
66f7337b | 664 | } else if (strncasecmp(xbuf, "If-Modified-Since:", 18) == 0) { |
6bf8443a | 665 | if (EBIT_TEST(hdr_flags, HDR_IMS)) |
666 | continue; | |
b3b64e58 | 667 | } else if (strncasecmp(xbuf, "Max-Forwards:", 13) == 0) { |
668 | if (orig_request->method == METHOD_TRACE) { | |
669 | for (s = xbuf + 13; *s && isspace(*s); s++); | |
670 | n = atoi(s); | |
56878878 | 671 | snprintf(xbuf, 4096, "Max-Forwards: %d", n - 1); |
b3b64e58 | 672 | } |
66f7337b | 673 | } |
067bea91 | 674 | if (!handleConnectionHeader(1, no_forward, xbuf)) |
ab013258 | 675 | httpAppendRequestHeader(hdr_out, xbuf, &len, out_sz - 512, 1); |
88738790 | 676 | } |
677 | hdr_len = t - hdr_in; | |
678 | if (Config.fake_ua && strstr(hdr_out, "User-Agent") == NULL) { | |
1294c0fc | 679 | snprintf(ybuf, YBUF_SZ, "User-Agent: %s", Config.fake_ua); |
88738790 | 680 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 0); |
6bf8443a | 681 | } |
56878878 | 682 | /* Append Via: */ |
683 | /* snprintf would fail here too */ | |
1294c0fc | 684 | snprintf(ybuf, YBUF_SZ, "%3.1f %s", orig_request->http_ver, ThisCache); |
6bf8443a | 685 | strcat(viabuf, ybuf); |
88738790 | 686 | httpAppendRequestHeader(hdr_out, viabuf, &len, out_sz, 1); |
6bf8443a | 687 | /* Append to X-Forwarded-For: */ |
a08307eb | 688 | strcat(fwdbuf, cfd < 0 ? "unknown" : fd_table[cfd].ipaddr); |
88738790 | 689 | httpAppendRequestHeader(hdr_out, fwdbuf, &len, out_sz, 1); |
6bf8443a | 690 | if (!EBIT_TEST(hdr_flags, HDR_HOST)) { |
2262fa23 | 691 | if (orig_request->port == urlDefaultPort(orig_request->protocol)) |
692 | snprintf(ybuf, YBUF_SZ, "Host: %s", orig_request->host); | |
693 | else | |
694 | snprintf(ybuf, YBUF_SZ, "Host: %s:%d", orig_request->host, | |
695 | orig_request->port); | |
88738790 | 696 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1); |
6bf8443a | 697 | } |
151a0b6d | 698 | if (!EBIT_TEST(cc_flags, CCC_MAXAGE)) { |
9fb13bb6 | 699 | url = entry ? storeUrl(entry) : urlCanonical(orig_request, NULL); |
1294c0fc | 700 | snprintf(ybuf, YBUF_SZ, "Cache-control: Max-age=%d", (int) getMaxAge(url)); |
88738790 | 701 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1); |
02922e76 | 702 | if (strLen(request->urlpath)) |
703 | assert(strstr(url, strBuf(request->urlpath))); | |
6bf8443a | 704 | } |
603a02fd | 705 | /* maybe append Connection: Keep-Alive */ |
79a15e0a | 706 | if (EBIT_TEST(flags, HTTP_KEEPALIVE)) { |
707 | if (EBIT_TEST(flags, HTTP_PROXYING)) { | |
1294c0fc | 708 | snprintf(ybuf, YBUF_SZ, "Proxy-Connection: Keep-Alive"); |
603a02fd | 709 | } else { |
1294c0fc | 710 | snprintf(ybuf, YBUF_SZ, "Connection: Keep-Alive"); |
603a02fd | 711 | } |
712 | httpAppendRequestHeader(hdr_out, ybuf, &len, out_sz, 1); | |
713 | } | |
88738790 | 714 | httpAppendRequestHeader(hdr_out, null_string, &len, out_sz, 1); |
3f6c0fb2 | 715 | memFree(MEM_4K_BUF, xbuf); |
716 | memFree(MEM_4K_BUF, viabuf); | |
717 | memFree(MEM_4K_BUF, fwdbuf); | |
6bf8443a | 718 | if (in_len) |
719 | *in_len = hdr_len; | |
9d9d144b | 720 | if ((l = strlen(hdr_out)) != len) { |
721 | debug_trap("httpBuildRequestHeader: size mismatch"); | |
722 | len = l; | |
723 | } | |
a3d5953d | 724 | debug(11, 3) ("httpBuildRequestHeader: OUTPUT:\n%s\n", hdr_out); |
6bf8443a | 725 | return len; |
726 | } | |
727 | ||
090089c4 | 728 | /* This will be called when connect completes. Write request. */ |
b8d8561b | 729 | static void |
b177367b | 730 | httpSendRequest(int fd, void *data) |
090089c4 | 731 | { |
b177367b | 732 | HttpStateData *httpState = data; |
090089c4 | 733 | char *buf = NULL; |
090089c4 | 734 | int len = 0; |
735 | int buflen; | |
30a4f2a8 | 736 | request_t *req = httpState->request; |
9864ee44 | 737 | int buftype = 0; |
620da955 | 738 | StoreEntry *entry = httpState->entry; |
2a26c096 | 739 | int cfd; |
1294c0fc | 740 | peer *p = httpState->peer; |
901e234d | 741 | CWCB *sendHeaderDone; |
090089c4 | 742 | |
a3d5953d | 743 | debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState); |
02922e76 | 744 | buflen = strLen(req->urlpath); |
62dec5a7 | 745 | if (req->headers) |
746 | buflen += req->headers_sz + 1; | |
090089c4 | 747 | buflen += 512; /* lots of extra */ |
748 | ||
efb9218c | 749 | if (pumpMethod(req->method)) |
7db8b16d | 750 | sendHeaderDone = httpSendRequestEntry; |
751 | else | |
752 | sendHeaderDone = httpSendComplete; | |
54220df8 | 753 | |
30a4f2a8 | 754 | if (buflen < DISK_PAGE_SIZE) { |
7021844c | 755 | buf = memAllocate(MEM_8K_BUF); |
9864ee44 | 756 | buftype = BUF_TYPE_8K; |
6bf8443a | 757 | buflen = DISK_PAGE_SIZE; |
30a4f2a8 | 758 | } else { |
9864ee44 | 759 | buf = xcalloc(buflen, 1); |
760 | buftype = BUF_TYPE_MALLOC; | |
090089c4 | 761 | } |
2a26c096 | 762 | if (!opt_forwarded_for) |
6bf8443a | 763 | cfd = -1; |
2a26c096 | 764 | else if (entry->mem_obj == NULL) |
6bf8443a | 765 | cfd = -1; |
2a26c096 | 766 | else |
382d851a | 767 | cfd = entry->mem_obj->fd; |
1294c0fc | 768 | if (p != NULL) |
79a15e0a | 769 | EBIT_SET(httpState->flags, HTTP_PROXYING); |
efb9218c | 770 | /* |
771 | * Is Keepalive okay for all request methods? | |
772 | */ | |
773 | if (p == NULL) | |
774 | EBIT_SET(httpState->flags, HTTP_KEEPALIVE); | |
775 | else if (p->stats.n_keepalives_sent < 10) | |
776 | EBIT_SET(httpState->flags, HTTP_KEEPALIVE); | |
777 | else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50) | |
778 | EBIT_SET(httpState->flags, HTTP_KEEPALIVE); | |
6bf8443a | 779 | len = httpBuildRequestHeader(req, |
79a15e0a | 780 | httpState->orig_request, |
6bf8443a | 781 | entry, |
6bf8443a | 782 | NULL, |
783 | buf, | |
784 | buflen, | |
603a02fd | 785 | cfd, |
786 | httpState->flags); | |
a3d5953d | 787 | debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, buf); |
30a4f2a8 | 788 | comm_write(fd, |
14e59844 | 789 | buf, |
790 | len, | |
54220df8 | 791 | sendHeaderDone, |
9864ee44 | 792 | httpState, |
3f6c0fb2 | 793 | buftype == BUF_TYPE_8K ? memFree8K : xfree); |
090089c4 | 794 | } |
795 | ||
603a02fd | 796 | static int |
b716a8ad | 797 | httpSocketOpen(StoreEntry * entry, request_t * request) |
090089c4 | 798 | { |
9e4ad609 | 799 | int fd; |
9b312a19 | 800 | ErrorState *err; |
9e4ad609 | 801 | fd = comm_open(SOCK_STREAM, |
16b204c4 | 802 | 0, |
803 | Config.Addrs.tcp_outgoing, | |
804 | 0, | |
805 | COMM_NONBLOCKING, | |
9fb13bb6 | 806 | storeUrl(entry)); |
603a02fd | 807 | if (fd < 0) { |
79a15e0a | 808 | debug(50, 4) ("httpSocketOpen: %s\n", xstrerror()); |
fe40a877 | 809 | err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR); |
c45ed9ad | 810 | err->xerrno = errno; |
79a15e0a | 811 | err->request = requestLink(request); |
9b312a19 | 812 | errorAppendEntry(entry, err); |
090089c4 | 813 | } |
603a02fd | 814 | return fd; |
815 | } | |
816 | ||
817 | static HttpStateData * | |
818 | httpBuildState(int fd, StoreEntry * entry, request_t * orig_request, peer * e) | |
819 | { | |
cb87dab6 | 820 | HttpStateData *httpState = memAllocate(MEM_HTTP_STATE_DATA); |
603a02fd | 821 | request_t *request; |
770f051d | 822 | storeLockObject(entry); |
cb87dab6 | 823 | cbdataAdd(httpState, MEM_HTTP_STATE_DATA); |
0a0bf5db | 824 | httpState->entry = entry; |
9e4ad609 | 825 | httpState->fd = fd; |
603a02fd | 826 | if (e) { |
7021844c | 827 | request = memAllocate(MEM_REQUEST_T); |
603a02fd | 828 | request->method = orig_request->method; |
829 | xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN); | |
830 | request->port = e->http_port; | |
02922e76 | 831 | stringReset(&request->urlpath, storeUrl(entry)); |
603a02fd | 832 | httpState->request = requestLink(request); |
1294c0fc | 833 | httpState->peer = e; |
603a02fd | 834 | httpState->orig_request = requestLink(orig_request); |
79a15e0a | 835 | EBIT_SET(request->flags, REQ_PROXYING); |
603a02fd | 836 | } else { |
837 | httpState->request = requestLink(orig_request); | |
79a15e0a | 838 | httpState->orig_request = requestLink(orig_request); |
603a02fd | 839 | } |
0d4d4170 | 840 | /* register the handler to free HTTP state data when the FD closes */ |
603a02fd | 841 | comm_add_close_handler(httpState->fd, httpStateFree, httpState); |
e102ebda | 842 | storeRegisterAbort(entry, httpAbort, httpState); |
603a02fd | 843 | return httpState; |
844 | } | |
845 | ||
846 | void | |
847 | httpStart(request_t * request, StoreEntry * entry, peer * e) | |
848 | { | |
849 | HttpStateData *httpState; | |
850 | int fd; | |
851 | debug(11, 3) ("httpStart: \"%s %s\"\n", | |
9fb13bb6 | 852 | RequestMethodStr[request->method], storeUrl(entry)); |
a0f32775 | 853 | Counter.server.all.requests++; |
854 | Counter.server.http.requests++; | |
603a02fd | 855 | if (e) { |
a369131d | 856 | if (EBIT_TEST(e->options, NEIGHBOR_PROXY_ONLY)) |
603a02fd | 857 | storeReleaseRequest(entry); |
603a02fd | 858 | if ((fd = pconnPop(e->host, e->http_port)) >= 0) { |
51fdcbd5 | 859 | debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd); |
603a02fd | 860 | httpState = httpBuildState(fd, entry, request, e); |
b716a8ad | 861 | commSetTimeout(httpState->fd, |
862 | Config.Timeout.connect, | |
863 | httpTimeout, | |
864 | httpState); | |
603a02fd | 865 | httpConnectDone(fd, COMM_OK, httpState); |
866 | return; | |
867 | } | |
868 | } else { | |
869 | if ((fd = pconnPop(request->host, request->port)) >= 0) { | |
51fdcbd5 | 870 | debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd); |
603a02fd | 871 | httpState = httpBuildState(fd, entry, request, e); |
b716a8ad | 872 | commSetTimeout(httpState->fd, |
873 | Config.Timeout.connect, | |
874 | httpTimeout, | |
875 | httpState); | |
603a02fd | 876 | httpConnectDone(fd, COMM_OK, httpState); |
877 | return; | |
878 | } | |
879 | } | |
79a15e0a | 880 | if ((fd = httpSocketOpen(entry, request)) < 0) |
603a02fd | 881 | return; |
882 | httpState = httpBuildState(fd, entry, request, e); | |
603a02fd | 883 | commSetTimeout(httpState->fd, |
884 | Config.Timeout.connect, | |
885 | httpTimeout, | |
cd1fb0eb | 886 | httpState); |
edeb28fd | 887 | commConnectStart(httpState->fd, |
603a02fd | 888 | httpState->request->host, |
889 | httpState->request->port, | |
e924600d | 890 | httpConnectDone, |
891 | httpState); | |
e5f6c5c2 | 892 | } |
893 | ||
efb9218c | 894 | static int |
895 | httpTryRestart(HttpStateData * httpState) | |
896 | { | |
897 | /* | |
898 | * We only retry the request if it looks like it was | |
899 | * on a persistent/pipelined connection | |
900 | */ | |
901 | if (fd_table[httpState->fd].uses < 2) | |
902 | return 0; | |
903 | if (pumpMethod(httpState->orig_request->method)) | |
904 | if (0 == pumpRestart(httpState->orig_request)) | |
905 | return 0; | |
906 | return 1; | |
907 | } | |
908 | ||
b716a8ad | 909 | static void |
910 | httpRestart(HttpStateData * httpState) | |
911 | { | |
912 | /* restart a botched request from a persistent connection */ | |
9fb13bb6 | 913 | debug(11, 2) ("Retrying HTTP request for %s\n", storeUrl(httpState->entry)); |
901e234d | 914 | if (pumpMethod(httpState->orig_request->method)) { |
efb9218c | 915 | debug(11, 1) ("Potential Coredump: httpRestart %s %s\n", |
916 | RequestMethodStr[httpState->orig_request->method], | |
917 | storeUrl(httpState->entry)); | |
e371ed5c | 918 | } |
b716a8ad | 919 | if (httpState->fd >= 0) { |
920 | comm_remove_close_handler(httpState->fd, httpStateFree, httpState); | |
921 | comm_close(httpState->fd); | |
922 | httpState->fd = -1; | |
923 | } | |
79a15e0a | 924 | httpState->fd = httpSocketOpen(httpState->entry, httpState->orig_request); |
b716a8ad | 925 | if (httpState->fd < 0) |
926 | return; | |
927 | comm_add_close_handler(httpState->fd, httpStateFree, httpState); | |
928 | commSetTimeout(httpState->fd, | |
929 | Config.Timeout.connect, | |
930 | httpTimeout, | |
931 | httpState); | |
932 | commConnectStart(httpState->fd, | |
933 | httpState->request->host, | |
934 | httpState->request->port, | |
935 | httpConnectDone, | |
936 | httpState); | |
937 | } | |
938 | ||
e5f6c5c2 | 939 | static void |
940 | httpConnectDone(int fd, int status, void *data) | |
941 | { | |
942 | HttpStateData *httpState = data; | |
943 | request_t *request = httpState->request; | |
944 | StoreEntry *entry = httpState->entry; | |
9b312a19 | 945 | ErrorState *err; |
edeb28fd | 946 | if (status == COMM_ERR_DNS) { |
a3d5953d | 947 | debug(11, 4) ("httpConnectDone: Unknown host: %s\n", request->host); |
fe40a877 | 948 | err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE); |
9b312a19 | 949 | err->dnsserver_msg = xstrdup(dns_error_message); |
79a15e0a | 950 | err->request = requestLink(httpState->orig_request); |
9b312a19 | 951 | errorAppendEntry(entry, err); |
edeb28fd | 952 | comm_close(fd); |
953 | } else if (status != COMM_OK) { | |
fe40a877 | 954 | err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE); |
c45ed9ad | 955 | err->xerrno = errno; |
9b312a19 | 956 | err->host = xstrdup(request->host); |
957 | err->port = request->port; | |
79a15e0a | 958 | err->request = requestLink(httpState->orig_request); |
9b312a19 | 959 | errorAppendEntry(entry, err); |
1294c0fc | 960 | if (httpState->peer) |
961 | peerCheckConnectStart(httpState->peer); | |
e5f6c5c2 | 962 | comm_close(fd); |
963 | } else { | |
9fb13bb6 | 964 | fd_note(fd, storeUrl(entry)); |
b716a8ad | 965 | fd_table[fd].uses++; |
bfcaf585 | 966 | commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0); |
86cf9987 | 967 | commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); |
090089c4 | 968 | } |
090089c4 | 969 | } |
970 | ||
bfcaf585 | 971 | static void |
972 | httpAbort(void *data) | |
973 | { | |
974 | HttpStateData *httpState = data; | |
9fb13bb6 | 975 | debug(11, 2) ("httpAbort: %s\n", storeUrl(httpState->entry)); |
bfcaf585 | 976 | comm_close(httpState->fd); |
977 | } | |
9b312a19 | 978 | |
54220df8 | 979 | static void |
7db8b16d | 980 | httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data) |
54220df8 | 981 | { |
982 | HttpStateData *httpState = data; | |
983 | StoreEntry *entry = httpState->entry; | |
984 | ErrorState *err; | |
985 | debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n", | |
7db8b16d | 986 | fd, size, errflag); |
54220df8 | 987 | if (size > 0) { |
7db8b16d | 988 | fd_bytes(fd, size, FD_WRITE); |
54220df8 | 989 | kb_incr(&Counter.server.all.kbytes_out, size); |
990 | kb_incr(&Counter.server.http.kbytes_out, size); | |
991 | } | |
992 | if (errflag == COMM_ERR_CLOSING) | |
7db8b16d | 993 | return; |
54220df8 | 994 | if (errflag) { |
7db8b16d | 995 | err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); |
996 | err->xerrno = errno; | |
997 | err->request = requestLink(httpState->orig_request); | |
998 | errorAppendEntry(entry, err); | |
999 | comm_close(fd); | |
1000 | return; | |
54220df8 | 1001 | } |
7db8b16d | 1002 | pumpStart(fd, entry, httpState->orig_request, httpSendComplete, httpState); |
54220df8 | 1003 | } |