]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/FwdState.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / FwdState.cc
index d3f826376fa99bc4abe349c90716176fb651186b..8397fe443575eb5ecbebdc1be83cb4a714e3a846 100644 (file)
@@ -1,35 +1,13 @@
 /*
- * DEBUG: section 17    Request Forwarding
- * AUTHOR: Duane Wessels
- *
- * SQUID Web Proxy Cache          http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- *  Squid is the result of efforts by numerous individuals from
- *  the Internet community; see the CONTRIBUTORS file for full
- *  details.   Many organizations have provided support for Squid's
- *  development; see the SPONSORS file for full details.  Squid is
- *  Copyrighted (C) 2001 by the Regents of the University of
- *  California; see the COPYRIGHT file for full details.  Squid
- *  incorporates software developed and/or copyrighted by other
- *  sources; see the CREDITS file for full details.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
+/* DEBUG: section 17    Request Forwarding */
+
 #include "squid.h"
 #include "AccessLogEntry.h"
 #include "acl/AclAddress.h"
@@ -39,6 +17,7 @@
 #include "CacheManager.h"
 #include "CachePeer.h"
 #include "client_side.h"
+#include "clients/forward.h"
 #include "comm/Connection.h"
 #include "comm/ConnOpener.h"
 #include "comm/Loops.h"
@@ -47,7 +26,6 @@
 #include "event.h"
 #include "fd.h"
 #include "fde.h"
-#include "ftp.h"
 #include "FwdState.h"
 #include "globals.h"
 #include "gopher.h"
@@ -64,6 +42,7 @@
 #include "mgr/Registration.h"
 #include "neighbors.h"
 #include "pconn.h"
+#include "PeerPoolMgr.h"
 #include "PeerSelectState.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
@@ -80,9 +59,8 @@
 #include "ssl/ServerBump.h"
 #include "ssl/support.h"
 #endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
+
+#include <cerrno>
 
 static PSC fwdPeerSelectionCompleteWrapper;
 static CLCB fwdServerClosedWrapper;
@@ -93,7 +71,7 @@ static OBJH fwdStats;
 #define MAX_FWD_STATS_IDX 9
 static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][Http::scInvalidHeader + 1];
 
-static PconnPool *fwdPconnPool = new PconnPool("server-side");
+static PconnPool *fwdPconnPool = new PconnPool("server-peers", NULL);
 CBDATA_CLASS_INIT(FwdState);
 
 #if USE_OPENSSL
@@ -109,7 +87,8 @@ public:
     virtual bool canDial(AsyncCall &call) { return fwd_.valid(); }
     void dial(AsyncCall &call) { ((&(*fwd_))->*method_)(answer_); }
     virtual void print(std::ostream &os) const {
-        os << '(' << fwd_.get() << ", " << answer_ << ')'; }
+        os << '(' << fwd_.get() << ", " << answer_ << ')';
+    }
 
     /* Ssl::PeerConnector::CbDialer API */
     virtual Ssl::PeerConnectorAnswer &answer() { return answer_; }
@@ -121,7 +100,6 @@ private:
 };
 #endif
 
-
 void
 FwdState::abort(void* d)
 {
@@ -129,10 +107,7 @@ FwdState::abort(void* d)
     Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope.
 
     if (Comm::IsConnOpen(fwd->serverConnection())) {
-        comm_remove_close_handler(fwd->serverConnection()->fd, fwdServerClosedWrapper, fwd);
-        debugs(17, 3, HERE << "store entry aborted; closing " <<
-               fwd->serverConnection());
-        fwd->serverConnection()->close();
+        fwd->closeServerConnection("store entry aborted");
     } else {
         debugs(17, 7, HERE << "store entry aborted; no connection to close");
     }
@@ -140,10 +115,19 @@ FwdState::abort(void* d)
     fwd->self = NULL;
 }
 
