]>
Commit | Line | Data |
---|---|---|
da2b3a17 | 1 | |
30a4f2a8 | 2 | /* |
eb824054 | 3 | * $Id: http.cc,v 1.351 1999/07/13 14:51:14 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/ |
e25c139f | 9 | * ---------------------------------------------------------- |
30a4f2a8 | 10 | * |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
e25c139f | 13 | * National Laboratory for Applied Network Research and funded by the |
14 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
15 | * Duane Wessels and the University of California San Diego. Please | |
16 | * see the COPYRIGHT file for full details. Squid incorporates | |
17 | * software developed and/or copyrighted by other sources. Please see | |
18 | * the CREDITS file for full details. | |
30a4f2a8 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
30a4f2a8 | 34 | */ |
019dd986 | 35 | |
4a83b852 | 36 | /* |
37 | * Anonymizing patch by lutz@as-node.jena.thur.de | |
de3bdb4c | 38 | * have a look into http-anon.c to get more informations. |
4a83b852 | 39 | */ |
40 | ||
44a47c6e | 41 | #include "squid.h" |
090089c4 | 42 | |
6bf8443a | 43 | static const char *const crlf = "\r\n"; |
4db43fab | 44 | |
9e4ad609 | 45 | static CWCB httpSendComplete; |
54220df8 | 46 | static CWCB httpSendRequestEntry; |
376bb137 | 47 | static CWCB httpSendRequestEntryDone; |
54220df8 | 48 | |
9e4ad609 | 49 | static PF httpReadReply; |
b6a2f15e | 50 | static void httpSendRequest(HttpStateData *); |
9e4ad609 | 51 | static PF httpStateFree; |
52 | static PF httpTimeout; | |
f5b8bbc4 | 53 | static void httpCacheNegatively(StoreEntry *); |
54 | static void httpMakePrivate(StoreEntry *); | |
55 | static void httpMakePublic(StoreEntry *); | |
f8309b15 | 56 | static int httpCachableReply(HttpStateData *); |
f9cece6e | 57 | static void httpMaybeRemovePublic(StoreEntry *, http_status); |
b8d8561b | 58 | |
b177367b | 59 | static void |
59715b38 | 60 | httpStateFree(int fd, void *data) |
f5558c95 | 61 | { |
b177367b | 62 | HttpStateData *httpState = data; |
59715b38 | 63 | #if DELAY_POOLS |
64 | delayClearNoDelay(fd); | |
65 | #endif | |
0d4d4170 | 66 | if (httpState == NULL) |
b177367b | 67 | return; |
f88211e8 | 68 | storeUnlockObject(httpState->entry); |
0d4d4170 | 69 | if (httpState->reply_hdr) { |
db1cd23c | 70 | memFree(httpState->reply_hdr, MEM_8K_BUF); |
0d4d4170 | 71 | httpState->reply_hdr = NULL; |
72 | } | |
30a4f2a8 | 73 | requestUnlink(httpState->request); |
20cc1450 | 74 | requestUnlink(httpState->orig_request); |
7dd44885 | 75 | httpState->request = NULL; |
76 | httpState->orig_request = NULL; | |
77 | cbdataFree(httpState); | |
f5558c95 | 78 | } |
79 | ||
b8d8561b | 80 | int |
75e88d56 | 81 | httpCachable(method_t method) |
090089c4 | 82 | { |
090089c4 | 83 | /* GET and HEAD are cachable. Others are not. */ |
6eb42cae | 84 | if (method != METHOD_GET && method != METHOD_HEAD) |
090089c4 | 85 | return 0; |
090089c4 | 86 | /* else cachable */ |
87 | return 1; | |
88 | } | |
89 | ||
b8d8561b | 90 | static void |
5c5783a2 | 91 | httpTimeout(int fd, void *data) |
090089c4 | 92 | { |
b177367b | 93 | HttpStateData *httpState = data; |
593c9a75 | 94 | StoreEntry *entry = httpState->entry; |
9fb13bb6 | 95 | debug(11, 4) ("httpTimeout: FD %d: '%s'\n", fd, storeUrl(entry)); |
12158bdc | 96 | if (entry->store_status == STORE_PENDING) { |
97 | if (entry->mem_obj->inmem_hi == 0) { | |
98 | fwdFail(httpState->fwd, | |
99 | errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT)); | |
100 | } | |
9b312a19 | 101 | } |
0d4d4170 | 102 | comm_close(fd); |
090089c4 | 103 | } |
104 | ||
30a4f2a8 | 105 | /* This object can be cached for a long time */ |
b8d8561b | 106 | static void |
107 | httpMakePublic(StoreEntry * entry) | |
30a4f2a8 | 108 | { |
d46a87a8 | 109 | if (EBIT_TEST(entry->flags, ENTRY_CACHABLE)) |
30a4f2a8 | 110 | storeSetPublicKey(entry); |
111 | } | |
112 | ||
113 | /* This object should never be cached at all */ | |
b8d8561b | 114 | static void |
115 | httpMakePrivate(StoreEntry * entry) | |
30a4f2a8 | 116 | { |
30a4f2a8 | 117 | storeExpireNow(entry); |
30a4f2a8 | 118 | storeReleaseRequest(entry); /* delete object when not used */ |
f3e570e9 | 119 | /* storeReleaseRequest clears ENTRY_CACHABLE flag */ |
30a4f2a8 | 120 | } |
121 | ||
122 | /* This object may be negatively cached */ | |
b8d8561b | 123 | static void |
124 | httpCacheNegatively(StoreEntry * entry) | |
30a4f2a8 | 125 | { |
79b5cc5f | 126 | storeNegativeCache(entry); |
d46a87a8 | 127 | if (EBIT_TEST(entry->flags, ENTRY_CACHABLE)) |
30a4f2a8 | 128 | storeSetPublicKey(entry); |
30a4f2a8 | 129 | } |
130 | ||
f9cece6e | 131 | static void |
132 | httpMaybeRemovePublic(StoreEntry * e, http_status status) | |
133 | { | |
134 | int remove = 0; | |
f9cece6e | 135 | StoreEntry *pe; |
d46a87a8 | 136 | if (!EBIT_TEST(e->flags, KEY_PRIVATE)) |
9dc1202d | 137 | return; |
f9cece6e | 138 | switch (status) { |
139 | case HTTP_OK: | |
140 | case HTTP_NON_AUTHORITATIVE_INFORMATION: | |
141 | case HTTP_MULTIPLE_CHOICES: | |
142 | case HTTP_MOVED_PERMANENTLY: | |
143 | case HTTP_MOVED_TEMPORARILY: | |
144 | case HTTP_FORBIDDEN: | |
145 | case HTTP_NOT_FOUND: | |
146 | case HTTP_METHOD_NOT_ALLOWED: | |
147 | case HTTP_GONE: | |
148 | remove = 1; | |
149 | break; | |
150 | #if WORK_IN_PROGRESS | |
c8fd0193 | 151 | case HTTP_UNAUTHORIZED: |
152 | remove = 1; | |
f9cece6e | 153 | break; |
154 | #endif | |
155 | default: | |
156 | remove = 0; | |
157 | break; | |
158 | } | |
159 | if (!remove) | |
160 | return; | |
161 | assert(e->mem_obj); | |
08e5d64f | 162 | if ((pe = storeGetPublic(e->mem_obj->url, e->mem_obj->method)) != NULL) { |
0856d155 | 163 | assert(e != pe); |
164 | storeRelease(pe); | |
165 | } | |
166 | if (e->mem_obj->method == METHOD_GET) { | |
167 | /* A fresh GET should eject old HEAD objects */ | |
08e5d64f | 168 | if ((pe = storeGetPublic(e->mem_obj->url, METHOD_HEAD)) != NULL) { |
0856d155 | 169 | assert(e != pe); |
170 | storeRelease(pe); | |
171 | } | |
172 | } | |
f9cece6e | 173 | } |
174 | ||
f8309b15 | 175 | static int |
176 | httpCachableReply(HttpStateData * httpState) | |
c54e9052 | 177 | { |
d8b249ef | 178 | HttpReply *rep = httpState->entry->mem_obj->reply; |
179 | HttpHeader *hdr = &rep->header; | |
180 | const int cc_mask = (rep->cache_control) ? rep->cache_control->mask : 0; | |
c68e9c6b | 181 | const char *v; |
7faf2bdb | 182 | if (EBIT_TEST(cc_mask, CC_PRIVATE)) |
f8309b15 | 183 | return 0; |
7faf2bdb | 184 | if (EBIT_TEST(cc_mask, CC_NO_CACHE)) |
f8309b15 | 185 | return 0; |
ed2f05a1 | 186 | if (EBIT_TEST(cc_mask, CC_NO_STORE)) |
187 | return 0; | |
92695e5e | 188 | if (httpState->request->flags.auth) { |
a6dfe2d9 | 189 | /* |
190 | * Responses to requests with authorization may be cached | |
68aefb7d | 191 | * only if a Cache-Control: public reply header is present. |
a6dfe2d9 | 192 | * RFC 2068, sec 14.9.4 |
193 | */ | |
194 | if (!EBIT_TEST(cc_mask, CC_PUBLIC)) | |
fee0cebb | 195 | return 0; |
a6dfe2d9 | 196 | } |
f8309b15 | 197 | /* |
02fe0fbc | 198 | * We don't properly deal with Vary features yet, so we can't |
199 | * cache these | |
f8309b15 | 200 | */ |
783e4699 | 201 | if (httpHeaderHas(hdr, HDR_VARY)) |
202 | return 0; | |
c68e9c6b | 203 | /* Pragma: no-cache in _replies_ is not documented in HTTP, |
204 | * but servers like "Active Imaging Webcast/2.0" sure do use it */ | |
205 | if (httpHeaderHas(hdr, HDR_PRAGMA)) { | |
206 | String s = httpHeaderGetList(hdr, HDR_PRAGMA); | |
207 | const int no_cache = strListIsMember(&s, "no-cache", ','); | |
208 | stringClean(&s); | |
209 | if (no_cache) | |
210 | return 0; | |
211 | } | |
212 | /* | |
213 | * The "multipart/x-mixed-replace" content type is used for | |
214 | * continuous push replies. These are generally dynamic and | |
215 | * probably should not be cachable | |
216 | */ | |
217 | if ((v = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE))) | |
218 | if (!strncasecmp(v, "multipart/x-mixed-replace", 25)) | |
219 | return 0; | |
cb69b4c7 | 220 | switch (httpState->entry->mem_obj->reply->sline.status) { |
c54e9052 | 221 | /* Responses that are cacheable */ |
19a04dac | 222 | case HTTP_OK: |
223 | case HTTP_NON_AUTHORITATIVE_INFORMATION: | |
224 | case HTTP_MULTIPLE_CHOICES: | |
225 | case HTTP_MOVED_PERMANENTLY: | |
226 | case HTTP_GONE: | |
cfa9f1cb | 227 | /* |
228 | * Don't cache objects that need to be refreshed on next request, | |
229 | * unless we know how to refresh it. | |
230 | */ | |
231 | if (!refreshIsCachable(httpState->entry)) | |
232 | return 0; | |
1294c0fc | 233 | /* don't cache objects from peers w/o LMT, Date, or Expires */ |
cb69b4c7 | 234 | /* check that is it enough to check headers @?@ */ |
d8b249ef | 235 | if (rep->date > -1) |
c54e9052 | 236 | return 1; |
d8b249ef | 237 | else if (rep->last_modified > -1) |
c54e9052 | 238 | return 1; |
1294c0fc | 239 | else if (!httpState->peer) |
c54e9052 | 240 | return 1; |
d8b249ef | 241 | /* @?@ (here and 302): invalid expires header compiles to squid_curtime */ |
242 | else if (rep->expires > -1) | |
c54e9052 | 243 | return 1; |
c54e9052 | 244 | else |
245 | return 0; | |
79d39a72 | 246 | /* NOTREACHED */ |
c54e9052 | 247 | break; |
248 | /* Responses that only are cacheable if the server says so */ | |
19a04dac | 249 | case HTTP_MOVED_TEMPORARILY: |
d8b249ef | 250 | if (rep->expires > -1) |
c54e9052 | 251 | return 1; |
252 | else | |
253 | return 0; | |
79d39a72 | 254 | /* NOTREACHED */ |
c54e9052 | 255 | break; |
256 | /* Errors can be negatively cached */ | |
19a04dac | 257 | case HTTP_NO_CONTENT: |
258 | case HTTP_USE_PROXY: | |
259 | case HTTP_BAD_REQUEST: | |
260 | case HTTP_FORBIDDEN: | |
261 | case HTTP_NOT_FOUND: | |
262 | case HTTP_METHOD_NOT_ALLOWED: | |
263 | case HTTP_REQUEST_URI_TOO_LARGE: | |
264 | case HTTP_INTERNAL_SERVER_ERROR: | |
265 | case HTTP_NOT_IMPLEMENTED: | |
266 | case HTTP_BAD_GATEWAY: | |
267 | case HTTP_SERVICE_UNAVAILABLE: | |
268 | case HTTP_GATEWAY_TIMEOUT: | |
c54e9052 | 269 | return -1; |
79d39a72 | 270 | /* NOTREACHED */ |
c54e9052 | 271 | break; |
272 | /* Some responses can never be cached */ | |
0cdcddb9 | 273 | case HTTP_PARTIAL_CONTENT: /* Not yet supported */ |
19a04dac | 274 | case HTTP_SEE_OTHER: |
275 | case HTTP_NOT_MODIFIED: | |
276 | case HTTP_UNAUTHORIZED: | |
277 | case HTTP_PROXY_AUTHENTICATION_REQUIRED: | |
0cdcddb9 | 278 | case HTTP_INVALID_HEADER: /* Squid header parsing error */ |
c54e9052 | 279 | default: /* Unknown status code */ |
280 | return 0; | |
79d39a72 | 281 | /* NOTREACHED */ |
c54e9052 | 282 | break; |
283 | } | |
79d39a72 | 284 | /* NOTREACHED */ |
c54e9052 | 285 | } |
090089c4 | 286 | |
cb69b4c7 | 287 | /* rewrite this later using new interfaces @?@ */ |
b8d8561b | 288 | void |
0ee4272b | 289 | httpProcessReplyHeader(HttpStateData * httpState, const char *buf, int size) |
f5558c95 | 290 | { |
291 | char *t = NULL; | |
30a4f2a8 | 292 | StoreEntry *entry = httpState->entry; |
d3fb4dea | 293 | int room; |
294 | int hdr_len; | |
cb69b4c7 | 295 | HttpReply *reply = entry->mem_obj->reply; |
b6cfb65c | 296 | debug(11, 3) ("httpProcessReplyHeader: key '%s'\n", |
297 | storeKeyText(entry->key)); | |
e924600d | 298 | if (httpState->reply_hdr == NULL) |
7021844c | 299 | httpState->reply_hdr = memAllocate(MEM_8K_BUF); |
30a4f2a8 | 300 | if (httpState->reply_hdr_state == 0) { |
301 | hdr_len = strlen(httpState->reply_hdr); | |
ed85b771 | 302 | room = 8191 - hdr_len; |
30a4f2a8 | 303 | strncat(httpState->reply_hdr, buf, room < size ? room : size); |
d3fb4dea | 304 | hdr_len += room < size ? room : size; |
30a4f2a8 | 305 | if (hdr_len > 4 && strncmp(httpState->reply_hdr, "HTTP/", 5)) { |
84fa351c | 306 | debug(11, 3) ("httpProcessReplyHeader: Non-HTTP-compliant header: '%s'\n", httpState->reply_hdr); |
30a4f2a8 | 307 | httpState->reply_hdr_state += 2; |
728da2ee | 308 | reply->sline.status = HTTP_INVALID_HEADER; |
ed85b771 | 309 | return; |
d3fb4dea | 310 | } |
d1a43e28 | 311 | t = httpState->reply_hdr + hdr_len; |
312 | /* headers can be incomplete only if object still arriving */ | |
2334c194 | 313 | if (!httpState->eof) { |
314 | size_t k = headersEnd(httpState->reply_hdr, 8192); | |
315 | if (0 == k) | |
d1a43e28 | 316 | return; /* headers not complete */ |
2334c194 | 317 | t = httpState->reply_hdr + k; |
318 | } | |
2285407f | 319 | *t = '\0'; |
30a4f2a8 | 320 | httpState->reply_hdr_state++; |
f5558c95 | 321 | } |
30a4f2a8 | 322 | if (httpState->reply_hdr_state == 1) { |
123abbe1 | 323 | const Ctx ctx = ctx_enter(entry->mem_obj->url); |
30a4f2a8 | 324 | httpState->reply_hdr_state++; |
a3d5953d | 325 | debug(11, 9) ("GOT HTTP REPLY HDR:\n---------\n%s\n----------\n", |
30a4f2a8 | 326 | httpState->reply_hdr); |
327 | /* Parse headers into reply structure */ | |
2246b732 | 328 | /* what happens if we fail to parse here? */ |
ee1679df | 329 | httpReplyParse(reply, httpState->reply_hdr); /* httpState->eof); */ |
ca98227c | 330 | storeTimestampsSet(entry); |
30a4f2a8 | 331 | /* Check if object is cacheable or not based on reply code */ |
cb69b4c7 | 332 | debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status); |
9f239bed | 333 | if (neighbors_do_private_keys) |
334 | httpMaybeRemovePublic(entry, reply->sline.status); | |
f8309b15 | 335 | switch (httpCachableReply(httpState)) { |
c54e9052 | 336 | case 1: |
337 | httpMakePublic(entry); | |
30a4f2a8 | 338 | break; |
c54e9052 | 339 | case 0: |
340 | httpMakePrivate(entry); | |
f5558c95 | 341 | break; |
c54e9052 | 342 | case -1: |
851eeef7 | 343 | httpCacheNegatively(entry); |
30a4f2a8 | 344 | break; |
c54e9052 | 345 | default: |
346 | assert(0); | |
4e38e700 | 347 | break; |
f5558c95 | 348 | } |
0336304c | 349 | if (reply->cache_control) { |
350 | if (EBIT_TEST(reply->cache_control->mask, CC_PROXY_REVALIDATE)) | |
d46a87a8 | 351 | EBIT_SET(entry->flags, ENTRY_REVALIDATE); |
308e4a84 | 352 | else if (EBIT_TEST(reply->cache_control->mask, CC_MUST_REVALIDATE)) |
d46a87a8 | 353 | EBIT_SET(entry->flags, ENTRY_REVALIDATE); |
0336304c | 354 | } |
b515fc11 | 355 | if (httpState->flags.keepalive) |
9a47da71 | 356 | if (httpState->peer) |
357 | httpState->peer->stats.n_keepalives_sent++; | |
9f5a2895 | 358 | if (reply->keep_alive) |
1294c0fc | 359 | if (httpState->peer) |
360 | httpState->peer->stats.n_keepalives_recv++; | |
123abbe1 | 361 | ctx_exit(ctx); |
fc4d5b17 | 362 | if (reply->date > -1 && !httpState->peer) { |
363 | int skew = abs(reply->date - squid_curtime); | |
e1c811de | 364 | if (skew > 86400) |
365 | debug(11, 3) ("%s's clock is skewed by %d seconds!\n", | |
7d47d8e6 | 366 | httpState->request->host, skew); |
fc4d5b17 | 367 | } |
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; |
35282fbf | 377 | int clen; |
51fdcbd5 | 378 | debug(11, 3) ("httpPconnTransferDone: FD %d\n", httpState->fd); |
978e455f | 379 | /* |
99edd1c3 | 380 | * If we didn't send a keep-alive request header, then this |
978e455f | 381 | * can not be a persistent connection. |
382 | */ | |
b515fc11 | 383 | if (!httpState->flags.keepalive) |
603a02fd | 384 | return 0; |
9f5a2895 | 385 | /* |
386 | * What does the reply have to say about keep-alive? | |
387 | */ | |
b6a2f15e | 388 | /* |
389 | * XXX BUG? | |
390 | * If the origin server (HTTP/1.0) does not send a keep-alive | |
391 | * header, but keeps the connection open anyway, what happens? | |
392 | * We'll return here and http.c waits for an EOF before changing | |
393 | * store_status to STORE_OK. Combine this with ENTRY_FWD_HDR_WAIT | |
394 | * and an error status code, and we might have to wait until | |
395 | * the server times out the socket. | |
396 | */ | |
9f5a2895 | 397 | if (!reply->keep_alive) |
398 | return 0; | |
51fdcbd5 | 399 | debug(11, 5) ("httpPconnTransferDone: content_length=%d\n", |
d8b249ef | 400 | reply->content_length); |
35282fbf | 401 | /* If we haven't seen the end of reply headers, we are not done */ |
978e455f | 402 | if (httpState->reply_hdr_state < 2) |
403 | return 0; | |
35282fbf | 404 | clen = httpReplyBodySize(httpState->request->method, reply); |
405 | /* If there is no message body, we can be persistent */ | |
406 | if (0 == clen) | |
a3c60429 | 407 | return 1; |
35282fbf | 408 | /* If the body size is unknown we must wait for EOF */ |
409 | if (clen < 0) | |
603a02fd | 410 | return 0; |
35282fbf | 411 | /* If the body size is known, we must wait until we've gotten all of it. */ |
412 | if (mem->inmem_hi < reply->content_length + reply->hdr_sz) | |
603a02fd | 413 | return 0; |
35282fbf | 414 | /* We got it all */ |
415 | return 1; | |
603a02fd | 416 | } |
090089c4 | 417 | |
418 | /* This will be called when data is ready to be read from fd. Read until | |
419 | * error or connection closed. */ | |
f5558c95 | 420 | /* XXX this function is too long! */ |
b8d8561b | 421 | static void |
b177367b | 422 | httpReadReply(int fd, void *data) |
090089c4 | 423 | { |
b177367b | 424 | HttpStateData *httpState = data; |
95d659f0 | 425 | LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF); |
bfcaf585 | 426 | StoreEntry *entry = httpState->entry; |
603a02fd | 427 | const request_t *request = httpState->request; |
090089c4 | 428 | int len; |
30a4f2a8 | 429 | int bin; |
090089c4 | 430 | int clen; |
447e176b | 431 | size_t read_sz; |
432 | #if DELAY_POOLS | |
59715b38 | 433 | delay_id delay_id; |
434 | ||
435 | /* special "if" only for http (for nodelay proxy conns) */ | |
436 | if (delayIsNoDelay(fd)) | |
437 | delay_id = 0; | |
438 | else | |
439 | delay_id = delayMostBytesAllowed(entry->mem_obj); | |
447e176b | 440 | #endif |
e92e4e44 | 441 | if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { |
442 | comm_close(fd); | |
443 | return; | |
444 | } | |
234967c9 | 445 | /* check if we want to defer reading */ |
1513873c | 446 | errno = 0; |
447e176b | 447 | read_sz = SQUID_TCP_SO_RCVBUF; |
448 | #if DELAY_POOLS | |
56e64999 | 449 | read_sz = delayBytesWanted(delay_id, 1, read_sz); |
447e176b | 450 | #endif |
886f2785 | 451 | Counter.syscalls.sock.reads++; |
447e176b | 452 | len = read(fd, buf, read_sz); |
a3d5953d | 453 | debug(11, 5) ("httpReadReply: FD %d: len %d.\n", fd, len); |
30a4f2a8 | 454 | if (len > 0) { |
ee1679df | 455 | fd_bytes(fd, len, FD_READ); |
447e176b | 456 | #if DELAY_POOLS |
457 | delayBytesIn(delay_id, len); | |
458 | #endif | |
a0f32775 | 459 | kb_incr(&Counter.server.all.kbytes_in, len); |
460 | kb_incr(&Counter.server.http.kbytes_in, len); | |
4f92c80c | 461 | commSetTimeout(fd, Config.Timeout.read, NULL, NULL); |
4a63c85f | 462 | IOStats.Http.reads++; |
30a4f2a8 | 463 | for (clen = len - 1, bin = 0; clen; bin++) |
464 | clen >>= 1; | |
465 | IOStats.Http.read_hist[bin]++; | |
466 | } | |
5ede6c8f | 467 | if (!httpState->reply_hdr && len > 0) { |
468 | /* Skip whitespace */ | |
b6a2f15e | 469 | while (len > 0 && xisspace(*buf)) |
5ede6c8f | 470 | xmemmove(buf, buf + 1, len--); |
471 | if (len == 0) { | |
472 | /* Continue to read... */ | |
473 | commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); | |
474 | return; | |
475 | } | |
476 | } | |
ba718c8f | 477 | if (len < 0) { |
55cb44f1 | 478 | debug(50, 2) ("httpReadReply: FD %d: read failure: %s.\n", |
479 | fd, xstrerror()); | |
b224ea98 | 480 | if (ignoreErrno(errno)) { |
9b312a19 | 481 | commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); |
910169e5 | 482 | } else if (entry->mem_obj->inmem_hi == 0) { |
ec250dfd | 483 | ErrorState *err; |
484 | err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR); | |
485 | err->xerrno = errno; | |
486 | fwdFail(httpState->fwd, err); | |
1afe05c5 | 487 | comm_close(fd); |
090089c4 | 488 | } else { |
0d4d4170 | 489 | comm_close(fd); |
090089c4 | 490 | } |
8350fe9b | 491 | } else if (len == 0 && entry->mem_obj->inmem_hi == 0) { |
ec250dfd | 492 | ErrorState *err; |
493 | err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE); | |
494 | err->xerrno = errno; | |
495 | fwdFail(httpState->fwd, err); | |
910169e5 | 496 | httpState->eof = 1; |
497 | comm_close(fd); | |
090089c4 | 498 | } else if (len == 0) { |
499 | /* Connection closed; retrieval done. */ | |
f86a6a46 | 500 | httpState->eof = 1; |
d1a43e28 | 501 | if (httpState->reply_hdr_state < 2) |
b34ed725 | 502 | /* |
503 | * Yes Henrik, there is a point to doing this. When we | |
504 | * called httpProcessReplyHeader() before, we didn't find | |
505 | * the end of headers, but now we are definately at EOF, so | |
506 | * we want to process the reply headers. | |
507 | */ | |
d1a43e28 | 508 | httpProcessReplyHeader(httpState, buf, len); |
db1cd23c | 509 | fwdComplete(httpState->fwd); |
0d4d4170 | 510 | comm_close(fd); |
090089c4 | 511 | } else { |
7e3e1d01 | 512 | if (httpState->reply_hdr_state < 2) { |
30a4f2a8 | 513 | httpProcessReplyHeader(httpState, buf, len); |
db1cd23c | 514 | if (httpState->reply_hdr_state == 2) { |
515 | http_status s = entry->mem_obj->reply->sline.status; | |
b6a2f15e | 516 | /* |
517 | * If its not a reply that we will re-forward, then | |
518 | * allow the client to get it. | |
db1cd23c | 519 | */ |
b6a2f15e | 520 | if (!fwdReforwardableStatus(s)) |
db1cd23c | 521 | EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); |
522 | } | |
7e3e1d01 | 523 | } |
620da955 | 524 | storeAppend(entry, buf, len); |
b7fe0ab0 | 525 | if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { |
9d66d521 | 526 | /* |
527 | * the above storeAppend() call could ABORT this entry, | |
528 | * in that case, the server FD should already be closed. | |
529 | * there's nothing for us to do. | |
530 | */ | |
531 | (void) 0; | |
d9627979 | 532 | } else if (httpPconnTransferDone(httpState)) { |
5b29969a | 533 | /* yes we have to clear all these! */ |
8796b9e9 | 534 | commSetDefer(fd, NULL, NULL); |
5b29969a | 535 | commSetTimeout(fd, -1, NULL, NULL); |
536 | commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); | |
ec603b25 | 537 | #if DELAY_POOLS |
538 | delayClearNoDelay(fd); | |
539 | #endif | |
603a02fd | 540 | comm_remove_close_handler(fd, httpStateFree, httpState); |
db1cd23c | 541 | fwdUnregister(fd, httpState->fwd); |
8796b9e9 | 542 | pconnPush(fd, request->host, request->port); |
8a28f65f | 543 | fwdComplete(httpState->fwd); |
603a02fd | 544 | httpState->fd = -1; |
59715b38 | 545 | httpStateFree(fd, httpState); |
603a02fd | 546 | } else { |
9f5a2895 | 547 | /* Wait for EOF condition */ |
603a02fd | 548 | commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); |
549 | } | |
090089c4 | 550 | } |
551 | } | |
552 | ||
553 | /* This will be called when request write is complete. Schedule read of | |
554 | * reply. */ | |
b8d8561b | 555 | static void |
79a15e0a | 556 | httpSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) |
090089c4 | 557 | { |
30a4f2a8 | 558 | HttpStateData *httpState = data; |
9b312a19 | 559 | StoreEntry *entry = httpState->entry; |
560 | ErrorState *err; | |
a3d5953d | 561 | debug(11, 5) ("httpSendComplete: FD %d: size %d: errflag %d.\n", |
090089c4 | 562 | fd, size, errflag); |
ee1679df | 563 | if (size > 0) { |
564 | fd_bytes(fd, size, FD_WRITE); | |
a0f32775 | 565 | kb_incr(&Counter.server.all.kbytes_out, size); |
399e85ea | 566 | kb_incr(&Counter.server.http.kbytes_out, size); |
ee1679df | 567 | } |
ea3a2a69 | 568 | if (errflag == COMM_ERR_CLOSING) |
569 | return; | |
090089c4 | 570 | if (errflag) { |
fe40a877 | 571 | err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); |
c45ed9ad | 572 | err->xerrno = errno; |
79a15e0a | 573 | err->request = requestLink(httpState->orig_request); |
9b312a19 | 574 | errorAppendEntry(entry, err); |
0d4d4170 | 575 | comm_close(fd); |
090089c4 | 576 | return; |
577 | } else { | |
578 | /* Schedule read reply. */ | |
b6a2f15e | 579 | commSetSelect(fd, COMM_SELECT_READ, httpReadReply, httpState, 0); |
580 | /* | |
581 | * Set the read timeout here because it hasn't been set yet. | |
582 | * We only set the read timeout after the request has been | |
583 | * fully written to the server-side. If we start the timeout | |
584 | * after connection establishment, then we are likely to hit | |
585 | * the timeout for POST/PUT requests that have very large | |
586 | * request bodies. | |
587 | */ | |
588 | commSetTimeout(fd, Config.Timeout.read, httpTimeout, httpState); | |
41462d93 | 589 | commSetDefer(fd, fwdCheckDeferRead, entry); |
090089c4 | 590 | } |
591 | } | |
592 | ||
99edd1c3 | 593 | /* |
594 | * build request headers and append them to a given MemBuf | |
595 | * used by httpBuildRequestPrefix() | |
596 | * note: calls httpHeaderInit(), the caller is responsible for Clean()-ing | |
597 | */ | |
e1e72f06 | 598 | void |
6bf8443a | 599 | httpBuildRequestHeader(request_t * request, |
600 | request_t * orig_request, | |
601 | StoreEntry * entry, | |
5999b776 | 602 | HttpHeader * hdr_out, |
603a02fd | 603 | int cfd, |
b515fc11 | 604 | http_state_flags flags) |
6bf8443a | 605 | { |
99edd1c3 | 606 | /* building buffer for complex strings */ |
5999b776 | 607 | #define BBUF_SZ (MAX_URL+32) |
99edd1c3 | 608 | LOCAL_ARRAY(char, bbuf, BBUF_SZ); |
609 | String strConnection = StringNull; | |
610 | const HttpHeader *hdr_in = &orig_request->header; | |
5d679edb | 611 | int we_do_ranges; |
99edd1c3 | 612 | const HttpHeaderEntry *e; |
613 | HttpHeaderPos pos = HttpHeaderInitPos; | |
2246b732 | 614 | httpHeaderInit(hdr_out, hoRequest); |
99edd1c3 | 615 | /* append our IMS header */ |
e17dc75c | 616 | if (entry && entry->lastmod > -1 && request->method == METHOD_GET) |
99edd1c3 | 617 | httpHeaderPutTime(hdr_out, HDR_IF_MODIFIED_SINCE, entry->lastmod); |
618 | ||
5d679edb | 619 | /* decide if we want to do Ranges ourselves |
620 | * (and fetch the whole object now) | |
621 | * We want to handle Ranges ourselves iff | |
622 | * - we can actually parse client Range specs | |
623 | * - the specs are expected to be simple enough (e.g. no out-of-order ranges) | |
624 | * - reply will be cachable | |
db1cd23c | 625 | * (If the reply will be uncachable we have to throw it away after |
5d679edb | 626 | * serving this request, so it is better to forward ranges to |
627 | * the server and fetch only the requested content) | |
628 | */ | |
629 | we_do_ranges = | |
c68e9c6b | 630 | orig_request->range && orig_request->flags.cachable && !httpHdrRangeWillBeComplex(orig_request->range) && (Config.rangeOffsetLimit == -1 || httpHdrRangeFirstOffset(orig_request->range) <= Config.rangeOffsetLimit); |
5d679edb | 631 | debug(11, 8) ("httpBuildRequestHeader: range specs: %p, cachable: %d; we_do_ranges: %d\n", |
632 | orig_request->range, orig_request->flags.cachable, we_do_ranges); | |
137ee196 | 633 | |
99edd1c3 | 634 | strConnection = httpHeaderGetList(hdr_in, HDR_CONNECTION); |
635 | while ((e = httpHeaderGetEntry(hdr_in, &pos))) { | |
636 | debug(11, 5) ("httpBuildRequestHeader: %s: %s\n", | |
637 | strBuf(e->name), strBuf(e->value)); | |
638 | if (!httpRequestHdrAllowed(e, &strConnection)) | |
6bf8443a | 639 | continue; |
99edd1c3 | 640 | switch (e->id) { |
641 | case HDR_PROXY_AUTHORIZATION: | |
c68e9c6b | 642 | /* If we're not doing proxy auth, then it must be passed on */ |
92695e5e | 643 | if (!request->flags.used_proxy_auth) |
99edd1c3 | 644 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); |
645 | break; | |
c68e9c6b | 646 | case HDR_AUTHORIZATION: |
647 | /* If we're not doing www auth, then it must be passed on */ | |
648 | if (!request->flags.accelerated || !request->flags.used_proxy_auth) | |
649 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); | |
650 | else | |
651 | request->flags.auth = 0; /* We have used the authentication */ | |
652 | break; | |
99edd1c3 | 653 | case HDR_HOST: |
77ed547a | 654 | /* Don't use client's Host: header for redirected requests */ |
c68e9c6b | 655 | if (!request->flags.redirected || !Config.onoff.redir_rewrites_host) |
99edd1c3 | 656 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); |
657 | break; | |
658 | case HDR_IF_MODIFIED_SINCE: | |
659 | /* append unless we added our own; | |
660 | * note: at most one client's ims header can pass through */ | |
661 | if (!httpHeaderHas(hdr_out, HDR_IF_MODIFIED_SINCE)) | |
662 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); | |
663 | break; | |
664 | case HDR_MAX_FORWARDS: | |
b3b64e58 | 665 | if (orig_request->method == METHOD_TRACE) { |
99edd1c3 | 666 | /* sacrificing efficiency over clarity, etc. */ |
667 | const int hops = httpHeaderGetInt(hdr_in, HDR_MAX_FORWARDS); | |
668 | if (hops > 0) | |
5999b776 | 669 | httpHeaderPutInt(hdr_out, HDR_MAX_FORWARDS, hops - 1); |
b3b64e58 | 670 | } |
99edd1c3 | 671 | break; |
137ee196 | 672 | case HDR_RANGE: |
a9771e51 | 673 | case HDR_IF_RANGE: |
5d679edb | 674 | case HDR_REQUEST_RANGE: |
675 | if (!we_do_ranges) | |
137ee196 | 676 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); |
677 | break; | |
99edd1c3 | 678 | case HDR_PROXY_CONNECTION: |
679 | case HDR_CONNECTION: | |
680 | case HDR_VIA: | |
681 | case HDR_X_FORWARDED_FOR: | |
682 | case HDR_CACHE_CONTROL: | |
683 | /* append these after the loop if needed */ | |
684 | break; | |
685 | default: | |
686 | /* pass on all other header fields */ | |
687 | httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e)); | |
66f7337b | 688 | } |
88738790 | 689 | } |
99edd1c3 | 690 | |
691 | /* append fake user agent if configured and | |
692 | * the real one is not supplied by the client */ | |
693 | if (Config.fake_ua && !httpHeaderHas(hdr_out, HDR_USER_AGENT)) | |
694 | httpHeaderPutStr(hdr_out, HDR_USER_AGENT, Config.fake_ua); | |
695 | ||
696 | /* append Via */ | |
697 | { | |
698 | String strVia = httpHeaderGetList(hdr_in, HDR_VIA); | |
699 | snprintf(bbuf, BBUF_SZ, "%3.1f %s", orig_request->http_ver, ThisCache); | |
700 | strListAdd(&strVia, bbuf, ','); | |
701 | httpHeaderPutStr(hdr_out, HDR_VIA, strBuf(strVia)); | |
702 | stringClean(&strVia); | |
6bf8443a | 703 | } |
99edd1c3 | 704 | /* append X-Forwarded-For */ |
705 | { | |
706 | String strFwd = httpHeaderGetList(hdr_in, HDR_X_FORWARDED_FOR); | |
707 | strListAdd(&strFwd, (cfd < 0 ? "unknown" : fd_table[cfd].ipaddr), ','); | |
708 | httpHeaderPutStr(hdr_out, HDR_X_FORWARDED_FOR, strBuf(strFwd)); | |
709 | stringClean(&strFwd); | |
710 | } | |
711 | /* append Host if not there already */ | |
712 | if (!httpHeaderHas(hdr_out, HDR_HOST)) { | |
713 | /* use port# only if not default */ | |
714 | if (orig_request->port == urlDefaultPort(orig_request->protocol)) { | |
715 | httpHeaderPutStr(hdr_out, HDR_HOST, orig_request->host); | |
716 | } else { | |
2246b732 | 717 | httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d", |
99edd1c3 | 718 | orig_request->host, (int) orig_request->port); |
99edd1c3 | 719 | } |
6bf8443a | 720 | } |
c68e9c6b | 721 | /* append Authorization if known in URL, not in header and going direct */ |
722 | if (!httpHeaderHas(hdr_out, HDR_AUTHORIZATION)) { | |
723 | if (!request->flags.proxying && *request->login) { | |
724 | httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", | |
725 | base64_encode(request->login)); | |
726 | } | |
727 | } | |
728 | /* append Proxy-Authorization if configured for peer, and proxying */ | |
729 | if (!httpHeaderHas(hdr_out, HDR_PROXY_AUTHORIZATION)) { | |
730 | if (request->flags.proxying && request->peer_login) { | |
731 | httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s", | |
732 | base64_encode(request->peer_login)); | |
733 | } | |
734 | } | |
99edd1c3 | 735 | /* append Cache-Control, add max-age if not there already */ |
736 | { | |
737 | HttpHdrCc *cc = httpHeaderGetCc(hdr_in); | |
738 | if (!cc) | |
739 | cc = httpHdrCcCreate(); | |
740 | if (!EBIT_TEST(cc->mask, CC_MAX_AGE)) { | |
9b5d1d21 | 741 | const char *url = entry ? storeUrl(entry) : urlCanonical(orig_request); |
99edd1c3 | 742 | httpHdrCcSetMaxAge(cc, getMaxAge(url)); |
743 | if (strLen(request->urlpath)) | |
744 | assert(strstr(url, strBuf(request->urlpath))); | |
745 | } | |
db1cd23c | 746 | if (flags.only_if_cached) |
747 | EBIT_SET(cc->mask, CC_ONLY_IF_CACHED); | |
99edd1c3 | 748 | httpHeaderPutCc(hdr_out, cc); |
749 | httpHdrCcDestroy(cc); | |
6bf8443a | 750 | } |
99edd1c3 | 751 | /* maybe append Connection: keep-alive */ |
b515fc11 | 752 | if (flags.keepalive) { |
753 | if (flags.proxying) { | |
99edd1c3 | 754 | httpHeaderPutStr(hdr_out, HDR_PROXY_CONNECTION, "keep-alive"); |
603a02fd | 755 | } else { |
99edd1c3 | 756 | httpHeaderPutStr(hdr_out, HDR_CONNECTION, "keep-alive"); |
603a02fd | 757 | } |
603a02fd | 758 | } |
99edd1c3 | 759 | stringClean(&strConnection); |
760 | } | |
761 | ||
762 | /* build request prefix and append it to a given MemBuf; | |
763 | * return the length of the prefix */ | |
764 | size_t | |
765 | httpBuildRequestPrefix(request_t * request, | |
766 | request_t * orig_request, | |
767 | StoreEntry * entry, | |
5999b776 | 768 | MemBuf * mb, |
99edd1c3 | 769 | int cfd, |
b515fc11 | 770 | http_state_flags flags) |
99edd1c3 | 771 | { |
772 | const int offset = mb->size; | |
773 | memBufPrintf(mb, "%s %s HTTP/1.0\r\n", | |
774 | RequestMethodStr[request->method], | |
775 | strLen(request->urlpath) ? strBuf(request->urlpath) : "/"); | |
776 | /* build and pack headers */ | |
777 | { | |
778 | HttpHeader hdr; | |
779 | Packer p; | |
780 | httpBuildRequestHeader(request, orig_request, entry, &hdr, cfd, flags); | |
781 | packerToMemInit(&p, mb); | |
782 | httpHeaderPackInto(&hdr, &p); | |
783 | httpHeaderClean(&hdr); | |
784 | packerClean(&p); | |
9d9d144b | 785 | } |
99edd1c3 | 786 | /* append header terminator */ |
b8890359 | 787 | memBufAppend(mb, crlf, 2); |
99edd1c3 | 788 | return mb->size - offset; |
6bf8443a | 789 | } |
090089c4 | 790 | /* This will be called when connect completes. Write request. */ |
b8d8561b | 791 | static void |
b6a2f15e | 792 | httpSendRequest(HttpStateData * httpState) |
090089c4 | 793 | { |
99edd1c3 | 794 | MemBuf mb; |
30a4f2a8 | 795 | request_t *req = httpState->request; |
620da955 | 796 | StoreEntry *entry = httpState->entry; |
2a26c096 | 797 | int cfd; |
1294c0fc | 798 | peer *p = httpState->peer; |
901e234d | 799 | CWCB *sendHeaderDone; |
090089c4 | 800 | |
b6a2f15e | 801 | debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", httpState->fd, httpState); |
090089c4 | 802 | |
eb824054 | 803 | if (httpState->orig_request->body) |
7db8b16d | 804 | sendHeaderDone = httpSendRequestEntry; |
805 | else | |
806 | sendHeaderDone = httpSendComplete; | |
54220df8 | 807 | |
2a26c096 | 808 | if (!opt_forwarded_for) |
6bf8443a | 809 | cfd = -1; |
2a26c096 | 810 | else if (entry->mem_obj == NULL) |
6bf8443a | 811 | cfd = -1; |
2a26c096 | 812 | else |
382d851a | 813 | cfd = entry->mem_obj->fd; |
b0a1e5bf | 814 | assert(-1 == cfd || FD_SOCKET == fd_table[cfd].type); |
1294c0fc | 815 | if (p != NULL) |
b515fc11 | 816 | httpState->flags.proxying = 1; |
efb9218c | 817 | /* |
99edd1c3 | 818 | * Is keep-alive okay for all request methods? |
efb9218c | 819 | */ |
820 | if (p == NULL) | |
b515fc11 | 821 | httpState->flags.keepalive = 1; |
efb9218c | 822 | else if (p->stats.n_keepalives_sent < 10) |
b515fc11 | 823 | httpState->flags.keepalive = 1; |
efb9218c | 824 | else if ((double) p->stats.n_keepalives_recv / (double) p->stats.n_keepalives_sent > 0.50) |
b515fc11 | 825 | httpState->flags.keepalive = 1; |
db1cd23c | 826 | if (httpState->peer) |
827 | if (neighborType(httpState->peer, httpState->request) == PEER_SIBLING) | |
828 | httpState->flags.only_if_cached = 1; | |
99edd1c3 | 829 | memBufDefInit(&mb); |
830 | httpBuildRequestPrefix(req, | |
79a15e0a | 831 | httpState->orig_request, |
6bf8443a | 832 | entry, |
99edd1c3 | 833 | &mb, |
603a02fd | 834 | cfd, |
835 | httpState->flags); | |
b6a2f15e | 836 | debug(11, 6) ("httpSendRequest: FD %d:\n%s\n", httpState->fd, mb.buf); |
837 | comm_write_mbuf(httpState->fd, mb, sendHeaderDone, httpState); | |
090089c4 | 838 | } |
b6a2f15e | 839 | |
910169e5 | 840 | void |
db1cd23c | 841 | httpStart(FwdState * fwd) |
603a02fd | 842 | { |
db1cd23c | 843 | int fd = fwd->server_fd; |
cb87dab6 | 844 | HttpStateData *httpState = memAllocate(MEM_HTTP_STATE_DATA); |
910169e5 | 845 | request_t *proxy_req; |
db1cd23c | 846 | request_t *orig_req = fwd->request; |
910169e5 | 847 | debug(11, 3) ("httpStart: \"%s %s\"\n", |
848 | RequestMethodStr[orig_req->method], | |
db1cd23c | 849 | storeUrl(fwd->entry)); |
850 | cbdataAdd(httpState, memFree, MEM_HTTP_STATE_DATA); | |
851 | storeLockObject(fwd->entry); | |
852 | httpState->fwd = fwd; | |
853 | httpState->entry = fwd->entry; | |
9e4ad609 | 854 | httpState->fd = fd; |
db1cd23c | 855 | if (fwd->servers) |
856 | httpState->peer = fwd->servers->peer; /* might be NULL */ | |
910169e5 | 857 | if (httpState->peer) { |
858 | proxy_req = requestCreate(orig_req->method, | |
859 | PROTO_NONE, storeUrl(httpState->entry)); | |
860 | xstrncpy(proxy_req->host, httpState->peer->host, SQUIDHOSTNAMELEN); | |
861 | proxy_req->port = httpState->peer->http_port; | |
23e8446b | 862 | proxy_req->flags = orig_req->flags; |
c68e9c6b | 863 | proxy_req->peer_login = httpState->peer->login; |
910169e5 | 864 | httpState->request = requestLink(proxy_req); |
910169e5 | 865 | httpState->orig_request = requestLink(orig_req); |
92695e5e | 866 | proxy_req->flags.proxying = 1; |
910169e5 | 867 | /* |
868 | * This NEIGHBOR_PROXY_ONLY check probably shouldn't be here. | |
869 | * We might end up getting the object from somewhere else if, | |
870 | * for example, the request to this neighbor fails. | |
871 | */ | |
cd196bc8 | 872 | if (httpState->peer->options.proxy_only) |
910169e5 | 873 | storeReleaseRequest(httpState->entry); |
95e36d02 | 874 | #if DELAY_POOLS |
59715b38 | 875 | assert(delayIsNoDelay(fd) == 0); |
876 | if (httpState->peer->options.no_delay) | |
877 | delaySetNoDelay(fd); | |
95e36d02 | 878 | #endif |
603a02fd | 879 | } else { |
910169e5 | 880 | httpState->request = requestLink(orig_req); |
881 | httpState->orig_request = requestLink(orig_req); | |
603a02fd | 882 | } |
910169e5 | 883 | /* |
884 | * register the handler to free HTTP state data when the FD closes | |
885 | */ | |
886 | comm_add_close_handler(fd, httpStateFree, httpState); | |
a0f32775 | 887 | Counter.server.all.requests++; |
888 | Counter.server.http.requests++; | |
b6a2f15e | 889 | httpSendRequest(httpState); |
890 | /* | |
891 | * We used to set the read timeout here, but not any more. | |
892 | * Now its set in httpSendComplete() after the full request, | |
893 | * including request body, has been written to the server. | |
894 | */ | |
090089c4 | 895 | } |
896 | ||
54220df8 | 897 | static void |
7db8b16d | 898 | httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data) |
54220df8 | 899 | { |
900 | HttpStateData *httpState = data; | |
901 | StoreEntry *entry = httpState->entry; | |
902 | ErrorState *err; | |
903 | debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n", | |
7db8b16d | 904 | fd, size, errflag); |
54220df8 | 905 | if (size > 0) { |
7db8b16d | 906 | fd_bytes(fd, size, FD_WRITE); |
54220df8 | 907 | kb_incr(&Counter.server.all.kbytes_out, size); |
908 | kb_incr(&Counter.server.http.kbytes_out, size); | |
909 | } | |
910 | if (errflag == COMM_ERR_CLOSING) | |
7db8b16d | 911 | return; |
54220df8 | 912 | if (errflag) { |
7db8b16d | 913 | err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); |
914 | err->xerrno = errno; | |
915 | err->request = requestLink(httpState->orig_request); | |
916 | errorAppendEntry(entry, err); | |
917 | comm_close(fd); | |
918 | return; | |
54220df8 | 919 | } |
b6a2f15e | 920 | if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { |
921 | comm_close(fd); | |
922 | return; | |
923 | } | |
376bb137 | 924 | pumpStart(fd, httpState->fwd, httpSendRequestEntryDone, httpState); |
925 | } | |
926 | ||
927 | static void | |
928 | httpSendRequestEntryDone(int fd, char *bufnotused, size_t size, int errflag, void *data) | |
929 | { | |
930 | HttpStateData *httpState = data; | |
931 | StoreEntry *entry = httpState->entry; | |
932 | ErrorState *err; | |
933 | aclCheck_t ch; | |
934 | debug(11, 5) ("httpSendRequestEntryDone: FD %d: size %d: errflag %d.\n", | |
935 | fd, size, errflag); | |
936 | if (size > 0) { | |
937 | fd_bytes(fd, size, FD_WRITE); | |
938 | kb_incr(&Counter.server.all.kbytes_out, size); | |
939 | kb_incr(&Counter.server.http.kbytes_out, size); | |
940 | } | |
941 | if (errflag == COMM_ERR_CLOSING) | |
942 | return; | |
943 | if (errflag) { | |
944 | err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR); | |
945 | err->xerrno = errno; | |
946 | err->request = requestLink(httpState->orig_request); | |
947 | errorAppendEntry(entry, err); | |
948 | comm_close(fd); | |
949 | return; | |
950 | } | |
951 | memset(&ch, '\0', sizeof(ch)); | |
952 | ch.request = httpState->request; | |
953 | if (!Config.accessList.brokenPosts) { | |
954 | debug(11, 5) ("httpSendRequestEntryDone: No brokenPosts list\n"); | |
955 | httpSendComplete(fd, NULL, 0, 0, data); | |
956 | } else if (!aclCheckFast(Config.accessList.brokenPosts, &ch)) { | |
957 | debug(11, 5) ("httpSendRequestEntryDone: didn't match brokenPosts\n"); | |
958 | httpSendComplete(fd, NULL, 0, 0, data); | |
959 | } else { | |
960 | debug(11, 2) ("httpSendRequestEntryDone: matched brokenPosts\n"); | |
961 | comm_write(fd, "\r\n", 2, httpSendComplete, data, NULL); | |
962 | } | |
54220df8 | 963 | } |