Stop HttpRequest and AccessLogEntry memory leaks. The leaks were introduced by recent request_header_add improvements (r12213) which needed server-side access to client-side information like client certificate so that it can be stuffed into the outgoing request headers. Stuffing was done via Format API that uses AccessLogEntry as the source of information. This change breaks the HttpRequest->AccessLogEntry->HttpRequest refcounting loop that prevented both request and ale objects from being destroyed. The ale object is now delivered to the server side using FwdState::Start() API. Only HTTP code currently takes advantage of ale availability on the server side. Also had to modify httpHdrAdd() API because ale is no longer available via the request pointer. === modified file 'src/HttpHeaderTools.cc' --- src/HttpHeaderTools.cc 2012-07-18 00:12:18 +0000 +++ src/HttpHeaderTools.cc 2012-07-20 22:11:42 +0000 @@ -611,47 +611,47 @@ if (e.id == HDR_OTHER) { // does it have an ACL list configured? // Optimize: use a name type that we do not need to convert to here const ManglersByName::const_iterator i = custom.find(e.name.termedBuf()); if (i != custom.end()) return &i->second; } // Next-to-last resort: "Other" rules match any custom header if (e.id == HDR_OTHER && known[HDR_OTHER].access_list) return &known[HDR_OTHER]; // Last resort: "All" rules match any header if (all.access_list) return &all; return NULL; } void -httpHdrAdd(HttpHeader *heads, HttpRequest *request, HeaderWithAclList &headersAdd) +httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd) { ACLFilledChecklist checklist(NULL, request, NULL); for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) { if (!hwa->aclList || checklist.fastCheck(hwa->aclList) == ACCESS_ALLOWED) { const char *fieldValue = NULL; MemBuf mb; if (hwa->quoted) { - if (request->al != NULL) { + if (al != NULL) { mb.init(); - hwa->valueFormat->assemble(mb, request->al, 0); + hwa->valueFormat->assemble(mb, al, 0); fieldValue = mb.content(); } } else { fieldValue = hwa->fieldValue.c_str(); } if (!fieldValue || fieldValue[0] == '\0') fieldValue = "-"; HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, hwa->fieldName.c_str(), fieldValue); heads->addEntry(e); } } } === modified file 'src/HttpRequest.cc' --- src/HttpRequest.cc 2012-07-18 16:21:47 +0000 +++ src/HttpRequest.cc 2012-07-20 01:37:01 +0000 @@ -247,41 +247,40 @@ #if USE_ADAPTATION adaptHistory_ = aReq->adaptHistory(); #endif #if ICAP_CLIENT icapHistory_ = aReq->icapHistory(); #endif // This may be too conservative for the 204 No Content case // may eventually need cloneNullAdaptationImmune() for that. flags = aReq->flags.cloneAdaptationImmune(); errType = aReq->errType; errDetail = aReq->errDetail; #if USE_AUTH auth_user_request = aReq->auth_user_request; #endif // main property is which connection the request was received on (if any) clientConnectionManager = aReq->clientConnectionManager; - al = aReq->al; return true; } /** * Checks the first line of an HTTP request is valid * currently just checks the request method is present. * * NP: Other errors are left for detection later in the parse. */ bool HttpRequest::sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, http_status *error) { // content is long enough to possibly hold a reply // 2 being magic size of a 1-byte request method plus space delimiter if ( buf->contentSize() < 2 ) { // this is ony a real error if the headers apparently complete. if (hdr_len > 0) { debugs(58, 3, HERE << "Too large request header (" << hdr_len << " bytes)"); *error = HTTP_INVALID_HEADER; } === modified file 'src/HttpRequest.h' --- src/HttpRequest.h 2012-07-18 16:21:47 +0000 +++ src/HttpRequest.h 2012-07-20 22:34:00 +0000 @@ -38,42 +38,40 @@ #endif #if ICAP_CLIENT #include "adaptation/icap/History.h" #endif #include "base/CbcPointer.h" #include "client_side.h" #if USE_SQUID_EUI #include "eui/Eui48.h" #include "eui/Eui64.h" #endif #include "HierarchyLogEntry.h" #include "HttpMsg.h" #include "HttpRequestMethod.h" /* Http Request */ //DEAD?: extern int httpRequestHdrAllowedByName(http_hdr_type id); extern void httpRequestPack(void *obj, Packer *p); class HttpHdrRange; class DnsLookupDetails; -class AccessLogEntry; -typedef RefCount AccessLogEntryPointer; class HttpRequest: public HttpMsg { public: typedef HttpMsgPointerT Pointer; MEMPROXY_CLASS(HttpRequest); HttpRequest(); HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath); ~HttpRequest(); virtual void reset(); // use HTTPMSGLOCK() instead of calling this directly virtual HttpRequest *_lock() { return static_cast(HttpMsg::_lock()); }; void initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath); @@ -227,46 +225,40 @@ static void httpRequestPack(void *obj, Packer *p); static HttpRequest * CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method); static HttpRequest * CreateFromUrl(char * url); ConnStateData *pinnedConnection() { if (clientConnectionManager.valid() && clientConnectionManager->pinning.pinned) return clientConnectionManager.get(); return NULL; } /** * The client connection manager, if known; * Used for any response actions needed directly to the client. * ie 1xx forwarding or connection pinning state changes */ CbcPointer clientConnectionManager; - /** - * The AccessLogEntry for the current ClientHttpRequest/Server HttpRequest - * pair, if known; - */ - AccessLogEntryPointer al; - int64_t getRangeOffsetLimit(); /* the result of this function gets cached in rangeOffsetLimit */ private: const char *packableURI(bool full_uri) const; mutable int64_t rangeOffsetLimit; /* caches the result of getRangeOffsetLimit */ protected: virtual void packFirstLineInto(Packer * p, bool full_uri) const; virtual bool sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, http_status *error); virtual void hdrCacheInit(); virtual bool inheritProperties(const HttpMsg *aMsg); }; MEMPROXY_CLASS_INLINE(HttpRequest); #endif /* SQUID_HTTPREQUEST_H */ === modified file 'src/client_side.cc' --- src/client_side.cc 2012-07-19 00:12:22 +0000 +++ src/client_side.cc 2012-07-20 22:35:49 +0000 @@ -2617,41 +2617,40 @@ } /* compile headers */ /* we should skip request line! */ /* XXX should actually know the damned buffer size here */ if (http_ver.major >= 1 && !request->parseHeader(HttpParserHdrBuf(hp), HttpParserHdrSz(hp))) { clientStreamNode *node = context->getClientReplyContext(); debugs(33, 5, "Failed to parse request headers:\n" << HttpParserHdrBuf(hp)); conn->quitAfterError(request); // setLogUri should called before repContext->setReplyToError setLogUri(http, http->uri, true); clientReplyContext *repContext = dynamic_cast(node->data.getRaw()); assert (repContext); repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL); assert(context->http->out.offset == 0); context->pullData(); goto finish; } request->clientConnectionManager = conn; - request->al = http->al; request->flags.accelerated = http->flags.accel; request->flags.sslBumped = conn->switchedToHttps(); request->flags.canRePin = request->flags.sslBumped && conn->pinning.pinned; request->flags.ignore_cc = conn->port->ignore_cc; // TODO: decouple http->flags.accel from request->flags.sslBumped request->flags.no_direct = (request->flags.accelerated && !request->flags.sslBumped) ? !conn->port->allow_direct : 0; #if USE_AUTH if (request->flags.sslBumped) { if (conn->auth_user_request != NULL) request->auth_user_request = conn->auth_user_request; } #endif /** \par * If transparent or interception mode is working clone the transparent and interception flags * from the port settings to the request. */ if (http->clientConnection != NULL) { === modified file 'src/client_side_reply.cc' --- src/client_side_reply.cc 2012-07-18 16:21:47 +0000 +++ src/client_side_reply.cc 2012-07-20 22:22:52 +0000 @@ -278,41 +278,41 @@ entry = storeCreateEntry(url, http->log_uri, http->request->flags, http->request->method); /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */ sc = storeClientListAdd(entry, this); #if USE_DELAY_POOLS /* delay_id is already set on original store client */ sc->setDelayId(DelayId::DelayClient(http)); #endif http->request->lastmod = old_entry->lastmod; debugs(88, 5, "clientReplyContext::processExpired : lastmod " << entry->lastmod ); http->storeEntry(entry); assert(http->out.offset == 0); assert(http->request->clientConnectionManager == http->getConn()); /* * A refcounted pointer so that FwdState stays around as long as * this clientReplyContext does */ Comm::ConnectionPointer conn = http->getConn() != NULL ? http->getConn()->clientConnection : NULL; - FwdState::fwdStart(conn, http->storeEntry(), http->request); + FwdState::Start(conn, http->storeEntry(), http->request, http->al); /* Register with storage manager to receive updates when data comes in. */ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) debugs(88, 0, "clientReplyContext::processExpired: Found ENTRY_ABORTED object"); { /* start counting the length from 0 */ StoreIOBuffer localTempBuffer(HTTP_REQBUF_SZ, 0, tempbuf); storeClientCopy(sc, entry, localTempBuffer, HandleIMSReply, this); } } void clientReplyContext::sendClientUpstreamResponse() { StoreIOBuffer tempresult; removeStoreReference(&old_sc, &old_entry); /* here the data to send is the data we just received */ @@ -663,41 +663,41 @@ triggerInitialStoreRead(); if (http->redirect.status) { HttpReply *rep = new HttpReply; http->logType = LOG_TCP_REDIRECT; http->storeEntry()->releaseRequest(); rep->redirect(http->redirect.status, http->redirect.location); http->storeEntry()->replaceHttpReply(rep); http->storeEntry()->complete(); return; } /** Check for internal requests. Update Protocol info if so. */ if (http->flags.internal) r->protocol = AnyP::PROTO_INTERNAL; assert(r->clientConnectionManager == http->getConn()); /** Start forwarding to get the new object from network */ Comm::ConnectionPointer conn = http->getConn() != NULL ? http->getConn()->clientConnection : NULL; - FwdState::fwdStart(conn, http->storeEntry(), r); + FwdState::Start(conn, http->storeEntry(), r, http->al); } } /** * client issued a request with an only-if-cached cache-control directive; * we did not find a cached object that can be returned without * contacting other servers; * respond with a 504 (Gateway Timeout) as suggested in [RFC 2068] */ void clientReplyContext::processOnlyIfCachedMiss() { debugs(88, 4, "clientProcessOnlyIfCachedMiss: '" << RequestMethodStr(http->request->method) << " " << http->uri << "'"); http->al->http.code = HTTP_GATEWAY_TIMEOUT; ErrorState *err = clientBuildError(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT, NULL, http->getConn()->clientConnection->remote, http->request); removeClientStoreReference(&sc, http); startError(err); } === modified file 'src/forward.cc' --- src/forward.cc 2012-07-19 00:12:22 +0000 +++ src/forward.cc 2012-07-20 22:41:40 +0000 @@ -16,40 +16,41 @@ * * 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. * */ #include "squid-old.h" #include "forward.h" +#include "AccessLogEntry.h" #include "acl/FilledChecklist.h" #include "acl/Gadgets.h" #include "anyp/PortCfg.h" #include "CacheManager.h" #include "comm/Connection.h" #include "comm/ConnOpener.h" #include "CommCalls.h" #include "comm/Loops.h" #include "event.h" #include "errorpage.h" #include "fde.h" #include "hier_code.h" #include "HttpReply.h" #include "HttpRequest.h" #include "ip/QosConfig.h" #include "MemObject.h" #include "pconn.h" #include "PeerSelectState.h" #include "SquidTime.h" #include "Store.h" @@ -81,41 +82,42 @@ void FwdState::abort(void* d) { FwdState* fwd = (FwdState*)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(); } else { debugs(17, 7, HERE << "store entry aborted; no connection to close"); } fwd->serverDestinations.clean(); fwd->self = NULL; } /**** PUBLIC INTERFACE ********************************************************/ -FwdState::FwdState(const Comm::ConnectionPointer &client, StoreEntry * e, HttpRequest * r) +FwdState::FwdState(const Comm::ConnectionPointer &client, StoreEntry * e, HttpRequest * r, const AccessLogEntryPointer &alp): + al(alp) { debugs(17, 2, HERE << "Forwarding client request " << client << ", url=" << e->url() ); entry = e; clientConn = client; request = HTTPMSGLOCK(r); pconnRace = raceImpossible; start_t = squid_curtime; serverDestinations.reserve(Config.forward_max_tries); e->lock(); EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT); } // Called once, right after object creation, when it is safe to set self void FwdState::start(Pointer aSelf) { // Protect ourselves from being destroyed when the only Server pointing // to us is gone (while we expect to talk to more Servers later). // Once we set self, we are responsible for clearing it when we do not // expect to talk to any servers. self = aSelf; // refcounted @@ -226,41 +228,41 @@ 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(); } serverDestinations.clean(); debugs(17, 3, HERE << "FwdState destructor done"); } /** * This is the entry point for client-side to start forwarding * a transaction. It is a static method that may or may not * allocate a FwdState. */ void -FwdState::fwdStart(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request) +FwdState::Start(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request, const AccessLogEntryPointer &al) { /** \note * client_addr == no_addr indicates this is an "internal" request * from peer_digest.c, asn.c, netdb.c, etc and should always * be allowed. yuck, I know. */ if ( Config.accessList.miss && !request->client_addr.IsNoAddr() && request->protocol != AnyP::PROTO_INTERNAL && request->protocol != AnyP::PROTO_CACHE_OBJECT) { /** * Check if this host is allowed to fetch MISSES from us (miss_access) */ ACLFilledChecklist ch(Config.accessList.miss, request, NULL); ch.src_addr = request->client_addr; ch.my_addr = request->my_addr; if (ch.fastCheck() == ACCESS_DENIED) { err_type page_id; page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1); if (page_id == ERR_NONE) @@ -288,49 +290,56 @@ ErrorState *anErr = new ErrorState(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request); errorAppendEntry(entry, anErr); // frees anErr return; } switch (request->protocol) { case AnyP::PROTO_INTERNAL: internalStart(clientConn, request, entry); return; case AnyP::PROTO_CACHE_OBJECT: CacheManager::GetInstance()->Start(clientConn, request, entry); return; case AnyP::PROTO_URN: urnStart(request, entry); return; default: - FwdState::Pointer fwd = new FwdState(clientConn, entry, request); + FwdState::Pointer fwd = new FwdState(clientConn, entry, request, al); fwd->start(fwd); return; } /* NOTREACHED */ } void +FwdState::fwdStart(const Comm::ConnectionPointer &clientConn, StoreEntry *entry, HttpRequest *request) +{ + // Hides AccessLogEntry.h from code that does not supply ALE anyway. + Start(clientConn, entry, request, NULL); +} + +void FwdState::startConnectionOrFail() { debugs(17, 3, HERE << entry->url()); if (serverDestinations.size() > 0) { // Ditch error page if it was created before. // A new one will be created if there's another problem delete err; err = NULL; // Update the logging information about this new server connection. // Done here before anything else so the errors get logged for // this server link regardless of what happens when connecting to it. // IF sucessfuly connected this top destination will become the serverConnection(). request->hier.note(serverDestinations[0], request->GetHost()); request->clearError(); connectStart(); } else { debugs(17, 3, HERE << "Connection failed: " << entry->url()); === modified file 'src/forward.h' --- src/forward.h 2012-07-18 17:45:54 +0000 +++ src/forward.h 2012-07-20 22:18:51 +0000 @@ -1,102 +1,109 @@ #ifndef SQUID_FORWARD_H #define SQUID_FORWARD_H /* forward decls */ +class AccessLogEntry; +typedef RefCount AccessLogEntryPointer; class ErrorState; class HttpRequest; #include "comm.h" #include "comm/Connection.h" #include "fde.h" #include "ip/Address.h" #include "Array.h" /** * Returns the TOS value that we should be setting on the connection * to the server, based on the ACL. */ tos_t GetTosToServer(HttpRequest * request); /** * Returns the Netfilter mark value that we should be setting on the * connection to the server, based on the ACL. */ nfmark_t GetNfmarkToServer(HttpRequest * request); class FwdState : public RefCountable { public: typedef RefCount Pointer; ~FwdState(); static void initModule(); + /// Initiates request forwarding to a peer or origin server. + static void Start(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp); + /// Same as Start() but no master xaction info (AccessLogEntry) available. static void fwdStart(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *); /// This is the real beginning of server connection. Call it whenever /// the forwarding server destination has changed and a new one needs to be opened. /// Produces the cannot-forward error on fail if no better error exists. void startConnectionOrFail(); void fail(ErrorState *err); void unregister(Comm::ConnectionPointer &conn); void unregister(int fd); void complete(); void handleUnregisteredServerEnd(); int reforward(); bool reforwardableStatus(http_status s); void serverClosed(int fd); void connectStart(); void connectDone(const Comm::ConnectionPointer & conn, comm_err_t status, int xerrno); void connectTimeout(int fd); void initiateSSL(); void negotiateSSL(int fd); bool checkRetry(); bool checkRetriable(); void dispatch(); void pconnPush(Comm::ConnectionPointer & conn, const char *domain); bool dontRetry() { return flags.dont_retry; } void dontRetry(bool val) { flags.dont_retry = val; } /** return a ConnectionPointer to the current server connection (may or may not be open) */ Comm::ConnectionPointer const & serverConnection() const { return serverConn; }; private: // hidden for safer management of self; use static fwdStart - FwdState(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *); + FwdState(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp); void start(Pointer aSelf); void selectPeerForIntercepted(); static void logReplyStatus(int tries, http_status status); void doneWithRetries(); void completed(); void retryOrBail(); ErrorState *makeConnectingError(const err_type type) const; static void RegisterWithCacheManager(void); public: StoreEntry *entry; HttpRequest *request; + AccessLogEntryPointer al; ///< info for the future access.log entry + static void abort(void*); private: Pointer self; ErrorState *err; Comm::ConnectionPointer clientConn; ///< a possibly open connection to the client. time_t start_t; int n_tries; int origin_tries; // AsyncCalls which we set and may need cancelling. struct { AsyncCall::Pointer connector; ///< a call linking us to the ConnOpener producing serverConn. } calls; struct { unsigned int connected_okay:1; ///< TCP link ever opened properly. This affects retry of POST,PUT,CONNECT,etc unsigned int dont_retry:1; unsigned int forward_completed:1; } flags; === modified file 'src/htcp.cc' --- src/htcp.cc 2012-07-17 14:11:24 +0000 +++ src/htcp.cc 2012-07-20 21:42:07 +0000 @@ -1557,41 +1557,41 @@ HttpHeader hdr(hoRequest); Packer pa; MemBuf mb; http_state_flags flags; if (!Comm::IsConnOpen(htcpIncomingConn)) return 0; old_squid_format = p->options.htcp_oldsquid; memset(&flags, '\0', sizeof(flags)); snprintf(vbuf, sizeof(vbuf), "%d/%d", req->http_ver.major, req->http_ver.minor); stuff.op = HTCP_TST; stuff.rr = RR_REQUEST; stuff.f1 = 1; stuff.response = 0; stuff.msg_id = ++msg_id_counter; stuff.S.method = (char *) RequestMethodStr(req->method); stuff.S.uri = (char *) e->url(); stuff.S.version = vbuf; - HttpStateData::httpBuildRequestHeader(req, e, &hdr, flags); + HttpStateData::httpBuildRequestHeader(req, e, NULL, &hdr, flags); mb.init(); packerToMemInit(&pa, &mb); hdr.packInto(&pa); hdr.clean(); packerClean(&pa); stuff.S.req_hdrs = mb.buf; pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff); mb.clean(); if (!pktlen) { debugs(31, 3, "htcpQuery: htcpBuildPacket() failed"); return -1; } htcpSend(pkt, (int) pktlen, p->in_addr); queried_id[stuff.msg_id % N_QUERIED_KEYS] = stuff.msg_id; save_key = queried_keys[stuff.msg_id % N_QUERIED_KEYS]; storeKeyCopy(save_key, (const cache_key *)e->key); queried_addr[stuff.msg_id % N_QUERIED_KEYS] = p->in_addr; debugs(31, 3, "htcpQuery: key (" << save_key << ") " << storeKeyText(save_key)); @@ -1628,41 +1628,41 @@ stuff.msg_id = ++msg_id_counter; switch (reason) { case HTCP_CLR_INVALIDATION: stuff.reason = 1; break; default: stuff.reason = 0; break; } stuff.S.method = (char *) RequestMethodStr(req->method); if (e == NULL || e->mem_obj == NULL) { if (uri == NULL) { return; } stuff.S.uri = xstrdup(uri); } else { stuff.S.uri = (char *) e->url(); } stuff.S.version = vbuf; if (reason != HTCP_CLR_INVALIDATION) { - HttpStateData::httpBuildRequestHeader(req, e, &hdr, flags); + HttpStateData::httpBuildRequestHeader(req, e, NULL, &hdr, flags); mb.init(); packerToMemInit(&pa, &mb); hdr.packInto(&pa); hdr.clean(); packerClean(&pa); stuff.S.req_hdrs = mb.buf; } else { stuff.S.req_hdrs = NULL; } pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff); if (reason != HTCP_CLR_INVALIDATION) { mb.clean(); } if (e == NULL) { xfree(stuff.S.uri); } if (!pktlen) { debugs(31, 3, "htcpClear: htcpBuildPacket() failed"); return; } === modified file 'src/http.cc' --- src/http.cc 2012-07-18 16:21:47 +0000 +++ src/http.cc 2012-07-20 21:45:21 +0000 @@ -72,41 +72,41 @@ #include "Store.h" #define SQUID_ENTER_THROWING_CODE() try { #define SQUID_EXIT_THROWING_CODE(status) \ status = true; \ } \ catch (const std::exception &e) { \ debugs (11, 1, "Exception error:" << e.what()); \ status = false; \ } CBDATA_CLASS_INIT(HttpStateData); static const char *const crlf = "\r\n"; static void httpMaybeRemovePublic(StoreEntry *, http_status); static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request, HttpHeader * hdr_out, const int we_do_ranges, const http_state_flags); //Declared in HttpHeaderTools.cc -void httpHdrAdd(HttpHeader *heads, HttpRequest *request, HeaderWithAclList &headers_add); +void httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headers_add); HttpStateData::HttpStateData(FwdState *theFwdState) : AsyncJob("HttpStateData"), ServerStateData(theFwdState), lastChunk(0), header_bytes_read(0), reply_bytes_read(0), body_bytes_truncated(0), httpChunkDecoder(NULL) { debugs(11,5,HERE << "HttpStateData " << this << " created"); ignoreCacheControl = false; surrogateNoStore = false; serverConnection = fwd->serverConnection(); readBuf = new MemBuf; readBuf->init(16*1024, 256*1024); // reset peer response time stats for %hier.peer_http_request_sent.tv_sec = 0; request->hier.peer_http_request_sent.tv_usec = 0; if (fwd->serverConnection() != NULL) _peer = cbdataReference(fwd->serverConnection()->getPeer()); /* might be NULL */ if (_peer) { @@ -1591,40 +1591,41 @@ if (Token) { httpHeaderPutStrf(hdr_out, header, "Negotiate %s",Token); } return; } #endif /* HAVE_KRB5 && HAVE_GSSAPI */ httpHeaderPutStrf(hdr_out, header, "Basic %s", old_base64_encode(request->peer_login)); return; } /* * build request headers and append them to a given MemBuf * used by buildRequestPrefix() * note: initialised the HttpHeader, the caller is responsible for Clean()-ing */ void HttpStateData::httpBuildRequestHeader(HttpRequest * request, StoreEntry * entry, + const AccessLogEntryPointer &al, HttpHeader * hdr_out, const http_state_flags flags) { /* building buffer for complex strings */ #define BBUF_SZ (MAX_URL+32) LOCAL_ARRAY(char, bbuf, BBUF_SZ); LOCAL_ARRAY(char, ntoabuf, MAX_IPSTRLEN); const HttpHeader *hdr_in = &request->header; const HttpHeaderEntry *e = NULL; HttpHeaderPos pos = HttpHeaderInitPos; assert (hdr_out->owner == hoRequest); /* append our IMS header */ if (request->lastmod > -1) hdr_out->putTime(HDR_IF_MODIFIED_SINCE, request->lastmod); bool we_do_ranges = decideIfWeDoRanges (request); String strConnection (hdr_in->getList(HDR_CONNECTION)); @@ -1768,41 +1769,41 @@ hdr_out->putStr(HDR_CONNECTION, "keep-alive"); } /* append Front-End-Https */ if (flags.front_end_https) { if (flags.front_end_https == 1 || request->protocol == AnyP::PROTO_HTTPS) hdr_out->putStr(HDR_FRONT_END_HTTPS, "On"); } if (flags.chunked_request) { // Do not just copy the original value so that if the client-side // starts decode other encodings, this code may remain valid. hdr_out->putStr(HDR_TRANSFER_ENCODING, "chunked"); } /* Now mangle the headers. */ if (Config2.onoff.mangle_request_headers) httpHdrMangleList(hdr_out, request, ROR_REQUEST); if (Config.request_header_add && !Config.request_header_add->empty()) - httpHdrAdd(hdr_out, request, *Config.request_header_add); + httpHdrAdd(hdr_out, request, al, *Config.request_header_add); strConnection.clean(); } /** * Decides whether a particular header may be cloned from the received Clients request * to our outgoing fetch request. */ void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request, HttpHeader * hdr_out, const int we_do_ranges, const http_state_flags flags) { debugs(11, 5, "httpBuildRequestHeader: " << e->name << ": " << e->value ); switch (e->id) { /** \par RFC 2616 sect 13.5.1 - Hop-by-Hop headers which Squid should not pass on. */ case HDR_PROXY_AUTHORIZATION: /** \par Proxy-Authorization: * Only pass on proxy authentication to peers for which @@ -1991,41 +1992,41 @@ * return the length of the prefix */ mb_size_t HttpStateData::buildRequestPrefix(MemBuf * mb) { const int offset = mb->size; HttpVersion httpver(1,1); const char * url; if (_peer && !_peer->options.originserver) url = entry->url(); else url = request->urlpath.termedBuf(); mb->Printf("%s %s %s/%d.%d\r\n", RequestMethodStr(request->method), url && *url ? url : "/", AnyP::ProtocolType_str[httpver.protocol], httpver.major,httpver.minor); /* build and pack headers */ { HttpHeader hdr(hoRequest); Packer p; - httpBuildRequestHeader(request, entry, &hdr, flags); + httpBuildRequestHeader(request, entry, fwd->al, &hdr, flags); if (request->flags.pinned && request->flags.connection_auth) request->flags.auth_sent = 1; else if (hdr.has(HDR_AUTHORIZATION)) request->flags.auth_sent = 1; packerToMemInit(&p, mb); hdr.packInto(&p); hdr.clean(); packerClean(&p); } /* append header terminator */ mb->append(crlf, 2); return mb->size - offset; } /* This will be called when connect completes. Write request. */ bool HttpStateData::sendRequest() { === modified file 'src/http.h' --- src/http.h 2011-10-21 16:20:42 +0000 +++ src/http.h 2012-07-20 21:40:49 +0000 @@ -33,40 +33,41 @@ #ifndef SQUID_HTTP_H #define SQUID_HTTP_H #include "StoreIOBuffer.h" #include "comm.h" #include "comm/forward.h" #include "forward.h" #include "Server.h" #include "ChunkedCodingParser.h" class HttpStateData : public ServerStateData { public: HttpStateData(FwdState *); ~HttpStateData(); static void httpBuildRequestHeader(HttpRequest * request, StoreEntry * entry, + const AccessLogEntryPointer &al, HttpHeader * hdr_out, const http_state_flags flags); virtual const Comm::ConnectionPointer & dataConnection() const; /* should be private */ bool sendRequest(); void processReplyHeader(); void processReplyBody(); void readReply(const CommIoCbParams &io); virtual void maybeReadVirginBody(); // read response data from the network int cacheableReply(); peer *_peer; /* peer request made to */ int eof; /* reached end-of-object? */ int lastChunk; /* reached last chunk of a chunk-encoded reply */ http_state_flags flags; size_t read_sz; int header_bytes_read; // to find end of response, int64_t reply_bytes_read; // without relying on StoreEntry int body_bytes_truncated; // positive when we read more than we wanted === modified file 'src/tunnel.cc' --- src/tunnel.cc 2012-07-19 00:12:22 +0000 +++ src/tunnel.cc 2012-07-20 21:38:24 +0000 @@ -677,40 +677,41 @@ NULL, tunnelPeerSelectComplete, tunnelState); } static void tunnelRelayConnectRequest(const Comm::ConnectionPointer &srv, void *data) { TunnelStateData *tunnelState = (TunnelStateData *)data; HttpHeader hdr_out(hoRequest); Packer p; http_state_flags flags; debugs(26, 3, HERE << srv << ", tunnelState=" << tunnelState); memset(&flags, '\0', sizeof(flags)); flags.proxying = tunnelState->request->flags.proxying; MemBuf mb; mb.init(); mb.Printf("CONNECT %s HTTP/1.1\r\n", tunnelState->url); HttpStateData::httpBuildRequestHeader(tunnelState->request, NULL, /* StoreEntry */ + NULL, /* AccessLogEntry */ &hdr_out, flags); /* flags */ packerToMemInit(&p, &mb); hdr_out.packInto(&p); hdr_out.clean(); packerClean(&p); mb.append("\r\n", 2); AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectedWriteDone", CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); Comm::Write(srv, &mb, writeCall); AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); commSetConnTimeout(srv, Config.Timeout.read, timeoutCall); } static void tunnelPeerSelectComplete(Comm::ConnectionList *peer_paths, ErrorState *err, void *data) {