~clint-fewbar/ubuntu/precise/squid3/ignore-sighup-early

« back to all changes in this revision

Viewing changes to src/forward.cc

  • Committer: Bazaar Package Importer
  • Author(s): Luigi Gangitano
  • Date: 2006-11-11 10:32:06 UTC
  • Revision ID: james.westby@ubuntu.com-20061111103206-f3p0r9g0vq44rp3r
Tags: upstream-3.0.PRE5
ImportĀ upstreamĀ versionĀ 3.0.PRE5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/*
 
3
 * $Id: forward.cc,v 1.151 2006/09/13 15:54:21 adrian Exp $
 
4
 *
 
5
 * DEBUG: section 17    Request Forwarding
 
6
 * AUTHOR: Duane Wessels
 
7
 *
 
8
 * SQUID Web Proxy Cache          http://www.squid-cache.org/
 
9
 * ----------------------------------------------------------
 
10
 *
 
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.
 
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
 
32
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 
33
 *
 
34
 */
 
35
 
 
36
 
 
37
#include "squid.h"
 
38
#include "forward.h"
 
39
#include "ACLChecklist.h"
 
40
#include "ACL.h"
 
41
#include "CacheManager.h"
 
42
#include "event.h"
 
43
#include "errorpage.h"
 
44
#include "fde.h"
 
45
#include "HttpReply.h"
 
46
#include "HttpRequest.h"
 
47
#include "MemObject.h"
 
48
#include "pconn.h"
 
49
#include "SquidTime.h"
 
50
#include "Store.h"
 
51
 
 
52
static PSC fwdStartCompleteWrapper;
 
53
static PF fwdServerClosedWrapper;
 
54
#if USE_SSL
 
55
static PF fwdNegotiateSSLWrapper;
 
56
#endif
 
57
static PF fwdConnectTimeoutWrapper;
 
58
static EVH fwdConnectStartWrapper;
 
59
static CNCB fwdConnectDoneWrapper;
 
60
 
 
61
static OBJH fwdStats;
 
62
static void fwdServerFree(FwdServer * fs);
 
63
 
 
64
#define MAX_FWD_STATS_IDX 9
 
65
static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1];
 
66
 
 
67
#if WIP_FWD_LOG
 
68
static void fwdLog(FwdState * fwdState);
 
69
static Logfile *logfile = NULL;
 
70
#endif
 
71
 
 
72
static PconnPool *fwdPconnPool = new PconnPool("server-side");
 
73
CBDATA_CLASS_INIT(FwdState);
 
74
 
 
75
void
 
76
FwdState::abort(void* d)
 
77
{
 
78
    FwdState* fwd = (FwdState*)d;
 
79
 
 
80
    if (fwd->server_fd >= 0) {
 
81
        comm_close(fwd->server_fd);
 
82
        fwd->server_fd = -1;
 
83
    }
 
84
 
 
85
    fwd->self = NULL;
 
86
}
 
87
 
 
88
/**** PUBLIC INTERFACE ********************************************************/
 
89
 
 
90
FwdState::FwdState(int fd, StoreEntry * e, HttpRequest * r)
 
91
{
 
92
    entry = e;
 
93
    client_fd = fd;
 
94
    server_fd = -1;
 
95
    request = HTTPMSGLOCK(r);
 
96
    start_t = squid_curtime;
 
97
 
 
98
    e->lock()
 
99
 
 
100
    ;
 
101
    EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
 
102
 
 
103
    self = this;        // refcounted
 
104
 
 
105
    storeRegisterAbort(e, FwdState::abort, this);
 
106
}
 
107
 
 
108
void
 
109
FwdState::completed()
 
110
{
 
111
        if (flags.forward_completed == 1) {
 
112
                debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!");
 
113
                return;
 
114
        }
 
115
        flags.forward_completed = 1;
 
116
 
 
117
#if URL_CHECKSUM_DEBUG
 
118
 
 
119
    entry->mem_obj->checkUrlChecksum();
 
120
#endif
 
121
#if WIP_FWD_LOG
 
122
 
 
123
    log();
 
124
#endif
 
125
 
 
126
    if (entry->store_status == STORE_PENDING) {
 
127
        if (entry->isEmpty()) {
 
128
            assert(err);
 
129
            errorAppendEntry(entry, err);
 
130
            err = NULL;
 
131
        } else {
 
132
            EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
 
133
            entry->complete();
 
134
            storeReleaseRequest(entry);
 
135
        }
 
136
    }
 
137
 
 
138
    if (storePendingNClients(entry) > 0)
 
139
        assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
 
140
 
 
141
}
 
142
 
 
143
FwdState::~FwdState()
 
144
{
 
145
    debugs(17, 3, HERE << "FwdState destructor starting");
 
146
    if (! flags.forward_completed)
 
147
            completed();
 
148
 
 
149
    serversFree(&servers);
 
150
 
 
151
    HTTPMSGUNLOCK(request);
 
152
 
 
153
    if (err)
 
154
        errorStateFree(err);
 
155
 
 
156
    storeUnregisterAbort(entry);
 
157
 
 
158
    entry->unlock();
 
159
 
 
160
    entry = NULL;
 
161
 
 
162
    int fd = server_fd;
 
163
 
 
164
    if (fd > -1) {
 
165
        server_fd = -1;
 
166
        comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
 
167
        debug(17, 3) ("fwdStateFree: closing FD %d\n", fd);
 
168
        comm_close(fd);
 
169
    }
 
170
    debugs(17, 3, HERE << "FwdState destructor done");
 
171
}
 