+void
+FwdState::closeServerConnection(const char *reason)
+{
+    debugs(17, 3, "because " << reason << "; " << serverConn);
+    comm_remove_close_handler(serverConn->fd, fwdServerClosedWrapper, this);
+    fwdPconnPool->noteUses(fd_table[serverConn->fd].pconn.uses);
+    serverConn->close();
+}
+
 /**** PUBLIC INTERFACE ********************************************************/
 
 FwdState::FwdState(const Comm::ConnectionPointer &client, StoreEntry * e, HttpRequest * r, const AccessLogEntryPointer &alp):
-        al(alp)
+    al(alp)
 {
     debugs(17, 2, HERE << "Forwarding client request " << client << ", url=" << e->url() );
     entry = e;
@@ -236,6 +220,8 @@ FwdState::completed()
 
     flags.forward_completed = true;
 
+    request->hier.stopPeerClock(false);
+
     if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
         debugs(17, 3, HERE << "entry aborted");
         return ;
@@ -295,11 +281,8 @@ FwdState::~FwdState()
         calls.connector = NULL;
     }
 
-    if (Comm::IsConnOpen(serverConn)) {
-        comm_remove_close_handler(serverConnection()->fd, fwdServerClosedWrapper, this);
-        debugs(17, 3, HERE << "closing FD " << serverConnection()->fd);
-        serverConn->close();
-    }
+    if (Comm::IsConnOpen(serverConn))
+        closeServerConnection("~FwdState");
 
     serverDestinations.clear();
 
