]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merged from trunk 13199.
authorAlex Rousskov <rousskov@measurement-factory.com>
Wed, 1 Jan 2014 19:20:49 +0000 (12:20 -0700)
committerAlex Rousskov <rousskov@measurement-factory.com>
Wed, 1 Jan 2014 19:20:49 +0000 (12:20 -0700)
1  2 
src/Makefile.am
src/Server.cc
src/SquidConfig.h
src/cf.data.pre
src/client_side_reply.cc
src/client_side_reply.h
src/format/Format.cc
src/ftp.cc

diff --combined src/Makefile.am
index 25b1358de25ccccecffea5120a1e764847688611,f39833ce9115a505cc091c0bf511d13b10baf8b2..fa354c6c7375c6f184e6ace6b55a55562b15d69d
@@@ -15,6 -15,7 +15,7 @@@ DNSSOURCE = 
        DnsLookupDetails.cc
  
  SBUF_SOURCE= \
+       base/CharacterSet.h \
        base/InstanceId.h \
        MemBlob.h \
        MemBlob.cc \
@@@ -312,8 -313,6 +313,8 @@@ squid_SOURCES = 
        ClientRequestContext.h \
        clientStream.cc \
        clientStream.h \
 +      CollapsedForwarding.cc \
 +      CollapsedForwarding.h \
        CompletionDispatcher.cc \
        CompletionDispatcher.h \
        CommRead.h \
        swap_log_op.h \
        SwapDir.cc \
        SwapDir.h \
 +      Transients.cc \
 +      Transients.h \
        MemStore.cc \
        MemStore.h \
        time.cc \
@@@ -926,7 -923,7 +927,7 @@@ DEFAULT_ERROR_DIR  = $(datadir)/error
  # Make location configure settings available to the code
  DEFS += -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\" -DDEFAULT_SQUID_DATA_DIR=\"$(datadir)\" -DDEFAULT_SQUID_CONFIG_DIR=\"$(sysconfdir)\"
  
- snmp_core.o snmp_agent.o: ../lib/snmplib/libsnmplib.a $(top_srcdir)/include/cache_snmp.h
+ snmp_core.o snmp_agent.o: ../lib/snmplib/libsnmplib.la $(top_srcdir)/include/cache_snmp.h
  
  globals.cc: globals.h mk-globals-c.awk
        $(AWK) -f $(srcdir)/mk-globals-c.awk < $(srcdir)/globals.h > $@ || ($(RM) -f $@ && exit 1)
@@@ -1077,6 -1074,7 +1078,7 @@@ check_PROGRAMS+=
        tests/testString \
        tests/testURL \
        tests/testSBuf \
+       tests/testSBufList \
        tests/testConfigParser \
        tests/testStatHist \
        tests/testVector
