]> git.ipfire.org Git - thirdparty/squid.git/blame - src/forward.cc
Avoid NULL http->entry pointer in clientBuildReplyHeader().
[thirdparty/squid.git] / src / forward.cc
CommitLineData
41462d93 1
2/*
2b6662ba 3 * $Id: forward.cc,v 1.80 2001/01/12 00:37:17 wessels Exp $
41462d93 4 *
5 * DEBUG: section 17 Request Forwarding
6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
41462d93 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see 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;
c7f9eb6d 52static peer *fwdStateServerPeer(FwdState *);
8ddcc35d 53
54#define MAX_FWD_STATS_IDX 9
9977e14b 55static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1];
db1cd23c 56
225644d7 57#if WIP_FWD_LOG
58static void fwdLog(FwdState * fwdState);
59static Logfile *logfile = NULL;
60#endif
61
c7f9eb6d 62static peer *
63fwdStateServerPeer(FwdState * fwdState)
64{
65 if (NULL == fwdState)
66 return NULL;
67 if (NULL == fwdState->servers)
68 return NULL;
69 return fwdState->servers->peer;
70}
71
db1cd23c 72static void
73fwdServerFree(FwdServer * fs)
74{
75 if (fs->peer)
76 cbdataUnlock(fs->peer);
77 memFree(fs, MEM_FWD_SERVER);
78}
79
41462d93 80static void
4ca83e82 81fwdStateFree(FwdState * fwdState)
41462d93 82{
f563eea9 83 StoreEntry *e = fwdState->entry;
6801f8a8 84 int sfd;
c7f9eb6d 85 peer *p;
0bdf2621 86 debug(17, 3) ("fwdStateFree: %p\n", fwdState);
f563eea9 87 assert(e->mem_obj);
bc87dc25 88#if URL_CHECKSUM_DEBUG
89 assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url));
225644d7 90#endif
91#if WIP_FWD_LOG
92 fwdLog(fwdState);
bc87dc25 93#endif
1640c2df 94 if (e->store_status == STORE_PENDING) {
95 if (e->mem_obj->inmem_hi == 0) {
ec250dfd 96 assert(fwdState->err);
97 errorAppendEntry(e, fwdState->err);
2c450e4e 98 fwdState->err = NULL;
b746bd8a 99 } else {
8f10798d 100 EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
b746bd8a 101 storeComplete(e);
8a4f6dd6 102 storeReleaseRequest(e);
1640c2df 103 }
f563eea9 104 }
8a4f6dd6 105 if (storePendingNClients(e) > 0)
106 assert(!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT));
c7f9eb6d 107 p = fwdStateServerPeer(fwdState);
db1cd23c 108 fwdServersFree(&fwdState->servers);
41462d93 109 requestUnlink(fwdState->request);
4ca83e82 110 fwdState->request = NULL;
2c450e4e 111 if (fwdState->err)
112 errorStateFree(fwdState->err);
f563eea9 113 storeUnregisterAbort(e);
114 storeUnlockObject(e);
4ca83e82 115 fwdState->entry = NULL;
6801f8a8 116 sfd = fwdState->server_fd;
117 if (sfd > -1) {
118 comm_remove_close_handler(sfd, fwdServerClosed, fwdState);
119 fwdState->server_fd = -1;
32b2931b 120 debug(17, 3) ("fwdStateFree: closing FD %d\n", sfd);
6801f8a8 121 comm_close(sfd);
c7f9eb6d 122 if (p)
123 p->stats.conn_open--;
6801f8a8 124 }
41462d93 125 cbdataFree(fwdState);
126}
127
68bd6892 128static int
129fwdCheckRetry(FwdState * fwdState)
130{
1640c2df 131 if (fwdState->entry->store_status != STORE_PENDING)
132 return 0;
68bd6892 133 if (fwdState->entry->mem_obj->inmem_hi > 0)
134 return 0;
135 if (fwdState->n_tries > 10)
136 return 0;
efd900cb 137 if (squid_curtime - fwdState->start > Config.Timeout.connect)
68bd6892 138 return 0;
ee08bdf5 139 if (fwdState->flags.dont_retry)
140 return 0;
94439e4e 141 if (fwdState->request->flags.body_sent)
142 return 0;
68bd6892 143 return 1;
144}
145
910169e5 146static void
147fwdServerClosed(int fd, void *data)
148{
4ca83e82 149 FwdState *fwdState = data;
ec250dfd 150 debug(17, 2) ("fwdServerClosed: FD %d %s\n", fd, storeUrl(fwdState->entry));
6801f8a8 151 assert(fwdState->server_fd == fd);
152 fwdState->server_fd = -1;
68bd6892 153 if (fwdCheckRetry(fwdState)) {
32b2931b 154 debug(17, 3) ("fwdServerClosed: re-forwarding (%d tries, %d secs)\n",
9dbf253d 155 fwdState->n_tries,
156 (int) (squid_curtime - fwdState->start));
6b8e7481 157 if (fwdState->servers->next) {
eb406bb7 158 /* use next, or cycle if origin server isn't last */
6b8e7481 159 FwdServer *fs = fwdState->servers;
eb406bb7 160 FwdServer **T, *T2 = NULL;
6b8e7481 161 fwdState->servers = fs->next;
a4b8110e 162 for (T = &fwdState->servers; *T; T2 = *T, T = &(*T)->next);
163 if (T2 && T2->peer) {
eb406bb7 164 /* cycle */
165 *T = fs;
166 fs->next = NULL;
167 } else {
168 /* Use next. The last "direct" entry is retried multiple times */
169 fwdState->servers = fs->next;
170 fwdServerFree(fs);
171 }
6b8e7481 172 }
ee08bdf5 173 /* use eventAdd to break potential call sequence loops */
f726e966 174 eventAdd("fwdConnectStart", fwdConnectStart, fwdState, 0.0, 0);
f563eea9 175 } else {
176 fwdStateFree(fwdState);
177 }
910169e5 178}
179
41462d93 180static void
181fwdConnectDone(int server_fd, int status, void *data)
182{
183 FwdState *fwdState = data;
e07b2888 184 static FwdState *current = NULL;
db1cd23c 185 FwdServer *fs = fwdState->servers;
41462d93 186 ErrorState *err;
db1cd23c 187 request_t *request = fwdState->request;
e07b2888 188 assert(current != fwdState);
189 current = fwdState;
6801f8a8 190 assert(fwdState->server_fd == server_fd);
41462d93 191 if (status == COMM_ERR_DNS) {
3280e9d6 192 /*
193 * Only set the dont_retry flag if the DNS lookup fails on
194 * a direct connection. If DNS lookup fails when trying
195 * a neighbor cache, we may want to retry another option.
196 */
197 if (NULL == fs->peer)
198 fwdState->flags.dont_retry = 1;
41462d93 199 debug(17, 4) ("fwdConnectDone: Unknown host: %s\n",
db1cd23c 200 request->host);
41462d93 201 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
202 err->dnsserver_msg = xstrdup(dns_error_message);
db1cd23c 203 err->request = requestLink(request);
ec250dfd 204 fwdFail(fwdState, err);
c7f9eb6d 205 if (fs->peer)
206 fs->peer->stats.conn_open--;
41462d93 207 comm_close(server_fd);
208 } else if (status != COMM_OK) {
db1cd23c 209 assert(fs);
41462d93 210 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
211 err->xerrno = errno;
db1cd23c 212 if (fs->peer) {
213 err->host = xstrdup(fs->peer->host);
214 err->port = fs->peer->http_port;
215 } else {
216 err->host = xstrdup(request->host);
217 err->port = request->port;
218 }
219 err->request = requestLink(request);
ec250dfd 220 fwdFail(fwdState, err);
c7f9eb6d 221 if (fs->peer) {
eb406bb7 222 peerConnectFailed(fs->peer);
c7f9eb6d 223 fs->peer->stats.conn_open--;
224 }
41462d93 225 comm_close(server_fd);
226 } else {
8a4f6dd6 227 debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(fwdState->entry));
890b0fa8 228 if (fs->peer)
229 hierarchyNote(&fwdState->request->hier, fs->code, fs->peer->host);
230 else if (Config.onoff.log_ip_on_direct)
231 hierarchyNote(&fwdState->request->hier, fs->code, fd_table[server_fd].ipaddr);
232 else
233 hierarchyNote(&fwdState->request->hier, fs->code, request->host);
41462d93 234 fd_note(server_fd, storeUrl(fwdState->entry));
235 fd_table[server_fd].uses++;
eb406bb7 236 if (fs->peer)
237 peerConnectSucceded(fs->peer);
6801f8a8 238 fwdDispatch(fwdState);
41462d93 239 }
e07b2888 240 current = NULL;
41462d93 241}
242
243static void
244fwdConnectTimeout(int fd, void *data)
245{
246 FwdState *fwdState = data;
247 StoreEntry *entry = fwdState->entry;
248 ErrorState *err;
c7f9eb6d 249 peer *p = fwdStateServerPeer(fwdState);
ec250dfd 250 debug(17, 2) ("fwdConnectTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
6801f8a8 251 assert(fd == fwdState->server_fd);
41462d93 252 if (entry->mem_obj->inmem_hi == 0) {
b5ea121c 253 err = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT);
41462d93 254 err->request = requestLink(fwdState->request);
ec250dfd 255 err->xerrno = ETIMEDOUT;
256 fwdFail(fwdState, err);
efd900cb 257 /*
258 * This marks the peer DOWN ...
259 */
260 if (fwdState->servers)
261 if (fwdState->servers->peer)
eb406bb7 262 peerConnectFailed(fwdState->servers->peer);
41462d93 263 }
c7f9eb6d 264 if (p)
265 p->stats.conn_open--;
41462d93 266 comm_close(fd);
267}
268
269static void
ee08bdf5 270fwdConnectStart(void *data)
41462d93 271{
8a4f6dd6 272 FwdState *fwdState = data;
41462d93 273 const char *url = storeUrl(fwdState->entry);
274 int fd;
275 ErrorState *err;
db1cd23c 276 FwdServer *fs = fwdState->servers;
277 const char *host;
278 unsigned short port;
3f62decd 279 time_t ctimeout;
db1cd23c 280 assert(fs);
cddc721b 281 assert(fwdState->server_fd == -1);
41462d93 282 debug(17, 3) ("fwdConnectStart: %s\n", url);
db1cd23c 283 if (fs->peer) {
284 host = fs->peer->host;
285 port = fs->peer->http_port;
3f62decd 286 ctimeout = fs->peer->connect_timeout > 0 ? fs->peer->connect_timeout
287 : Config.Timeout.peer_connect;
13c7936a 288 } else if (fwdState->request->flags.accelerated &&
a4b8110e 289 Config.Accel.single_host && Config.Accel.host) {
13c7936a 290 host = Config.Accel.host;
291 port = Config.Accel.port;
292 ctimeout = Config.Timeout.connect;
db1cd23c 293 } else {
294 host = fwdState->request->host;
295 port = fwdState->request->port;
3f62decd 296 ctimeout = Config.Timeout.connect;
db1cd23c 297 }
298 if ((fd = pconnPop(host, port)) >= 0) {
41462d93 299 debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd);
9dbf253d 300 fwdState->server_fd = fd;
9977e14b 301 fwdState->n_tries++;
910169e5 302 comm_add_close_handler(fd, fwdServerClosed, fwdState);
41462d93 303 fwdConnectDone(fd, COMM_OK, fwdState);
304 return;
305 }
bc87dc25 306#if URL_CHECKSUM_DEBUG
307 assert(fwdState->entry->mem_obj->chksum == url_checksum(url));
308#endif
41462d93 309 fd = comm_open(SOCK_STREAM,
310 0,
311 Config.Addrs.tcp_outgoing,
312 0,
313 COMM_NONBLOCKING,
314 url);
315 if (fd < 0) {
316 debug(50, 4) ("fwdConnectStart: %s\n", xstrerror());
317 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
318 err->xerrno = errno;
319 err->request = requestLink(fwdState->request);
ec250dfd 320 fwdFail(fwdState, err);
910169e5 321 fwdStateFree(fwdState);
41462d93 322 return;
323 }
6801f8a8 324 fwdState->server_fd = fd;
68bd6892 325 fwdState->n_tries++;
c7f9eb6d 326 /*
327 * stats.conn_open is used to account for the number of
328 * connections that we have open to the peer, so we can limit
329 * based on the max-conn option. We need to increment here,
330 * even if the connection may fail.
331 */
332 if (fs->peer)
333 fs->peer->stats.conn_open++;
910169e5 334 comm_add_close_handler(fd, fwdServerClosed, fwdState);
41462d93 335 commSetTimeout(fd,
3f62decd 336 ctimeout,
41462d93 337 fwdConnectTimeout,
338 fwdState);
db1cd23c 339 commConnectStart(fd, host, port, fwdConnectDone, fwdState);
41462d93 340}
341
342static void
db1cd23c 343fwdStartComplete(FwdServer * servers, void *data)
41462d93 344{
345 FwdState *fwdState = data;
8a4f6dd6 346 debug(17, 3) ("fwdStartComplete: %s\n", storeUrl(fwdState->entry));
db1cd23c 347 if (servers != NULL) {
348 fwdState->servers = servers;
349 fwdConnectStart(fwdState);
41462d93 350 } else {
db1cd23c 351 fwdStartFail(fwdState);
41462d93 352 }
41462d93 353}
354
355static void
db1cd23c 356fwdStartFail(FwdState * fwdState)
41462d93 357{
41462d93 358 ErrorState *err;
8a4f6dd6 359 debug(17, 3) ("fwdStartFail: %s\n", storeUrl(fwdState->entry));
41462d93 360 err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE);
361 err->request = requestLink(fwdState->request);
ec250dfd 362 err->xerrno = errno;
363 fwdFail(fwdState, err);
910169e5 364 fwdStateFree(fwdState);
41462d93 365}
910169e5 366
41462d93 367static void
6801f8a8 368fwdDispatch(FwdState * fwdState)
41462d93 369{
c7f9eb6d 370 peer *p = NULL;
41462d93 371 request_t *request = fwdState->request;
372 StoreEntry *entry = fwdState->entry;
1f38f50a 373 ErrorState *err;
b746bd8a 374 debug(17, 3) ("fwdDispatch: FD %d: Fetching '%s %s'\n",
910169e5 375 fwdState->client_fd,
41462d93 376 RequestMethodStr[request->method],
377 storeUrl(entry));
d46a87a8 378 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
41462d93 379 assert(entry->ping_status != PING_WAITING);
4ca83e82 380 assert(entry->lock_count);
d46a87a8 381 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
41462d93 382 netdbPingSite(request->host);
e0ebe27c 383 /*
384 * Assert that server_fd is set. This is to guarantee that fwdState
385 * is attached to something and will be deallocated when server_fd
386 * is closed.
387 */
388 assert(fwdState->server_fd > -1);
41462d93 389 if (fwdState->servers && (p = fwdState->servers->peer)) {
390 p->stats.fetches++;
1f38f50a 391 fwdState->request->peer_login = p->login;
db1cd23c 392 httpStart(fwdState);
41462d93 393 } else {
94439e4e 394 fwdState->request->peer_login = NULL;
41462d93 395 switch (request->protocol) {
396 case PROTO_HTTP:
db1cd23c 397 httpStart(fwdState);
41462d93 398 break;
399 case PROTO_GOPHER:
db1cd23c 400 gopherStart(fwdState);
41462d93 401 break;
402 case PROTO_FTP:
db1cd23c 403 ftpStart(fwdState);
41462d93 404 break;
405 case PROTO_WAIS:
db1cd23c 406 waisStart(fwdState);
41462d93 407 break;
408 case PROTO_CACHEOBJ:
e0ebe27c 409 case PROTO_INTERNAL:
41462d93 410 case PROTO_URN:
ae0b4725 411 fatal_dump("Should never get here");
41462d93 412 break;
413 case PROTO_WHOIS:
db1cd23c 414 whoisStart(fwdState);
41462d93 415 break;
41462d93 416 default:
ce45d320 417 debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n",
418 storeUrl(entry));
1f38f50a 419 err = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST);
420 err->request = requestLink(request);
421 fwdFail(fwdState, err);
422 /*
423 * Force a persistent connection to be closed because
424 * some Netscape browsers have a bug that sends CONNECT
425 * requests as GET's over persistent connections.
426 */
427 request->flags.proxy_keepalive = 0;
428 /*
429 * Set the dont_retry flag becuase this is not a
430 * transient (network) error; its a bug.
431 */
432 fwdState->flags.dont_retry = 1;
c7f9eb6d 433 /*
434 * this assertion exists because if we are connected to
435 * a peer, then we need to decrement p->stats.conn_open.
436 */
437 assert(NULL == p);
c68e9c6b 438 comm_close(fwdState->server_fd);
ce45d320 439 break;
41462d93 440 }
441 }
442}
443
db1cd23c 444static int
445fwdReforward(FwdState * fwdState)
446{
447 StoreEntry *e = fwdState->entry;
448 FwdServer *fs = fwdState->servers;
449 http_status s;
450 assert(e->store_status == STORE_PENDING);
451 assert(e->mem_obj);
bc87dc25 452#if URL_CHECKSUM_DEBUG
453 assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url));
454#endif
b746bd8a 455 debug(17, 3) ("fwdReforward: %s?\n", storeUrl(e));
d6eb18d6 456 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
457 debug(17, 3) ("fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set\n");
458 return 0;
459 }
db1cd23c 460 if (fwdState->n_tries > 9)
461 return 0;
94439e4e 462 if (fwdState->request->flags.body_sent)
463 return 0;
db1cd23c 464 assert(fs);
465 fwdState->servers = fs->next;
466 fwdServerFree(fs);
467 if (fwdState->servers == NULL) {
468 debug(17, 3) ("fwdReforward: No forward-servers left\n");
469 return 0;
470 }
471 s = e->mem_obj->reply->sline.status;
472 debug(17, 3) ("fwdReforward: status %d\n", (int) s);
b6a2f15e 473 return fwdReforwardableStatus(s);
db1cd23c 474}
475
41462d93 476/* PUBLIC FUNCTIONS */
477
64d8034e 478void
479fwdServersFree(FwdServer ** FS)
480{
481 FwdServer *fs;
482 while ((fs = *FS)) {
483 *FS = fs->next;
484 fwdServerFree(fs);
485 }
486}
487
41462d93 488void
7e3ce7b9 489fwdStart(int fd, StoreEntry * e, request_t * r)
41462d93 490{
491 FwdState *fwdState;
d1061d2b 492 aclCheck_t ch;
493 int answer;
494 ErrorState *err;
5843eb62 495 /*
db1cd23c 496 * client_addr == no_addr indicates this is an "internal" request
5843eb62 497 * from peer_digest.c, asn.c, netdb.c, etc and should always
498 * be allowed. yuck, I know.
d1061d2b 499 */
7e3ce7b9 500 if (r->client_addr.s_addr != no_addr.s_addr) {
5843eb62 501 /*
502 * Check if this host is allowed to fetch MISSES from us (miss_access)
503 */
504 memset(&ch, '\0', sizeof(aclCheck_t));
7e3ce7b9 505 ch.src_addr = r->client_addr;
506 ch.my_addr = r->my_addr;
507 ch.my_port = r->my_port;
5843eb62 508 ch.request = r;
509 answer = aclCheckFast(Config.accessList.miss, &ch);
510 if (answer == 0) {
511 err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN);
512 err->request = requestLink(r);
7e3ce7b9 513 err->src_addr = r->client_addr;
5843eb62 514 errorAppendEntry(e, err);
515 return;
516 }
d1061d2b 517 }
7197b20d 518 debug(17, 3) ("fwdStart: '%s'\n", storeUrl(e));
519 e->mem_obj->request = requestLink(r);
520 e->mem_obj->fd = fd;
bc87dc25 521#if URL_CHECKSUM_DEBUG
522 assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url));
523#endif
05b744f3 524 if (shutting_down) {
525 /* more yuck */
526 err = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE);
527 err->request = requestLink(r);
528 errorAppendEntry(e, err);
529 return;
530 }
d1061d2b 531 switch (r->protocol) {
0cdcddb9 532 /*
533 * Note, don't create fwdState for these requests
534 */
e0ebe27c 535 case PROTO_INTERNAL:
d1061d2b 536 internalStart(r, e);
e0ebe27c 537 return;
538 case PROTO_CACHEOBJ:
d1061d2b 539 cachemgrStart(fd, r, e);
e0ebe27c 540 return;
ae0b4725 541 case PROTO_URN:
542 urnStart(r, e);
543 return;
e0ebe27c 544 default:
545 break;
546 }
28c60158 547 fwdState = CBDATA_ALLOC(FwdState, NULL);
d1061d2b 548 fwdState->entry = e;
910169e5 549 fwdState->client_fd = fd;
6801f8a8 550 fwdState->server_fd = -1;
d1061d2b 551 fwdState->request = requestLink(r);
f563eea9 552 fwdState->start = squid_curtime;
d1061d2b 553 storeLockObject(e);
ec250dfd 554 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
7197b20d 555 storeRegisterAbort(e, fwdAbort, fwdState);
db1cd23c 556 peerSelect(r, e, fwdStartComplete, fwdState);
41462d93 557}
558
41462d93 559int
59715b38 560fwdCheckDeferRead(int fd, void *data)
41462d93 561{
562 StoreEntry *e = data;
563 MemObject *mem = e->mem_obj;
7e3ce7b9 564 int rc = 0;
41462d93 565 if (mem == NULL)
566 return 0;
bc87dc25 567#if URL_CHECKSUM_DEBUG
568 assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url));
569#endif
447e176b 570#if DELAY_POOLS
1ecaa0a0 571 if (fd < 0)
572 (void) 0;
573 else if (delayIsNoDelay(fd))
574 (void) 0;
7e3ce7b9 575 else {
576 int i = delayMostBytesWanted(mem, INT_MAX);
577 if (0 == i)
578 return 1;
579 /* was: rc = -(rc != INT_MAX); */
580 else if (INT_MAX == i)
581 rc = 0;
582 else
583 rc = -1;
584 }
447e176b 585#endif
b6a2f15e 586 if (EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT))
7e3ce7b9 587 return rc;
41462d93 588 if (mem->inmem_hi - storeLowestMemReaderOffset(e) < READ_AHEAD_GAP)
7e3ce7b9 589 return rc;
41462d93 590 return 1;
591}
910169e5 592
593void
b8890359 594fwdFail(FwdState * fwdState, ErrorState * errorState)
910169e5 595{
d46a87a8 596 assert(EBIT_TEST(fwdState->entry->flags, ENTRY_FWD_HDR_WAIT));
32b2931b 597 debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n",
ec250dfd 598 err_type_str[errorState->type],
599 httpStatusString(errorState->http_status),
4ca83e82 600 storeUrl(fwdState->entry));
ec250dfd 601 if (fwdState->err)
602 errorStateFree(fwdState->err);
603 fwdState->err = errorState;
910169e5 604}
6801f8a8 605
7197b20d 606/*
607 * Called when someone else calls StoreAbort() on this entry
608 */
9bc73deb 609static void
7197b20d 610fwdAbort(void *data)
611{
612 FwdState *fwdState = data;
ec250dfd 613 debug(17, 2) ("fwdAbort: %s\n", storeUrl(fwdState->entry));
7197b20d 614 fwdStateFree(fwdState);
615}
616
0b49cd31 617/*
618 * Frees fwdState without closing FD or generating an abort
619 */
620void
9dbf253d 621fwdUnregister(int fd, FwdState * fwdState)
0b49cd31 622{
9dbf253d 623 debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry));
7f646cdb 624 assert(fd == fwdState->server_fd);
b7fe0ab0 625 assert(fd > -1);
9dbf253d 626 comm_remove_close_handler(fd, fwdServerClosed, fwdState);
627 fwdState->server_fd = -1;
0b49cd31 628}
db1cd23c 629
630/*
631 * server-side modules call fwdComplete() when they are done
632 * downloading an object. Then, we either 1) re-forward the
633 * request somewhere else if needed, or 2) call storeComplete()
634 * to finish it off
635 */
636void
637fwdComplete(FwdState * fwdState)
638{
639 StoreEntry *e = fwdState->entry;
640 assert(e->store_status == STORE_PENDING);
8a28f65f 641 debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e),
db1cd23c 642 e->mem_obj->reply->sline.status);
bc87dc25 643#if URL_CHECKSUM_DEBUG
644 assert(e->mem_obj->chksum == url_checksum(e->mem_obj->url));
645#endif
9977e14b 646 fwdLogReplyStatus(fwdState->n_tries, e->mem_obj->reply->sline.status);
d6eb18d6 647 if (fwdReforward(fwdState)) {
77b32a34 648 debug(17, 3) ("fwdComplete: re-forwarding %d %s\n",
0bdf2621 649 e->mem_obj->reply->sline.status,
650 storeUrl(e));
8a28f65f 651 if (fwdState->server_fd > -1)
0bdf2621 652 fwdUnregister(fwdState->server_fd, fwdState);
db1cd23c 653 storeEntryReset(e);
654 fwdStartComplete(fwdState->servers, fwdState);
655 } else {
d6eb18d6 656 debug(17, 3) ("fwdComplete: not re-forwarding status %d\n",
657 e->mem_obj->reply->sline.status);
db1cd23c 658 EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
659 storeComplete(e);
d6eb18d6 660 /*
661 * If fwdState isn't associated with a server FD, it
662 * won't get freed unless we do it here.
663 */
664 if (fwdState->server_fd < 0)
665 fwdStateFree(fwdState);
db1cd23c 666 }
667}
8ddcc35d 668
669void
670fwdInit(void)
671{
9977e14b 672 cachemgrRegister("forward",
673 "Request Forwarding Statistics",
674 fwdStats, 0, 1);
225644d7 675#if WIP_FWD_LOG
676 if (logfile)
677 (void) 0;
678 else if (NULL == Config.Log.forward)
679 (void) 0;
680 else
681 logfile = logfileOpen(Config.Log.forward, 0, 1);
682#endif
8ddcc35d 683}
684
685static void
686fwdLogReplyStatus(int tries, http_status status)
687{
688 if (status > HTTP_INVALID_HEADER)
689 return;
690 assert(tries);
691 tries--;
692 if (tries > MAX_FWD_STATS_IDX)
693 tries = MAX_FWD_STATS_IDX;
694 FwdReplyCodes[tries][status]++;
695}
696
697static void
9977e14b 698fwdStats(StoreEntry * s)
8ddcc35d 699{
9977e14b 700 int i;
701 int j;
702 storeAppendPrintf(s, "Status");
703 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
704 storeAppendPrintf(s, "\ttry#%d", j + 1);
705 }
706 storeAppendPrintf(s, "\n");
707 for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
708 if (FwdReplyCodes[0][i] == 0)
709 continue;
710 storeAppendPrintf(s, "%3d", i);
711 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
712 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
8ddcc35d 713 }
9977e14b 714 storeAppendPrintf(s, "\n");
715 }
8ddcc35d 716}
b6a2f15e 717
718int
719fwdReforwardableStatus(http_status s)
720{
721 switch (s) {
722 case HTTP_FORBIDDEN:
723 case HTTP_INTERNAL_SERVER_ERROR:
724 case HTTP_NOT_IMPLEMENTED:
725 case HTTP_BAD_GATEWAY:
726 case HTTP_SERVICE_UNAVAILABLE:
727 case HTTP_GATEWAY_TIMEOUT:
728 return 1;
729 default:
730 return 0;
731 }
732 /* NOTREACHED */
733}
225644d7 734
735#if WIP_FWD_LOG
736void
737fwdUninit(void)
738{
739 logfileClose(logfile);
740 logfile = NULL;
741}
742
743void
744fwdLogRotate(void)
745{
746 if (logfile)
747 logfileRotate(logfile);
748}
749
750static void
751fwdLog(FwdState * fwdState)
752{
753 if (NULL == logfile)
754 return;
755 logfilePrintf(logfile, "%9d.%03d %03d %s %s\n",
756 (int) current_time.tv_sec,
757 (int) current_time.tv_usec / 1000,
758 fwdState->last_status,
759 RequestMethodStr[fwdState->request->method],
760 fwdState->request->canonical);
761}
762
763void
764fwdStatus(FwdState * fwdState, http_status s)
765{
766 fwdState->last_status = s;
767}
768
769#endif