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