From: Amos Jeffries Date: Fri, 17 Jun 2011 06:04:05 +0000 (+1200) Subject: Merged from trunk X-Git-Tag: take08~55^2~124^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9815f129cf106b6bf21887a756bbeaf5d38e8b45;p=thirdparty%2Fsquid.git Merged from trunk --- 9815f129cf106b6bf21887a756bbeaf5d38e8b45 diff --cc src/adaptation/icap/ServiceRep.cc index 033b96b702,1310d9dec9..486934fafb --- a/src/adaptation/icap/ServiceRep.cc +++ b/src/adaptation/icap/ServiceRep.cc @@@ -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 @@@ -129,10 -148,10 +150,10 @@@ } // 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() diff --cc src/client_side.cc index 35f75a7325,bb874e05e2..4a709fc9ba --- a/src/client_side.cc +++ b/src/client_side.cc @@@ -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(); diff --cc src/comm/Connection.h index 78ccaddcf7,0000000000..8fe0a94a53 mode 100644,000000..100644 --- a/src/comm/Connection.h +++ b/src/comm/Connection.h @@@ -1,198 -1,0 +1,198 @@@ +/* + * 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 + * Copyright (c) 2010, Amos Jeffries + */ + +#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 +#endif +#if HAVE_OSTREAM +#include +#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 (fd >= 0) ++ if (conn.fd >= 0) + os << " FD " << conn.fd; - if (flags != COMM_UNSET) ++ 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 diff --cc src/pconn.cc index 7058da6b84,02811e6d9d..a5e101e7bc --- a/src/pconn.cc +++ b/src/pconn.cc @@@ -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 94cd4a4e96,869dd3b63a..0f9ee8830c --- a/src/pconn.h +++ b/src/pconn.h @@@ -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 */