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