172
 
 
173
/*
 
174
 * This is the entry point for client-side to start forwarding
 
175
 * a transaction.  It is a static method that may or may not
 
176
 * allocate a FwdState.
 
177
 */
 
178
void
 
179
FwdState::fwdStart(int client_fd, StoreEntry *entry, HttpRequest *request)
 
180
{
 
181
    /*
 
182
     * client_addr == no_addr indicates this is an "internal" request
 
183
     * from peer_digest.c, asn.c, netdb.c, etc and should always
 
184
     * be allowed.  yuck, I know.
 
185
     */
 
186
 
 
187
    if (request->client_addr.s_addr != no_addr.s_addr && request->protocol != PROTO_INTERNAL && request->protocol != PROTO_CACHEOBJ) {
 
188
        /*
 
189
         * Check if this host is allowed to fetch MISSES from us (miss_access)
 
190
         */
 
191
        ACLChecklist ch;
 
192
        ch.src_addr = request->client_addr;
 
193
        ch.my_addr = request->my_addr;
 
194
        ch.my_port = request->my_port;
 
195
        ch.request = HTTPMSGLOCK(request);
 
196
        ch.accessList = cbdataReference(Config.accessList.miss);
 
197
        /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
 
198
        int answer = ch.fastCheck();
 
199
 
 
200
        if (answer == 0) {
 
201
            err_type page_id;
 
202
            page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
 
203
 
 
204
            if (page_id == ERR_NONE)
 
205
                page_id = ERR_FORWARDING_DENIED;
 
206
 
 
207
            ErrorState *anErr = errorCon(page_id, HTTP_FORBIDDEN, request);
 
208
 
 
209
            errorAppendEntry(entry, anErr);     // frees anErr
 
210
 
 
211
            return;
 
212
        }
 
213
    }
 
214
 
 
215
    debug(17, 3) ("FwdState::start() '%s'\n", storeUrl(entry));
 
216
    /*
 
217
     * This seems like an odd place to bind mem_obj and request.
 
218
     * Might want to assert that request is NULL at this point
 
219
     */
 
220
    entry->mem_obj->request = HTTPMSGLOCK(request);
 
221
#if URL_CHECKSUM_DEBUG
 
222
 
 
223
    entry->mem_obj->checkUrlChecksum();
 
224
#endif
 
225
 
 
226
    if (shutting_down) {
 
227
        /* more yuck */
 
228
        ErrorState *anErr = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
 
229
        errorAppendEntry(entry, anErr); // frees anErr
 
230
        return;
 
231
    }
 
232
 
 
233
    switch (request->protocol) {
 
234
 
 
235
    case PROTO_INTERNAL:
 
236
        internalStart(request, entry);
 
237
        return;
 
238
 
 
239
    case PROTO_CACHEOBJ:
 
240
        cachemgrStart(client_fd, request, entry);
 
241
        return;
 
242
 
 
243
    case PROTO_URN:
 
244
        urnStart(request, entry);
 
245
        return;
 
246
 
 
247
    default:
 
248
        FwdState *fwd = new FwdState(client_fd, entry, request);
 
249
        fwd->flags.forward_completed = 0;
 
250
        peerSelect(request, entry, fwdStartCompleteWrapper, fwd);
 
251
        return;
 
252
    }
 
253
 
 
254
    /* NOTREACHED */
 
255
}
 
256
 
 
257
void
 
258
FwdState::fail(ErrorState * errorState)
 
259
{
 
260
    debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n",
 
261
                  err_type_str[errorState->type],
 
262
                  httpStatusString(errorState->httpStatus),
 
263
                  storeUrl(entry));
 
264
 
 
265
    if (err)
 
266
        errorStateFree(err);
 
267
 
 
268
    err = errorState;
 
269
 
 
270
    if (!errorState->request)
 
271
        errorState->request = HTTPMSGLOCK(request);
 
272
}
 
273
 
 
274
/*
 
275
 * Frees fwdState without closing FD or generating an abort
 
276
 */
 
277
void
 
278
FwdState::unregister(int fd)
 
279
{
 
280
    debug(17, 3) ("fwdUnregister: %s\n", storeUrl(entry));
 
281
    assert(fd == server_fd);
 
282
    assert(fd > -1);
 
283
    comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
 
284
    server_fd = -1;
 
285
}
 
286
 
 
287
/*
 
288
 * server-side modules call fwdComplete() when they are done
 
289
 * downloading an object.  Then, we either 1) re-forward the
 
290
 * request somewhere else if needed, or 2) call storeComplete()
 
291
 * to finish it off
 
292
 */
 
293
void
 
294
FwdState::complete()
 
295
{
 
296
    StoreEntry *e = entry;
 
297
    assert(entry->store_status == STORE_PENDING);
 
298
    debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e),
 
299
                  entry->getReply()->sline.status);
 
300
#if URL_CHECKSUM_DEBUG
 
301
 
 
302
    entry->mem_obj->checkUrlChecksum();
 
303
#endif
 
