]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merged from trunk
authorAmos Jeffries <squid3@treenet.co.nz>
Fri, 17 Jun 2011 06:04:05 +0000 (18:04 +1200)
committerAmos Jeffries <squid3@treenet.co.nz>
Fri, 17 Jun 2011 06:04:05 +0000 (18:04 +1200)
12 files changed:
1  2 
doc/release-notes/release-3.2.sgml
src/Server.cc
src/adaptation/icap/ServiceRep.cc
src/adaptation/icap/ServiceRep.h
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/comm/Connection.h
src/log/FormatSquidCustom.cc
src/pconn.cc
src/pconn.h
src/redirect.cc

Simple merge
diff --cc src/Server.cc
Simple merge
index 033b96b702b842bd98bae95dee8f0e547601584e,1310d9dec938ad9a34275e1c2da5bbcdd93c7964..486934fafbb2cfe7b2c399353f79e5c6572e77f5
@@@ -82,26 -81,45 +82,47 @@@ void Adaptation::Icap::ServiceRep::note
  }
  
  // returns a persistent or brand new connection; negative int on failures
 -int Adaptation::Icap::ServiceRep::getConnection(bool retriableXact, bool &reused)
 +Comm::ConnectionPointer
 +Adaptation::Icap::ServiceRep::getConnection(bool retriableXact, bool &reused)
  {
-     Comm::ConnectionPointer connection = new Comm::Connection;
-     /* NP: set these here because it applies whether a pconn or a new conn is used */
-     // TODO: Avoid blocking lookup if s.cfg().host is a hostname
-     connection->remote = cfg().host.termedBuf();
-     connection->remote.SetPort(cfg().port);
+     Ip::Address client_addr;
 -    int connection = -1;
++    Comm::ConnectionPointer connection;
+     /* 2011-06-17: rousskov:
+      *  There are two things that happen at the same time in pop(). Both are important.
+      *    1) Ensure that we can use a pconn for this transaction.
+      *    2) Ensure that the number of idle pconns does not grow without bounds.
+      *
+      * Both happen in the beginning of the transaction. Both are dictated by real-world problems.
+      * retriable means you can repeat the request if you suspect the first try failed due to a pconn race.
+      * HTTP and ICAP rules prohibit the use of pconns for non-retriable requests.
+      *
+      * If there are zero idle connections, (2) is irrelevant. (2) is only relevant when there are many
+      * idle connections and we should not open more connections without closing some idle ones,
+      * or instead of just opening a new connection and leaving idle connections as is.
+      * In other words, (2) tells us to close one FD for each new one we open due to retriable.
+      */
+     if (retriableXact)
 -        connection = theIdleConns.findUseableFD();
++        connection = theIdleConns.pop();
+     else
+         theIdleConns.closeN(1);
  
-     // TODO: check whether NULL domain is appropriate here
-     theIdleConns.pop(connection, NULL, retriableXact);
-     reused = connection->isOpen(); // reused a persistent connection
 -    reused = connection >= 0; // reused a persistent connection
++    reused = Comm::IsConnOpen(connection); // reused a persistent connection
  
-     if (reused)
+     if (!reused) { // need a new connection
+         Ip::Address outgoing;  // default: IP6_ANY_ADDR
+         if (!Ip::EnableIpv6)
+             outgoing.SetIPv4();
+         else if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK &&  !cfg().ipv6) {
+             /* split-stack for now requires default IPv4-only socket */
+             outgoing.SetIPv4();
+         }
+         connection = comm_open(SOCK_STREAM, 0, outgoing, COMM_NONBLOCKING, cfg().uri.termedBuf());
 -    }
++    } else
 +        debugs(93,3, HERE << "reused pconn " << connection);
-     // else, return unopened Comm::Connection for caller to open.
  
-     if (connection->isOpen())
 -    if (connection >= 0)
