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