@@ -321,7 +304,7 @@ FwdState::Start(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, Ht
      */
 
     if ( Config.accessList.miss && !request->client_addr.isNoAddr() &&
-            request->protocol != AnyP::PROTO_INTERNAL && request->protocol != AnyP::PROTO_CACHE_OBJECT) {
+            !request->flags.internal && request->url.getScheme() != AnyP::PROTO_CACHE_OBJECT) {
         /**
          * Check if this host is allowed to fetch MISSES from us (miss_access).
          * Intentionally replace the src_addr automatically selected by the checklist code
@@ -337,7 +320,7 @@ FwdState::Start(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, Ht
                 page_id = ERR_FORWARDING_DENIED;
 
             ErrorState *anErr = new ErrorState(page_id, Http::scForbidden, request);
-            errorAppendEntry(entry, anErr);    // frees anErr
+            errorAppendEntry(entry, anErr); // frees anErr
             return;
         }
     }
@@ -357,17 +340,20 @@ FwdState::Start(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, Ht
     if (shutting_down) {
         /* more yuck */
         ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
-        errorAppendEntry(entry, anErr);        // frees anErr
+        errorAppendEntry(entry, anErr); // frees anErr
         return;
     }
 
-    switch (request->protocol) {
-
-    case AnyP::PROTO_INTERNAL:
+    if (request->flags.internal) {
+        debugs(17, 2, "calling internalStart() due to request flag");
         internalStart(clientConn, request, entry);
         return;
+    }
+
+    switch (request->url.getScheme()) {
 
     case AnyP::PROTO_CACHE_OBJECT:
+        debugs(17, 2, "calling CacheManager due to request scheme " << request->url.getScheme());
         CacheManager::GetInstance()->Start(clientConn, request, entry);
         return;
 
@@ -461,7 +447,7 @@ FwdState::unregister(Comm::ConnectionPointer &conn)
     serverConn = NULL;
 }
 
-// Legacy method to be removed in favor of the above as soon as possible
+// \deprecated use unregister(Comm::ConnectionPointer &conn) instead
 void
 FwdState::unregister(int fd)
 {
@@ -471,7 +457,7 @@ FwdState::unregister(int fd)
 }
 
 /**
- * server-side modules call fwdComplete() when they are done
+ * FooClient modules call fwdComplete() when they are done
  * downloading an object.  Then, we either 1) re-forward the
  * request somewhere else if needed, or 2) call storeComplete()
  * to finish it off
@@ -517,7 +503,7 @@ FwdState::complete()
 /**** CALLBACK WRAPPERS ************************************************************/
 
 static void
-fwdPeerSelectionCompleteWrapper(Comm::ConnectionList * unused, ErrorState *err, void *data)
+fwdPeerSelectionCompleteWrapper(Comm::ConnectionList *, ErrorState *err, void *data)
 {
     FwdState *fwd = (FwdState *) data;
     if (err)
@@ -533,7 +519,7 @@ fwdServerClosedWrapper(const CommCloseCbParams &params)
 }
 
 void
-fwdConnectDoneWrapper(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
+fwdConnectDoneWrapper(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data)
 {
     FwdState *fwd = (FwdState *) data;
     fwd->connectDone(conn, status, xerrno);
@@ -546,7 +532,7 @@ fwdConnectDoneWrapper(const Comm::ConnectionPointer &conn, comm_err_t status, in
  *
  * Return TRUE if the request SHOULD be retried.  This method is
  * called when the HTTP connection fails, or when the connection
- * is closed before server-side read the end of HTTP headers.
+ * is closed before reading the end of HTTP headers from the server.
  */
 bool
 FwdState::checkRetry()
@@ -612,7 +598,11 @@ FwdState::checkRetriable()
 void
 FwdState::serverClosed(int fd)
 {
-    debugs(17, 2, HERE << "FD " << fd << " " << entry->url());
+    // XXX: fd is often -1 here
+    debugs(17, 2, "FD " << fd << " " << entry->url() << " after " <<
+           (fd >= 0 ? fd_table[fd].pconn.uses : -1) << " requests");
+    if (fd >= 0 && serverConnection()->fd == fd)
+        fwdPconnPool->noteUses(fd_table[fd].pconn.uses);
     retryOrBail();
 }
 
@@ -633,12 +623,14 @@ FwdState::retryOrBail()
     // TODO: should we call completed() here and move doneWithRetries there?
     doneWithRetries();
 
+    request->hier.stopPeerClock(false);
+
     if (self != NULL && !err && shutting_down) {
         ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, Http::scServiceUnavailable, request);
         errorAppendEntry(entry, anErr);
     }
 
-    self = NULL;       // refcounted
+    self = NULL;    // refcounted
 }
 
 // If the Server quits before nibbling at the request body, the body sender
@@ -661,9 +653,9 @@ FwdState::handleUnregisteredServerEnd()
 }
 
 void
-FwdState::connectDone(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno)
+FwdState::connectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno)
 {
-    if (status != COMM_OK) {
+    if (status != Comm::OK) {
         ErrorState *const anErr = makeConnectingError(ERR_CONNECT_FAIL);
         anErr->xerrno = xerrno;
         fail(anErr);
@@ -692,21 +684,27 @@ FwdState::connectDone(const Comm::ConnectionPointer &conn, comm_err_t status, in
 #if USE_OPENSSL
     if (!request->flags.pinned) {
         if ((serverConnection()->getPeer() && serverConnection()->getPeer()->use_ssl) ||
-                (!serverConnection()->getPeer() && request->protocol == AnyP::PROTO_HTTPS) ||
+                (!serverConnection()->getPeer() && request->url.getScheme() == AnyP::PROTO_HTTPS) ||
                 request->flags.sslPeek) {
 
             HttpRequest::Pointer requestPointer = request;
             AsyncCall::Pointer callback = asyncCall(17,4,
-                "FwdState::ConnectedToPeer",
-                FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this));
+                                                    "FwdState::ConnectedToPeer",
+                                                    FwdStatePeerAnswerDialer(&FwdState::connectedToPeer, this));
+            // Use positive timeout when less than one second is left.
+            const time_t sslNegotiationTimeout = max(static_cast<time_t>(1), timeLeft());
             Ssl::PeerConnector *connector =
-                new Ssl::PeerConnector(requestPointer, serverConnection(), callback);
+                new Ssl::PeerConnector(requestPointer, serverConnection(), clientConn, callback, sslNegotiationTimeout);
             AsyncJob::Start(connector); // will call our callback
             return;
         }
     }
 #endif
 
+    // should reach ConnStateData before the dispatched Client job starts
+    CallJobHere1(17, 4, request->clientConnectionManager, ConnStateData,
+                 ConnStateData::notePeerConnection, serverConnection());
+
     dispatch();
 }
 
@@ -747,21 +745,9 @@ FwdState::connectTimeout(int fd)
     }
 }
 