@@@ -1220,7 -1218,6 +1222,7 @@@ tests_testHttpReply_DEPENDENCIES= $(SQU
  tests_testACLMaxUserIP_SOURCES= \
        cbdata.cc \
        ClientInfo.h \
 +      tests/stub_CollapsedForwarding.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        DiskIO/ReadRequest.cc \
        swap_log_op.h \
        tests/stub_SwapDir.cc \
        SwapDir.h \
 +      Transients.cc \
        log/access_log.h \
        tests/stub_access_log.cc \
        cache_cf.h \
        tests/stub_Port.cc \
        repl_modules.h \
        tests/stub_store.cc \
 +      tests/stub_store_client.cc \
        store_rebuild.h \
        tests/stub_store_rebuild.cc \
        tests/stub_store_stats.cc \
@@@ -1423,7 -1418,6 +1425,7 @@@ tests_testCacheManager_SOURCES = 
        client_side_request.cc \
        ClientInfo.h \
        clientStream.cc \
 +      tests/stub_CollapsedForwarding.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        CpuAffinityMap.cc \
        StoreSwapLogData.cc \
        tools.h \
        tools.cc \
 +      Transients.cc \
        tests/stub_tunnel.cc \
        tests/stub_SwapDir.cc \
        MemStore.cc \
@@@ -1634,7 -1627,6 +1636,7 @@@ tests_testDiskIO_SOURCES = 
        cbdata.cc \
        client_db.h \
        ClientInfo.h \
 +      tests/stub_CollapsedForwarding.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        $(DELAY_POOL_SOURCE) \
        StrList.h \
        StrList.cc \
        tests/stub_SwapDir.cc \
 +      Transients.cc \
        log/access_log.h \
        tests/stub_access_log.cc \
        tests/stub_acl.cc \
        tests/stub_mime.cc \
        tests/stub_pconn.cc \
        tests/stub_Port.cc \
 +      tests/stub_stat.cc \
        tests/stub_store_client.cc \
        tests/stub_store_stats.cc \
        store_rebuild.h \
@@@ -1835,7 -1825,6 +1837,7 @@@ tests_testEvent_SOURCES = 
        client_side_request.cc \
        ClientInfo.h \
        clientStream.cc \
 +      tests/stub_CollapsedForwarding.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        CpuAffinityMap.cc \
        time.cc \
        tools.h \
        tools.cc \
 +      Transients.cc \
        tests/stub_tunnel.cc \
        MemStore.cc \
        $(UNLINKDSOURCE) \
@@@ -2083,7 -2071,6 +2085,7 @@@ tests_testEventLoop_SOURCES = 
        client_side_request.cc \
        ClientInfo.h \
        clientStream.cc \
 +      tests/stub_CollapsedForwarding.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        CpuAffinityMap.cc \
        time.cc \
        tools.h \
        tools.cc \
 +      Transients.cc \
        tests/stub_tunnel.cc \
        MemStore.cc \
        $(UNLINKDSOURCE) \
@@@ -2330,7 -2316,6 +2332,7 @@@ tests_test_http_range_SOURCES = 
        client_side_request.cc \
        ClientInfo.h \
        clientStream.cc \
 +      tests/stub_CollapsedForwarding.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        CpuAffinityMap.cc \
        StrList.h \
        StrList.cc \
        tests/stub_SwapDir.cc \
 +      Transients.cc \
        tests/test_http_range.cc \
        tests/stub_external_acl.cc \
        tests/stub_ipc_Forwarder.cc \
@@@ -2552,7 -2536,6 +2554,7 @@@ tests_testHttpParser_SOURCES = 
        HttpParser.h \
        MemBuf.cc \
        MemBuf.h \
 +      tests/stub_MemObject.cc \
        Mem.h \
        tests/stub_mem.cc \
        String.cc \
        tests/stub_SBufDetailedStats.cc \
        tests/stub_cache_cf.cc \
        tests/stub_cache_manager.cc \
 +      tests/stub_comm.cc \
 +      tests/stub_cbdata.cc \
        tests/stub_debug.cc \
        tests/stub_event.cc \
        tests/stub_HelperChildConfig.cc \
 +      tests/stub_stmem.cc \
 +      tests/stub_store.cc \
 +      tests/stub_store_stats.cc \
        tools.h \
        tests/stub_tools.cc \
        tests/testHttpParser.cc \
@@@ -2641,7 -2619,6 +2643,7 @@@ tests_testHttpRequest_SOURCES = 
        client_side_request.cc \
        ClientInfo.h \
        clientStream.cc \
 +      tests/stub_CollapsedForwarding.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        CpuAffinityMap.cc \
        event.cc \
        tools.h \
        tools.cc \
 +      Transients.cc \
        tests/stub_tunnel.cc \
        tests/stub_SwapDir.cc \
        MemStore.cc \
@@@ -2843,7 -2819,6 +2845,7 @@@ tests_testStore_SOURCES= 
        tests/stub_CacheDigest.cc \
        cbdata.cc \
        ClientInfo.h \
 +      tests/stub_CollapsedForwarding.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        $(DELAY_POOL_SOURCE) \
        mime.h \
        tests/stub_mime.cc \
        tests/stub_Port.cc \
 +      tests/stub_stat.cc \
        tests/stub_store_client.cc \
        tests/stub_store_stats.cc \
        store_rebuild.h \
        tests/stub_store_rebuild.cc \
        tests/stub_store_swapout.cc \
        tools.h \
 +      Transients.cc \
        tests/stub_tools.cc \
        tests/stub_UdsOp.cc \
        tests/testMain.cc \
@@@ -3019,6 -2992,9 +3021,9 @@@ tests_testString_SOURCES = 
        tests/stub_mem.cc \
        MemBuf.cc \
        String.cc \
+       $(SBUF_SOURCE) \
+       SBufDetailedStats.h \
+       tests/stub_SBufDetailedStats.cc \
        tests/testMain.cc \
        tests/testString.cc \
        tests/testString.h \
@@@ -3071,7 -3047,6 +3076,7 @@@ tests_testUfs_SOURCES = 
        tests/testUfs.h \
        tests/stub_cache_manager.cc \
        tests/stub_client_db.cc \
 +      tests/stub_CollapsedForwarding.cc \
        tests/stub_HelperChildConfig.cc \
        tests/stub_icp.cc \
        tests/stub_ipc.cc \
        internal.h \
        tests/stub_internal.cc \
        tests/stub_libformat.cc \
 +      tests/stub_stat.cc \
        store_rebuild.h \
        tests/stub_store_rebuild.cc \
        tests/stub_store_stats.cc \
        RequestFlags.cc \
        SquidList.h \
        SquidList.cc \
 +      Transients.cc \
        MasterXaction.cc \
        MasterXaction.h \
        MemObject.cc \
@@@ -3261,8 -3234,6 +3266,8 @@@ testRefCount_LDADD = 
  tests_testRock_SOURCES = \
        cbdata.cc \
        CacheDigest.h \
 +      CollapsedForwarding.h \
 +      CollapsedForwarding.cc \
        tests/stub_CacheDigest.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        tests/stub_StatHist.cc \
        stmem.cc \
        repl_modules.h \
 +      tests/stub_stat.cc \
        store.cc \
        StoreFileSystem.cc \
        StoreIOState.cc \
        StrList.h \
        StrList.cc \
        SwapDir.cc \
 +      Transients.h \
 +      Transients.cc \
        tests/testRock.cc \
        tests/testMain.cc \
        tests/testRock.h \
@@@ -3448,7 -3416,6 +3453,7 @@@ tests_testURL_SOURCES = 
        client_side_request.cc \
        ClientInfo.h \
        clientStream.cc \
 +      tests/stub_CollapsedForwarding.cc \
        ConfigOption.cc \
        ConfigParser.cc \
        CpuAffinityMap.cc \
        String.cc \
        StrList.h \
        StrList.cc \
 +      Transients.cc \
        tests/stub_SwapDir.cc \
        MemStore.cc \
        tests/stub_debug.cc \
@@@ -3709,12 -3675,54 +3714,55 @@@ tests_testSBuf_LDADD=
        $(COMMON_LIBS)
  tests_testSBuf_DEPENDENCIES= $(SQUID_CPPUNIT_LA)
  
+ tests_testSBufList_SOURCES= \
+       tests/testSBufList.h \
+       tests/testSBufList.cc \
+       tests/testMain.cc \
+       $(SBUF_SOURCE) \
+   SBufList.h \
+   SBufList.cc \
+   SBufAlgos.h \
+       SBufDetailedStats.h \
+       tests/stub_SBufDetailedStats.cc \
+       SBufStream.h \
+       tests/stub_time.cc \
+       mem.cc \
+       tests/stub_debug.cc \
+       tests/stub_event.cc \
+       tests/stub_fatal.cc \
+       tests/stub_HelperChildConfig.cc \
+       tests/stub_cache_cf.cc \
+       tests/stub_cache_manager.cc \
++      tests/stub_store.cc \
+       tests/stub_store_stats.cc \
+       tests/stub_tools.cc \
+       SquidString.h \
+       String.cc \
+       tests/stub_wordlist.cc \
+       tests/stub_MemBuf.cc
+ nodist_tests_testSBufList_SOURCES=$(TESTSOURCES)
+ tests_testSBufList_LDFLAGS = $(LIBADD_DL)
+ tests_testSBufList_LDADD=\
+       $(SQUID_CPPUNIT_LIBS) \
+       $(SQUID_CPPUNIT_LA) \
+       $(COMPAT_LIB) \
+       libsquid.la \
+       ip/libip.la \
+       mgr/libmgr.la \
+       base/libbase.la \
+       $(top_builddir)/lib/libmiscutil.la \
+       $(COMMON_LIBS)
+ tests_testSBufList_DEPENDENCIES= $(SQUID_CPPUNIT_LA)
  tests_testConfigParser_SOURCES = \
        ClientInfo.h \
        Mem.h \
        tests/stub_mem.cc \
        tests/stub_MemBuf.cc \
        tests/stub_time.cc \
+       $(SBUF_SOURCE) \
+       SBufDetailedStats.h \
+       tests/stub_SBufDetailedStats.cc \
        String.cc \
        ConfigParser.cc \
        fatal.h \
diff --combined src/Server.cc
index 295bb5509c8f879dc2dbbf1396b6028ca9f31639,5cbb391c02e6c50df2c5ae3528c23419e669dfbc..8438cd29786ef00615c34d8e3af06c4fe251e33f
@@@ -31,6 -31,7 +31,7 @@@
   */
  
  #include "squid.h"
+ #include "acl/FilledChecklist.h"
  #include "acl/Gadgets.h"
  #include "base/TextException.h"
  #include "comm/Connection.h"
@@@ -43,6 -44,7 +44,7 @@@
  #include "HttpReply.h"
  #include "HttpRequest.h"
  #include "Server.h"
+ #include "SquidConfig.h"
  #include "SquidTime.h"
  #include "StatCounters.h"
  #include "Store.h"
@@@ -54,7 -56,6 +56,6 @@@
  #include "adaptation/Answer.h"
  #include "adaptation/Iterator.h"
  #include "base/AsyncCall.h"
- #include "SquidConfig.h"
  #endif
  
  // implemented in client_side_reply.cc until sides have a common parent
@@@ -74,7 -75,7 +75,7 @@@ ServerStateData::ServerStateData(FwdSta
      fwd = theFwdState;
      entry = fwd->entry;
  
 -    entry->lock();
 +    entry->lock("ServerStateData");
  
      request = fwd->request;
      HTTPMSGLOCK(request);
@@@ -89,7 -90,7 +90,7 @@@ ServerStateData::~ServerStateData(
      assert(!adaptedBodySource);
  #endif
  
 -    entry->unlock();
 +    entry->unlock("ServerStateData");
  
      HTTPMSGUNLOCK(request);
      HTTPMSGUNLOCK(theVirginReply);
@@@ -174,6 -175,8 +175,8 @@@ ServerStateData::setFinalReply(HttpRepl
      // give entry the reply because haveParsedReplyHeaders() expects it there
      entry->replaceHttpReply(theFinalReply, false); // but do not write yet
      haveParsedReplyHeaders(); // update the entry/reply (e.g., set timestamps)
 -    if (EBIT_TEST(entry->flags, ENTRY_CACHABLE) && blockCaching())
++    if (!EBIT_TEST(entry->flags, RELEASE_REQUEST) && blockCaching())
+         entry->release();
      entry->startWriting(); // write the updated entry to store
  
      return theFinalReply;
@@@ -533,6 -536,24 +536,24 @@@ ServerStateData::haveParsedReplyHeaders
      currentOffset = partial ? theFinalReply->content_range->spec.offset : 0;
  }
  
+ /// whether to prevent caching of an otherwise cachable response
+ bool
+ ServerStateData::blockCaching()
+ {
+     if (const Acl::Tree *acl = Config.accessList.storeMiss) {
+         // This relatively expensive check is not in StoreEntry::checkCachable:
+         // That method lacks HttpRequest and may be called too many times.
+         ACLFilledChecklist ch(acl, originalRequest(), NULL);
+         ch.reply = const_cast<HttpReply*>(entry->getReply()); // ACLFilledChecklist API bug
+         HTTPMSGLOCK(ch.reply);
+         if (ch.fastCheck() != ACCESS_ALLOWED) { // when in doubt, block
+             debugs(20, 3, "store_miss prohibits caching");
+             return true;
+         }
+     }
+     return false;
+ }
  HttpRequest *
  ServerStateData::originalRequest()
  {
diff --combined src/SquidConfig.h
index bd25195960eef2d482cdf1deeea5030c906c05da,8efc16c6c27d4529f5ea4d56a12a0221470af446..fea163ecbb5fce474de7c7b4a4caaaf2270e52a9
@@@ -330,7 -330,6 +330,7 @@@ public
          int emailErrData;
          int httpd_suppress_version_string;
          int global_internal_static;
 +        int collapsed_forwarding;
  
  #if FOLLOW_X_FORWARDED_FOR
          int acl_uses_indirect_client;
          acl_access *AlwaysDirect;
          acl_access *ASlists;
          acl_access *noCache;
+         acl_access *sendHit;
+         acl_access *storeMiss;
          acl_access *stats_collection;
  #if SQUID_SNMP
  
diff --combined src/cf.data.pre
index 60fcd335f0fb20c4848fa93f605cc39880786ea5,8293d8c1995d33cc6ea04b0bb4b1a0bddc509d58..657d1ca210d15222cb099b22eae9fed743059801
@@@ -130,6 -130,12 +130,6 @@@ DOC_STAR
        This option is not yet supported by Squid-3.
  DOC_END
  
 -NAME: collapsed_forwarding
 -TYPE: obsolete
 -DOC_START
 -      This option is not yet supported by Squid-3. see http://bugs.squid-cache.org/show_bug.cgi?id=3495
 -DOC_END
 -
  NAME: error_map
  TYPE: obsolete
  DOC_START
@@@ -2683,8 -2689,8 +2683,8 @@@ DOC_STAR
                concurrency=
        
        The number of requests each certificate validator helper can handle in
-       parallel. Defaults to 0 which indicates the certficate validator
-       is a old-style single threaded redirector.
+       parallel. A value of 0 indicates the certficate validator does not
+       support concurrency. Defaults to 1.
        
        When this directive is set to a value >= 1 then the protocol
        used to communicate with the helper is modified to include
@@@ -3432,11 -3438,13 +3432,11 @@@ DOC_STAR
        ====  The rock store type  ====
  
        Usage:
 -          cache_dir rock Directory-Name Mbytes <max-size=bytes> [options]
 +          cache_dir rock Directory-Name Mbytes [options]
  
        The Rock Store type is a database-style storage. All cached
 -      entries are stored in a "database" file, using fixed-size slots,
 -      one entry per slot. The database size is specified in MB. The
 -      slot size is specified in bytes using the max-size option. See
 -      below for more info on the max-size option.
 +      entries are stored in a "database" file, using fixed-size slots.
 +      A single entry occupies one or more slots.
  
        If possible, Squid using Rock Store creates a dedicated kid
        process called "disker" to avoid blocking Squid worker(s) on disk
        and when set to zero, disables the disk I/O rate limit
        enforcement. Currently supported by IpcIo module only.
  
 +      slot-size=bytes: The size of a database "record" used for
 +      storing cached responses. A cached response occupies at least
 +      one slot and all database I/O is done using individual slots so
 +      increasing this parameter leads to more disk space waste while
 +      decreasing it leads to more disk I/O overheads. Should be a
 +      multiple of your operating system I/O page size. Defaults to
 +      16KBytes. A housekeeping header is stored with each slot and
 +      smaller slot-sizes will be rejected. The header is smaller than
 +      100 bytes.
 +
  
        ==== COMMON OPTIONS ====
  
@@@ -4853,18 -4851,97 +4853,97 @@@ COMMENT_EN
  NAME: cache no_cache
  TYPE: acl_access
  DEFAULT: none
- DEFAULT_DOC: Allow caching, unless rules exist in squid.conf.
+ DEFAULT_DOC: By default, this directive is unused and has no effect.
  LOC: Config.accessList.noCache
  DOC_START
-       A list of ACL elements which, if matched and denied, cause the request to
-       not be satisfied from the cache and the reply to not be cached.
-       In other words, use this to force certain objects to never be cached.
-       You must use the words 'allow' or 'deny' to indicate whether items
-       matching the ACL should be allowed or denied into the cache.
+       Requests denied by this directive will not be served from the cache
+       and their responses will not be stored in the cache. This directive
+       has no effect on other transactions and on already cached responses.
  
        This clause supports both fast and slow acl types.
        See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details.
+       This and the two other similar caching directives listed below are
+       checked at different transaction processing stages, have different
+       access to response information, affect different cache operations,
+       and differ in slow ACLs support:
+       * cache: Checked before Squid makes a hit/miss determination.
+               No access to reply information!
+               Denies both serving a hit and storing a miss.
+               Supports both fast and slow ACLs.
+       * send_hit: Checked after a hit was detected.
+               Has access to reply (hit) information.
+               Denies serving a hit only.
+               Supports fast ACLs only.
+       * store_miss: Checked before storing a cachable miss.
+               Has access to reply (miss) information.
+               Denies storing a miss only.
+               Supports fast ACLs only.
+       If you are not sure which of the three directives to use, apply the
+       following decision logic:
+       * If your ACL(s) are of slow type _and_ need response info, redesign.
+         Squid does not support that particular combination at this time.
+         Otherwise:
+       * If your directive ACL(s) are of slow type, use "cache"; and/or
+       * if your directive ACL(s) need no response info, use "cache".
+         Otherwise:
+       * If you do not want the response cached, use store_miss; and/or
+       * if you do not want a hit on a cached response, use send_hit.
+ DOC_END
+ NAME: send_hit
+ TYPE: acl_access
+ DEFAULT: none
+ DEFAULT_DOC: By default, this directive is unused and has no effect.
+ LOC: Config.accessList.sendHit
+ DOC_START
+       Responses denied by this directive will not be served from the cache
+       (but may still be cached, see store_miss). This directive has no
+       effect on the responses it allows and on the cached objects.
+       Please see the "cache" directive for a summary of differences among
+       store_miss, send_hit, and cache directives.
+       Unlike the "cache" directive, send_hit only supports fast acl
+       types.  See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details.
+       For example:
+               # apply custom Store ID mapping to some URLs
+               acl MapMe dstdomain .c.example.com
+               store_id_program ...
+               store_id_access allow MapMe
+               # but prevent caching of special responses
+               # such as 302 redirects that cause StoreID loops
+               acl Ordinary http_status 200-299
+               store_miss deny MapMe !Ordinary
+               # and do not serve any previously stored special responses
+               # from the cache (in case they were already cached before
+               # the above store_miss rule was in effect).
+               send_hit deny MapMe !Ordinary
+ DOC_END
+ NAME: store_miss
+ TYPE: acl_access
+ DEFAULT: none
+ DEFAULT_DOC: By default, this directive is unused and has no effect.
+ LOC: Config.accessList.storeMiss
+ DOC_START
+       Responses denied by this directive will not be cached (but may still
+       be served from the cache, see send_hit). This directive has no
+       effect on the responses it allows and on the already cached responses.
+       Please see the "cache" directive for a summary of differences among
+       store_miss, send_hit, and cache directives. See the
+       send_hit directive for a usage example.
+       Unlike the "cache" directive, store_miss only supports fast acl
+       types.  See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details.
  DOC_END
  
  NAME: max_stale
@@@ -5625,25 -5702,6 +5704,25 @@@ DOC_STAR
        or response to be rejected.
  DOC_END
  
 +NAME: collapsed_forwarding
 +COMMENT: (on|off)
 +TYPE: onoff
 +LOC: Config.onoff.collapsed_forwarding
 +DEFAULT: off
 +DOC_START
 +       This option controls whether Squid is allowed to merge multiple
 +       potentially cachable requests for the same URI before Squid knows
 +       whether the response is going to be cachable.
 +
 +       This feature is disabled by default: Enabling collapsed forwarding
 +       needlessly delays forwarding requests that look cachable (when they are
 +       collapsed) but then need to be forwarded individually anyway because
 +       they end up being for uncachable content. However, in some cases, such
 +       as accelleration of highly cachable content with periodic or groupped
 +       expiration times, the gains from collapsing [large volumes of
 +       simultenous refresh requests] outweigh losses from such delays.
 +DOC_END
 +
  COMMENT_START
   TIMEOUTS
   -----------------------------------------------------------------------------
diff --combined src/client_side_reply.cc
index d55272222b308f94b6a470fd846455707d045456,8432693b821fbff090891668c5cb1a6b2f6f2a56..6b57588bba272d93a114aae0ded3ab7e32371232
@@@ -69,6 -69,8 +69,8 @@@
  #include "esi/Esi.h"
  #endif
  
+ #include <memory>
  CBDATA_CLASS_INIT(clientReplyContext);
  
  /* Local functions */
@@@ -138,7 -140,7 +140,7 @@@ void clientReplyContext::setReplyToErro
  
  void clientReplyContext::setReplyToStoreEntry(StoreEntry *entry)
  {
 -    entry->lock(); // removeClientStoreReference() unlocks
 +    entry->lock("clientReplyContext::setReplyToStoreEntry"); // removeClientStoreReference() unlocks
      sc = storeClientListAdd(entry, this);
  #if USE_DELAY_POOLS
      sc->setDelayId(DelayId::DelayClient(http));
@@@ -160,7 -162,7 +162,7 @@@ clientReplyContext::removeStoreReferenc
          *ep = NULL;
          storeUnregister(sc_tmp, e, this);
          *scp = NULL;
 -        e->unlock();
 +        e->unlock("clientReplyContext::removeStoreReference");
      }
  }
  
@@@ -485,8 -487,8 +487,8 @@@ clientReplyContext::cacheHit(StoreIOBuf
       */
      assert(http->logType == LOG_TCP_HIT);
  
 -    if (strcmp(e->mem_obj->url, http->request->storeId()) != 0) {
 -        debugs(33, DBG_IMPORTANT, "clientProcessHit: URL mismatch, '" << e->mem_obj->url << "' != '" << http->request->storeId() << "'");
 +    if (strcmp(e->mem_obj->storeId(), http->request->storeId()) != 0) {
 +        debugs(33, DBG_IMPORTANT, "clientProcessHit: URL mismatch, '" << e->mem_obj->storeId() << "' != '" << http->request->storeId() << "'");
          http->logType = LOG_TCP_MISS; // we lack a more precise LOG_*_MISS code
          processMiss();
          return;
         ) {
          http->logType = LOG_TCP_NEGATIVE_HIT;
          sendMoreData(result);
+     } else if (blockedHit()) {
+         debugs(88, 5, "send_hit forces a MISS");
+         http->logType = LOG_TCP_MISS;
+         processMiss();
+         return;
      } else if (!http->flags.internal && refreshCheckHTTP(e, r)) {
          debugs(88, 5, "clientCacheHit: in refreshCheck() block");
          /*
@@@ -764,6 -771,30 +771,30 @@@ clientReplyContext::processConditional(
      }
  }
  
+ /// whether squid.conf send_hit prevents us from serving this hit
+ bool
+ clientReplyContext::blockedHit() const
+ {
+     if (!Config.accessList.sendHit)
+         return false; // hits are not blocked by default
+     if (http->flags.internal)
+         return false; // internal content "hits" cannot be blocked
+     if (const HttpReply *rep = http->storeEntry()->getReply()) {
+         std::auto_ptr<ACLFilledChecklist> chl(clientAclChecklistCreate(Config.accessList.sendHit, http));
+         chl->reply = const_cast<HttpReply*>(rep); // ACLChecklist API bug
+         HTTPMSGLOCK(chl->reply);
+         return chl->fastCheck() != ACCESS_ALLOWED; // when in doubt, block
+     }
+     // This does not happen, I hope, because we are called from CacheHit, which
+     // is called via a storeClientCopy() callback, and store should initialize
+     // the reply before calling that callback.
+     debugs(88, 3, "Missing reply!");
+     return false;
+ }
  void
  clientReplyContext::purgeRequestFindObjectToPurge()
  {
@@@ -792,7 -823,7 +823,7 @@@ purgeEntriesByUrl(HttpRequest * req, co
      for (HttpRequestMethod m(Http::METHOD_NONE); m != Http::METHOD_ENUM_END; ++m) {
          if (m.respMaybeCacheable()) {
              if (StoreEntry *entry = storeGetPublic(url, m)) {
 -                debugs(88, 5, "purging " << RequestMethodStr(m) << ' ' << url);
 +                debugs(88, 5, "purging " << *entry << ' ' << RequestMethodStr(m) << ' ' << url);
  #if USE_HTCP
                  neighborsHtcpClear(entry, url, req, m, HTCP_CLR_INVALIDATION);
                  if (m == Http::METHOD_GET || m == Http::METHOD_HEAD) {
@@@ -862,16 -893,17 +893,16 @@@ clientReplyContext::purgeFoundObject(St
          ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL,
                                             http->getConn()->clientConnection->remote, http->request);
          startError(err);
 -        return;
 +        return; // XXX: leaking unused entry if some store does not keep it
      }
  
      StoreIOBuffer localTempBuffer;
      /* Swap in the metadata */
      http->storeEntry(entry);
  
 -    http->storeEntry()->lock();
 -    http->storeEntry()->createMemObject(storeId(), http->log_uri);
 -
 -    http->storeEntry()->mem_obj->method = http->request->method;
 +    http->storeEntry()->lock("clientReplyContext::purgeFoundObject");
 +    http->storeEntry()->createMemObject(storeId(), http->log_uri,
 +                                        http->request->method);
  
      sc = storeClientListAdd(http->storeEntry(), this);
  
@@@ -1172,7 -1204,7 +1203,7 @@@ clientReplyContext::replyStatus(
      }
  
      if ((done = checkTransferDone()) != 0 || flags.complete) {
 -        debugs(88, 5, "clientReplyStatus: transfer is DONE");
 +        debugs(88, 5, "clientReplyStatus: transfer is DONE: " << done << flags.complete);
          /* Ok we're finished, but how? */
  
          const int64_t expectedBodySize =
              return STREAM_UNPLANNED_COMPLETE;
          }
  
 -        if (http->request->flags.proxyKeepalive) {
 -            debugs(88, 5, "clientReplyStatus: stream complete and can keepalive");
 -            return STREAM_COMPLETE;
 -        }
 -
 -        debugs(88, 5, "clientReplyStatus: stream was not expected to complete!");
 -        return STREAM_UNPLANNED_COMPLETE;
 +        debugs(88, 5, "clientReplyStatus: stream complete; keepalive=" <<
 +               http->request->flags.proxyKeepalive);
 +        return STREAM_COMPLETE;
      }
  
      // XXX: Should this be checked earlier? We could return above w/o checking.
@@@ -1530,23 -1566,6 +1561,23 @@@ clientReplyContext::cloneReply(
      buildReplyHeader();
  }
  
 +/// Safely disposes of an entry pointing to a cache hit that we do not want.
 +/// We cannot just ignore the entry because it may be locking or otherwise
 +/// holding an associated cache resource of some sort.
 +void
 +clientReplyContext::forgetHit()
 +{
 +    StoreEntry *e = http->storeEntry();
 +    assert(e); // or we are not dealing with a hit
 +    // We probably have not locked the entry earlier, unfortunately. We lock it
 +    // now so that we can unlock two lines later (and trigger cleanup).
 +    // Ideally, ClientHttpRequest::storeEntry() should lock/unlock, but it is
 +    // used so inconsistently that simply adding locking there leads to bugs.
 +    e->lock("clientReplyContext::forgetHit");
 +    http->storeEntry(NULL);
 +    e->unlock("clientReplyContext::forgetHit"); // may delete e
 +}
 +
  void
  clientReplyContext::identifyStoreObject()
  {
@@@ -1592,7 -1611,7 +1623,7 @@@ clientReplyContext::identifyFoundObject
  
      if (NULL == http->storeEntry()) {
          /** \li If no StoreEntry object is current assume this object isn't in the cache set MISS*/
 -        debugs(85, 3, "clientProcessRequest2: StoreEntry is NULL -  MISS");
 +        debugs(85, 3, "StoreEntry is NULL -  MISS");
          http->logType = LOG_TCP_MISS;
          doGetMoreData();
          return;
  
      if (Config.onoff.offline) {
          /** \li If we are running in offline mode set to HIT */
 -        debugs(85, 3, "clientProcessRequest2: offline HIT");
 +        debugs(85, 3, "offline HIT " << *e);
          http->logType = LOG_TCP_HIT;
          doGetMoreData();
          return;
  
      if (http->redirect.status) {
          /** \li If redirection status is True force this to be a MISS */
 -        debugs(85, 3, HERE << "REDIRECT status forced StoreEntry to NULL (no body on 3XX responses)");
 -        http->storeEntry(NULL);
 +        debugs(85, 3, "REDIRECT status forced StoreEntry to NULL (no body on 3XX responses) " << *e);
 +        forgetHit();
          http->logType = LOG_TCP_REDIRECT;
          doGetMoreData();
          return;
      }
  
      if (!e->validToSend()) {
 -        debugs(85, 3, "clientProcessRequest2: !storeEntryValidToSend MISS" );
 -        http->storeEntry(NULL);
 +        debugs(85, 3, "!storeEntryValidToSend MISS " << *e);
 +        forgetHit();
          http->logType = LOG_TCP_MISS;
          doGetMoreData();
          return;
  
      if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
          /* \li Special entries are always hits, no matter what the client says */
 -        debugs(85, 3, "clientProcessRequest2: ENTRY_SPECIAL HIT");
 +        debugs(85, 3, "ENTRY_SPECIAL HIT " << *e);
          http->logType = LOG_TCP_HIT;
          doGetMoreData();
          return;
      }
  
      if (r->flags.noCache) {
 -        debugs(85, 3, "clientProcessRequest2: no-cache REFRESH MISS");
 -        http->storeEntry(NULL);
 +        debugs(85, 3, "no-cache REFRESH MISS " << *e);
 +        forgetHit();
          http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
          doGetMoreData();
          return;
      }
  
 -    debugs(85, 3, "clientProcessRequest2: default HIT");
 +    debugs(85, 3, "default HIT " << *e);
      http->logType = LOG_TCP_HIT;
      doGetMoreData();
  }
@@@ -1711,10 -1730,9 +1742,10 @@@ clientReplyContext::doGetMoreData(
          /* someone found the object in the cache for us */
          StoreIOBuffer localTempBuffer;
  
 -        http->storeEntry()->lock();
 +        http->storeEntry()->lock("clientReplyContext::doGetMoreData");
  
 -        if (http->storeEntry()->mem_obj == NULL) {
 +        MemObject *mem_obj = http->storeEntry()->makeMemObject();
 +        if (!mem_obj->hasUris()) {
              /*
               * This if-block exists because we don't want to clobber
               * a preexiting mem_obj->method value if the mem_obj
               * is a cache hit for a GET response, we want to keep
               * the method as GET.
               */
 -            http->storeEntry()->createMemObject(storeId(), http->log_uri);
 -            http->storeEntry()->mem_obj->method = http->request->method;
 +            mem_obj->setUris(storeId(), http->log_uri, http->request->method);
              /**
               * Here we can see if the object was
               * created using URL or alternative StoreID from helper.
               */
 -            debugs(88, 3, "mem_obj->url: " << http->storeEntry()->mem_obj->url);
 +            debugs(88, 3, "storeId: " << http->storeEntry()->mem_obj->storeId());
          }
  
          sc = storeClientListAdd(http->storeEntry(), this);
@@@ -2163,16 -2182,6 +2194,16 @@@ clientReplyContext::createStoreEntry(co
  
      StoreEntry *e = storeCreateEntry(storeId(), http->log_uri, reqFlags, m);
  
 +    // Make entry collapsable ASAP, to increase collapsing chances for others,
 +    // TODO: every must-revalidate and similar request MUST reach the origin,
 +    // but do we have to prohibit others from collapsing on that request?
 +    if (Config.onoff.collapsed_forwarding && reqFlags.cachable &&
 +            !reqFlags.needValidation &&
 +            (m == Http::METHOD_GET || m == Http::METHOD_HEAD)) {
 +        // make the entry available for future requests now
 +        Store::Root().allowCollapsing(e, reqFlags, m);
 +    }
 +
      sc = storeClientListAdd(e, this);
  
  #if USE_DELAY_POOLS
diff --combined src/client_side_reply.h
index ac1e369ceaf0a6b44f36bf70d0d18c0515a2746b,7cebe1cc7bcbc5895cc902e479f9fd72a427a337..29f72ac71a500c22cc1876ad6b992d36c4ade723
@@@ -140,7 -140,7 +140,8 @@@ private
      void triggerInitialStoreRead();
      void sendClientOldEntry();
      void purgeAllCached();
 +    void forgetHit();
+     bool blockedHit() const;
  
      void sendBodyTooLargeError();
      void sendPreconditionFailedError();
diff --combined src/format/Format.cc
index 366a8b116308930682456517be2d3bddfb8aec9b,f6d903c6f27bd1b86a610e5ec8cb86f5f127027f..f15ff7596c85e73052c27fc9b8fcafd3cdc61e29
@@@ -496,11 -496,11 +496,11 @@@ Format::Format::assemble(MemBuf &mb, co
          break;
  
          case LFT_TIME_START: {
-             int precision = fmt->widthMax >=0 ? fmt->widthMax :3;
-             snprintf(tmp, sizeof(tmp), "%0*" PRId64 ".%0*d", fmt->zero && (fmt->widthMin - precision - 1 >= 0) ? fmt->widthMin - precision - 1 : 0, (int64_t)al->cache.start_time.tv_sec, precision, (int)(al->cache.start_time.tv_usec / fmt->divisor));
+             int precision = fmt->widthMax >=0 ? fmt->widthMax : 3;
+             snprintf(tmp, sizeof(tmp), "%0*" PRId64 ".%0*d", fmt->zero && (fmt->widthMin - precision - 1 >= 0) ? fmt->widthMin - precision - 1 : 0, static_cast<int64_t>(al->cache.start_time.tv_sec), precision, (int)(al->cache.start_time.tv_usec / fmt->divisor));
              out = tmp;
          }
 -            break;
 +        break;
  
          case LFT_TIME_TO_HANDLE_REQUEST:
              outint = al->cache.msec;
diff --combined src/ftp.cc
index a866282b77b8aa99a0ce9f651aaba27e1de266b5,71f3d30359c9eac31a75c435512f6da94493fea2..05275efaa8916a780f6439f2632c4de0e816e7ac
@@@ -3051,6 -3051,13 +3051,13 @@@ void FtpStateData::readStor(
      debugs(9, 3, HERE);
  
      if (code == 125 || (code == 150 && Comm::IsConnOpen(data.conn))) {
+         if (!originalRequest()->body_pipe) {
+             debugs(9, 3, "zero-size STOR?");
+             state = WRITING_DATA; // make ftpWriteTransferDone() responsible
+             dataComplete(); // XXX: keep in sync with doneSendingRequestBody()
+             return;
+         }
          if (!startRequestBodyFlow()) { // register to receive body data
              ftpFail(this);
              return;
@@@ -3256,7 -3263,7 +3263,7 @@@ voi
  FtpStateData::completedListing()
  {
      assert(entry);
 -    entry->lock();
 +    entry->lock("FtpStateData");
      ErrorState ferr(ERR_DIR_LISTING, Http::scOkay, request);
      ferr.ftp.listing = &listing;
      ferr.ftp.cwd_msg = xstrdup(cwd_message.size()? cwd_message.termedBuf() : "");
      entry->replaceHttpReply( ferr.BuildHttpReply() );
      EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
      entry->flush();
 -    entry->unlock();
 +    entry->unlock("FtpStateData");
  }
  
  /// \ingroup ServerProtocolFTPInternal
@@@ -3683,7 -3690,7 +3690,7 @@@ FtpStateData::haveParsedReplyHeaders(
           * Authenticated requests can't be cached.
           */
          e->release();
 -    } else if (EBIT_TEST(e->flags, ENTRY_CACHABLE) && !getCurrentOffset()) {
 +    } else if (!EBIT_TEST(e->flags, RELEASE_REQUEST) && !getCurrentOffset()) {
          e->setPublicKey();
      } else {
          e->release();