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