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