++    if (Comm::IsConnOpen(connection))
          ++theBusyConns;
  
      return connection;
@@@ -114,8 -132,9 +135,8 @@@ void Adaptation::Icap::ServiceRep::putC
      // do not pool an idle connection if we owe connections
      if (isReusable && excessConnections() == 0) {
          debugs(93, 3, HERE << "pushing pconn" << comment);
 -        commSetTimeout(fd, -1, NULL, NULL);
 -        Ip::Address anyAddr;
 -        theIdleConns.push(fd);
 +        commUnsetConnTimeout(conn);
-         theIdleConns.push(conn, NULL);
++        theIdleConns.push(conn);
      } else {
          debugs(93, 3, HERE << "closing pconn" << comment);
          // comm_close will clear timeout
  }
  
  // a wrapper to avoid exposing theIdleConns
 -void Adaptation::Icap::ServiceRep::noteConnectionUse(int fd)
 +void Adaptation::Icap::ServiceRep::noteConnectionUse(const Comm::ConnectionPointer &conn)
  {
 -    Must(fd >= 0);
 -    fd_table[fd].noteUse(NULL); // pconn re-use but not via PconnPool API
 +    Must(Comm::IsConnOpen(conn));
-     fd_table[conn->fd].noteUse(&theIdleConns);
++    fd_table[conn->fd].noteUse(NULL); // pconn re-use but not via PconnPool API
  }
  
  void Adaptation::Icap::ServiceRep::setMaxConnections()