-/**
- * Called after Forwarding path selection (via peer select) has taken place.
- * And whenever forwarding needs to attempt a new connection (routing failover)
- * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
- */
-void
-FwdState::connectStart()
+time_t
+FwdState::timeLeft() const
 {
-    assert(serverDestinations.size() > 0);
-
-    debugs(17, 3, "fwdConnectStart: " << entry->url());
-
-    if (!request->hier.first_conn_start.tv_sec) // first attempt
-        request->hier.first_conn_start = current_time;
-
     /* connection timeout */
     int ctimeout;
     if (serverDestinations[0]->getPeer()) {
@@ -777,7 +763,24 @@ FwdState::connectStart()
         ftimeout = 5;
 
     if (ftimeout < ctimeout)
-        ctimeout = ftimeout;
+        return (time_t)ftimeout;
+    else
+        return (time_t)ctimeout;
+}
+
+/**
+ * Called after forwarding path selection (via peer select) has taken place
+ * and whenever forwarding needs to attempt a new connection (routing failover).
+ * We have a vector of possible localIP->remoteIP paths now ready to start being connected.
+ */
+void
+FwdState::connectStart()
+{
+    assert(serverDestinations.size() > 0);
+
+    debugs(17, 3, "fwdConnectStart: " << entry->url());
+
+    request->hier.startPeerClock();
 
     if (serverDestinations[0]->getPeer() && request->flags.sslBumped) {
         debugs(50, 4, "fwdConnectStart: Ssl bumped connections through parent proxy are not allowed");
@@ -795,7 +798,7 @@ FwdState::connectStart()
         debugs(17,7, "pinned peer connection: " << pinned_connection);
         // pinned_connection may become nil after a pconn race
         if (pinned_connection)
-            serverConn = pinned_connection->validatePinnedConnection(request, serverDestinations[0]->getPeer());
+            serverConn = pinned_connection->borrowPinnedConnection(request, serverDestinations[0]->getPeer());
         else
             serverConn = NULL;
         if (Comm::IsConnOpen(serverConn)) {
@@ -807,6 +810,20 @@ FwdState::connectStart()
             if (pinned_connection->pinnedAuth())
                 request->flags.auth = true;
             comm_add_close_handler(serverConn->fd, fwdServerClosedWrapper, this);
+
+            /* Update server side TOS and Netfilter mark on the connection. */
+            if (Ip::Qos::TheConfig.isAclTosActive()) {
+                debugs(17, 3, HERE << "setting tos for pinned connection to " << (int)serverConn->tos );
+                serverConn->tos = GetTosToServer(request);
+                Ip::Qos::setSockTos(serverConn, serverConn->tos);
+            }
+#if SO_MARK
+            if (Ip::Qos::TheConfig.isAclNfmarkActive()) {
+                serverConn->nfmark = GetNfmarkToServer(request);
+                Ip::Qos::setSockNfmark(serverConn, serverConn->nfmark);
+            }
+#endif
+
             // the server may close the pinned connection before this request
             pconnRace = racePossible;
             dispatch();
@@ -830,7 +847,7 @@ FwdState::connectStart()
     // This does not increase the total number of connections because we just
     // closed the connection that failed the race. And re-pinning assumes this.
     if (pconnRace != raceHappened)
-        temp = fwdPconnPool->pop(serverDestinations[0], host, checkRetriable());
+        temp = pconnPop(serverDestinations[0], host);
 
     const bool openedPconn = Comm::IsConnOpen(temp);
     pconnRace = openedPconn ? racePossible : raceImpossible;
@@ -873,7 +890,7 @@ FwdState::connectStart()
     GetMarkingsToServer(request, *serverDestinations[0]);
 
     calls.connector = commCbCall(17,3, "fwdConnectDoneWrapper", CommConnectCbPtrFun(fwdConnectDoneWrapper, this));
-    Comm::ConnOpener *cs = new Comm::ConnOpener(serverDestinations[0], calls.connector, ctimeout);
+    Comm::ConnOpener *cs = new Comm::ConnOpener(serverDestinations[0], calls.connector, timeLeft());
     if (host)
         cs->setHost(host);
     AsyncJob::Start(cs);
@@ -892,7 +909,7 @@ FwdState::dispatch()
 
     fd_note(serverConnection()->fd, entry->url());
 
-    fd_table[serverConnection()->fd].noteUse(fwdPconnPool);
+    fd_table[serverConnection()->fd].noteUse();
 
     /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
     assert(entry->ping_status != PING_WAITING);
@@ -946,7 +963,7 @@ FwdState::dispatch()
         request->peer_login = NULL;
         request->peer_domain = NULL;
 
-        switch (request->protocol) {
+        switch (request->url.getScheme()) {
 #if USE_OPENSSL
 
         case AnyP::PROTO_HTTPS:
@@ -963,13 +980,14 @@ FwdState::dispatch()
             break;
 
         case AnyP::PROTO_FTP:
-            ftpStart(this);
+            if (request->flags.ftpNative)
+                Ftp::StartRelay(this);
+            else
+                Ftp::StartGateway(this);
             break;
 
         case AnyP::PROTO_CACHE_OBJECT:
 
-        case AnyP::PROTO_INTERNAL:
-
         case AnyP::PROTO_URN:
             fatal_dump("Should never get here");
             break;
@@ -978,7 +996,7 @@ FwdState::dispatch()
             whoisStart(this);
             break;
 
-        case AnyP::PROTO_WAIS: /* Not implemented */
+        case AnyP::PROTO_WAIS:  /* Not implemented */
 
         default:
             debugs(17, DBG_IMPORTANT, "WARNING: Cannot retrieve '" << entry->url() << "'.");
@@ -999,7 +1017,7 @@ FwdState::dispatch()
  *
  * returns TRUE if the transaction SHOULD be re-forwarded to the
  * next choice in the serverDestinations list.  This method is called when
- * server-side communication completes normally, or experiences
+ * peer communication completes normally, or experiences
  * some error after receiving the end of HTTP headers.
  */
 int
@@ -1127,6 +1145,22 @@ FwdState::pconnPush(Comm::ConnectionPointer &conn, const char *domain)
     }
 }
 
+Comm::ConnectionPointer
+FwdState::pconnPop(const Comm::ConnectionPointer &dest, const char *domain)
+{
+    // always call shared pool first because we need to close an idle
+    // connection there if we have to use a standby connection.
+    Comm::ConnectionPointer conn = fwdPconnPool->pop(dest, domain, checkRetriable());
+    if (!Comm::IsConnOpen(conn)) {
+        // either there was no pconn to pop or this is not a retriable xaction
+        if (CachePeer *peer = dest->getPeer()) {
+            if (peer->standby.pool)
+                conn = peer->standby.pool->pop(dest, domain, true);
+        }
+    }
+    return conn; // open, closed, or nil
+}
+
 void
 FwdState::initModule()
 {
@@ -1269,3 +1303,4 @@ GetMarkingsToServer(HttpRequest * request, Comm::Connection &conn)
     conn.nfmark = 0;
 #endif
 }
+