diff -u -r -N squid-3.5.21/ChangeLog squid-3.5.22/ChangeLog
--- squid-3.5.21/ChangeLog	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/ChangeLog	2016-10-10 08:58:01.000000000 +1300
@@ -1,3 +1,17 @@
+Changes to squid-3.5.22 (09 Oct 2016):
+
+	- Bug 4594: build failure with clang 3.9
+	- Bug 4471: revalidation does not work when expired cached object lacks Last-Modified
+	- Bug 4302 pt2: IPv6 support for IPFilter v5 transparent interception
+	- Bug 4228: ./configure bug/typo in r14394
+	- Bug 3819: "fd >= 0" assertion in file_write() during reconfiguration
+	- Bug 2833: Collapse internal revalidation requests (SMP-unaware caches)
+	- Fix logged request size (%http::>st) and other size-related %codes
+	- Fix some memory leaks from putenv()
+	- Fix memory leaks from url_rewrite_extras and store_id_extras on reconfigure/shutdown
+	- Fix segfault crash when debugging section 4 at level 9
+	- HTTP: MUST ignore a [revalidation] response with an older Date header
+
 Changes to squid-3.5.21 (08 Sep 2016):
 
 	- Bug 4563: duplicate code in httpMakeVaryMark
diff -u -r -N squid-3.5.21/configure squid-3.5.22/configure
--- squid-3.5.21/configure	2016-09-09 07:00:42.000000000 +1200
+++ squid-3.5.22/configure	2016-10-10 09:04:24.000000000 +1300
@@ -1,7 +1,7 @@
 #! /bin/sh
 # From configure.ac Revision.
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for Squid Web Proxy 3.5.21.
+# Generated by GNU Autoconf 2.69 for Squid Web Proxy 3.5.22.
 #
 # Report bugs to <http://bugs.squid-cache.org/>.
 #
@@ -595,8 +595,8 @@
 # Identity of this package.
 PACKAGE_NAME='Squid Web Proxy'
 PACKAGE_TARNAME='squid'
-PACKAGE_VERSION='3.5.21'
-PACKAGE_STRING='Squid Web Proxy 3.5.21'
+PACKAGE_VERSION='3.5.22'
+PACKAGE_STRING='Squid Web Proxy 3.5.22'
 PACKAGE_BUGREPORT='http://bugs.squid-cache.org/'
 PACKAGE_URL=''
 
