]>
Commit | Line | Data |
---|---|---|
41462d93 | 1 | |
2 | /* | |
798b0889 | 3 | * $Id: forward.cc,v 1.16 1998/07/13 21:37:15 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/ | |
9 | * -------------------------------------------------------- | |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
31 | ||
32 | ||
33 | #include "squid.h" | |
34 | ||
41462d93 | 35 | static void fwdStartComplete(peer * p, void *data); |
36 | static void fwdStartFail(peer * p, void *data); | |
6801f8a8 | 37 | static void fwdDispatch(FwdState *); |
910169e5 | 38 | static void fwdConnectStart(FwdState * fwdState); |
39 | static void fwdStateFree(FwdState * fwdState); | |
40 | static PF fwdConnectTimeout; | |
41 | static PF fwdServerClosed; | |
42 | static CNCB fwdConnectDone; | |
68bd6892 | 43 | static int fwdCheckRetry(FwdState * fwdState); |
41462d93 | 44 | |
45 | static void | |
4ca83e82 | 46 | fwdStateFree(FwdState * fwdState) |
41462d93 | 47 | { |
910169e5 | 48 | FwdServer *s; |
49 | FwdServer *n = fwdState->servers; | |
f563eea9 | 50 | StoreEntry *e = fwdState->entry; |
51 | ErrorState *err; | |
6801f8a8 | 52 | int sfd; |
53 | static int loop_detect = 0; | |
6801f8a8 | 54 | assert(loop_detect++ == 0); |
f563eea9 | 55 | assert(e->mem_obj); |
1640c2df | 56 | if (e->store_status == STORE_PENDING) { |
57 | if (e->mem_obj->inmem_hi == 0) { | |
58 | assert(fwdState->fail.err_code); | |
59 | err = errorCon(fwdState->fail.err_code, fwdState->fail.http_code); | |
60 | err->request = requestLink(fwdState->request); | |
61 | err->xerrno = fwdState->fail.xerrno; | |
62 | errorAppendEntry(e, err); | |
63 | } else { | |
64 | storeAbort(e, 0); | |
65 | } | |
f563eea9 | 66 | } |
41462d93 | 67 | while ((s = n)) { |
68 | n = s->next; | |
69 | xfree(s->host); | |
798b0889 | 70 | memFree(MEM_FWD_SERVER, s); |
41462d93 | 71 | } |
72 | fwdState->servers = NULL; | |
73 | requestUnlink(fwdState->request); | |
4ca83e82 | 74 | fwdState->request = NULL; |
f563eea9 | 75 | storeUnregisterAbort(e); |
76 | storeUnlockObject(e); | |
4ca83e82 | 77 | fwdState->entry = NULL; |
6801f8a8 | 78 | sfd = fwdState->server_fd; |
79 | if (sfd > -1) { | |
80 | comm_remove_close_handler(sfd, fwdServerClosed, fwdState); | |
81 | fwdState->server_fd = -1; | |
9dbf253d | 82 | debug(17, 1) ("fwdStateFree: closing FD %d\n", sfd); |
6801f8a8 | 83 | comm_close(sfd); |
84 | } | |
41462d93 | 85 | cbdataFree(fwdState); |
6801f8a8 | 86 | loop_detect--; |
41462d93 | 87 | } |
88 | ||
68bd6892 | 89 | static int |
90 | fwdCheckRetry(FwdState * fwdState) | |
91 | { | |
1640c2df | 92 | if (fwdState->entry->store_status != STORE_PENDING) |
93 | return 0; | |
68bd6892 | 94 | if (fwdState->entry->mem_obj->inmem_hi > 0) |
95 | return 0; | |
96 | if (fwdState->n_tries > 10) | |
97 | return 0; | |
98 | if (squid_curtime - fwdState->start > 120) | |
99 | return 0; | |
1d22e062 | 100 | if (pumpMethod(fwdState->request->method)) |
101 | if (0 == pumpRestart(fwdState->request)) | |
102 | return 0; | |
68bd6892 | 103 | return 1; |
104 | } | |
105 | ||
910169e5 | 106 | static void |
107 | fwdServerClosed(int fd, void *data) | |
108 | { | |
4ca83e82 | 109 | FwdState *fwdState = data; |
68bd6892 | 110 | debug(17, 3) ("fwdServerClosed: FD %d %s\n", fd, storeUrl(fwdState->entry)); |
6801f8a8 | 111 | assert(fwdState->server_fd == fd); |
112 | fwdState->server_fd = -1; | |
68bd6892 | 113 | if (fwdCheckRetry(fwdState)) { |
114 | debug(17, 1) ("fwdServerClosed: re-forwarding (%d tries, %d secs)\n", | |
9dbf253d | 115 | fwdState->n_tries, |
116 | (int) (squid_curtime - fwdState->start)); | |
f563eea9 | 117 | fwdConnectStart(fwdState); |
118 | } else { | |
119 | fwdStateFree(fwdState); | |
120 | } | |
910169e5 | 121 | } |
122 | ||
41462d93 | 123 | static void |
124 | fwdConnectDone(int server_fd, int status, void *data) | |
125 | { | |
126 | FwdState *fwdState = data; | |
127 | ErrorState *err; | |
6801f8a8 | 128 | assert(fwdState->server_fd == server_fd); |
41462d93 | 129 | if (status == COMM_ERR_DNS) { |
130 | debug(17, 4) ("fwdConnectDone: Unknown host: %s\n", | |
131 | fwdState->request->host); | |
132 | err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE); | |
133 | err->dnsserver_msg = xstrdup(dns_error_message); | |
134 | err->request = requestLink(fwdState->request); | |
135 | errorAppendEntry(fwdState->entry, err); | |
136 | comm_close(server_fd); | |
137 | } else if (status != COMM_OK) { | |
138 | err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE); | |
139 | err->xerrno = errno; | |
140 | err->host = xstrdup(fwdState->servers->host); | |
141 | err->port = fwdState->servers->port; | |
142 | err->request = requestLink(fwdState->request); | |
143 | errorAppendEntry(fwdState->entry, err); | |
144 | assert(fwdState->servers); | |
145 | if (fwdState->servers->peer) | |
146 | peerCheckConnectStart(fwdState->servers->peer); | |
147 | comm_close(server_fd); | |
148 | } else { | |
149 | fd_note(server_fd, storeUrl(fwdState->entry)); | |
150 | fd_table[server_fd].uses++; | |
6801f8a8 | 151 | fwdDispatch(fwdState); |
41462d93 | 152 | } |
41462d93 | 153 | } |
154 | ||
155 | static void | |
156 | fwdConnectTimeout(int fd, void *data) | |
157 | { | |
158 | FwdState *fwdState = data; | |
159 | StoreEntry *entry = fwdState->entry; | |
160 | ErrorState *err; | |
161 | debug(17, 3) ("fwdConnectTimeout: FD %d: '%s'\n", fd, storeUrl(entry)); | |
6801f8a8 | 162 | assert(fd == fwdState->server_fd); |
41462d93 | 163 | if (entry->mem_obj->inmem_hi == 0) { |
164 | err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT); | |
165 | err->request = requestLink(fwdState->request); | |
166 | errorAppendEntry(entry, err); | |
167 | } else { | |
168 | storeAbort(entry, 0); | |
169 | } | |
170 | comm_close(fd); | |
171 | } | |
172 | ||
173 | static void | |
174 | fwdConnectStart(FwdState * fwdState) | |
175 | { | |
176 | const char *url = storeUrl(fwdState->entry); | |
177 | int fd; | |
178 | ErrorState *err; | |
910169e5 | 179 | FwdServer *srv = fwdState->servers; |
41462d93 | 180 | assert(srv); |
cddc721b | 181 | assert(fwdState->server_fd == -1); |
41462d93 | 182 | debug(17, 3) ("fwdConnectStart: %s\n", url); |
183 | if ((fd = pconnPop(srv->host, srv->port)) >= 0) { | |
184 | debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd); | |
9dbf253d | 185 | fwdState->server_fd = fd; |
910169e5 | 186 | comm_add_close_handler(fd, fwdServerClosed, fwdState); |
41462d93 | 187 | fwdConnectDone(fd, COMM_OK, fwdState); |
188 | return; | |
189 | } | |
190 | fd = comm_open(SOCK_STREAM, | |
191 | 0, | |
192 | Config.Addrs.tcp_outgoing, | |
193 | 0, | |
194 | COMM_NONBLOCKING, | |
195 | url); | |
196 | if (fd < 0) { | |
197 | debug(50, 4) ("fwdConnectStart: %s\n", xstrerror()); | |
198 | err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR); | |
199 | err->xerrno = errno; | |
200 | err->request = requestLink(fwdState->request); | |
201 | errorAppendEntry(fwdState->entry, err); | |
910169e5 | 202 | fwdStateFree(fwdState); |
41462d93 | 203 | return; |
204 | } | |
6801f8a8 | 205 | fwdState->server_fd = fd; |
68bd6892 | 206 | fwdState->n_tries++; |
910169e5 | 207 | comm_add_close_handler(fd, fwdServerClosed, fwdState); |
41462d93 | 208 | commSetTimeout(fd, |
209 | Config.Timeout.connect, | |
210 | fwdConnectTimeout, | |
211 | fwdState); | |
212 | commConnectStart(fd, | |
213 | srv->host, | |
214 | srv->port, | |
215 | fwdConnectDone, | |
216 | fwdState); | |
217 | } | |
218 | ||
219 | static void | |
220 | fwdStartComplete(peer * p, void *data) | |
221 | { | |
222 | FwdState *fwdState = data; | |
910169e5 | 223 | FwdServer *s; |
798b0889 | 224 | s = memAllocate(MEM_FWD_SERVER); |
41462d93 | 225 | if (NULL != p) { |
226 | s->host = xstrdup(p->host); | |
227 | s->port = p->http_port; | |
228 | s->peer = p; | |
229 | } else { | |
230 | s->host = xstrdup(fwdState->request->host); | |
231 | s->port = fwdState->request->port; | |
232 | } | |
233 | fwdState->servers = s; | |
234 | fwdConnectStart(fwdState); | |
235 | } | |
236 | ||
237 | static void | |
238 | fwdStartFail(peer * peernotused, void *data) | |
239 | { | |
240 | FwdState *fwdState = data; | |
241 | ErrorState *err; | |
41462d93 | 242 | err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE); |
243 | err->request = requestLink(fwdState->request); | |
244 | errorAppendEntry(fwdState->entry, err); | |
245 | requestUnlink(fwdState->request); | |
910169e5 | 246 | fwdStateFree(fwdState); |
41462d93 | 247 | } |
910169e5 | 248 | |
41462d93 | 249 | static void |
6801f8a8 | 250 | fwdDispatch(FwdState * fwdState) |
41462d93 | 251 | { |
252 | peer *p; | |
253 | request_t *request = fwdState->request; | |
254 | StoreEntry *entry = fwdState->entry; | |
255 | debug(17, 5) ("fwdDispatch: FD %d: Fetching '%s %s'\n", | |
910169e5 | 256 | fwdState->client_fd, |
41462d93 | 257 | RequestMethodStr[request->method], |
258 | storeUrl(entry)); | |
9dbf253d | 259 | /*assert(!EBIT_TEST(entry->flag, ENTRY_DISPATCHED)); */ |
41462d93 | 260 | assert(entry->ping_status != PING_WAITING); |
4ca83e82 | 261 | assert(entry->lock_count); |
41462d93 | 262 | EBIT_SET(entry->flag, ENTRY_DISPATCHED); |
263 | netdbPingSite(request->host); | |
e0ebe27c | 264 | /* |
265 | * Assert that server_fd is set. This is to guarantee that fwdState | |
266 | * is attached to something and will be deallocated when server_fd | |
267 | * is closed. | |
268 | */ | |
269 | assert(fwdState->server_fd > -1); | |
41462d93 | 270 | if (fwdState->servers && (p = fwdState->servers->peer)) { |
271 | p->stats.fetches++; | |
6801f8a8 | 272 | httpStart(fwdState, fwdState->server_fd); |
41462d93 | 273 | } else { |
274 | switch (request->protocol) { | |
275 | case PROTO_HTTP: | |
6801f8a8 | 276 | httpStart(fwdState, fwdState->server_fd); |
41462d93 | 277 | break; |
278 | case PROTO_GOPHER: | |
6801f8a8 | 279 | gopherStart(entry, fwdState->server_fd); |
41462d93 | 280 | break; |
281 | case PROTO_FTP: | |
6801f8a8 | 282 | ftpStart(request, entry, fwdState->server_fd); |
41462d93 | 283 | break; |
284 | case PROTO_WAIS: | |
6801f8a8 | 285 | waisStart(request, entry, fwdState->server_fd); |
41462d93 | 286 | break; |
287 | case PROTO_CACHEOBJ: | |
e0ebe27c | 288 | case PROTO_INTERNAL: |
289 | fatal_dump("Should never get here"); | |
41462d93 | 290 | break; |
291 | case PROTO_URN: | |
292 | urnStart(request, entry); | |
293 | break; | |
294 | case PROTO_WHOIS: | |
1640c2df | 295 | whoisStart(fwdState, fwdState->server_fd); |
41462d93 | 296 | break; |
41462d93 | 297 | default: |
298 | if (request->method == METHOD_CONNECT) { | |
299 | ErrorState *err; | |
300 | debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n", | |
301 | storeUrl(entry)); | |
302 | err = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST); | |
303 | err->request = requestLink(request); | |
304 | errorAppendEntry(entry, err); | |
305 | } | |
306 | } | |
307 | } | |
308 | } | |
309 | ||
310 | /* PUBLIC FUNCTIONS */ | |
311 | ||
41462d93 | 312 | void |
313 | fwdStart(int fd, StoreEntry * entry, request_t * request) | |
314 | { | |
315 | FwdState *fwdState; | |
316 | debug(17, 3) ("fwdStart: '%s'\n", storeUrl(entry)); | |
317 | entry->mem_obj->request = requestLink(request); | |
318 | entry->mem_obj->fd = fd; | |
e0ebe27c | 319 | switch (request->protocol) { |
320 | /* | |
321 | * Note, don't create fwdState for these requests | |
322 | */ | |
323 | case PROTO_INTERNAL: | |
324 | internalStart(request, entry); | |
325 | return; | |
326 | case PROTO_CACHEOBJ: | |
327 | cachemgrStart(fd, request, entry); | |
328 | return; | |
329 | default: | |
330 | break; | |
331 | } | |
798b0889 | 332 | fwdState = memAllocate(MEM_FWD_STATE); |
333 | cbdataAdd(fwdState, MEM_FWD_STATE); | |
41462d93 | 334 | fwdState->entry = entry; |
910169e5 | 335 | fwdState->client_fd = fd; |
6801f8a8 | 336 | fwdState->server_fd = -1; |
41462d93 | 337 | fwdState->request = requestLink(request); |
f563eea9 | 338 | fwdState->start = squid_curtime; |
4ca83e82 | 339 | storeLockObject(entry); |
6801f8a8 | 340 | storeRegisterAbort(entry, fwdAbort, fwdState); |
41462d93 | 341 | peerSelect(request, |
342 | entry, | |
343 | fwdStartComplete, | |
344 | fwdStartFail, | |
345 | fwdState); | |
346 | } | |
347 | ||
348 | /* This is called before reading data from the server side to | |
349 | * decide if the server side should abort the fetch. | |
350 | * XXX This probably breaks quick_abort! | |
351 | * When to abort? | |
352 | * - NOT if there are clients reading | |
353 | * - YES if we don't know the content length | |
354 | * - YES if we do know the content length and we don't have the | |
355 | * whole object | |
356 | */ | |
357 | int | |
358 | fwdAbortFetch(StoreEntry * entry) | |
359 | { | |
360 | MemObject *mem; | |
361 | const HttpReply *reply; | |
362 | if (storeClientWaiting(entry)) | |
363 | return 0; | |
364 | mem = entry->mem_obj; | |
365 | reply = mem->reply; | |
366 | if (reply->content_length < 0) | |
367 | return 1; | |
368 | if (mem->inmem_hi < reply->content_length + reply->hdr_sz) | |
369 | return 1; | |
370 | return 0; | |
371 | } | |
372 | ||
373 | int | |
374 | fwdCheckDeferRead(int fdnotused, void *data) | |
375 | { | |
376 | StoreEntry *e = data; | |
377 | MemObject *mem = e->mem_obj; | |
378 | if (mem == NULL) | |
379 | return 0; | |
380 | if (mem->inmem_hi - storeLowestMemReaderOffset(e) < READ_AHEAD_GAP) | |
381 | return 0; | |
382 | return 1; | |
383 | } | |
910169e5 | 384 | |
385 | void | |
4ca83e82 | 386 | fwdFail(FwdState * fwdState, int err_code, http_status http_code, int xerrno) |
910169e5 | 387 | { |
4ca83e82 | 388 | debug(17, 1) ("fwdFail: %s \"%s\"\n\t%s\n", |
389 | err_type_str[err_code], | |
390 | httpStatusString(http_code), | |
391 | storeUrl(fwdState->entry)); | |
392 | fwdState->fail.err_code = err_code; | |
393 | fwdState->fail.http_code = http_code; | |
394 | fwdState->fail.xerrno = xerrno; | |
910169e5 | 395 | } |
6801f8a8 | 396 | |
397 | /* | |
398 | * Called when someone else calls StoreAbort() on this entry | |
399 | */ | |
400 | void | |
401 | fwdAbort(void *data) | |
402 | { | |
9dbf253d | 403 | FwdState *fwdState = data; |
404 | debug(17, 1) ("fwdAbort: %s\n", storeUrl(fwdState->entry)); | |
405 | fwdStateFree(fwdState); | |
6801f8a8 | 406 | } |
0b49cd31 | 407 | |
408 | /* | |
409 | * Frees fwdState without closing FD or generating an abort | |
410 | */ | |
411 | void | |
9dbf253d | 412 | fwdUnregister(int fd, FwdState * fwdState) |
0b49cd31 | 413 | { |
9dbf253d | 414 | debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry)); |
415 | assert(fd = fwdState->server_fd); | |
416 | comm_remove_close_handler(fd, fwdServerClosed, fwdState); | |
417 | fwdState->server_fd = -1; | |
418 | fwdStateFree(fwdState); | |
0b49cd31 | 419 | } |