]>
Commit | Line | Data |
---|---|---|
da2b3a17 | 1 | |
30a4f2a8 | 2 | /* |
e1e72f06 | 3 | * $Id: http.cc,v 1.273 1998/05/21 22:01:08 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; | |
99edd1c3 | 134 | #if OLD_CODE |
f5b8bbc4 | 135 | static void httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int); |
99edd1c3 | 136 | #endif |
f5b8bbc4 | 137 | static void httpCacheNegatively(StoreEntry *); |
138 | static void httpMakePrivate(StoreEntry *); | |
139 | static void httpMakePublic(StoreEntry *); | |
bfcaf585 | 140 | static STABH httpAbort; |
f5b8bbc4 | 141 | static HttpStateData *httpBuildState(int, StoreEntry *, request_t *, peer *); |
142 | static int httpSocketOpen(StoreEntry *, request_t *); | |
143 | static void httpRestart(HttpStateData *); | |
efb9218c | 144 | static int httpTryRestart(HttpStateData *); |
f8309b15 | 145 | static int httpCachableReply(HttpStateData *); |
b8d8561b | 146 | |
b177367b | 147 | static void |
79d39a72 | 148 | httpStateFree(int fdnotused, void *data) |
f5558c95 | 149 | { |
b177367b | 150 | HttpStateData *httpState = data; |
0d4d4170 | 151 | if (httpState == NULL) |
b177367b | 152 | return; |
bfcaf585 | 153 | storeUnregisterAbort(httpState->entry); |
ddb6142d | 154 | assert(httpState->entry->store_status != STORE_PENDING); |
f88211e8 | 155 | storeUnlockObject(httpState->entry); |
0d4d4170 | 156 | if (httpState->reply_hdr) { |
3f6c0fb2 | 157 | memFree(MEM_8K_BUF, httpState->reply_hdr); |
0d4d4170 | 158 | httpState->reply_hdr = NULL; |
159 | } | |
30a4f2a8 | 160 | requestUnlink(httpState->request); |
20cc1450 | 161 | requestUnlink(httpState->orig_request); |
7dd44885 | 162 | httpState->request = NULL; |
163 | httpState->orig_request = NULL; | |
164 | cbdataFree(httpState); | |
f5558c95 | 165 | } |
166 | ||
b8d8561b | 167 | int |
75e88d56 | 168 | httpCachable(method_t method) |
090089c4 | 169 | { |
090089c4 | 170 | /* GET and HEAD are cachable. Others are not. */ |
6eb42cae | 171 | if (method != METHOD_GET && method != METHOD_HEAD) |
090089c4 | 172 | return 0; |
090089c4 | 173 | /* else cachable */ |
174 | return 1; | |
175 | } | |
176 | ||
b8d8561b | 177 | static void |
5c5783a2 | 178 | httpTimeout(int fd, void *data) |
090089c4 | 179 | { |
b177367b | 180 | HttpStateData *httpState = data; |
593c9a75 | 181 | StoreEntry *entry = httpState->entry; |
9b312a19 | 182 | ErrorState *err; |
9fb13bb6 | 183 | debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry)); |
8796b9e9 | 184 | assert(entry->store_status == STORE_PENDING); |
73a3014d | 185 | if (entry->mem_obj->inmem_hi == 0) { |
fe40a877 | 186 | err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT); |
79a15e0a | 187 | err->request = requestLink(httpState->orig_request); |
9b312a19 | 188 | errorAppendEntry(entry, err); |
b50179a6 | 189 | } else { |
b34ed725 | 190 | storeAbort(entry, 0); |
9b312a19 | 191 | } |
0d4d4170 | 192 | comm_close(fd); |
090089c4 | 193 | } |
194 | ||
30a4f2a8 | 195 | /* This object can be cached for a long time */ |
b8d8561b | 196 | static void |
197 | httpMakePublic(StoreEntry * entry) | |
30a4f2a8 | 198 | { |
79a15e0a | 199 | if (EBIT_TEST(entry->flag, ENTRY_CACHABLE)) |
30a4f2a8 | 200 | storeSetPublicKey(entry); |
201 | } | |
202 | ||
203 | /* This object should never be cached at all */ | |
b8d8561b | 204 | static void |
205 | httpMakePrivate(StoreEntry * entry) | |
30a4f2a8 | 206 | { |
30a4f2a8 | 207 | storeExpireNow(entry); |
79a15e0a | 208 | EBIT_CLR(entry->flag, ENTRY_CACHABLE); |
30a4f2a8 | 209 | storeReleaseRequest(entry); /* delete object when not used */ |
210 | } | |
211 | ||
212 | /* This object may be negatively cached */ | |
b8d8561b | 213 | static void |
214 | httpCacheNegatively(StoreEntry * entry) | |
30a4f2a8 | 215 | { |
79b5cc5f | 216 | storeNegativeCache(entry); |
79a15e0a | 217 | if (EBIT_TEST(entry->flag, ENTRY_CACHABLE)) |
30a4f2a8 | 218 | storeSetPublicKey(entry); |
30a4f2a8 | 219 | } |
220 | ||
f8309b15 | 221 | static int |
222 | httpCachableReply(HttpStateData * httpState) | |
c54e9052 | 223 | { |
d8b249ef | 224 | HttpReply *rep = httpState->entry->mem_obj->reply; |
225 | HttpHeader *hdr = &rep->header; | |
226 | const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0; | |
7faf2bdb | 227 | if (EBIT_TEST(cc_mask, CC_PRIVATE)) |
f8309b15 | 228 | return 0; |
7faf2bdb | 229 | if (EBIT_TEST(cc_mask, CC_NO_CACHE)) |
f8309b15 | 230 | return 0; |
79a15e0a | 231 | if (EBIT_TEST(httpState->request->flags, REQ_AUTH)) |
7faf2bdb | 232 | if (!EBIT_TEST(cc_mask, CC_PROXY_REVALIDATE)) |
fee0cebb | 233 | return 0; |
f8309b15 | 234 | /* |
235 | * Dealing with cookies is quite a bit more complicated | |
236 | * than this. Ideally we should strip the cookie | |
237 | * header from the reply but still cache the reply body. | |
238 | * More confusion at draft-ietf-http-state-mgmt-05.txt. | |
239 | */ | |
cb69b4c7 | 240 | /* With new headers the above stripping should be easy to do? @?@ */ |
241 | if (httpHeaderHas(hdr, HDR_SET_COOKIE)) | |
f8309b15 | 242 | return 0; |
783e4699 | 243 | if (httpHeaderHas(hdr, HDR_VARY)) |
244 | return 0; | |
cb69b4c7 | 245 | switch (httpState->entry->mem_obj->reply->sline.status) { |
c54e9052 | 246 | /* Responses that are cacheable */ |
247 | case 200: /* OK */ | |
248 | case 203: /* Non-Authoritative Information */ | |
249 | case 300: /* Multiple Choices */ | |
250 | case 301: /* Moved Permanently */ | |
251 | case 410: /* Gone */ | |
1294c0fc | 252 | /* don't cache objects from peers w/o LMT, Date, or Expires */ |
cb69b4c7 | 253 | /* check that is it enough to check headers @?@ */ |
d8b249ef | 254 | if (rep->date > -1) |
c54e9052 | 255 | return 1; |
d8b249ef | 256 | else if (rep->last_modified > -1) |
c54e9052 | 257 | return 1; |
1294c0fc | 258 | else if (!httpState->peer) |
c54e9052 | 259 | return 1; |
d8b249ef | 260 | /* @?@ (here and 302): invalid expires header compiles to squid_curtime */ |
261 | else if (rep->expires > -1) | |
c54e9052 | 262 | return 1; |
c54e9052 | 263 | else |
264 | return 0; | |
79d39a72 | 265 | /* NOTREACHED */ |
c54e9052 | 266 | break; |
267 | /* Responses that only are cacheable if the server says so */ | |
268 | case 302: /* Moved temporarily */ | |
d8b249ef | 269 | if (rep->expires > -1) |
c54e9052 | 270 | return 1; |
271 | else | |
272 | return 0; | |
79d39a72 | 273 | /* NOTREACHED */ |
c54e9052 | 274 | break; |
cb69b4c7 | 275 | /* @?@ should we replace these magic numbers with http_status enums? */ |
c54e9052 | 276 | /* Errors can be negatively cached */ |
277 | case 204: /* No Content */ | |
278 | case 305: /* Use Proxy (proxy redirect) */ | |
279 | case 400: /* Bad Request */ | |
280 | case 403: /* Forbidden */ | |
281 | case 404: /* Not Found */ | |
282 | case 405: /* Method Now Allowed */ | |
283 | case 414: /* Request-URI Too Long */ | |
284 | case 500: /* Internal Server Error */ | |
285 | case 501: /* Not Implemented */ | |
286 | case 502: /* Bad Gateway */ | |
287 | case 503: /* Service Unavailable */ | |
288 | case 504: /* Gateway Timeout */ | |
289 | return -1; | |
79d39a72 | 290 | /* NOTREACHED */ |
c54e9052 | 291 | break; |
292 | /* Some responses can never be cached */ | |
88738790 | 293 | case 206: /* Partial Content -- Not yet supported */ |
c54e9052 | 294 | case 303: /* See Other */ |
295 | case 304: /* Not Modified */ | |
296 | case 401: /* Unauthorized */ | |
297 | case 407: /* Proxy Authentication Required */ | |
298 | case 600: /* Squid header parsing error */ | |
299 | default: /* Unknown status code */ | |
300 | return 0; | |
79d39a72 | 301 | /* NOTREACHED */ |
c54e9052 | 302 | break; |
303 | } | |
79d39a72 | 304 | /* NOTREACHED */ |
c54e9052 | 305 | } |
090089c4 | 306 | |
cb69b4c7 | 307 | /* rewrite this later using new interfaces @?@ */ |
b8d8561b | 308 | void |
0ee4272b | 309 | httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) |
f5558c95 | 310 | { |
311 | char *t = NULL; | |
30a4f2a8 | 312 | StoreEntry *entry = httpState->entry; |
d3fb4dea | 313 | int room; |
314 | int hdr_len; | |
cb69b4c7 | 315 | HttpReply *reply = entry->mem_obj->reply; |
b6cfb65c | 316 | debug(11, 3) ("httpProcessReplyHeader: key '%s'\n", |
317 | storeKeyText(entry->key)); | |
e924600d | 318 | if (httpState->reply_hdr == NULL) |
7021844c | 319 | httpState->reply_hdr = memAllocate(MEM_8K_BUF); |
30a4f2a8 | 320 | if (httpState->reply_hdr_state == 0) { |
321 | hdr_len = strlen(httpState->reply_hdr); | |
ed85b771 | 322 | room = 8191 - hdr_len; |
30a4f2a8 | 323 | strncat(httpState->reply_hdr, buf, room < size ? room : size); |
d3fb4dea | 324 | hdr_len += room < size ? room : size; |
30a4f2a8 | 325 | if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) { |
84fa351c | 326 | debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr); |
30a4f2a8 | 327 | httpState->reply_hdr_state += 2; |
cb69b4c7 | 328 | reply->sline.status = 555; |
ed85b771 | 329 | return; |
d3fb4dea | 330 | } |
d1a43e28 | 331 | t = httpState->reply_hdr + hdr_len; |
332 | /* headers can be incomplete only if object still arriving */ | |
2334c194 | 333 | if (!httpState->eof) { |
334 | size_t k = headersEnd(httpState->reply_hdr, 8192); | |
335 | if (0 == k) | |
d1a43e28 | 336 | return; /* headers not complete */ |
2334c194 | 337 | t = httpState->reply_hdr + k; |
338 | } | |
2285407f | 339 | *t = '\0'; |
30a4f2a8 | 340 | httpState->reply_hdr_state++; |
f5558c95 | 341 | } |
30a4f2a8 | 342 | if (httpState->reply_hdr_state == 1) { |
123abbe1 | 343 | const Ctx ctx = ctx_enter(entry->mem_obj->url); |
30a4f2a8 | 344 | httpState->reply_hdr_state++; |
a3d5953d | 345 | debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", |
30a4f2a8 | 346 | httpState->reply_hdr); |
347 | /* Parse headers into reply structure */ | |
2334c194 | 348 | /* Old code never parsed headers if headersEnd failed, was it intentional ? @?@ @?@ */ |
cb69b4c7 | 349 | /* what happens if we fail to parse here? @?@ @?@ */ |
ee1679df | 350 | httpReplyParse(reply, httpState->reply_hdr); /* httpState->eof); */ |
ca98227c | 351 | storeTimestampsSet(entry); |
30a4f2a8 | 352 | /* Check if object is cacheable or not based on reply code */ |
cb69b4c7 | 353 | debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status); |
f8309b15 | 354 | switch (httpCachableReply(httpState)) { |
c54e9052 | 355 | case 1: |
356 | httpMakePublic(entry); | |
30a4f2a8 | 357 | break; |
c54e9052 | 358 | case 0: |
359 | httpMakePrivate(entry); | |
f5558c95 | 360 | break; |
c54e9052 | 361 | case -1: |
851eeef7 | 362 | httpCacheNegatively(entry); |
30a4f2a8 | 363 | break; |
c54e9052 | 364 | default: |
365 | assert(0); | |
4e38e700 | 366 | break; |
f5558c95 | 367 | } |
d8b249ef | 368 | if (reply->cache_control && EBIT_TEST(reply->cache_control->mask, CC_PROXY_REVALIDATE)) |
79a15e0a | 369 | EBIT_SET(entry->flag, ENTRY_REVALIDATE); |
9a47da71 | 370 | if (EBIT_TEST(httpState->flags, HTTP_KEEPALIVE)) |
371 | if (httpState->peer) | |
372 | httpState->peer->stats.n_keepalives_sent++; | |
9f5a2895 | 373 | if (reply->keep_alive) |
1294c0fc | 374 | if (httpState->peer) |
375 | httpState->peer->stats.n_keepalives_recv++; | |
123abbe1 | 376 | ctx_exit(ctx); |
f5558c95 | 377 | } |
378 | } | |
379 | ||
603a02fd | 380 | static int |
381 | httpPconnTransferDone(HttpStateData * httpState) | |
382 | { | |
383 | /* return 1 if we got the last of the data on a persistent connection */ | |
384 | MemObject *mem = httpState->entry->mem_obj; | |
cb69b4c7 | 385 | HttpReply *reply = mem->reply; |
51fdcbd5 | 386 | debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); |
978e455f | 387 | /* |
99edd1c3 | 388 | * If we didn't send a keep-alive request header, then this |
978e455f | 389 | * can not be a persistent connection. |
390 | */ | |
79a15e0a | 391 | if (!EBIT_TEST(httpState->flags, HTTP_KEEPALIVE)) |
603a02fd | 392 | return 0; |
9f5a2895 | 393 | /* |
394 | * What does the reply have to say about keep-alive? | |
395 | */ | |
396 | if (!reply->keep_alive) | |
397 | return 0; | |
51fdcbd5 | 398 | debug(11, 5) ("httpPconnTransferDone: content_length=%d\n", |
d8b249ef | 399 | reply->content_length); |
603a02fd | 400 | /* |
978e455f | 401 | * Deal with gross HTTP stuff |
402 | * - If we haven't seen the end of the reply headers, we can't | |
403 | * be persistent. | |
404 | * - For "200 OK" check the content-length in the next block. | |
978e455f | 405 | * - For "204 No Content" (even with content-length) we're done. |
406 | * - For "304 Not Modified" (even with content-length) we're done. | |
a3c60429 | 407 | * - 1XX replies never have a body; we're done. |
978e455f | 408 | * - For HEAD requests with content-length we're done. |
a3c60429 | 409 | * - For all other replies, check content length in next block. |
603a02fd | 410 | */ |
978e455f | 411 | if (httpState->reply_hdr_state < 2) |
412 | return 0; | |
cb69b4c7 | 413 | else if (reply->sline.status == HTTP_OK) |
a3c60429 | 414 | (void) 0; /* common case, continue */ |
cb69b4c7 | 415 | else if (reply->sline.status == HTTP_NO_CONTENT) |
978e455f | 416 | return 1; |
cb69b4c7 | 417 | else if (reply->sline.status == HTTP_NOT_MODIFIED) |
978e455f | 418 | return 1; |
cb69b4c7 | 419 | else if (reply->sline.status < HTTP_OK) |
a3c60429 | 420 | return 1; |
978e455f | 421 | else if (httpState->request->method == METHOD_HEAD) |
422 | return 1; | |
603a02fd | 423 | /* |
a3c60429 | 424 | * If there is no content-length, then we can't be |
978e455f | 425 | * persistent. If there is a content length, then we must |
426 | * wait until we've seen the end of the body. | |
603a02fd | 427 | */ |
d8b249ef | 428 | if (reply->content_length < 0) |
603a02fd | 429 | return 0; |
d8b249ef | 430 | else if (mem->inmem_hi < reply->content_length + reply->hdr_sz) |
603a02fd | 431 | return 0; |
978e455f | 432 | else |
b34ed725 | 433 | return 1; |
603a02fd | 434 | } |
090089c4 | 435 | |
436 | /* This will be called when data is ready to be read from fd. Read until | |
437 | * error or connection closed. */ | |
f5558c95 | 438 | /* XXX this function is too long! */ |
b8d8561b | 439 | static void |
b177367b | 440 | httpReadReply(int fd, void *data) |
090089c4 | 441 | { |
b177367b | 442 | HttpStateData *httpState = data; |
95d659f0 | 443 | LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF); |
bfcaf585 | 444 | StoreEntry *entry = httpState->entry; |
603a02fd | 445 | const request_t *request = httpState->request; |
090089c4 | 446 | int len; |
30a4f2a8 | 447 | int bin; |
090089c4 | 448 | int clen; |
9b312a19 | 449 | ErrorState *err; |
d89d1fb6 | 450 | if (protoAbortFetch(entry)) { |
9b312a19 | 451 | storeAbort(entry, 0); |
a3d5953d | 452 | comm_close(fd); |
453 | return; | |
234967c9 | 454 | } |
455 | /* check if we want to defer reading */ | |
8350fe9b | 456 | clen = entry->mem_obj->inmem_hi; |
1513873c | 457 | errno = 0; |
30a4f2a8 | 458 | len = read(fd, buf, SQUID_TCP_SO_RCVBUF); |
a3d5953d | 459 | debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len); |
30a4f2a8 | 460 | if (len > 0) { |
ee1679df | 461 | fd_bytes(fd, len, FD_READ); |
a0f32775 | 462 | kb_incr(&Counter.server.all.kbytes_in, len); |
463 | kb_incr(&Counter.server.http.kbytes_in, len); | |
4f92c80c | 464 | commSetTimeout(fd, Config.Timeout.read, NULL, NULL); |
4a63c85f | 465 | IOStats.Http.reads++; |
30a4f2a8 | 466 | for (clen = len - 1, bin = 0; clen; bin++) |
467 | clen >>= 1; | |
468 | IOStats.Http.read_hist[bin]++; | |
469 | } | |
5ede6c8f | 470 | if (!httpState->reply_hdr && len > 0) { |
471 | /* Skip whitespace */ | |
472 | while (len > 0 && isspace(*buf)) | |
473 | xmemmove(buf, buf + 1, len--); | |
474 | if (len == 0) { | |
475 | /* Continue to read... */ | |
476 | commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); | |
477 | return; | |
478 | } | |
479 | } | |
ba718c8f | 480 | if (len < 0) { |
55cb44f1 | 481 | debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n", |
482 | fd, xstrerror()); | |
b224ea98 | 483 | if (ignoreErrno(errno)) { |
9b312a19 | 484 | commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); |
55cb44f1 | 485 | } else if (entry->mem_obj->inmem_hi == 0 && httpTryRestart(httpState)) { |
486 | httpRestart(httpState); | |
487 | } else if (clen == 0) { | |
488 | err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR); | |
489 | err->xerrno = errno; | |
490 | err->request = requestLink(httpState->orig_request); | |
491 | errorAppendEntry(entry, err); | |
1afe05c5 | 492 | comm_close(fd); |
090089c4 | 493 | } else { |
55cb44f1 | 494 | storeAbort(entry, 0); |
0d4d4170 | 495 | comm_close(fd); |
090089c4 | 496 | } |
8350fe9b | 497 | } else if (len == 0 && entry->mem_obj->inmem_hi == 0) { |
efb9218c | 498 | if (httpTryRestart(httpState)) { |
b716a8ad | 499 | httpRestart(httpState); |
500 | } else { | |
501 | httpState->eof = 1; | |
fe40a877 | 502 | err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE); |
c45ed9ad | 503 | err->xerrno = errno; |
79a15e0a | 504 | err->request = requestLink(httpState->orig_request); |
b716a8ad | 505 | errorAppendEntry(entry, err); |
b716a8ad | 506 | comm_close(fd); |
507 | } | |
090089c4 | 508 | } else if (len == 0) { |
509 | /* Connection closed; retrieval done. */ | |
f86a6a46 | 510 | httpState->eof = 1; |
d1a43e28 | 511 | if (httpState->reply_hdr_state < 2) |
b34ed725 | 512 | /* |
513 | * Yes Henrik, there is a point to doing this. When we | |
514 | * called httpProcessReplyHeader() before, we didn't find | |
515 | * the end of headers, but now we are definately at EOF, so | |
516 | * we want to process the reply headers. | |
517 | */ | |
d1a43e28 | 518 | httpProcessReplyHeader(httpState, buf, len); |
d1a43e28 | 519 | storeComplete(entry); /* deallocates mem_obj->request */ |
0d4d4170 | 520 | comm_close(fd); |
090089c4 | 521 | } else { |
d1a43e28 | 522 | if (httpState->reply_hdr_state < 2) |
30a4f2a8 | 523 | httpProcessReplyHeader(httpState, buf, len); |
620da955 | 524 | storeAppend(entry, buf, len); |
603a02fd | 525 | if (httpPconnTransferDone(httpState)) { |
5b29969a | 526 | /* yes we have to clear all these! */ |
8796b9e9 | 527 | commSetDefer(fd, NULL, NULL); |
5b29969a | 528 | commSetTimeout(fd, -1, NULL, NULL); |
529 | commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); | |
603a02fd | 530 | comm_remove_close_handler(fd, httpStateFree, httpState); |
531 | storeComplete(entry); /* deallocates mem_obj->request */ | |
8796b9e9 | 532 | pconnPush(fd, request->host, request->port); |
603a02fd | 533 | httpState->fd = -1; |
534 | httpStateFree(-1, httpState); | |
535 | } else { | |
9f5a2895 | 536 | /* Wait for EOF condition */ |
603a02fd | 537 | commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); |
538 | } | |
090089c4 | 539 | } |
540 | } | |
541 | ||
542 | /* This will be called when request write is complete. Schedule read of | |
543 | * reply. */ | |
b8d8561b | 544 | static void |
79a15e0a | 545 | httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) |
090089c4 | 546 | { |
30a4f2a8 | 547 | HttpStateData *httpState = data; |
9b312a19 | 548 | StoreEntry *entry = httpState->entry; |
549 | ErrorState *err; | |
a3d5953d | 550 | debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n", |
090089c4 | 551 | fd, size, errflag); |
ee1679df | 552 | if (size > 0) { |
553 | fd_bytes(fd, size, FD_WRITE); | |
a0f32775 | 554 | kb_incr(&Counter.server.all.kbytes_out, size); |
399e85ea | 555 | kb_incr(&Counter.server.http.kbytes_out, size); |
ee1679df | 556 | } |
ea3a2a69 | 557 | if (errflag == COMM_ERR_CLOSING) |
558 | return; | |
090089c4 | 559 | if (errflag) { |
fe40a877 | 560 | err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); |
c45ed9ad | 561 | err->xerrno = errno; |
79a15e0a | 562 | err->request = requestLink(httpState->orig_request); |
9b312a19 | 563 | errorAppendEntry(entry, err); |
0d4d4170 | 564 | comm_close(fd); |
090089c4 | 565 | return; |
566 | } else { | |
567 | /* Schedule read reply. */ | |
b177367b | 568 | commSetSelect(fd, |
019dd986 | 569 | COMM_SELECT_READ, |
b177367b | 570 | httpReadReply, |
cd1fb0eb | 571 | httpState, 0); |
70a9dab4 | 572 | commSetDefer(fd, protoCheckDeferRead, entry); |
090089c4 | 573 | } |
574 | } | |
575 | ||
99edd1c3 | 576 | #if OLD_CODE |
6bf8443a | 577 | static void |
88738790 | 578 | httpAppendRequestHeader(char *hdr, const char *line, size_t * sz, size_t max, int check) |
6bf8443a | 579 | { |
580 | size_t n = *sz + strlen(line) + 2; | |
581 | if (n >= max) | |
582 | return; | |
88738790 | 583 | if (check) { |
17a0a4ee | 584 | if (Config.onoff.anonymizer == ANONYMIZER_PARANOID) { |
88738790 | 585 | if (!httpAnonAllowed(line)) |
586 | return; | |
17a0a4ee | 587 | } else if (Config.onoff.anonymizer == ANONYMIZER_STANDARD) { |
88738790 | 588 | if (httpAnonDenied(line)) |
589 | return; | |
590 | } | |
4a83b852 | 591 | } |
4a83b852 | 592 | /* allowed header, explicitly known to be not dangerous */ |
a3d5953d | 593 | debug(11, 5) ("httpAppendRequestHeader: %s\n", line); |
929545fe | 594 | strcpy(hdr + (*sz), line); |
6bf8443a | 595 | strcat(hdr + (*sz), crlf); |
596 | *sz = n; | |
597 | } | |
99edd1c3 | 598 | #endif |
6bf8443a | 599 | |
99edd1c3 | 600 | /* |
601 | * build request headers and append them to a given MemBuf | |
602 | * used by httpBuildRequestPrefix() | |
603 | * note: calls httpHeaderInit(), the caller is responsible for Clean()-ing | |
604 | */ | |
e1e72f06 | 605 | void |
6bf8443a | 606 | httpBuildRequestHeader(request_t * request, |
607 | request_t * orig_request, | |
608 | StoreEntry * entry, | |
99edd1c3 | 609 | HttpHeader *hdr_out, |
603a02fd | 610 | int cfd, |
611 | int flags) | |
6bf8443a | 612 | { |
99edd1c3 | 613 | /* building buffer for complex strings */ |
614 | #define BBUF_SZ (MAX_URL+32) | |
615 | LOCAL_ARRAY(char, bbuf, BBUF_SZ); | |
616 | String strConnection = StringNull; | |
617 | const HttpHeader *hdr_in = &orig_request->header; | |
618 | const HttpHeaderEntry *e; | |
619 | HttpHeaderPos pos = HttpHeaderInitPos; | |
620 | ||
621 | assert(orig_request->prefix != NULL); | |
622 | debug(11, 3) ("httpBuildRequestHeader:\n%s", orig_request->prefix); | |
623 | httpHeaderInit(hdr_out); | |
624 | ||
625 | /* append our IMS header */ | |
626 | if (entry && entry->lastmod && request->method == METHOD_GET) | |
627 | httpHeaderPutTime(hdr_out, HDR_IF_MODIFIED_SINCE, entry->lastmod); | |
628 | ||
629 | strConnection = httpHeaderGetList(hdr_in, HDR_CONNECTION); | |
630 | while ((e = httpHeaderGetEntry(hdr_in, &pos))) { | |
631 | debug(11, 5) ("httpBuildRequestHeader: %s: %s\n", | |
632 | strBuf(e->name), strBuf(e->value)); | |
633 | if (!httpRequestHdrAllowed(e, &strConnection)) | |
6bf8443a | 634 | continue; |
99edd1c3 | 635 | switch (e->id) { |
636 | case HDR_PROXY_AUTHORIZATION: | |
afe95a7e | 637 | /* If we're not going to do proxy auth, then it must be passed on */ |
99edd1c3 | 638 | if (!EBIT_TEST(request->flags, REQ_USED_PROXY_AUTH)) |
639 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); | |
640 | break; | |
641 | case HDR_HOST: | |
77ed547a | 642 | /* Don't use client's Host: header for redirected requests */ |
99edd1c3 | 643 | if (!EBIT_TEST(request->flags, REQ_REDIRECTED)) |
644 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); | |
645 | break; | |
646 | case HDR_IF_MODIFIED_SINCE: | |
647 | /* append unless we added our own; | |
648 | * note: at most one client's ims header can pass through */ | |
649 | if (!httpHeaderHas(hdr_out, HDR_IF_MODIFIED_SINCE)) | |
650 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); | |
651 | break; | |
652 | case HDR_MAX_FORWARDS: | |
b3b64e58 | 653 | if (orig_request->method == METHOD_TRACE) { |
99edd1c3 | 654 | /* sacrificing efficiency over clarity, etc. */ |
655 | const int hops = httpHeaderGetInt(hdr_in, HDR_MAX_FORWARDS); | |
656 | if (hops > 0) | |
657 | httpHeaderPutInt(hdr_out, HDR_MAX_FORWARDS, hops-1); | |
b3b64e58 | 658 | } |
99edd1c3 | 659 | break; |
660 | case HDR_PROXY_CONNECTION: | |
661 | case HDR_CONNECTION: | |
662 | case HDR_VIA: | |
663 | case HDR_X_FORWARDED_FOR: | |
664 | case HDR_CACHE_CONTROL: | |
665 | /* append these after the loop if needed */ | |
666 | break; | |
667 | default: | |
668 | /* pass on all other header fields */ | |
669 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); | |
66f7337b | 670 | } |
88738790 | 671 | } |
99edd1c3 | 672 | |
673 | /* append fake user agent if configured and | |
674 | * the real one is not supplied by the client */ | |
675 | if (Config.fake_ua && !httpHeaderHas(hdr_out, HDR_USER_AGENT)) | |
676 | httpHeaderPutStr(hdr_out, HDR_USER_AGENT, Config.fake_ua); | |
677 | ||
678 | /* append Via */ | |
679 | { | |
680 | String strVia = httpHeaderGetList(hdr_in, HDR_VIA); | |
681 | snprintf(bbuf, BBUF_SZ, "%3.1f %s", orig_request->http_ver, ThisCache); | |
682 | strListAdd(&strVia, bbuf, ','); | |
683 | httpHeaderPutStr(hdr_out, HDR_VIA, strBuf(strVia)); | |
684 | stringClean(&strVia); | |
6bf8443a | 685 | } |
99edd1c3 | 686 | /* append X-Forwarded-For */ |
687 | { | |
688 | String strFwd = httpHeaderGetList(hdr_in, HDR_X_FORWARDED_FOR); | |
689 | strListAdd(&strFwd, (cfd < 0 ? "unknown" : fd_table[cfd].ipaddr), ','); | |
690 | httpHeaderPutStr(hdr_out, HDR_X_FORWARDED_FOR, strBuf(strFwd)); | |
691 | stringClean(&strFwd); | |
692 | } | |
693 | /* append Host if not there already */ | |
694 | if (!httpHeaderHas(hdr_out, HDR_HOST)) { | |
695 | /* use port# only if not default */ | |
696 | if (orig_request->port == urlDefaultPort(orig_request->protocol)) { | |
697 | httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->host); | |
698 | } else { | |
699 | snprintf(bbuf, BBUF_SZ, "%s:%d", | |
700 | orig_request->host, (int) orig_request->port); | |
701 | httpHeaderPutStr(hdr_out, HDR_HOST, bbuf); | |
702 | } | |
6bf8443a | 703 | } |
99edd1c3 | 704 | /* append Cache-Control, add max-age if not there already */ |
705 | { | |
706 | HttpHdrCc *cc = httpHeaderGetCc(hdr_in); | |
707 | if (!cc) | |
708 | cc = httpHdrCcCreate(); | |
709 | if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { | |
710 | const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request, NULL); | |
711 | httpHdrCcSetMaxAge(cc, getMaxAge(url)); | |
712 | if (strLen(request->urlpath)) | |
713 | assert(strstr(url, strBuf(request->urlpath))); | |
714 | } | |
715 | httpHeaderPutCc(hdr_out, cc); | |
716 | httpHdrCcDestroy(cc); | |
6bf8443a | 717 | } |
99edd1c3 | 718 | /* maybe append Connection: keep-alive */ |
79a15e0a | 719 | if (EBIT_TEST(flags, HTTP_KEEPALIVE)) { |
720 | if (EBIT_TEST(flags, HTTP_PROXYING)) { | |
99edd1c3 | 721 | httpHeaderPutStr(hdr_out, HDR_PROXY_CONNECTION, "keep-alive"); |
603a02fd | 722 | } else { |
99edd1c3 | 723 | httpHeaderPutStr(hdr_out, HDR_CONNECTION, "keep-alive"); |
603a02fd | 724 | } |
603a02fd | 725 | } |
99edd1c3 | 726 | stringClean(&strConnection); |
727 | } | |
728 | ||
729 | /* build request prefix and append it to a given MemBuf; | |
730 | * return the length of the prefix */ | |
731 | size_t | |
732 | httpBuildRequestPrefix(request_t * request, | |
733 | request_t * orig_request, | |
734 | StoreEntry * entry, | |
735 | MemBuf *mb, | |
736 | int cfd, | |
737 | int flags) | |
738 | { | |
739 | const int offset = mb->size; | |
740 | memBufPrintf(mb, "%s %s HTTP/1.0\r\n", | |
741 | RequestMethodStr[request->method], | |
742 | strLen(request->urlpath) ? strBuf(request->urlpath) : "/"); | |
743 | /* build and pack headers */ | |
744 | { | |
745 | HttpHeader hdr; | |
746 | Packer p; | |
747 | httpBuildRequestHeader(request, orig_request, entry, &hdr, cfd, flags); | |
748 | packerToMemInit(&p, mb); | |
749 | httpHeaderPackInto(&hdr, &p); | |
750 | httpHeaderClean(&hdr); | |
751 | packerClean(&p); | |
9d9d144b | 752 | } |
99edd1c3 | 753 | /* append header terminator */ |
754 | memBufAppend(mb, "\r\n", 2); | |
755 | return mb->size - offset; | |
6bf8443a | 756 | } |
757 | ||
090089c4 | 758 | /* This will be called when connect completes. Write request. */ |
b8d8561b | 759 | static void |
b177367b | 760 | httpSendRequest(int fd, void *data) |
090089c4 | 761 | { |
b177367b | 762 | HttpStateData *httpState = data; |
99edd1c3 | 763 | MemBuf mb; |
30a4f2a8 | 764 | request_t *req = httpState->request; |
620da955 | 765 | StoreEntry *entry = httpState->entry; |
2a26c096 | 766 | int cfd; |
1294c0fc | 767 | peer *p = httpState->peer; |
901e234d | 768 | CWCB *sendHeaderDone; |
090089c4 | 769 | |
a3d5953d | 770 | debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", fd, httpState); |
090089c4 | 771 | |
efb9218c | 772 | if (pumpMethod(req->method)) |
7db8b16d | 773 | sendHeaderDone = httpSendRequestEntry; |
774 | else | |
775 | sendHeaderDone = httpSendComplete; | |
54220df8 | 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 |
382d851a | 782 | cfd = entry->mem_obj->fd; |
b0a1e5bf | 783 | assert(-1 == cfd || FD_SOCKET == fd_table[cfd].type); |
1294c0fc | 784 | if (p != NULL) |
79a15e0a | 785 | EBIT_SET(httpState->flags, HTTP_PROXYING); |
efb9218c | 786 | /* |
99edd1c3 | 787 | * Is keep-alive okay for all request methods? |
efb9218c | 788 | */ |
789 | if (p == NULL) | |
790 | EBIT_SET(httpState->flags, HTTP_KEEPALIVE); | |
791 | else if (p->stats.n_keepalives_sent < 10) | |
792 | EBIT_SET(httpState->flags, HTTP_KEEPALIVE); | |
793 | else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50) | |
794 | EBIT_SET(httpState->flags, HTTP_KEEPALIVE); | |
99edd1c3 | 795 | memBufDefInit(&mb); |
796 | httpBuildRequestPrefix(req, | |
79a15e0a | 797 | httpState->orig_request, |
6bf8443a | 798 | entry, |
99edd1c3 | 799 | &mb, |
603a02fd | 800 | cfd, |
801 | httpState->flags); | |
99edd1c3 | 802 | debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", fd, mb.buf); |
803 | comm_write_mbuf(fd, mb, sendHeaderDone, httpState); | |
090089c4 | 804 | } |
805 | ||
603a02fd | 806 | static int |
b716a8ad | 807 | httpSocketOpen(StoreEntry * entry, request_t * request) |
090089c4 | 808 | { |
9e4ad609 | 809 | int fd; |
9b312a19 | 810 | ErrorState *err; |
9e4ad609 | 811 | fd = comm_open(SOCK_STREAM, |
16b204c4 | 812 | 0, |
813 | Config.Addrs.tcp_outgoing, | |
814 | 0, | |
815 | COMM_NONBLOCKING, | |
9fb13bb6 | 816 | storeUrl(entry)); |
603a02fd | 817 | if (fd < 0) { |
79a15e0a | 818 | debug(50, 4) ("httpSocketOpen: %s\n", xstrerror()); |
fe40a877 | 819 | err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR); |
c45ed9ad | 820 | err->xerrno = errno; |
79a15e0a | 821 | err->request = requestLink(request); |
9b312a19 | 822 | errorAppendEntry(entry, err); |
090089c4 | 823 | } |
603a02fd | 824 | return fd; |
825 | } | |
826 | ||
827 | static HttpStateData * | |
828 | httpBuildState(int fd, StoreEntry * entry, request_t * orig_request, peer * e) | |
829 | { | |
cb87dab6 | 830 | HttpStateData *httpState = memAllocate(MEM_HTTP_STATE_DATA); |
603a02fd | 831 | request_t *request; |
770f051d | 832 | storeLockObject(entry); |
cb87dab6 | 833 | cbdataAdd(httpState, MEM_HTTP_STATE_DATA); |
0a0bf5db | 834 | httpState->entry = entry; |
9e4ad609 | 835 | httpState->fd = fd; |
603a02fd | 836 | if (e) { |
99edd1c3 | 837 | request = requestCreate( |
838 | orig_request->method, PROTO_NONE, storeUrl(entry)); | |
603a02fd | 839 | xstrncpy(request->host, e->host, SQUIDHOSTNAMELEN); |
840 | request->port = e->http_port; | |
603a02fd | 841 | httpState->request = requestLink(request); |
1294c0fc | 842 | httpState->peer = e; |
603a02fd | 843 | httpState->orig_request = requestLink(orig_request); |
79a15e0a | 844 | EBIT_SET(request->flags, REQ_PROXYING); |
603a02fd | 845 | } else { |
846 | httpState->request = requestLink(orig_request); | |
79a15e0a | 847 | httpState->orig_request = requestLink(orig_request); |
603a02fd | 848 | } |
0d4d4170 | 849 | /* register the handler to free HTTP state data when the FD closes */ |
603a02fd | 850 | comm_add_close_handler(httpState->fd, httpStateFree, httpState); |
e102ebda | 851 | storeRegisterAbort(entry, httpAbort, httpState); |
603a02fd | 852 | return httpState; |
853 | } | |
854 | ||
855 | void | |
856 | httpStart(request_t * request, StoreEntry * entry, peer * e) | |
857 | { | |
858 | HttpStateData *httpState; | |
859 | int fd; | |
860 | debug(11, 3) ("httpStart: \"%s %s\"\n", | |
9fb13bb6 | 861 | RequestMethodStr[request->method], storeUrl(entry)); |
a0f32775 | 862 | Counter.server.all.requests++; |
863 | Counter.server.http.requests++; | |
603a02fd | 864 | if (e) { |
a369131d | 865 | if (EBIT_TEST(e->options, NEIGHBOR_PROXY_ONLY)) |
603a02fd | 866 | storeReleaseRequest(entry); |
603a02fd | 867 | if ((fd = pconnPop(e->host, e->http_port)) >= 0) { |
51fdcbd5 | 868 | debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd); |
603a02fd | 869 | httpState = httpBuildState(fd, entry, request, e); |
b716a8ad | 870 | commSetTimeout(httpState->fd, |
871 | Config.Timeout.connect, | |
872 | httpTimeout, | |
873 | httpState); | |
603a02fd | 874 | httpConnectDone(fd, COMM_OK, httpState); |
875 | return; | |
876 | } | |
877 | } else { | |
878 | if ((fd = pconnPop(request->host, request->port)) >= 0) { | |
51fdcbd5 | 879 | debug(11, 3) ("httpStart: reusing pconn FD %d\n", fd); |
603a02fd | 880 | httpState = httpBuildState(fd, entry, request, e); |
b716a8ad | 881 | commSetTimeout(httpState->fd, |
882 | Config.Timeout.connect, | |
883 | httpTimeout, | |
884 | httpState); | |
603a02fd | 885 | httpConnectDone(fd, COMM_OK, httpState); |
886 | return; | |
887 | } | |
888 | } | |
79a15e0a | 889 | if ((fd = httpSocketOpen(entry, request)) < 0) |
603a02fd | 890 | return; |
891 | httpState = httpBuildState(fd, entry, request, e); | |
603a02fd | 892 | commSetTimeout(httpState->fd, |
893 | Config.Timeout.connect, | |
894 | httpTimeout, | |
cd1fb0eb | 895 | httpState); |
edeb28fd | 896 | commConnectStart(httpState->fd, |
603a02fd | 897 | httpState->request->host, |
898 | httpState->request->port, | |
e924600d | 899 | httpConnectDone, |
900 | httpState); | |
e5f6c5c2 | 901 | } |
902 | ||
efb9218c | 903 | static int |
904 | httpTryRestart(HttpStateData * httpState) | |
905 | { | |
906 | /* | |
907 | * We only retry the request if it looks like it was | |
908 | * on a persistent/pipelined connection | |
909 | */ | |
910 | if (fd_table[httpState->fd].uses < 2) | |
911 | return 0; | |
912 | if (pumpMethod(httpState->orig_request->method)) | |
913 | if (0 == pumpRestart(httpState->orig_request)) | |
914 | return 0; | |
915 | return 1; | |
916 | } | |
917 | ||
b716a8ad | 918 | static void |
919 | httpRestart(HttpStateData * httpState) | |
920 | { | |
921 | /* restart a botched request from a persistent connection */ | |
9fb13bb6 | 922 | debug(11, 2) ("Retrying HTTP request for %s\n", storeUrl(httpState->entry)); |
901e234d | 923 | if (pumpMethod(httpState->orig_request->method)) { |
cca378c3 | 924 | debug(11, 3) ("Potential Coredump: httpRestart %s %s\n", |
efb9218c | 925 | RequestMethodStr[httpState->orig_request->method], |
926 | storeUrl(httpState->entry)); | |
e371ed5c | 927 | } |
b716a8ad | 928 | if (httpState->fd >= 0) { |
929 | comm_remove_close_handler(httpState->fd, httpStateFree, httpState); | |
930 | comm_close(httpState->fd); | |
931 | httpState->fd = -1; | |
932 | } | |
79a15e0a | 933 | httpState->fd = httpSocketOpen(httpState->entry, httpState->orig_request); |
b716a8ad | 934 | if (httpState->fd < 0) |
935 | return; | |
936 | comm_add_close_handler(httpState->fd, httpStateFree, httpState); | |
937 | commSetTimeout(httpState->fd, | |
938 | Config.Timeout.connect, | |
939 | httpTimeout, | |
940 | httpState); | |
941 | commConnectStart(httpState->fd, | |
942 | httpState->request->host, | |
943 | httpState->request->port, | |
944 | httpConnectDone, | |
945 | httpState); | |
946 | } | |
947 | ||
e5f6c5c2 | 948 | static void |
949 | httpConnectDone(int fd, int status, void *data) | |
950 | { | |
951 | HttpStateData *httpState = data; | |
952 | request_t *request = httpState->request; | |
953 | StoreEntry *entry = httpState->entry; | |
9b312a19 | 954 | ErrorState *err; |
edeb28fd | 955 | if (status == COMM_ERR_DNS) { |
a3d5953d | 956 | debug(11, 4) ("httpConnectDone: Unknown host: %s\n", request->host); |
fe40a877 | 957 | err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE); |
9b312a19 | 958 | err->dnsserver_msg = xstrdup(dns_error_message); |
79a15e0a | 959 | err->request = requestLink(httpState->orig_request); |
9b312a19 | 960 | errorAppendEntry(entry, err); |
edeb28fd | 961 | comm_close(fd); |
962 | } else if (status != COMM_OK) { | |
fe40a877 | 963 | err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE); |
c45ed9ad | 964 | err->xerrno = errno; |
9b312a19 | 965 | err->host = xstrdup(request->host); |
966 | err->port = request->port; | |
79a15e0a | 967 | err->request = requestLink(httpState->orig_request); |
9b312a19 | 968 | errorAppendEntry(entry, err); |
1294c0fc | 969 | if (httpState->peer) |
970 | peerCheckConnectStart(httpState->peer); | |
e5f6c5c2 | 971 | comm_close(fd); |
972 | } else { | |
9fb13bb6 | 973 | fd_note(fd, storeUrl(entry)); |
b716a8ad | 974 | fd_table[fd].uses++; |
bfcaf585 | 975 | commSetSelect(fd, COMM_SELECT_WRITE, httpSendRequest, httpState, 0); |
86cf9987 | 976 | commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); |
090089c4 | 977 | } |
090089c4 | 978 | } |
979 | ||
bfcaf585 | 980 | static void |
981 | httpAbort(void *data) | |
982 | { | |
983 | HttpStateData *httpState = data; | |
9fb13bb6 | 984 | debug(11, 2) ("httpAbort: %s\n", storeUrl(httpState->entry)); |
bfcaf585 | 985 | comm_close(httpState->fd); |
986 | } | |
9b312a19 | 987 | |
54220df8 | 988 | static void |
7db8b16d | 989 | httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data) |
54220df8 | 990 | { |
991 | HttpStateData *httpState = data; | |
992 | StoreEntry *entry = httpState->entry; | |
993 | ErrorState *err; | |
994 | debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n", | |
7db8b16d | 995 | fd, size, errflag); |
54220df8 | 996 | if (size > 0) { |
7db8b16d | 997 | fd_bytes(fd, size, FD_WRITE); |
54220df8 | 998 | kb_incr(&Counter.server.all.kbytes_out, size); |
999 | kb_incr(&Counter.server.http.kbytes_out, size); | |
1000 | } | |
1001 | if (errflag == COMM_ERR_CLOSING) | |
7db8b16d | 1002 | return; |
54220df8 | 1003 | if (errflag) { |
7db8b16d | 1004 | err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); |
1005 | err->xerrno = errno; | |
1006 | err->request = requestLink(httpState->orig_request); | |
1007 | errorAppendEntry(entry, err); | |
1008 | comm_close(fd); | |
1009 | return; | |
54220df8 | 1010 | } |
7db8b16d | 1011 | pumpStart(fd, entry, httpState->orig_request, httpSendComplete, httpState); |
54220df8 | 1012 | } |