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