304
 
 
305
    logReplyStatus(n_tries, entry->getReply()->sline.status);
 
306
 
 
307
    if (reforward()) {
 
308
        debug(17, 3) ("fwdComplete: re-forwarding %d %s\n",
 
309
                      entry->getReply()->sline.status,
 
310
                      storeUrl(e));
 
311
 
 
312
        if (server_fd > -1)
 
313
            unregister(server_fd);
 
314
 
 
315
        storeEntryReset(e);
 
316
 
 
317
        /*
 
318
         * Need to re-establish the self-reference here since we'll
 
319
         * be trying to forward the request again.  Otherwise the
 
320
         * ref count could go to zero before a connection is
 
321
         * established.
 
322
         */
 
323
        self = this;    // refcounted
 
324
 
 
325
        startComplete(servers);
 
326
    } else {
 
327
        debug(17, 3) ("fwdComplete: not re-forwarding status %d\n",
 
328
                      entry->getReply()->sline.status);
 
329
        EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
 
330
        entry->complete();
 
331
        if (server_fd < 0)
 
332
                completed();
 
333
    }
 
334
}
 
335
 
 
336
 
 
337
/**** CALLBACK WRAPPERS ************************************************************/
 
338
 
 
339
static void
 
340
fwdStartCompleteWrapper(FwdServer * servers, void *data)
 
341
{
 
342
    FwdState *fwd = (FwdState *) data;
 
343
    fwd->startComplete(servers);
 
344
}
 
345
 
 
346
static void
 
347
fwdServerClosedWrapper(int fd, void *data)
 
348
{
 
349
    FwdState *fwd = (FwdState *) data;
 
350
    fwd->serverClosed(fd);
 
351
}
 
352
 
 
353
static void
 
354
fwdConnectStartWrapper(void *data)
 
355
{
 
356
    FwdState *fwd = (FwdState *) data;
 
357
    fwd->connectStart();
 
358
}
 
359
 
 
360
#if USE_SSL
 
361
static void
 
362
fwdNegotiateSSLWrapper(int fd, void *data)
 
363
{
 
364
    FwdState *fwd = (FwdState *) data;
 
365
    fwd->negotiateSSL(fd);
 
366
}
 
367
 
 
368
#endif
 
369
 
 
370
static void
 
371
fwdConnectDoneWrapper(int server_fd, comm_err_t status, int xerrno, void *data)
 
372
{
 
373
    FwdState *fwd = (FwdState *) data;
 
374
    fwd->connectDone(server_fd, status, xerrno);
 
375
}
 
376
 
 
377
static void
 
378
fwdConnectTimeoutWrapper(int fd, void *data)
 
379
{
 
380
    FwdState *fwd = (FwdState *) data;
 
381
    fwd->connectTimeout(fd);
 
382
}
 
383
 
 
384
/*
 
385
 * Accounts for closed persistent connections
 
386
 */
 
387
static void
 
388
fwdPeerClosed(int fd, void *data)
 
389
{
 
390
    peer *p = (peer *)data;
 
391
    p->stats.conn_open--;
 
392
}
 
393
 
 
394
/**** PRIVATE *****************************************************************/
 
395
 
 
396
bool
 
397
FwdState::checkRetry()
 
398
{
 
399
    if (shutting_down)
 
400
        return false;
 
401
 
 
402
    if (entry->store_status != STORE_PENDING)
 
403
        return false;
 
404
 
 
405
    if (!entry->isEmpty())
 
406
        return false;
 
407
 
 
408
    if (n_tries > 10)
 
409
        return false;
 
410
 
 
411
    if (origin_tries > 2)
 
412
        return false;
 
413
 
 
414
    if (squid_curtime - start_t > Config.Timeout.forward)
 
415
        return false;
 
416
 
 
417
    if (flags.dont_retry)
 
418
        return false;
 
419
 
 
420
    if (request->flags.body_sent)
 
421
        return false;
 
422
 
 
423
    return true;
 
424
}
 
425
 
 
426
bool
 
427
FwdState::checkRetriable()
 
428
{
 
429
    /* If there is a request body then Squid can only try once
 
430
     * even if the method is indempotent
 
431
     */
 
432
 
 
433
    if (request->body_reader != NULL)
 
434
        return false;
 
435
 
 
436
    /* RFC2616 9.1 Safe and Idempotent Methods */
 
437
    switch (request->method) {
 
438
        /* 9.1.1 Safe Methods */
 
439
 
 
440
    case METHOD_GET:
 
441
 
 
442
    case METHOD_HEAD:
 
443
        /* 9.1.2 Indepontent Methods */
 
444
 
 
445
    case METHOD_PUT:
 
446
 
 
447
    case METHOD_DELETE:
 
448
 
 
449
    case METHOD_OPTIONS:
 
450
 
 
451
    case METHOD_TRACE:
 
452
        break;
 
453
 
 
454
    default:
 
455
        return false;
 
456
    }
 
457
 
 
458
    return true;
 
459
}
 
460
 
 
461
void
 
462
FwdState::serverClosed(int fd)
 
