]>
Commit | Line | Data |
---|---|---|
41462d93 | 1 | |
2 | /* | |
3280e9d6 | 3 | * $Id: forward.cc,v 1.74 2000/10/04 01:12:48 wessels Exp $ |
41462d93 | 4 | * |
5 | * DEBUG: section 17 Request Forwarding | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
e25c139f | 9 | * ---------------------------------------------------------- |
41462d93 | 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 | |
efd900cb | 15 | * the Regents of the University of California. Please see the |
16 | * COPYRIGHT file for full details. Squid incorporates software | |
17 | * developed and/or copyrighted by other sources. Please see the | |
18 | * CREDITS file for full details. | |
41462d93 | 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 | * |
41462d93 | 34 | */ |
35 | ||
36 | ||
37 | #include "squid.h" | |
38 | ||
db1cd23c | 39 | static PSC fwdStartComplete; |
6801f8a8 | 40 | static void fwdDispatch(FwdState *); |
ee08bdf5 | 41 | static void fwdConnectStart(void *); /* should be same as EVH */ |
910169e5 | 42 | static void fwdStateFree(FwdState * fwdState); |
43 | static PF fwdConnectTimeout; | |
44 | static PF fwdServerClosed; | |
45 | static CNCB fwdConnectDone; | |
68bd6892 | 46 | static int fwdCheckRetry(FwdState * fwdState); |
db1cd23c | 47 | static int fwdReforward(FwdState *); |
48 | static void fwdStartFail(FwdState *); | |
8ddcc35d | 49 | static void fwdLogReplyStatus(int tries, http_status status); |
50 | static OBJH fwdStats; | |
7197b20d | 51 | static STABH fwdAbort; |
8ddcc35d | 52 | |
53 | #define MAX_FWD_STATS_IDX 9 | |
9977e14b | 54 | static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1]; |
db1cd23c | 55 | |
56 | static void | |
57 | fwdServerFree(FwdServer * fs) | |
58 | { | |
59 | if (fs->peer) | |
60 | cbdataUnlock(fs->peer); | |
61 | memFree(fs, MEM_FWD_SERVER); | |
62 | } | |
63 | ||
41462d93 | 64 | static void |
4ca83e82 | 65 | fwdStateFree(FwdState * fwdState) |
41462d93 | 66 | { |
f563eea9 | 67 | StoreEntry *e = fwdState->entry; |
6801f8a8 | 68 | int sfd; |
0bdf2621 | 69 | debug(17, 3) ("fwdStateFree: %p\n", fwdState); |
f563eea9 | 70 | assert(e->mem_obj); |
bc87dc25 | 71 | #if URL_CHECKSUM_DEBUG |
72 | assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url)); | |
73 | #endif | |
1640c2df | 74 | if (e->store_status == STORE_PENDING) { |
75 | if (e->mem_obj->inmem_hi == 0) { | |
ec250dfd | 76 | assert(fwdState->err); |
77 | errorAppendEntry(e, fwdState->err); | |
2c450e4e | 78 | fwdState->err = NULL; |
b746bd8a | 79 | } else { |
8f10798d | 80 | EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT); |
b746bd8a | 81 | storeComplete(e); |
8a4f6dd6 | 82 | storeReleaseRequest(e); |
1640c2df | 83 | } |
f563eea9 | 84 | } |
8a4f6dd6 | 85 | if (storePendingNClients(e) > 0) |
86 | assert(!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)); | |
db1cd23c | 87 | fwdServersFree(&fwdState->servers); |
41462d93 | 88 | requestUnlink(fwdState->request); |
4ca83e82 | 89 | fwdState->request = NULL; |
2c450e4e | 90 | if (fwdState->err) |
91 | errorStateFree(fwdState->err); | |
f563eea9 | 92 | storeUnregisterAbort(e); |
93 | storeUnlockObject(e); | |
4ca83e82 | 94 | fwdState->entry = NULL; |
6801f8a8 | 95 | sfd = fwdState->server_fd; |
96 | if (sfd > -1) { | |
97 | comm_remove_close_handler(sfd, fwdServerClosed, fwdState); | |
98 | fwdState->server_fd = -1; | |
32b2931b | 99 | debug(17, 3) ("fwdStateFree: closing FD %d\n", sfd); |
6801f8a8 | 100 | comm_close(sfd); |
101 | } | |
41462d93 | 102 | cbdataFree(fwdState); |
103 | } | |
104 | ||
68bd6892 | 105 | static int |
106 | fwdCheckRetry(FwdState * fwdState) | |
107 | { | |
1640c2df | 108 | if (fwdState->entry->store_status != STORE_PENDING) |
109 | return 0; | |
68bd6892 | 110 | if (fwdState->entry->mem_obj->inmem_hi > 0) |
111 | return 0; | |
112 | if (fwdState->n_tries > 10) | |
113 | return 0; | |
efd900cb | 114 | if (squid_curtime - fwdState->start > Config.Timeout.connect) |
68bd6892 | 115 | return 0; |
ee08bdf5 | 116 | if (fwdState->flags.dont_retry) |
117 | return 0; | |
1f38f50a | 118 | if (fwdState->request->content_length >= 0) |
1d22e062 | 119 | if (0 == pumpRestart(fwdState->request)) |
120 | return 0; | |
68bd6892 | 121 | return 1; |
122 | } | |
123 | ||
910169e5 | 124 | static void |
125 | fwdServerClosed(int fd, void *data) | |
126 | { | |
4ca83e82 | 127 | FwdState *fwdState = data; |
ec250dfd | 128 | debug(17, 2) ("fwdServerClosed: FD %d %s\n", fd, storeUrl(fwdState->entry)); |
6801f8a8 | 129 | assert(fwdState->server_fd == fd); |
130 | fwdState->server_fd = -1; | |
68bd6892 | 131 | if (fwdCheckRetry(fwdState)) { |
32b2931b | 132 | debug(17, 3) ("fwdServerClosed: re-forwarding (%d tries, %d secs)\n", |
9dbf253d | 133 | fwdState->n_tries, |
134 | (int) (squid_curtime - fwdState->start)); | |
6b8e7481 | 135 | if (fwdState->servers->next) { |
eb406bb7 | 136 | /* use next, or cycle if origin server isn't last */ |
6b8e7481 | 137 | FwdServer *fs = fwdState->servers; |
eb406bb7 | 138 | FwdServer **T, *T2 = NULL; |
6b8e7481 | 139 | fwdState->servers = fs->next; |
a4b8110e | 140 | for (T = &fwdState->servers; *T; T2 = *T, T = &(*T)->next); |
141 | if (T2 && T2->peer) { | |
eb406bb7 | 142 | /* cycle */ |
143 | *T = fs; | |
144 | fs->next = NULL; | |
145 | } else { | |
146 | /* Use next. The last "direct" entry is retried multiple times */ | |
147 | fwdState->servers = fs->next; | |
148 | fwdServerFree(fs); | |
149 | } | |
6b8e7481 | 150 | } |
ee08bdf5 | 151 | /* use eventAdd to break potential call sequence loops */ |
f726e966 | 152 | eventAdd("fwdConnectStart", fwdConnectStart, fwdState, 0.0, 0); |
f563eea9 | 153 | } else { |
154 | fwdStateFree(fwdState); | |
155 | } | |
910169e5 | 156 | } |
157 | ||
41462d93 | 158 | static void |
159 | fwdConnectDone(int server_fd, int status, void *data) | |
160 | { | |
161 | FwdState *fwdState = data; | |
e07b2888 | 162 | static FwdState *current = NULL; |
db1cd23c | 163 | FwdServer *fs = fwdState->servers; |
41462d93 | 164 | ErrorState *err; |
db1cd23c | 165 | request_t *request = fwdState->request; |
e07b2888 | 166 | assert(current != fwdState); |
167 | current = fwdState; | |
6801f8a8 | 168 | assert(fwdState->server_fd == server_fd); |
41462d93 | 169 | if (status == COMM_ERR_DNS) { |
3280e9d6 | 170 | /* |
171 | * Only set the dont_retry flag if the DNS lookup fails on | |
172 | * a direct connection. If DNS lookup fails when trying | |
173 | * a neighbor cache, we may want to retry another option. | |
174 | */ | |
175 | if (NULL == fs->peer) | |
176 | fwdState->flags.dont_retry = 1; | |
41462d93 | 177 | debug(17, 4) ("fwdConnectDone: Unknown host: %s\n", |
db1cd23c | 178 | request->host); |
41462d93 | 179 | err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE); |
180 | err->dnsserver_msg = xstrdup(dns_error_message); | |
db1cd23c | 181 | err->request = requestLink(request); |
ec250dfd | 182 | fwdFail(fwdState, err); |
41462d93 | 183 | comm_close(server_fd); |
184 | } else if (status != COMM_OK) { | |
db1cd23c | 185 | assert(fs); |
41462d93 | 186 | err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE); |
187 | err->xerrno = errno; | |
db1cd23c | 188 | if (fs->peer) { |
189 | err->host = xstrdup(fs->peer->host); | |
190 | err->port = fs->peer->http_port; | |
191 | } else { | |
192 | err->host = xstrdup(request->host); | |
193 | err->port = request->port; | |
194 | } | |
195 | err->request = requestLink(request); | |
ec250dfd | 196 | fwdFail(fwdState, err); |
db1cd23c | 197 | if (fs->peer) |
eb406bb7 | 198 | peerConnectFailed(fs->peer); |
41462d93 | 199 | comm_close(server_fd); |
200 | } else { | |
8a4f6dd6 | 201 | debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(fwdState->entry)); |
890b0fa8 | 202 | if (fs->peer) |
203 | hierarchyNote(&fwdState->request->hier, fs->code, fs->peer->host); | |
204 | else if (Config.onoff.log_ip_on_direct) | |
205 | hierarchyNote(&fwdState->request->hier, fs->code, fd_table[server_fd].ipaddr); | |
206 | else | |
207 | hierarchyNote(&fwdState->request->hier, fs->code, request->host); | |
41462d93 | 208 | fd_note(server_fd, storeUrl(fwdState->entry)); |
209 | fd_table[server_fd].uses++; | |
eb406bb7 | 210 | if (fs->peer) |
211 | peerConnectSucceded(fs->peer); | |
6801f8a8 | 212 | fwdDispatch(fwdState); |
41462d93 | 213 | } |
e07b2888 | 214 | current = NULL; |
41462d93 | 215 | } |
216 | ||
217 | static void | |
218 | fwdConnectTimeout(int fd, void *data) | |
219 | { | |
220 | FwdState *fwdState = data; | |
221 | StoreEntry *entry = fwdState->entry; | |
222 | ErrorState *err; | |
ec250dfd | 223 | debug(17, 2) ("fwdConnectTimeout: FD %d: '%s'\n", fd, storeUrl(entry)); |
6801f8a8 | 224 | assert(fd == fwdState->server_fd); |
41462d93 | 225 | if (entry->mem_obj->inmem_hi == 0) { |
b5ea121c | 226 | err = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT); |
41462d93 | 227 | err->request = requestLink(fwdState->request); |
ec250dfd | 228 | err->xerrno = ETIMEDOUT; |
229 | fwdFail(fwdState, err); | |
efd900cb | 230 | /* |
231 | * This marks the peer DOWN ... | |
232 | */ | |
233 | if (fwdState->servers) | |
234 | if (fwdState->servers->peer) | |
eb406bb7 | 235 | peerConnectFailed(fwdState->servers->peer); |
41462d93 | 236 | } |
237 | comm_close(fd); | |
238 | } | |
239 | ||
240 | static void | |
ee08bdf5 | 241 | fwdConnectStart(void *data) |
41462d93 | 242 | { |
8a4f6dd6 | 243 | FwdState *fwdState = data; |
41462d93 | 244 | const char *url = storeUrl(fwdState->entry); |
245 | int fd; | |
246 | ErrorState *err; | |
db1cd23c | 247 | FwdServer *fs = fwdState->servers; |
248 | const char *host; | |
249 | unsigned short port; | |
3f62decd | 250 | time_t ctimeout; |
db1cd23c | 251 | assert(fs); |
cddc721b | 252 | assert(fwdState->server_fd == -1); |
41462d93 | 253 | debug(17, 3) ("fwdConnectStart: %s\n", url); |
db1cd23c | 254 | if (fs->peer) { |
255 | host = fs->peer->host; | |
256 | port = fs->peer->http_port; | |
3f62decd | 257 | ctimeout = fs->peer->connect_timeout > 0 ? fs->peer->connect_timeout |
258 | : Config.Timeout.peer_connect; | |
13c7936a | 259 | } else if (fwdState->request->flags.accelerated && |
a4b8110e | 260 | Config.Accel.single_host && Config.Accel.host) { |
13c7936a | 261 | host = Config.Accel.host; |
262 | port = Config.Accel.port; | |
263 | ctimeout = Config.Timeout.connect; | |
db1cd23c | 264 | } else { |
265 | host = fwdState->request->host; | |
266 | port = fwdState->request->port; | |
3f62decd | 267 | ctimeout = Config.Timeout.connect; |
db1cd23c | 268 | } |
269 | if ((fd = pconnPop(host, port)) >= 0) { | |
41462d93 | 270 | debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd); |
9dbf253d | 271 | fwdState->server_fd = fd; |
9977e14b | 272 | fwdState->n_tries++; |
910169e5 | 273 | comm_add_close_handler(fd, fwdServerClosed, fwdState); |
41462d93 | 274 | fwdConnectDone(fd, COMM_OK, fwdState); |
275 | return; | |
276 | } | |
bc87dc25 | 277 | #if URL_CHECKSUM_DEBUG |
278 | assert(fwdState->entry->mem_obj->chksum == url_checksum(url)); | |
279 | #endif | |
41462d93 | 280 | fd = comm_open(SOCK_STREAM, |
281 | 0, | |
282 | Config.Addrs.tcp_outgoing, | |
283 | 0, | |
284 | COMM_NONBLOCKING, | |
285 | url); | |
286 | if (fd < 0) { | |
287 | debug(50, 4) ("fwdConnectStart: %s\n", xstrerror()); | |
288 | err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR); | |
289 | err->xerrno = errno; | |
290 | err->request = requestLink(fwdState->request); | |
ec250dfd | 291 | fwdFail(fwdState, err); |
910169e5 | 292 | fwdStateFree(fwdState); |
41462d93 | 293 | return; |
294 | } | |
6801f8a8 | 295 | fwdState->server_fd = fd; |
68bd6892 | 296 | fwdState->n_tries++; |
910169e5 | 297 | comm_add_close_handler(fd, fwdServerClosed, fwdState); |
41462d93 | 298 | commSetTimeout(fd, |
3f62decd | 299 | ctimeout, |
41462d93 | 300 | fwdConnectTimeout, |
301 | fwdState); | |
db1cd23c | 302 | commConnectStart(fd, host, port, fwdConnectDone, fwdState); |
41462d93 | 303 | } |
304 | ||
305 | static void | |
db1cd23c | 306 | fwdStartComplete(FwdServer * servers, void *data) |
41462d93 | 307 | { |
308 | FwdState *fwdState = data; | |
8a4f6dd6 | 309 | debug(17, 3) ("fwdStartComplete: %s\n", storeUrl(fwdState->entry)); |
db1cd23c | 310 | if (servers != NULL) { |
311 | fwdState->servers = servers; | |
312 | fwdConnectStart(fwdState); | |
41462d93 | 313 | } else { |
db1cd23c | 314 | fwdStartFail(fwdState); |
41462d93 | 315 | } |
41462d93 | 316 | } |
317 | ||
318 | static void | |
db1cd23c | 319 | fwdStartFail(FwdState * fwdState) |
41462d93 | 320 | { |
41462d93 | 321 | ErrorState *err; |
8a4f6dd6 | 322 | debug(17, 3) ("fwdStartFail: %s\n", storeUrl(fwdState->entry)); |
41462d93 | 323 | err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE); |
324 | err->request = requestLink(fwdState->request); | |
ec250dfd | 325 | err->xerrno = errno; |
326 | fwdFail(fwdState, err); | |
910169e5 | 327 | fwdStateFree(fwdState); |
41462d93 | 328 | } |
910169e5 | 329 | |
41462d93 | 330 | static void |
6801f8a8 | 331 | fwdDispatch(FwdState * fwdState) |
41462d93 | 332 | { |
333 | peer *p; | |
334 | request_t *request = fwdState->request; | |
335 | StoreEntry *entry = fwdState->entry; | |
1f38f50a | 336 | ErrorState *err; |
b746bd8a | 337 | debug(17, 3) ("fwdDispatch: FD %d: Fetching '%s %s'\n", |
910169e5 | 338 | fwdState->client_fd, |
41462d93 | 339 | RequestMethodStr[request->method], |
340 | storeUrl(entry)); | |
d46a87a8 | 341 | /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */ |
41462d93 | 342 | assert(entry->ping_status != PING_WAITING); |
4ca83e82 | 343 | assert(entry->lock_count); |
d46a87a8 | 344 | EBIT_SET(entry->flags, ENTRY_DISPATCHED); |
41462d93 | 345 | netdbPingSite(request->host); |
e0ebe27c | 346 | /* |
347 | * Assert that server_fd is set. This is to guarantee that fwdState | |
348 | * is attached to something and will be deallocated when server_fd | |
349 | * is closed. | |
350 | */ | |
351 | assert(fwdState->server_fd > -1); | |
41462d93 | 352 | if (fwdState->servers && (p = fwdState->servers->peer)) { |
353 | p->stats.fetches++; | |
1f38f50a | 354 | fwdState->request->peer_login = p->login; |
db1cd23c | 355 | httpStart(fwdState); |
41462d93 | 356 | } else { |
357 | switch (request->protocol) { | |
358 | case PROTO_HTTP: | |
db1cd23c | 359 | httpStart(fwdState); |
41462d93 | 360 | break; |
361 | case PROTO_GOPHER: | |
db1cd23c | 362 | gopherStart(fwdState); |
41462d93 | 363 | break; |
364 | case PROTO_FTP: | |
db1cd23c | 365 | ftpStart(fwdState); |
41462d93 | 366 | break; |
367 | case PROTO_WAIS: | |
db1cd23c | 368 | waisStart(fwdState); |
41462d93 | 369 | break; |
370 | case PROTO_CACHEOBJ: | |
e0ebe27c | 371 | case PROTO_INTERNAL: |
41462d93 | 372 | case PROTO_URN: |
ae0b4725 | 373 | fatal_dump("Should never get here"); |
41462d93 | 374 | break; |
375 | case PROTO_WHOIS: | |
db1cd23c | 376 | whoisStart(fwdState); |
41462d93 | 377 | break; |
41462d93 | 378 | default: |
ce45d320 | 379 | debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n", |
380 | storeUrl(entry)); | |
1f38f50a | 381 | err = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST); |
382 | err->request = requestLink(request); | |
383 | fwdFail(fwdState, err); | |
384 | /* | |
385 | * Force a persistent connection to be closed because | |
386 | * some Netscape browsers have a bug that sends CONNECT | |
387 | * requests as GET's over persistent connections. | |
388 | */ | |
389 | request->flags.proxy_keepalive = 0; | |
390 | /* | |
391 | * Set the dont_retry flag becuase this is not a | |
392 | * transient (network) error; its a bug. | |
393 | */ | |
394 | fwdState->flags.dont_retry = 1; | |
c68e9c6b | 395 | comm_close(fwdState->server_fd); |
ce45d320 | 396 | break; |
41462d93 | 397 | } |
398 | } | |
399 | } | |
400 | ||
db1cd23c | 401 | static int |
402 | fwdReforward(FwdState * fwdState) | |
403 | { | |
404 | StoreEntry *e = fwdState->entry; | |
405 | FwdServer *fs = fwdState->servers; | |
406 | http_status s; | |
407 | assert(e->store_status == STORE_PENDING); | |
408 | assert(e->mem_obj); | |
bc87dc25 | 409 | #if URL_CHECKSUM_DEBUG |
410 | assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url)); | |
411 | #endif | |
b746bd8a | 412 | debug(17, 3) ("fwdReforward: %s?\n", storeUrl(e)); |
d6eb18d6 | 413 | if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) { |
414 | debug(17, 3) ("fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set\n"); | |
415 | return 0; | |
416 | } | |
db1cd23c | 417 | if (fwdState->n_tries > 9) |
418 | return 0; | |
1f38f50a | 419 | if (fwdState->request->content_length >= 0) |
d2946396 | 420 | if (0 == pumpRestart(fwdState->request)) |
421 | return 0; | |
db1cd23c | 422 | assert(fs); |
423 | fwdState->servers = fs->next; | |
424 | fwdServerFree(fs); | |
425 | if (fwdState->servers == NULL) { | |
426 | debug(17, 3) ("fwdReforward: No forward-servers left\n"); | |
427 | return 0; | |
428 | } | |
429 | s = e->mem_obj->reply->sline.status; | |
430 | debug(17, 3) ("fwdReforward: status %d\n", (int) s); | |
b6a2f15e | 431 | return fwdReforwardableStatus(s); |
db1cd23c | 432 | } |
433 | ||
41462d93 | 434 | /* PUBLIC FUNCTIONS */ |
435 | ||
64d8034e | 436 | void |
437 | fwdServersFree(FwdServer ** FS) | |
438 | { | |
439 | FwdServer *fs; | |
440 | while ((fs = *FS)) { | |
441 | *FS = fs->next; | |
442 | fwdServerFree(fs); | |
443 | } | |
444 | } | |
445 | ||
41462d93 | 446 | void |
7e3ce7b9 | 447 | fwdStart(int fd, StoreEntry * e, request_t * r) |
41462d93 | 448 | { |
449 | FwdState *fwdState; | |
d1061d2b | 450 | aclCheck_t ch; |
451 | int answer; | |
452 | ErrorState *err; | |
5843eb62 | 453 | /* |
db1cd23c | 454 | * client_addr == no_addr indicates this is an "internal" request |
5843eb62 | 455 | * from peer_digest.c, asn.c, netdb.c, etc and should always |
456 | * be allowed. yuck, I know. | |
d1061d2b | 457 | */ |
7e3ce7b9 | 458 | if (r->client_addr.s_addr != no_addr.s_addr) { |
5843eb62 | 459 | /* |
460 | * Check if this host is allowed to fetch MISSES from us (miss_access) | |
461 | */ | |
462 | memset(&ch, '\0', sizeof(aclCheck_t)); | |
7e3ce7b9 | 463 | ch.src_addr = r->client_addr; |
464 | ch.my_addr = r->my_addr; | |
465 | ch.my_port = r->my_port; | |
5843eb62 | 466 | ch.request = r; |
467 | answer = aclCheckFast(Config.accessList.miss, &ch); | |
468 | if (answer == 0) { | |
469 | err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN); | |
470 | err->request = requestLink(r); | |
7e3ce7b9 | 471 | err->src_addr = r->client_addr; |
5843eb62 | 472 | errorAppendEntry(e, err); |
473 | return; | |
474 | } | |
d1061d2b | 475 | } |
7197b20d | 476 | debug(17, 3) ("fwdStart: '%s'\n", storeUrl(e)); |
477 | e->mem_obj->request = requestLink(r); | |
478 | e->mem_obj->fd = fd; | |
bc87dc25 | 479 | #if URL_CHECKSUM_DEBUG |
480 | assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url)); | |
481 | #endif | |
05b744f3 | 482 | if (shutting_down) { |
483 | /* more yuck */ | |
484 | err = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE); | |
485 | err->request = requestLink(r); | |
486 | errorAppendEntry(e, err); | |
487 | return; | |
488 | } | |
d1061d2b | 489 | switch (r->protocol) { |
0cdcddb9 | 490 | /* |
491 | * Note, don't create fwdState for these requests | |
492 | */ | |
e0ebe27c | 493 | case PROTO_INTERNAL: |
d1061d2b | 494 | internalStart(r, e); |
e0ebe27c | 495 | return; |
496 | case PROTO_CACHEOBJ: | |
d1061d2b | 497 | cachemgrStart(fd, r, e); |
e0ebe27c | 498 | return; |
ae0b4725 | 499 | case PROTO_URN: |
500 | urnStart(r, e); | |
501 | return; | |
e0ebe27c | 502 | default: |
503 | break; | |
504 | } | |
798b0889 | 505 | fwdState = memAllocate(MEM_FWD_STATE); |
db1cd23c | 506 | cbdataAdd(fwdState, memFree, MEM_FWD_STATE); |
d1061d2b | 507 | fwdState->entry = e; |
910169e5 | 508 | fwdState->client_fd = fd; |
6801f8a8 | 509 | fwdState->server_fd = -1; |
d1061d2b | 510 | fwdState->request = requestLink(r); |
f563eea9 | 511 | fwdState->start = squid_curtime; |
d1061d2b | 512 | storeLockObject(e); |
ec250dfd | 513 | EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT); |
7197b20d | 514 | storeRegisterAbort(e, fwdAbort, fwdState); |
db1cd23c | 515 | peerSelect(r, e, fwdStartComplete, fwdState); |
41462d93 | 516 | } |
517 | ||
41462d93 | 518 | int |
59715b38 | 519 | fwdCheckDeferRead(int fd, void *data) |
41462d93 | 520 | { |
521 | StoreEntry *e = data; | |
522 | MemObject *mem = e->mem_obj; | |
7e3ce7b9 | 523 | int rc = 0; |
41462d93 | 524 | if (mem == NULL) |
525 | return 0; | |
bc87dc25 | 526 | #if URL_CHECKSUM_DEBUG |
527 | assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url)); | |
528 | #endif | |
447e176b | 529 | #if DELAY_POOLS |
1ecaa0a0 | 530 | if (fd < 0) |
531 | (void) 0; | |
532 | else if (delayIsNoDelay(fd)) | |
533 | (void) 0; | |
7e3ce7b9 | 534 | else { |
535 | int i = delayMostBytesWanted(mem, INT_MAX); | |
536 | if (0 == i) | |
537 | return 1; | |
538 | /* was: rc = -(rc != INT_MAX); */ | |
539 | else if (INT_MAX == i) | |
540 | rc = 0; | |
541 | else | |
542 | rc = -1; | |
543 | } | |
447e176b | 544 | #endif |
b6a2f15e | 545 | if (EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) |
7e3ce7b9 | 546 | return rc; |
41462d93 | 547 | if (mem->inmem_hi - storeLowestMemReaderOffset(e) < READ_AHEAD_GAP) |
7e3ce7b9 | 548 | return rc; |
41462d93 | 549 | return 1; |
550 | } | |
910169e5 | 551 | |
552 | void | |
b8890359 | 553 | fwdFail(FwdState * fwdState, ErrorState * errorState) |
910169e5 | 554 | { |
d46a87a8 | 555 | assert(EBIT_TEST(fwdState->entry->flags, ENTRY_FWD_HDR_WAIT)); |
32b2931b | 556 | debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n", |
ec250dfd | 557 | err_type_str[errorState->type], |
558 | httpStatusString(errorState->http_status), | |
4ca83e82 | 559 | storeUrl(fwdState->entry)); |
ec250dfd | 560 | if (fwdState->err) |
561 | errorStateFree(fwdState->err); | |
562 | fwdState->err = errorState; | |
910169e5 | 563 | } |
6801f8a8 | 564 | |
7197b20d | 565 | /* |
566 | * Called when someone else calls StoreAbort() on this entry | |
567 | */ | |
9bc73deb | 568 | static void |
7197b20d | 569 | fwdAbort(void *data) |
570 | { | |
571 | FwdState *fwdState = data; | |
ec250dfd | 572 | debug(17, 2) ("fwdAbort: %s\n", storeUrl(fwdState->entry)); |
7197b20d | 573 | fwdStateFree(fwdState); |
574 | } | |
575 | ||
0b49cd31 | 576 | /* |
577 | * Frees fwdState without closing FD or generating an abort | |
578 | */ | |
579 | void | |
9dbf253d | 580 | fwdUnregister(int fd, FwdState * fwdState) |
0b49cd31 | 581 | { |
9dbf253d | 582 | debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); |
583 | assert(fd = fwdState->server_fd); | |
b7fe0ab0 | 584 | assert(fd > -1); |
9dbf253d | 585 | comm_remove_close_handler(fd, fwdServerClosed, fwdState); |
586 | fwdState->server_fd = -1; | |
0b49cd31 | 587 | } |
db1cd23c | 588 | |
589 | /* | |
590 | * server-side modules call fwdComplete() when they are done | |
591 | * downloading an object. Then, we either 1) re-forward the | |
592 | * request somewhere else if needed, or 2) call storeComplete() | |
593 | * to finish it off | |
594 | */ | |
595 | void | |
596 | fwdComplete(FwdState * fwdState) | |
597 | { | |
598 | StoreEntry *e = fwdState->entry; | |
599 | assert(e->store_status == STORE_PENDING); | |
8a28f65f | 600 | debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e), |
db1cd23c | 601 | e->mem_obj->reply->sline.status); |
bc87dc25 | 602 | #if URL_CHECKSUM_DEBUG |
603 | assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url)); | |
604 | #endif | |
9977e14b | 605 | fwdLogReplyStatus(fwdState->n_tries, e->mem_obj->reply->sline.status); |
d6eb18d6 | 606 | if (fwdReforward(fwdState)) { |
77b32a34 | 607 | debug(17, 3) ("fwdComplete: re-forwarding %d %s\n", |
0bdf2621 | 608 | e->mem_obj->reply->sline.status, |
609 | storeUrl(e)); | |
8a28f65f | 610 | if (fwdState->server_fd > -1) |
0bdf2621 | 611 | fwdUnregister(fwdState->server_fd, fwdState); |
db1cd23c | 612 | storeEntryReset(e); |
613 | fwdStartComplete(fwdState->servers, fwdState); | |
614 | } else { | |
d6eb18d6 | 615 | debug(17, 3) ("fwdComplete: not re-forwarding status %d\n", |
616 | e->mem_obj->reply->sline.status); | |
db1cd23c | 617 | EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT); |
618 | storeComplete(e); | |
d6eb18d6 | 619 | /* |
620 | * If fwdState isn't associated with a server FD, it | |
621 | * won't get freed unless we do it here. | |
622 | */ | |
623 | if (fwdState->server_fd < 0) | |
624 | fwdStateFree(fwdState); | |
db1cd23c | 625 | } |
626 | } | |
8ddcc35d | 627 | |
628 | void | |
629 | fwdInit(void) | |
630 | { | |
9977e14b | 631 | cachemgrRegister("forward", |
632 | "Request Forwarding Statistics", | |
633 | fwdStats, 0, 1); | |
8ddcc35d | 634 | } |
635 | ||
636 | static void | |
637 | fwdLogReplyStatus(int tries, http_status status) | |
638 | { | |
639 | if (status > HTTP_INVALID_HEADER) | |
640 | return; | |
641 | assert(tries); | |
642 | tries--; | |
643 | if (tries > MAX_FWD_STATS_IDX) | |
644 | tries = MAX_FWD_STATS_IDX; | |
645 | FwdReplyCodes[tries][status]++; | |
646 | } | |
647 | ||
648 | static void | |
9977e14b | 649 | fwdStats(StoreEntry * s) |
8ddcc35d | 650 | { |
9977e14b | 651 | int i; |
652 | int j; | |
653 | storeAppendPrintf(s, "Status"); | |
654 | for (j = 0; j <= MAX_FWD_STATS_IDX; j++) { | |
655 | storeAppendPrintf(s, "\ttry#%d", j + 1); | |
656 | } | |
657 | storeAppendPrintf(s, "\n"); | |
658 | for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) { | |
659 | if (FwdReplyCodes[0][i] == 0) | |
660 | continue; | |
661 | storeAppendPrintf(s, "%3d", i); | |
662 | for (j = 0; j <= MAX_FWD_STATS_IDX; j++) { | |
663 | storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]); | |
8ddcc35d | 664 | } |
9977e14b | 665 | storeAppendPrintf(s, "\n"); |
666 | } | |
8ddcc35d | 667 | } |
b6a2f15e | 668 | |
669 | int | |
670 | fwdReforwardableStatus(http_status s) | |
671 | { | |
672 | switch (s) { | |
673 | case HTTP_FORBIDDEN: | |
674 | case HTTP_INTERNAL_SERVER_ERROR: | |
675 | case HTTP_NOT_IMPLEMENTED: | |
676 | case HTTP_BAD_GATEWAY: | |
677 | case HTTP_SERVICE_UNAVAILABLE: | |
678 | case HTTP_GATEWAY_TIMEOUT: | |
679 | return 1; | |
680 | default: | |
681 | return 0; | |
682 | } | |
683 | /* NOTREACHED */ | |
684 | } |