]> git.ipfire.org Git - thirdparty/squid.git/blame - src/forward.cc
Summary: Final MSVC fixups.
[thirdparty/squid.git] / src / forward.cc
CommitLineData
41462d93 1
2/*
190154cf 3 * $Id: forward.cc,v 1.109 2003/08/10 11:00:43 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"
e6ccf245 38#include "Store.h"
528b2c61 39#include "HttpRequest.h"
40#include "fde.h"
41#include "MemObject.h"
4fb35c3c 42#include "ACLChecklist.h"
8000a965 43#include "ACL.h"
924f73bc 44#include "HttpReply.h"
41462d93 45
db1cd23c 46static PSC fwdStartComplete;
6801f8a8 47static void fwdDispatch(FwdState *);
ee08bdf5 48static void fwdConnectStart(void *); /* should be same as EVH */
910169e5 49static void fwdStateFree(FwdState * fwdState);
50static PF fwdConnectTimeout;
51static PF fwdServerClosed;
52static CNCB fwdConnectDone;
68bd6892 53static int fwdCheckRetry(FwdState * fwdState);
db1cd23c 54static int fwdReforward(FwdState *);
55static void fwdStartFail(FwdState *);
8ddcc35d 56static void fwdLogReplyStatus(int tries, http_status status);
57static OBJH fwdStats;
7197b20d 58static STABH fwdAbort;
c7f9eb6d 59static peer *fwdStateServerPeer(FwdState *);
8ddcc35d 60
61#define MAX_FWD_STATS_IDX 9
9977e14b 62static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1];
db1cd23c 63
225644d7 64#if WIP_FWD_LOG
65static void fwdLog(FwdState * fwdState);
66static Logfile *logfile = NULL;
67#endif
68
c7f9eb6d 69static peer *
70fwdStateServerPeer(FwdState * fwdState)
71{
72 if (NULL == fwdState)
62e76326 73 return NULL;
74
c7f9eb6d 75 if (NULL == fwdState->servers)
62e76326 76 return NULL;
77
29b8d8d6 78 return fwdState->servers->_peer;
c7f9eb6d 79}
80
db1cd23c 81static void
82fwdServerFree(FwdServer * fs)
83{
29b8d8d6 84 cbdataReferenceDone(fs->_peer);
db1cd23c 85 memFree(fs, MEM_FWD_SERVER);
86}
87
41462d93 88static void
4ca83e82 89fwdStateFree(FwdState * fwdState)
41462d93 90{
f563eea9 91 StoreEntry *e = fwdState->entry;
6801f8a8 92 int sfd;
c7f9eb6d 93 peer *p;
0bdf2621 94 debug(17, 3) ("fwdStateFree: %p\n", fwdState);
f563eea9 95 assert(e->mem_obj);
bc87dc25 96#if URL_CHECKSUM_DEBUG
62e76326 97
528b2c61 98 e->mem_obj->checkUrlChecksum();
225644d7 99#endif
100#if WIP_FWD_LOG
62e76326 101
225644d7 102 fwdLog(fwdState);
bc87dc25 103#endif
62e76326 104
1640c2df 105 if (e->store_status == STORE_PENDING) {
62e76326 106 if (e->isEmpty()) {
107 assert(fwdState->err);
108 errorAppendEntry(e, fwdState->err);
109 fwdState->err = NULL;
110 } else {
111 EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
112 e->complete();
113 storeReleaseRequest(e);
114 }
f563eea9 115 }
62e76326 116
8a4f6dd6 117 if (storePendingNClients(e) > 0)
62e76326 118 assert(!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT));
119
c7f9eb6d 120 p = fwdStateServerPeer(fwdState);
62e76326 121
db1cd23c 122 fwdServersFree(&fwdState->servers);
62e76326 123
41462d93 124 requestUnlink(fwdState->request);
62e76326 125
4ca83e82 126 fwdState->request = NULL;
62e76326 127
2c450e4e 128 if (fwdState->err)
62e76326 129 errorStateFree(fwdState->err);
130
f563eea9 131 storeUnregisterAbort(e);
62e76326 132
f563eea9 133 storeUnlockObject(e);
62e76326 134
4ca83e82 135 fwdState->entry = NULL;
62e76326 136
6801f8a8 137 sfd = fwdState->server_fd;
62e76326 138
6801f8a8 139 if (sfd > -1) {
62e76326 140 comm_remove_close_handler(sfd, fwdServerClosed, fwdState);
141 fwdState->server_fd = -1;
142 debug(17, 3) ("fwdStateFree: closing FD %d\n", sfd);
143 comm_close(sfd);
144
145 if (p)
146 p->stats.conn_open--;
6801f8a8 147 }
62e76326 148
41462d93 149 cbdataFree(fwdState);
150}
151
68bd6892 152static int
153fwdCheckRetry(FwdState * fwdState)
154{
d8fd0f18 155 if (shutting_down)
62e76326 156 return 0;
157
1640c2df 158 if (fwdState->entry->store_status != STORE_PENDING)
62e76326 159 return 0;
160
528b2c61 161 if (!fwdState->entry->isEmpty())
62e76326 162 return 0;
163
68bd6892 164 if (fwdState->n_tries > 10)
62e76326 165 return 0;
166
efd900cb 167 if (squid_curtime - fwdState->start > Config.Timeout.connect)
62e76326 168 return 0;
169
ee08bdf5 170 if (fwdState->flags.dont_retry)
62e76326 171 return 0;
172
94439e4e 173 if (fwdState->request->flags.body_sent)
62e76326 174 return 0;
175
68bd6892 176 return 1;
177}
178
cb928909 179static int
180fwdCheckRetriable(FwdState * fwdState)
181{
182 /* If there is a request body then Squid can only try once
183 * even if the method is indempotent
184 */
185
a2ac85d9 186 if (fwdState->request->body_connection.getRaw() != NULL)
cb928909 187 return 0;
188
189 /* RFC2616 9.1 Safe and Idempotent Methods */
190 switch (fwdState->request->method) {
191 /* 9.1.1 Safe Methods */
192
193 case METHOD_GET:
194
195 case METHOD_HEAD:
196 /* 9.1.2 Indepontent Methods */
197
198 case METHOD_PUT:
199
200 case METHOD_DELETE:
201
202 case METHOD_OPTIONS:
203
204 case METHOD_TRACE:
205 break;
206
207 default:
208 return 0;
209 }
210
211 return 1;
212}
213
910169e5 214static void
215fwdServerClosed(int fd, void *data)
216{
e6ccf245 217 FwdState *fwdState = (FwdState *)data;
ec250dfd 218 debug(17, 2) ("fwdServerClosed: FD %d %s\n", fd, storeUrl(fwdState->entry));
6801f8a8 219 assert(fwdState->server_fd == fd);
220 fwdState->server_fd = -1;
62e76326 221
68bd6892 222 if (fwdCheckRetry(fwdState)) {
62e76326 223 debug(17, 3) ("fwdServerClosed: re-forwarding (%d tries, %d secs)\n",
224 fwdState->n_tries,
225 (int) (squid_curtime - fwdState->start));
226
227 if (fwdState->servers->next) {
228 /* use next, or cycle if origin server isn't last */
229 FwdServer *fs = fwdState->servers;
230 FwdServer **T, *T2 = NULL;
231 fwdState->servers = fs->next;
232
233 for (T = &fwdState->servers; *T; T2 = *T, T = &(*T)->next)
234
235 ;
236 if (T2 && T2->_peer) {
237 /* cycle */
238 *T = fs;
239 fs->next = NULL;
240 } else {
241 /* Use next. The last "direct" entry is retried multiple times */
242 fwdState->servers = fs->next;
243 fwdServerFree(fs);
244 }
245 }
246
247 /* use eventAdd to break potential call sequence loops */
248 eventAdd("fwdConnectStart", fwdConnectStart, fwdState, 0.0, 0);
249
250 return;
d8fd0f18 251 }
62e76326 252
d8fd0f18 253 if (!fwdState->err && shutting_down) {
62e76326 254 fwdState->err =errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE);
255 fwdState->err->request = requestLink(fwdState->request);
f563eea9 256 }
62e76326 257
d8fd0f18 258 fwdStateFree(fwdState);
910169e5 259}
260
a7ad6e4e 261#if USE_SSL
262static void
263fwdNegotiateSSL(int fd, void *data)
264{
265 FwdState *fwdState = (FwdState *)data;
266 FwdServer *fs = fwdState->servers;
267 SSL *ssl = fd_table[fd].ssl;
268 int ret;
269 ErrorState *err;
190154cf 270 HttpRequest *request = fwdState->request;
62e76326 271
a7ad6e4e 272 if ((ret = SSL_connect(ssl)) <= 0) {
62e76326 273 int ssl_error = SSL_get_error(ssl, ret);
274
275 switch (ssl_error) {
276
277 case SSL_ERROR_WANT_READ:
278 commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSL, fwdState, 0);
279 return;
280
281 case SSL_ERROR_WANT_WRITE:
282 commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSL, fwdState, 0);
283 return;
284
285 default:
286 debug(81, 1) ("fwdNegotiateSSL: Error negotiating SSL connection on FD %d: %s (%d/%d)\n", fd, ERR_error_string(ERR_get_error(), NULL), ssl_error, ret);
287 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
a7ad6e4e 288#ifdef EPROTO
62e76326 289
290 err->xerrno = EPROTO;
a7ad6e4e 291#else
62e76326 292
293 err->xerrno = EACCES;
a7ad6e4e 294#endif
62e76326 295
296 if (fs->_peer) {
297 err->host = xstrdup(fs->_peer->host);
298 err->port = fs->_peer->http_port;
299 } else {
300 err->host = xstrdup(request->host);
301 err->port = request->port;
302 }
303
304 err->request = requestLink(request);
305 fwdFail(fwdState, err);
306
307 if (fs->_peer) {
308 peerConnectFailed(fs->_peer);
309 fs->_peer->stats.conn_open--;
310 }
311
312 comm_close(fd);
313 return;
314 }
a7ad6e4e 315 }
62e76326 316
a7ad6e4e 317 fwdDispatch(fwdState);
318}
319
320static void
321fwdInitiateSSL(FwdState * fwdState)
322{
323 FwdServer *fs = fwdState->servers;
324 int fd = fwdState->server_fd;
325 SSL *ssl;
326 SSL_CTX *sslContext = NULL;
327 peer *peer = fs->_peer;
62e76326 328
a7ad6e4e 329 if (peer) {
62e76326 330 assert(peer->use_ssl);
331 sslContext = peer->sslContext;
a7ad6e4e 332 } else {
62e76326 333 sslContext = Config.ssl_client.sslContext;
a7ad6e4e 334 }
62e76326 335
a7ad6e4e 336 assert(sslContext);
62e76326 337
a7ad6e4e 338 if ((ssl = SSL_new(sslContext)) == NULL) {
62e76326 339 ErrorState *err;
340 debug(83, 1) ("fwdInitiateSSL: Error allocating handle: %s\n",
341 ERR_error_string(ERR_get_error(), NULL));
342 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
343 err->xerrno = errno;
344 err->request = requestLink(fwdState->request);
345 fwdFail(fwdState, err);
346 fwdStateFree(fwdState);
347 return;
a7ad6e4e 348 }
62e76326 349
a7ad6e4e 350 SSL_set_fd(ssl, fd);
62e76326 351
a7ad6e4e 352 if (peer) {
62e76326 353 if (peer->ssldomain)
354 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain);
355
a7ad6e4e 356#if NOT_YET
62e76326 357
358 else if (peer->name)
359 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name);
360
a7ad6e4e 361#endif
62e76326 362
363 else
364 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host);
a7ad6e4e 365 } else {
62e76326 366 SSL_set_ex_data(ssl, ssl_ex_index_server, fwdState->request->host);
a7ad6e4e 367 }
62e76326 368
a7ad6e4e 369 fd_table[fd].ssl = ssl;
370 fd_table[fd].read_method = &ssl_read_method;
371 fd_table[fd].write_method = &ssl_write_method;
372 fwdNegotiateSSL(fd, fwdState);
373}
62e76326 374
a7ad6e4e 375#endif
376
41462d93 377static void
f3400a93 378fwdConnectDone(int server_fd, comm_err_t status, int xerrno, void *data)
41462d93 379{
e6ccf245 380 FwdState *fwdState = (FwdState *)data;
db1cd23c 381 FwdServer *fs = fwdState->servers;
41462d93 382 ErrorState *err;
190154cf 383 HttpRequest *request = fwdState->request;
6801f8a8 384 assert(fwdState->server_fd == server_fd);
62e76326 385
41462d93 386 if (status == COMM_ERR_DNS) {
62e76326 387 /*
388 * Only set the dont_retry flag if the DNS lookup fails on
389 * a direct connection. If DNS lookup fails when trying
390 * a neighbor cache, we may want to retry another option.
391 */
392
393 if (NULL == fs->_peer)
394 fwdState->flags.dont_retry = 1;
395
396 debug(17, 4) ("fwdConnectDone: Unknown host: %s\n",
397 request->host);
398
399 err = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE);
400
401 err->dnsserver_msg = xstrdup(dns_error_message);
402
403 err->request = requestLink(request);
404
405 fwdFail(fwdState, err);
406
407 if (fs->_peer)
408 fs->_peer->stats.conn_open--;
409
410 comm_close(server_fd);
41462d93 411 } else if (status != COMM_OK) {
62e76326 412 assert(fs);
413 err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
f3400a93 414 err->xerrno = xerrno;
62e76326 415
416 if (fs->_peer) {
417 err->host = xstrdup(fs->_peer->host);
418 err->port = fs->_peer->http_port;
419 } else {
420 err->host = xstrdup(request->host);
421 err->port = request->port;
422 }
423
424 err->request = requestLink(request);
425 fwdFail(fwdState, err);
426
427 if (fs->_peer) {
428 peerConnectFailed(fs->_peer);
429 fs->_peer->stats.conn_open--;
430 }
431
432 comm_close(server_fd);
41462d93 433 } else {
62e76326 434 debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(fwdState->entry));
435
436 if (fs->_peer)
437 peerConnectSucceded(fs->_peer);
438
a7ad6e4e 439#if USE_SSL
62e76326 440
441 if ((fs->_peer && fs->_peer->use_ssl) ||
442 (!fs->_peer && request->protocol == PROTO_HTTPS)) {
443 fwdInitiateSSL(fwdState);
444 return;
445 }
446
a7ad6e4e 447#endif
62e76326 448 fwdDispatch(fwdState);
41462d93 449 }
41462d93 450}
451
452static void
453fwdConnectTimeout(int fd, void *data)
454{
e6ccf245 455 FwdState *fwdState = (FwdState *)data;
41462d93 456 StoreEntry *entry = fwdState->entry;
457 ErrorState *err;
c7f9eb6d 458 peer *p = fwdStateServerPeer(fwdState);
ec250dfd 459 debug(17, 2) ("fwdConnectTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
6801f8a8 460 assert(fd == fwdState->server_fd);
62e76326 461
528b2c61 462 if (entry->isEmpty()) {
62e76326 463 err = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT);
464 err->request = requestLink(fwdState->request);
465 err->xerrno = ETIMEDOUT;
466 fwdFail(fwdState, err);
467 /*
468 * This marks the peer DOWN ...
469 */
470
471 if (fwdState->servers)
472 if (fwdState->servers->_peer)
473 peerConnectFailed(fwdState->servers->_peer);
41462d93 474 }
62e76326 475
c7f9eb6d 476 if (p)
62e76326 477 p->stats.conn_open--;
478
41462d93 479 comm_close(fd);
480}
481
d6827718 482static struct in_addr
62e76326 483 aclMapAddr(acl_address * head, ACLChecklist * ch)
d6827718 484{
485 acl_address *l;
62e76326 486
d6827718 487 struct in_addr addr;
62e76326 488
489 for (l = head; l; l = l->next)
490 {
7684c4b1 491 if (ch->matchAclListFast(l->aclList))
62e76326 492 return l->addr;
d6827718 493 }
62e76326 494
d6827718 495 addr.s_addr = INADDR_ANY;
496 return addr;
497}
498
499static int
4fb35c3c 500aclMapTOS(acl_tos * head, ACLChecklist * ch)
d6827718 501{
502 acl_tos *l;
62e76326 503
d6827718 504 for (l = head; l; l = l->next) {
7684c4b1 505 if (ch->matchAclListFast(l->aclList))
62e76326 506 return l->tos;
d6827718 507 }
62e76326 508
d6827718 509 return 0;
510}
511
512struct in_addr
190154cf 513 getOutgoingAddr(HttpRequest * request)
d6827718 514{
4fb35c3c 515 ACLChecklist ch;
62e76326 516
517 if (request)
518 {
519 ch.src_addr = request->client_addr;
520 ch.my_addr = request->my_addr;
521 ch.my_port = request->my_port;
522 ch.request = requestLink(request);
d6827718 523 }
62e76326 524
d6827718 525 return aclMapAddr(Config.accessList.outgoing_address, &ch);
526}
527
528unsigned long
190154cf 529getOutgoingTOS(HttpRequest * request)
d6827718 530{
4fb35c3c 531 ACLChecklist ch;
62e76326 532
d6827718 533 if (request) {
62e76326 534 ch.src_addr = request->client_addr;
535 ch.my_addr = request->my_addr;
536 ch.my_port = request->my_port;
537 ch.request = requestLink(request);
d6827718 538 }
62e76326 539
d6827718 540 return aclMapTOS(Config.accessList.outgoing_tos, &ch);
541}
542
41462d93 543static void
ee08bdf5 544fwdConnectStart(void *data)
41462d93 545{
e6ccf245 546 FwdState *fwdState = (FwdState *)data;
41462d93 547 const char *url = storeUrl(fwdState->entry);
cb928909 548 int fd = -1;
41462d93 549 ErrorState *err;
db1cd23c 550 FwdServer *fs = fwdState->servers;
551 const char *host;
552 unsigned short port;
bd0723ad 553 const char *domain = NULL;
3f62decd 554 time_t ctimeout;
62e76326 555
d6827718 556 struct in_addr outgoing;
557 unsigned short tos;
db1cd23c 558 assert(fs);
cddc721b 559 assert(fwdState->server_fd == -1);
41462d93 560 debug(17, 3) ("fwdConnectStart: %s\n", url);
62e76326 561
29b8d8d6 562 if (fs->_peer) {
bd0723ad 563 host = fs->_peer->name;
62e76326 564 port = fs->_peer->http_port;
565 ctimeout = fs->_peer->connect_timeout > 0 ? fs->_peer->connect_timeout
566 : Config.Timeout.peer_connect;
bd0723ad 567
1fdcd380 568 if (fs->_peer->options.originserver) {
bd0723ad 569 domain = fwdState->request->host;
1fdcd380 570 port = fwdState->request->port;
571 }
db1cd23c 572 } else {
62e76326 573 host = fwdState->request->host;
574 port = fwdState->request->port;
575 ctimeout = Config.Timeout.connect;
db1cd23c 576 }
62e76326 577
cb928909 578 if (fwdCheckRetriable(fwdState)) {
bd0723ad 579 if ((fd = pconnPop(host, port, domain)) >= 0) {
cb928909 580 debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd);
581 fwdState->server_fd = fd;
582 fwdState->n_tries++;
583 comm_add_close_handler(fd, fwdServerClosed, fwdState);
584 fwdDispatch(fwdState);
585 return;
586 }
41462d93 587 }
62e76326 588
bc87dc25 589#if URL_CHECKSUM_DEBUG
528b2c61 590 fwdState->entry->mem_obj->checkUrlChecksum();
62e76326 591
bc87dc25 592#endif
62e76326 593
d6827718 594 outgoing = getOutgoingAddr(fwdState->request);
62e76326 595
d6827718 596 tos = getOutgoingTOS(fwdState->request);
597
598 debug(17, 3) ("fwdConnectStart: got addr %s, tos %d\n",
62e76326 599 inet_ntoa(outgoing), tos);
600
d6827718 601 fd = comm_openex(SOCK_STREAM,
bdb741f4 602 IPPROTO_TCP,
62e76326 603 outgoing,
604 0,
605 COMM_NONBLOCKING,
606 tos,
607 url);
608
41462d93 609 if (fd < 0) {
62e76326 610 debug(50, 4) ("fwdConnectStart: %s\n", xstrerror());
611 err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR);
612 err->xerrno = errno;
613 err->request = requestLink(fwdState->request);
614 fwdFail(fwdState, err);
615 fwdStateFree(fwdState);
616 return;
41462d93 617 }
62e76326 618
6801f8a8 619 fwdState->server_fd = fd;
68bd6892 620 fwdState->n_tries++;
c7f9eb6d 621 /*
622 * stats.conn_open is used to account for the number of
623 * connections that we have open to the peer, so we can limit
624 * based on the max-conn option. We need to increment here,
625 * even if the connection may fail.
626 */
62e76326 627
29b8d8d6 628 if (fs->_peer)
62e76326 629 fs->_peer->stats.conn_open++;
630
910169e5 631 comm_add_close_handler(fd, fwdServerClosed, fwdState);
62e76326 632
41462d93 633 commSetTimeout(fd,
62e76326 634 ctimeout,
635 fwdConnectTimeout,
636 fwdState);
637
db1cd23c 638 commConnectStart(fd, host, port, fwdConnectDone, fwdState);
41462d93 639}
640
641static void
db1cd23c 642fwdStartComplete(FwdServer * servers, void *data)
41462d93 643{
e6ccf245 644 FwdState *fwdState = (FwdState *)data;
8a4f6dd6 645 debug(17, 3) ("fwdStartComplete: %s\n", storeUrl(fwdState->entry));
62e76326 646
db1cd23c 647 if (servers != NULL) {
62e76326 648 fwdState->servers = servers;
649 fwdConnectStart(fwdState);
41462d93 650 } else {
62e76326 651 fwdStartFail(fwdState);
41462d93 652 }
41462d93 653}
654
655static void
db1cd23c 656fwdStartFail(FwdState * fwdState)
41462d93 657{
41462d93 658 ErrorState *err;
8a4f6dd6 659 debug(17, 3) ("fwdStartFail: %s\n", storeUrl(fwdState->entry));
41462d93 660 err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE);
661 err->request = requestLink(fwdState->request);
ec250dfd 662 err->xerrno = errno;
663 fwdFail(fwdState, err);
910169e5 664 fwdStateFree(fwdState);
41462d93 665}
910169e5 666
41462d93 667static void
6801f8a8 668fwdDispatch(FwdState * fwdState)
41462d93 669{
c7f9eb6d 670 peer *p = NULL;
190154cf 671 HttpRequest *request = fwdState->request;
41462d93 672 StoreEntry *entry = fwdState->entry;
1f38f50a 673 ErrorState *err;
a7ad6e4e 674 FwdServer *fs = fwdState->servers;
675 int server_fd = fwdState->server_fd;
b746bd8a 676 debug(17, 3) ("fwdDispatch: FD %d: Fetching '%s %s'\n",
62e76326 677 fwdState->client_fd,
678 RequestMethodStr[request->method],
679 storeUrl(entry));
e0ebe27c 680 /*
681 * Assert that server_fd is set. This is to guarantee that fwdState
682 * is attached to something and will be deallocated when server_fd
683 * is closed.
684 */
a7ad6e4e 685 assert(server_fd > -1);
62e76326 686
a7ad6e4e 687 if (fs->_peer)
62e76326 688 hierarchyNote(&fwdState->request->hier, fs->code, fs->_peer->host);
a7ad6e4e 689 else if (Config.onoff.log_ip_on_direct)
62e76326 690 hierarchyNote(&fwdState->request->hier, fs->code, fd_table[server_fd].ipaddr);
a7ad6e4e 691 else
62e76326 692 hierarchyNote(&fwdState->request->hier, fs->code, request->host);
693
a7ad6e4e 694 fd_note(server_fd, storeUrl(fwdState->entry));
62e76326 695
a7ad6e4e 696 fd_table[server_fd].uses++;
62e76326 697
a7ad6e4e 698 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
699 assert(entry->ping_status != PING_WAITING);
62e76326 700
a7ad6e4e 701 assert(entry->lock_count);
62e76326 702
a7ad6e4e 703 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 704
a7ad6e4e 705 netdbPingSite(request->host);
62e76326 706
29b8d8d6 707 if (fwdState->servers && (p = fwdState->servers->_peer)) {
62e76326 708 p->stats.fetches++;
709 fwdState->request->peer_login = p->login;
710 fwdState->request->peer_domain = p->domain;
711 httpStart(fwdState);
41462d93 712 } else {
62e76326 713 fwdState->request->peer_login = NULL;
714 fwdState->request->peer_domain = NULL;
715
716 switch (request->protocol) {
a7ad6e4e 717#if USE_SSL
62e76326 718
719 case PROTO_HTTPS:
720 httpStart(fwdState);
721 break;
a7ad6e4e 722#endif
62e76326 723
724 case PROTO_HTTP:
725 httpStart(fwdState);
726 break;
727
728 case PROTO_GOPHER:
729 gopherStart(fwdState);
730 break;
731
732 case PROTO_FTP:
733 ftpStart(fwdState);
734 break;
735
736 case PROTO_WAIS:
737 waisStart(fwdState);
738 break;
739
740 case PROTO_CACHEOBJ:
741
742 case PROTO_INTERNAL:
743
744 case PROTO_URN:
745 fatal_dump("Should never get here");
746 break;
747
748 case PROTO_WHOIS:
749 whoisStart(fwdState);
750 break;
751
752 default:
753 debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n",
754 storeUrl(entry));
755 err = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST);
756 err->request = requestLink(request);
757 fwdFail(fwdState, err);
758 /*
759 * Force a persistent connection to be closed because
760 * some Netscape browsers have a bug that sends CONNECT
761 * requests as GET's over persistent connections.
762 */
763 request->flags.proxy_keepalive = 0;
764 /*
765 * Set the dont_retry flag becuase this is not a
766 * transient (network) error; its a bug.
767 */
768 fwdState->flags.dont_retry = 1;
769 /*
770 * this assertion exists because if we are connected to
771 * a peer, then we need to decrement p->stats.conn_open.
772 */
773 assert(NULL == p);
774 comm_close(fwdState->server_fd);
775 break;
776 }
41462d93 777 }
778}
779
db1cd23c 780static int
781fwdReforward(FwdState * fwdState)
782{
783 StoreEntry *e = fwdState->entry;
784 FwdServer *fs = fwdState->servers;
785 http_status s;
786 assert(e->store_status == STORE_PENDING);
787 assert(e->mem_obj);
bc87dc25 788#if URL_CHECKSUM_DEBUG
62e76326 789
528b2c61 790 e->mem_obj->checkUrlChecksum();
bc87dc25 791#endif
62e76326 792
b746bd8a 793 debug(17, 3) ("fwdReforward: %s?\n", storeUrl(e));
62e76326 794
d6eb18d6 795 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
62e76326 796 debug(17, 3) ("fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set\n");
797 return 0;
d6eb18d6 798 }
62e76326 799
db1cd23c 800 if (fwdState->n_tries > 9)
62e76326 801 return 0;
802
94439e4e 803 if (fwdState->request->flags.body_sent)
62e76326 804 return 0;
805
db1cd23c 806 assert(fs);
62e76326 807
db1cd23c 808 fwdState->servers = fs->next;
62e76326 809
db1cd23c 810 fwdServerFree(fs);
62e76326 811
db1cd23c 812 if (fwdState->servers == NULL) {
62e76326 813 debug(17, 3) ("fwdReforward: No forward-servers left\n");
814 return 0;
db1cd23c 815 }
62e76326 816
528b2c61 817 s = e->getReply()->sline.status;
db1cd23c 818 debug(17, 3) ("fwdReforward: status %d\n", (int) s);
b6a2f15e 819 return fwdReforwardableStatus(s);
db1cd23c 820}
821
41462d93 822/* PUBLIC FUNCTIONS */
823
64d8034e 824void
825fwdServersFree(FwdServer ** FS)
826{
827 FwdServer *fs;
62e76326 828
64d8034e 829 while ((fs = *FS)) {
62e76326 830 *FS = fs->next;
831 fwdServerFree(fs);
64d8034e 832 }
833}
834
41462d93 835void
190154cf 836fwdStart(int fd, StoreEntry * e, HttpRequest * r)
41462d93 837{
838 FwdState *fwdState;
d1061d2b 839 int answer;
840 ErrorState *err;
5843eb62 841 /*
db1cd23c 842 * client_addr == no_addr indicates this is an "internal" request
5843eb62 843 * from peer_digest.c, asn.c, netdb.c, etc and should always
844 * be allowed. yuck, I know.
d1061d2b 845 */
62e76326 846
7e3ce7b9 847 if (r->client_addr.s_addr != no_addr.s_addr) {
62e76326 848 /*
849 * Check if this host is allowed to fetch MISSES from us (miss_access)
850 */
851 ACLChecklist ch;
852 ch.src_addr = r->client_addr;
853 ch.my_addr = r->my_addr;
854 ch.my_port = r->my_port;
855 ch.request = requestLink(r);
856 answer = aclCheckFast(Config.accessList.miss, &ch);
857
858 if (answer == 0) {
859 err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN);
860 err->request = requestLink(r);
861 err->src_addr = r->client_addr;
862 errorAppendEntry(e, err);
863 return;
864 }
d1061d2b 865 }
62e76326 866
7197b20d 867 debug(17, 3) ("fwdStart: '%s'\n", storeUrl(e));
868 e->mem_obj->request = requestLink(r);
bc87dc25 869#if URL_CHECKSUM_DEBUG
62e76326 870
528b2c61 871 e->mem_obj->checkUrlChecksum();
bc87dc25 872#endif
62e76326 873
05b744f3 874 if (shutting_down) {
62e76326 875 /* more yuck */
876 err = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE);
877 err->request = requestLink(r);
878 errorAppendEntry(e, err);
879 return;
05b744f3 880 }
62e76326 881
d1061d2b 882 switch (r->protocol) {
62e76326 883 /*
884 * Note, don't create fwdState for these requests
885 */
886
e0ebe27c 887 case PROTO_INTERNAL:
62e76326 888 internalStart(r, e);
889 return;
890
e0ebe27c 891 case PROTO_CACHEOBJ:
62e76326 892 cachemgrStart(fd, r, e);
893 return;
894
ae0b4725 895 case PROTO_URN:
62e76326 896 urnStart(r, e);
897 return;
898
e0ebe27c 899 default:
62e76326 900 break;
e0ebe27c 901 }
62e76326 902
72711e31 903 fwdState = cbdataAlloc(FwdState);
d1061d2b 904 fwdState->entry = e;
910169e5 905 fwdState->client_fd = fd;
6801f8a8 906 fwdState->server_fd = -1;
d1061d2b 907 fwdState->request = requestLink(r);
f563eea9 908 fwdState->start = squid_curtime;
d1061d2b 909 storeLockObject(e);
ec250dfd 910 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
7197b20d 911 storeRegisterAbort(e, fwdAbort, fwdState);
db1cd23c 912 peerSelect(r, e, fwdStartComplete, fwdState);
41462d93 913}
914
910169e5 915void
b8890359 916fwdFail(FwdState * fwdState, ErrorState * errorState)
910169e5 917{
d46a87a8 918 assert(EBIT_TEST(fwdState->entry->flags, ENTRY_FWD_HDR_WAIT));
32b2931b 919 debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n",
62e76326 920 err_type_str[errorState->type],
921 httpStatusString(errorState->httpStatus),
922 storeUrl(fwdState->entry));
923
ec250dfd 924 if (fwdState->err)
62e76326 925 errorStateFree(fwdState->err);
926
ec250dfd 927 fwdState->err = errorState;
910169e5 928}
6801f8a8 929
7197b20d 930/*
931 * Called when someone else calls StoreAbort() on this entry
932 */
9bc73deb 933static void
7197b20d 934fwdAbort(void *data)
935{
e6ccf245 936 FwdState *fwdState = (FwdState *)data;
ec250dfd 937 debug(17, 2) ("fwdAbort: %s\n", storeUrl(fwdState->entry));
7197b20d 938 fwdStateFree(fwdState);
939}
940
0b49cd31 941/*
942 * Frees fwdState without closing FD or generating an abort
943 */
944void
9dbf253d 945fwdUnregister(int fd, FwdState * fwdState)
0b49cd31 946{
9dbf253d 947 debug(17, 3) ("fwdUnregister: %s\n", storeUrl(fwdState->entry));
7f646cdb 948 assert(fd == fwdState->server_fd);
b7fe0ab0 949 assert(fd > -1);
9dbf253d 950 comm_remove_close_handler(fd, fwdServerClosed, fwdState);
951 fwdState->server_fd = -1;
0b49cd31 952}
db1cd23c 953
954/*
955 * server-side modules call fwdComplete() when they are done
956 * downloading an object. Then, we either 1) re-forward the
957 * request somewhere else if needed, or 2) call storeComplete()
958 * to finish it off
959 */
960void
961fwdComplete(FwdState * fwdState)
962{
963 StoreEntry *e = fwdState->entry;
964 assert(e->store_status == STORE_PENDING);
8a28f65f 965 debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e),
62e76326 966 e->getReply()->sline.status);
bc87dc25 967#if URL_CHECKSUM_DEBUG
62e76326 968
528b2c61 969 e->mem_obj->checkUrlChecksum();
bc87dc25 970#endif
62e76326 971
528b2c61 972 fwdLogReplyStatus(fwdState->n_tries, e->getReply()->sline.status);
62e76326 973
d6eb18d6 974 if (fwdReforward(fwdState)) {
62e76326 975 debug(17, 3) ("fwdComplete: re-forwarding %d %s\n",
976 e->getReply()->sline.status,
977 storeUrl(e));
978
979 if (fwdState->server_fd > -1)
980 fwdUnregister(fwdState->server_fd, fwdState);
981
982 storeEntryReset(e);
983
984 fwdStartComplete(fwdState->servers, fwdState);
db1cd23c 985 } else {
62e76326 986 debug(17, 3) ("fwdComplete: not re-forwarding status %d\n",
987 e->getReply()->sline.status);
988 EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT);
989 e->complete();
990 /*
991 * If fwdState isn't associated with a server FD, it
992 * won't get freed unless we do it here.
993 */
994
995 if (fwdState->server_fd < 0)
996 fwdStateFree(fwdState);
db1cd23c 997 }
998}
8ddcc35d 999
1000void
1001fwdInit(void)
1002{
9977e14b 1003 cachemgrRegister("forward",
62e76326 1004 "Request Forwarding Statistics",
1005 fwdStats, 0, 1);
225644d7 1006#if WIP_FWD_LOG
62e76326 1007
225644d7 1008 if (logfile)
62e76326 1009 (void) 0;
225644d7 1010 else if (NULL == Config.Log.forward)
62e76326 1011 (void) 0;
225644d7 1012 else
62e76326 1013 logfile = logfileOpen(Config.Log.forward, 0, 1);
1014
225644d7 1015#endif
8ddcc35d 1016}
1017
1018static void
1019fwdLogReplyStatus(int tries, http_status status)
1020{
1021 if (status > HTTP_INVALID_HEADER)
62e76326 1022 return;
1023
8ddcc35d 1024 assert(tries);
62e76326 1025
8ddcc35d 1026 tries--;
62e76326 1027
8ddcc35d 1028 if (tries > MAX_FWD_STATS_IDX)
62e76326 1029 tries = MAX_FWD_STATS_IDX;
1030
8ddcc35d 1031 FwdReplyCodes[tries][status]++;
1032}
1033
1034static void
9977e14b 1035fwdStats(StoreEntry * s)
8ddcc35d 1036{
9977e14b 1037 int i;
1038 int j;
1039 storeAppendPrintf(s, "Status");
62e76326 1040
9977e14b 1041 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
62e76326 1042 storeAppendPrintf(s, "\ttry#%d", j + 1);
9977e14b 1043 }
62e76326 1044
9977e14b 1045 storeAppendPrintf(s, "\n");
62e76326 1046
9977e14b 1047 for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
62e76326 1048 if (FwdReplyCodes[0][i] == 0)
1049 continue;
1050
1051 storeAppendPrintf(s, "%3d", i);
1052
1053 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1054 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
1055 }
1056
1057 storeAppendPrintf(s, "\n");
9977e14b 1058 }
8ddcc35d 1059}
b6a2f15e 1060
1061int
1062fwdReforwardableStatus(http_status s)
1063{
1064 switch (s) {
62e76326 1065
b6a2f15e 1066 case HTTP_FORBIDDEN:
62e76326 1067
b6a2f15e 1068 case HTTP_INTERNAL_SERVER_ERROR:
62e76326 1069
b6a2f15e 1070 case HTTP_NOT_IMPLEMENTED:
62e76326 1071
b6a2f15e 1072 case HTTP_BAD_GATEWAY:
62e76326 1073
b6a2f15e 1074 case HTTP_SERVICE_UNAVAILABLE:
62e76326 1075
b6a2f15e 1076 case HTTP_GATEWAY_TIMEOUT:
62e76326 1077 return 1;
1078
b6a2f15e 1079 default:
62e76326 1080 return 0;
b6a2f15e 1081 }
62e76326 1082
b6a2f15e 1083 /* NOTREACHED */
1084}
225644d7 1085
1086#if WIP_FWD_LOG
1087void
1088fwdUninit(void)
1089{
dab0cec3 1090 if (NULL == logfile)
62e76326 1091 return;
1092
225644d7 1093 logfileClose(logfile);
62e76326 1094
225644d7 1095 logfile = NULL;
1096}
1097
1098void
1099fwdLogRotate(void)
1100{
1101 if (logfile)
62e76326 1102 logfileRotate(logfile);
225644d7 1103}
1104
1105static void
1106fwdLog(FwdState * fwdState)
1107{
1108 if (NULL == logfile)
62e76326 1109 return;
1110
225644d7 1111 logfilePrintf(logfile, "%9d.%03d %03d %s %s\n",
62e76326 1112 (int) current_time.tv_sec,
1113 (int) current_time.tv_usec / 1000,
1114 fwdState->last_status,
1115 RequestMethodStr[fwdState->request->method],
1116 fwdState->request->canonical);
225644d7 1117}
1118
1119void
1120fwdStatus(FwdState * fwdState, http_status s)
1121{
1122 fwdState->last_status = s;
1123}
1124
1125#endif