463
{
 
464
    debug(17, 2) ("fwdServerClosed: FD %d %s\n", fd, storeUrl(entry));
 
465
    assert(server_fd == fd);
 
466
    server_fd = -1;
 
467
 
 
468
    if (checkRetry()) {
 
469
        int originserver = (servers->_peer == NULL);
 
470
        debug(17, 3) ("fwdServerClosed: re-forwarding (%d tries, %d secs)\n",
 
471
                      n_tries,
 
472
                      (int) (squid_curtime - start_t));
 
473
 
 
474
        if (servers->next) {
 
475
            /* use next, or cycle if origin server isn't last */
 
476
            FwdServer *fs = servers;
 
477
            FwdServer **T, *T2 = NULL;
 
478
            servers = fs->next;
 
479
 
 
480
            for (T = &servers; *T; T2 = *T, T = &(*T)->next)
 
481
 
 
482
                ;
 
483
            if (T2 && T2->_peer) {
 
484
                /* cycle */
 
485
                *T = fs;
 
486
                fs->next = NULL;
 
487
            } else {
 
488
                /* Use next. The last "direct" entry is retried multiple times */
 
489
                servers = fs->next;
 
490
                fwdServerFree(fs);
 
491
                originserver = 0;
 
492
            }
 
493
        }
 
494
 
 
495
        /* use eventAdd to break potential call sequence loops and to slow things down a little */
 
496
        eventAdd("fwdConnectStart", fwdConnectStartWrapper, this, originserver ? 0.05 : 0.005, 0);
 
497
 
 
498
        return;
 
499
    }
 
500
 
 
501
    if (!err && shutting_down) {
 
502
        errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
 
503
    }
 
504
 
 
505
    self = NULL;        // refcounted
 
506
}
 
507
 
 
508
#if USE_SSL
 
509
void
 
510
FwdState::negotiateSSL(int fd)
 
511
{
 
512
    FwdServer *fs = servers;
 
513
    SSL *ssl = fd_table[fd].ssl;
 
514
    int ret;
 
515
 
 
516
    if ((ret = SSL_connect(ssl)) <= 0) {
 
517
        int ssl_error = SSL_get_error(ssl, ret);
 
518
 
 
519
        switch (ssl_error) {
 
520
 
 
521
        case SSL_ERROR_WANT_READ:
 
522
            commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0);
 
523
            return;
 
524
 
 
525
        case SSL_ERROR_WANT_WRITE:
 
526
            commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
 
527
            return;
 
528
 
 
529
        default:
 
530
            debug(81, 1) ("fwdNegotiateSSL: Error negotiating SSL connection on FD %d: %s (%d/%d/%d)\n", fd, ERR_error_string(ERR_get_error(), NULL), ssl_error, ret, errno);
 
531
            ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
 
532
#ifdef EPROTO
 
533
 
 
534
            anErr->xerrno = EPROTO;
 
535
#else
 
536
 
 
537
            anErr->xerrno = EACCES;
 
538
#endif
 
539
 
 
540
            fail(anErr);
 
541
 
 
542
            if (fs->_peer) {
 
543
                peerConnectFailed(fs->_peer);
 
544
                fs->_peer->stats.conn_open--;
 
545
            }
 
546
 
 
547
            comm_close(fd);
 
548
            return;
 
549
        }
 
550
    }
 
551
 
 
552
    if (fs->_peer && !SSL_session_reused(ssl)) {
 
553
        if (fs->_peer->sslSession)
 
554
            SSL_SESSION_free(fs->_peer->sslSession);
 
555
 
 
556
        fs->_peer->sslSession = SSL_get1_session(ssl);
 
557
    }
 
558
 
 
559
    dispatch();
 
560
}
 
561
 
 
562
void
 
563
FwdState::initiateSSL()
 
564
{
 
565
    FwdServer *fs = servers;
 
566
    int fd = server_fd;
 
567
    SSL *ssl;
 
568
    SSL_CTX *sslContext = NULL;
 
569
    peer *peer = fs->_peer;
 
570
 
 
571
    if (peer) {
 
572
        assert(peer->use_ssl);
 
573
        sslContext = peer->sslContext;
 
574
    } else {
 
575
        sslContext = Config.ssl_client.sslContext;
 
576
    }
 
577
 
 
578
    assert(sslContext);
 
579
 
 
580
    if ((ssl = SSL_new(sslContext)) == NULL) {
 
581
        debug(83, 1) ("fwdInitiateSSL: Error allocating handle: %s\n",
 
582
                      ERR_error_string(ERR_get_error(), NULL));
 
583
        ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
 
584
        anErr->xerrno = errno;
 
585
        fail(anErr);
 
586
        self = NULL;            // refcounted
 
587
        return;
 
588
    }
 
589
 
 
590
    SSL_set_fd(ssl, fd);
 
591
 
 
592
    if (peer) {
 
593
        if (peer->ssldomain)
 
594
            SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain);
 
595
 
 
596
#if NOT_YET
 
597
 
 
598
        else if (peer->name)
 
599
            SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name);
 
600
 
 
601
#endif
 
602
 
 
603
        else
 
604
            SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host);
 
605
 
 
606
        if (peer->sslSession)
 
607
            SSL_set_session(ssl, peer->sslSession);
 
608
 
 
609
    } else {
 
610
        SSL_set_ex_data(ssl, ssl_ex_index_server, request->host);
 
611
    }
 
612
 
 
613
    fd_table[fd].ssl = ssl;
 
