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