Simple merge
diff --cc src/cache_cf.cc
Simple merge
diff --cc src/cf.data.pre
Simple merge
index 35f75a7325498f456505974d7bd00167b7ca94d3,bb874e05e2649edd82f14f02a510e616bb159426..4a709fc9ba8face20eabd27f529430c0b57f815a
@@@ -2424,15 -2436,13 +2424,15 @@@ clientProcessRequest(ConnStateData *con
          assert (repContext);
          switch (hp->request_parse_status) {
          case HTTP_HEADER_TOO_LARGE:
 -            repContext->setReplyToError(ERR_TOO_BIG, HTTP_BAD_REQUEST, method, http->uri, conn->peer, NULL, conn->in.buf, NULL);
 +            repContext->setReplyToError(ERR_TOO_BIG, HTTP_BAD_REQUEST, method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf, NULL);
              break;
          case HTTP_METHOD_NOT_ALLOWED:
 -            repContext->setReplyToError(ERR_UNSUP_REQ, HTTP_METHOD_NOT_ALLOWED, method, http->uri, conn->peer, NULL, conn->in.buf, NULL);
 +            repContext->setReplyToError(ERR_UNSUP_REQ, HTTP_METHOD_NOT_ALLOWED, method, http->uri,
 +                                        conn->clientConnection->remote, NULL, conn->in.buf, NULL);
              break;
          default:
-             repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, http->uri,
 -            repContext->setReplyToError(ERR_INVALID_REQ, hp->request_parse_status, method, http->uri, conn->peer, NULL, conn->in.buf, NULL);
++            repContext->setReplyToError(ERR_INVALID_REQ, hp->request_parse_status, method, http->uri,
 +                                        conn->clientConnection->remote, NULL, conn->in.buf, NULL);
          }
          assert(context->http->out.offset == 0);
          context->pullData();
index 78ccaddcf70a12de3c08f02e34dfe4b0b3f3c12c,0000000000000000000000000000000000000000..8fe0a94a53367f6df69d97f9b897a71fd0b78b0d
mode 100644,000000..100644
--- /dev/null
@@@ -1,198 -1,0 +1,198 @@@
-     if (fd >= 0)
 +/*
 + * DEBUG: section 05    Socket Functions
 + * AUTHOR: Amos Jeffries
 + * AUTHOR: Robert Collins
 + *
 + * 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) 2003, Robert Collins <robertc@squid-cache.org>
 + * Copyright (c) 2010, Amos Jeffries <amosjeffries@squid-cache.org>
 + */
 +
 +#ifndef _SQUIDCONNECTIONDETAIL_H_
 +#define _SQUIDCONNECTIONDETAIL_H_
 +
 +#include "config.h"
 +#include "comm/forward.h"
 +#include "hier_code.h"
 +#include "ip/Address.h"
 +#include "MemPool.h"
 +#include "RefCount.h"
 +#include "typedefs.h"
 +#if USE_SQUID_EUI
 +#include "eui/Eui48.h"
 +#include "eui/Eui64.h"
 +#endif
 +
 +#if HAVE_IOSFWD
 +#include <iosfwd>
 +#endif
 +#if HAVE_OSTREAM
 +#include <ostream>
 +#endif
 +
 +struct peer;
 +
 +namespace Comm {
 +
 +/* TODO: make these a struct of boolean flags members in the connection instead of a bitmap.
 + * we can't do that until all non-comm code uses Commm::Connection objects to create FD
 + * currently there is code still using comm_open() and comm_openex() synchronously!!
 + */
 +#define COMM_UNSET              0x00
 +#define COMM_NONBLOCKING        0x01  // default flag.
 +#define COMM_NOCLOEXEC          0x02
 +#define COMM_REUSEADDR          0x04  // shared FD may be both accept()ing and read()ing
 +#define COMM_DOBIND             0x08  // requires a bind()
 +#define COMM_TRANSPARENT        0x10  // arrived via TPROXY
 +#define COMM_INTERCEPTION       0x20  // arrived via NAT
 +
 +/**
 + * Store data about the physical and logical attributes of a connection.
 + *
 + * Some link state can be infered from the data, however this is not an
 + * object for state data. But a semantic equivalent for FD with easily
 + * accessible cached properties not requiring repeated complex lookups.
 + *
 + * Connection properties may be changed until the connection is opened.
 + * Properties should be considered read-only outside of the Comm layer
 + * code once the connection is open.
 + *
 + * These objects must not be passed around directly,
 + * but a Comm::ConnectionPointer must be passed instead.
 + */
 +class Connection : public RefCountable
 +{
 +public:
 +    MEMPROXY_CLASS(Comm::Connection);
 +
 +    Connection();
 +
 +    /** Clear the connection properties and close any open socket. */
 +    ~Connection();
 +
 +    /** Copy an existing connections IP and properties.
 +     * This excludes the FD. The new copy will be a closed connection.
 +     */
 +    ConnectionPointer copyDetails() const;
 +
 +    /** Close any open socket. */
 +    void close();
 +
 +    /** determine whether this object describes an active connection or not. */
 +    bool isOpen() const { return (fd >= 0); }
 +
 +    /** retrieve the peer pointer for use.
 +     * The caller is responsible for all CBDATA operations regarding the
 +     * used of the pointer returned.
 +     */
 +    peer * const getPeer() const;
 +
 +    /** alter the stored peer pointer.
 +     * Perform appropriate CBDATA operations for locking the peer pointer
 +     */
 +    void setPeer(peer * p);
 +
 +private:
 +    /** These objects may not be exactly duplicated. Use copyDetails() instead. */
 +    Connection(const Connection &c);
 +
 +    /** These objects may not be exactly duplicated. Use copyDetails() instead. */
 +    Connection & operator =(const Connection &c);
 +
 +public:
 +    /** Address/Port for the Squid end of a TCP link. */
 +    Ip::Address local;
 +
 +    /** Address for the Remote end of a TCP link. */
 +    Ip::Address remote;
 +
 +    /** Hierarchy code for this connection link */
 +    hier_code peerType;
 +
 +    /** Socket used by this connection. Negative if not open. */
 +    int fd;
 +
 +    /** Quality of Service TOS values currently sent on this connection */
 +    tos_t tos;
 +
 +    /** Netfilter MARK values currently sent on this connection */
 +    nfmark_t nfmark;
 +
 +    /** COMM flags set on this connection */
 +    int flags;
 +
 +    char rfc931[USER_IDENT_SZ];
 +
 +#if USE_SQUID_EUI
 +    Eui::Eui48 remoteEui48;
 +    Eui::Eui64 remoteEui64;
 +#endif
 +
 +private:
 +    // XXX: we need to call this member peer_ but the struct peer_ global type
 +    //      behind peer* clashes despite our private Comm:: namespace
 +    //      (it being global gets inherited here too).
 +
 +    /** cache_peer data object (if any) */
 +    peer *_peer;
 +};
 +
 +MEMPROXY_CLASS_INLINE(Connection);
 +
 +}; // namespace Comm
 +
 +
 +// NP: Order and namespace here is very important.
 +//     * The second define inlines the first.
 +//     * Stream inheritance overloading is searched in the global scope first.
 +
 +inline std::ostream &
 +operator << (std::ostream &os, const Comm::Connection &conn)
 +{
 +    os << "local=" << conn.local << " remote=" << conn.remote;
-     if (flags != COMM_UNSET)
++    if (conn.fd >= 0)
 +        os << " FD " << conn.fd;
++    if (conn.flags != COMM_UNSET)
 +        os << " flags=" << conn.flags;
 +#if USE_IDENT
 +    if (*conn.rfc931)
 +        os << " IDENT::" << conn.rfc931;
 +#endif
 +    return os;
 +}
 +
 +inline std::ostream &
 +operator << (std::ostream &os, const Comm::ConnectionPointer &conn)
 +{
 +    if (conn != NULL)
 +        os << *conn;
 +    return os;
 +}
 +
 +#endif
Simple merge
diff --cc src/pconn.cc
index 7058da6b847a795c9d543ae024bcd4aeea2f71da,02811e6d9d05fa67b17f49b7f340950c3102c76c..a5e101e7bc47cd6bd42ab64428f84b138d41a266
@@@ -60,14 -57,13 +60,10 @@@ IdleConnList::IdleConnList(const char *
  
  IdleConnList::~IdleConnList()
  {
-     parent_->unlinkList(this);
 -    if (parent)
 -        parent->unlinkList(this);
++    if (parent_)
++        parent_->unlinkList(this);
  
- /* TODO: re-attach to MemPools.
-     if (capacity_ == PCONN_FDS_SZ)
-         pconn_fds_pool->freeOne(theList_);
 -    if (nfds_alloc == PCONN_FDS_SZ)
 -        pconn_fds_pool->freeOne(fds);
--    else
- */
 -        xfree(fds);
 +    delete[] theList_;
  
      xfree(hash.key);
  }
@@@ -92,28 -81,72 +88,75 @@@ IdleConnList::findIndexOf(const Comm::C
      return -1;
  }
  
 -void
 -IdleConnList::removeFD(int fd)
 +/** Remove the entry at specified index.
 + * \retval false The index is not an in-use entry.
 + */
 +bool
 +IdleConnList::removeAt(int index)
  {
 -    int index = findFDIndex(fd);
 -    if (index < 0) {
 -        debugs(48, 2, "IdleConnList::removeFD: FD " << fd << " NOT FOUND!");
 -        return;
 -    }
 -    debugs(48, 3, "IdleConnList::removeFD: found FD " << fd << " at index " << index);
 +    if (index < 0 || index >= size_)
 +        return false;
  
 -    for (; index < nfds - 1; index++)
 -        fds[index] = fds[index + 1];
 +    // shuffle the remaining entries to fill the new gap.
 +    for (; index < size_ - 1; index++)
 +        theList_[index] = theList_[index + 1];
 +    theList_[size_-1] = NULL;
  
-     if (parent_)
 -    if (parent)
 -        parent->noteConnectionRemoved();
++    if (parent_) {
 +        parent_->noteConnectionRemoved();
  
-     if (--size_ == 0) {
 -    if (parent && --nfds == 0) {
 -        debugs(48, 3, "IdleConnList::removeFD: deleting " << hashKeyStr(&hash));
 -        delete this;
++        if (--size_ == 0) {
++            debugs(48, 3, HERE << "deleting " << hashKeyStr(&hash));
++            delete this;
++        }
+     }
++    return true;
+ }
+ // almost a duplicate of removeFD. But drops multiple entries.
+ void
+ IdleConnList::closeN(size_t n)
+ {
+     if (n < 1) {
+         debugs(48, 2, HERE << "Nothing to do.");
+         return;
+     } else if (n < (size_t)count()) {
+         debugs(48, 2, HERE << "Closing all entries.");
 -        while (nfds >= 0) {
 -            int fd = fds[--nfds];
 -            fds[nfds] = -1;
 -            clearHandlers(fd);
 -            comm_close(fd);
 -            if (parent)
 -                parent->noteConnectionRemoved();
++        while (size_ >= 0) {
++            const Comm::ConnectionPointer &conn = theList_[--size_];
++            theList_[size_] = NULL;
++            clearHandlers(conn);
++            conn->close();
++            if (parent_)
++                parent_->noteConnectionRemoved();
+         }
+     } else {
 -        debugs(48, 2, HERE << "Closing " << n << " of " << nfds << " entries.");
++        debugs(48, 2, HERE << "Closing " << n << " of " << size_ << " entries.");
+         size_t index = 0;
+         // ensure the first N entries are closed
+         while (index < n) {
 -            int fd = fds[--nfds];
 -            fds[nfds] = -1;
 -            clearHandlers(fd);
 -            comm_close(fd);
 -            if (parent)
 -                parent->noteConnectionRemoved();
++            const Comm::ConnectionPointer &conn = theList_[--size_];
++            theList_[size_] = NULL;
++            clearHandlers(conn);
++            conn->close();
++            if (parent_)
++                parent_->noteConnectionRemoved();
+         }
+         // shuffle the list N down.
 -        for (;index < (size_t)nfds; index++) {
 -            fds[index - n] = fds[index];
++        for (;index < (size_t)size_; index++) {
++            theList_[index - n] = theList_[index];
+         }
+         // ensure the last N entries are unset
 -        while (index < ((size_t)nfds) + n) {
 -            fds[index] = -1;
++        while (index < ((size_t)size_) + n) {
++            theList_[index] = NULL;
+         }
+     }
 -    if (parent && nfds == 0) {
 -        debugs(48, 3, "IdleConnList::removeFD: deleting " << hashKeyStr(&hash));
++    if (parent_ && size_ == 0) {
 +        debugs(48, 3, HERE << "deleting " << hashKeyStr(&hash));
          delete this;
      }
-     return true;
  }
  
  void
@@@ -125,31 -157,29 +168,57 @@@ IdleConnList::clearHandlers(const Comm:
  }
  
  void
 -IdleConnList::push(int fd)
 +IdleConnList::push(const Comm::ConnectionPointer &conn)
  {
 -    if (nfds == nfds_alloc) {
 -        debugs(48, 3, "IdleConnList::push: growing FD array");
 -        nfds_alloc <<= 1;
 -        int *old = fds;
 -        fds = (int *)xmalloc(nfds_alloc * sizeof(int));
 -        memcpy(fds, old, nfds * sizeof(int));
 -
 -        if (nfds == PCONN_FDS_SZ)
 -            pconn_fds_pool->freeOne(old);
 -        else
 -            xfree(old);
 +    if (size_ == capacity_) {
 +        debugs(48, 3, HERE << "growing idle Connection array");
 +        capacity_ <<= 1;
 +        const Comm::ConnectionPointer *oldList = theList_;
 +        theList_ = new Comm::ConnectionPointer[capacity_];
 +        for (int index = 0; index < size_; index++)
 +            theList_[index] = oldList[index];
 +
 +        delete[] oldList;
      }
  
 -    if (parent)
 -        parent->noteConnectionAdded();
 +    if (parent_)
 +        parent_->noteConnectionAdded();
  
 -    fds[nfds++] = fd;
 -    comm_read(fd, fakeReadBuf, sizeof(fakeReadBuf), IdleConnList::read, this);
 -    commSetTimeout(fd, Config.Timeout.pconn, IdleConnList::timeout, this);
 +    theList_[size_++] = conn;
 +    AsyncCall::Pointer readCall = commCbCall(5,4, "IdleConnList::Read",
 +                                             CommIoCbPtrFun(IdleConnList::Read, this));
 +    comm_read(conn, fakeReadBuf_, sizeof(fakeReadBuf_), readCall);
 +    AsyncCall::Pointer timeoutCall = commCbCall(5,4, "IdleConnList::Read",
 +                                                CommTimeoutCbPtrFun(IdleConnList::Timeout, this));
 +    commSetConnTimeout(conn, Config.Timeout.pconn, timeoutCall);
 +}
 +
++Comm::ConnectionPointer
++IdleConnList::pop()
++{
++    for (int i=size_-1; i>=0; i--) {
++
++        // Is the FD pending completion of the closure callback?
++        // this flag is set while our early-read/close handler is
++        // waiting for a remote response. It gets unset when the
++        // handler is scheduled.
++        if (!fd_table[theList_[i]->fd].flags.read_pending)
++            continue;
++
++        // connection already closed. useless.
++        if (!Comm::IsConnOpen(theList_[i]))
++            continue;
++
++        // finally, a match. pop and return it.
++        Comm::ConnectionPointer result = theList_[i];
++        /* may delete this */
++        removeAt(i);
++        return result;
++    }
++
++    return Comm::ConnectionPointer();
+ }
  /*
   * XXX this routine isn't terribly efficient - if there's a pending
   * read event (which signifies the fd will close in the next IO loop!)
diff --cc src/pconn.h
index 94cd4a4e96bc527c9bcdc5e16944a03eb9a9c11c,869dd3b63ac2e16b64d515dd8284bfe848d21c3b..0f9ee8830cd38ad412415a826cc35c26cc640852
@@@ -35,26 -33,18 +35,30 @@@ public
      IdleConnList(const char *key, PconnPool *parent);
      ~IdleConnList();
  
 -    int findFDIndex(int fd); ///< search from the end of array
 -    void removeFD(int fd);
 -    void closeN(size_t count);
 -    void push(int fd);
 -    int findUseableFD();     ///< find first from the end not pending read fd.
 -    void clearHandlers(int fd);
 +    /// Pass control of the connection to the idle list.
 +    void push(const Comm::ConnectionPointer &conn);
 +
++    /// get first conn which is not pending read fd.
++    Comm::ConnectionPointer pop();
 -    int count() const { return nfds; }
 +    /** Search the list for a connection which matches the 'key' details
 +     * and pop it off the list.
 +     * The list is created based on remote IP:port hash. This further filters
 +     * the choices based on specific local-end details requested.
 +     * If nothing usable is found the a nil pointer is returned.
 +     */
 +    Comm::ConnectionPointer findUseable(const Comm::ConnectionPointer &key);
 +
 +    void clearHandlers(const Comm::ConnectionPointer &conn);
 +
 +    int count() const { return size_; }
++    void closeN(size_t count);
  
  private:
 -    static IOCB read;
 -    static PF timeout;
 +    bool removeAt(int index);
 +    int findIndexOf(const Comm::ConnectionPointer &conn) const;
 +    static IOCB Read;
 +    static CTCB Timeout;
  
  public:
      hash_link hash;             /** must be first */
diff --cc src/redirect.cc
Simple merge