614
    fd_table[fd].read_method = &ssl_read_method;
 
615
    fd_table[fd].write_method = &ssl_write_method;
 
616
    negotiateSSL(fd);
 
617
}
 
618
 
 
619
#endif
 
620
 
 
621
void
 
622
FwdState::connectDone(int aServerFD, comm_err_t status, int xerrno)
 
623
{
 
624
    FwdServer *fs = servers;
 
625
    assert(server_fd == aServerFD);
 
626
 
 
627
    if (Config.onoff.log_ip_on_direct && status != COMM_ERR_DNS && fs->code == HIER_DIRECT)
 
628
        hierarchyNote(&request->hier, fs->code, fd_table[server_fd].ipaddr);
 
629
 
 
630
    if (status == COMM_ERR_DNS) {
 
631
        /*
 
632
         * Only set the dont_retry flag if the DNS lookup fails on
 
633
         * a direct connection.  If DNS lookup fails when trying
 
634
         * a neighbor cache, we may want to retry another option.
 
635
         */
 
636
 
 
637
        if (NULL == fs->_peer)
 
638
            flags.dont_retry = 1;
 
639
 
 
640
        debug(17, 4) ("fwdConnectDone: Unknown host: %s\n",
 
641
                      request->host);
 
642
 
 
643
        ErrorState *anErr = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
 
644
 
 
645
        anErr->dnsserver_msg = xstrdup(dns_error_message);
 
646
 
 
647
        fail(anErr);
 
648
 
 
649
        comm_close(server_fd);
 
650
    } else if (status != COMM_OK) {
 
651
        assert(fs);
 
652
        ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
 
653
        anErr->xerrno = xerrno;
 
654
 
 
655
        fail(anErr);
 
656
 
 
657
        if (fs->_peer)
 
658
            peerConnectFailed(fs->_peer);
 
659
 
 
660
        comm_close(server_fd);
 
661
    } else {
 
662
        debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(entry));
 
663
 
 
664
        if (fs->_peer)
 
665
            peerConnectSucceded(fs->_peer);
 
666
 
 
667
#if USE_SSL
 
668
 
 
669
        if ((fs->_peer && fs->_peer->use_ssl) ||
 
670
                (!fs->_peer && request->protocol == PROTO_HTTPS)) {
 
671
            initiateSSL();
 
672
            return;
 
673
        }
 
674
 
 
675
#endif
 
676
        dispatch();
 
677
    }
 
678
}
 
679
 
 
680
void
 
681
FwdState::connectTimeout(int fd)
 
682
{
 
683
    FwdServer *fs = servers;
 
684
 
 
685
    debug(17, 2) ("fwdConnectTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
 
686
    assert(fd == server_fd);
 
687
 
 
688
    if (Config.onoff.log_ip_on_direct && fs->code == HIER_DIRECT && fd_table[fd].ipaddr[0])
 
689
        hierarchyNote(&request->hier, fs->code, fd_table[fd].ipaddr);
 
690
 
 
691
    if (entry->isEmpty()) {
 
692
        ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT, request);
 
693
        anErr->xerrno = ETIMEDOUT;
 
694
        fail(anErr);
 
695
        /*
 
696
         * This marks the peer DOWN ... 
 
697
         */
 
698
 
 
699
        if (servers)
 
700
            if (servers->_peer)
 
701
                peerConnectFailed(servers->_peer);
 
702
    }
 
703
 
 
704
    comm_close(fd);
 
705
}
 
706
 
 
707
void
 
708
FwdState::connectStart()
 
709
{
 
710
    const char *url = storeUrl(entry);
 
711
    int fd = -1;
 
712
    FwdServer *fs = servers;
 
713
    const char *host;
 
714
    unsigned short port;
 
715
    const char *domain = NULL;
 
716
    int ctimeout;
 
717
    int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
 
718
 
 
719
    struct IN_ADDR outgoing;
 
720
    unsigned short tos;
 
721
    assert(fs);
 
722
    assert(server_fd == -1);
 
723
    debug(17, 3) ("fwdConnectStart: %s\n", url);
 
724
 
 
725
    if (fs->_peer) {
 
726
        host = fs->_peer->host;
 
727
        port = fs->_peer->http_port;
 
728
        ctimeout = fs->_peer->connect_timeout > 0 ? fs->_peer->connect_timeout
 
729
                   : Config.Timeout.peer_connect;
 
730
 
 
731
        if (fs->_peer->options.originserver)
 
732
            domain = request->host;
 
733
    } else {
 
734
        host = request->host;
 
735
        port = request->port;
 
736
        ctimeout = Config.Timeout.connect;
 
737
    }
 
738
 
 
739
    if (ftimeout < 0)
 
740
        ftimeout = 5;
 
741
 
 
742
    if (ftimeout < ctimeout)
 
743
        ctimeout = ftimeout;
 
744
 
 
745
    if ((fd = fwdPconnPool->pop(host, port, domain)) >= 0) {
 
746
        if (checkRetriable()) {
 
747
            debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd);
 
748
            server_fd = fd;
 
749
            n_tries++;
 
750
 
 
751
            if (!fs->_peer)
 
752
                origin_tries++;
 
753
 
 
754
            comm_add_close_handler(fd, fwdServerClosedWrapper, this);
 
755
 
 
756
            dispatch();
 
757
 
 
758
            return;
 
759
        } else {
 
760
            /* Discard the persistent connection to not cause
 
761
             * an imbalance in number of connections open if there
 
762
             * is a lot of POST requests
 
763
             */
 
764
            comm_close(fd);
 
765
        }
 
766
    }
 
