]> git.ipfire.org Git - thirdparty/squid.git/blame - src/forward.cc
Fixed handling of pconns. fwdUnregister no longer frees
[thirdparty/squid.git] / src / forward.cc
CommitLineData
41462d93 1
2/*
8a28f65f 3 * $Id: forward.cc,v 1.34 1998/12/11 23:10: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
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
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 39static PSC fwdStartComplete;
6801f8a8 40static void fwdDispatch(FwdState *);
910169e5 41static void fwdConnectStart(FwdState * fwdState);
42static void fwdStateFree(FwdState * fwdState);
43static PF fwdConnectTimeout;
44static PF fwdServerClosed;
45static CNCB fwdConnectDone;
68bd6892 46static int fwdCheckRetry(FwdState * fwdState);
db1cd23c 47static int fwdReforward(FwdState *);
48static void fwdStartFail(FwdState *);
49
50static void
51fwdServerFree(FwdServer * fs)
52{
53 if (fs->peer)
54 cbdataUnlock(fs->peer);
55 memFree(fs, MEM_FWD_SERVER);
56}
57
58static void
59fwdServersFree(FwdServer ** FS)
60{
61 FwdServer *fs;
62 while ((fs = *FS)) {
63 *FS = fs->next;
64 fwdServerFree(fs);
65 }
66}
41462d93 67
68static void
4ca83e82 69fwdStateFree(FwdState * fwdState)
41462d93 70{
f563eea9 71 StoreEntry *e = fwdState->entry;
72 ErrorState *err;
6801f8a8 73 int sfd;
74 static int loop_detect = 0;
8a28f65f 75 debug(17,3)("fwdStateFree: %p\n", fwdState);
6801f8a8 76 assert(loop_detect++ == 0);
f563eea9 77 assert(e->mem_obj);
1640c2df 78 if (e->store_status == STORE_PENDING) {
79 if (e->mem_obj->inmem_hi == 0) {
80 assert(fwdState->fail.err_code);
81 err = errorCon(fwdState->fail.err_code, fwdState->fail.http_code);
82 err->request = requestLink(fwdState->request);
83 err->xerrno = fwdState->fail.xerrno;
84 errorAppendEntry(e, err);
85 } else {
86 storeAbort(e, 0);
87 }
f563eea9 88 }
db1cd23c 89 fwdServersFree(&fwdState->servers);
41462d93 90 requestUnlink(fwdState->request);
4ca83e82 91 fwdState->request = NULL;
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);
6801f8a8 103 loop_detect--;
41462d93 104}
105
68bd6892 106static int
107fwdCheckRetry(FwdState * fwdState)
108{
1640c2df 109 if (fwdState->entry->store_status != STORE_PENDING)
110 return 0;
68bd6892 111 if (fwdState->entry->mem_obj->inmem_hi > 0)
112 return 0;
113 if (fwdState->n_tries > 10)
114 return 0;
115 if (squid_curtime - fwdState->start > 120)
116 return 0;
1d22e062 117 if (pumpMethod(fwdState->request->method))
118 if (0 == pumpRestart(fwdState->request))
119 return 0;
68bd6892 120 return 1;
121}
122
910169e5 123static void
124fwdServerClosed(int fd, void *data)
125{
4ca83e82 126 FwdState *fwdState = data;
68bd6892 127 debug(17, 3) ("fwdServerClosed: FD %d %s\n", fd, storeUrl(fwdState->entry));
6801f8a8 128 assert(fwdState->server_fd == fd);
129 fwdState->server_fd = -1;
68bd6892 130 if (fwdCheckRetry(fwdState)) {
32b2931b 131 debug(17, 3) ("fwdServerClosed: re-forwarding (%d tries, %d secs)\n",
9dbf253d 132 fwdState->n_tries,
133 (int) (squid_curtime - fwdState->start));
f563eea9 134 fwdConnectStart(fwdState);
135 } else {
136 fwdStateFree(fwdState);
137 }
910169e5 138}
139
41462d93 140static void
141fwdConnectDone(int server_fd, int status, void *data)
142{
143 FwdState *fwdState = data;
db1cd23c 144 FwdServer *fs = fwdState->servers;
41462d93 145 ErrorState *err;
db1cd23c 146 request_t *request = fwdState->request;
6801f8a8 147 assert(fwdState->server_fd == server_fd);
41462d93 148 if (status == COMM_ERR_DNS) {
149 debug(17, 4) ("fwdConnectDone: Unknown host: %s\n",
db1cd23c 150 request->host);
41462d93 151 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
152 err->dnsserver_msg = xstrdup(dns_error_message);
db1cd23c 153 err->request = requestLink(request);
41462d93 154 errorAppendEntry(fwdState->entry, err);
155 comm_close(server_fd);
156 } else if (status != COMM_OK) {
db1cd23c 157 assert(fs);
41462d93 158 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
159 err->xerrno = errno;
db1cd23c 160 if (fs->peer) {
161 err->host = xstrdup(fs->peer->host);
162 err->port = fs->peer->http_port;
163 } else {
164 err->host = xstrdup(request->host);
165 err->port = request->port;
166 }
167 err->request = requestLink(request);
41462d93 168 errorAppendEntry(fwdState->entry, err);
db1cd23c 169 if (fs->peer)
170 peerCheckConnectStart(fs->peer);
41462d93 171 comm_close(server_fd);
172 } else {
173 fd_note(server_fd, storeUrl(fwdState->entry));
174 fd_table[server_fd].uses++;
6801f8a8 175 fwdDispatch(fwdState);
41462d93 176 }
41462d93 177}
178
179static void
180fwdConnectTimeout(int fd, void *data)
181{
182 FwdState *fwdState = data;
183 StoreEntry *entry = fwdState->entry;
184 ErrorState *err;
185 debug(17, 3) ("fwdConnectTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
6801f8a8 186 assert(fd == fwdState->server_fd);
41462d93 187 if (entry->mem_obj->inmem_hi == 0) {
188 err = errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT);
189 err->request = requestLink(fwdState->request);
190 errorAppendEntry(entry, err);
191 } else {
192 storeAbort(entry, 0);
193 }
194 comm_close(fd);
195}
196
197static void
198fwdConnectStart(FwdState * fwdState)
199{
200 const char *url = storeUrl(fwdState->entry);
201 int fd;
202 ErrorState *err;
db1cd23c 203 FwdServer *fs = fwdState->servers;
204 const char *host;
205 unsigned short port;
206 assert(fs);
cddc721b 207 assert(fwdState->server_fd == -1);
41462d93 208 debug(17, 3) ("fwdConnectStart: %s\n", url);
db1cd23c 209 if (fs->peer) {
210 host = fs->peer->host;
211 port = fs->peer->http_port;
212 } else {
213 host = fwdState->request->host;
214 port = fwdState->request->port;
215 }
216 if ((fd = pconnPop(host, port)) >= 0) {
41462d93 217 debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd);
9dbf253d 218 fwdState->server_fd = fd;
910169e5 219 comm_add_close_handler(fd, fwdServerClosed, fwdState);
41462d93 220 fwdConnectDone(fd, COMM_OK, fwdState);
221 return;
222 }
223 fd = comm_open(SOCK_STREAM,
224 0,
225 Config.Addrs.tcp_outgoing,
226 0,
227 COMM_NONBLOCKING,
228 url);
229 if (fd < 0) {
230 debug(50, 4) ("fwdConnectStart: %s\n", xstrerror());
231 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
232 err->xerrno = errno;
233 err->request = requestLink(fwdState->request);
234 errorAppendEntry(fwdState->entry, err);
910169e5 235 fwdStateFree(fwdState);
41462d93 236 return;
237 }
6801f8a8 238 fwdState->server_fd = fd;
68bd6892 239 fwdState->n_tries++;
910169e5 240 comm_add_close_handler(fd, fwdServerClosed, fwdState);
41462d93 241 commSetTimeout(fd,
242 Config.Timeout.connect,
243 fwdConnectTimeout,
244 fwdState);
db1cd23c 245 commConnectStart(fd, host, port, fwdConnectDone, fwdState);
41462d93 246}
247
248static void
db1cd23c 249fwdStartComplete(FwdServer * servers, void *data)
41462d93 250{
251 FwdState *fwdState = data;
db1cd23c 252 if (servers != NULL) {
253 fwdState->servers = servers;
254 fwdConnectStart(fwdState);
41462d93 255 } else {
db1cd23c 256 fwdStartFail(fwdState);
41462d93 257 }
41462d93 258}
259
260static void
db1cd23c 261fwdStartFail(FwdState * fwdState)
41462d93 262{
41462d93 263 ErrorState *err;
41462d93 264 err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE);
265 err->request = requestLink(fwdState->request);
266 errorAppendEntry(fwdState->entry, err);
910169e5 267 fwdStateFree(fwdState);
41462d93 268}
910169e5 269
41462d93 270static void
6801f8a8 271fwdDispatch(FwdState * fwdState)
41462d93 272{
273 peer *p;
274 request_t *request = fwdState->request;
275 StoreEntry *entry = fwdState->entry;
276 debug(17, 5) ("fwdDispatch: FD %d: Fetching '%s %s'\n",
910169e5 277 fwdState->client_fd,
41462d93 278 RequestMethodStr[request->method],
279 storeUrl(entry));
d46a87a8 280 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
41462d93 281 assert(entry->ping_status != PING_WAITING);
4ca83e82 282 assert(entry->lock_count);
d46a87a8 283 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
41462d93 284 netdbPingSite(request->host);
e0ebe27c 285 /*
286 * Assert that server_fd is set. This is to guarantee that fwdState
287 * is attached to something and will be deallocated when server_fd
288 * is closed.
289 */
290 assert(fwdState->server_fd > -1);
41462d93 291 if (fwdState->servers && (p = fwdState->servers->peer)) {
292 p->stats.fetches++;
db1cd23c 293 httpStart(fwdState);
41462d93 294 } else {
295 switch (request->protocol) {
296 case PROTO_HTTP:
db1cd23c 297 httpStart(fwdState);
41462d93 298 break;
299 case PROTO_GOPHER:
db1cd23c 300 gopherStart(fwdState);
41462d93 301 break;
302 case PROTO_FTP:
db1cd23c 303 ftpStart(fwdState);
41462d93 304 break;
305 case PROTO_WAIS:
db1cd23c 306 waisStart(fwdState);
41462d93 307 break;
308 case PROTO_CACHEOBJ:
e0ebe27c 309 case PROTO_INTERNAL:
41462d93 310 case PROTO_URN:
ae0b4725 311 fatal_dump("Should never get here");
41462d93 312 break;
313 case PROTO_WHOIS:
db1cd23c 314 whoisStart(fwdState);
41462d93 315 break;
41462d93 316 default:
ce45d320 317 debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n",
318 storeUrl(entry));
c68e9c6b 319 fwdFail(fwdState, ERR_UNSUP_REQ, HTTP_BAD_REQUEST, -1);
320 comm_close(fwdState->server_fd);
ce45d320 321 break;
41462d93 322 }
323 }
324}
325
db1cd23c 326static int
327fwdReforward(FwdState * fwdState)
328{
329 StoreEntry *e = fwdState->entry;
330 FwdServer *fs = fwdState->servers;
331 http_status s;
332 assert(e->store_status == STORE_PENDING);
333 assert(e->mem_obj);
334 if (fwdState->n_tries > 9)
335 return 0;
336 assert(fs);
337 fwdState->servers = fs->next;
338 fwdServerFree(fs);
339 if (fwdState->servers == NULL) {
340 debug(17, 3) ("fwdReforward: No forward-servers left\n");
341 return 0;
342 }
343 s = e->mem_obj->reply->sline.status;
344 debug(17, 3) ("fwdReforward: status %d\n", (int) s);
345 switch (s) {
346 case HTTP_FORBIDDEN:
347 case HTTP_INTERNAL_SERVER_ERROR:
348 case HTTP_NOT_IMPLEMENTED:
349 case HTTP_BAD_GATEWAY:
350 case HTTP_SERVICE_UNAVAILABLE:
351 case HTTP_GATEWAY_TIMEOUT:
352 return 1;
353 default:
354 return 0;
355 }
356 /* NOTREACHED */
357}
358
41462d93 359/* PUBLIC FUNCTIONS */
360
41462d93 361void
db1cd23c 362fwdStart(int fd, StoreEntry * e, request_t * r, struct in_addr client_addr)
41462d93 363{
364 FwdState *fwdState;
d1061d2b 365 aclCheck_t ch;
366 int answer;
367 ErrorState *err;
5843eb62 368 /*
db1cd23c 369 * client_addr == no_addr indicates this is an "internal" request
5843eb62 370 * from peer_digest.c, asn.c, netdb.c, etc and should always
371 * be allowed. yuck, I know.
d1061d2b 372 */
db1cd23c 373 if (client_addr.s_addr != no_addr.s_addr) {
5843eb62 374 /*
375 * Check if this host is allowed to fetch MISSES from us (miss_access)
376 */
377 memset(&ch, '\0', sizeof(aclCheck_t));
db1cd23c 378 ch.src_addr = client_addr;
5843eb62 379 ch.request = r;
380 answer = aclCheckFast(Config.accessList.miss, &ch);
381 if (answer == 0) {
382 err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN);
383 err->request = requestLink(r);
db1cd23c 384 err->src_addr = client_addr;
5843eb62 385 errorAppendEntry(e, err);
386 return;
387 }
d1061d2b 388 }
389 debug(17, 3) ("fwdStart: '%s'\n", storeUrl(e));
390 e->mem_obj->request = requestLink(r);
391 e->mem_obj->fd = fd;
392 switch (r->protocol) {
0cdcddb9 393 /*
394 * Note, don't create fwdState for these requests
395 */
e0ebe27c 396 case PROTO_INTERNAL:
d1061d2b 397 internalStart(r, e);
e0ebe27c 398 return;
399 case PROTO_CACHEOBJ:
d1061d2b 400 cachemgrStart(fd, r, e);
e0ebe27c 401 return;
ae0b4725 402 case PROTO_URN:
403 urnStart(r, e);
404 return;
e0ebe27c 405 default:
406 break;
407 }
798b0889 408 fwdState = memAllocate(MEM_FWD_STATE);
db1cd23c 409 cbdataAdd(fwdState, memFree, MEM_FWD_STATE);
d1061d2b 410 fwdState->entry = e;
910169e5 411 fwdState->client_fd = fd;
6801f8a8 412 fwdState->server_fd = -1;
d1061d2b 413 fwdState->request = requestLink(r);
f563eea9 414 fwdState->start = squid_curtime;
d1061d2b 415 storeLockObject(e);
416 storeRegisterAbort(e, fwdAbort, fwdState);
db1cd23c 417 peerSelect(r, e, fwdStartComplete, fwdState);
41462d93 418}
419
41462d93 420int
421fwdCheckDeferRead(int fdnotused, void *data)
422{
423 StoreEntry *e = data;
424 MemObject *mem = e->mem_obj;
425 if (mem == NULL)
426 return 0;
447e176b 427#if DELAY_POOLS
428 if (delayMostBytesWanted(mem, 1) == 0)
429 return 1;
430#endif
41462d93 431 if (mem->inmem_hi - storeLowestMemReaderOffset(e) < READ_AHEAD_GAP)
432 return 0;
433 return 1;
434}
910169e5 435
436void
4ca83e82 437fwdFail(FwdState * fwdState, int err_code, http_status http_code, int xerrno)
910169e5 438{
d46a87a8 439 assert(EBIT_TEST(fwdState->entry->flags, ENTRY_FWD_HDR_WAIT));
32b2931b 440 debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n",
4ca83e82 441 err_type_str[err_code],
442 httpStatusString(http_code),
443 storeUrl(fwdState->entry));
444 fwdState->fail.err_code = err_code;
445 fwdState->fail.http_code = http_code;
446 fwdState->fail.xerrno = xerrno;
910169e5 447}
6801f8a8 448
449/*
450 * Called when someone else calls StoreAbort() on this entry
451 */
452void
453fwdAbort(void *data)
454{
9dbf253d 455 FwdState *fwdState = data;
32b2931b 456 debug(17, 3) ("fwdAbort: %s\n", storeUrl(fwdState->entry));
9dbf253d 457 fwdStateFree(fwdState);
6801f8a8 458}
0b49cd31 459
460/*
461 * Frees fwdState without closing FD or generating an abort
462 */
463void
9dbf253d 464fwdUnregister(int fd, FwdState * fwdState)
0b49cd31 465{
9dbf253d 466 debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry));
467 assert(fd = fwdState->server_fd);
468 comm_remove_close_handler(fd, fwdServerClosed, fwdState);
469 fwdState->server_fd = -1;
0b49cd31 470}
db1cd23c 471
472/*
473 * server-side modules call fwdComplete() when they are done
474 * downloading an object. Then, we either 1) re-forward the
475 * request somewhere else if needed, or 2) call storeComplete()
476 * to finish it off
477 */
478void
479fwdComplete(FwdState * fwdState)
480{
481 StoreEntry *e = fwdState->entry;
482 assert(e->store_status == STORE_PENDING);
8a28f65f 483 debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e),
db1cd23c 484 e->mem_obj->reply->sline.status);
485 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
486 debug(17, 3) ("ENTRY_FWD_HDR_WAIT not set, calling storeComplete\n");
487 storeComplete(e);
488 } else if (fwdReforward(fwdState)) {
489debug(0,0)("fwdComplete: re-forwarding %d %s\n",
490 e->mem_obj->reply->sline.status,
491 storeUrl(e));
8a28f65f 492 if (fwdState->server_fd > -1)
493 fwdUnregister(fwdState->server_fd, fwdState);
db1cd23c 494 storeEntryReset(e);
495 fwdStartComplete(fwdState->servers, fwdState);
496 } else {
497 EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
498 storeComplete(e);
499 }
500}