@@ -1636,7 +1636,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures Squid Web Proxy 3.5.21 to adapt to many kinds of systems.
+\`configure' configures Squid Web Proxy 3.5.22 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1707,7 +1707,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Squid Web Proxy 3.5.21:";;
+     short | recursive ) echo "Configuration of Squid Web Proxy 3.5.22:";;
    esac
   cat <<\_ACEOF
 
@@ -2119,7 +2119,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Squid Web Proxy configure 3.5.21
+Squid Web Proxy configure 3.5.22
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -3223,7 +3223,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by Squid Web Proxy $as_me 3.5.21, which was
+It was created by Squid Web Proxy $as_me 3.5.22, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -4090,7 +4090,7 @@
 
 # Define the identity of the package.
  PACKAGE='squid'
- VERSION='3.5.21'
+ VERSION='3.5.22'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -30339,7 +30339,7 @@
 
   fi
   if test "x$KRB5LIBS" = "x"; then
-    if test test "x$with_heimdal_krb5" = "xyes"; then
+    if test "x$with_heimdal_krb5" = "xyes"; then
       as_fn_error $? "Required Heimdal Kerberos library not found" "$LINENO" 5
     else
       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Heimdal Kerberos library not found" >&5
@@ -30857,7 +30857,7 @@
 
   fi
   if test "x$KRB5LIBS" = "x"; then
-    if test test "x$with_gnugss" = "xyes"; then
+    if test "x$with_gnugss" = "xyes"; then
       as_fn_error $? "Required GNU GSS Kerberos library not found" "$LINENO" 5
     else
       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: GNU GSS Kerberos library not found" >&5
@@ -41876,7 +41876,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by Squid Web Proxy $as_me 3.5.21, which was
+This file was extended by Squid Web Proxy $as_me 3.5.22, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -41942,7 +41942,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-Squid Web Proxy config.status 3.5.21
+Squid Web Proxy config.status 3.5.22
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff -u -r -N squid-3.5.21/configure.ac squid-3.5.22/configure.ac
--- squid-3.5.21/configure.ac	2016-09-09 07:00:41.000000000 +1200
+++ squid-3.5.22/configure.ac	2016-10-10 09:04:22.000000000 +1300
@@ -5,7 +5,7 @@
 ## Please see the COPYING and CONTRIBUTORS files for details.
 ##
 
-AC_INIT([Squid Web Proxy],[3.5.21],[http://bugs.squid-cache.org/],[squid])
+AC_INIT([Squid Web Proxy],[3.5.22],[http://bugs.squid-cache.org/],[squid])
 AC_PREREQ(2.61)
 AC_CONFIG_HEADERS([include/autoconf.h])
 AC_CONFIG_AUX_DIR(cfgaux)
@@ -1778,7 +1778,7 @@
     SQUID_CHECK_KRB5_FUNCS
   fi
   if test "x$KRB5LIBS" = "x"; then
-    if test test "x$with_heimdal_krb5" = "xyes"; then
+    if test "x$with_heimdal_krb5" = "xyes"; then
       AC_MSG_ERROR([Required Heimdal Kerberos library not found])
     else
       AC_MSG_WARN([Heimdal Kerberos library not found])
@@ -1848,7 +1848,7 @@
     SQUID_DEFINE_BOOL(HAVE_KRB5,$squid_cv_working_krb5,[KRB5 support])
   fi
   if test "x$KRB5LIBS" = "x"; then
-    if test test "x$with_gnugss" = "xyes"; then
+    if test "x$with_gnugss" = "xyes"; then
       AC_MSG_ERROR([Required GNU GSS Kerberos library not found])
     else
       AC_MSG_WARN([GNU GSS Kerberos library not found])
diff -u -r -N squid-3.5.21/doc/release-notes/release-3.5.html squid-3.5.22/doc/release-notes/release-3.5.html
--- squid-3.5.21/doc/release-notes/release-3.5.html	2016-09-09 08:06:08.000000000 +1200
+++ squid-3.5.22/doc/release-notes/release-3.5.html	2016-10-10 12:34:07.000000000 +1300
@@ -2,10 +2,10 @@
 <HTML>
 <HEAD>
  <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.72">
- <TITLE>Squid 3.5.21 release notes</TITLE>
+ <TITLE>Squid 3.5.22 release notes</TITLE>
 </HEAD>
 <BODY>
-<H1>Squid 3.5.21 release notes</H1>
+<H1>Squid 3.5.22 release notes</H1>
 
 <H2>Squid Developers</H2>
 <HR>
@@ -64,7 +64,7 @@
 <HR>
 <H2><A NAME="s1">1.</A> <A HREF="#toc1">Notice</A></H2>
 
-<P>The Squid Team are pleased to announce the release of Squid-3.5.21.</P>
+<P>The Squid Team are pleased to announce the release of Squid-3.5.22.</P>
 <P>This new release is available for download from 
 <A HREF="http://www.squid-cache.org/Versions/v3/3.5/">http://www.squid-cache.org/Versions/v3/3.5/</A> or the
 <A HREF="http://www.squid-cache.org/Download/http-mirrors.html">mirrors</A>.</P>
diff -u -r -N squid-3.5.21/helpers/basic_auth/DB/basic_db_auth.8 squid-3.5.22/helpers/basic_auth/DB/basic_db_auth.8
--- squid-3.5.21/helpers/basic_auth/DB/basic_db_auth.8	2016-09-09 08:06:12.000000000 +1200
+++ squid-3.5.22/helpers/basic_auth/DB/basic_db_auth.8	2016-10-10 12:34:14.000000000 +1300
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
+.\" Automatically generated by Pod::Man 4.08 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -46,7 +46,7 @@
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" If the F register is >0, we'll generate index entries on stderr for
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
@@ -54,20 +54,16 @@
 .\" Avoid warning from groff about undefined register 'F'.
 .de IX
 ..
-.nr rF 0
-.if \n(.g .if rF .nr rF 1
-.if (\n(rF:(\n(.g==0)) \{
-.    if \nF \{
-.        de IX
-.        tm Index:\\$1\t\\n%\t"\\$2"
+.if !\nF .nr F 0
+.if \nF>0 \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
 ..
-.        if !\nF==2 \{
-.            nr % 0
-.            nr F 2
-.        \}
+.    if !\nF==2 \{\
+.        nr % 0
+.        nr F 2
 .    \}
 .\}
-.rr rF
 .\"
 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
 .\" Fear.  Run.  Save yourself.  No user-serviceable parts.
@@ -133,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "BASIC_DB_AUTH 8"
-.TH BASIC_DB_AUTH 8 "2016-09-08" "perl v5.22.2" "User Contributed Perl Documentation"
+.TH BASIC_DB_AUTH 8 "2016-10-09" "perl v5.24.1" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -191,7 +187,7 @@
 Keep a persistent database connection open between queries.
 .IP "\fB\-\-joomla\fR" 12
 .IX Item "--joomla"
-Tells helper that user database is Joomla \s-1DB. \s0 So their unusual salt 
+Tells helper that user database is Joomla \s-1DB.\s0  So their unusual salt 
 hashing is understood.
 .SH "AUTHOR"
 .IX Header "AUTHOR"
@@ -230,7 +226,7 @@
 Report ideas for new improvements to the \fISquid Developers mailing list <squid\-dev@squid\-cache.org\fR>
 .SH "SEE ALSO"
 .IX Header "SEE ALSO"
-squid (8), \s-1GPL \\fIs0\fR\|(7),
+squid (8), \s-1GPL\s0 (7),
 .PP
 The Squid \s-1FAQ\s0 wiki http://wiki.squid\-cache.org/SquidFaq
 .PP
diff -u -r -N squid-3.5.21/helpers/basic_auth/MSNT-multi-domain/basic_msnt_multi_domain_auth.8 squid-3.5.22/helpers/basic_auth/MSNT-multi-domain/basic_msnt_multi_domain_auth.8
--- squid-3.5.21/helpers/basic_auth/MSNT-multi-domain/basic_msnt_multi_domain_auth.8	2016-09-09 08:06:18.000000000 +1200
+++ squid-3.5.22/helpers/basic_auth/MSNT-multi-domain/basic_msnt_multi_domain_auth.8	2016-10-10 12:34:22.000000000 +1300
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
+.\" Automatically generated by Pod::Man 4.08 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -46,7 +46,7 @@
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" If the F register is >0, we'll generate index entries on stderr for
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
@@ -54,20 +54,16 @@
 .\" Avoid warning from groff about undefined register 'F'.
 .de IX
 ..
-.nr rF 0
-.if \n(.g .if rF .nr rF 1
-.if (\n(rF:(\n(.g==0)) \{
-.    if \nF \{
-.        de IX
-.        tm Index:\\$1\t\\n%\t"\\$2"
+.if !\nF .nr F 0
+.if \nF>0 \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
 ..
-.        if !\nF==2 \{
-.            nr % 0
-.            nr F 2
-.        \}
+.    if !\nF==2 \{\
+.        nr % 0
+.        nr F 2
 .    \}
 .\}
-.rr rF
 .\"
 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
 .\" Fear.  Run.  Save yourself.  No user-serviceable parts.
@@ -133,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "BASIC_MSNT_MULTI_DOMAIN_AUTH 1"
-.TH BASIC_MSNT_MULTI_DOMAIN_AUTH 1 "2016-09-08" "perl v5.22.2" "User Contributed Perl Documentation"
+.TH BASIC_MSNT_MULTI_DOMAIN_AUTH 1 "2016-10-09" "perl v5.24.1" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -205,7 +201,7 @@
 Report ideas for new improvements to the \fISquid Developers mailing list <squid\-dev@squid\-cache.org\fR>
 .SH "SEE ALSO"
 .IX Header "SEE ALSO"
-squid (8), \s-1GPL \\fIs0\fR\|(7),
+squid (8), \s-1GPL\s0 (7),
 .PP
 The Squid \s-1FAQ\s0 wiki http://wiki.squid\-cache.org/SquidFaq
 .PP
diff -u -r -N squid-3.5.21/helpers/basic_auth/POP3/basic_pop3_auth.8 squid-3.5.22/helpers/basic_auth/POP3/basic_pop3_auth.8
--- squid-3.5.21/helpers/basic_auth/POP3/basic_pop3_auth.8	2016-09-09 08:06:24.000000000 +1200
+++ squid-3.5.22/helpers/basic_auth/POP3/basic_pop3_auth.8	2016-10-10 12:34:31.000000000 +1300
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
+.\" Automatically generated by Pod::Man 4.08 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -46,7 +46,7 @@
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" If the F register is >0, we'll generate index entries on stderr for
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
@@ -54,20 +54,16 @@
 .\" Avoid warning from groff about undefined register 'F'.
 .de IX
 ..
-.nr rF 0
-.if \n(.g .if rF .nr rF 1
-.if (\n(rF:(\n(.g==0)) \{
-.    if \nF \{
-.        de IX
-.        tm Index:\\$1\t\\n%\t"\\$2"
+.if !\nF .nr F 0
+.if \nF>0 \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
 ..
-.        if !\nF==2 \{
-.            nr % 0
-.            nr F 2
-.        \}
+.    if !\nF==2 \{\
+.        nr % 0
+.        nr F 2
 .    \}
 .\}
-.rr rF
 .\"
 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
 .\" Fear.  Run.  Save yourself.  No user-serviceable parts.
@@ -133,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "BASIC_POP3_AUTH 8"
-.TH BASIC_POP3_AUTH 8 "2016-09-08" "perl v5.22.2" "User Contributed Perl Documentation"
+.TH BASIC_POP3_AUTH 8 "2016-10-09" "perl v5.24.1" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -202,7 +198,7 @@
 Report ideas for new improvements to the \fISquid Developers mailing list <squid\-dev@squid\-cache.org\fR>
 .SH "SEE ALSO"
 .IX Header "SEE ALSO"
-squid (8), \s-1GPL \\fIs0\fR\|(7),
+squid (8), \s-1GPL\s0 (7),
 .PP
 The Squid \s-1FAQ\s0 wiki http://wiki.squid\-cache.org/SquidFaq
 .PP
diff -u -r -N squid-3.5.21/helpers/external_acl/delayer/ext_delayer_acl.8 squid-3.5.22/helpers/external_acl/delayer/ext_delayer_acl.8
--- squid-3.5.21/helpers/external_acl/delayer/ext_delayer_acl.8	2016-09-09 08:06:41.000000000 +1200
+++ squid-3.5.22/helpers/external_acl/delayer/ext_delayer_acl.8	2016-10-10 12:34:52.000000000 +1300
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
+.\" Automatically generated by Pod::Man 4.08 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -46,7 +46,7 @@
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" If the F register is >0, we'll generate index entries on stderr for
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
@@ -54,20 +54,16 @@
 .\" Avoid warning from groff about undefined register 'F'.
 .de IX
 ..
-.nr rF 0
-.if \n(.g .if rF .nr rF 1
-.if (\n(rF:(\n(.g==0)) \{
-.    if \nF \{
-.        de IX
-.        tm Index:\\$1\t\\n%\t"\\$2"
+.if !\nF .nr F 0
+.if \nF>0 \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
 ..
-.        if !\nF==2 \{
-.            nr % 0
-.            nr F 2
-.        \}
+.    if !\nF==2 \{\
+.        nr % 0
+.        nr F 2
 .    \}
 .\}
-.rr rF
 .\"
 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
 .\" Fear.  Run.  Save yourself.  No user-serviceable parts.
@@ -133,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "EXT_DELAYER_ACL 8"
-.TH EXT_DELAYER_ACL 8 "2016-09-08" "perl v5.22.2" "User Contributed Perl Documentation"
+.TH EXT_DELAYER_ACL 8 "2016-10-09" "perl v5.24.1" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -225,7 +221,7 @@
 Report ideas for new improvements to the \fISquid Developers mailing list <squid\-dev@squid\-cache.org\fR>
 .SH "SEE ALSO"
 .IX Header "SEE ALSO"
-squid (8), \s-1GPL \\fIs0\fR\|(7),
+squid (8), \s-1GPL\s0 (7),
 .PP
 The Squid \s-1FAQ\s0 wiki http://wiki.squid\-cache.org/SquidFaq
 .PP
diff -u -r -N squid-3.5.21/helpers/external_acl/SQL_session/ext_sql_session_acl.8 squid-3.5.22/helpers/external_acl/SQL_session/ext_sql_session_acl.8
--- squid-3.5.21/helpers/external_acl/SQL_session/ext_sql_session_acl.8	2016-09-09 08:06:50.000000000 +1200
+++ squid-3.5.22/helpers/external_acl/SQL_session/ext_sql_session_acl.8	2016-10-10 12:35:06.000000000 +1300
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
+.\" Automatically generated by Pod::Man 4.08 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -46,7 +46,7 @@
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" If the F register is >0, we'll generate index entries on stderr for
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
@@ -54,20 +54,16 @@
 .\" Avoid warning from groff about undefined register 'F'.
 .de IX
 ..
-.nr rF 0
-.if \n(.g .if rF .nr rF 1
-.if (\n(rF:(\n(.g==0)) \{
-.    if \nF \{
-.        de IX
-.        tm Index:\\$1\t\\n%\t"\\$2"
+.if !\nF .nr F 0
+.if \nF>0 \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
 ..
-.        if !\nF==2 \{
-.            nr % 0
-.            nr F 2
-.        \}
+.    if !\nF==2 \{\
+.        nr % 0
+.        nr F 2
 .    \}
 .\}
-.rr rF
 .\"
 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
 .\" Fear.  Run.  Save yourself.  No user-serviceable parts.
@@ -133,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "EXT_SQL_SESSION_ACL 8"
-.TH EXT_SQL_SESSION_ACL 8 "2016-09-08" "perl v5.22.2" "User Contributed Perl Documentation"
+.TH EXT_SQL_SESSION_ACL 8 "2016-10-09" "perl v5.24.1" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -154,7 +150,7 @@
 Taking an identity token to be validated (as determined by the external_acl_type format)
 it returns a username or tag associated with the identity token passed in.
 .PP
-Common forms of identifiers are \s-1IP\s0 address, \s-1EUI \s0(\s-1MAC\s0) address, passwords, or \s-1UUID\s0 tokens.
+Common forms of identifiers are \s-1IP\s0 address, \s-1EUI\s0 (\s-1MAC\s0) address, passwords, or \s-1UUID\s0 tokens.
 .PP
 This program uses Squid concurrency support.
 .SH "OPTIONS"
@@ -225,7 +221,7 @@
 Report ideas for new improvements to the \fISquid Developers mailing list <squid\-dev@squid\-cache.org\fR>
 .SH "SEE ALSO"
 .IX Header "SEE ALSO"
-squid (8), \s-1GPL \\fIs0\fR\|(7),
+squid (8), \s-1GPL\s0 (7),
 .PP
 The Squid \s-1FAQ\s0 wiki http://wiki.squid\-cache.org/SquidFaq
 .PP
diff -u -r -N squid-3.5.21/helpers/external_acl/wbinfo_group/ext_wbinfo_group_acl.8 squid-3.5.22/helpers/external_acl/wbinfo_group/ext_wbinfo_group_acl.8
--- squid-3.5.21/helpers/external_acl/wbinfo_group/ext_wbinfo_group_acl.8	2016-09-09 08:06:55.000000000 +1200
+++ squid-3.5.22/helpers/external_acl/wbinfo_group/ext_wbinfo_group_acl.8	2016-10-10 12:35:12.000000000 +1300
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
+.\" Automatically generated by Pod::Man 4.08 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -46,7 +46,7 @@
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" If the F register is >0, we'll generate index entries on stderr for
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
@@ -54,20 +54,16 @@
 .\" Avoid warning from groff about undefined register 'F'.
 .de IX
 ..
-.nr rF 0
-.if \n(.g .if rF .nr rF 1
-.if (\n(rF:(\n(.g==0)) \{
-.    if \nF \{
-.        de IX
-.        tm Index:\\$1\t\\n%\t"\\$2"
+.if !\nF .nr F 0
+.if \nF>0 \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
 ..
-.        if !\nF==2 \{
-.            nr % 0
-.            nr F 2
-.        \}
+.    if !\nF==2 \{\
+.        nr % 0
+.        nr F 2
 .    \}
 .\}
-.rr rF
 .\"
 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
 .\" Fear.  Run.  Save yourself.  No user-serviceable parts.
@@ -133,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "EXT_WBINFO_GROUP_ACL 8"
-.TH EXT_WBINFO_GROUP_ACL 8 "2016-09-08" "perl v5.22.2" "User Contributed Perl Documentation"
+.TH EXT_WBINFO_GROUP_ACL 8 "2016-10-09" "perl v5.24.1" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-3.5.21/helpers/log_daemon/DB/log_db_daemon.8 squid-3.5.22/helpers/log_daemon/DB/log_db_daemon.8
--- squid-3.5.21/helpers/log_daemon/DB/log_db_daemon.8	2016-09-09 08:06:58.000000000 +1200
+++ squid-3.5.22/helpers/log_daemon/DB/log_db_daemon.8	2016-10-10 12:35:15.000000000 +1300
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
+.\" Automatically generated by Pod::Man 4.08 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -46,7 +46,7 @@
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" If the F register is >0, we'll generate index entries on stderr for
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
@@ -54,20 +54,16 @@
 .\" Avoid warning from groff about undefined register 'F'.
 .de IX
 ..
-.nr rF 0
-.if \n(.g .if rF .nr rF 1
-.if (\n(rF:(\n(.g==0)) \{
-.    if \nF \{
-.        de IX
-.        tm Index:\\$1\t\\n%\t"\\$2"
+.if !\nF .nr F 0
+.if \nF>0 \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
 ..
-.        if !\nF==2 \{
-.            nr % 0
-.            nr F 2
-.        \}
+.    if !\nF==2 \{\
+.        nr % 0
+.        nr F 2
 .    \}
 .\}
-.rr rF
 .\"
 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
 .\" Fear.  Run.  Save yourself.  No user-serviceable parts.
@@ -133,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "LOG_DB_DAEMON 8"
-.TH LOG_DB_DAEMON 8 "2016-09-08" "perl v5.22.2" "User Contributed Perl Documentation"
+.TH LOG_DB_DAEMON 8 "2016-10-09" "perl v5.24.1" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -u -r -N squid-3.5.21/helpers/storeid_rewrite/file/storeid_file_rewrite.8 squid-3.5.22/helpers/storeid_rewrite/file/storeid_file_rewrite.8
--- squid-3.5.21/helpers/storeid_rewrite/file/storeid_file_rewrite.8	2016-09-09 08:07:15.000000000 +1200
+++ squid-3.5.22/helpers/storeid_rewrite/file/storeid_file_rewrite.8	2016-10-10 12:35:38.000000000 +1300
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
+.\" Automatically generated by Pod::Man 4.08 (Pod::Simple 3.32)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -46,7 +46,7 @@
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" If the F register is >0, we'll generate index entries on stderr for
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
@@ -54,20 +54,16 @@
 .\" Avoid warning from groff about undefined register 'F'.
 .de IX
 ..
-.nr rF 0
-.if \n(.g .if rF .nr rF 1
-.if (\n(rF:(\n(.g==0)) \{
-.    if \nF \{
-.        de IX
-.        tm Index:\\$1\t\\n%\t"\\$2"
+.if !\nF .nr F 0
+.if \nF>0 \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
 ..
-.        if !\nF==2 \{
-.            nr % 0
-.            nr F 2
-.        \}
+.    if !\nF==2 \{\
+.        nr % 0
+.        nr F 2
 .    \}
 .\}
-.rr rF
 .\"
 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
 .\" Fear.  Run.  Save yourself.  No user-serviceable parts.
@@ -133,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "STOREID_FILE_REWRITE 8"
-.TH STOREID_FILE_REWRITE 8 "2016-09-08" "perl v5.22.2" "User Contributed Perl Documentation"
+.TH STOREID_FILE_REWRITE 8 "2016-10-09" "perl v5.24.1" "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -213,7 +209,7 @@
 Report ideas for new improvements to the \fISquid Developers mailing list <squid\-dev@squid\-cache.org\fR>
 .SH "SEE ALSO"
 .IX Header "SEE ALSO"
-squid (8), \s-1GPL \\fIs0\fR\|(7),
+squid (8), \s-1GPL\s0 (7),
 .PP
 The Squid wiki http://wiki.squid\-cache.org/Features/StoreID
 .PP
diff -u -r -N squid-3.5.21/include/version.h squid-3.5.22/include/version.h
--- squid-3.5.21/include/version.h	2016-09-09 07:00:42.000000000 +1200
+++ squid-3.5.22/include/version.h	2016-10-10 09:04:24.000000000 +1300
@@ -7,7 +7,7 @@
  */
 
 #ifndef SQUID_RELEASE_TIME
-#define SQUID_RELEASE_TIME 1473360998
+#define SQUID_RELEASE_TIME 1476043069
 #endif
 
 /*
diff -u -r -N squid-3.5.21/RELEASENOTES.html squid-3.5.22/RELEASENOTES.html
--- squid-3.5.21/RELEASENOTES.html	2016-09-09 08:06:08.000000000 +1200
+++ squid-3.5.22/RELEASENOTES.html	2016-10-10 12:34:07.000000000 +1300
@@ -2,10 +2,10 @@
 <HTML>
 <HEAD>
  <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.72">
- <TITLE>Squid 3.5.21 release notes</TITLE>
+ <TITLE>Squid 3.5.22 release notes</TITLE>
 </HEAD>
 <BODY>
-<H1>Squid 3.5.21 release notes</H1>
+<H1>Squid 3.5.22 release notes</H1>
 
 <H2>Squid Developers</H2>
 <HR>
@@ -64,7 +64,7 @@
 <HR>
 <H2><A NAME="s1">1.</A> <A HREF="#toc1">Notice</A></H2>
 
-<P>The Squid Team are pleased to announce the release of Squid-3.5.21.</P>
+<P>The Squid Team are pleased to announce the release of Squid-3.5.22.</P>
 <P>This new release is available for download from 
 <A HREF="http://www.squid-cache.org/Versions/v3/3.5/">http://www.squid-cache.org/Versions/v3/3.5/</A> or the
 <A HREF="http://www.squid-cache.org/Download/http-mirrors.html">mirrors</A>.</P>
diff -u -r -N squid-3.5.21/src/adaptation/ecap/XactionRep.cc squid-3.5.22/src/adaptation/ecap/XactionRep.cc
--- squid-3.5.21/src/adaptation/ecap/XactionRep.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/adaptation/ecap/XactionRep.cc	2016-10-10 08:58:01.000000000 +1300
@@ -726,7 +726,7 @@
             buf.append(" A?", 3);
     }
 
-    buf.Printf(" %s%u]", id.Prefix, id.value);
+    buf.Printf(" %s%u]", id.prefix(), id.value);
 
     buf.terminate();
 
diff -u -r -N squid-3.5.21/src/adaptation/History.cc squid-3.5.22/src/adaptation/History.cc
--- squid-3.5.21/src/adaptation/History.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/adaptation/History.cc	2016-10-10 08:58:01.000000000 +1300
@@ -150,9 +150,9 @@
 void Adaptation::History::recordMeta(const HttpHeader *lm)
 {
     lastMeta.clean();
-    lastMeta.update(lm, NULL);
+    lastMeta.update(lm);
 
-    allMeta.update(lm, NULL);
+    allMeta.update(lm);
     allMeta.compact();
 }
 
diff -u -r -N squid-3.5.21/src/adaptation/icap/ModXact.cc squid-3.5.22/src/adaptation/icap/ModXact.cc
--- squid-3.5.21/src/adaptation/icap/ModXact.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/adaptation/icap/ModXact.cc	2016-10-10 08:58:01.000000000 +1300
@@ -1259,31 +1259,32 @@
 
 void Adaptation::Icap::ModXact::finalizeLogInfo()
 {
-    HttpRequest * request_ = NULL;
-    HttpRequest * adapted_request_ = NULL;
-    HttpReply * reply_ = NULL;
-    request_ = (virgin.cause? virgin.cause: dynamic_cast<HttpRequest*>(virgin.header));
+    HttpRequest *adapted_request_ = NULL;
+    HttpReply *adapted_reply_ = NULL;
+    HttpRequest *virgin_request_ = const_cast<HttpRequest*>(&virginRequest());
     if (!(adapted_request_ = dynamic_cast<HttpRequest*>(adapted.header))) {
-        adapted_request_ = request_;
-        reply_ = dynamic_cast<HttpReply*>(adapted.header);
+        // if the request was not adapted, use virgin request to simplify
+        // the code further below
+        adapted_request_ = virgin_request_;
+        adapted_reply_ = dynamic_cast<HttpReply*>(adapted.header);
     }
 
-    Adaptation::Icap::History::Pointer h = (request_ ? request_->icapHistory() : NULL);
+    Adaptation::Icap::History::Pointer h = virgin_request_->icapHistory();
     Must(h != NULL); // ICAPXaction::maybeLog calls only if there is a log
     al.icp.opcode = ICP_INVALID;
     al.url = h->log_uri.termedBuf();
     const Adaptation::Icap::ServiceRep  &s = service();
     al.icap.reqMethod = s.cfg().method;
 
-    al.cache.caddr = request_->client_addr;
+    al.cache.caddr = virgin_request_->client_addr;
 
-    al.request = request_;
+    al.request = virgin_request_;
     HTTPMSGLOCK(al.request);
     al.adapted_request = adapted_request_;
     HTTPMSGLOCK(al.adapted_request);
 
-    if (reply_) {
-        al.reply = reply_;
+    if (adapted_reply_) {
+        al.reply = adapted_reply_;
         HTTPMSGLOCK(al.reply);
     } else
         al.reply = NULL;
@@ -1296,25 +1297,29 @@
         al.cache.ssluser = h->ssluser.termedBuf();
 #endif
     al.cache.code = h->logType;
-    // XXX: should use icap-specific counters instead ?
-    al.http.clientRequestSz.payloadData = h->req_sz;
+
+    const HttpMsg *virgin_msg = dynamic_cast<HttpReply*>(virgin.header);
+    if (!virgin_msg)
+        virgin_msg = virgin_request_;
+    assert(virgin_msg != virgin.cause);
+    al.http.clientRequestSz.header = virgin_msg->hdr_sz;
+    al.http.clientRequestSz.payloadData = virgin_msg->body_pipe->producedSize();
 
     // leave al.icap.bodyBytesRead negative if no body
     if (replyHttpHeaderSize >= 0 || replyHttpBodySize >= 0) {
         const int64_t zero = 0; // to make max() argument types the same
-        al.icap.bodyBytesRead =
-            max(zero, replyHttpHeaderSize) + max(zero, replyHttpBodySize);
+        const uint64_t headerSize = max(zero, replyHttpHeaderSize);
+        const uint64_t bodySize =  max(zero, replyHttpBodySize);
+        al.icap.bodyBytesRead = headerSize + bodySize;
+        al.http.clientReplySz.header = headerSize;
+        al.http.clientReplySz.payloadData = bodySize;
     }
 
-    if (reply_) {
-        al.http.code = reply_->sline.status();
-        al.http.content_type = reply_->content_type.termedBuf();
-        if (replyHttpBodySize >= 0) {
-            // XXX: should use icap-specific counters instead ?
-            al.http.clientReplySz.payloadData = replyHttpBodySize;
-            al.http.clientReplySz.header =  reply_->hdr_sz;
+    if (adapted_reply_) {
+        al.http.code = adapted_reply_->sline.status();
+        al.http.content_type = adapted_reply_->content_type.termedBuf();
+        if (replyHttpBodySize >= 0)
             al.cache.highOffset = replyHttpBodySize;
-        }
         //don't set al.cache.objectSize because it hasn't exist yet
 
         Packer p;
@@ -1323,7 +1328,7 @@
         mb.init();
         packerToMemInit(&p, &mb);
 
-        reply_->header.packInto(&p);
+        adapted_reply_->header.packInto(&p);
         al.headers.reply = xstrdup(mb.buf);
 
         packerClean(&p);
diff -u -r -N squid-3.5.21/src/adaptation/icap/Xaction.cc squid-3.5.22/src/adaptation/icap/Xaction.cc
--- squid-3.5.21/src/adaptation/icap/Xaction.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/adaptation/icap/Xaction.cc	2016-10-10 08:58:01.000000000 +1300
@@ -598,9 +598,7 @@
     fillPendingStatus(buf);
     buf.append("/", 1);
     fillDoneStatus(buf);
-
-    buf.Printf(" %s%u]", id.Prefix, id.value);
-
+    buf.Printf(" %s%u]", id.prefix(), id.value);
     buf.terminate();
 
     return buf.content();
diff -u -r -N squid-3.5.21/src/base/AsyncJob.cc squid-3.5.22/src/base/AsyncJob.cc
--- squid-3.5.21/src/base/AsyncJob.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/base/AsyncJob.cc	2016-10-10 08:58:01.000000000 +1300
@@ -164,7 +164,7 @@
         buf.Printf("Stopped, reason:");
         buf.Printf("%s",stopReason);
     }
-    buf.Printf(" %s%u]", id.Prefix, id.value);
+    buf.Printf(" %s%u]", id.prefix(), id.value);
     buf.terminate();
 
     return buf.content();
diff -u -r -N squid-3.5.21/src/base/InstanceId.h squid-3.5.22/src/base/InstanceId.h
--- squid-3.5.21/src/base/InstanceId.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/base/InstanceId.h	2016-10-10 08:58:01.000000000 +1300
@@ -25,35 +25,41 @@
 public:
     typedef unsigned int Value; ///< id storage type; \todo: parameterize?
 
-    InstanceId(): value(++Last ? Last : ++Last) {}
+    InstanceId(): value(0) {change();}
 
     operator Value() const { return value; }
     bool operator ==(const InstanceId &o) const { return value == o.value; }
     bool operator !=(const InstanceId &o) const { return !(*this == o); }
-    void change() {value = ++Last ? Last : ++Last;}
+    void change();
 
-    /// prints Prefix followed by ID value; \todo: use HEX for value printing?
+    /// prints class-pecific prefix followed by ID value; \todo: use HEX for value printing?
     std::ostream &print(std::ostream &os) const;
 
+    /// returns the class-pecific prefix
+    const char * const prefix() const;
+
 public:
-    static const char *Prefix; ///< Class shorthand string for debugging
     Value value; ///< instance identifier
 
 private:
     InstanceId(const InstanceId& right); ///< not implemented; IDs are unique
     InstanceId& operator=(const InstanceId &right); ///< not implemented
-
-private:
-    static Value Last; ///< the last used ID value
 };
 
 /// convenience macro to instantiate Class-specific stuff in .cc files
-#define InstanceIdDefinitions(Class, prefix) \
-    template<> const char *InstanceId<Class>::Prefix = prefix; \
-    template<> InstanceId<Class>::Value InstanceId<Class>::Last = 0; \
+#define InstanceIdDefinitions(Class, pfx) \
+    template<> const char * const \
+    InstanceId<Class>::prefix() const { \
+        return pfx; \
+    } \
     template<> std::ostream & \
     InstanceId<Class>::print(std::ostream &os) const { \
-        return os << Prefix << value; \
+        return os << pfx << value; \
+    } \
+    template<> void \
+    InstanceId<Class>::change() { \
+        static InstanceId<Class>::Value Last = 0; \
+        value = ++Last ? Last : ++Last; \
     }
 
 /// print the id
diff -u -r -N squid-3.5.21/src/cache_cf.cc squid-3.5.22/src/cache_cf.cc
--- squid-3.5.21/src/cache_cf.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/cache_cf.cc	2016-10-10 08:58:01.000000000 +1300
@@ -849,7 +849,7 @@
             if (pwd->pw_dir && *pwd->pw_dir) {
                 // putenv() leaks by design; avoid leaks when nothing changes
                 static SBuf lastDir;
-                if (lastDir.isEmpty() || !lastDir.cmp(pwd->pw_dir)) {
+                if (lastDir.isEmpty() || lastDir.cmp(pwd->pw_dir) != 0) {
                     lastDir = pwd->pw_dir;
                     int len = strlen(pwd->pw_dir) + 6;
                     char *env_str = (char *)xcalloc(len, 1);
diff -u -r -N squid-3.5.21/src/cf.data.pre squid-3.5.22/src/cf.data.pre
--- squid-3.5.21/src/cf.data.pre	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/cf.data.pre	2016-10-10 08:58:01.000000000 +1300
@@ -4427,13 +4427,35 @@
 	ICAP transaction log lines will correspond to a single access
 	log line.
 
-	ICAP log uses logformat codes that make sense for an ICAP
-	transaction. Header-related codes are applied to the HTTP header
-	embedded in an ICAP server response, with the following caveats:
-	For REQMOD, there is no HTTP response header unless the ICAP
-	server performed request satisfaction. For RESPMOD, the HTTP
-	request header is the header sent to the ICAP server. For
-	OPTIONS, there are no HTTP headers.
+	ICAP log supports many access.log logformat %codes. In ICAP context,
+	HTTP message-related %codes are applied to the HTTP message embedded
+	in an ICAP message. Logformat "%http::>..." codes are used for HTTP
+	messages embedded in ICAP requests while "%http::<..." codes are used
+	for HTTP messages embedded in ICAP responses. For example:
+
+		http::>h	To-be-adapted HTTP message headers sent by Squid to
+				the ICAP service. For REQMOD transactions, these are
+				HTTP request headers. For RESPMOD, these are HTTP
+				response headers, but Squid currently cannot log them
+				(i.e., %http::>h will expand to "-" for RESPMOD).
+
+		http::<h	Adapted HTTP message headers sent by the ICAP
+				service to Squid (i.e., HTTP request headers in regular
+				REQMOD; HTTP response headers in RESPMOD and during
+				request satisfaction in REQMOD).
+
+	ICAP OPTIONS transactions do not embed HTTP messages.
+
+	Several logformat codes below deal with ICAP message bodies. An ICAP
+	message body, if any, typically includes a complete HTTP message
+	(required HTTP headers plus optional HTTP message body). When
+	computing HTTP message body size for these logformat codes, Squid
+	either includes or excludes chunked encoding overheads; see
+	code-specific documentation for details.
+
+	For Secure ICAP services, all size-related information is currently
+	computed before/after TLS encryption/decryption, as if TLS was not
+	in use at all.
 
 	The following format codes are also available for ICAP logs:
 
@@ -4447,19 +4469,16 @@
 		icap::rm	ICAP request method (REQMOD, RESPMOD, or 
 				OPTIONS). Similar to existing rm.
 
-		icap::>st	Bytes sent to the ICAP server (TCP payload
-				only; i.e., what Squid writes to the socket).
+		icap::>st	The total size of the ICAP request sent to the ICAP
+				server (ICAP headers + ICAP body), including chunking
+				metadata (if any).
+
+		icap::<st	The total size of the ICAP response received from the
+				ICAP server (ICAP headers + ICAP body), including
+				chunking metadata (if any).
 
-		icap::<st	Bytes received from the ICAP server (TCP
-				payload only; i.e., what Squid reads from
-				the socket).
-
-		icap::<bs	Number of message body bytes received from the
-				ICAP server. ICAP message body, if any, usually
-				includes encapsulated HTTP message headers and
-				possibly encapsulated HTTP message body. The
-				HTTP body part is dechunked before its size is
-				computed.
+		icap::<bs	The size of the ICAP response body received from the
+				ICAP server, excluding chunking metadata (if any).
 
 		icap::tr 	Transaction response time (in
 				milliseconds).  The timer starts when
@@ -4489,9 +4508,9 @@
 	The default ICAP log format, which can be used without an explicit
 	definition, is called icap_squid:
 
-logformat icap_squid %ts.%03tu %6icap::tr %>a %icap::to/%03icap::Hs %icap::<size %icap::rm %icap::ru% %un -/%icap::<A -
+logformat icap_squid %ts.%03tu %6icap::tr %>A %icap::to/%03icap::Hs %icap::<st %icap::rm %icap::ru %un -/%icap::<A -
 
-	See also: logformat, log_icap, and %adapt::<last_h 
+	See also: logformat and %adapt::<last_h
 DOC_END
 
 NAME: logfile_daemon
@@ -6086,17 +6105,29 @@
 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.
+       When enabled, instead of forwarding each concurrent request for
+       the same URL, Squid just sends the first of them. The other, so
+       called "collapsed" requests, wait for the response to the first
+       request and, if it happens to be cachable, use that response.
+       Here, "concurrent requests" means "received after the first
+       request headers were parsed and before the corresponding response
+       headers were parsed".
+
+       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 acceleration of highly
+       cachable content with periodic or grouped expiration times, the
+       gains from collapsing [large volumes of simultaneous refresh
+       requests] outweigh losses from such delays.
+
+       Squid collapses two kinds of requests: regular client requests
+       received on one of the listening ports and internal "cache
+       revalidation" requests which are triggered by those regular
+       requests hitting a stale cached object. Revalidation collapsing
+       is currently disabled for Squid instances containing SMP-aware
+       disk or memory caches and for Vary-controlled cached objects.
 DOC_END
 
 COMMENT_START
diff -u -r -N squid-3.5.21/src/client_side.cc squid-3.5.22/src/client_side.cc
--- squid-3.5.21/src/client_side.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/client_side.cc	2016-10-10 08:58:01.000000000 +1300
@@ -555,8 +555,6 @@
     aLogEntry->http.method = request->method;
     aLogEntry->http.version = request->http_ver;
     aLogEntry->hier = request->hier;
-    if (request->content_length > 0) // negative when no body or unknown length
-        aLogEntry->http.clientRequestSz.payloadData += request->content_length; // XXX: actually adaptedRequest payload size ??
     aLogEntry->cache.extuser = request->extacl_user.termedBuf();
 
     // Adapted request, if any, inherits and then collects all the stats, but
@@ -593,6 +591,9 @@
         al->cache.objectSize = loggingEntry()->contentLen(); // payload duplicate ?? with or without TE ?
 
     al->http.clientRequestSz.header = req_sz;
+    // the virgin request is saved to al->request
+    if (al->request && al->request->body_pipe != NULL)
+        al->http.clientRequestSz.payloadData = al->request->body_pipe->producedSize();
     al->http.clientReplySz.header = out.headers_sz;
     // XXX: calculate without payload encoding or headers !!
     al->http.clientReplySz.payloadData = out.size - out.headers_sz; // pretend its all un-encoded data for now.
@@ -1236,7 +1237,7 @@
 
     /* got modification time? */
     if (spec.time >= 0) {
-        return http->storeEntry()->lastmod <= spec.time;
+        return !http->storeEntry()->modifiedSince(spec.time);
     }
 
     assert(0);          /* should not happen */
@@ -2744,6 +2745,7 @@
     request->my_addr = conn->clientConnection->local;
     request->myportname = conn->port->name;
     request->http_ver = http_ver;
+    request->hdr_sz = http->req_sz;
 
     // Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it
     // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
diff -u -r -N squid-3.5.21/src/client_side_reply.cc squid-3.5.22/src/client_side_reply.cc
--- squid-3.5.21/src/client_side_reply.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/client_side_reply.cc	2016-10-10 08:58:01.000000000 +1300
@@ -72,7 +72,8 @@
     HTTPMSGUNLOCK(reply);
 }
 
-clientReplyContext::clientReplyContext(ClientHttpRequest *clientContext) : http (cbdataReference(clientContext)), old_entry (NULL), old_sc(NULL), deleting(false)
+clientReplyContext::clientReplyContext(ClientHttpRequest *clientContext) : http (cbdataReference(clientContext)), old_entry (NULL), old_sc(NULL), deleting(false),
+    collapsedRevalidation(crNone)
 {}
 
 /** Create an error in the store awaiting the client side to read it.
@@ -245,9 +246,9 @@
 clientReplyContext::processExpired()
 {
     const char *url = storeId();
-    StoreEntry *entry = NULL;
     debugs(88, 3, "clientReplyContext::processExpired: '" << http->uri << "'");
-    assert(http->storeEntry()->lastmod >= 0);
+    const time_t lastmod = http->storeEntry()->lastModified();
+    assert(lastmod >= 0);
     /*
      * check if we are allowed to contact other servers
      * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return
@@ -267,16 +268,43 @@
 #endif
     /* Prepare to make a new temporary request */
     saveState();
-    entry = storeCreateEntry(url,
-                             http->log_uri, http->request->flags, http->request->method);
-    /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
+
+    // TODO: support collapsed revalidation of Vary-controlled entries
+    const bool collapsingAllowed = Config.onoff.collapsed_forwarding &&
+                                   !Store::Root().smpAware() &&
+                                   http->request->vary_headers.isEmpty();
+
+    StoreEntry *entry = nullptr;
+    if (collapsingAllowed) {
+        if ((entry = storeGetPublicByRequest(http->request, ksRevalidation)))
+            entry->lock("clientReplyContext::processExpired#alreadyRevalidating");
+    }
+
+    if (entry) {
+        debugs(88, 5, "collapsed on existing revalidation entry: " << *entry);
+        collapsedRevalidation = crSlave;
+    } else {
+        entry = storeCreateEntry(url,
+                                 http->log_uri, http->request->flags, http->request->method);
+        /* NOTE, don't call StoreEntry->lock(), storeCreateEntry() does it */
+
+        if (collapsingAllowed) {
+            debugs(88, 5, "allow other revalidation requests to collapse on " << *entry);
+            Store::Root().allowCollapsing(entry, http->request->flags,
+                                          http->request->method);
+            collapsedRevalidation = crInitiator;
+        } else {
+            collapsedRevalidation = crNone;
+        }
+    }
+
     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;
+    http->request->lastmod = lastmod;
 
     if (!http->request->header.has(HDR_IF_NONE_MATCH)) {
         ETag etag = {NULL, -1}; // TODO: make that a default ETag constructor
@@ -284,17 +312,19 @@
             http->request->etag = etag.str;
     }
 
-    debugs(88, 5, "clientReplyContext::processExpired : lastmod " << entry->lastmod );
+    debugs(88, 5, "lastmod " << entry->lastModified());
     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::Start(conn, http->storeEntry(), http->request, http->al);
+    if (collapsedRevalidation != crSlave) {
+        /*
+         * 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::Start(conn, http->storeEntry(), http->request, http->al);
+    }
 
     /* Register with storage manager to receive updates when data comes in. */
 
@@ -358,7 +388,7 @@
     if (deleting)
         return;
 
-    debugs(88, 3, "handleIMSReply: " << http->storeEntry()->url() << ", " << (long unsigned) result.length << " bytes" );
+    debugs(88, 3, http->storeEntry()->url() << ", " << (long unsigned) result.length << " bytes");
 
     if (http->storeEntry() == NULL)
         return;
@@ -373,7 +403,7 @@
 
     // request to origin was aborted
     if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) {
-        debugs(88, 3, "handleIMSReply: request to origin aborted '" << http->storeEntry()->url() << "', sending old entry to client" );
+        debugs(88, 3, "request to origin aborted '" << http->storeEntry()->url() << "', sending old entry to client");
         http->logType = LOG_TCP_REFRESH_FAIL_OLD;
         sendClientOldEntry();
     }
@@ -391,13 +421,13 @@
 
         // if client sent IMS
 
-        if (http->request->flags.ims && !old_entry->modifiedSince(http->request)) {
+        if (http->request->flags.ims && !old_entry->modifiedSince(http->request->ims, http->request->imslen)) {
             // forward the 304 from origin
-            debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and forwarding 304 to client");
+            debugs(88, 3, "origin replied 304, revalidating existing entry and forwarding 304 to client");
             sendClientUpstreamResponse();
         } else {
             // send existing entry, it's still valid
-            debugs(88, 3, "handleIMSReply: origin replied 304, revalidating existing entry and sending " <<
+            debugs(88, 3, "origin replied 304, revalidating existing entry and sending " <<
                    old_rep->sline.status() << " to client");
             sendClientOldEntry();
         }
@@ -405,22 +435,37 @@
 
     // origin replied with a non-error code
     else if (status > Http::scNone && status < Http::scInternalServerError) {
-        // forward response from origin
-        http->logType = LOG_TCP_REFRESH_MODIFIED;
-        debugs(88, 3, "handleIMSReply: origin replied " << status << ", replacing existing entry and forwarding to client");
-        sendClientUpstreamResponse();
+        const HttpReply *new_rep = http->storeEntry()->getReply();
+        // RFC 7234 section 4: a cache MUST use the most recent response
+        // (as determined by the Date header field)
+        if (new_rep->olderThan(old_rep)) {
+            http->logType = LOG_TCP_REFRESH_IGNORED;
+            debugs(88, 3, "origin replied " << status <<
+                   " but with an older date header, sending old entry (" <<
+                   old_rep->sline.status() << ") to client");
+            sendClientOldEntry();
+        } else {
+            http->logType = LOG_TCP_REFRESH_MODIFIED;
+            debugs(88, 3, "origin replied " << status <<
+                   ", replacing existing entry and forwarding to client");
+
+            if (collapsedRevalidation)
+                http->storeEntry()->clearPublicKeyScope();
+
+            sendClientUpstreamResponse();
+        }
     }
 
     // origin replied with an error
     else if (http->request->flags.failOnValidationError) {
         http->logType = LOG_TCP_REFRESH_FAIL_ERR;
-        debugs(88, 3, "handleIMSReply: origin replied with error " << status <<
+        debugs(88, 3, "origin replied with error " << status <<
                ", forwarding to client due to fail_on_validation_err");
         sendClientUpstreamResponse();
     } else {
         // ignore and let client have old entry
         http->logType = LOG_TCP_REFRESH_FAIL_OLD;
-        debugs(88, 3, "handleIMSReply: origin replied with error " <<
+        debugs(88, 3, "origin replied with error " <<
                status << ", sending old entry (" << old_rep->sline.status() << ") to client");
         sendClientOldEntry();
     }
@@ -563,11 +608,12 @@
          */
         r->flags.needValidation = true;
 
-        if (e->lastmod < 0) {
-            debugs(88, 3, "validate HIT object? NO. Missing Last-Modified header. Do MISS.");
+        if (e->lastModified() < 0) {
+            debugs(88, 3, "validate HIT object? NO. Can't calculate entry modification time. Do MISS.");
             /*
-             * Previous reply didn't have a Last-Modified header,
-             * we cannot revalidate it.
+             * We cannot revalidate entries without knowing their
+             * modification time.
+             * XXX: BUG 1890 objects without Date do not get one added.
              */
             http->logType = LOG_TCP_MISS;
             processMiss();
@@ -756,7 +802,7 @@
 
     if (r.flags.ims) {
         // handle If-Modified-Since requests from the client
-        if (e->modifiedSince(&r)) {
+        if (e->modifiedSince(r.ims, r.imslen)) {
             http->logType = LOG_TCP_IMS_HIT;
             sendMoreData(result);
             return;
diff -u -r -N squid-3.5.21/src/client_side_reply.h squid-3.5.22/src/client_side_reply.h
--- squid-3.5.21/src/client_side_reply.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/client_side_reply.h	2016-10-10 08:58:01.000000000 +1300
@@ -133,6 +133,14 @@
     store_client *old_sc;   /* ... for entry to be validated */
     bool deleting;
 
+    typedef enum {
+        crNone = 0, ///< collapsed revalidation is not allowed for this context
+        crInitiator, ///< we initiated collapsed revalidation request
+        crSlave ///< we collapsed on the existing revalidation request
+    } CollapsedRevalidation;
+
+    CollapsedRevalidation collapsedRevalidation;
+
     CBDATA_CLASS2(clientReplyContext);
 };
 
diff -u -r -N squid-3.5.21/src/client_side_request.cc squid-3.5.22/src/client_side_request.cc
--- squid-3.5.21/src/client_side_request.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/client_side_request.cc	2016-10-10 08:58:01.000000000 +1300
@@ -336,7 +336,7 @@
      * correctness.
      */
     if (header)
-        request->header.update(header, NULL);
+        request->header.update(header);
 
     http->log_uri = xstrdup(urlCanonicalClean(request));
 
diff -u -r -N squid-3.5.21/src/errorpage.cc squid-3.5.22/src/errorpage.cc
--- squid-3.5.21/src/errorpage.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/errorpage.cc	2016-10-10 08:58:01.000000000 +1300
@@ -358,7 +358,6 @@
 bool strHdrAcptLangGetItem(const String &hdr, char *lang, int langLen, size_t &pos)
 {
     while (pos < hdr.size()) {
-        char *dt = lang;
 
         /* skip any initial whitespace. */
         while (pos < hdr.size() && xisspace(hdr[pos]))
@@ -372,6 +371,7 @@
          *    with preference given to an exact match.
          */
         bool invalid_byte = false;
+        char *dt = lang;
         while (pos < hdr.size() && hdr[pos] != ';' && hdr[pos] != ',' && !xisspace(hdr[pos]) && dt < (lang + (langLen -1)) ) {
             if (!invalid_byte) {
 #if USE_HTTP_VIOLATIONS
@@ -391,7 +391,6 @@
             ++pos;
         }
         *dt = '\0'; // nul-terminated the filename content string before system use.
-        ++dt;
 
         // if we terminated the tag on garbage or ';' we need to skip to the next ',' or end of header.
         while (pos < hdr.size() && hdr[pos] != ',')
@@ -400,7 +399,7 @@
         if (pos < hdr.size() && hdr[pos] == ',')
             ++pos;
 
-        debugs(4, 9, HERE << "STATE: dt='" << dt << "', lang='" << lang << "', pos=" << pos << ", buf='" << ((pos < hdr.size()) ? hdr.substr(pos,hdr.size()) : "") << "'");
+        debugs(4, 9, "STATE: lang=" << lang << ", pos=" << pos << ", buf='" << ((pos < hdr.size()) ? hdr.substr(pos,hdr.size()) : "") << "'");
 
         /* if we found anything we might use, try it. */
         if (*lang != '\0' && !invalid_byte)
diff -u -r -N squid-3.5.21/src/esi/Include.cc squid-3.5.22/src/esi/Include.cc
--- squid-3.5.21/src/esi/Include.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/esi/Include.cc	2016-10-10 08:58:01.000000000 +1300
@@ -279,7 +279,7 @@
 void
 ESIInclude::prepareRequestHeaders(HttpHeader &tempheaders, ESIVarState *vars)
 {
-    tempheaders.update (&vars->header(), NULL);
+    tempheaders.update (&vars->header());
     tempheaders.removeHopByHopEntries();
 }
 
diff -u -r -N squid-3.5.21/src/format/Format.cc squid-3.5.22/src/format/Format.cc
--- squid-3.5.21/src/format/Format.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/format/Format.cc	2016-10-10 08:58:01.000000000 +1300
@@ -309,6 +309,38 @@
     *p = '\0';
 }
 
+/// XXX: Misnamed. TODO: Split <h (and this function) to distinguish received
+/// headers from sent headers rather than failing to distinguish requests from responses.
+/// \retval HttpReply sent to the HTTP client (access.log and default context).
+/// \retval HttpReply received (encapsulated) from the ICAP server (icap.log context).
+/// \retval HttpRequest received (encapsulated) from the ICAP server (icap.log context).
+static const HttpMsg *
+actualReplyHeader(const AccessLogEntry::Pointer &al)
+{
+    const HttpMsg *msg = al->reply;
+#if USE_ADAPTATION
+    // al->icap.reqMethod is methodNone in access.log context
+    if (!msg && al->icap.reqMethod == Adaptation::methodReqmod)
+        msg = al->adapted_request;
+#endif
+    return msg;
+}
+
+/// XXX: Misnamed. See actualReplyHeader().
+/// \return HttpRequest or HttpReply for %http::>h.
+static const HttpMsg *
+actualRequestHeader(const AccessLogEntry::Pointer &al)
+{
+#if USE_ADAPTATION
+    // al->icap.reqMethod is methodNone in access.log context
+    if (al->icap.reqMethod == Adaptation::methodRespmod) {
+        // XXX: for now AccessLogEntry lacks virgin response headers
+        return NULL;
+    }
+#endif
+    return al->request;
+}
+
 void
 Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const
 {
@@ -547,8 +579,8 @@
 
         case LFT_REQUEST_HEADER:
 
-            if (al->request)
-                sb = al->request->header.getByName(fmt->data.header.header);
+            if (const HttpMsg *msg = actualRequestHeader(al))
+                sb = msg->header.getByName(fmt->data.header.header);
 
             out = sb.termedBuf();
 
@@ -567,15 +599,15 @@
 
             break;
 
-        case LFT_REPLY_HEADER:
-            if (al->reply)
-                sb = al->reply->header.getByName(fmt->data.header.header);
+        case LFT_REPLY_HEADER: {
+            if (const HttpMsg *msg = actualReplyHeader(al))
+                sb = msg->header.getByName(fmt->data.header.header);
 
             out = sb.termedBuf();
 
             quote = 1;
-
-            break;
+        }
+        break;
 
 #if USE_ADAPTATION
         case LFT_ADAPTATION_SUM_XACT_TIMES:
@@ -757,8 +789,8 @@
             break;
 #endif
         case LFT_REQUEST_HEADER_ELEM:
-            if (al->request)
-                sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+            if (const HttpMsg *msg = actualRequestHeader(al))
+                sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
 
             out = sb.termedBuf();
 
@@ -776,18 +808,27 @@
 
             break;
 
-        case LFT_REPLY_HEADER_ELEM:
-            if (al->reply)
-                sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+        case LFT_REPLY_HEADER_ELEM: {
+            if (const HttpMsg *msg = actualReplyHeader(al))
+                sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
 
             out = sb.termedBuf();
 
             quote = 1;
-
-            break;
+        }
+        break;
 
         case LFT_REQUEST_ALL_HEADERS:
-            out = al->headers.request;
+#if USE_ADAPTATION
+            if (al->icap.reqMethod == Adaptation::methodRespmod) {
+                // XXX: since AccessLogEntry::Headers lacks virgin response
+                // headers, do nothing for now
+                out = NULL;
+            } else
+#endif
+            {
+                out = al->headers.request;
+            }
 
             quote = 1;
 
@@ -802,6 +843,10 @@
 
         case LFT_REPLY_ALL_HEADERS:
             out = al->headers.reply;
+#if USE_ADAPTATION
+            if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
+                out = al->headers.adapted_request;
+#endif
 
             quote = 1;
 
diff -u -r -N squid-3.5.21/src/fs/rock/RockSwapDir.cc squid-3.5.22/src/fs/rock/RockSwapDir.cc
--- squid-3.5.21/src/fs/rock/RockSwapDir.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/fs/rock/RockSwapDir.cc	2016-10-10 08:58:01.000000000 +1300
@@ -134,7 +134,7 @@
     e.lastref = basics.lastref;
     e.timestamp = basics.timestamp;
     e.expires = basics.expires;
-    e.lastmod = basics.lastmod;
+    e.lastModified(basics.lastmod);
     e.refcount = basics.refcount;
     e.flags = basics.flags;
 
diff -u -r -N squid-3.5.21/src/fs/rock/RockSwapDir.h squid-3.5.22/src/fs/rock/RockSwapDir.h
--- squid-3.5.21/src/fs/rock/RockSwapDir.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/fs/rock/RockSwapDir.h	2016-10-10 08:58:01.000000000 +1300
@@ -48,6 +48,7 @@
     virtual void swappedOut(const StoreEntry &e);
     virtual void create();
     virtual void parse(int index, char *path);
+    virtual bool smpAware() const { return true; }
 
     // temporary path to the shared memory map of first slots of cached entries
     SBuf inodeMapPath() const;
diff -u -r -N squid-3.5.21/src/fs/ufs/RebuildState.cc squid-3.5.22/src/fs/ufs/RebuildState.cc
--- squid-3.5.21/src/fs/ufs/RebuildState.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/fs/ufs/RebuildState.cc	2016-10-10 08:58:01.000000000 +1300
@@ -74,9 +74,11 @@
 Fs::Ufs::RebuildState::RebuildStep(void *data)
 {
     RebuildState *rb = (RebuildState *)data;
-    rb->rebuildStep();
+    if (!reconfiguring)
+        rb->rebuildStep();
 
-    if (!rb->isDone())
+    // delay storeRebuildComplete() when reconfiguring to protect storeCleanup()
+    if (!rb->isDone() || reconfiguring)
         eventAdd("storeRebuild", RebuildStep, rb, 0.01, 1);
     else {
         -- StoreController::store_dirs_rebuilding;
@@ -199,7 +201,7 @@
                                     tmpe.expires,
                                     tmpe.timestamp,
                                     tmpe.lastref,
-                                    tmpe.lastmod,
+                                    tmpe.lastModified(),
                                     tmpe.refcount,  /* refcount */
                                     tmpe.flags,     /* flags */
                                     (int) flags.clean));
@@ -326,7 +328,7 @@
             currentEntry()->lastref = swapData.timestamp;
             currentEntry()->timestamp = swapData.timestamp;
             currentEntry()->expires = swapData.expires;
-            currentEntry()->lastmod = swapData.lastmod;
+            currentEntry()->lastModified(swapData.lastmod);
             currentEntry()->flags = swapData.flags;
             currentEntry()->refcount += swapData.refcount;
             sd->dereference(*currentEntry(), false);
diff -u -r -N squid-3.5.21/src/fs/ufs/UFSStoreState.cc squid-3.5.22/src/fs/ufs/UFSStoreState.cc
--- squid-3.5.21/src/fs/ufs/UFSStoreState.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/fs/ufs/UFSStoreState.cc	2016-10-10 08:58:01.000000000 +1300
@@ -421,7 +421,7 @@
     if (flags.write_draining)
         return;
 
-    if (!theFile->canWrite())
+    if (!theFile || !theFile->canWrite())
         return;
 
     flags.write_draining = true;
diff -u -r -N squid-3.5.21/src/fs/ufs/UFSSwapDir.cc squid-3.5.22/src/fs/ufs/UFSSwapDir.cc
--- squid-3.5.21/src/fs/ufs/UFSSwapDir.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/fs/ufs/UFSSwapDir.cc	2016-10-10 08:58:01.000000000 +1300
@@ -87,7 +87,7 @@
     s.timestamp = e.timestamp;
     s.lastref = e.lastref;
     s.expires = e.expires;
-    s.lastmod = e.lastmod;
+    s.lastmod = e.lastModified();
     s.swap_file_sz = e.swap_file_sz;
     s.refcount = e.refcount;
     s.flags = e.flags;
@@ -316,7 +316,8 @@
     currentIOOptions(new ConfigOptionVector()),
     ioType(xstrdup(anIOType)),
     cur_size(0),
-    n_disk_objects(0)
+    n_disk_objects(0),
+    rebuilding_(false)
 {
     /* modulename is only set to disk modules that are built, by configure,
      * so the Find call should never return NULL here.
@@ -723,6 +724,15 @@
 void
 Fs::Ufs::UFSSwapDir::openLog()
 {
+    assert(NumberOfUFSDirs || !UFSDirToGlobalDirMapping);
+    ++NumberOfUFSDirs;
+    assert(NumberOfUFSDirs <= Config.cacheSwap.n_configured);
+
+    if (rebuilding_) { // we did not close the temporary log used for rebuilding
+        assert(swaplog_fd >= 0);
+        return;
+    }
+
     char *logPath;
     logPath = logFile();
     swaplog_fd = file_open(logPath, O_WRONLY | O_CREAT | O_BINARY);
@@ -733,13 +743,6 @@
     }
 
     debugs(50, 3, HERE << "Cache Dir #" << index << " log opened on FD " << swaplog_fd);
-
-    if (0 == NumberOfUFSDirs)
-        assert(NULL == UFSDirToGlobalDirMapping);
-
-    ++NumberOfUFSDirs;
-
-    assert(NumberOfUFSDirs <= Config.cacheSwap.n_configured);
 }
 
 void
@@ -748,18 +751,19 @@
     if (swaplog_fd < 0) /* not open */
         return;
 
+    --NumberOfUFSDirs;
+    assert(NumberOfUFSDirs >= 0);
+    if (!NumberOfUFSDirs)
+        safe_free(UFSDirToGlobalDirMapping);
+
+    if (rebuilding_) // we cannot close the temporary log used for rebuilding
+        return;
+
     file_close(swaplog_fd);
 
     debugs(47, 3, "Cache Dir #" << index << " log closed on FD " << swaplog_fd);
 
     swaplog_fd = -1;
-
-    --NumberOfUFSDirs;
-
-    assert(NumberOfUFSDirs >= 0);
-
-    if (0 == NumberOfUFSDirs)
-        safe_free(UFSDirToGlobalDirMapping);
 }
 
 bool
@@ -801,7 +805,7 @@
     e->lastref = lastref;
     e->timestamp = timestamp;
     e->expires = expires;
-    e->lastmod = lastmod;
+    e->lastModified(lastmod);
     e->refcount = refcount;
     e->flags = newFlags;
     EBIT_CLR(e->flags, RELEASE_REQUEST);
@@ -839,6 +843,9 @@
 void
 Fs::Ufs::UFSSwapDir::closeTmpSwapLog()
 {
+    assert(rebuilding_);
+    rebuilding_ = false;
+
     char *swaplog_path = xstrdup(logFile(NULL)); // where the swaplog should be
     char *tmp_path = xstrdup(logFile(".new")); // the temporary file we have generated
     int fd;
@@ -864,6 +871,8 @@
 FILE *
 Fs::Ufs::UFSSwapDir::openTmpSwapLog(int *clean_flag, int *zero_flag)
 {
+    assert(!rebuilding_);
+
     char *swaplog_path = xstrdup(logFile(NULL));
     char *clean_path = xstrdup(logFile(".last-clean"));
     char *new_path = xstrdup(logFile(".new"));
@@ -897,6 +906,7 @@
     }
 
     swaplog_fd = fd;
+    rebuilding_ = true;
 
     {
         const StoreSwapLogHeader header;
@@ -1053,18 +1063,17 @@
     cleanLog = NULL;
 }
 
-void
-Fs::Ufs::UFSSwapDir::CleanEvent(void *unused)
+/// safely cleans a few unused files if possible
+int
+Fs::Ufs::UFSSwapDir::HandleCleanEvent()
 {
     static int swap_index = 0;
     int i;
     int j = 0;
     int n = 0;
-    /*
-     * Assert that there are UFS cache_dirs configured, otherwise
-     * we should never be called.
-     */
-    assert(NumberOfUFSDirs);
+
+    if (!NumberOfUFSDirs)
+        return 0; // probably in the middle of reconfiguration
 
     if (NULL == UFSDirToGlobalDirMapping) {
         SwapDir *sd;
@@ -1106,6 +1115,13 @@
         ++swap_index;
     }
 
+    return n;
+}
+
+void
+Fs::Ufs::UFSSwapDir::CleanEvent(void *)
+{
+    const int n = HandleCleanEvent();
     eventAdd("storeDirClean", CleanEvent, NULL,
              15.0 * exp(-0.25 * n), 1);
 }
@@ -1283,13 +1299,18 @@
 void
 Fs::Ufs::UFSSwapDir::logEntry(const StoreEntry & e, int op) const
 {
+    if (swaplog_fd < 0) {
+        debugs(36, 5, "cannot log " << e << " in the middle of reconfiguration");
+        return;
+    }
+
     StoreSwapLogData *s = new StoreSwapLogData;
     s->op = (char) op;
     s->swap_filen = e.swap_filen;
     s->timestamp = e.timestamp;
     s->lastref = e.lastref;
     s->expires = e.expires;
-    s->lastmod = e.lastmod;
+    s->lastmod = e.lastModified();
     s->swap_file_sz = e.swap_file_sz;
     s->refcount = e.refcount;
     s->flags = e.flags;
diff -u -r -N squid-3.5.21/src/fs/ufs/UFSSwapDir.h squid-3.5.22/src/fs/ufs/UFSSwapDir.h
--- squid-3.5.21/src/fs/ufs/UFSSwapDir.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/fs/ufs/UFSSwapDir.h	2016-10-10 08:58:01.000000000 +1300
@@ -90,6 +90,7 @@
     virtual void swappedOut(const StoreEntry &e);
     virtual uint64_t currentSize() const { return cur_size; }
     virtual uint64_t currentCount() const { return n_disk_objects; }
+    virtual bool smpAware() const { return false; }
 
     void unlinkFile(sfileno f);
     // move down when unlink is a virtual method
@@ -145,6 +146,7 @@
     bool pathIsDirectory(const char *path)const;
     int swaplog_fd;
     static EVH CleanEvent;
+    static int HandleCleanEvent();
     /** Verify that the the CacheDir exists
      *
      * If this returns < 0, then Squid exits, complains about swap
@@ -164,6 +166,7 @@
     char const *ioType;
     uint64_t cur_size; ///< currently used space in the storage area
     uint64_t n_disk_objects; ///< total number of objects stored
+    bool rebuilding_; ///< whether RebuildState is writing the new swap.state
 };
 
 } //namespace Ufs
diff -u -r -N squid-3.5.21/src/htcp.cc squid-3.5.22/src/htcp.cc
--- squid-3.5.21/src/htcp.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/htcp.cc	2016-10-10 08:58:01.000000000 +1300
@@ -886,8 +886,8 @@
         if (e && e->expires > -1)
             hdr.putTime(HDR_EXPIRES, e->expires);
 
-        if (e && e->lastmod > -1)
-            hdr.putTime(HDR_LAST_MODIFIED, e->lastmod);
+        if (e && e->lastModified() > -1)
+            hdr.putTime(HDR_LAST_MODIFIED, e->lastModified());
 
         hdr.packInto(&p);
 
diff -u -r -N squid-3.5.21/src/http.cc squid-3.5.22/src/http.cc
--- squid-3.5.21/src/http.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/http.cc	2016-10-10 08:58:01.000000000 +1300
@@ -84,7 +84,8 @@
 
 HttpStateData::HttpStateData(FwdState *theFwdState) : AsyncJob("HttpStateData"), Client(theFwdState),
     lastChunk(0), header_bytes_read(0), reply_bytes_read(0),
-    body_bytes_truncated(0), httpChunkDecoder(NULL)
+    body_bytes_truncated(0), httpChunkDecoder(NULL),
+    sawDateGoBack(false)
 {
     debugs(11,5,HERE << "HttpStateData " << this << " created");
     ignoreCacheControl = false;
@@ -169,6 +170,14 @@
     mustStop("HttpStateData::httpTimeout");
 }
 
+static StoreEntry *
+findPreviouslyCachedEntry(StoreEntry *newEntry) {
+    assert(newEntry->mem_obj);
+    return newEntry->mem_obj->request ?
+           storeGetPublicByRequest(newEntry->mem_obj->request) :
+           storeGetPublic(newEntry->mem_obj->storeId(), newEntry->mem_obj->method);
+}
+
 /// Remove an existing public store entry if the incoming response (to be
 /// stored in a currently private entry) is going to invalidate it.
 static void
@@ -176,7 +185,6 @@
 {
     int remove = 0;
     int forbidden = 0;
-    StoreEntry *pe;
 
     // If the incoming response already goes into a public entry, then there is
     // nothing to remove. This protects ready-for-collapsing entries as well.
@@ -235,12 +243,7 @@
     if (!remove && !forbidden)
         return;
 
-    assert(e->mem_obj);
-
-    if (e->mem_obj->request)
-        pe = storeGetPublicByRequest(e->mem_obj->request);
-    else
-        pe = storeGetPublic(e->mem_obj->storeId(), e->mem_obj->method);
+    StoreEntry *pe = findPreviouslyCachedEntry(e);
 
     if (pe != NULL) {
         assert(e != pe);
@@ -331,6 +334,13 @@
         return 0;
     }
 
+    // RFC 7234 section 4: a cache MUST use the most recent response
+    // (as determined by the Date header field)
+    if (sawDateGoBack) {
+        debugs(22, 3, "NO because " << *entry << " has an older date header.");
+        return 0;
+    }
+
     // Check for Surrogate/1.0 protocol conditions
     // NP: reverse-proxy traffic our parent server has instructed us never to cache
     if (surrogateNoStore) {
@@ -886,7 +896,10 @@
     /* Check if object is cacheable or not based on reply code */
     debugs(11, 3, "HTTP CODE: " << rep->sline.status());
 
-    if (neighbors_do_private_keys)
+    if (const StoreEntry *oldEntry = findPreviouslyCachedEntry(entry))
+        sawDateGoBack = rep->olderThan(oldEntry->getReply());
+
+    if (neighbors_do_private_keys && !sawDateGoBack)
         httpMaybeRemovePublic(entry, rep->sline.status());
 
     bool varyFailure = false;
diff -u -r -N squid-3.5.21/src/http.h squid-3.5.22/src/http.h
--- squid-3.5.21/src/http.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/http.h	2016-10-10 08:58:01.000000000 +1300
@@ -112,6 +112,9 @@
     bool peerSupportsConnectionPinning() const;
 
     ChunkedCodingParser *httpChunkDecoder;
+    /// Whether we received a Date header older than that of a matching
+    /// cached response.
+    bool sawDateGoBack;
 private:
     CBDATA_CLASS2(HttpStateData);
 };
diff -u -r -N squid-3.5.21/src/HttpHeader.cc squid-3.5.22/src/HttpHeader.cc
--- squid-3.5.21/src/HttpHeader.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/HttpHeader.cc	2016-10-10 08:58:01.000000000 +1300
@@ -450,7 +450,7 @@
 HttpHeader::HttpHeader(const HttpHeader &other): owner(other.owner), len(other.len), conflictingContentLength_(false)
 {
     httpHeaderMaskInit(&mask, 0);
-    update(&other, NULL); // will update the mask as well
+    update(&other); // will update the mask as well
 }
 
 HttpHeader::~HttpHeader()
@@ -465,7 +465,7 @@
         // we do not really care, but the caller probably does
         assert(owner == other.owner);
         clean();
-        update(&other, NULL); // will update the mask as well
+        update(&other); // will update the mask as well
         len = other.len;
         conflictingContentLength_ = other.conflictingContentLength_;
     }
@@ -535,26 +535,71 @@
     }
 }
 
+/// check whether the fresh header has any new/changed updatable fields
+bool
+HttpHeader::needUpdate(HttpHeader const *fresh) const
+{
+    for (unsigned int i = 0; i < fresh->entries.size(); ++i) {
+        const HttpHeaderEntry *e = fresh->entries[i];
+        if (!e || skipUpdateHeader(e->id))
+            continue;
+        String value;
+        const char *name = e->name.termedBuf();
+        if (!getByNameIfPresent(name, value) ||
+                (value != fresh->getByName(name)))
+            return true;
+    }
+    return false;
+}
+
 /* use fresh entries to replace old ones */
 void
 httpHeaderUpdate(HttpHeader * old, const HttpHeader * fresh, const HttpHeaderMask * denied_mask)
 {
     assert (old);
-    old->update (fresh, denied_mask);
+    old->update(fresh);
 }
 
 void
-HttpHeader::update (HttpHeader const *fresh, HttpHeaderMask const *denied_mask)
+HttpHeader::updateWarnings()
 {
-    const HttpHeaderEntry *e;
+    int count = 0;
     HttpHeaderPos pos = HttpHeaderInitPos;
+
+    // RFC 7234, section 4.3.4: delete 1xx warnings and retain 2xx warnings
+    while (HttpHeaderEntry *e = getEntry(&pos)) {
+        if (e->id == HDR_WARNING && (e->getInt()/100 == 1) )
+            delAt(pos, count);
+    }
+}
+
+bool
+HttpHeader::skipUpdateHeader(const http_hdr_type id) const
+{
+    // RFC 7234, section 4.3.4: use fields other from Warning for update
+    return id == HDR_WARNING;
+}
+
+bool
+HttpHeader::update(HttpHeader const *fresh)
+{
     assert(fresh);
     assert(this != fresh);
 
+    // Optimization: Finding whether a header field changed is expensive
+    // and probably not worth it except for collapsed revalidation needs.
+    if (Config.onoff.collapsed_forwarding && !needUpdate(fresh))
+        return false;
+
+    updateWarnings();
+
+    const HttpHeaderEntry *e;
+    HttpHeaderPos pos = HttpHeaderInitPos;
+
     while ((e = fresh->getEntry(&pos))) {
         /* deny bad guys (ok to check for HDR_OTHER) here */
 
-        if (denied_mask && CBIT_TEST(*denied_mask, e->id))
+        if (skipUpdateHeader(e->id))
             continue;
 
         if (e->id != HDR_OTHER)
@@ -567,13 +612,14 @@
     while ((e = fresh->getEntry(&pos))) {
         /* deny bad guys (ok to check for HDR_OTHER) here */
 
-        if (denied_mask && CBIT_TEST(*denied_mask, e->id))
+        if (skipUpdateHeader(e->id))
             continue;
 
         debugs(55, 7, "Updating header '" << HeadersAttrs[e->id].name << "' in cached entry");
 
         addEntry(e->clone());
     }
+    return true;
 }
 
 /* just handy in parsing: resets and returns false */
@@ -1720,7 +1766,6 @@
 HttpHeaderEntry::getInt() const
 {
     assert_eid (id);
-    assert (Headers[id].type == ftInt);
     int val = -1;
     int ok = httpHeaderParseInt(value.termedBuf(), &val);
     httpHeaderNoteParsedEntry(id, value, !ok);
@@ -1734,7 +1779,6 @@
 HttpHeaderEntry::getInt64() const
 {
     assert_eid (id);
-    assert (Headers[id].type == ftInt64);
     int64_t val = -1;
     int ok = httpHeaderParseOffset(value.termedBuf(), &val);
     httpHeaderNoteParsedEntry(id, value, !ok);
diff -u -r -N squid-3.5.21/src/HttpHeader.h squid-3.5.22/src/HttpHeader.h
--- squid-3.5.21/src/HttpHeader.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/HttpHeader.h	2016-10-10 08:58:01.000000000 +1300
@@ -217,7 +217,7 @@
     /* Interface functions */
     void clean();
     void append(const HttpHeader * src);
-    void update (HttpHeader const *fresh, HttpHeaderMask const *denied_mask);
+    bool update(HttpHeader const *fresh);
     void compact();
     int reset();
     int parse(const char *header_start, const char *header_end);
@@ -278,6 +278,9 @@
 protected:
     /** \deprecated Public access replaced by removeHopByHopEntries() */
     void removeConnectionHeaderEntries();
+    bool needUpdate(const HttpHeader *fresh) const;
+    bool skipUpdateHeader(const http_hdr_type id) const;
+    void updateWarnings();
 
 private:
     HttpHeaderEntry *findLastEntry(http_hdr_type id) const;
diff -u -r -N squid-3.5.21/src/HttpReply.cc squid-3.5.22/src/HttpReply.cc
--- squid-3.5.21/src/HttpReply.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/HttpReply.cc	2016-10-10 08:58:01.000000000 +1300
@@ -24,40 +24,6 @@
 #include "Store.h"
 #include "StrList.h"
 
-/* local constants */
-
-/* If we receive a 304 from the origin during a cache revalidation, we must
- * update the headers of the existing entry. Specifically, we need to update all
- * end-to-end headers and not any hop-by-hop headers (rfc2616 13.5.3).
- *
- * This is not the whole story though: since it is possible for a faulty/malicious
- * origin server to set headers it should not in a 304, we must explicitly ignore
- * these too. Specifically all entity-headers except those permitted in a 304
- * (rfc2616 10.3.5) must be ignored.
- *
- * The list of headers we don't update is made up of:
- *     all hop-by-hop headers
- *     all entity-headers except Expires and Content-Location
- */
-static HttpHeaderMask Denied304HeadersMask;
-static http_hdr_type Denied304HeadersArr[] = {
-    // hop-by-hop headers
-    HDR_CONNECTION, HDR_KEEP_ALIVE, HDR_PROXY_AUTHENTICATE, HDR_PROXY_AUTHORIZATION,
-    HDR_TE, HDR_TRAILER, HDR_TRANSFER_ENCODING, HDR_UPGRADE,
-    // entity headers
-    HDR_ALLOW, HDR_CONTENT_ENCODING, HDR_CONTENT_LANGUAGE, HDR_CONTENT_LENGTH,
-    HDR_CONTENT_MD5, HDR_CONTENT_RANGE, HDR_CONTENT_TYPE, HDR_LAST_MODIFIED
-};
-
-/* module initialization */
-void
-httpReplyInitModule(void)
-{
-    assert(Http::scNone == 0); // HttpReply::parse() interface assumes that
-    httpHeaderMaskInit(&Denied304HeadersMask, 0);
-    httpHeaderCalcMask(&Denied304HeadersMask, Denied304HeadersArr, countof(Denied304HeadersArr));
-}
-
 HttpReply::HttpReply() : HttpMsg(hoReply), date (0), last_modified (0),
     expires (0), surrogate_control (NULL), content_range (NULL), keep_alive (0),
     protoPrefix("HTTP/"), bodySizeMax(-2)
@@ -271,20 +237,23 @@
     return 1;
 }
 
-void
+bool
 HttpReply::updateOnNotModified(HttpReply const * freshRep)
 {
     assert(freshRep);
 
+    /* update raw headers */
+    if (!header.update(&freshRep->header))
+        return false;
+
     /* clean cache */
     hdrCacheClean();
-    /* update raw headers */
-    header.update(&freshRep->header,
-                  (const HttpHeaderMask *) &Denied304HeadersMask);
 
     header.compact();
     /* init cache */
     hdrCacheInit();
+
+    return true;
 }
 
 /* internal routines */
@@ -659,3 +628,11 @@
     return newValue;
 }
 
+bool
+HttpReply::olderThan(const HttpReply *them) const
+{
+    if (!them || !them->date || !date)
+        return false;
+    return date < them->date;
+}
+
diff -u -r -N squid-3.5.21/src/HttpReply.h squid-3.5.22/src/HttpReply.h
--- squid-3.5.21/src/HttpReply.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/HttpReply.h	2016-10-10 08:58:01.000000000 +1300
@@ -72,7 +72,7 @@
 
     virtual bool inheritProperties(const HttpMsg *aMsg);
 
-    void updateOnNotModified(HttpReply const *other);
+    bool updateOnNotModified(HttpReply const *other);
 
     /** set commonly used info with one call */
     void setHeaders(Http::StatusCode status,
@@ -112,6 +112,10 @@
 
     virtual void hdrCacheInit();
 
+    /// whether our Date header value is smaller than theirs
+    /// \returns false if any information is missing
+    bool olderThan(const HttpReply *them) const;
+
 private:
     /** initialize */
     void init();
diff -u -r -N squid-3.5.21/src/ip/Intercept.cc squid-3.5.22/src/ip/Intercept.cc
--- squid-3.5.21/src/ip/Intercept.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/ip/Intercept.cc	2016-10-10 08:58:01.000000000 +1300
@@ -207,16 +207,22 @@
         debugs(89, warningLevel, "IPF (IPFilter v4) NAT does not support IPv6. Please upgrade to IPFilter v5.1");
         warningLevel = (warningLevel + 1) % 10;
         return false;
+    }
+    newConn->local.getInAddr(natLookup.nl_inip);
+    newConn->remote.getInAddr(natLookup.nl_outip);
 #else
         natLookup.nl_v = 6;
-    } else {
+        newConn->local.getInAddr(natLookup.nl_inipaddr.in6);
+        newConn->remote.getInAddr(natLookup.nl_outipaddr.in6);
+    }
+    else {
         natLookup.nl_v = 4;
-#endif
+        newConn->local.getInAddr(natLookup.nl_inipaddr.in4);
+        newConn->remote.getInAddr(natLookup.nl_outipaddr.in4);
     }
+#endif
     natLookup.nl_inport = htons(newConn->local.port());
-    newConn->local.getInAddr(natLookup.nl_inip);
     natLookup.nl_outport = htons(newConn->remote.port());
-    newConn->remote.getInAddr(natLookup.nl_outip);
     // ... and the TCP flag
     natLookup.nl_flags = IPN_TCP;
 
@@ -281,7 +287,14 @@
         debugs(89, 9, HERE << "address: " << newConn);
         return false;
     } else {
+#if IPFILTER_VERSION < 5000003
         newConn->local = natLookup.nl_realip;
+#else
+        if (newConn->remote.isIPv6())
+            newConn->local = natLookup.nl_realipaddr.in6;
+        else
+            newConn->local = natLookup.nl_realipaddr.in4;
+#endif
         newConn->local.port(ntohs(natLookup.nl_realport));
         debugs(89, 5, HERE << "address NAT: " << newConn);
         return true;
diff -u -r -N squid-3.5.21/src/ipc/StoreMap.cc squid-3.5.22/src/ipc/StoreMap.cc
--- squid-3.5.21/src/ipc/StoreMap.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/ipc/StoreMap.cc	2016-10-10 08:58:01.000000000 +1300
@@ -491,7 +491,7 @@
     basics.timestamp = from.timestamp;
     basics.lastref = from.lastref;
     basics.expires = from.expires;
-    basics.lastmod = from.lastmod;
+    basics.lastmod = from.lastModified();
     basics.swap_file_sz = from.swap_file_sz;
     basics.refcount = from.refcount;
     basics.flags = from.flags;
diff -u -r -N squid-3.5.21/src/LogTags.h squid-3.5.22/src/LogTags.h
--- squid-3.5.21/src/LogTags.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/LogTags.h	2016-10-10 08:58:01.000000000 +1300
@@ -25,6 +25,7 @@
     LOG_TCP_REFRESH_FAIL_OLD,   // refresh from origin failed, stale reply sent
     LOG_TCP_REFRESH_FAIL_ERR,   // refresh from origin failed, error forwarded
     LOG_TCP_REFRESH_MODIFIED,   // refresh from origin replaced existing entry
+    LOG_TCP_REFRESH_IGNORED,    // refresh from origin ignored, stale entry sent
     LOG_TCP_CLIENT_REFRESH_MISS,
     LOG_TCP_IMS_HIT,
     LOG_TCP_SWAPFAIL_MISS,
diff -u -r -N squid-3.5.21/src/main.cc squid-3.5.22/src/main.cc
--- squid-3.5.21/src/main.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/main.cc	2016-10-10 08:58:01.000000000 +1300
@@ -1069,8 +1069,6 @@
 
     httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
 
-    httpReplyInitModule();  /* must go before accepting replies */
-
     errorInitialize();
 
     accessLogInit();
diff -u -r -N squid-3.5.21/src/MemBlob.h squid-3.5.22/src/MemBlob.h
--- squid-3.5.21/src/MemBlob.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/MemBlob.h	2016-10-10 08:58:01.000000000 +1300
@@ -72,7 +72,7 @@
      */
     bool canAppend(const size_type off, const size_type n) const {
         // TODO: ignore offset (and adjust size) when the blob is not shared?
-        return isAppendOffset(off) && willFit(n);
+        return (isAppendOffset(off) && willFit(n)) || !n;
     }
 
     /** adjusts internal object state as if exactly n bytes were append()ed
diff -u -r -N squid-3.5.21/src/MemStore.cc squid-3.5.22/src/MemStore.cc
--- squid-3.5.21/src/MemStore.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/MemStore.cc	2016-10-10 08:58:01.000000000 +1300
@@ -281,7 +281,7 @@
     e.lastref = basics.lastref;
     e.timestamp = basics.timestamp;
     e.expires = basics.expires;
-    e.lastmod = basics.lastmod;
+    e.lastModified(basics.lastmod);
     e.refcount = basics.refcount;
     e.flags = basics.flags;
 
diff -u -r -N squid-3.5.21/src/MemStore.h squid-3.5.22/src/MemStore.h
--- squid-3.5.21/src/MemStore.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/MemStore.h	2016-10-10 08:58:01.000000000 +1300
@@ -63,6 +63,7 @@
     virtual void maintain();
     virtual bool anchorCollapsed(StoreEntry &collapsed, bool &inSync);
     virtual bool updateCollapsed(StoreEntry &collapsed);
+    virtual bool smpAware() const { return true; }
 
     static int64_t EntryLimit();
 
diff -u -r -N squid-3.5.21/src/peer_digest.cc squid-3.5.22/src/peer_digest.cc
--- squid-3.5.21/src/peer_digest.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/peer_digest.cc	2016-10-10 08:58:01.000000000 +1300
@@ -359,7 +359,7 @@
     /* set lastmod to trigger IMS request if possible */
 
     if (old_e)
-        e->lastmod = old_e->lastmod;
+        e->lastModified(old_e->lastModified());
 
     /* push towards peer cache */
     debugs(72, 3, "peerDigestRequest: forwarding to fwdStart...");
@@ -942,8 +942,8 @@
     debugs(72, 3, "peerDigestFetchFinish: expires: " <<
            (long int) fetch->expires << " (" << std::showpos <<
            (int) (fetch->expires - squid_curtime) << "), lmt: " <<
-           std::noshowpos << (long int) fetch->entry->lastmod << " (" <<
-           std::showpos << (int) (fetch->entry->lastmod - squid_curtime) <<
+           std::noshowpos << (long int) fetch->entry->lastModified() << " (" <<
+           std::showpos << (int) (fetch->entry->lastModified() - squid_curtime) <<
            ")");
 
 }
diff -u -r -N squid-3.5.21/src/redirect.cc squid-3.5.22/src/redirect.cc
--- squid-3.5.21/src/redirect.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/redirect.cc	2016-10-10 08:58:01.000000000 +1300
@@ -369,11 +369,13 @@
     }
 
     if (Config.redirector_extras) {
+        delete redirectorExtrasFmt;
         redirectorExtrasFmt = new ::Format::Format("url_rewrite_extras");
         (void)redirectorExtrasFmt->parse(Config.redirector_extras);
     }
 
     if (Config.storeId_extras) {
+        delete storeIdExtrasFmt;
         storeIdExtrasFmt = new ::Format::Format("store_id_extras");
         (void)storeIdExtrasFmt->parse(Config.storeId_extras);
     }
@@ -388,9 +390,6 @@
      * When and if needed for more helpers a separated shutdown
      * method will be added for each of them.
      */
-    if (!storeIds && !redirectors)
-        return;
-
     if (redirectors)
         helperShutdown(redirectors);
 
diff -u -r -N squid-3.5.21/src/refresh.cc squid-3.5.22/src/refresh.cc
--- squid-3.5.21/src/refresh.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/refresh.cc	2016-10-10 08:58:01.000000000 +1300
@@ -186,13 +186,8 @@
     }
 
     // 3. If there is a Last-Modified header, try the last-modified factor algorithm.
-    if (entry->lastmod > -1 && entry->timestamp > entry->lastmod) {
-
-        /* lastmod_delta is the difference between the last-modified date of the response
-         * and the time we cached it. It's how "old" the response was when we got it.
-         */
-        time_t lastmod_delta = entry->timestamp - entry->lastmod;
-
+    const time_t lastmod_delta = entry->timestamp - entry->lastModified();
+    if (lastmod_delta > 0) {
         /* stale_age is the age of the response when it became/becomes stale according to
          * the last-modified factor algorithm. It's how long we can consider the response
          * fresh from the time we cached it.
@@ -553,8 +548,8 @@
         /* Does not need refresh. This is certainly cachable */
         return true;
 
-    if (entry->lastmod < 0)
-        /* Last modified is needed to do a refresh */
+    if (entry->lastModified() < 0)
+        /* We should know entry's modification time to do a refresh */
         return false;
 
     if (entry->mem_obj == NULL)
diff -u -r -N squid-3.5.21/src/ssl/PeerConnector.cc squid-3.5.22/src/ssl/PeerConnector.cc
--- squid-3.5.21/src/ssl/PeerConnector.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/ssl/PeerConnector.cc	2016-10-10 08:58:01.000000000 +1300
@@ -771,7 +771,7 @@
     }
     if (serverConn != NULL)
         buf.Printf(" FD %d", serverConn->fd);
-    buf.Printf(" %s%u]", id.Prefix, id.value);
+    buf.Printf(" %s%u]", id.prefix(), id.value);
     buf.terminate();
 
     return buf.content();
diff -u -r -N squid-3.5.21/src/ssl/support.cc squid-3.5.22/src/ssl/support.cc
--- squid-3.5.21/src/ssl/support.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/ssl/support.cc	2016-10-10 08:58:01.000000000 +1300
@@ -1350,7 +1350,6 @@
 ssl_read_method(int fd, char *buf, int len)
 {
     SSL *ssl = fd_table[fd].ssl;
-    int i;
 
 #if DONT_DO_THIS
 
@@ -1361,7 +1360,10 @@
 
 #endif
 
-    i = SSL_read(ssl, buf, len);
+    int i = SSL_read(ssl, buf, len);
+    if (i > 0) {
+        (void)VALGRIND_MAKE_MEM_DEFINED(buf, i);
+    }
 
     if (i > 0 && SSL_pending(ssl) > 0) {
         debugs(83, 2, "SSL FD " << fd << " is pending");
diff -u -r -N squid-3.5.21/src/stat.cc squid-3.5.22/src/stat.cc
--- squid-3.5.21/src/stat.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/stat.cc	2016-10-10 08:58:01.000000000 +1300
@@ -77,7 +77,6 @@
 
 /* LOCALS */
 static const char *describeStatuses(const StoreEntry *);
-static const char *describeTimestamps(const StoreEntry *);
 static void statAvgTick(void *notused);
 static void statAvgDump(StoreEntry *, int minutes, int hours);
 #if STAT_GRAPHS
@@ -328,18 +327,6 @@
     return buf;
 }
 
-static const char *
-describeTimestamps(const StoreEntry * entry)
-{
-    LOCAL_ARRAY(char, buf, 256);
-    snprintf(buf, 256, "LV:%-9d LU:%-9d LM:%-9d EX:%-9d",
-             (int) entry->timestamp,
-             (int) entry->lastref,
-             (int) entry->lastmod,
-             (int) entry->expires);
-    return buf;
-}
-
 static void
 statStoreEntry(MemBuf * mb, StoreEntry * e)
 {
@@ -347,7 +334,7 @@
     mb->Printf("KEY %s\n", e->getMD5Text());
     mb->Printf("\t%s\n", describeStatuses(e));
     mb->Printf("\t%s\n", storeEntryFlags(e));
-    mb->Printf("\t%s\n", describeTimestamps(e));
+    mb->Printf("\t%s\n", e->describeTimestamps());
     mb->Printf("\t%d locks, %d clients, %d refs\n",
                (int) e->locks(),
                storePendingNClients(e),
diff -u -r -N squid-3.5.21/src/store.cc squid-3.5.22/src/store.cc
--- squid-3.5.21/src/store.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/store.cc	2016-10-10 08:58:01.000000000 +1300
@@ -162,12 +162,12 @@
 }
 
 void
-StoreEntry::makePublic()
+StoreEntry::makePublic(const KeyScope scope)
 {
     /* This object can be cached for a long time */
 
     if (!EBIT_TEST(flags, RELEASE_REQUEST))
-        setPublicKey();
+        setPublicKey(scope);
 }
 
 void
@@ -355,7 +355,7 @@
     timestamp(-1),
     lastref(-1),
     expires(-1),
-    lastmod(-1),
+    lastModified_(-1),
     swap_file_sz(0),
     refcount(0),
     flags(0),
@@ -585,19 +585,19 @@
 }
 
 StoreEntry *
-storeGetPublicByRequestMethod(HttpRequest * req, const HttpRequestMethod& method)
+storeGetPublicByRequestMethod(HttpRequest * req, const HttpRequestMethod& method, const KeyScope keyScope)
 {
-    return Store::Root().get(storeKeyPublicByRequestMethod(req, method));
+    return Store::Root().get(storeKeyPublicByRequestMethod(req, method, keyScope));
 }
 
 StoreEntry *
-storeGetPublicByRequest(HttpRequest * req)
+storeGetPublicByRequest(HttpRequest * req, const KeyScope keyScope)
 {
-    StoreEntry *e = storeGetPublicByRequestMethod(req, req->method);
+    StoreEntry *e = storeGetPublicByRequestMethod(req, req->method, keyScope);
 
     if (e == NULL && req->method == Http::METHOD_HEAD)
         /* We can generate a HEAD reply from a cached GET object */
-        e = storeGetPublicByRequestMethod(req, Http::METHOD_GET);
+        e = storeGetPublicByRequestMethod(req, Http::METHOD_GET, keyScope);
 
     return e;
 }
@@ -653,10 +653,8 @@
 }
 
 void
-StoreEntry::setPublicKey()
+StoreEntry::setPublicKey(const KeyScope scope)
 {
-    const cache_key *newkey;
-
     if (key && !EBIT_TEST(flags, KEY_PRIVATE))
         return;                 /* is already public */
 
@@ -680,80 +678,35 @@
 
     assert(!EBIT_TEST(flags, RELEASE_REQUEST));
 
-    if (mem_obj->request) {
-        HttpRequest *request = mem_obj->request;
-
-        if (mem_obj->vary_headers.isEmpty()) {
-            /* First handle the case where the object no longer varies */
-            request->vary_headers.clear();
-        } else {
-            if (!request->vary_headers.isEmpty() && request->vary_headers.cmp(mem_obj->vary_headers) != 0) {
-                /* Oops.. the variance has changed. Kill the base object
-                 * to record the new variance key
-                 */
-                request->vary_headers.clear();       /* free old "bad" variance key */
-                if (StoreEntry *pe = storeGetPublic(mem_obj->storeId(), mem_obj->method))
-                    pe->release();
-            }
-
-            /* Make sure the request knows the variance status */
-            if (request->vary_headers.isEmpty())
-                request->vary_headers = httpMakeVaryMark(request, mem_obj->getReply());
-        }
-
-        // TODO: storeGetPublic() calls below may create unlocked entries.
-        // We should add/use storeHas() API or lock/unlock those entries.
-        if (!mem_obj->vary_headers.isEmpty() && !storeGetPublic(mem_obj->storeId(), mem_obj->method)) {
-            /* Create "vary" base object */
-            String vary;
-            StoreEntry *pe = storeCreateEntry(mem_obj->storeId(), mem_obj->logUri(), request->flags, request->method);
-            /* We are allowed to do this typecast */
-            HttpReply *rep = new HttpReply;
-            rep->setHeaders(Http::scOkay, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000);
-            vary = mem_obj->getReply()->header.getList(HDR_VARY);
-
-            if (vary.size()) {
-                /* Again, we own this structure layout */
-                rep->header.putStr(HDR_VARY, vary.termedBuf());
-                vary.clean();
-            }
-
-#if X_ACCELERATOR_VARY
-            vary = mem_obj->getReply()->header.getList(HDR_X_ACCELERATOR_VARY);
-
-            if (vary.size() > 0) {
-                /* Again, we own this structure layout */
-                rep->header.putStr(HDR_X_ACCELERATOR_VARY, vary.termedBuf());
-                vary.clean();
-            }
-
-#endif
-            pe->replaceHttpReply(rep, false); // no write until key is public
-
-            pe->timestampsSet();
-
-            pe->makePublic();
+    adjustVary();
+    forcePublicKey(calcPublicKey(scope));
+}
 
-            pe->startWriting(); // after makePublic()
+void
+StoreEntry::clearPublicKeyScope()
+{
+    if (!key || EBIT_TEST(flags, KEY_PRIVATE))
+        return; // probably the old public key was deleted or made private
 
-            pe->complete();
+    // TODO: adjustVary() when collapsed revalidation supports that
 
-            pe->unlock("StoreEntry::setPublicKey+Vary");
-        }
+    const cache_key *newKey = calcPublicKey(ksDefault);
+    if (!storeKeyHashCmp(key, newKey))
+        return; // probably another collapsed revalidation beat us to this change
 
-        newkey = storeKeyPublicByRequest(mem_obj->request);
-    } else
-        newkey = storeKeyPublic(mem_obj->storeId(), mem_obj->method);
+    forcePublicKey(newKey);
+}
 
+/// Unconditionally sets public key for this store entry.
+/// Releases the old entry with the same public key (if any).
+void
+StoreEntry::forcePublicKey(const cache_key *newkey)
+{
     if (StoreEntry *e2 = (StoreEntry *)hash_lookup(store_table, newkey)) {
+        assert(e2 != this);
         debugs(20, 3, "Making old " << *e2 << " private.");
         e2->setPrivateKey();
         e2->release();
-
-        if (mem_obj->request)
-            newkey = storeKeyPublicByRequest(mem_obj->request);
-        else
-            newkey = storeKeyPublic(mem_obj->storeId(), mem_obj->method);
     }
 
     if (key)
@@ -767,6 +720,84 @@
         storeDirSwapLog(this, SWAP_LOG_ADD);
 }
 
+/// Calculates correct public key for feeding forcePublicKey().
+/// Assumes adjustVary() has been called for this entry already.
+const cache_key *StoreEntry::calcPublicKey(const KeyScope keyScope) {
+    assert(mem_obj);
+    return mem_obj->request ?  storeKeyPublicByRequest(mem_obj->request, keyScope) :
+           storeKeyPublic(mem_obj->storeId(), mem_obj->method, keyScope);
+}
+
+/// Updates mem_obj->request->vary_headers to reflect the current Vary.
+/// The vary_headers field is used to calculate the Vary marker key.
+/// Releases the old Vary marker with an outdated key (if any).
+void StoreEntry::adjustVary() {
+    assert(mem_obj);
+
+    if (!mem_obj->request)
+        return;
+
+    HttpRequest *request = mem_obj->request;
+
+    if (mem_obj->vary_headers.isEmpty()) {
+        /* First handle the case where the object no longer varies */
+        request->vary_headers.clear();
+    } else {
+        if (!request->vary_headers.isEmpty() && request->vary_headers.cmp(mem_obj->vary_headers) != 0) {
+            /* Oops.. the variance has changed. Kill the base object
+             * to record the new variance key
+             */
+            request->vary_headers.clear();       /* free old "bad" variance key */
+            if (StoreEntry *pe = storeGetPublic(mem_obj->storeId(), mem_obj->method))
+                pe->release();
+        }
+
+        /* Make sure the request knows the variance status */
+        if (request->vary_headers.isEmpty())
+            request->vary_headers = httpMakeVaryMark(request, mem_obj->getReply());
+    }
+
+    // TODO: storeGetPublic() calls below may create unlocked entries.
+    // We should add/use storeHas() API or lock/unlock those entries.
+    if (!mem_obj->vary_headers.isEmpty() && !storeGetPublic(mem_obj->storeId(), mem_obj->method)) {
+        /* Create "vary" base object */
+        String vary;
+        StoreEntry *pe = storeCreateEntry(mem_obj->storeId(), mem_obj->logUri(), request->flags, request->method);
+        /* We are allowed to do this typecast */
+        HttpReply *rep = new HttpReply;
+        rep->setHeaders(Http::scOkay, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000);
+        vary = mem_obj->getReply()->header.getList(HDR_VARY);
+
+        if (vary.size()) {
+            /* Again, we own this structure layout */
+            rep->header.putStr(HDR_VARY, vary.termedBuf());
+            vary.clean();
+        }
+
+#if X_ACCELERATOR_VARY
+        vary = mem_obj->getReply()->header.getList(HDR_X_ACCELERATOR_VARY);
+
+        if (vary.size() > 0) {
+            /* Again, we own this structure layout */
+            rep->header.putStr(HDR_X_ACCELERATOR_VARY, vary.termedBuf());
+            vary.clean();
+        }
+
+#endif
+        pe->replaceHttpReply(rep, false); // no write until key is public
+
+        pe->timestampsSet();
+
+        pe->makePublic();
+
+        pe->startWriting(); // after makePublic()
+
+        pe->complete();
+
+        pe->unlock("StoreEntry::forcePublicKey+Vary");
+    }
+}
+
 StoreEntry *
 storeCreatePureEntry(const char *url, const char *log_url, const RequestFlags &flags, const HttpRequestMethod& method)
 {
@@ -1549,7 +1580,7 @@
     return 1;
 }
 
-void
+bool
 StoreEntry::timestampsSet()
 {
     const HttpReply *reply = getReply();
@@ -1587,14 +1618,27 @@
             served_date -= (squid_curtime - request_sent);
     }
 
+    time_t exp = 0;
     if (reply->expires > 0 && reply->date > -1)
-        expires = served_date + (reply->expires - reply->date);
+        exp = served_date + (reply->expires - reply->date);
     else
-        expires = reply->expires;
+        exp = reply->expires;
+
+    if (timestamp == served_date && expires == exp) {
+        // if the reply lacks LMT, then we now know that our effective
+        // LMT (i.e., timestamp) will stay the same, otherwise, old and
+        // new modification times must match
+        if (reply->last_modified < 0 || reply->last_modified == lastModified())
+            return false; // nothing has changed
+    }
+
+    expires = exp;
 
-    lastmod = reply->last_modified;
+    lastModified_ = reply->last_modified;
 
     timestamp = served_date;
+
+    return true;
 }
 
 void
@@ -1625,7 +1669,7 @@
     debugs(20, l, "StoreEntry->timestamp: " << timestamp);
     debugs(20, l, "StoreEntry->lastref: " << lastref);
     debugs(20, l, "StoreEntry->expires: " << expires);
-    debugs(20, l, "StoreEntry->lastmod: " << lastmod);
+    debugs(20, l, "StoreEntry->lastModified_: " << lastModified_);
     debugs(20, l, "StoreEntry->swap_file_sz: " << swap_file_sz);
     debugs(20, l, "StoreEntry->refcount: " << refcount);
     debugs(20, l, "StoreEntry->flags: " << storeEntryFlags(this));
@@ -1756,7 +1800,7 @@
     mem_obj->reset();
     HttpReply *rep = (HttpReply *) getReply();       // bypass const
     rep->reset();
-    expires = lastmod = timestamp = -1;
+    expires = lastModified_ = timestamp = -1;
 }
 
 /*
@@ -1966,13 +2010,10 @@
 }
 
 bool
-StoreEntry::modifiedSince(HttpRequest * request) const
+StoreEntry::modifiedSince(const time_t ims, const int imslen) const
 {
     int object_length;
-    time_t mod_time = lastmod;
-
-    if (mod_time < 0)
-        mod_time = timestamp;
+    const time_t mod_time = lastModified();
 
     debugs(88, 3, "modifiedSince: '" << url() << "'");
 
@@ -1987,16 +2028,16 @@
     if (object_length < 0)
         object_length = contentLen();
 
-    if (mod_time > request->ims) {
+    if (mod_time > ims) {
         debugs(88, 3, "--> YES: entry newer than client");
         return true;
-    } else if (mod_time < request->ims) {
+    } else if (mod_time < ims) {
         debugs(88, 3, "-->  NO: entry older than client");
         return false;
-    } else if (request->imslen < 0) {
+    } else if (imslen < 0) {
         debugs(88, 3, "-->  NO: same LMT, no client length");
         return false;
-    } else if (request->imslen == object_length) {
+    } else if (imslen == object_length) {
         debugs(88, 3, "-->  NO: same LMT, same length");
         return false;
     } else {
@@ -2093,6 +2134,18 @@
     return true;
 }
 
+const char *
+StoreEntry::describeTimestamps() const
+{
+    LOCAL_ARRAY(char, buf, 256);
+    snprintf(buf, 256, "LV:%-9d LU:%-9d LM:%-9d EX:%-9d",
+             static_cast<int>(timestamp),
+             static_cast<int>(lastref),
+             static_cast<int>(lastModified_),
+             static_cast<int>(expires));
+    return buf;
+}
+
 std::ostream &operator <<(std::ostream &os, const StoreEntry &e)
 {
     os << "e:";
diff -u -r -N squid-3.5.21/src/store_dir.cc squid-3.5.22/src/store_dir.cc
--- squid-3.5.21/src/store_dir.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/store_dir.cc	2016-10-10 08:58:01.000000000 +1300
@@ -77,7 +77,7 @@
         debugs(47, DBG_IMPORTANT, "Using Least Load store dir selection");
     }
 
-    if (UsingSmp() && IamWorkerProcess() && Config.onoff.collapsed_forwarding) {
+    if (UsingSmp() && IamWorkerProcess() && Config.onoff.collapsed_forwarding && smpAware()) {
         transients = new Transients;
         transients->init();
     }
@@ -916,7 +916,8 @@
 StoreController::allowCollapsing(StoreEntry *e, const RequestFlags &reqFlags,
                                  const HttpRequestMethod &reqMethod)
 {
-    e->makePublic(); // this is needed for both local and SMP collapsing
+    const KeyScope keyScope = reqFlags.refresh ? ksRevalidation : ksDefault;
+    e->makePublic(keyScope); // this is needed for both local and SMP collapsing
     if (transients)
         transients->startWriting(e, reqFlags, reqMethod);
     debugs(20, 3, "may " << (transients && e->mem_obj->xitTable.index >= 0 ?
@@ -1003,6 +1004,12 @@
     return found;
 }
 
+bool
+StoreController::smpAware() const
+{
+    return memStore || (swapDir.getRaw() && swapDir->smpAware());
+}
+
 StoreHashIndex::StoreHashIndex()
 {
     if (store_table)
@@ -1267,6 +1274,19 @@
     return new StoreSearchHashIndex (this);
 }
 
+bool
+StoreHashIndex::smpAware() const
+{
+    for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
+        // A mix is not supported, but we conservatively check every
+        // dir because features like collapsed revalidation should
+        // currently be disabled if any dir is SMP-aware
+        if (dir(i).smpAware())
+            return true;
+    }
+    return false;
+}
+
 CBDATA_CLASS_INIT(StoreSearchHashIndex);
 
 StoreSearchHashIndex::StoreSearchHashIndex(RefCount<StoreHashIndex> aSwapDir) :
diff -u -r -N squid-3.5.21/src/Store.h squid-3.5.22/src/Store.h
--- squid-3.5.21/src/Store.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/Store.h	2016-10-10 08:58:01.000000000 +1300
@@ -23,6 +23,7 @@
 #include "MemObject.h"
 #include "Range.h"
 #include "RemovalPolicy.h"
+#include "store_key_md5.h"
 #include "StoreIOBuffer.h"
 #include "StoreStats.h"
 
@@ -93,9 +94,13 @@
 
     void abort();
     void unlink();
-    void makePublic();
+    void makePublic(const KeyScope keyScope = ksDefault);
     void makePrivate();
-    void setPublicKey();
+    void setPublicKey(const KeyScope keyScope = ksDefault);
+    /// Resets existing public key to a public key with default scope,
+    /// releasing the old default-scope entry (if any).
+    /// Does nothing if the existing public key already has default scope.
+    void clearPublicKeyScope();
     void setPrivateKey();
     void expireNow();
     void releaseRequest();
@@ -130,7 +135,7 @@
     void registerAbort(STABH * cb, void *);
     void reset();
     void setMemStatus(mem_status_t);
-    void timestampsSet();
+    bool timestampsSet();
     void unregisterAbort();
     void destroyMemObject();
     int checkTooSmall();
@@ -138,7 +143,16 @@
     void delayAwareRead(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer callback);
 
     void setNoDelay (bool const);
-    bool modifiedSince(HttpRequest * request) const;
+    void lastModified(const time_t when) { lastModified_ = when; }
+    /// \returns entry's 'effective' modification time
+    time_t lastModified() const {
+        // may still return -1 if timestamp is not set
+        return lastModified_ < 0 ? timestamp : lastModified_;
+    }
+    /// \returns a formatted string with entry's timestamps
+    const char *describeTimestamps() const;
+    // TODO: consider removing currently unsupported imslen parameter
+    bool modifiedSince(const time_t ims, const int imslen = -1) const;
     /// has ETag matching at least one of the If-Match etags
     bool hasIfMatchEtag(const HttpRequest &request) const;
     /// has ETag matching at least one of the If-None-Match etags
@@ -155,7 +169,9 @@
     time_t timestamp;
     time_t lastref;
     time_t expires;
-    time_t lastmod;
+private:
+    time_t lastModified_; ///< received Last-Modified value or -1; use lastModified()
+public:
     uint64_t swap_file_sz;
     uint16_t refcount;
     uint16_t flags;
@@ -228,6 +244,9 @@
 
 private:
     bool checkTooBig() const;
+    void forcePublicKey(const cache_key *newkey);
+    void adjustVary();
+    const cache_key *calcPublicKey(const KeyScope keyScope);
 
     static MemAllocator *pool;
 
@@ -430,6 +449,9 @@
     /// update a local collapsed entry with fresh info from this cache (if any)
     virtual bool updateCollapsed(StoreEntry &collapsed) { return false; }
 
+    /// whether this storage is capable of serving multiple workers
+    virtual bool smpAware() const = 0;
+
 private:
     static RefCount<Store> CurrentRoot;
 };
@@ -450,10 +472,10 @@
 StoreEntry *storeGetPublic(const char *uri, const HttpRequestMethod& method);
 
 /// \ingroup StoreAPI
-StoreEntry *storeGetPublicByRequest(HttpRequest * request);
+StoreEntry *storeGetPublicByRequest(HttpRequest * request, const KeyScope keyScope = ksDefault);
 
 /// \ingroup StoreAPI
-StoreEntry *storeGetPublicByRequestMethod(HttpRequest * request, const HttpRequestMethod& method);
+StoreEntry *storeGetPublicByRequestMethod(HttpRequest * request, const HttpRequestMethod& method, const KeyScope keyScope = ksDefault);
 
 /// \ingroup StoreAPI
 /// Like storeCreatePureEntry(), but also locks the entry and sets entry key.
diff -u -r -N squid-3.5.21/src/StoreHashIndex.h squid-3.5.22/src/StoreHashIndex.h
--- squid-3.5.21/src/StoreHashIndex.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/StoreHashIndex.h	2016-10-10 08:58:01.000000000 +1300
@@ -59,6 +59,8 @@
 
     virtual StoreSearch *search(String const url, HttpRequest *);
 
+    virtual bool smpAware() const;
+
 private:
     /* migration logic */
     StorePointer store(int const x) const;
diff -u -r -N squid-3.5.21/src/store_key_md5.cc squid-3.5.22/src/store_key_md5.cc
--- squid-3.5.21/src/store_key_md5.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/store_key_md5.cc	2016-10-10 08:58:01.000000000 +1300
@@ -96,7 +96,7 @@
 }
 
 const cache_key *
-storeKeyPublic(const char *url, const HttpRequestMethod& method)
+storeKeyPublic(const char *url, const HttpRequestMethod& method, const KeyScope keyScope)
 {
     static cache_key digest[SQUID_MD5_DIGEST_LENGTH];
     unsigned char m = (unsigned char) method.id();
@@ -104,18 +104,20 @@
     SquidMD5Init(&M);
     SquidMD5Update(&M, &m, sizeof(m));
     SquidMD5Update(&M, (unsigned char *) url, strlen(url));
+    if (keyScope)
+        SquidMD5Update(&M, &keyScope, sizeof(keyScope));
     SquidMD5Final(digest, &M);
     return digest;
 }
 
 const cache_key *
-storeKeyPublicByRequest(HttpRequest * request)
+storeKeyPublicByRequest(HttpRequest * request, const KeyScope keyScope)
 {
-    return storeKeyPublicByRequestMethod(request, request->method);
+    return storeKeyPublicByRequestMethod(request, request->method, keyScope);
 }
 
 const cache_key *
-storeKeyPublicByRequestMethod(HttpRequest * request, const HttpRequestMethod& method)
+storeKeyPublicByRequestMethod(HttpRequest * request, const HttpRequestMethod& method, const KeyScope keyScope)
 {
     static cache_key digest[SQUID_MD5_DIGEST_LENGTH];
     unsigned char m = (unsigned char) method.id();
@@ -125,6 +127,9 @@
     SquidMD5Update(&M, &m, sizeof(m));
     SquidMD5Update(&M, (unsigned char *) url, strlen(url));
 
+    if (keyScope)
+        SquidMD5Update(&M, &keyScope, sizeof(keyScope));
+
     if (!request->vary_headers.isEmpty()) {
         SquidMD5Update(&M, request->vary_headers.rawContent(), request->vary_headers.length());
         debugs(20, 3, "updating public key by vary headers: " << request->vary_headers << " for: " << url);
diff -u -r -N squid-3.5.21/src/store_key_md5.h squid-3.5.22/src/store_key_md5.h
--- squid-3.5.21/src/store_key_md5.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/store_key_md5.h	2016-10-10 08:58:01.000000000 +1300
@@ -17,14 +17,19 @@
 class HttpRequestMethod;
 class HttpRequest;
 
+typedef enum {
+    ksDefault,
+    ksRevalidation
+} KeyScope;
+
 cache_key *storeKeyDup(const cache_key *);
 cache_key *storeKeyCopy(cache_key *, const cache_key *);
 void storeKeyFree(const cache_key *);
 const cache_key *storeKeyScan(const char *);
 const char *storeKeyText(const cache_key *);
-const cache_key *storeKeyPublic(const char *, const HttpRequestMethod&);
-const cache_key *storeKeyPublicByRequest(HttpRequest *);
-const cache_key *storeKeyPublicByRequestMethod(HttpRequest *, const HttpRequestMethod&);
+const cache_key *storeKeyPublic(const char *, const HttpRequestMethod&, const KeyScope keyScope = ksDefault);
+const cache_key *storeKeyPublicByRequest(HttpRequest *, const KeyScope keyScope = ksDefault);
+const cache_key *storeKeyPublicByRequestMethod(HttpRequest *, const HttpRequestMethod&, const KeyScope keyScope = ksDefault);
 const cache_key *storeKeyPrivate(const char *, const HttpRequestMethod&, int);
 int storeKeyHashBuckets(int);
 int storeKeyNull(const cache_key *);
diff -u -r -N squid-3.5.21/src/store_rebuild.cc squid-3.5.22/src/store_rebuild.cc
--- squid-3.5.21/src/store_rebuild.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/store_rebuild.cc	2016-10-10 08:58:01.000000000 +1300
@@ -254,7 +254,7 @@
             what->timestamp = tmp->timestamp;
             what->lastref = tmp->lastref;
             what->expires = tmp->expires;
-            what->lastmod = tmp->lastmod;
+            what->lastModified(tmp->lastmod);
             what->swap_file_sz = tmp->swap_file_sz;
             what->refcount = tmp->refcount;
             what->flags = tmp->flags;
diff -u -r -N squid-3.5.21/src/SwapDir.h squid-3.5.22/src/SwapDir.h
--- squid-3.5.21/src/SwapDir.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/SwapDir.h	2016-10-10 08:58:01.000000000 +1300
@@ -51,6 +51,7 @@
     virtual void memoryDisconnect(StoreEntry &e);
     virtual void allowCollapsing(StoreEntry *e, const RequestFlags &reqFlags, const HttpRequestMethod &reqMethod);
     virtual void syncCollapsed(const sfileno xitIndex);
+    virtual bool smpAware() const;
 
     virtual void init();
 
@@ -158,6 +159,7 @@
     virtual void getStats(StoreInfoStats &stats) const;
     virtual void stat (StoreEntry &anEntry) const;
     virtual StoreSearch *search(String const url, HttpRequest *) = 0;
+    virtual bool smpAware() const { return false; }
 
     /* migrated from store_dir.cc */
     bool objectSizeIsAcceptable(int64_t objsize) const;
diff -u -r -N squid-3.5.21/src/tests/stub_store.cc squid-3.5.22/src/tests/stub_store.cc
--- squid-3.5.21/src/tests/stub_store.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/tests/stub_store.cc	2016-10-10 08:58:01.000000000 +1300
@@ -42,9 +42,9 @@
 void StoreEntry::trimMemory(const bool preserveSwappable) STUB
 void StoreEntry::abort() STUB
 void StoreEntry::unlink() STUB
-void StoreEntry::makePublic() STUB
+void StoreEntry::makePublic(const KeyScope keyScope) STUB
 void StoreEntry::makePrivate() STUB
-void StoreEntry::setPublicKey() STUB
+void StoreEntry::setPublicKey(const KeyScope keyScope) STUB
 void StoreEntry::setPrivateKey() STUB
 void StoreEntry::expireNow() STUB
 void StoreEntry::releaseRequest() STUB
@@ -67,13 +67,13 @@
 void StoreEntry::registerAbort(STABH * cb, void *) STUB
 void StoreEntry::reset() STUB
 void StoreEntry::setMemStatus(mem_status_t) STUB
-void StoreEntry::timestampsSet() STUB
+bool StoreEntry::timestampsSet() STUB_RETVAL(false)
 void StoreEntry::unregisterAbort() STUB
 void StoreEntry::destroyMemObject() STUB
 int StoreEntry::checkTooSmall() STUB_RETVAL(0)
 void StoreEntry::delayAwareRead(const Comm::ConnectionPointer&, char *buf, int len, AsyncCall::Pointer callback) STUB
 void StoreEntry::setNoDelay (bool const) STUB
-bool StoreEntry::modifiedSince(HttpRequest * request) const STUB_RETVAL(false)
+bool StoreEntry::modifiedSince(const time_t ims, const int imslen) const STUB_RETVAL(false);
 bool StoreEntry::hasIfMatchEtag(const HttpRequest &request) const STUB_RETVAL(false)
 bool StoreEntry::hasIfNoneMatchEtag(const HttpRequest &request) const STUB_RETVAL(false)
 RefCount<SwapDir> StoreEntry::store() const STUB_RETVAL(NULL)
@@ -125,8 +125,8 @@
 size_t storeEntryInUse() STUB_RETVAL(0)
 void storeEntryReplaceObject(StoreEntry *, HttpReply *) STUB
 StoreEntry *storeGetPublic(const char *uri, const HttpRequestMethod& method) STUB_RETVAL(NULL)
-StoreEntry *storeGetPublicByRequest(HttpRequest * request) STUB_RETVAL(NULL)
-StoreEntry *storeGetPublicByRequestMethod(HttpRequest * request, const HttpRequestMethod& method) STUB_RETVAL(NULL)
+StoreEntry *storeGetPublicByRequest(HttpRequest * request, const KeyScope keyScope) STUB_RETVAL(NULL)
+StoreEntry *storeGetPublicByRequestMethod(HttpRequest * request, const HttpRequestMethod& method, const KeyScope keyScope) STUB_RETVAL(NULL)
 StoreEntry *storeCreateEntry(const char *, const char *, const RequestFlags &, const HttpRequestMethod&) STUB_RETVAL(NULL)
 StoreEntry *storeCreatePureEntry(const char *storeId, const char *logUrl, const RequestFlags &, const HttpRequestMethod&) STUB_RETVAL(NULL)
 void storeInit(void) STUB
diff -u -r -N squid-3.5.21/src/tests/testRock.cc squid-3.5.22/src/tests/testRock.cc
--- squid-3.5.21/src/tests/testRock.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/tests/testRock.cc	2016-10-10 08:58:01.000000000 +1300
@@ -143,8 +143,6 @@
 
     httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
 
-    httpReplyInitModule();  /* must go before accepting replies */
-
     mem_policy = createRemovalPolicy(Config.replPolicy);
 
     inited = true;
diff -u -r -N squid-3.5.21/src/tests/testStoreController.cc squid-3.5.22/src/tests/testStoreController.cc
--- squid-3.5.21/src/tests/testStoreController.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/tests/testStoreController.cc	2016-10-10 08:58:01.000000000 +1300
@@ -113,7 +113,7 @@
     e->lastref = squid_curtime;
     e->timestamp = squid_curtime;
     e->expires = squid_curtime;
-    e->lastmod = squid_curtime;
+    e->lastModified(squid_curtime);
     e->refcount = 1;
     EBIT_CLR(e->flags, RELEASE_REQUEST);
     EBIT_CLR(e->flags, KEY_PRIVATE);
diff -u -r -N squid-3.5.21/src/tests/testStore.h squid-3.5.22/src/tests/testStore.h
--- squid-3.5.21/src/tests/testStore.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/tests/testStore.h	2016-10-10 08:58:01.000000000 +1300
@@ -76,6 +76,8 @@
     virtual bool dereference(StoreEntry &, bool) { return true; }
 
     virtual StoreSearch *search(String const url, HttpRequest *);
+
+    virtual bool smpAware() const { return false; }
 };
 
 typedef RefCount<TestStore> TestStorePointer;
diff -u -r -N squid-3.5.21/src/tests/testStoreHashIndex.cc squid-3.5.22/src/tests/testStoreHashIndex.cc
--- squid-3.5.21/src/tests/testStoreHashIndex.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/tests/testStoreHashIndex.cc	2016-10-10 08:58:01.000000000 +1300
@@ -94,7 +94,7 @@
     e->lastref = squid_curtime;
     e->timestamp = squid_curtime;
     e->expires = squid_curtime;
-    e->lastmod = squid_curtime;
+    e->lastModified(squid_curtime);
     e->refcount = 1;
     EBIT_CLR(e->flags, RELEASE_REQUEST);
     EBIT_CLR(e->flags, KEY_PRIVATE);
diff -u -r -N squid-3.5.21/src/tests/testUfs.cc squid-3.5.22/src/tests/testUfs.cc
--- squid-3.5.21/src/tests/testUfs.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/tests/testUfs.cc	2016-10-10 08:58:01.000000000 +1300
@@ -75,8 +75,6 @@
 
     httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
 
-    httpReplyInitModule();  /* must go before accepting replies */
-
     inited = true;
 }
 
diff -u -r -N squid-3.5.21/src/Transients.cc squid-3.5.22/src/Transients.cc
--- squid-3.5.21/src/Transients.cc	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/Transients.cc	2016-10-10 08:58:01.000000000 +1300
@@ -197,7 +197,8 @@
     e->mem_obj->xitTable.io = MemObject::ioReading;
     e->mem_obj->xitTable.index = index;
 
-    e->setPublicKey();
+    // TODO: Support collapsed revalidation for SMP-aware caches
+    e->setPublicKey(ksDefault);
     assert(e->key);
 
     // How do we know its SMP- and not just locally-collapsed? A worker gets
diff -u -r -N squid-3.5.21/src/Transients.h squid-3.5.22/src/Transients.h
--- squid-3.5.21/src/Transients.h	2016-09-09 06:56:46.000000000 +1200
+++ squid-3.5.22/src/Transients.h	2016-10-10 08:58:01.000000000 +1300
@@ -72,6 +72,7 @@
     virtual bool dereference(StoreEntry &, bool);
     virtual void markForUnlink(StoreEntry &e);
     virtual void maintain();
+    virtual bool smpAware() const { return true; }
 
     static int64_t EntryLimit();
 