767
 
 
768
#if URL_CHECKSUM_DEBUG
 
769
    entry->mem_obj->checkUrlChecksum();
 
770
 
 
771
#endif
 
772
 
 
773
    outgoing = getOutgoingAddr(request);
 
774
 
 
775
    tos = getOutgoingTOS(request);
 
776
 
 
777
    debug(17, 3) ("fwdConnectStart: got addr %s, tos %d\n",
 
778
                  inet_ntoa(outgoing), tos);
 
779
 
 
780
    fd = comm_openex(SOCK_STREAM,
 
781
                     IPPROTO_TCP,
 
782
                     outgoing,
 
783
                     0,
 
784
                     COMM_NONBLOCKING,
 
785
                     tos,
 
786
                     url);
 
787
 
 
788
    if (fd < 0) {
 
789
        debug(50, 4) ("fwdConnectStart: %s\n", xstrerror());
 
790
        ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
 
791
        anErr->xerrno = errno;
 
792
        fail(anErr);
 
793
        self = NULL;    // refcounted
 
794
        return;
 
795
    }
 
796
 
 
797
    server_fd = fd;
 
798
    n_tries++;
 
799
 
 
800
    if (!fs->_peer)
 
801
        origin_tries++;
 
802
 
 
803
    /*
 
804
     * stats.conn_open is used to account for the number of
 
805
     * connections that we have open to the peer, so we can limit
 
806
     * based on the max-conn option.  We need to increment here,
 
807
     * even if the connection may fail.
 
808
     */
 
809
 
 
810
    if (fs->_peer) {
 
811
        fs->_peer->stats.conn_open++;
 
812
        comm_add_close_handler(fd, fwdPeerClosed, fs->_peer);
 
813
    }
 
814
 
 
815
    comm_add_close_handler(fd, fwdServerClosedWrapper, this);
 
816
 
 
817
    commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this);
 
818
 
 
819
    if (fs->_peer)
 
820
        hierarchyNote(&request->hier, fs->code, fs->_peer->host);
 
821
    else
 
822
        hierarchyNote(&request->hier, fs->code, request->host);
 
823
 
 
824
    commConnectStart(fd, host, port, fwdConnectDoneWrapper, this);
 
825
}
 
826
 
 
827
void
 
828
FwdState::startComplete(FwdServer * theServers)
 
829
{
 
830
    debug(17, 3) ("fwdStartComplete: %s\n", storeUrl(entry));
 
831
 
 
832
    if (theServers != NULL) {
 
833
        servers = theServers;
 
834
        connectStart();
 
835
    } else {
 
836
        startFail();
 
837
    }
 
838
}
 
839
 
 
840
void
 
841
FwdState::startFail()
 
842
{
 
843
    debug(17, 3) ("fwdStartFail: %s\n", storeUrl(entry));
 
844
    ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
 
845
    anErr->xerrno = errno;
 
846
    fail(anErr);
 
847
    self = NULL;        // refcounted
 
848
}
 
849
 
 
850
void
 
851
FwdState::dispatch()
 
852
{
 
853
    peer *p = NULL;
 
854
    debug(17, 3) ("fwdDispatch: FD %d: Fetching '%s %s'\n",
 
855
                  client_fd,
 
856
                  RequestMethodStr[request->method],
 
857
                  storeUrl(entry));
 
858
    /*
 
859
     * Assert that server_fd is set.  This is to guarantee that fwdState
 
860
     * is attached to something and will be deallocated when server_fd
 
861
     * is closed.
 
862
     */
 
863
    assert(server_fd > -1);
 
864
 
 
865
    fd_note(server_fd, storeUrl(entry));
 
866
 
 
867
    fd_table[server_fd].noteUse(fwdPconnPool);
 
868
 
 
869
    /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
 
870
    assert(entry->ping_status != PING_WAITING);
 
871
 
 
872
    assert(entry->lock_count);
 
873
 
 
874
    EBIT_SET(entry->flags, ENTRY_DISPATCHED);
 
875
 
 
876
    netdbPingSite(request->host);
 
877
 
 
878
    if (servers && (p = servers->_peer)) {
 
879
        p->stats.fetches++;
 
880
        request->peer_login = p->login;
 
881
        request->peer_domain = p->domain;
 
882
        httpStart(this);
 
883
    } else {
 
884
        request->peer_login = NULL;
 
885
        request->peer_domain = NULL;
 
886
 
 
887
        switch (request->protocol) {
 
888
#if USE_SSL
 
889
 
 
890
        case PROTO_HTTPS:
 
891
            httpStart(this);
 
892
            break;
 
893
#endif
 
894
 
 
895
        case PROTO_HTTP:
 
896
            httpStart(this);
 
897
            break;
 
898
 
 
899
        case PROTO_GOPHER:
 
900
            gopherStart(this);
 
901
            break;
 
902
 
 
903
        case PROTO_FTP:
 
904
            ftpStart(this);
 
905
            break;
 
906
 
 
907
        case PROTO_WAIS:
 
908
            waisStart(this);
 
909
            break;
 
910
 
 
911
        case PROTO_CACHEOBJ:
 
912
 
 
913
        case PROTO_INTERNAL:
 
914
 
 
915
        case PROTO_URN:
 
916
            fatal_dump("Should never get here");
 
917
            break;
 
918
 
 
919
        case PROTO_WHOIS:
 
920
            whoisStart(this);
 
921
            break;
 
922
 
 
923
        default:
 
924
            debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n",
 
925
                          storeUrl(entry));
 
926
            ErrorState *anErr = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST, request);
 
927
            fail(anErr);
 
928
            /*
 
929
             * Force a persistent connection to be closed because
 
930
             * some Netscape browsers have a bug that sends CONNECT
 
931
             * requests as GET's over persistent connections.
 
932
             */
 
933
            request->flags.proxy_keepalive = 0;
 
934
            /*
 
935
             * Set the dont_retry flag becuase this is not a
 
936
             * transient (network) error; its a bug.
 
937
             */
 
938
            flags.dont_retry = 1;
 
939
            comm_close(server_fd);
 
940
            break;
 
941
        }
 
942
    }
 
943
 
 
944
    /*
 
945
     * remove our self-refcount now that we've handed off the request
 
946
     * to a server-side module
 
947
     */
 
948
    self = NULL;
 
949
}
 
950
 
 
951
int
 
952
FwdState::reforward()
 
953
{
 
954
    StoreEntry *e = entry;
 
955
    FwdServer *fs = servers;
 
956
    http_status s;
 
957
    assert(e->store_status == STORE_PENDING);
 
958
    assert(e->mem_obj);
 
959
#if URL_CHECKSUM_DEBUG
 
960
 
 
961
    e->mem_obj->checkUrlChecksum();
 
962
#endif
 
963
 
 
964
    debug(17, 3) ("fwdReforward: %s?\n", storeUrl(e));
 
965
 
 
966
    if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
 
967
        debug(17, 3) ("fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set\n");
 
968
        return 0;
 
969
    }
 
970
 
 
971
    if (n_tries > 9)
 
972
        return 0;
 
973
 
 
974
    if (origin_tries > 1)
 
975
        return 0;
 
976
 
 
977
    if (request->flags.body_sent)
 
978
        return 0;
 
979
 
 
980
    assert(fs);
 
981
 
 
982
    servers = fs->next;
 
983
 
 
984
    fwdServerFree(fs);
 
985
 
 
986
    if (servers == NULL) {
 
987
        debug(17, 3) ("fwdReforward: No forward-servers left\n");
 
988
        return 0;
 
989
    }
 
990
 
 
991
    s = e->getReply()->sline.status;
 
992
    debug(17, 3) ("fwdReforward: status %d\n", (int) s);
 
993
    return reforwardableStatus(s);
 
994
}
 
995
 
 
996
static void
 
997
fwdStats(StoreEntry * s)
 
998
{
 
999
    int i;
 
1000
    int j;
 
1001
    storeAppendPrintf(s, "Status");
 
1002
 
 
1003
    for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
 
1004
        storeAppendPrintf(s, "\ttry#%d", j + 1);
 
1005
    }
 
1006
 
 
1007
    storeAppendPrintf(s, "\n");
 
1008
 
 
1009
    for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
 
1010
        if (FwdReplyCodes[0][i] == 0)
 
1011
            continue;
 
1012
 
 
1013
        storeAppendPrintf(s, "%3d", i);
 
1014
 
 
1015
        for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
 
1016
            storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
 
1017
        }
 
1018
 
 
1019
        storeAppendPrintf(s, "\n");
 
1020
    }
 
1021
}
 
1022
 
 
1023
 
 
1024
/**** STATIC MEMBER FUNCTIONS *************************************************/
 
1025
 
 
1026
bool
 
1027
FwdState::reforwardableStatus(http_status s)
 
1028
{
 
1029
    switch (s) {
 
1030
 
 
1031
    case HTTP_BAD_GATEWAY:
 
1032
 
 
1033
    case HTTP_GATEWAY_TIMEOUT:
 
1034
        return true;
 
1035
 
 
1036
    case HTTP_FORBIDDEN:
 
1037
 
 
1038
    case HTTP_INTERNAL_SERVER_ERROR:
 
1039
 
 
1040
    case HTTP_NOT_IMPLEMENTED:
 
1041
 
 
1042
    case HTTP_SERVICE_UNAVAILABLE:
 
1043
        return Config.retry.onerror;
 
1044
 
 
1045
    default:
 
1046
        return false;
 
1047
    }
 
1048
 
 
1049
    /* NOTREACHED */
 
1050
}
 
1051
 
 
1052
void
 
1053
FwdState::pconnPush(int fd, const char *host, int port, const char *domain)
 
1054
{
 
1055
    fwdPconnPool->push(fd, host, port, domain);
 
1056
}
 
1057
 
 
1058
void
 
1059
FwdState::initModule()
 
1060
{
 
1061
    memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
 
1062
 
 
1063
#if WIP_FWD_LOG
 
1064
 
 
1065
    if (logfile)
 
1066
        (void) 0;
 
1067
    else if (NULL == Config.Log.forward)
 
1068
        (void) 0;
 
1069
    else
 
1070
        logfile = logfileOpen(Config.Log.forward, 0, 1);
 
1071
 
 
1072
#endif
 
1073
}
 
1074
 
 
1075
void
 
1076
FwdState::RegisterWithCacheManager(CacheManager & manager)
 
1077
{
 
1078
    manager.registerAction("forward",
 
1079
                           "Request Forwarding Statistics",
 
1080
                           fwdStats, 0, 1);
 
1081
}
 
1082
 
 
1083
void
 
1084
FwdState::logReplyStatus(int tries, http_status status)
 
1085
{
 
1086
    if (status > HTTP_INVALID_HEADER)
 
1087
        return;
 
1088
 
 
1089
    assert(tries);
 
1090
 
 
1091
    tries--;
 
1092
 
 
1093
    if (tries > MAX_FWD_STATS_IDX)
 
1094
        tries = MAX_FWD_STATS_IDX;
 
1095
 
 
1096
    FwdReplyCodes[tries][status]++;
 
1097
}
 
1098
 
 
1099
void
 
1100
FwdState::serversFree(FwdServer ** FSVR)
 
1101
{
 
1102
    FwdServer *fs;
 
1103
 
 
1104
    while ((fs = *FSVR)) {
 
1105
        *FSVR = fs->next;
 
1106
        fwdServerFree(fs);
 
1107
    }
 
1108
}
 
1109
 
 
1110
/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
 
1111
 
 
1112
static void
 
1113
fwdServerFree(FwdServer * fs)
 
1114
{
 
1115
    cbdataReferenceDone(fs->_peer);
 
1116
    memFree(fs, MEM_FWD_SERVER);
 
1117
}
 
1118
 
 
1119
static struct IN_ADDR
 
1120
            aclMapAddr(acl_address * head, ACLChecklist * ch)
 
1121
{
 
1122
    acl_address *l;
 
1123
 
 
1124
    struct IN_ADDR addr;
 
1125
 
 
1126
    for (l = head; l; l = l->next)
 
1127
    {
 
1128
        if (ch->matchAclListFast(l->aclList))
 
1129
            return l->addr;
 
1130
    }
 
1131
 
 
1132
    addr.s_addr = INADDR_ANY;
 
1133
    return addr;
 
1134
}
 
1135
 
 
1136
static int
 
1137
aclMapTOS(acl_tos * head, ACLChecklist * ch)
 
1138
{
 
1139
    acl_tos *l;
 
1140
 
 
1141
    for (l = head; l; l = l->next) {
 
1142
        if (ch->matchAclListFast(l->aclList))
 
1143
            return l->tos;
 
1144
    }
 
1145
 
 
1146
    return 0;
 
1147
}
 
1148
 
 
1149
struct IN_ADDR
 
1150
            getOutgoingAddr(HttpRequest * request)
 
1151
{
 
1152
    ACLChecklist ch;
 
1153
 
 
1154
    if (request)
 
1155
    {
 
1156
        ch.src_addr = request->client_addr;
 
1157
        ch.my_addr = request->my_addr;
 
1158
        ch.my_port = request->my_port;
 
1159
        ch.request = HTTPMSGLOCK(request);
 
1160
    }
 
1161
 
 
1162
    return aclMapAddr(Config.accessList.outgoing_address, &ch);
 
1163
}
 
1164
 
 
1165
unsigned long
 
1166
getOutgoingTOS(HttpRequest * request)
 
1167
{
 
1168
    ACLChecklist ch;
 
1169
 
 
1170
    if (request) {
 
1171
        ch.src_addr = request->client_addr;
 
1172
        ch.my_addr = request->my_addr;
 
1173
        ch.my_port = request->my_port;
 
1174
        ch.request = HTTPMSGLOCK(request);
 
1175
    }
 
1176
 
 
1177
    return aclMapTOS(Config.accessList.outgoing_tos, &ch);
 
1178
}
 
1179
 
 
1180
 
 
1181
/**** WIP_FWD_LOG *************************************************************/
 
1182
 
 
1183
#if WIP_FWD_LOG
 
1184
void
 
1185
fwdUninit(void)
 
1186
{
 
1187
    if (NULL == logfile)
 
1188
        return;
 
1189
 
 
1190
    logfileClose(logfile);
 
1191
 
 
1192
    logfile = NULL;
 
1193
}
 
1194
 
 
1195
void
 
1196
fwdLogRotate(void)
 
1197
{
 
1198
    if (logfile)
 
1199
        logfileRotate(logfile);
 
1200
}
 
1201
 
 
1202
static void
 
1203
FwdState::log()
 
1204
{
 
1205
    if (NULL == logfile)
 
1206
        return;
 
1207
 
 
1208
    logfilePrintf(logfile, "%9d.%03d %03d %s %s\n",
 
1209
                  (int) current_time.tv_sec,
 
1210
                  (int) current_time.tv_usec / 1000,
 
1211
                  last_status,
 
1212
                  RequestMethodStr[request->method],
 
1213
                  request->canonical);
 
1214
}
 
1215
 
 
1216
void
 
1217
FwdState::status(http_status s)
 
1218
{
 
1219
    last_status = s;
 
1220
}
 
1221
 
 
1222
#endif