diff -u -r -N squid-4.0.16/cfgaux/config.guess squid-4.0.17/cfgaux/config.guess
--- squid-4.0.16/cfgaux/config.guess	2016-10-31 01:26:37.000000000 +1300
+++ squid-4.0.17/cfgaux/config.guess	2016-12-16 23:13:09.000000000 +1300
@@ -2,7 +2,7 @@
 # Attempt to guess a canonical system name.
 #   Copyright 1992-2016 Free Software Foundation, Inc.
 
-timestamp='2016-04-02'
+timestamp='2016-10-02'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -186,9 +186,12 @@
 	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
 	esac
 	# The Operating System including object format, if it has switched
-	# to ELF recently, or will in the future.
+	# to ELF recently (or will in the future) and ABI.
 	case "${UNAME_MACHINE_ARCH}" in
-	    arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax)
+	    earm*)
+		os=netbsdelf
+		;;
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
 		eval $set_cc_for_build
 		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
 			| grep -q __ELF__
@@ -997,6 +1000,9 @@
 	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
 	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
 	;;
+    mips64el:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
     openrisc*:Linux:*:*)
 	echo or1k-unknown-linux-${LIBC}
 	exit ;;
@@ -1029,6 +1035,9 @@
     ppcle:Linux:*:*)
 	echo powerpcle-unknown-linux-${LIBC}
 	exit ;;
+    riscv32:Linux:*:* | riscv64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
     s390:Linux:*:* | s390x:Linux:*:*)
 	echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
 	exit ;;
@@ -1408,18 +1417,17 @@
 cat >&2 <<EOF
 $0: unable to guess system type
 
-This script, last modified $timestamp, has failed to recognize
-the operating system you are using. It is advised that you
-download the most up to date version of the config scripts from
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite
+config.guess and config.sub with the latest versions from:
 
   http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
 and
   http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
 
-If the version you run ($0) is already up to date, please
-send the following data and any information you think might be
-pertinent to <config-patches@gnu.org> in order to provide the needed
-information to handle your system.
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
 
 config.guess timestamp = $timestamp
 
diff -u -r -N squid-4.0.16/cfgaux/config.sub squid-4.0.17/cfgaux/config.sub
--- squid-4.0.16/cfgaux/config.sub	2016-10-31 01:26:37.000000000 +1300
+++ squid-4.0.17/cfgaux/config.sub	2016-12-16 23:13:09.000000000 +1300
@@ -2,7 +2,7 @@
 # Configuration validation subroutine script.
 #   Copyright 1992-2016 Free Software Foundation, Inc.
 
-timestamp='2016-03-30'
+timestamp='2016-11-04'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -117,7 +117,7 @@
   nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
   linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
   knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
-  kopensolaris*-gnu* | \
+  kopensolaris*-gnu* | cloudabi*-eabi* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
     os=-$maybe_os
     basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
@@ -301,6 +301,7 @@
 	| open8 | or1k | or1knd | or32 \
 	| pdp10 | pdp11 | pj | pjl \
 	| powerpc | powerpc64 | powerpc64le | powerpcle \
+	| pru \
 	| pyramid \
 	| riscv32 | riscv64 \
 	| rl78 | rx \
@@ -428,6 +429,7 @@
 	| orion-* \
 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
 	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+	| pru-* \
 	| pyramid-* \
 	| riscv32-* | riscv64-* \
 	| rl78-* | romp-* | rs6000-* | rx-* \
@@ -643,6 +645,14 @@
 		basic_machine=m68k-bull
 		os=-sysv3
 		;;
+	e500v[12])
+		basic_machine=powerpc-unknown
+		os=$os"spe"
+		;;
+	e500v[12]-*)
+		basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+		os=$os"spe"
+		;;
 	ebmon29k)
 		basic_machine=a29k-amd
 		os=-ebmon
@@ -1022,7 +1032,7 @@
 	ppc-* | ppcbe-*)
 		basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
 		;;
-	ppcle | powerpclittle | ppc-le | powerpc-little)
+	ppcle | powerpclittle)
 		basic_machine=powerpcle-unknown
 		;;
 	ppcle-* | powerpclittle-*)
@@ -1032,7 +1042,7 @@
 		;;
 	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
 		;;
-	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+	ppc64le | powerpc64little)
 		basic_machine=powerpc64le-unknown
 		;;
 	ppc64le-* | powerpc64little-*)
@@ -1389,7 +1399,7 @@
 	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
 	      | -chorusos* | -chorusrdb* | -cegcc* \
 	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
-	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+	      | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
 	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
 	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
 	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
@@ -1399,7 +1409,7 @@
 	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
 	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
 	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
-	      | -onefs* | -tirtos*)
+	      | -onefs* | -tirtos* | -phoenix* | -fuchsia*)
 	# Remember, each alternative MUST END IN *, to match a version number.
 		;;
 	-qnx*)
diff -u -r -N squid-4.0.16/ChangeLog squid-4.0.17/ChangeLog
--- squid-4.0.16/ChangeLog	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/ChangeLog	2016-12-16 23:06:20.000000000 +1300
@@ -1,3 +1,13 @@
+Changes to squid-4.0.17 (16 Dec 2016):
+
+	- Bug 4630: user credentials cache cleanup not re-scheduled
+	- Bug 4610 partial: compile errors on Solaris 11.3 with Oracle Studio 12.5
+	- Bug 4599 partial: initial support for OpenSSL v1.1
+	- TLS: Support tunneling of bumped non-HTTP traffic
+	- ... and many code polishing and performance updates
+	- ... and some documentation updates
+	- ... and some fixes from 3.5.23
+
 Changes to squid-4.0.16 (30 Oct 2016):
 
 	- Avoid segfaults when lacking the server name for certificate validator
@@ -245,6 +255,29 @@
 	- ... and many documentation changes
 	- ... and much code cleanup and polishing
 
+Changes to squid-3.5.23 (16 Dec 2016):
+
+	- Bug 4627: fix generate-host-certificates and dynamic_cert_mem_cache_size docs
+	- Bug 4620: NetBSD build error with --enable-ipf-transparent
+	- Bug 4567: Strange IPv6 shown in access.log
+	- Bug 4406: SIGSEV in TunnelStateData::handleConnectResponse() during reconfigure and restart
+	- Bug 4174 partial: fix Write.cc:41 "!ccb->active()" assertion.
+	- Bug 4169: HIT marked as MISS when If-None-Match does not match
+	- Bug 4007: Hang on DNS query with dead-end CNAME
+	- Bug 4004 partial: Fix segfault via Ftp::Client::readControlReply
+	- Bug 3940 partial: hostHeaderVerify failures MISS when they should be HIT
+	- Bug 3533: Cache still valid after HTTP/1.1 303 See Other
+	- Bug 3379: Combination of If-Match and a Cache Hit result in TCP Connection Failure
+	- Bug 3290: authenticate_ttl not working for digest authentication
+	- Bug 2258: bypassing cache but not destroying cache entry
+	- HTTP/1.1: make Vary:* objects cacheable
+	- HTTP/1.1: Add registered codes entry for new 103 (Early Hints) status code
+	- Support IPv6 NAT with PF for NetBSD and FreeBSD
+	- TLS: Make key= before cert= an error instead of quietly hiding the issue
+	- ... and some debug updates
+	- ... and some build fixes
+	- ... and several documentation updates
+
 Changes to squid-3.5.22 (09 Oct 2016):
 
 	- Bug 4594: build failure with clang 3.9
diff -u -r -N squid-4.0.16/compat/compat.h squid-4.0.17/compat/compat.h
--- squid-4.0.16/compat/compat.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/compat/compat.h	2016-12-16 23:06:20.000000000 +1300
@@ -29,19 +29,6 @@
 /******************************************************/
 #include "compat/osdetect.h"
 
-/* ugly hack. But we need to set this REALLY soon in the header */
-#if _SQUID_SOLARIS_ && !defined(__GNUC__) && !defined(__GNUG__)
-#ifndef __EXTENSIONS__
-#define __EXTENSIONS__ 1
-#endif
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 1
-#endif
-#ifndef _XOPEN_SOURCE_EXTENDED
-#define _XOPEN_SOURCE_EXTENDED 1
-#endif
-#endif
-
 /* Solaris 10 has a broken definition for minor_t in IPFilter compat.
  * We must pre-define before doing anything with OS headers so the OS
  * do not. Then un-define it before using the IPFilter *_compat.h headers.
diff -u -r -N squid-4.0.16/compat/os/solaris.h squid-4.0.17/compat/os/solaris.h
--- squid-4.0.16/compat/os/solaris.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/compat/os/solaris.h	2016-12-16 23:06:20.000000000 +1300
@@ -48,6 +48,11 @@
 #include <sys/resource.h>
 SQUIDCEXTERN int getrusage(int, struct rusage *);
 
+// Solaris 11 needs this before <sys/socket.h> to get the definition for msg_control
+// and possibly other type definitions we dont know about specifically
+#define _XPG4_2 1
+#include <sys/socket.h>
+
 /**
  * prototypes for system function missing from system includes
  * on some Solaris systems.
diff -u -r -N squid-4.0.16/configure squid-4.0.17/configure
--- squid-4.0.16/configure	2016-10-31 01:28:43.000000000 +1300
+++ squid-4.0.17/configure	2016-12-16 23:19:31.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 4.0.16.
+# Generated by GNU Autoconf 2.69 for Squid Web Proxy 4.0.17.
 #
 # 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='4.0.16'
-PACKAGE_STRING='Squid Web Proxy 4.0.16'
+PACKAGE_VERSION='4.0.17'
+PACKAGE_STRING='Squid Web Proxy 4.0.17'
 PACKAGE_BUGREPORT='http://bugs.squid-cache.org/'
 PACKAGE_URL=''
 
@@ -1648,7 +1648,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 4.0.16 to adapt to many kinds of systems.
+\`configure' configures Squid Web Proxy 4.0.17 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1719,7 +1719,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Squid Web Proxy 4.0.16:";;
+     short | recursive ) echo "Configuration of Squid Web Proxy 4.0.17:";;
    esac
   cat <<\_ACEOF
 
@@ -2148,7 +2148,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Squid Web Proxy configure 4.0.16
+Squid Web Proxy configure 4.0.17
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -3252,7 +3252,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 4.0.16, which was
+It was created by Squid Web Proxy $as_me 4.0.17, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -4119,7 +4119,7 @@
 
 # Define the identity of the package.
  PACKAGE='squid'
- VERSION='4.0.16'
+ VERSION='4.0.17'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -42599,7 +42599,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 4.0.16, which was
+This file was extended by Squid Web Proxy $as_me 4.0.17, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -42665,7 +42665,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 4.0.16
+Squid Web Proxy config.status 4.0.17
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff -u -r -N squid-4.0.16/configure.ac squid-4.0.17/configure.ac
--- squid-4.0.16/configure.ac	2016-10-31 01:28:42.000000000 +1300
+++ squid-4.0.17/configure.ac	2016-12-16 23:19:28.000000000 +1300
@@ -5,7 +5,7 @@
 ## Please see the COPYING and CONTRIBUTORS files for details.
 ##
 
-AC_INIT([Squid Web Proxy],[4.0.16],[http://bugs.squid-cache.org/],[squid])
+AC_INIT([Squid Web Proxy],[4.0.17],[http://bugs.squid-cache.org/],[squid])
 AC_PREREQ(2.61)
 AC_CONFIG_HEADERS([include/autoconf.h])
 AC_CONFIG_AUX_DIR(cfgaux)
diff -u -r -N squid-4.0.16/doc/release-notes/release-4.html squid-4.0.17/doc/release-notes/release-4.html
--- squid-4.0.16/doc/release-notes/release-4.html	2016-10-31 03:14:55.000000000 +1300
+++ squid-4.0.17/doc/release-notes/release-4.html	2016-12-17 07:18:56.000000000 +1300
@@ -2,10 +2,10 @@
 <HTML>
 <HEAD>
  <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.72">
- <TITLE>Squid 4.0.16 release notes</TITLE>
+ <TITLE>Squid 4.0.17 release notes</TITLE>
 </HEAD>
 <BODY>
-<H1>Squid 4.0.16 release notes</H1>
+<H1>Squid 4.0.17 release notes</H1>
 
 <H2>Squid Developers</H2>
 <HR>
@@ -61,7 +61,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-4.0.16 for testing.</P>
+<P>The Squid Team are pleased to announce the release of Squid-4.0.17 for testing.</P>
 <P>This new release is available for download from 
 <A HREF="http://www.squid-cache.org/Versions/v4/">http://www.squid-cache.org/Versions/v4/</A> or the
 <A HREF="http://www.squid-cache.org/Download/http-mirrors.html">mirrors</A>.</P>
@@ -355,6 +355,7 @@
 <P>Removed <EM>version=</EM> option. Use <EM>tls-options=</EM> instead.</P>
 <P>Manual squid.conf update may be required on upgrade.</P>
 <P>Replaced <EM>cafile=</EM> with <EM>tls-cafile=</EM> which takes multiple entries.</P>
+<P>Changed default value of <EM>generate-host-certificates</EM> to ON.</P>
 
 <DT><B>https_port</B><DD>
 <P>New option <EM>tls-min-version=1.N</EM> to set minimum TLS version allowed.</P>
@@ -366,6 +367,7 @@
 <P>Removed <EM>version=</EM> option. Use <EM>tls-options=</EM> instead.</P>
 <P>Manual squid.conf update may be required on upgrade.</P>
 <P>Replaced <EM>cafile=</EM> with <EM>tls-cafile=</EM> which takes multiple entries.</P>
+<P>Changed default value of <EM>generate-host-certificates</EM> to ON.</P>
 
 <DT><B>icap_service</B><DD>
 <P>New scheme <EM>icaps://</EM> to enable TLS/SSL connections to Secure ICAP
diff -u -r -N squid-4.0.16/include/version.h squid-4.0.17/include/version.h
--- squid-4.0.16/include/version.h	2016-10-31 01:28:44.000000000 +1300
+++ squid-4.0.17/include/version.h	2016-12-16 23:19:32.000000000 +1300
@@ -7,7 +7,7 @@
  */
 
 #ifndef SQUID_RELEASE_TIME
-#define SQUID_RELEASE_TIME 1477830283
+#define SQUID_RELEASE_TIME 1481882763
 #endif
 
 /*
diff -u -r -N squid-4.0.16/RELEASENOTES.html squid-4.0.17/RELEASENOTES.html
--- squid-4.0.16/RELEASENOTES.html	2016-10-31 03:14:55.000000000 +1300
+++ squid-4.0.17/RELEASENOTES.html	2016-12-17 07:18:56.000000000 +1300
@@ -2,10 +2,10 @@
 <HTML>
 <HEAD>
  <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.72">
- <TITLE>Squid 4.0.16 release notes</TITLE>
+ <TITLE>Squid 4.0.17 release notes</TITLE>
 </HEAD>
 <BODY>
-<H1>Squid 4.0.16 release notes</H1>
+<H1>Squid 4.0.17 release notes</H1>
 
 <H2>Squid Developers</H2>
 <HR>
@@ -61,7 +61,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-4.0.16 for testing.</P>
+<P>The Squid Team are pleased to announce the release of Squid-4.0.17 for testing.</P>
 <P>This new release is available for download from 
 <A HREF="http://www.squid-cache.org/Versions/v4/">http://www.squid-cache.org/Versions/v4/</A> or the
 <A HREF="http://www.squid-cache.org/Download/http-mirrors.html">mirrors</A>.</P>
@@ -355,6 +355,7 @@
 <P>Removed <EM>version=</EM> option. Use <EM>tls-options=</EM> instead.</P>
 <P>Manual squid.conf update may be required on upgrade.</P>
 <P>Replaced <EM>cafile=</EM> with <EM>tls-cafile=</EM> which takes multiple entries.</P>
+<P>Changed default value of <EM>generate-host-certificates</EM> to ON.</P>
 
 <DT><B>https_port</B><DD>
 <P>New option <EM>tls-min-version=1.N</EM> to set minimum TLS version allowed.</P>
@@ -366,6 +367,7 @@
 <P>Removed <EM>version=</EM> option. Use <EM>tls-options=</EM> instead.</P>
 <P>Manual squid.conf update may be required on upgrade.</P>
 <P>Replaced <EM>cafile=</EM> with <EM>tls-cafile=</EM> which takes multiple entries.</P>
+<P>Changed default value of <EM>generate-host-certificates</EM> to ON.</P>
 
 <DT><B>icap_service</B><DD>
 <P>New scheme <EM>icaps://</EM> to enable TLS/SSL connections to Secure ICAP
diff -u -r -N squid-4.0.16/src/acl/external/delayer/ext_delayer_acl.8 squid-4.0.17/src/acl/external/delayer/ext_delayer_acl.8
--- squid-4.0.16/src/acl/external/delayer/ext_delayer_acl.8	2016-10-31 03:17:46.000000000 +1300
+++ squid-4.0.17/src/acl/external/delayer/ext_delayer_acl.8	2016-12-17 07:21:40.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "EXT_DELAYER_ACL 8"
-.TH EXT_DELAYER_ACL 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH EXT_DELAYER_ACL 8 "2016-12-16" "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-4.0.16/src/acl/external/SQL_session/ext_sql_session_acl.8 squid-4.0.17/src/acl/external/SQL_session/ext_sql_session_acl.8
--- squid-4.0.16/src/acl/external/SQL_session/ext_sql_session_acl.8	2016-10-31 03:18:16.000000000 +1300
+++ squid-4.0.17/src/acl/external/SQL_session/ext_sql_session_acl.8	2016-12-17 07:22:03.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "EXT_SQL_SESSION_ACL 8"
-.TH EXT_SQL_SESSION_ACL 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH EXT_SQL_SESSION_ACL 8 "2016-12-16" "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-4.0.16/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8 squid-4.0.17/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8
--- squid-4.0.16/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8	2016-10-31 03:18:28.000000000 +1300
+++ squid-4.0.17/src/acl/external/wbinfo_group/ext_wbinfo_group_acl.8	2016-12-17 07:22:13.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "EXT_WBINFO_GROUP_ACL 8"
-.TH EXT_WBINFO_GROUP_ACL 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH EXT_WBINFO_GROUP_ACL 8 "2016-12-16" "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-4.0.16/src/acl/ServerCertificate.cc squid-4.0.17/src/acl/ServerCertificate.cc
--- squid-4.0.16/src/acl/ServerCertificate.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/acl/ServerCertificate.cc	2016-12-16 23:06:20.000000000 +1300
@@ -21,16 +21,16 @@
 int
 ACLServerCertificateStrategy::match(ACLData<MatchType> * &data, ACLFilledChecklist *checklist, ACLFlags &)
 {
-    X509 *cert = NULL;
-    if (checklist->serverCert.get())
-        cert = checklist->serverCert.get();
+    Security::CertPointer cert;
+    if (checklist->serverCert)
+        cert = checklist->serverCert;
     else if (checklist->conn() != NULL && checklist->conn()->serverBump())
-        cert = checklist->conn()->serverBump()->serverCert.get();
+        cert = checklist->conn()->serverBump()->serverCert;
 
     if (!cert)
         return 0;
 
-    return data->match(cert);
+    return data->match(cert.get());
 }
 
 ACLServerCertificateStrategy *
diff -u -r -N squid-4.0.16/src/acl/Tree.cc squid-4.0.17/src/acl/Tree.cc
--- squid-4.0.16/src/acl/Tree.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/acl/Tree.cc	2016-12-16 23:06:20.000000000 +1300
@@ -57,35 +57,6 @@
     InnerNode::add(rule);
 }
 
-SBufList
-Acl::Tree::treeDump(const char *prefix, const ActionToString &convert) const
-{
-    SBufList text;
-    Actions::const_iterator action = actions.begin();
-    typedef Nodes::const_iterator NCI;
-    for (NCI node = nodes.begin(); node != nodes.end(); ++node) {
-
-        text.push_back(SBuf(prefix));
-
-        if (action != actions.end()) {
-            const char *act = convert ? convert[action->kind] :
-                              (*action == ACCESS_ALLOWED ? "allow" : "deny");
-            text.push_back(act?SBuf(act):SBuf("???"));
-            ++action;
-        }
-
-#if __cplusplus >= 201103L
-        text.splice(text.end(), (*node)->dump());
-#else
-        // temp is needed until c++11 move constructor
-        SBufList temp = (*node)->dump();
-        text.splice(text.end(), temp);
-#endif
-        text.push_back(SBuf("\n"));
-    }
-    return text;
-}
-
 bool
 Acl::Tree::bannedAction(ACLChecklist *checklist, Nodes::const_iterator node) const
 {
diff -u -r -N squid-4.0.16/src/acl/Tree.h squid-4.0.17/src/acl/Tree.h
--- squid-4.0.16/src/acl/Tree.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/acl/Tree.h	2016-12-16 23:06:20.000000000 +1300
@@ -25,9 +25,9 @@
 
 public:
     /// dumps <name, action, rule, new line> tuples
-    /// action.kind is mapped to a string using the supplied conversion table
-    typedef const char **ActionToString;
-    SBufList treeDump(const char *name, const ActionToString &convert) const;
+    /// the supplied converter maps action.kind to a string
+    template <class ActionToStringConverter>
+    SBufList treeDump(const char *name, ActionToStringConverter converter) const;
 
     /// Returns the corresponding action after a successful tree match.
     allow_t winningAction() const;
@@ -49,6 +49,42 @@
     Actions actions;
 };
 
+inline const char *
+AllowOrDeny(const allow_t &action)
+{
+    return action == ACCESS_ALLOWED ? "allow" : "deny";
+}
+
+template <class ActionToStringConverter>
+inline SBufList
+Tree::treeDump(const char *prefix, ActionToStringConverter converter) const
+{
+    SBufList text;
+    Actions::const_iterator action = actions.begin();
+    typedef Nodes::const_iterator NCI;
+    for (NCI node = nodes.begin(); node != nodes.end(); ++node) {
+
+        text.push_back(SBuf(prefix));
+
+        if (action != actions.end()) {
+            static const SBuf DefaultActString("???");
+            const char *act = converter(*action);
+            text.push_back(act ? SBuf(act) : DefaultActString);
+            ++action;
+        }
+
+#if __cplusplus >= 201103L
+        text.splice(text.end(), (*node)->dump());
+#else
+        // temp is needed until c++11 move constructor
+        SBufList temp = (*node)->dump();
+        text.splice(text.end(), temp);
+#endif
+        text.push_back(SBuf("\n"));
+    }
+    return text;
+}
+
 } // namespace Acl
 
 #endif /* SQUID_ACL_TREE_H */
diff -u -r -N squid-4.0.16/src/anyp/ProtocolType.cc squid-4.0.17/src/anyp/ProtocolType.cc
--- squid-4.0.16/src/anyp/ProtocolType.cc	2016-10-31 03:16:31.000000000 +1300
+++ squid-4.0.17/src/anyp/ProtocolType.cc	2016-12-17 07:20:24.000000000 +1300
@@ -25,6 +25,7 @@
 	"ICY",
 	"TLS",
 	"SSL",
+	"AUTHORITY_FORM",
 	"UNKNOWN",
 	"MAX"
 };
diff -u -r -N squid-4.0.16/src/anyp/ProtocolType.h squid-4.0.17/src/anyp/ProtocolType.h
--- squid-4.0.16/src/anyp/ProtocolType.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/anyp/ProtocolType.h	2016-12-16 23:06:20.000000000 +1300
@@ -38,6 +38,7 @@
     PROTO_ICY,
     PROTO_TLS,
     PROTO_SSL,
+    PROTO_AUTHORITY_FORM,
     PROTO_UNKNOWN,
     PROTO_MAX
 } ProtocolType;
diff -u -r -N squid-4.0.16/src/auth/basic/DB/basic_db_auth.8 squid-4.0.17/src/auth/basic/DB/basic_db_auth.8
--- squid-4.0.16/src/auth/basic/DB/basic_db_auth.8	2016-10-31 03:20:15.000000000 +1300
+++ squid-4.0.17/src/auth/basic/DB/basic_db_auth.8	2016-12-17 07:23:24.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "BASIC_DB_AUTH 8"
-.TH BASIC_DB_AUTH 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH BASIC_DB_AUTH 8 "2016-12-16" "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-4.0.16/src/auth/basic/POP3/basic_pop3_auth.8 squid-4.0.17/src/auth/basic/POP3/basic_pop3_auth.8
--- squid-4.0.16/src/auth/basic/POP3/basic_pop3_auth.8	2016-10-31 03:20:41.000000000 +1300
+++ squid-4.0.17/src/auth/basic/POP3/basic_pop3_auth.8	2016-12-17 07:23:49.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "BASIC_POP3_AUTH 8"
-.TH BASIC_POP3_AUTH 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH BASIC_POP3_AUTH 8 "2016-12-16" "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-4.0.16/src/auth/CredentialsCache.cc squid-4.0.17/src/auth/CredentialsCache.cc
--- squid-4.0.16/src/auth/CredentialsCache.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/auth/CredentialsCache.cc	2016-12-16 23:06:20.000000000 +1300
@@ -98,6 +98,7 @@
             ++i;
         }
     }
+    gcScheduled_ = false;
     scheduleCleanup();
 }
 
diff -u -r -N squid-4.0.16/src/auth/digest/Config.cc squid-4.0.17/src/auth/digest/Config.cc
--- squid-4.0.16/src/auth/digest/Config.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/auth/digest/Config.cc	2016-12-16 23:06:20.000000000 +1300
@@ -215,7 +215,7 @@
     if (!digest_nonce_cache) {
         digest_nonce_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
         assert(digest_nonce_cache);
-        eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->nonceGCInterval, 1);
+        eventAdd("Digest nonce cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->nonceGCInterval, 1);
     }
 }
 
@@ -279,7 +279,7 @@
     debugs(29, 3, "Finished cleaning the nonce cache.");
 
     if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->active())
-        eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->nonceGCInterval, 1);
+        eventAdd("Digest nonce cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->nonceGCInterval, 1);
 }
 
 static void
@@ -1064,6 +1064,10 @@
          * the user agent won't change user name without warning.
          */
         authDigestUserLinkNonce(digest_user, nonce);
+
+        /* auth_user is now linked, we reset these values
+         * after external auth occurs anyway */
+        auth_user->expiretime = current_time.tv_sec;
     } else {
         debugs(29, 9, "Found user '" << username << "' in the user cache as '" << auth_user << "'");
         digest_user = static_cast<Auth::Digest::User *>(auth_user.getRaw());
diff -u -r -N squid-4.0.16/src/auth/digest/UserRequest.cc squid-4.0.17/src/auth/digest/UserRequest.cc
--- squid-4.0.16/src/auth/digest/UserRequest.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/auth/digest/UserRequest.cc	2016-12-16 23:06:20.000000000 +1300
@@ -187,12 +187,7 @@
     auth_user->credentials(Auth::Ok);
 
     /* password was checked and did match */
-    debugs(29, 4, HERE << "user '" << auth_user->username() << "' validated OK");
-
-    /* auth_user is now linked, we reset these values
-     * after external auth occurs anyway */
-    auth_user->expiretime = current_time.tv_sec;
-    return;
+    debugs(29, 4, "user '" << auth_user->username() << "' validated OK");
 }
 
 Auth::Direction
diff -u -r -N squid-4.0.16/src/base/HardFun.h squid-4.0.17/src/base/HardFun.h
--- squid-4.0.16/src/base/HardFun.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/base/HardFun.h	2016-12-16 23:06:20.000000000 +1300
@@ -14,7 +14,7 @@
  */
 template <class ReturnType, class ArgType, ReturnType (*fun)(ArgType)>
 struct HardFun {
-    ReturnType operator()(ArgType arg) { fun(arg); }
+    ReturnType operator()(ArgType arg) { return fun(arg); }
 };
 
 #endif /* _SQUID_SRC_BASE_HARDFUN_H */
diff -u -r -N squid-4.0.16/src/cache_cf.cc squid-4.0.17/src/cache_cf.cc
--- squid-4.0.16/src/cache_cf.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/cache_cf.cc	2016-12-16 23:06:20.000000000 +1300
@@ -1340,7 +1340,7 @@
 dump_acl_access(StoreEntry * entry, const char *name, acl_access * head)
 {
     if (head)
-        dump_SBufList(entry, head->treeDump(name,NULL));
+        dump_SBufList(entry, head->treeDump(name, &Acl::AllowOrDeny));
 }
 
 static void
@@ -1826,6 +1826,23 @@
 }
 #endif /* USE_AUTH */
 
+static void
+ParseAclWithAction(acl_access **access, const allow_t &action, const char *desc, ACL *acl = nullptr)
+{
+    assert(access);
+    SBuf name;
+    if (!*access) {
+        *access = new Acl::Tree;
+        name.Printf("(%s rules)", desc);
+        (*access)->context(name.c_str(), config_input_line);
+    }
+    Acl::AndNode *rule = new Acl::AndNode;
+    name.Printf("(%s rule)", desc);
+    rule->context(name.c_str(), config_input_line);
+    acl ? rule->add(acl) : rule->lineParse();
+    (*access)->add(rule, action);
+}
+
 /* TODO: just return the object, the # is irrelevant */
 static int
 find_fstype(char *type)
@@ -2291,17 +2308,14 @@
 static void
 dump_cachemgrpasswd(StoreEntry * entry, const char *name, Mgr::ActionPasswordList * list)
 {
-    wordlist *w;
-
-    while (list != NULL) {
+    while (list) {
         if (strcmp(list->passwd, "none") && strcmp(list->passwd, "disable"))
             storeAppendPrintf(entry, "%s XXXXXXXXXX", name);
         else
             storeAppendPrintf(entry, "%s %s", name, list->passwd);
 
-        for (w = list->actions; w != NULL; w = w->next) {
-            storeAppendPrintf(entry, " %s", w->key);
-        }
+        for (auto w : list->actions)
+            entry->appendf(" " SQUIDSBUFPH, SQUIDSBUFPRINT(w));
 
         storeAppendPrintf(entry, "\n");
         list = list->next;
@@ -2311,16 +2325,16 @@
 static void
 parse_cachemgrpasswd(Mgr::ActionPasswordList ** head)
 {
-    char *passwd = NULL;
-    wordlist *actions = NULL;
-    Mgr::ActionPasswordList *p;
-    Mgr::ActionPasswordList **P;
+    char *passwd = nullptr;
     parse_string(&passwd);
-    parse_wordlist(&actions);
-    p = new Mgr::ActionPasswordList;
+
+    Mgr::ActionPasswordList *p = new Mgr::ActionPasswordList;
     p->passwd = passwd;
-    p->actions = actions;
 
+    while (char *token = ConfigParser::NextQuotedToken())
+        p->actions.push_back(SBuf(token));
+
+    Mgr::ActionPasswordList **P;
     for (P = head; *P; P = &(*P)->next) {
         /*
          * See if any of the actions from this line already have a
@@ -2330,15 +2344,12 @@
          * requested action.  Thus, we should warn users who might
          * think they can have two passwords for the same action.
          */
-        wordlist *w;
-        wordlist *u;
-
-        for (w = (*P)->actions; w; w = w->next) {
-            for (u = actions; u; u = u->next) {
-                if (strcmp(w->key, u->key))
+        for (const auto &w : (*P)->actions) {
+            for (const auto &u : p->actions) {
+                if (w != u)
                     continue;
 
-                debugs(0, DBG_CRITICAL, "WARNING: action '" << u->key << "' (line " << config_lineno << ") already has a password");
+                debugs(0, DBG_PARSE_NOTE(1), "ERROR: action '" << u << "' (line " << config_lineno << ") already has a password");
             }
         }
     }
@@ -2349,14 +2360,8 @@
 static void
 free_cachemgrpasswd(Mgr::ActionPasswordList ** head)
 {
-    Mgr::ActionPasswordList *p;
-
-    while ((p = *head) != NULL) {
-        *head = p->next;
-        xfree(p->passwd);
-        wordlistDestroy(&p->actions);
-        xfree(p);
-    }
+    delete *head;
+    *head = nullptr;
 }
 
 static void
@@ -4638,24 +4643,16 @@
 
     bumpCfgStyleLast = bumpCfgStyleNow;
 
-    Acl::AndNode *rule = new Acl::AndNode;
-    rule->context("(ssl_bump rule)", config_input_line);
-    rule->lineParse();
     // empty rule OK
-
-    assert(ssl_bump);
-    if (!*ssl_bump) {
-        *ssl_bump = new Acl::Tree;
-        (*ssl_bump)->context("(ssl_bump rules)", config_input_line);
-    }
-
-    (*ssl_bump)->add(rule, action);
+    ParseAclWithAction(ssl_bump, action, "ssl_bump");
 }
 
 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
 {
     if (ssl_bump)
-        dump_SBufList(entry, ssl_bump->treeDump(name, Ssl::BumpModeStr));
+        dump_SBufList(entry, ssl_bump->treeDump(name, [](const allow_t &action) {
+        return Ssl::BumpModeStr.at(action.kind);
+    }));
 }
 
 static void free_sslproxy_ssl_bump(acl_access **ssl_bump)
@@ -4782,21 +4779,16 @@
     if (ftpEpsvIsDeprecatedRule) {
         // overwrite previous ftp_epsv lines
         delete *ftp_epsv;
+        *ftp_epsv = nullptr;
+
         if (ftpEpsvDeprecatedAction == allow_t(ACCESS_DENIED)) {
-            Acl::AndNode *ftpEpsvRule = new Acl::AndNode;
-            ftpEpsvRule->context("(ftp_epsv rule)", config_input_line);
-            ACL *a = ACL::FindByName("all");
-            if (!a) {
-                delete ftpEpsvRule;
+            if (ACL *a = ACL::FindByName("all"))
+                ParseAclWithAction(ftp_epsv, ftpEpsvDeprecatedAction, "ftp_epsv", a);
+            else {
                 self_destruct();
                 return;
             }
-            ftpEpsvRule->add(a);
-            *ftp_epsv = new Acl::Tree;
-            (*ftp_epsv)->context("(ftp_epsv rules)", config_input_line);
-            (*ftp_epsv)->add(ftpEpsvRule, ftpEpsvDeprecatedAction);
-        } else
-            *ftp_epsv = NULL;
+        }
         FtpEspvDeprecated = true;
     } else {
         aclParseAccessLine(cfg_directive, LegacyParser, ftp_epsv);
@@ -4806,7 +4798,7 @@
 static void dump_ftp_epsv(StoreEntry *entry, const char *name, acl_access *ftp_epsv)
 {
     if (ftp_epsv)
-        dump_SBufList(entry, ftp_epsv->treeDump(name, NULL));
+        dump_SBufList(entry, ftp_epsv->treeDump(name, Acl::AllowOrDeny));
 }
 
 static void free_ftp_epsv(acl_access **ftp_epsv)
@@ -4931,31 +4923,22 @@
         return;
     }
 
-    Acl::AndNode *rule = new Acl::AndNode;
-    rule->context("(on_unsupported_protocol rule)", config_input_line);
-    rule->lineParse();
     // empty rule OK
-
-    assert(access);
-    if (!*access) {
-        *access = new Acl::Tree;
-        (*access)->context("(on_unsupported_protocol rules)", config_input_line);
-    }
-
-    (*access)->add(rule, action);
+    ParseAclWithAction(access, action, "on_unsupported_protocol");
 }
 
 static void
 dump_on_unsupported_protocol(StoreEntry *entry, const char *name, acl_access *access)
 {
-    const char *on_error_tunnel_mode_str[] = {
+    static const std::vector<const char *> onErrorTunnelMode = {
         "none",
         "tunnel",
-        "respond",
-        NULL
+        "respond"
     };
     if (access) {
-        SBufList lines = access->treeDump(name, on_error_tunnel_mode_str);
+        SBufList lines = access->treeDump(name, [](const allow_t &action) {
+            return onErrorTunnelMode.at(action.kind);
+        });
         dump_SBufList(entry, lines);
     }
 }
diff -u -r -N squid-4.0.16/src/cache_manager.cc squid-4.0.17/src/cache_manager.cc
--- squid-4.0.16/src/cache_manager.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/cache_manager.cc	2016-12-16 23:06:20.000000000 +1300
@@ -445,14 +445,13 @@
 char *
 CacheManager::PasswdGet(Mgr::ActionPasswordList * a, const char *action)
 {
-    wordlist *w;
-
-    while (a != NULL) {
-        for (w = a->actions; w != NULL; w = w->next) {
-            if (0 == strcmp(w->key, action))
+    while (a) {
+        for (auto &w : a->actions) {
+            if (w.cmp(action) == 0)
                 return a->passwd;
 
-            if (0 == strcmp(w->key, "all"))
+            static const SBuf allAction("all");
+            if (w == allAction)
                 return a->passwd;
         }
 
diff -u -r -N squid-4.0.16/src/cf.data.pre squid-4.0.17/src/cf.data.pre
--- squid-4.0.16/src/cf.data.pre	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/cf.data.pre	2016-12-16 23:06:20.000000000 +1300
@@ -3402,7 +3402,7 @@
 			certificate. If not specified the peer hostname will be
 			used.
 	
-	front-end-https
+	front-end-https[=off|on|auto]
 			Enable the "Front-End-Https: On" header needed when
 			using Squid as a SSL frontend in front of Microsoft OWA.
 			See MS KB document Q307347 for details on this header.
diff -u -r -N squid-4.0.16/src/clients/FtpClient.cc squid-4.0.17/src/clients/FtpClient.cc
--- squid-4.0.16/src/clients/FtpClient.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/clients/FtpClient.cc	2016-12-16 23:06:20.000000000 +1300
@@ -442,6 +442,11 @@
     char *buf;
     debugs(9, 3, status());
 
+    if (!Comm::IsConnOpen(ctrl.conn)) {
+        debugs(9, 5, "The control connection to the remote end is closed");
+        return false;
+    }
+
     if (code != 227) {
         debugs(9, 2, "PASV not supported by remote end");
         return false;
@@ -473,6 +478,11 @@
     char *buf;
     debugs(9, 3, status());
 
+    if (!Comm::IsConnOpen(ctrl.conn)) {
+        debugs(9, 5, "The control connection to the remote end is closed");
+        return false;
+    }
+
     if (code != 229 && code != 522) {
         if (code == 200) {
             /* handle broken servers (RFC 2428 says OK code for EPSV MUST be 229 not 200) */
@@ -735,6 +745,11 @@
 void
 Ftp::Client::connectDataChannel()
 {
+    if (!Comm::IsConnOpen(ctrl.conn)) {
+        debugs(9, 5, "The control connection to the remote end is closed");
+        return;
+    }
+
     safe_free(ctrl.last_command);
 
     safe_free(ctrl.last_reply);
diff -u -r -N squid-4.0.16/src/clients/FtpGateway.cc squid-4.0.17/src/clients/FtpGateway.cc
--- squid-4.0.16/src/clients/FtpGateway.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/clients/FtpGateway.cc	2016-12-16 23:06:20.000000000 +1300
@@ -209,7 +209,9 @@
 static FTPSM ftpReadMdtm;
 static FTPSM ftpSendSize;
 static FTPSM ftpReadSize;
+#if 0
 static FTPSM ftpSendEPRT;
+#endif
 static FTPSM ftpReadEPRT;
 static FTPSM ftpSendPORT;
 static FTPSM ftpReadPORT;
@@ -443,6 +445,11 @@
 void
 Ftp::Gateway::listenForDataChannel(const Comm::ConnectionPointer &conn)
 {
+    if (!Comm::IsConnOpen(ctrl.conn)) {
+        debugs(9, 5, "The control connection to the remote end is closed");
+        return;
+    }
+
     assert(!Comm::IsConnOpen(data.conn));
 
     typedef CommCbMemFunT<Gateway, CommAcceptCbParams> AcceptDialer;
@@ -1164,7 +1171,7 @@
 
     checkUrlpath();
     buildTitleUrl();
-    debugs(9, 5, "FD " << ctrl.conn->fd << " : host=" << request->url.host() <<
+    debugs(9, 5, "FD " << (ctrl.conn ? ctrl.conn->fd : -1) << " : host=" << request->url.host() <<
            ", path=" << request->url.path() << ", user=" << user << ", passwd=" << password);
     state = BEGIN;
     Ftp::Client::start();
@@ -1719,7 +1726,9 @@
     if (ftpState->handlePasvReply(srvAddr))
         ftpState->connectDataChannel();
     else {
-        ftpSendEPRT(ftpState);
+        ftpFail(ftpState);
+        // Currently disabled, does not work correctly:
+        // ftpSendEPRT(ftpState);
         return;
     }
 }
@@ -1759,6 +1768,11 @@
     }
     safe_free(ftpState->data.host);
 
+    if (!Comm::IsConnOpen(ftpState->ctrl.conn)) {
+        debugs(9, 5, "The control connection to the remote end is closed");
+        return;
+    }
+
     /*
      * Set up a listen socket on the same local address as the
      * control connection.
@@ -1850,9 +1864,14 @@
     ftpRestOrList(ftpState);
 }
 
+#if 0
 static void
 ftpSendEPRT(Ftp::Gateway * ftpState)
 {
+    /* check the server control channel is still available */
+    if (!ftpState || !ftpState->haveControlChannel("ftpSendEPRT"))
+        return;
+
     if (Config.Ftp.epsv_all && ftpState->flags.epsv_all_sent) {
         debugs(9, DBG_IMPORTANT, "FTP does not allow EPRT method after 'EPSV ALL' has been sent.");
         return;
@@ -1888,6 +1907,7 @@
     ftpState->writeCommand(cbuf);
     ftpState->state = Ftp::Client::SENT_EPRT;
 }
+#endif
 
 static void
 ftpReadEPRT(Ftp::Gateway * ftpState)
@@ -1914,10 +1934,8 @@
 {
     debugs(9, 3, HERE);
 
-    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
-        abortAll("entry aborted when accepting data conn");
-        data.listenConn->close();
-        data.listenConn = NULL;
+    if (!Comm::IsConnOpen(ctrl.conn)) { /*Close handlers will cleanup*/
+        debugs(9, 5, "The control connection to the remote end is closed");
         return;
     }
 
@@ -1930,6 +1948,14 @@
         return;
     }
 
+    if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
+        abortAll("entry aborted when accepting data conn");
+        data.listenConn->close();
+        data.listenConn = NULL;
+        io.conn->close();
+        return;
+    }
+
     /* data listening conn is no longer even open. abort. */
     if (!Comm::IsConnOpen(data.listenConn)) {
         data.listenConn = NULL; // ensure that it's cleared and not just closed.
@@ -2682,8 +2708,8 @@
 Ftp::Gateway::completeForwarding()
 {
     if (fwd == NULL || flags.completed_forwarding) {
-        debugs(9, 3, HERE << "completeForwarding avoids " <<
-               "double-complete on FD " << ctrl.conn->fd << ", Data FD " << data.conn->fd <<
+        debugs(9, 3, "avoid double-complete on FD " <<
+               (ctrl.conn ? ctrl.conn->fd : -1) << ", Data FD " << data.conn->fd <<
                ", this " << this << ", fwd " << fwd);
         return;
     }
diff -u -r -N squid-4.0.16/src/client_side.cc squid-4.0.17/src/client_side.cc
--- squid-4.0.16/src/client_side.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/client_side.cc	2016-12-16 23:06:20.000000000 +1300
@@ -229,6 +229,7 @@
         statCounter.client_http.nearHitSvcTime.count(svc_time);
         break;
 
+    case LOG_TCP_INM_HIT:
     case LOG_TCP_IMS_HIT:
         statCounter.client_http.nearMissSvcTime.count(svc_time);
         break;
@@ -814,6 +815,8 @@
     // TODO: enforces HTTP/1 MUST on pipeline order, but is irrelevant to HTTP/2
     if (context != http->getConn()->pipeline.front())
         context->deferRecipientForLater(node, rep, receivedData);
+    else if (http->getConn()->cbControlMsgSent) // 1xx to the user is pending
+        context->deferRecipientForLater(node, rep, receivedData);
     else
         http->getConn()->handleReply(rep, receivedData);
 
@@ -1298,8 +1301,6 @@
     {
         const bool parsedOk = hp->parse(csd->inBuf);
 
-        if (csd->port->flags.isIntercepted() && Config.accessList.on_unsupported_protocol)
-            csd->preservedClientData = csd->inBuf;
         // sync the buffers after parsing.
         csd->inBuf = hp->remaining();
 
@@ -1308,6 +1309,11 @@
             return NULL;
         }
 
+        if (csd->mayTunnelUnsupportedProto()) {
+            csd->preservedClientData = hp->parsed();
+            csd->preservedClientData.append(csd->inBuf);
+        }
+
         if (!parsedOk) {
             const bool tooBig =
                 hp->parseStatusCode == Http::scRequestHeaderFieldsTooLarge ||
@@ -1564,11 +1570,10 @@
  * or false otherwise
  */
 bool
-clientTunnelOnError(ConnStateData *conn, Http::Stream *context, HttpRequest *request, const HttpRequestMethod& method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
+clientTunnelOnError(ConnStateData *conn, Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod& method, err_type requestError)
 {
-    if (conn->port->flags.isIntercepted() &&
-            Config.accessList.on_unsupported_protocol && conn->pipeline.nrequests <= 1) {
-        ACLFilledChecklist checklist(Config.accessList.on_unsupported_protocol, request, NULL);
+    if (conn->mayTunnelUnsupportedProto()) {
+        ACLFilledChecklist checklist(Config.accessList.on_unsupported_protocol, request.getRaw(), nullptr);
         checklist.requestErrorType = requestError;
         checklist.src_addr = conn->clientConnection->remote;
         checklist.my_addr = conn->clientConnection->local;
@@ -1577,30 +1582,16 @@
         if (answer == ACCESS_ALLOWED && answer.kind == 1) {
             debugs(33, 3, "Request will be tunneled to server");
             if (context) {
-                // XXX: Either the context is finished() or it should stay queued.
-                // The below may leak client streams BodyPipe objects. BUT, we need
-                // to check if client-streams detatch is safe to do here (finished() will detatch).
                 assert(conn->pipeline.front() == context); // XXX: still assumes HTTP/1 semantics
-                conn->pipeline.popMe(Http::StreamPointer(context));
+                context->finished(); // Will remove from conn->pipeline queue
             }
             Comm::SetSelect(conn->clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
-            return conn->fakeAConnectRequest("unknown-protocol", conn->preservedClientData);
+            return conn->initiateTunneledRequest(request, Http::METHOD_NONE, "unknown-protocol", conn->preservedClientData);
         } else {
             debugs(33, 3, "Continue with returning the error: " << requestError);
         }
     }
 
-    if (context) {
-        conn->quitAfterError(request);
-        clientStreamNode *node = context->getClientReplyContext();
-        clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
-        assert (repContext);
-
-        repContext->setReplyToError(requestError, errStatusCode, method, context->http->uri, conn->clientConnection->remote, NULL, requestErrorBytes, NULL);
-
-        assert(context->http->out.offset == 0);
-        context->pullData();
-    } // else Probably an ERR_REQUEST_START_TIMEOUT error so just return.
     return false;
 }
 
@@ -2155,7 +2146,7 @@
         if (needProxyProtocolHeader_ && !parseProxyProtocolHeader())
             break;
 
-        if (Http::Stream *context = parseOneRequest()) {
+        if (Http::StreamPointer context = parseOneRequest()) {
             debugs(33, 5, clientConnection << ": done parsing a request");
 
             AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
@@ -2375,23 +2366,12 @@
     if (!Comm::IsConnOpen(io.conn))
         return;
 
-    if (Config.accessList.on_unsupported_protocol && !receivedFirstByte_) {
-#if USE_OPENSSL
-        if (serverBump() && (serverBump()->act.step1 == Ssl::bumpPeek || serverBump()->act.step1 == Ssl::bumpStare)) {
-            if (spliceOnError(ERR_REQUEST_START_TIMEOUT)) {
-                receivedFirstByte();
-                return;
-            }
-        } else if (!fd_table[io.conn->fd].ssl)
-#endif
-        {
-            const HttpRequestMethod method;
-            if (clientTunnelOnError(this, NULL, NULL, method, ERR_REQUEST_START_TIMEOUT, Http::scNone, NULL)) {
-                // Tunnel established. Set receivedFirstByte to avoid loop.
-                receivedFirstByte();
-                return;
-            }
-        }
+    if (mayTunnelUnsupportedProto() && !receivedFirstByte_) {
+        Http::StreamPointer context = pipeline.front();
+        Must(context && context->http);
+        HttpRequest::Pointer request = context->http->request;
+        if (clientTunnelOnError(this, context, request, HttpRequestMethod(), ERR_REQUEST_START_TIMEOUT))
+            return;
     }
     /*
     * Just close the connection to not confuse browsers
@@ -2624,7 +2604,7 @@
                 debugs(83, 2, "Error negotiating SSL connection on FD " << fd << ": Aborted by client: " << ssl_error);
             } else {
                 debugs(83, (xerrno == ECONNRESET) ? 1 : 2, "Error negotiating SSL connection on FD " << fd << ": " <<
-                       (xerrno == 0 ? ERR_error_string(ssl_error, NULL) : xstrerr(xerrno)));
+                       (xerrno == 0 ? Security::ErrorString(ssl_error) : xstrerr(xerrno)));
             }
             return -1;
 
@@ -2634,7 +2614,7 @@
 
         default:
             debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " <<
-                   fd << ": " << ERR_error_string(ERR_get_error(), NULL) <<
+                   fd << ": " << Security::ErrorString(ERR_get_error()) <<
                    " (" << ssl_error << "/" << ret << ")");
             return -1;
         }
@@ -2759,7 +2739,7 @@
 
     // Require both a match and a positive bump mode to work around exceptional
     // cases where ACL code may return ACCESS_ALLOWED with zero answer.kind.
-    if (answer == ACCESS_ALLOWED && (answer.kind != Ssl::bumpNone && answer.kind != Ssl::bumpSplice)) {
+    if (answer == ACCESS_ALLOWED && answer.kind != Ssl::bumpNone) {
         debugs(33, 2, "sslBump needed for " << connState->clientConnection << " method " << answer.kind);
         connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
     } else {
@@ -2891,22 +2871,9 @@
 {
     certProperties.commonName =  sslCommonName_.isEmpty() ? sslConnectHostOrIp.termedBuf() : sslCommonName_.c_str();
 
-    // fake certificate adaptation requires bump-server-first mode
-    if (!sslServerBump) {
-        assert(port->signingCert.get());
-        certProperties.signWithX509.resetAndLock(port->signingCert.get());
-        if (port->signPkey.get())
-            certProperties.signWithPkey.resetAndLock(port->signPkey.get());
-        certProperties.signAlgorithm = Ssl::algSignTrusted;
-        return;
-    }
-
-    // In case of an error while connecting to the secure server, use a fake
-    // trusted certificate, with no mimicked fields and no adaptation
-    // algorithms. There is nothing we can mimic so we want to minimize the
-    // number of warnings the user will have to see to get to the error page.
-    assert(sslServerBump->entry);
-    if (sslServerBump->entry->isEmpty()) {
+    const bool triedToConnect = sslServerBump && sslServerBump->entry;
+    const bool connectedOK = triedToConnect && sslServerBump->entry->isEmpty();
+    if (connectedOK) {
         if (X509 *mimicCert = sslServerBump->serverCert.get())
             certProperties.mimicCert.resetAndLock(mimicCert);
 
@@ -2949,11 +2916,13 @@
                 break;
             }
         }
-    } else {// if (!sslServerBump->entry->isEmpty())
-        // Use trusted certificate for a Squid-generated error
-        // or the user would have to add a security exception
-        // just to see the error page. We will close the connection
-        // so that the trust is not extended to non-Squid content.
+    } else {// did not try to connect (e.g. client-first) or failed to connect
+        // In case of an error while connecting to the secure server, use a
+        // trusted certificate, with no mimicked fields and no adaptation
+        // algorithms. There is nothing we can mimic, so we want to minimize the
+        // number of warnings the user will have to see to get to the error page.
+        // We will close the connection, so that the trust is not extended to
+        // non-Squid content.
         certProperties.signAlgorithm = Ssl::algSignTrusted;
     }
 
@@ -3176,6 +3145,9 @@
 
     parsingTlsHandshake = false;
 
+    if (mayTunnelUnsupportedProto())
+        preservedClientData = inBuf;
+
     // Even if the parser failed, each TLS detail should either be set
     // correctly or still be "unknown"; copying unknown detail is a no-op.
     Security::TlsDetails::Pointer const &details = tlsParser.details;
@@ -3190,7 +3162,18 @@
     Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
     Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, NULL, NULL, 0);
 
-    if (!sslServerBump) { // BumpClientFirst mode does not use this member
+    if (unsupportedProtocol) {
+        Http::StreamPointer context = pipeline.front();
+        Must(context && context->http);
+        HttpRequest::Pointer request = context->http->request;
+        debugs(83, 5, "Got something other than TLS Client Hello. Cannot SslBump.");
+        sslBumpMode = Ssl::bumpNone;
+        if (!clientTunnelOnError(this, context, request, HttpRequestMethod(), ERR_PROTOCOL_UNKNOWN))
+            clientConnection->close();
+        return;
+    }
+
+    if (!sslServerBump || sslServerBump->act.step1 == Ssl::bumpClientFirst) { // Either means client-first.
         getSslContextStart();
         return;
     } else if (sslServerBump->act.step1 == Ssl::bumpServerFirst) {
@@ -3198,36 +3181,8 @@
         FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
     } else {
         Must(sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare);
-        startPeekAndSplice(unsupportedProtocol);
-    }
-}
-
-bool
-ConnStateData::spliceOnError(const err_type err)
-{
-    if (Config.accessList.on_unsupported_protocol) {
-        assert(serverBump());
-        ACLFilledChecklist checklist(Config.accessList.on_unsupported_protocol, serverBump()->request.getRaw(), NULL);
-        checklist.requestErrorType = err;
-        checklist.conn(this);
-        allow_t answer = checklist.fastCheck();
-        if (answer == ACCESS_ALLOWED && answer.kind == 1) {
-            return splice();
-        }
-    }
-    return false;
-}
-
-void
-ConnStateData::startPeekAndSplice(const bool unsupportedProtocol)
-{
-    if (unsupportedProtocol) {
-        if (!spliceOnError(ERR_PROTOCOL_UNKNOWN))
-            clientConnection->close();
-        return;
+        startPeekAndSplice();
     }
-
-    startPeekAndSpliceDone();
 }
 
 void httpsSslBumpStep2AccessCheckDone(allow_t answer, void *data)
@@ -3252,7 +3207,7 @@
     if (bumpAction == Ssl::bumpTerminate) {
         connState->clientConnection->close();
     } else if (bumpAction != Ssl::bumpSplice) {
-        connState->startPeekAndSpliceDone();
+        connState->startPeekAndSplice();
     } else if (!connState->splice())
         connState->clientConnection->close();
 }
@@ -3268,37 +3223,30 @@
         fd_table[clientConnection->fd].write_method = &default_write_method;
     }
 
-    if (transparent()) {
-        // set the current protocol to something sensible (was "HTTPS" for the bumping process)
-        // we are sending a faked-up HTTP/1.1 message wrapper, so go with that.
-        transferProtocol = Http::ProtocolVersion();
-        return fakeAConnectRequest("intercepted TLS spliced", inBuf);
-    } else {
-        // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
-
-        // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
-        transferProtocol = Http::ProtocolVersion();
-        Http::StreamPointer context = pipeline.front();
-        ClientHttpRequest *http = context->http;
-        tunnelStart(http);
-        return true;
-    }
+    // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
+    // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
+    transferProtocol = Http::ProtocolVersion();
+    assert(!pipeline.empty());
+    Http::StreamPointer context = pipeline.front();
+    ClientHttpRequest *http = context->http;
+    tunnelStart(http);
+    return true;
 }
 
 void
-ConnStateData::startPeekAndSpliceDone()
+ConnStateData::startPeekAndSplice()
 {
     // This is the Step2 of the SSL bumping
     assert(sslServerBump);
     Http::StreamPointer context = pipeline.front();
-    ClientHttpRequest *http = context ? context->http : NULL;
+    ClientHttpRequest *http = context ? context->http : nullptr;
 
     if (sslServerBump->step == Ssl::bumpStep1) {
         sslServerBump->step = Ssl::bumpStep2;
         // Run a accessList check to check if want to splice or continue bumping
 
-        ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, sslServerBump->request.getRaw(), NULL);
-        acl_checklist->al = http ? http->al : NULL;
+        ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, sslServerBump->request.getRaw(), nullptr);
+        acl_checklist->al = http ? http->al : nullptr;
         //acl_checklist->src_addr = params.conn->remote;
         //acl_checklist->my_addr = s->s;
         acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpNone));
@@ -3330,8 +3278,8 @@
     int ret = 0;
     if ((ret = Squid_SSL_accept(this, NULL)) < 0) {
         debugs(83, 2, "SSL_accept failed.");
-        const err_type err = ERR_SECURE_ACCEPT_FAIL;
-        if (!spliceOnError(err))
+        HttpRequest::Pointer request(http ? http->request : nullptr);
+        if (!clientTunnelOnError(this, context, request, HttpRequestMethod(), ERR_SECURE_ACCEPT_FAIL))
             clientConnection->close();
         return;
     }
@@ -3382,41 +3330,125 @@
 #endif /* USE_OPENSSL */
 
 bool
-ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
+ConnStateData::initiateTunneledRequest(HttpRequest::Pointer const &cause, Http::MethodType const method, const char *reason, const SBuf &payload)
 {
     // fake a CONNECT request to force connState to tunnel
     SBuf connectHost;
+    unsigned short connectPort = 0;
+
+    if (pinning.serverConnection != nullptr) {
+        static char ip[MAX_IPSTRLEN];
+        connectHost.assign(pinning.serverConnection->remote.toStr(ip, sizeof(ip)));
+        connectPort = pinning.serverConnection->remote.port();
+    } else if (cause && cause->method == Http::METHOD_CONNECT) {
+        // We are inside a (not fully established) CONNECT request
+        connectHost = cause->url.host();
+        connectPort = cause->url.port();
+    } else {
+        debugs(33, 2, "Not able to compute URL, abort request tunneling for " << reason);
+        return false;
+    }
+
+    debugs(33, 2, "Request tunneling for " << reason);
+    ClientHttpRequest *http = buildFakeRequest(method, connectHost, connectPort, payload);
+    HttpRequest::Pointer request = http->request;
+    request->flags.forceTunnel = true;
+    http->calloutContext = new ClientRequestContext(http);
+    http->doCallouts();
+    clientProcessRequestFinished(this, request);
+    return true;
+}
+
+bool
+ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
+{
+    debugs(33, 2, "fake a CONNECT request to force connState to tunnel for " << reason);
+
+    SBuf connectHost;
+    assert(transparent());
+    const unsigned short connectPort = clientConnection->local.port();
+
 #if USE_OPENSSL
-    if (serverBump() && !serverBump()->clientSni.isEmpty()) {
+    if (serverBump() && !serverBump()->clientSni.isEmpty())
         connectHost.assign(serverBump()->clientSni);
-        if (clientConnection->local.port() > 0)
-            connectHost.appendf(":%d",clientConnection->local.port());
-    } else
+    else
 #endif
     {
         static char ip[MAX_IPSTRLEN];
-        connectHost.assign(clientConnection->local.toUrl(ip, sizeof(ip)));
+        connectHost.assign(clientConnection->local.toStr(ip, sizeof(ip)));
     }
-    // Pre-pend this fake request to the TLS bits already in the buffer
-    SBuf retStr;
-    retStr.append("CONNECT ");
-    retStr.append(connectHost);
-    retStr.append(" HTTP/1.1\r\nHost: ");
-    retStr.append(connectHost);
-    retStr.append("\r\n\r\n");
-    retStr.append(payload);
-    inBuf = retStr;
-    bool ret = handleReadData();
-    if (ret)
-        ret = clientParseRequests();
 
-    if (!ret) {
-        debugs(33, 2, "Failed to start fake CONNECT request for " << reason << " connection: " << clientConnection);
-        return false;
-    }
+    ClientHttpRequest *http = buildFakeRequest(Http::METHOD_CONNECT, connectHost, connectPort, payload);
+
+    http->calloutContext = new ClientRequestContext(http);
+    HttpRequest::Pointer request = http->request;
+    http->doCallouts();
+    clientProcessRequestFinished(this, request);
     return true;
 }
 
+ClientHttpRequest *
+ConnStateData::buildFakeRequest(Http::MethodType const method, SBuf &useHost, unsigned short usePort, const SBuf &payload)
+{
+    ClientHttpRequest *http = new ClientHttpRequest(this);
+    Http::Stream *stream = new Http::Stream(clientConnection, http);
+
+    StoreIOBuffer tempBuffer;
+    tempBuffer.data = stream->reqbuf;
+    tempBuffer.length = HTTP_REQBUF_SZ;
+
+    ClientStreamData newServer = new clientReplyContext(http);
+    ClientStreamData newClient = stream;
+    clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
+                     clientReplyStatus, newServer, clientSocketRecipient,
+                     clientSocketDetach, newClient, tempBuffer);
+
+    http->uri = SBufToCstring(useHost);
+    stream->flags.parsed_ok = 1; // Do we need it?
+    stream->mayUseConnection(true);
+
+    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
+                                     CommTimeoutCbPtrFun(clientLifetimeTimeout, stream->http));
+    commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
+
+    stream->registerWithConn();
+
+    // Setup Http::Request object. Maybe should be replaced by a call to (modified)
+    // clientProcessRequest
+    HttpRequest::Pointer request = new HttpRequest();
+    AnyP::ProtocolType proto = (method == Http::METHOD_NONE) ? AnyP::PROTO_AUTHORITY_FORM : AnyP::PROTO_HTTP;
+    request->url.setScheme(proto, nullptr);
+    request->method = method;
+    request->url.host(useHost.c_str());
+    request->url.port(usePort);
+    http->request = request.getRaw();
+    HTTPMSGLOCK(http->request);
+
+    request->clientConnectionManager = this;
+
+    if (proto == AnyP::PROTO_HTTP)
+        request->header.putStr(Http::HOST, useHost.c_str());
+    request->flags.intercepted = ((clientConnection->flags & COMM_INTERCEPTION) != 0);
+    request->flags.interceptTproxy = ((clientConnection->flags & COMM_TRANSPARENT) != 0 );
+    request->sources |= ((switchedToHttps() || port->transport.protocol == AnyP::PROTO_HTTPS) ? HttpMsg::srcHttps : HttpMsg::srcHttp);
+#if USE_AUTH
+    if (getAuth())
+        request->auth_user_request = getAuth();
+#endif
+    request->client_addr = clientConnection->remote;
+#if FOLLOW_X_FORWARDED_FOR
+    request->indirect_client_addr = clientConnection->remote;
+#endif /* FOLLOW_X_FORWARDED_FOR */
+    request->my_addr = clientConnection->local;
+    request->myportname = port->name;
+
+    inBuf = payload;
+    flags.readMore = false;
+
+    setLogUri(http, urlCanonicalClean(request.getRaw()));
+    return http;
+}
+
 /// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
 static bool
 OpenedHttpSocket(const Comm::ConnectionPointer &c, const Ipc::FdNoteId portType)
@@ -3794,7 +3826,10 @@
         typedef CommCbMemFunT<HttpControlMsgSink, CommIoCbParams> Dialer;
         AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, HttpControlMsgSink::wroteControlMsg);
 
-        writeControlMsgAndCall(rep.getRaw(), call);
+        if (!writeControlMsgAndCall(rep.getRaw(), call)) {
+            // but still inform the caller (so it may resume its operation)
+            doneWithControlMsg();
+        }
         return;
     }
 
@@ -3802,6 +3837,17 @@
     clientConnection->close();
 }
 
+void
+ConnStateData::doneWithControlMsg()
+{
+    HttpControlMsgSink::doneWithControlMsg();
+
+    if (Http::StreamPointer deferredRequest = pipeline.front()) {
+        debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded after control msg wrote");
+        ClientSocketContextPushDeferredIfNeeded(deferredRequest, this);
+    }
+}
+
 /// Our close handler called by Comm when the pinned connection is closed
 void
 ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
@@ -4052,10 +4098,7 @@
 
     // do not log connections that closed after a transaction (it is normal)
     // TODO: access_log needs ACLs to match received-no-bytes connections
-    // XXX: TLS may return here even though we got no transactions yet
-    // XXX: PROXY protocol may return here even though we got no
-    // transactions yet
-    if (receivedFirstByte_ && inBuf.isEmpty())
+    if (pipeline.nrequests && inBuf.isEmpty())
         return;
 
     /* Create a temporary ClientHttpRequest object. Its destructor will log. */
@@ -4066,3 +4109,15 @@
     setLogUri(&http, uri);
 }
 
+bool
+ConnStateData::mayTunnelUnsupportedProto()
+{
+    return Config.accessList.on_unsupported_protocol
+#if USE_OPENSSL
+           &&
+           ((port->flags.isIntercepted() && port->flags.tunnelSslBumping)
+            || (serverBump() && pinning.serverConnection))
+#endif
+           ;
+}
+
diff -u -r -N squid-4.0.16/src/client_side.h squid-4.0.17/src/client_side.h
--- squid-4.0.16/src/client_side.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/client_side.h	2016-12-16 23:06:20.000000000 +1300
@@ -77,6 +77,7 @@
 
     /* HttpControlMsgSink API */
     virtual void sendControlMsg(HttpControlMsg);
+    virtual void doneWithControlMsg();
 
     /// Traffic parsing
     bool clientParseRequests();
@@ -203,9 +204,8 @@
     void postHttpsAccept();
 
     /// Initializes and starts a peek-and-splice negotiation with the SSL client
-    void startPeekAndSplice(const bool unknownProtocol);
-    /// Called when the initialization of peek-and-splice negotiation finidhed
-    void startPeekAndSpliceDone();
+    void startPeekAndSplice();
+
     /// Called when a peek-and-splice step finished. For example after
     /// server SSL certificates received and fake server SSL certificates
     /// generated
@@ -216,11 +216,6 @@
     /// Splice a bumped client connection on peek-and-splice mode
     bool splice();
 
-    /// Check on_unsupported_protocol access list and splice if required
-    /// \retval true on splice
-    /// \retval false otherwise
-    bool spliceOnError(const err_type err);
-
     /// Start to create dynamic Security::ContextPointer for host or uses static port SSL context.
     void getSslContextStart();
     /**
@@ -269,7 +264,7 @@
     void connectionTag(const char *aTag) { connectionTag_ = aTag; }
 
     /// handle a control message received by context from a peer and call back
-    virtual void writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) = 0;
+    virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) = 0;
 
     /// ClientStream calls this to supply response header (once) and data
     /// for the current Http::Stream.
@@ -287,6 +282,15 @@
     /// at the beginning of the client I/O buffer
     bool fakeAConnectRequest(const char *reason, const SBuf &payload);
 
+    /// generates and sends to tunnel.cc a fake request with a given payload
+    bool initiateTunneledRequest(HttpRequest::Pointer const &cause, Http::MethodType const method, const char *reason, const SBuf &payload);
+
+    /// whether tunneling of unsupported protocol is allowed for this connection
+    bool mayTunnelUnsupportedProto();
+
+    /// build a fake http request
+    ClientHttpRequest *buildFakeRequest(Http::MethodType const method, SBuf &useHost, unsigned short usePort, const SBuf &payload);
+
     /// client data which may need to forward as-is to server after an
     /// on_unsupported_protocol tunnel decision.
     SBuf preservedClientData;
@@ -316,7 +320,7 @@
     virtual Http::Stream *parseOneRequest() = 0;
 
     /// start processing a freshly parsed request
-    virtual void processParsedRequest(Http::Stream *) = 0;
+    virtual void processParsedRequest(Http::StreamPointer &) = 0;
 
     /// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
     virtual int pipelinePrefetchMax() const;
diff -u -r -N squid-4.0.16/src/client_side_reply.cc squid-4.0.17/src/client_side_reply.cc
--- squid-4.0.16/src/client_side_reply.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/client_side_reply.cc	2016-12-16 23:06:20.000000000 +1300
@@ -415,6 +415,15 @@
     if (result.flags.error && !EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED))
         return;
 
+    if (collapsedRevalidation == crSlave && EBIT_TEST(http->storeEntry()->flags, KEY_PRIVATE)) {
+        debugs(88, 3, "CF slave hit private " << *http->storeEntry() << ". MISS");
+        // restore context to meet processMiss() expectations
+        restoreState();
+        http->logType = LOG_TCP_MISS;
+        processMiss();
+        return;
+    }
+
     /* update size of the request */
     reqsize = result.length + reqofs;
 
@@ -536,6 +545,16 @@
         return;
     }
 
+    // The previously identified hit suddenly became unsharable!
+    // This is common for collapsed forwarding slaves but might also
+    // happen to regular hits because we are called asynchronously.
+    if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
+        debugs(88, 3, "unsharable " << *e << ". MISS");
+        http->logType = LOG_TCP_MISS;
+        processMiss();
+        return;
+    }
+
     if (result.length == 0) {
         debugs(88, 5, "store IO buffer has no content. MISS");
         /* the store couldn't get enough data from the file for us to id the
@@ -607,6 +626,7 @@
         debugs(88, 5, "negative-HIT");
         http->logType = LOG_TCP_NEGATIVE_HIT;
         sendMoreData(result);
+        return;
     } else if (blockedHit()) {
         debugs(88, 5, "send_hit forces a MISS");
         http->logType = LOG_TCP_MISS;
@@ -659,27 +679,29 @@
             http->logType = LOG_TCP_MISS;
             processMiss();
         }
+        return;
     } else if (r->conditional()) {
         debugs(88, 5, "conditional HIT");
-        processConditional(result);
-    } else {
-        /*
-         * plain ol' cache hit
-         */
-        debugs(88, 5, "plain old HIT");
+        if (processConditional(result))
+            return;
+    }
+
+    /*
+     * plain ol' cache hit
+     */
+    debugs(88, 5, "plain old HIT");
 
 #if USE_DELAY_POOLS
-        if (e->store_status != STORE_OK)
-            http->logType = LOG_TCP_MISS;
-        else
+    if (e->store_status != STORE_OK)
+        http->logType = LOG_TCP_MISS;
+    else
 #endif
-            if (e->mem_status == IN_MEMORY)
-                http->logType = LOG_TCP_MEM_HIT;
-            else if (Config.onoff.offline)
-                http->logType = LOG_TCP_OFFLINE_HIT;
+        if (e->mem_status == IN_MEMORY)
+            http->logType = LOG_TCP_MEM_HIT;
+        else if (Config.onoff.offline)
+            http->logType = LOG_TCP_OFFLINE_HIT;
 
-        sendMoreData(result);
-    }
+    sendMoreData(result);
 }
 
 /**
@@ -773,17 +795,16 @@
 }
 
 /// process conditional request from client
-void
+bool
 clientReplyContext::processConditional(StoreIOBuffer &result)
 {
     StoreEntry *const e = http->storeEntry();
 
     if (e->getReply()->sline.status() != Http::scOkay) {
-        debugs(88, 4, "clientReplyContext::processConditional: Reply code " <<
-               e->getReply()->sline.status() << " != 200");
+        debugs(88, 4, "Reply code " << e->getReply()->sline.status() << " != 200");
         http->logType = LOG_TCP_MISS;
         processMiss();
-        return;
+        return true;
     }
 
     HttpRequest &r = *http->request;
@@ -791,51 +812,39 @@
     if (r.header.has(Http::HdrType::IF_MATCH) && !e->hasIfMatchEtag(r)) {
         // RFC 2616: reply with 412 Precondition Failed if If-Match did not match
         sendPreconditionFailedError();
-        return;
+        return true;
     }
 
-    bool matchedIfNoneMatch = false;
     if (r.header.has(Http::HdrType::IF_NONE_MATCH)) {
-        if (!e->hasIfNoneMatchEtag(r)) {
-            // RFC 2616: ignore IMS if If-None-Match did not match
-            r.flags.ims = false;
-            r.ims = -1;
-            r.imslen = 0;
-            r.header.delById(Http::HdrType::IF_MODIFIED_SINCE);
-            http->logType = LOG_TCP_MISS;
-            sendMoreData(result);
-            return;
-        }
+        // RFC 7232: If-None-Match recipient MUST ignore IMS
+        r.flags.ims = false;
+        r.ims = -1;
+        r.imslen = 0;
+        r.header.delById(Http::HdrType::IF_MODIFIED_SINCE);
 
-        if (!r.flags.ims) {
-            // RFC 2616: if If-None-Match matched and there is no IMS,
-            // reply with 304 Not Modified or 412 Precondition Failed
+        if (e->hasIfNoneMatchEtag(r)) {
             sendNotModifiedOrPreconditionFailedError();
-            return;
+            return true;
         }
 
-        // otherwise check IMS below to decide if we reply with 304 or 412
-        matchedIfNoneMatch = true;
+        // None-Match is true (no ETag matched); treat as an unconditional hit
+        return false;
     }
 
     if (r.flags.ims) {
         // handle If-Modified-Since requests from the client
         if (e->modifiedSince(r.ims, r.imslen)) {
-            http->logType = LOG_TCP_IMS_HIT;
-            sendMoreData(result);
-            return;
-        }
+            // Modified-Since is true; treat as an unconditional hit
+            return false;
 
-        if (matchedIfNoneMatch) {
-            // If-None-Match matched, reply with 304 Not Modified or
-            // 412 Precondition Failed
-            sendNotModifiedOrPreconditionFailedError();
-            return;
+        } else {
+            // otherwise reply with 304 Not Modified
+            sendNotModified();
         }
-
-        // otherwise reply with 304 Not Modified
-        sendNotModified();
+        return true;
     }
+
+    return false;
 }
 
 /// whether squid.conf send_hit prevents us from serving this hit
@@ -1361,7 +1370,7 @@
     hdr->delById(HDR_ETAG);
 #endif
 
-    if (is_hit)
+    if (is_hit || collapsedRevalidation == crSlave)
         hdr->delById(Http::HdrType::SET_COOKIE);
     // TODO: RFC 2965 : Must honour Cache-Control: no-cache="set-cookie2" and remove header.
 
@@ -1663,7 +1672,9 @@
 {
     HttpRequest *r = http->request;
 
-    if (r->flags.cachable || r->flags.internal) {
+    // client sent CC:no-cache or some other condition has been
+    // encountered which prevents delivering a public/cached object.
+    if (!r->flags.noCache || r->flags.internal) {
         lookingforstore = 5;
         StoreEntry::getPublicByRequest (this, r);
     } else {
@@ -1986,7 +1997,12 @@
     StoreEntry *e = http->storeEntry();
     const time_t timestamp = e->timestamp;
     HttpReply *const temprep = e->getReply()->make304();
-    http->logType = LOG_TCP_IMS_HIT;
+    // log as TCP_INM_HIT if code 304 generated for
+    // If-None-Match request
+    if (!http->request->flags.ims)
+        http->logType = LOG_TCP_INM_HIT;
+    else
+        http->logType = LOG_TCP_IMS_HIT;
     removeClientStoreReference(&sc, http);
     createStoreEntry(http->request->method, RequestFlags());
     e = http->storeEntry();
diff -u -r -N squid-4.0.16/src/client_side_reply.h squid-4.0.17/src/client_side_reply.h
--- squid-4.0.16/src/client_side_reply.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/client_side_reply.h	2016-12-16 23:06:20.000000000 +1300
@@ -115,7 +115,7 @@
     bool alwaysAllowResponse(Http::StatusCode sline) const;
     int checkTransferDone();
     void processOnlyIfCachedMiss();
-    void processConditional(StoreIOBuffer &result);
+    bool processConditional(StoreIOBuffer &result);
     void cacheHit(StoreIOBuffer result);
     void handleIMSReply(StoreIOBuffer result);
     void sendMoreData(StoreIOBuffer result);
diff -u -r -N squid-4.0.16/src/client_side_request.cc squid-4.0.17/src/client_side_request.cc
--- squid-4.0.16/src/client_side_request.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/client_side_request.cc	2016-12-16 23:06:20.000000000 +1300
@@ -1412,6 +1412,11 @@
         return false;
     }
 
+    if (http->request->flags.forceTunnel) {
+        debugs(85, 5, "not needed; already decided to tunnel " << http->getConn());
+        return false;
+    }
+
     // If SSL connection tunneling or bumping decision has been made, obey it.
     const Ssl::BumpMode bumpMode = http->getConn()->sslBumpMode;
     if (bumpMode != Ssl::bumpEnd) {
@@ -1490,13 +1495,17 @@
 {
     debugs(85, 4, request->method << ' ' << uri);
 
-    if (request->method == Http::METHOD_CONNECT && !redirect.status) {
+    const bool untouchedConnect = request->method == Http::METHOD_CONNECT && !redirect.status;
+
 #if USE_OPENSSL
-        if (sslBumpNeeded()) {
-            sslBumpStart();
-            return;
-        }
+    if (untouchedConnect && sslBumpNeeded()) {
+        assert(!request->flags.forceTunnel);
+        sslBumpStart();
+        return;
+    }
 #endif
+
+    if (untouchedConnect || request->flags.forceTunnel) {
         getConn()->stopReading(); // tunnels read for themselves
         tunnelStart(this);
         return;
@@ -1795,7 +1804,7 @@
             // We have to serve an error, so bump the client first.
             sslBumpNeed(Ssl::bumpClientFirst);
             // set final error but delay sending until we bump
-            Ssl::ServerBump *srvBump = new Ssl::ServerBump(request, e);
+            Ssl::ServerBump *srvBump = new Ssl::ServerBump(request, e, Ssl::bumpClientFirst);
             errorAppendEntry(e, calloutContext->error);
             calloutContext->error = NULL;
             getConn()->setServerBump(srvBump);
diff -u -r -N squid-4.0.16/src/format/Config.cc squid-4.0.17/src/format/Config.cc
--- squid-4.0.16/src/format/Config.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/format/Config.cc	2016-12-16 23:06:20.000000000 +1300
@@ -20,10 +20,12 @@
 {
     char *name, *def;
 
-    if ((name = ConfigParser::NextToken()) == NULL)
+    if ((name = ConfigParser::NextToken()) == nullptr) {
         self_destruct();
+        return;
+    }
 
-    if ((def = ConfigParser::NextQuotedOrToEol()) == NULL) {
+    if ((def = ConfigParser::NextQuotedOrToEol()) == nullptr) {
         self_destruct();
         return;
     }
diff -u -r -N squid-4.0.16/src/fqdncache.cc squid-4.0.17/src/fqdncache.cc
--- squid-4.0.16/src/fqdncache.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/fqdncache.cc	2016-12-16 23:06:20.000000000 +1300
@@ -22,7 +22,6 @@
 #include "StatCounters.h"
 #include "Store.h"
 #include "util.h"
-#include "wordlist.h"
 
 #if SQUID_SNMP
 #include "snmp_core.h"
@@ -642,27 +641,20 @@
 }
 
 /**
- \ingroup FQDNCacheAPI
- *
  * Adds a "static" entry from /etc/hosts.
- \par
- * The worldist is to be managed by the caller,
- * including pointed-to strings
  *
  \param addr        FQDN name to be added.
- \param hostnames   ??
+ \param hostnames   list of hostnames for the addr
  */
 void
-fqdncacheAddEntryFromHosts(char *addr, wordlist * hostnames)
+fqdncacheAddEntryFromHosts(char *addr, SBufList &hostnames)
 {
-    fqdncache_entry *fce;
-    int j = 0;
-
-    if ((fce = fqdncache_get(addr))) {
+    fqdncache_entry *fce= fqdncache_get(addr);
+    if (fce) {
         if (1 == fce->flags.fromhosts) {
             fqdncacheUnlockEntry(fce);
         } else if (fce->locks > 0) {
-            debugs(35, DBG_IMPORTANT, "fqdncacheAddEntryFromHosts: can't add static entry for locked address '" << addr << "'");
+            debugs(35, DBG_IMPORTANT, "WARNING: can't add static entry for locked address '" << addr << "'");
             return;
         } else {
             fqdncacheRelease(fce);
@@ -671,11 +663,11 @@
 
     fce = new fqdncache_entry(addr);
 
-    while (hostnames) {
-        fce->names[j] = xstrdup(hostnames->key);
+    int j = 0;
+    for (auto &h : hostnames) {
+        fce->names[j] = xstrdup(h.c_str());
         Tolower(fce->names[j]);
         ++j;
-        hostnames = hostnames->next;
 
         if (j >= FQDN_MAX_NAMES)
             break;
diff -u -r -N squid-4.0.16/src/fqdncache.h squid-4.0.17/src/fqdncache.h
--- squid-4.0.16/src/fqdncache.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/fqdncache.h	2016-12-16 23:06:20.000000000 +1300
@@ -12,9 +12,9 @@
 #define SQUID_FQDNCACHE_H_
 
 #include "ip/Address.h"
+#include "sbuf/forward.h"
 
 class StoreEntry;
-class wordlist;
 namespace Dns
 {
 class LookupDetails;
@@ -27,7 +27,7 @@
 void fqdncacheFreeMemory(void);
 void fqdncache_restart(void);
 void fqdncache_purgelru(void *);
-void fqdncacheAddEntryFromHosts(char *addr, wordlist * hostnames);
+void fqdncacheAddEntryFromHosts(char *addr, SBufList &hostnames);
 
 const char *fqdncache_gethostbyaddr(const Ip::Address &, int flags);
 void fqdncache_nbgethostbyaddr(const Ip::Address &, FQDNH *, void *);
diff -u -r -N squid-4.0.16/src/FwdState.cc squid-4.0.17/src/FwdState.cc
--- squid-4.0.16/src/FwdState.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/FwdState.cc	2016-12-16 23:06:20.000000000 +1300
@@ -847,28 +847,24 @@
         ConnStateData *pinned_connection = request->pinnedConnection();
         debugs(17,7, "pinned peer connection: " << pinned_connection);
         // pinned_connection may become nil after a pconn race
-        if (pinned_connection) {
-            serverConn = pinned_connection->borrowPinnedConnection(request, serverDestinations[0]->getPeer());
-            if (Comm::IsConnOpen(serverConn)) {
-                pinned_connection->stopPinnedConnectionMonitoring();
-                flags.connected_okay = true;
-                ++n_tries;
-                request->flags.pinned = true;
-                if (pinned_connection->pinnedAuth())
-                    request->flags.auth = true;
+        serverConn = pinned_connection ? pinned_connection->borrowPinnedConnection(request, serverDestinations[0]->getPeer()) : nullptr;
+        if (Comm::IsConnOpen(serverConn)) {
+            flags.connected_okay = true;
+            ++n_tries;
+            request->flags.pinned = true;
 
-                closeHandler = comm_add_close_handler(serverConn->fd,  fwdServerClosedWrapper, this);
+            if (pinned_connection->pinnedAuth())
+                request->flags.auth = true;
 
-                syncWithServerConn(pinned_connection->pinning.host);
+            closeHandler = comm_add_close_handler(serverConn->fd,  fwdServerClosedWrapper, this);
 
-                // the server may close the pinned connection before this request
-                pconnRace = racePossible;
-                dispatch();
-                return;
-            }
+            syncWithServerConn(pinned_connection->pinning.host);
 
-        } else
-            serverConn = nullptr;
+            // the server may close the pinned connection before this request
+            pconnRace = racePossible;
+            dispatch();
+            return;
+        }
 
         // Pinned connection failure.
         debugs(17,2,HERE << "Pinned connection failed: " << pinned_connection);
diff -u -r -N squid-4.0.16/src/htcp.cc squid-4.0.17/src/htcp.cc
--- squid-4.0.16/src/htcp.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/htcp.cc	2016-12-16 23:06:20.000000000 +1300
@@ -22,7 +22,6 @@
 #include "htcp.h"
 #include "http.h"
 #include "HttpRequest.h"
-#include "HttpStateFlags.h"
 #include "icmp/net_db.h"
 #include "ip/tools.h"
 #include "md5.h"
@@ -1428,7 +1427,7 @@
     ssize_t pktlen;
     char vbuf[32];
     HttpHeader hdr(hoRequest);
-    HttpStateFlags flags;
+    Http::StateFlags flags;
 
     if (!Comm::IsConnOpen(htcpIncomingConn))
         return 0;
@@ -1478,7 +1477,7 @@
     char vbuf[32];
     HttpHeader hdr(hoRequest);
     MemBuf mb;
-    HttpStateFlags flags;
+    Http::StateFlags flags;
 
     if (!Comm::IsConnOpen(htcpIncomingConn))
         return;
diff -u -r -N squid-4.0.16/src/http/Makefile.am squid-4.0.17/src/http/Makefile.am
--- squid-4.0.16/src/http/Makefile.am	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/http/Makefile.am	2016-12-16 23:06:20.000000000 +1300
@@ -25,6 +25,7 @@
 	RegisteredHeadersHash.cci \
 	RequestMethod.cc \
 	RequestMethod.h \
+	StateFlags.h \
 	StatusCode.cc \
 	StatusCode.h \
 	StatusLine.cc \
diff -u -r -N squid-4.0.16/src/http/Makefile.in squid-4.0.17/src/http/Makefile.in
--- squid-4.0.16/src/http/Makefile.in	2016-10-31 01:27:19.000000000 +1300
+++ squid-4.0.17/src/http/Makefile.in	2016-12-16 23:16:56.000000000 +1300
@@ -769,6 +769,7 @@
 	RegisteredHeadersHash.cci \
 	RequestMethod.cc \
 	RequestMethod.h \
+	StateFlags.h \
 	StatusCode.cc \
 	StatusCode.h \
 	StatusLine.cc \
diff -u -r -N squid-4.0.16/src/http/one/RequestParser.cc squid-4.0.17/src/http/one/RequestParser.cc
--- squid-4.0.16/src/http/one/RequestParser.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/http/one/RequestParser.cc	2016-12-16 23:06:20.000000000 +1300
@@ -20,8 +20,9 @@
     return Config.onoff.relaxed_header_parser < 0 ? DBG_IMPORTANT : 5;
 }
 
-Http::One::RequestParser::RequestParser() :
-    Parser()
+Http::One::RequestParser::RequestParser(bool preserveParsed) :
+    Parser(),
+    preserveParsed_(preserveParsed)
 {}
 
 Http1::Parser::size_type
@@ -347,6 +348,19 @@
 bool
 Http::One::RequestParser::parse(const SBuf &aBuf)
 {
+    const bool result = doParse(aBuf);
+    if (preserveParsed_) {
+        assert(aBuf.length() >= remaining().length());
+        parsed_.append(aBuf.substr(0, aBuf.length() - remaining().length())); // newly parsed bytes
+    }
+
+    return result;
+}
+
+// raw is not a reference because a reference might point back to our own buf_ or parsed_
+bool
+Http::One::RequestParser::doParse(const SBuf &aBuf)
+{
     buf_ = aBuf;
     debugs(74, DBG_DATA, "Parse buf={length=" << aBuf.length() << ", data='" << aBuf << "'}");
 
diff -u -r -N squid-4.0.16/src/http/one/RequestParser.h squid-4.0.17/src/http/one/RequestParser.h
--- squid-4.0.16/src/http/one/RequestParser.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/http/one/RequestParser.h	2016-12-16 23:06:20.000000000 +1300
@@ -30,7 +30,7 @@
 class RequestParser : public Http1::Parser
 {
 public:
-    RequestParser();
+    explicit RequestParser(bool preserveParsed = false);
     virtual ~RequestParser() {}
 
     /* Http::One::Parser API */
@@ -44,9 +44,14 @@
     /// the request-line URI if this is a request message, or an empty string.
     const SBuf &requestUri() const {return uri_;}
 
+    /// the accumulated parsed bytes
+    const SBuf &parsed() const { Must(preserveParsed_); return parsed_; }
+
 private:
     void skipGarbageLines();
     int parseRequestFirstLine();
+    /// called from parse() to do the parsing
+    bool doParse(const SBuf &aBuf);
 
     /* all these return false and set parseStatusCode on parsing failures */
     bool parseMethodField(Http1::Tokenizer &);
@@ -63,6 +68,11 @@
 
     /// raw copy of the original client request-line URI field
     SBuf uri_;
+
+    /// all parsed bytes (i.e., input prefix consumed by parse() calls)
+    /// meaningless unless preserveParsed_ is true
+    SBuf parsed_;
+    bool preserveParsed_; ///< whether to accumulate parsed bytes (in parsed_)
 };
 
 } // namespace One
diff -u -r -N squid-4.0.16/src/http/StateFlags.h squid-4.0.17/src/http/StateFlags.h
--- squid-4.0.16/src/http/StateFlags.h	1970-01-01 12:00:00.000000000 +1200
+++ squid-4.0.17/src/http/StateFlags.h	2016-12-16 23:06:20.000000000 +1300
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID_SRC_HTTP_STATEFLAGS_H
+#define SQUID_SRC_HTTP_STATEFLAGS_H
+
+namespace Http
+{
+
+class StateFlags
+{
+public:
+    unsigned int front_end_https = 0; ///< send "Front-End-Https: On" header (off/on/auto=2)
+    bool proxying = false;
+    bool keepalive = false;
+    bool only_if_cached = false;
+    bool handling1xx = false;       ///< we are ignoring or forwarding 1xx response
+    bool headers_parsed = false;
+    bool originpeer = false;
+    bool keepalive_broken = false;
+    bool abuse_detected = false;
+    bool request_sent = false;
+    bool do_next_read = false;
+    bool chunked = false;           ///< reading a chunked response; TODO: rename
+    bool chunked_request = false;   ///< writing a chunked request
+    bool sentLastChunk = false;     ///< do not try to write last-chunk again
+};
+
+} // namespace Http
+
+#endif /* SQUID_SRC_HTTP_STATEFLAGS_H */
+
diff -u -r -N squid-4.0.16/src/http/StatusCode.cc squid-4.0.17/src/http/StatusCode.cc
--- squid-4.0.16/src/http/StatusCode.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/http/StatusCode.cc	2016-12-16 23:06:20.000000000 +1300
@@ -33,6 +33,10 @@
         return "Processing";
         break;
 
+    case Http::scEarlyHints: // 103
+        return "Early Hints";
+        break;
+
     // 200-299
     case Http::scOkay:
         return "OK";
diff -u -r -N squid-4.0.16/src/http/StatusCode.h squid-4.0.17/src/http/StatusCode.h
--- squid-4.0.16/src/http/StatusCode.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/http/StatusCode.h	2016-12-16 23:06:20.000000000 +1300
@@ -22,6 +22,7 @@
     scContinue = 100,
     scSwitchingProtocols = 101,
     scProcessing = 102,      /**< RFC2518 section 10.1 */
+    scEarlyHints = 103,      /**< draft-kazuho-early-hints-status-code */
     scOkay = 200,
     scCreated = 201,
     scAccepted = 202,
diff -u -r -N squid-4.0.16/src/http/Stream.h squid-4.0.17/src/http/Stream.h
--- squid-4.0.16/src/http/Stream.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/http/Stream.h	2016-12-16 23:06:20.000000000 +1300
@@ -75,6 +75,9 @@
     /// register this stream with the Server
     void registerWithConn();
 
+    /// whether it is registered with a Server
+    bool connRegistered() const {return connRegistered_;};
+
     /// whether the reply has started being sent
     bool startOfOutput() const;
 
diff -u -r -N squid-4.0.16/src/http/url_rewriters/LFS/url_lfs_rewrite.8 squid-4.0.17/src/http/url_rewriters/LFS/url_lfs_rewrite.8
--- squid-4.0.16/src/http/url_rewriters/LFS/url_lfs_rewrite.8	2016-10-31 03:22:00.000000000 +1300
+++ squid-4.0.17/src/http/url_rewriters/LFS/url_lfs_rewrite.8	2016-12-17 07:25:18.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "URL_LFS_REWRITE 8"
-.TH URL_LFS_REWRITE 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH URL_LFS_REWRITE 8 "2016-12-16" "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-4.0.16/src/http.cc squid-4.0.17/src/http.cc
--- squid-4.0.16/src/http.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/http.cc	2016-12-16 23:06:20.000000000 +1300
@@ -41,7 +41,6 @@
 #include "HttpHeaderTools.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
-#include "HttpStateFlags.h"
 #include "log/access_log.h"
 #include "MemBuf.h"
 #include "MemObject.h"
@@ -82,7 +81,7 @@
 
 static void httpMaybeRemovePublic(StoreEntry *, Http::StatusCode);
 static void copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request,
-        HttpHeader * hdr_out, const int we_do_ranges, const HttpStateFlags &);
+        HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &);
 
 HttpStateData::HttpStateData(FwdState *theFwdState) :
     AsyncJob("HttpStateData"),
@@ -190,6 +189,12 @@
     if (!EBIT_TEST(e->flags, KEY_PRIVATE))
         return;
 
+    // If the new/incoming response cannot be stored, then it does not
+    // compete with the old stored response for the public key, and the
+    // old stored response should be left as is.
+    if (e->mem_obj->request && !e->mem_obj->request->flags.cachable)
+        return;
+
     switch (status) {
 
     case Http::scOkay:
@@ -202,6 +207,8 @@
 
     case Http::scFound:
 
+    case Http::scSeeOther:
+
     case Http::scGone:
 
     case Http::scNotFound:
@@ -1660,7 +1667,7 @@
  * Fixup authentication request headers for special cases
  */
 static void
-httpFixupAuthentication(HttpRequest * request, const HttpHeader * hdr_in, HttpHeader * hdr_out, const HttpStateFlags &flags)
+httpFixupAuthentication(HttpRequest * request, const HttpHeader * hdr_in, HttpHeader * hdr_out, const Http::StateFlags &flags)
 {
     Http::HdrType header = flags.originpeer ? Http::HdrType::AUTHORIZATION : Http::HdrType::PROXY_AUTHORIZATION;
 
@@ -1766,7 +1773,7 @@
                                       StoreEntry * entry,
                                       const AccessLogEntryPointer &al,
                                       HttpHeader * hdr_out,
-                                      const HttpStateFlags &flags)
+                                      const Http::StateFlags &flags)
 {
     /* building buffer for complex strings */
 #define BBUF_SZ (MAX_URL+32)
@@ -1956,7 +1963,7 @@
  * to our outgoing fetch request.
  */
 void
-copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request, HttpHeader * hdr_out, const int we_do_ranges, const HttpStateFlags &flags)
+copyOneHeaderFromClientsideRequestToUpstreamRequest(const HttpHeaderEntry *e, const String strConnection, const HttpRequest * request, HttpHeader * hdr_out, const int we_do_ranges, const Http::StateFlags &flags)
 {
     debugs(11, 5, "httpBuildRequestHeader: " << e->name << ": " << e->value );
 
diff -u -r -N squid-4.0.16/src/HttpControlMsg.cc squid-4.0.17/src/HttpControlMsg.cc
--- squid-4.0.16/src/HttpControlMsg.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/HttpControlMsg.cc	2016-12-16 23:06:20.000000000 +1300
@@ -11,6 +11,15 @@
 #include "CommCalls.h"
 #include "HttpControlMsg.h"
 
+void
+HttpControlMsgSink::doneWithControlMsg()
+{
+    if (cbControlMsgSent) {
+        ScheduleCallHere(cbControlMsgSent);
+        cbControlMsgSent = nullptr;
+    }
+}
+
 /// called when we wrote the 1xx response
 void
 HttpControlMsgSink::wroteControlMsg(const CommIoCbParams &params)
@@ -19,8 +28,7 @@
         return;
 
     if (params.flag == Comm::OK) {
-        if (cbControlMsgSent)
-            ScheduleCallHere(cbControlMsgSent);
+        doneWithControlMsg();
         return;
     }
 
diff -u -r -N squid-4.0.16/src/HttpControlMsg.h squid-4.0.17/src/HttpControlMsg.h
--- squid-4.0.16/src/HttpControlMsg.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/HttpControlMsg.h	2016-12-16 23:06:20.000000000 +1300
@@ -33,6 +33,8 @@
     /// called to send the 1xx message and notify the Source
     virtual void sendControlMsg(HttpControlMsg msg) = 0;
 
+    virtual void doneWithControlMsg();
+
     /// callback to handle Comm::Write completion
     void wroteControlMsg(const CommIoCbParams &);
 
diff -u -r -N squid-4.0.16/src/http.h squid-4.0.17/src/http.h
--- squid-4.0.16/src/http.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/http.h	2016-12-16 23:06:20.000000000 +1300
@@ -12,7 +12,7 @@
 #include "clients/Client.h"
 #include "comm.h"
 #include "http/forward.h"
-#include "HttpStateFlags.h"
+#include "http/StateFlags.h"
 #include "sbuf/SBuf.h"
 
 class FwdState;
@@ -30,7 +30,7 @@
                                        StoreEntry * entry,
                                        const AccessLogEntryPointer &al,
                                        HttpHeader * hdr_out,
-                                       const HttpStateFlags &flags);
+                                       const Http::StateFlags &flags);
 
     virtual const Comm::ConnectionPointer & dataConnection() const;
     /* should be private */
@@ -46,7 +46,7 @@
     CachePeer *_peer;       /* CachePeer request made to */
     int eof;            /* reached end-of-object? */
     int lastChunk;      /* reached last chunk of a chunk-encoded reply */
-    HttpStateFlags flags;
+    Http::StateFlags flags;
     size_t read_sz;
     SBuf inBuf;                ///< I/O buffer for receiving server responses
     bool ignoreCacheControl;
diff -u -r -N squid-4.0.16/src/HttpRequest.cc squid-4.0.17/src/HttpRequest.cc
--- squid-4.0.16/src/HttpRequest.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/HttpRequest.cc	2016-12-16 23:06:20.000000000 +1300
@@ -543,8 +543,13 @@
         if (!method.respMaybeCacheable())
             return false;
 
-        // XXX: this would seem the correct place to detect request cache-controls
-        //      no-store, private and related which block cacheability
+        // RFC 7234 section 5.2.1.5:
+        // "cache MUST NOT store any part of either this request or any response to it"
+        //
+        // NP: refresh_pattern ignore-no-store only applies to response messages
+        //     this test is handling request message CC header.
+        if (!flags.ignoreCc && cache_control && cache_control->noStore())
+            return false;
         break;
 
     case AnyP::PROTO_GOPHER:
@@ -657,7 +662,7 @@
 const SBuf &
 HttpRequest::effectiveRequestUri() const
 {
-    if (method.id() == Http::METHOD_CONNECT)
+    if (method.id() == Http::METHOD_CONNECT || url.getScheme() == AnyP::PROTO_AUTHORITY_FORM)
         return url.authority(true); // host:port
     return url.absolute();
 }
diff -u -r -N squid-4.0.16/src/HttpStateFlags.h squid-4.0.17/src/HttpStateFlags.h
--- squid-4.0.16/src/HttpStateFlags.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/HttpStateFlags.h	1970-01-01 12:00:00.000000000 +1200
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#ifndef SQUID_HTTPSTATEFLAGS_H_
-#define SQUID_HTTPSTATEFLAGS_H_
-
-// POD
-class HttpStateFlags
-{
-public:
-    bool proxying:1;
-    bool keepalive:1;
-    bool only_if_cached:1;
-    bool handling1xx:1; ///< we are ignoring or forwarding 1xx response
-    bool headers_parsed:1;
-    unsigned int front_end_https:2; //XXX: huh?
-    bool originpeer:1;
-    bool keepalive_broken:1;
-    bool abuse_detected:1;
-    bool request_sent:1;
-    bool do_next_read:1;
-    bool consume_body_data:1; //XXX: seems unused
-    bool chunked:1; ///< reading a chunked response; TODO: rename
-    bool chunked_request:1; ///< writing a chunked request
-    bool sentLastChunk:1; ///< do not try to write last-chunk again
-};
-
-#endif /* SQUID_HTTPSTATEFLAGS_H_ */
-
diff -u -r -N squid-4.0.16/src/ip/Intercept.cc squid-4.0.17/src/ip/Intercept.cc
--- squid-4.0.16/src/ip/Intercept.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ip/Intercept.cc	2016-12-16 23:06:20.000000000 +1300
@@ -343,13 +343,20 @@
     }
 
     memset(&nl, 0, sizeof(struct pfioc_natlook));
-    newConn->remote.getInAddr(nl.saddr.v4);
-    nl.sport = htons(newConn->remote.port());
 
-    newConn->local.getInAddr(nl.daddr.v4);
+    if (newConn->remote.isIPv6()) {
+        newConn->remote.getInAddr(nl.saddr.v6);
+        newConn->local.getInAddr(nl.daddr.v6);
+        nl.af = AF_INET6;
+    } else {
+        newConn->remote.getInAddr(nl.saddr.v4);
+        newConn->local.getInAddr(nl.daddr.v4);
+        nl.af = AF_INET;
+    }
+
+    nl.sport = htons(newConn->remote.port());
     nl.dport = htons(newConn->local.port());
 
-    nl.af = AF_INET;
     nl.proto = IPPROTO_TCP;
     nl.direction = PF_OUT;
 
@@ -366,7 +373,10 @@
         debugs(89, 9, HERE << "address: " << newConn);
         return false;
     } else {
-        newConn->local = nl.rdaddr.v4;
+        if (newConn->remote.isIPv6())
+            newConn->local = nl.rdaddr.v6;
+        else
+            newConn->local = nl.rdaddr.v4;
         newConn->local.port(ntohs(nl.rdport));
         debugs(89, 5, HERE << "address NAT: " << newConn);
         return true;
diff -u -r -N squid-4.0.16/src/ipcache.cc squid-4.0.17/src/ipcache.cc
--- squid-4.0.16/src/ipcache.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ipcache.cc	2016-12-16 23:06:20.000000000 +1300
@@ -129,7 +129,6 @@
 static FREE ipcacheFreeEntry;
 static IDNSCB ipcacheHandleReply;
 static int ipcacheExpiredEntry(ipcache_entry *);
-static int ipcacheParse(ipcache_entry *, const rfc1035_rr *, int, const char *error);
 static ipcache_entry *ipcache_get(const char *);
 static void ipcacheLockEntry(ipcache_entry *);
 static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
@@ -333,8 +332,7 @@
     ipcacheUnlockEntry(i);
 }
 
-/// \ingroup IPCacheAPI
-static int
+static void
 ipcacheParse(ipcache_entry *i, const rfc1035_rr * answers, int nr, const char *error_message)
 {
     int k;
@@ -355,25 +353,25 @@
     i->addrs.count = 0;
 
     if (nr < 0) {
-        debugs(14, 3, "ipcacheParse: Lookup failed '" << error_message << "' for '" << (const char *)i->hash.key << "'");
+        debugs(14, 3, "Lookup failed '" << error_message << "' for '" << (const char *)i->hash.key << "'");
         i->error_message = xstrdup(error_message);
-        return -1;
+        return;
     }
 
     if (nr == 0) {
-        debugs(14, 3, "ipcacheParse: No DNS records in response to '" << name << "'");
+        debugs(14, 3, "No DNS records in response to '" << name << "'");
         i->error_message = xstrdup("No DNS records");
-        return -1;
+        return;
     }
 
-    debugs(14, 3, "ipcacheParse: " << nr << " answers for '" << name << "'");
+    debugs(14, 3, nr << " answers for '" << name << "'");
     assert(answers);
 
     for (k = 0; k < nr; ++k) {
 
         if (Ip::EnableIpv6 && answers[k].type == RFC1035_TYPE_AAAA) {
             if (answers[k].rdlength != sizeof(struct in6_addr)) {
-                debugs(14, DBG_IMPORTANT, "ipcacheParse: Invalid IPv6 address in response to '" << name << "'");
+                debugs(14, DBG_IMPORTANT, MYNAME << "Invalid IPv6 address in response to '" << name << "'");
                 continue;
             }
             ++na;
@@ -383,7 +381,7 @@
 
         if (answers[k].type == RFC1035_TYPE_A) {
             if (answers[k].rdlength != sizeof(struct in_addr)) {
-                debugs(14, DBG_IMPORTANT, "ipcacheParse: Invalid IPv4 address in response to '" << name << "'");
+                debugs(14, DBG_IMPORTANT, MYNAME << "Invalid IPv4 address in response to '" << name << "'");
                 continue;
             }
             ++na;
@@ -399,14 +397,14 @@
         }
 
         // otherwise its an unknown RR. debug at level 9 since we usually want to ignore these and they are common.
-        debugs(14, 9, HERE << "Unknown RR type received: type=" << answers[k].type << " starting at " << &(answers[k]) );
+        debugs(14, 9, "Unknown RR type received: type=" << answers[k].type << " starting at " << &(answers[k]) );
     }
     if (na == 0) {
-        debugs(14, DBG_IMPORTANT, "ipcacheParse: No Address records in response to '" << name << "'");
+        debugs(14, DBG_IMPORTANT, MYNAME << "No Address records in response to '" << name << "'");
         i->error_message = xstrdup("No Address records");
         if (cname_found)
             ++IpcacheStats.cname_only;
-        return 0;
+        return;
     }
 
     i->addrs.in_addrs = static_cast<Ip::Address *>(xcalloc(na, sizeof(Ip::Address)));
@@ -424,7 +422,7 @@
             memcpy(&temp, answers[k].rdata, sizeof(struct in_addr));
             i->addrs.in_addrs[j] = temp;
 
-            debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j]);
+            debugs(14, 3, name << " #" << j << " " << i->addrs.in_addrs[j]);
             ++j;
 
         } else if (Ip::EnableIpv6 && answers[k].type == RFC1035_TYPE_AAAA) {
@@ -435,7 +433,7 @@
             memcpy(&temp, answers[k].rdata, sizeof(struct in6_addr));
             i->addrs.in_addrs[j] = temp;
 
-            debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j] );
+            debugs(14, 3, name << " #" << j << " " << i->addrs.in_addrs[j] );
             ++j;
         }
         if (ttl == 0 || (int) answers[k].ttl < ttl)
@@ -458,8 +456,6 @@
     i->expires = squid_curtime + ttl;
 
     i->flags.negcached = false;
-
-    return i->addrs.count;
 }
 
 /// \ingroup IPCacheInternal
@@ -472,13 +468,9 @@
     const int age = i->age();
     statCounter.dns.svcTime.count(age);
 
-    int done = ipcacheParse(i, answers, na, error_message);
-
-    /* If we have not produced either IPs or Error immediately, wait for recursion to finish. */
-    if (done != 0 || error_message != NULL) {
-        ipcacheAddEntry(i);
-        ipcacheCallback(i, age);
-    }
+    ipcacheParse(i, answers, na, error_message);
+    ipcacheAddEntry(i);
+    ipcacheCallback(i, age);
 }
 
 /**
diff -u -r -N squid-4.0.16/src/log/DB/log_db_daemon.8 squid-4.0.17/src/log/DB/log_db_daemon.8
--- squid-4.0.16/src/log/DB/log_db_daemon.8	2016-10-31 03:22:14.000000000 +1300
+++ squid-4.0.17/src/log/DB/log_db_daemon.8	2016-12-17 07:25:42.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "LOG_DB_DAEMON 8"
-.TH LOG_DB_DAEMON 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH LOG_DB_DAEMON 8 "2016-12-16" "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-4.0.16/src/LogTags.cc squid-4.0.17/src/LogTags.cc
--- squid-4.0.16/src/LogTags.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/LogTags.cc	2016-12-16 23:06:20.000000000 +1300
@@ -21,6 +21,7 @@
     "TCP_REFRESH",
     "TCP_CLIENT_REFRESH_MISS",
     "TCP_IMS_HIT",
+    "TCP_INM_HIT",
     "TCP_SWAPFAIL_MISS",
     "TCP_NEGATIVE_HIT",
     "TCP_MEM_HIT",
@@ -73,6 +74,7 @@
     return
         (oldType == LOG_TCP_HIT) ||
         (oldType == LOG_TCP_IMS_HIT) ||
+        (oldType == LOG_TCP_INM_HIT) ||
         (oldType == LOG_TCP_REFRESH_FAIL_OLD) ||
         (oldType == LOG_TCP_REFRESH_UNMODIFIED) ||
         (oldType == LOG_TCP_NEGATIVE_HIT) ||
diff -u -r -N squid-4.0.16/src/LogTags.h squid-4.0.17/src/LogTags.h
--- squid-4.0.16/src/LogTags.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/LogTags.h	2016-12-16 23:06:20.000000000 +1300
@@ -28,6 +28,7 @@
     LOG_TCP_REFRESH,            // refresh from origin started, but still pending
     LOG_TCP_CLIENT_REFRESH_MISS,
     LOG_TCP_IMS_HIT,
+    LOG_TCP_INM_HIT,
     LOG_TCP_SWAPFAIL_MISS,
     LOG_TCP_NEGATIVE_HIT,
     LOG_TCP_MEM_HIT,
diff -u -r -N squid-4.0.16/src/Makefile.am squid-4.0.17/src/Makefile.am
--- squid-4.0.16/src/Makefile.am	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/Makefile.am	2016-12-16 23:06:20.000000000 +1300
@@ -312,7 +312,6 @@
 	hier_code.h \
 	HierarchyLogEntry.h \
 	$(HTCPSOURCE) \
-	HttpStateFlags.h \
 	http.cc \
 	http.h \
 	HttpHeaderFieldStat.h \
@@ -1263,7 +1262,6 @@
 	hier_code.h \
 	helper.cc \
 	$(HTCPSOURCE) \
-	HttpStateFlags.h \
 	http.cc \
 	HttpBody.h \
 	HttpBody.cc \
diff -u -r -N squid-4.0.16/src/Makefile.in squid-4.0.17/src/Makefile.in
--- squid-4.0.16/src/Makefile.in	2016-10-31 01:26:51.000000000 +1300
+++ squid-4.0.17/src/Makefile.in	2016-12-16 23:14:21.000000000 +1300
@@ -265,9 +265,9 @@
 	fde.cc fde.h FileMap.h filemap.cc fqdncache.h fqdncache.cc \
 	FwdState.cc FwdState.h Generic.h globals.h gopher.h gopher.cc \
 	helper.cc helper.h hier_code.h HierarchyLogEntry.h htcp.cc \
-	htcp.h HttpStateFlags.h http.cc http.h HttpHeaderFieldStat.h \
-	HttpHdrCc.h HttpHdrCc.cc HttpHdrCc.cci HttpHdrRange.cc \
-	HttpHdrSc.cc HttpHdrSc.h HttpHdrScTarget.cc HttpHdrScTarget.h \
+	htcp.h http.cc http.h HttpHeaderFieldStat.h HttpHdrCc.h \
+	HttpHdrCc.cc HttpHdrCc.cci HttpHdrRange.cc HttpHdrSc.cc \
+	HttpHdrSc.h HttpHdrScTarget.cc HttpHdrScTarget.h \
 	HttpHdrContRange.cc HttpHdrContRange.h HttpHeaderStat.h \
 	HttpHeader.h HttpHeader.cc HttpHeaderMask.h HttpHeaderRange.h \
 	HttpHeaderFieldInfo.h HttpHeaderTools.h HttpHeaderTools.cc \
@@ -538,23 +538,23 @@
 	external_acl.cc ExternalACLEntry.cc fatal.h \
 	tests/stub_fatal.cc fd.h fd.cc fde.cc FileMap.h filemap.cc \
 	fqdncache.h fqdncache.cc FwdState.cc FwdState.h gopher.h \
-	gopher.cc hier_code.h helper.cc htcp.cc htcp.h \
-	HttpStateFlags.h http.cc HttpBody.h HttpBody.cc HttpHeader.h \
-	HttpHeader.cc HttpHeaderFieldInfo.h HttpHeaderTools.h \
-	HttpHeaderTools.cc HttpHeaderFieldStat.h HttpHdrCc.h \
-	HttpHdrCc.cc HttpHdrCc.cci HttpHdrContRange.cc HttpHdrRange.cc \
-	HttpHdrSc.cc HttpHdrScTarget.cc HttpMsg.cc HttpReply.cc \
-	icp_v2.cc icp_v3.cc SquidIpc.h ipc.cc ipc_win32.cc ipcache.cc \
-	int.h int.cc internal.h internal.cc LogTags.cc \
-	tests/stub_libsecurity.cc MasterXaction.cc MasterXaction.h \
-	multicast.h multicast.cc mem_node.cc MemBuf.cc MemObject.cc \
-	mime.h mime.cc mime_header.h mime_header.cc neighbors.h \
-	neighbors.cc Notes.cc Notes.h Parsing.cc pconn.cc \
-	peer_digest.cc peer_proxy_negotiate_auth.h \
-	peer_proxy_negotiate_auth.cc peer_select.cc peer_sourcehash.h \
-	peer_sourcehash.cc peer_userhash.h peer_userhash.cc \
-	PeerPoolMgr.h PeerPoolMgr.cc Pipeline.cc Pipeline.h redirect.h \
-	tests/stub_redirect.cc refresh.h refresh.cc RemovalPolicy.cc \
+	gopher.cc hier_code.h helper.cc htcp.cc htcp.h http.cc \
+	HttpBody.h HttpBody.cc HttpHeader.h HttpHeader.cc \
+	HttpHeaderFieldInfo.h HttpHeaderTools.h HttpHeaderTools.cc \
+	HttpHeaderFieldStat.h HttpHdrCc.h HttpHdrCc.cc HttpHdrCc.cci \
+	HttpHdrContRange.cc HttpHdrRange.cc HttpHdrSc.cc \
+	HttpHdrScTarget.cc HttpMsg.cc HttpReply.cc icp_v2.cc icp_v3.cc \
+	SquidIpc.h ipc.cc ipc_win32.cc ipcache.cc int.h int.cc \
+	internal.h internal.cc LogTags.cc tests/stub_libsecurity.cc \
+	MasterXaction.cc MasterXaction.h multicast.h multicast.cc \
+	mem_node.cc MemBuf.cc MemObject.cc mime.h mime.cc \
+	mime_header.h mime_header.cc neighbors.h neighbors.cc Notes.cc \
+	Notes.h Parsing.cc pconn.cc peer_digest.cc \
+	peer_proxy_negotiate_auth.h peer_proxy_negotiate_auth.cc \
+	peer_select.cc peer_sourcehash.h peer_sourcehash.cc \
+	peer_userhash.h peer_userhash.cc PeerPoolMgr.h PeerPoolMgr.cc \
+	Pipeline.cc Pipeline.h redirect.h tests/stub_redirect.cc \
+	refresh.h refresh.cc RemovalPolicy.cc \
 	tests/stub_SBufDetailedStats.cc SnmpRequest.h snmp_core.h \
 	snmp_core.cc snmp_agent.h snmp_agent.cc SquidMath.h \
 	SquidMath.cc IoStats.h stat.h stat.cc StatCounters.h \
@@ -2867,16 +2867,16 @@
 	fde.cc fde.h FileMap.h filemap.cc fqdncache.h fqdncache.cc \
 	FwdState.cc FwdState.h Generic.h globals.h gopher.h gopher.cc \
 	helper.cc helper.h hier_code.h HierarchyLogEntry.h \
-	$(HTCPSOURCE) HttpStateFlags.h http.cc http.h \
-	HttpHeaderFieldStat.h HttpHdrCc.h HttpHdrCc.cc HttpHdrCc.cci \
-	HttpHdrRange.cc HttpHdrSc.cc HttpHdrSc.h HttpHdrScTarget.cc \
-	HttpHdrScTarget.h HttpHdrContRange.cc HttpHdrContRange.h \
-	HttpHeaderStat.h HttpHeader.h HttpHeader.cc HttpHeaderMask.h \
-	HttpHeaderRange.h HttpHeaderFieldInfo.h HttpHeaderTools.h \
-	HttpHeaderTools.cc HttpBody.h HttpBody.cc HttpControlMsg.cc \
-	HttpControlMsg.h HttpMsg.cc HttpMsg.h HttpReply.cc HttpReply.h \
-	RequestFlags.h RequestFlags.cc HttpRequest.cc HttpRequest.h \
-	ICP.h icp_opcode.h icp_v2.cc icp_v3.cc int.h int.cc internal.h \
+	$(HTCPSOURCE) http.cc http.h HttpHeaderFieldStat.h HttpHdrCc.h \
+	HttpHdrCc.cc HttpHdrCc.cci HttpHdrRange.cc HttpHdrSc.cc \
+	HttpHdrSc.h HttpHdrScTarget.cc HttpHdrScTarget.h \
+	HttpHdrContRange.cc HttpHdrContRange.h HttpHeaderStat.h \
+	HttpHeader.h HttpHeader.cc HttpHeaderMask.h HttpHeaderRange.h \
+	HttpHeaderFieldInfo.h HttpHeaderTools.h HttpHeaderTools.cc \
+	HttpBody.h HttpBody.cc HttpControlMsg.cc HttpControlMsg.h \
+	HttpMsg.cc HttpMsg.h HttpReply.cc HttpReply.h RequestFlags.h \
+	RequestFlags.cc HttpRequest.cc HttpRequest.h ICP.h \
+	icp_opcode.h icp_v2.cc icp_v3.cc int.h int.cc internal.h \
 	internal.cc $(IPC_SOURCE) ipcache.cc ipcache.h \
 	$(LEAKFINDERSOURCE) LogTags.cc LogTags.h lookup_t.h main.cc \
 	MasterXaction.cc MasterXaction.h mem_node.cc mem_node.h \
@@ -3532,7 +3532,6 @@
 	hier_code.h \
 	helper.cc \
 	$(HTCPSOURCE) \
-	HttpStateFlags.h \
 	http.cc \
 	HttpBody.h \
 	HttpBody.cc \
diff -u -r -N squid-4.0.16/src/mgr/ActionPasswordList.cc squid-4.0.17/src/mgr/ActionPasswordList.cc
--- squid-4.0.16/src/mgr/ActionPasswordList.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/mgr/ActionPasswordList.cc	2016-12-16 23:06:20.000000000 +1300
@@ -8,12 +8,11 @@
 
 #include "squid.h"
 #include "mgr/ActionPasswordList.h"
-#include "wordlist.h"
+#include "sbuf/List.h"
 
 Mgr::ActionPasswordList::~ActionPasswordList()
 {
-    safe_free(passwd);
-    wordlistDestroy(&actions);
-    delete next;
+    xfree(passwd);
+    delete next; // recurse, these lists are usually not long
 }
 
diff -u -r -N squid-4.0.16/src/mgr/ActionPasswordList.h squid-4.0.17/src/mgr/ActionPasswordList.h
--- squid-4.0.16/src/mgr/ActionPasswordList.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/mgr/ActionPasswordList.h	2016-12-16 23:06:20.000000000 +1300
@@ -9,7 +9,7 @@
 #ifndef SQUID_MGR_CACHEMGRPASSWD_H_
 #define SQUID_MGR_CACHEMGRPASSWD_H_
 
-class wordlist;
+#include "sbuf/forward.h"
 
 namespace Mgr
 {
@@ -18,12 +18,11 @@
 class ActionPasswordList
 {
 public:
-    ActionPasswordList() : passwd(NULL), actions(NULL), next(NULL) {}
     ~ActionPasswordList();
 
-    char *passwd;
-    wordlist *actions;
-    ActionPasswordList *next;
+    char *passwd = nullptr;
+    SBufList actions;
+    ActionPasswordList *next = nullptr;
 };
 
 } //namespace Mgr
diff -u -r -N squid-4.0.16/src/RequestFlags.h squid-4.0.17/src/RequestFlags.h
--- squid-4.0.16/src/RequestFlags.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/RequestFlags.h	2016-12-16 23:06:20.000000000 +1300
@@ -13,108 +13,107 @@
 
 /** request-related flags
  *
- * The bit-field contains both flags marking a request's current state,
+ * Contains both flags marking a request's current state,
  * and flags requesting some processing to be done at a later stage.
  * TODO: better distinguish the two cases.
  */
 class RequestFlags
 {
 public:
-    RequestFlags() {
-        memset(this,0,sizeof(RequestFlags));
-    }
-
     /** true if the response to this request may not be READ from cache */
-    bool noCache :1;
+    bool noCache = false;
     /** request is if-modified-since */
-    bool ims :1;
+    bool ims = false;
     /** request is authenticated */
-    bool auth :1;
+    bool auth = false;
     /** do not use keytabs for peer Kerberos authentication */
-    bool auth_no_keytab :1;
+    bool auth_no_keytab = false;
     /** he response to the request may be stored in the cache */
-    bool cachable :1;
+    bool cachable = false;
     /** the request can be forwarded through the hierarchy */
-    bool hierarchical :1;
+    bool hierarchical = false;
     /** a loop was detected on this request */
-    bool loopDetected :1;
+    bool loopDetected = false;
     /** the connection can be kept alive */
-    bool proxyKeepalive :1;
+    bool proxyKeepalive = false;
     /* this should be killed, also in httpstateflags */
-    bool proxying :1;
+    bool proxying = false;
     /** content has expired, need to refresh it */
-    bool refresh :1;
+    bool refresh = false;
     /** request was redirected by redirectors */
-    bool redirected :1;
+    bool redirected = false;
     /** the requested object needs to be validated. See client_side_reply.cc
      * for further information.
      */
-    bool needValidation :1;
+    bool needValidation = false;
     /** whether we should fail if validation fails */
-    bool failOnValidationError :1;
+    bool failOnValidationError = false;
     /** reply is stale if it is a hit */
-    bool staleIfHit :1;
+    bool staleIfHit = false;
     /** request to override no-cache directives
      *
      * always use noCacheHack() for reading.
      * \note only meaningful if USE_HTTP_VIOLATIONS is defined at build time
      */
-    bool nocacheHack :1;
+    bool nocacheHack = false;
     /** this request is accelerated (reverse-proxy) */
-    bool accelerated :1;
+    bool accelerated = false;
     /** if set, ignore Cache-Control headers */
-    bool ignoreCc :1;
+    bool ignoreCc = false;
     /** set for intercepted requests */
-    bool intercepted :1;
+    bool intercepted = false;
     /** set if the Host: header passed verification */
-    bool hostVerified :1;
+    bool hostVerified = false;
     /// Set for requests handled by a "tproxy" port.
-    bool interceptTproxy :1;
+    bool interceptTproxy = false;
     /// The client IP address should be spoofed when connecting to the web server.
     /// This applies to TPROXY traffic that has not had spoofing disabled through
     /// the spoof_client_ip squid.conf ACL.
-    bool spoofClientIp :1;
+    bool spoofClientIp = false;
     /** set if the request is internal (\see ClientHttpRequest::flags.internal)*/
-    bool internal :1;
-    /** set for internally-generated requests */
+    bool internal = false;
     //XXX this is set in in clientBeginRequest, but never tested.
-    bool internalClient :1;
+    /** set for internally-generated requests */
+    bool internalClient = false;
     /** if set, request to try very hard to keep the connection alive */
-    bool mustKeepalive :1;
+    bool mustKeepalive = false;
     /** set if the rquest wants connection oriented auth */
-    bool connectionAuth :1;
+    bool connectionAuth = false;
     /** set if connection oriented auth can not be supported */
-    bool connectionAuthDisabled :1;
-    /** Request wants connection oriented auth */
+    bool connectionAuthDisabled = false;
     // XXX This is set in clientCheckPinning but never tested
-    bool connectionProxyAuth :1;
+    /** Request wants connection oriented auth */
+    bool connectionProxyAuth = false;
     /** set if the request was sent on a pinned connection */
-    bool pinned :1;
+    bool pinned = false;
     /** Authentication was already sent upstream (e.g. due tcp-level auth) */
-    bool authSent :1;
+    bool authSent = false;
     /** Deny direct forwarding unless overriden by always_direct
      * Used in accelerator mode */
-    bool noDirect :1;
+    bool noDirect = false;
     /** Reply with chunked transfer encoding */
-    bool chunkedReply :1;
+    bool chunkedReply = false;
     /** set if stream error has occured */
-    bool streamError :1;
+    bool streamError = false;
     /** internal ssl-bump request to get server cert */
-    bool sslPeek :1;
+    bool sslPeek = false;
     /** set if X-Forwarded-For checking is complete
      *
      * do not read directly; use doneFollowXff for reading
      */
-    bool done_follow_x_forwarded_for :1;
+    bool done_follow_x_forwarded_for = false;
     /** set for ssl-bumped requests */
-    bool sslBumped :1;
+    bool sslBumped = false;
     /// carries a representation of an FTP command [received on ftp_port]
-    bool ftpNative :1;
-    bool destinationIpLookedUp:1;
+    bool ftpNative = false;
+    bool destinationIpLookedUp = false;
     /** request to reset the TCP stream */
-    bool resetTcp:1;
+    bool resetTcp = false;
     /** set if the request is ranged */
-    bool isRanged :1;
+    bool isRanged = false;
+
+    /// whether to forward via TunnelStateData (instead of FwdState)
+    bool forceTunnel = false;
 
     /** clone the flags, resetting to default those which are not safe in
      *  a related (e.g. ICAP-adapted) request.
diff -u -r -N squid-4.0.16/src/sbuf/SBuf.cc squid-4.0.17/src/sbuf/SBuf.cc
--- squid-4.0.16/src/sbuf/SBuf.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/sbuf/SBuf.cc	2016-12-16 23:06:20.000000000 +1300
@@ -139,7 +139,8 @@
     if (!mustRealloc && len_ >= req.maxCapacity)
         return spaceSize(); // but we cannot reallocate
 
-    const size_type newSpace = std::min(req.idealSpace, maxSize - len_);
+    const size_type desiredSpace = std::max(req.minSpace, req.idealSpace);
+    const size_type newSpace = std::min(desiredSpace, maxSize - len_);
     reserveCapacity(std::min(len_ + newSpace, req.maxCapacity));
     debugs(24, 7, id << " now: " << off_ << '+' << len_ << '+' << spaceSize() <<
            '=' << store_->capacity);
@@ -187,6 +188,9 @@
 SBuf&
 SBuf::append(const SBuf &S)
 {
+    if (isEmpty() && store_ == GetStorePrototype())
+        return (*this = S); // optimization: avoid needless copying
+
     const Locker blobKeeper(this, S.buf());
     return lowAppend(S.buf(), S.length());
 }
diff -u -r -N squid-4.0.16/src/sbuf/SBuf.h squid-4.0.17/src/sbuf/SBuf.h
--- squid-4.0.16/src/sbuf/SBuf.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/sbuf/SBuf.h	2016-12-16 23:06:20.000000000 +1300
@@ -610,6 +610,11 @@
 
     // TODO: possibly implement erase() similar to std::string's erase
     // TODO: possibly implement a replace() call
+
+    /// SBuf object identifier meant for test cases and debugging.
+    /// Does not change when object does, including during assignment.
+    const InstanceId<SBuf> id;
+
 private:
 
     /**
@@ -638,10 +643,6 @@
     size_type len_; ///< number of our content bytes in shared store_
     static SBufStats stats; ///< class-wide statistics
 
-    /// SBuf object identifier; does not change when contents do,
-    ///   including during assignment
-    const InstanceId<SBuf> id;
-
     /** obtain prototype store
      *
      * Just-created SBufs all share to the same MemBlob.
@@ -693,9 +694,10 @@
     /*
      * Parameters are listed in the reverse order of importance: Satisfaction of
      * the lower-listed requirements may violate the higher-listed requirements.
+     * For example, idealSpace has no effect unless it exceeds minSpace.
      */
     size_type idealSpace = 0; ///< if allocating anyway, provide this much space
-    size_type minSpace = 0; ///< allocate if spaceSize() is smaller
+    size_type minSpace = 0; ///< allocate [at least this much] if spaceSize() is smaller
     size_type maxCapacity = SBuf::maxSize; ///< do not allocate more than this
     bool allowShared = true; ///< whether sharing our storage with others is OK
 };
diff -u -r -N squid-4.0.16/src/security/cert_generators/file/security_file_certgen.cc squid-4.0.17/src/security/cert_generators/file/security_file_certgen.cc
--- squid-4.0.16/src/security/cert_generators/file/security_file_certgen.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/security/cert_generators/file/security_file_certgen.cc	2016-12-16 23:06:20.000000000 +1300
@@ -200,7 +200,7 @@
         error = err.what();
     }
 
-    if (cert.get()) {
+    if (cert) {
         if (!Ssl::certificateMatchesProperties(cert.get(), certProperties)) {
             // The certificate changed (renewed or other reason).
             // Generete a new one with the updated fields.
diff -u -r -N squid-4.0.16/src/security/cert_validators/fake/security_fake_certverify.8 squid-4.0.17/src/security/cert_validators/fake/security_fake_certverify.8
--- squid-4.0.16/src/security/cert_validators/fake/security_fake_certverify.8	2016-10-31 03:22:36.000000000 +1300
+++ squid-4.0.17/src/security/cert_validators/fake/security_fake_certverify.8	2016-12-17 07:26:25.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "SECURITY_FAKE_CERTVERIFY 8"
-.TH SECURITY_FAKE_CERTVERIFY 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH SECURITY_FAKE_CERTVERIFY 8 "2016-12-16" "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-4.0.16/src/security/Context.h squid-4.0.17/src/security/Context.h
--- squid-4.0.16/src/security/Context.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/security/Context.h	2016-12-16 23:06:20.000000000 +1300
@@ -9,6 +9,7 @@
 #ifndef SQUID_SRC_SECURITY_CONTEXT_H
 #define SQUID_SRC_SECURITY_CONTEXT_H
 
+#include "security/forward.h"
 #include "security/LockingPointer.h"
 
 #if USE_OPENSSL
@@ -26,15 +27,18 @@
 
 #if USE_OPENSSL
 CtoCpp1(SSL_CTX_free, SSL_CTX *);
-typedef LockingPointer<SSL_CTX, SSL_CTX_free_cpp, CRYPTO_LOCK_SSL_CTX> ContextPointer;
+#if defined(CRYPTO_LOCK_SSL_CTX) // OpenSSL 1.0
+inline int SSL_CTX_up_ref(SSL_CTX *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_SSL_CTX); return 0;}
+#endif
+typedef Security::LockingPointer<SSL_CTX, SSL_CTX_free_cpp, HardFun<int, SSL_CTX *, SSL_CTX_up_ref> > ContextPointer;
 
 #elif USE_GNUTLS
 CtoCpp1(gnutls_certificate_free_credentials, gnutls_certificate_credentials_t);
-typedef Security::LockingPointer<struct gnutls_certificate_credentials_st, gnutls_certificate_free_credentials_cpp, -1> ContextPointer;
+typedef Security::LockingPointer<struct gnutls_certificate_credentials_st, gnutls_certificate_free_credentials_cpp> ContextPointer;
 
 #else
 // use void* so we can check against nullptr
-typedef Security::LockingPointer<void, nullptr, -1> ContextPointer;
+typedef Security::LockingPointer<void, nullptr> ContextPointer;
 
 #endif
 
diff -u -r -N squid-4.0.16/src/security/forward.h squid-4.0.17/src/security/forward.h
--- squid-4.0.16/src/security/forward.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/security/forward.h	2016-12-16 23:06:20.000000000 +1300
@@ -13,12 +13,13 @@
 #include "security/Context.h"
 #include "security/Session.h"
 
-#if USE_GNUTLS
-#if HAVE_GNUTLS_X509_H
+#if USE_GNUTLS && HAVE_GNUTLS_X509_H
 #include <gnutls/x509.h>
 #endif
-#endif
 #include <list>
+#if USE_OPENSSL && HAVE_OPENSSL_ERR_H
+#include <openssl/err.h>
+#endif
 #include <unordered_set>
 
 #if USE_OPENSSL
@@ -50,20 +51,26 @@
 
 #if USE_OPENSSL
 CtoCpp1(X509_free, X509 *)
-typedef Security::LockingPointer<X509, X509_free_cpp, CRYPTO_LOCK_X509> CertPointer;
+#if defined(CRYPTO_LOCK_X509) // OpenSSL 1.0
+inline int X509_up_ref(X509 *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_X509); return 0;}
+#endif
+typedef Security::LockingPointer<X509, X509_free_cpp, HardFun<int, X509 *, X509_up_ref> > CertPointer;
 #elif USE_GNUTLS
 CtoCpp1(gnutls_x509_crt_deinit, gnutls_x509_crt_t)
-typedef Security::LockingPointer<struct gnutls_x509_crt_int, gnutls_x509_crt_deinit, -1> CertPointer;
+typedef Security::LockingPointer<struct gnutls_x509_crt_int, gnutls_x509_crt_deinit> CertPointer;
 #else
 typedef void * CertPointer;
 #endif
 
 #if USE_OPENSSL
 CtoCpp1(X509_CRL_free, X509_CRL *)
-typedef LockingPointer<X509_CRL, X509_CRL_free_cpp, CRYPTO_LOCK_X509_CRL> CrlPointer;
+#if defined(CRYPTO_LOCK_X509_CRL) // OpenSSL 1.0
+inline int X509_CRL_up_ref(X509_CRL *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_X509_CRL); return 0;}
+#endif
+typedef Security::LockingPointer<X509_CRL, X509_CRL_free_cpp, HardFun<int, X509_CRL *, X509_CRL_up_ref> > CrlPointer;
 #elif USE_GNUTLS
 CtoCpp1(gnutls_x509_crl_deinit, gnutls_x509_crl_t)
-typedef Security::LockingPointer<struct gnutls_x509_crl_int, gnutls_x509_crl_deinit, -1> CrlPointer;
+typedef Security::LockingPointer<struct gnutls_x509_crl_int, gnutls_x509_crl_deinit> CrlPointer;
 #else
 typedef void *CrlPointer;
 #endif
@@ -74,7 +81,10 @@
 
 #if USE_OPENSSL
 CtoCpp1(DH_free, DH *);
-typedef Security::LockingPointer<DH, DH_free_cpp, CRYPTO_LOCK_DH> DhePointer;
+#if defined(CRYPTO_LOCK_DH) // OpenSSL 1.0
+inline int DH_up_ref(DH *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_DH); return 0;}
+#endif
+typedef Security::LockingPointer<DH, DH_free_cpp, HardFun<int, DH *, DH_up_ref> > DhePointer;
 #else
 typedef void *DhePointer;
 #endif
@@ -84,6 +94,16 @@
 /// Squid defined error code (<0), an error code returned by X.509 API, or SSL_ERROR_NONE
 typedef int ErrorCode;
 
+inline const char *ErrorString(const ErrorCode code) {
+#if USE_OPENSSL
+    return ERR_error_string(code, nullptr);
+#elif USE_GNUTLS
+    return gnutls_strerror(code);
+#else
+    return "[no TLS library]";
+#endif
+}
+
 /// set of Squid defined TLS error codes
 /// \note using std::unordered_set ensures values are unique, with fast lookup
 typedef std::unordered_set<Security::ErrorCode> Errors;
diff -u -r -N squid-4.0.16/src/security/LockingPointer.h squid-4.0.17/src/security/LockingPointer.h
--- squid-4.0.16/src/security/LockingPointer.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/security/LockingPointer.h	2016-12-16 23:06:20.000000000 +1300
@@ -9,6 +9,8 @@
 #ifndef SQUID_SRC_SECURITY_LOCKINGPOINTER_H
 #define SQUID_SRC_SECURITY_LOCKINGPOINTER_H
 
+#include "base/HardFun.h"
+
 #if USE_OPENSSL
 #if HAVE_OPENSSL_CRYPTO_H
 #include <openssl/crypto.h>
@@ -34,6 +36,9 @@
 namespace Security
 {
 
+inline bool nilFunction(const void *) { return false; }
+typedef HardFun<bool, const void *, nilFunction> NilFunctor;
+
 /**
  * A shared pointer to a reference-counting Object with library-specific
  * absorption, locking, and unlocking implementations. The API largely
@@ -44,12 +49,12 @@
  * pre-lock objects before they are fed to LockingPointer, necessitating
  * this resetWithoutLocking() customization hook.
  */
-template <typename T, void (*UnLocker)(T *t), int lockId>
+template <typename T, void (*UnLocker)(T *t), class Locker = NilFunctor>
 class LockingPointer
 {
 public:
     /// a helper label to simplify this objects API definitions below
-    typedef Security::LockingPointer<T, UnLocker, lockId> SelfType;
+    typedef Security::LockingPointer<T, UnLocker, Locker> SelfType;
 
     /**
      * Construct directly from a raw pointer.
@@ -119,14 +124,10 @@
 private:
     /// The lock() method increments Object's reference counter.
     void lock(T *t) {
-#if USE_OPENSSL
-        if (t)
-            CRYPTO_add(&t->references, 1, lockId);
-#elif USE_GNUTLS
-        // XXX: GnuTLS does not provide locking ?
-#else
-        assert(false);
-#endif
+        if (t) {
+            Locker doLock;
+            doLock(t);
+        }
     }
 
     /// Become a nil pointer. Decrements any pointed-to Object's reference counter
diff -u -r -N squid-4.0.16/src/security/PeerConnector.cc squid-4.0.17/src/security/PeerConnector.cc
--- squid-4.0.16/src/security/PeerConnector.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/security/PeerConnector.cc	2016-12-16 23:06:20.000000000 +1300
@@ -104,9 +104,11 @@
     assert(ctx);
 
     if (!Ssl::CreateClient(ctx, serverConnection(), "server https start")) {
+        const auto xerrno = errno;
+        const auto ssl_error = ERR_get_error();
         ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw());
-        anErr->xerrno = errno;
-        debugs(83, DBG_IMPORTANT, "Error allocating TLS handle: " << ERR_error_string(ERR_get_error(), NULL));
+        anErr->xerrno = xerrno;
+        debugs(83, DBG_IMPORTANT, "Error allocating TLS handle: " << Security::ErrorString(ssl_error));
         noteNegotiationDone(anErr);
         bail(anErr);
         return false;
@@ -443,7 +445,7 @@
 
     const int fd = serverConnection()->fd;
     debugs(83, DBG_IMPORTANT, "Error negotiating SSL on FD " << fd <<
-           ": " << ERR_error_string(ssl_lib_error, NULL) << " (" <<
+           ": " << Security::ErrorString(ssl_lib_error) << " (" <<
            ssl_error << "/" << ret << "/" << errno << ")");
 
     ErrorState *anErr = NULL;
diff -u -r -N squid-4.0.16/src/security/PeerOptions.cc squid-4.0.17/src/security/PeerOptions.cc
--- squid-4.0.16/src/security/PeerOptions.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/security/PeerOptions.cc	2016-12-16 23:06:20.000000000 +1300
@@ -59,7 +59,7 @@
         certs.emplace_back(t);
     } else if (strncmp(token, "key=", 4) == 0) {
         if (certs.empty() || certs.back().certFile.isEmpty()) {
-            debugs(3, DBG_PARSE_NOTE(1), "ERROR: cert= option must be set before key= is used.");
+            fatal("cert= option must be set before key= is used.");
             return;
         }
         KeyData &t = certs.back();
@@ -228,8 +228,8 @@
     SSL_CTX *t = SSL_CTX_new(SSLv23_client_method());
 #endif
     if (!t) {
-        const auto x = ERR_error_string(ERR_get_error(), nullptr);
-        fatalf("Failed to allocate TLS client context: %s\n", x);
+        const auto x = ERR_get_error();
+        fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
     }
     ctx.resetWithoutLocking(t);
 
@@ -237,7 +237,7 @@
     // Initialize for X.509 certificate exchange
     gnutls_certificate_credentials_t t;
     if (const int x = gnutls_certificate_allocate_credentials(&t)) {
-        fatalf("Failed to allocate TLS client context: error=%d\n", x);
+        fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
     }
     ctx.resetWithoutLocking(t);
 
@@ -574,12 +574,12 @@
 {
 #if USE_OPENSSL
     if (SSL_CTX_set_default_verify_paths(ctx.get()) == 0)
-        return ERR_error_string(ERR_get_error(), nullptr);
+        return Security::ErrorString(ERR_get_error());
 
 #elif USE_GNUTLS
     auto x = gnutls_certificate_set_x509_system_trust(ctx.get());
     if (x < 0)
-        return gnutls_strerror(x);
+        return Security::ErrorString(x);
 
 #endif
     return nullptr;
@@ -595,12 +595,15 @@
     for (auto i : caFiles) {
 #if USE_OPENSSL
         if (!SSL_CTX_load_verify_locations(ctx.get(), i.c_str(), path)) {
-            const int ssl_error = ERR_get_error();
-            debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate locations: " << ERR_error_string(ssl_error, NULL));
+            const auto x = ERR_get_error();
+            debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " <<
+                   i << ": " << Security::ErrorString(x));
         }
 #elif USE_GNUTLS
-        if (gnutls_certificate_set_x509_trust_file(ctx.get(), i.c_str(), GNUTLS_X509_FMT_PEM) < 0) {
-            debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location: " << i);
+        const auto x = gnutls_certificate_set_x509_trust_file(ctx.get(), i.c_str(), GNUTLS_X509_FMT_PEM);
+        if (x < 0) {
+            debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " <<
+                   i << ": " << Security::ErrorString(x));
         }
 #endif
     }
diff -u -r -N squid-4.0.16/src/security/ServerOptions.cc squid-4.0.17/src/security/ServerOptions.cc
--- squid-4.0.16/src/security/ServerOptions.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/security/ServerOptions.cc	2016-12-16 23:06:20.000000000 +1300
@@ -98,8 +98,8 @@
     SSL_CTX *t = SSL_CTX_new(SSLv23_server_method());
 #endif
     if (!t) {
-        const auto x = ERR_error_string(ERR_get_error(), nullptr);
-        debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: " << x);
+        const auto x = ERR_get_error();
+        debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: " << Security::ErrorString(x));
     }
     ctx.resetWithoutLocking(t);
 
@@ -107,7 +107,7 @@
     // Initialize for X.509 certificate exchange
     gnutls_certificate_credentials_t t;
     if (const int x = gnutls_certificate_allocate_credentials(&t)) {
-        debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: error=" << x);
+        debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: " << Security::ErrorString(x));
     }
     ctx.resetWithoutLocking(t);
 
@@ -183,14 +183,14 @@
 
         auto ecdh = EC_KEY_new_by_curve_name(nid);
         if (!ecdh) {
-            auto ssl_error = ERR_get_error();
-            debugs(83, DBG_CRITICAL, "ERROR: Unable to configure Ephemeral ECDH: " << ERR_error_string(ssl_error, NULL));
+            const auto x = ERR_get_error();
+            debugs(83, DBG_CRITICAL, "ERROR: Unable to configure Ephemeral ECDH: " << Security::ErrorString(x));
             return;
         }
 
         if (!SSL_CTX_set_tmp_ecdh(ctx.get(), ecdh)) {
-            auto ssl_error = ERR_get_error();
-            debugs(83, DBG_CRITICAL, "ERROR: Unable to set Ephemeral ECDH: " << ERR_error_string(ssl_error, NULL));
+            const auto x = ERR_get_error();
+            debugs(83, DBG_CRITICAL, "ERROR: Unable to set Ephemeral ECDH: " << Security::ErrorString(x));
         }
         EC_KEY_free(ecdh);
 
@@ -202,7 +202,7 @@
 
     // set DH parameters into the server context
 #if USE_OPENSSL
-    if (parsedDhParams.get()) {
+    if (parsedDhParams) {
         SSL_CTX_set_tmp_dh(ctx.get(), parsedDhParams.get());
     }
 #endif
diff -u -r -N squid-4.0.16/src/security/Session.cc squid-4.0.17/src/security/Session.cc
--- squid-4.0.16/src/security/Session.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/security/Session.cc	2016-12-16 23:06:20.000000000 +1300
@@ -43,7 +43,7 @@
         gnutls_datum_t *tmp = nullptr;
         const auto x = gnutls_session_get_data2(s.get(), tmp);
         if (x != GNUTLS_E_SUCCESS) {
-            debugs(83, 3, "session=" << (void*)s.get() << " error: " << gnutls_strerror(x));
+            debugs(83, 3, "session=" << (void*)s.get() << " error: " << Security::ErrorString(x));
         }
         data.reset(tmp);
 #endif
@@ -61,13 +61,13 @@
         if (!SSL_set_session(s.get(), data.get())) {
             const auto ssl_error = ERR_get_error();
             debugs(83, 3, "session=" << (void*)s.get() << " data=" << (void*)data.get() <<
-                   " resume error: " << ERR_error_string(ssl_error, nullptr));
+                   " resume error: " << Security::ErrorString(ssl_error));
         }
 #elif USE_GNUTLS
         const auto x = gnutls_session_set_data(s.get(), data->data, data->size);
         if (x != GNUTLS_E_SUCCESS) {
             debugs(83, 3, "session=" << (void*)s.get() << " data=" << (void*)data.get() <<
-                   " resume error: " << gnutls_strerror(x));
+                   " resume error: " << Security::ErrorString(x));
         }
 #else
         // critical because, how did it get here?
diff -u -r -N squid-4.0.16/src/security/Session.h squid-4.0.17/src/security/Session.h
--- squid-4.0.16/src/security/Session.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/security/Session.h	2016-12-16 23:06:20.000000000 +1300
@@ -9,7 +9,6 @@
 #ifndef SQUID_SRC_SECURITY_SESSION_H
 #define SQUID_SRC_SECURITY_SESSION_H
 
-#include "base/HardFun.h"
 #include "security/LockingPointer.h"
 
 #include <memory>
@@ -30,7 +29,10 @@
 
 #if USE_OPENSSL
 CtoCpp1(SSL_free, SSL *);
-typedef LockingPointer<SSL, Security::SSL_free_cpp, CRYPTO_LOCK_SSL> SessionPointer;
+#if defined(CRYPTO_LOCK_SSL) // OpenSSL 1.0
+inline int SSL_up_ref(SSL *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_SSL); return 0;}
+#endif
+typedef Security::LockingPointer<SSL, Security::SSL_free_cpp, HardFun<int, SSL *, SSL_up_ref> > SessionPointer;
 
 typedef std::unique_ptr<SSL_SESSION, HardFun<void, SSL_SESSION*, &SSL_SESSION_free>> SessionStatePointer;
 
@@ -39,7 +41,7 @@
 // objects using the gnutls_session_set_ptr()/gnutls_session_get_ptr ()
 // library functions
 CtoCpp1(gnutls_deinit, gnutls_session_t);
-typedef LockingPointer<struct gnutls_session_int, gnutls_deinit_cpp, -1> SessionPointer;
+typedef Security::LockingPointer<struct gnutls_session_int, gnutls_deinit_cpp> SessionPointer;
 
 // wrapper function to get around gnutls_free being a typedef
 inline void squid_gnutls_free(void *d) {gnutls_free(d);}
@@ -48,7 +50,7 @@
 #else
 // use void* so we can check against NULL
 CtoCpp1(xfree, void *);
-typedef LockingPointer<void, xfree_cpp, -1> SessionPointer;
+typedef Security::LockingPointer<void, xfree_cpp> SessionPointer;
 
 typedef std::unique_ptr<int> SessionStatePointer;
 
diff -u -r -N squid-4.0.16/src/servers/FtpServer.cc squid-4.0.17/src/servers/FtpServer.cc
--- squid-4.0.16/src/servers/FtpServer.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/servers/FtpServer.cc	2016-12-16 23:06:20.000000000 +1300
@@ -152,7 +152,7 @@
 }
 
 void
-Ftp::Server::processParsedRequest(Http::Stream *)
+Ftp::Server::processParsedRequest(Http::StreamPointer &)
 {
     Must(pipeline.count() == 1);
 
@@ -1153,12 +1153,13 @@
     writeErrorReply(reply, 451);
 }
 
-void
+bool
 Ftp::Server::writeControlMsgAndCall(HttpReply *reply, AsyncCall::Pointer &call)
 {
     // the caller guarantees that we are dealing with the current context only
     // the caller should also make sure reply->header.has(Http::HdrType::FTP_STATUS)
     writeForwardedReplyAndCall(reply, call);
+    return true;
 }
 
 void
diff -u -r -N squid-4.0.16/src/servers/FtpServer.h squid-4.0.17/src/servers/FtpServer.h
--- squid-4.0.16/src/servers/FtpServer.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/servers/FtpServer.h	2016-12-16 23:06:20.000000000 +1300
@@ -92,12 +92,12 @@
 
     /* ConnStateData API */
     virtual Http::Stream *parseOneRequest() override;
-    virtual void processParsedRequest(Http::Stream *context) override;
+    virtual void processParsedRequest(Http::StreamPointer &context) override;
     virtual void notePeerConnection(Comm::ConnectionPointer conn) override;
     virtual void clientPinnedConnectionClosed(const CommCloseCbParams &io) override;
     virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData) override;
     virtual int pipelinePrefetchMax() const override;
-    virtual void writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) override;
+    virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) override;
     virtual time_t idleTimeout() const override;
 
     /* BodyPipe API */
diff -u -r -N squid-4.0.16/src/servers/Http1Server.cc squid-4.0.17/src/servers/Http1Server.cc
--- squid-4.0.16/src/servers/Http1Server.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/servers/Http1Server.cc	2016-12-16 23:06:20.000000000 +1300
@@ -80,7 +80,7 @@
     // a) dont have one already
     // b) have completed the previous request parsing already
     if (!parser_ || !parser_->needsMoreData())
-        parser_ = new Http1::RequestParser();
+        parser_ = new Http1::RequestParser(mayTunnelUnsupportedProto());
 
     /* Process request */
     Http::Stream *context = parseHttpRequest(this, parser_);
@@ -90,10 +90,10 @@
 }
 
 void clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request);
-bool clientTunnelOnError(ConnStateData *conn, Http::Stream *context, HttpRequest *request, const HttpRequestMethod& method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes);
+bool clientTunnelOnError(ConnStateData *conn, Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod& method, err_type requestError);
 
 bool
-Http::One::Server::buildHttpRequest(Http::Stream *context)
+Http::One::Server::buildHttpRequest(Http::StreamPointer &context)
 {
     HttpRequest::Pointer request;
     ClientHttpRequest *http = context->http;
@@ -123,7 +123,8 @@
         // setLogUri should called before repContext->setReplyToError
         setLogUri(http, http->uri, true);
         const char * requestErrorBytes = inBuf.c_str();
-        if (!clientTunnelOnError(this, context, request.getRaw(), parser_->method(), errPage, parser_->parseStatusCode, requestErrorBytes)) {
+        if (!clientTunnelOnError(this, context, request, parser_->method(), errPage)) {
+            setReplyError(context, request, parser_->method(), errPage, parser_->parseStatusCode, requestErrorBytes);
             // HttpRequest object not build yet, there is no reason to call
             // clientProcessRequestFinished method
         }
@@ -137,7 +138,8 @@
         setLogUri(http, http->uri, true);
 
         const char * requestErrorBytes = inBuf.c_str();
-        if (!clientTunnelOnError(this, context, request.getRaw(), parser_->method(), ERR_INVALID_URL, Http::scBadRequest, requestErrorBytes)) {
+        if (!clientTunnelOnError(this, context, request, parser_->method(), ERR_INVALID_URL)) {
+            setReplyError(context, request, parser_->method(), ERR_INVALID_URL, Http::scBadRequest, requestErrorBytes);
             // HttpRequest object not build yet, there is no reason to call
             // clientProcessRequestFinished method
         }
@@ -155,7 +157,8 @@
         setLogUri(http, http->uri,  true);
 
         const char * requestErrorBytes = NULL; //HttpParserHdrBuf(parser_);
-        if (!clientTunnelOnError(this, context, request.getRaw(), parser_->method(), ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, requestErrorBytes)) {
+        if (!clientTunnelOnError(this, context, request, parser_->method(), ERR_UNSUP_HTTPVERSION)) {
+            setReplyError(context, request, parser_->method(), ERR_UNSUP_HTTPVERSION, Http::scHttpVersionNotSupported, requestErrorBytes);
             clientProcessRequestFinished(this, request);
         }
         return false;
@@ -167,7 +170,8 @@
         // setLogUri should called before repContext->setReplyToError
         setLogUri(http, http->uri, true);
         const char * requestErrorBytes = NULL; //HttpParserHdrBuf(parser_);
-        if (!clientTunnelOnError(this, context, request.getRaw(), parser_->method(), ERR_INVALID_REQ, Http::scBadRequest, requestErrorBytes)) {
+        if (!clientTunnelOnError(this, context, request, parser_->method(), ERR_INVALID_REQ)) {
+            setReplyError(context, request, parser_->method(), ERR_INVALID_REQ, Http::scBadRequest, requestErrorBytes);
             clientProcessRequestFinished(this, request);
         }
         return false;
@@ -190,6 +194,25 @@
 }
 
 void
+Http::One::Server::setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod& method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
+{
+    quitAfterError(request.getRaw());
+    if (!context->connRegistered()) {
+        debugs(33, 2, "Client stream deregister it self, nothing to do");
+        clientConnection->close();
+        return;
+    }
+    clientStreamNode *node = context->getClientReplyContext();
+    clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+    assert (repContext);
+
+    repContext->setReplyToError(requestError, errStatusCode, method, context->http->uri, clientConnection->remote, nullptr, requestErrorBytes, nullptr);
+
+    assert(context->http->out.offset == 0);
+    context->pullData();
+}
+
+void
 Http::One::Server::proceedAfterBodyContinuation(Http::StreamPointer context)
 {
     debugs(33, 5, "Body Continuation written");
@@ -197,7 +220,7 @@
 }
 
 void
-Http::One::Server::processParsedRequest(Http::Stream *context)
+Http::One::Server::processParsedRequest(Http::StreamPointer &context)
 {
     if (!buildHttpRequest(context))
         return;
@@ -239,7 +262,7 @@
             }
         }
     }
-    clientProcessRequest(this, parser_, context);
+    clientProcessRequest(this, parser_, context.getRaw());
 }
 
 void
@@ -283,10 +306,20 @@
     context->sendStartOfMessage(rep, receivedData);
 }
 
-void
+bool
 Http::One::Server::writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)
 {
-    const ClientHttpRequest *http = pipeline.front()->http;
+    Http::StreamPointer context = pipeline.front();
+    Must(context != nullptr);
+
+    // Ignore this late control message if we have started sending a
+    // reply to the user already (e.g., after an error).
+    if (context->reply) {
+        debugs(11, 2, "drop 1xx made late by " << context->reply);
+        return false;
+    }
+
+    const ClientHttpRequest *http = context->http;
 
     // apply selected clientReplyContext::buildReplyHeader() mods
     // it is not clear what headers are required for control messages
@@ -302,6 +335,7 @@
     Comm::Write(clientConnection, mb, call);
 
     delete mb;
+    return true;
 }
 
 ConnStateData *
diff -u -r -N squid-4.0.16/src/servers/Http1Server.h squid-4.0.17/src/servers/Http1Server.h
--- squid-4.0.16/src/servers/Http1Server.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/servers/Http1Server.h	2016-12-16 23:06:20.000000000 +1300
@@ -30,9 +30,9 @@
 protected:
     /* ConnStateData API */
     virtual Http::Stream *parseOneRequest();
-    virtual void processParsedRequest(Http::Stream *context);
+    virtual void processParsedRequest(Http::StreamPointer &context);
     virtual void handleReply(HttpReply *rep, StoreIOBuffer receivedData);
-    virtual void writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call);
+    virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call);
     virtual time_t idleTimeout() const;
 
     /* BodyPipe API */
@@ -52,7 +52,9 @@
     /// to the client if parsing is failed, or parses the url and build the
     /// HttpRequest object using parsing results.
     /// Return false if parsing is failed, true otherwise.
-    bool buildHttpRequest(Http::Stream *context);
+    bool buildHttpRequest(Http::StreamPointer &context);
+
+    void setReplyError(Http::StreamPointer &context, HttpRequest::Pointer &request, const HttpRequestMethod& method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes);
 
     Http1::RequestParserPointer parser_;
     HttpRequestMethod method_; ///< parsed HTTP method
diff -u -r -N squid-4.0.16/src/SquidConfig.h squid-4.0.17/src/SquidConfig.h
--- squid-4.0.16/src/SquidConfig.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/SquidConfig.h	2016-12-16 23:06:20.000000000 +1300
@@ -492,8 +492,6 @@
     } SSL;
 #endif
 
-    wordlist *ext_methods;
-
     struct {
         int high_rptm;
         int high_pf;
diff -u -r -N squid-4.0.16/src/ssl/bio.cc squid-4.0.17/src/ssl/bio.cc
--- squid-4.0.16/src/ssl/bio.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/bio.cc	2016-12-16 23:06:20.000000000 +1300
@@ -42,6 +42,7 @@
 /* SSL callbacks */
 static void squid_ssl_info(const SSL *ssl, int where, int ret);
 
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
 /// Initialization structure for the BIO table with
 /// Squid-specific methods and BIO method wrappers.
 static BIO_METHOD SquidMethods = {
@@ -56,14 +57,30 @@
     squid_bio_destroy,
     NULL // squid_callback_ctrl not supported
 };
+#else
+static BIO_METHOD *SquidMethods = NULL;
+#endif
 
 BIO *
 Ssl::Bio::Create(const int fd, Ssl::Bio::Type type)
 {
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
     if (BIO *bio = BIO_new(&SquidMethods)) {
         BIO_int_ctrl(bio, BIO_C_SET_FD, type, fd);
         return bio;
     }
+#else
+    if (!SquidMethods) {
+        SquidMethods = BIO_meth_new(BIO_TYPE_SOCKET, "squid");
+        BIO_meth_set_write(SquidMethods, squid_bio_write);
+        BIO_meth_set_read(SquidMethods, squid_bio_read);
+        BIO_meth_set_puts(SquidMethods, squid_bio_puts);
+        BIO_meth_set_gets(SquidMethods, NULL);
+        BIO_meth_set_ctrl(SquidMethods, squid_bio_ctrl);
+        BIO_meth_set_create(SquidMethods, squid_bio_create);
+        BIO_meth_set_destroy(SquidMethods, squid_bio_destroy);
+    }
+#endif
     return NULL;
 }
 
@@ -147,18 +164,6 @@
            SSL_state_string(ssl) << " (" << SSL_state_string_long(ssl) << ")");
 }
 
-bool
-Ssl::ClientBio::isClientHello(int state)
-{
-    return (
-               state == SSL3_ST_SR_CLNT_HELLO_A ||
-               state == SSL23_ST_SR_CLNT_HELLO_A ||
-               state == SSL23_ST_SR_CLNT_HELLO_B ||
-               state == SSL3_ST_SR_CLNT_HELLO_B ||
-               state == SSL3_ST_SR_CLNT_HELLO_C
-           );
-}
-
 void
 Ssl::ClientBio::stateChanged(const SSL *ssl, int where, int ret)
 {
@@ -509,10 +514,15 @@
 static int
 squid_bio_create(BIO *bi)
 {
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
     bi->init = 0; // set when we store Bio object and socket fd (BIO_C_SET_FD)
     bi->num = 0;
-    bi->ptr = NULL;
     bi->flags = 0;
+#else
+    // No need to set more, openSSL initialize BIO memory to zero.
+#endif
+
+    BIO_set_data(bi, NULL);
     return 1;
 }
 
@@ -520,8 +530,8 @@
 static int
 squid_bio_destroy(BIO *table)
 {
-    delete static_cast<Ssl::Bio*>(table->ptr);
-    table->ptr = NULL;
+    delete static_cast<Ssl::Bio*>(BIO_get_data(table));
+    BIO_set_data(table, NULL);
     return 1;
 }
 
@@ -529,7 +539,7 @@
 static int
 squid_bio_write(BIO *table, const char *buf, int size)
 {
-    Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr);
+    Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table));
     assert(bio);
     return bio->write(buf, size, table);
 }
@@ -538,7 +548,7 @@
 static int
 squid_bio_read(BIO *table, char *buf, int size)
 {
-    Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr);
+    Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table));
     assert(bio);
     return bio->read(buf, size, table);
 }
@@ -566,15 +576,15 @@
             bio = new Ssl::ServerBio(fd);
         else
             bio = new Ssl::ClientBio(fd);
-        assert(!table->ptr);
-        table->ptr = bio;
-        table->init = 1;
+        assert(!BIO_get_data(table));
+        BIO_set_data(table, bio);
+        BIO_set_init(table, 1);
         return 0;
     }
 
     case BIO_C_GET_FD:
-        if (table->init) {
-            Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr);
+        if (BIO_get_init(table)) {
+            Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table));
             assert(bio);
             if (arg2)
                 *static_cast<int*>(arg2) = bio->fd();
@@ -588,8 +598,8 @@
         return 0;
 
     case BIO_CTRL_FLUSH:
-        if (table->init) {
-            Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr);
+        if (BIO_get_init(table)) {
+            Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table));
             assert(bio);
             bio->flush(table);
             return 1;
@@ -619,7 +629,7 @@
 squid_ssl_info(const SSL *ssl, int where, int ret)
 {
     if (BIO *table = SSL_get_rbio(ssl)) {
-        if (Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr))
+        if (Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table)))
             bio->stateChanged(ssl, where, ret);
     }
 }
@@ -648,16 +658,16 @@
             cbytes[0] = (cipherId >> 8) & 0xFF;
             cbytes[1] = cipherId & 0xFF;
             cbytes[2] = 0;
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-            const SSL_METHOD *method = TLS_method();
-#else
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
             const SSL_METHOD *method = SSLv23_method();
-#endif
             const SSL_CIPHER *c = method->get_cipher_by_char(cbytes);
+#else
+            const SSL_CIPHER *c = SSL_CIPHER_find(ssl, cbytes);
+#endif
             if (c != NULL) {
                 if (!strCiphers.isEmpty())
                     strCiphers.append(":");
-                strCiphers.append(c->name);
+                strCiphers.append(SSL_CIPHER_get_name(c));
             }
         }
         if (!strCiphers.isEmpty())
diff -u -r -N squid-4.0.16/src/ssl/bio.h squid-4.0.17/src/ssl/bio.h
--- squid-4.0.16/src/ssl/bio.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/bio.h	2016-12-16 23:06:20.000000000 +1300
@@ -89,8 +89,6 @@
     /// by the caller.
     void setReadBufData(SBuf &data) {rbuf = data;}
 private:
-    /// True if the SSL state corresponds to a hello message
-    bool isClientHello(int state);
     bool holdRead_; ///< The read hold state of the bio.
     bool holdWrite_;  ///< The write hold state of the bio.
     int helloSize; ///< The SSL hello message sent by client size
@@ -196,5 +194,13 @@
 void
 applyTlsDetailsToSSL(SSL *ssl, Security::TlsDetails::Pointer const &details, Ssl::BumpMode bumpMode);
 
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
+// OpenSSL v1.0 bio compatibility functions
+inline void *BIO_get_data(BIO *table) { return table->ptr; }
+inline void BIO_set_data(BIO *table, void *data) { table->ptr = data; }
+inline int BIO_get_init(BIO *table) { return table->init; }
+inline void BIO_set_init(BIO *table, int init) { table->init = init; }
+#endif
+
 #endif /* SQUID_SSL_BIO_H */
 
diff -u -r -N squid-4.0.16/src/ssl/ErrorDetail.cc squid-4.0.17/src/ssl/ErrorDetail.cc
--- squid-4.0.16/src/ssl/ErrorDetail.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/ErrorDetail.cc	2016-12-16 23:06:20.000000000 +1300
@@ -553,7 +553,7 @@
     if (errReason.size() > 0)
         return errReason.termedBuf();
     else if (lib_error_no != SSL_ERROR_NONE)
-        return ERR_error_string(lib_error_no, NULL);
+        return Security::ErrorString(lib_error_no);
     else
         return "[No Error]";
 }
@@ -564,7 +564,7 @@
  * Error meta information:
  * %err_name: The name of a high-level SSL error (e.g., X509_V_ERR_*)
  * %ssl_error_descr: A short description of the SSL error
- * %ssl_lib_error: human-readable low-level error string by ERR_error_string(3SSL)
+ * %ssl_lib_error: human-readable low-level error string by Security::ErrorString()
  *
  * Certificate information extracted from broken (not necessarily peer!) cert
  * %ssl_cn: The comma-separated list of common and alternate names
diff -u -r -N squid-4.0.16/src/ssl/gadgets.cc squid-4.0.17/src/ssl/gadgets.cc
--- squid-4.0.16/src/ssl/gadgets.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/gadgets.cc	2016-12-16 23:06:20.000000000 +1300
@@ -109,7 +109,7 @@
     if (!pkey || !cert)
         return false;
 
-    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
     if (!bio)
         return false;
     if (!BIO_write_filename(bio.get(), const_cast<char *>(filename)))
@@ -650,7 +650,7 @@
 {
     if (!certFilename)
         return NULL;
-    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
     if (!bio)
         return NULL;
     if (!BIO_read_filename(bio.get(), certFilename))
@@ -663,7 +663,7 @@
 {
     if (!keyFilename)
         return NULL;
-    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
     if (!bio)
         return NULL;
     if (!BIO_read_filename(bio.get(), keyFilename))
diff -u -r -N squid-4.0.16/src/ssl/gadgets.h squid-4.0.17/src/ssl/gadgets.h
--- squid-4.0.16/src/ssl/gadgets.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/gadgets.h	2016-12-16 23:06:20.000000000 +1300
@@ -46,7 +46,10 @@
 typedef std::unique_ptr<STACK_OF(X509), sk_X509_free_wrapper> X509_STACK_Pointer;
 
 CtoCpp1(EVP_PKEY_free, EVP_PKEY *)
-typedef Security::LockingPointer<EVP_PKEY, EVP_PKEY_free_cpp, CRYPTO_LOCK_EVP_PKEY> EVP_PKEY_Pointer;
+#if defined(CRYPTO_LOCK_EVP_PKEY) // OpenSSL 1.0
+inline int EVP_PKEY_up_ref(EVP_PKEY *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_EVP_PKEY); return 0;}
+#endif
+typedef Security::LockingPointer<EVP_PKEY, EVP_PKEY_free_cpp, HardFun<int, EVP_PKEY *, EVP_PKEY_up_ref> > EVP_PKEY_Pointer;
 
 typedef std::unique_ptr<BIGNUM, HardFun<void, BIGNUM*, &BN_free>> BIGNUM_Pointer;
 
diff -u -r -N squid-4.0.16/src/ssl/helper.cc squid-4.0.17/src/ssl/helper.cc
--- squid-4.0.16/src/ssl/helper.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/helper.cc	2016-12-16 23:06:20.000000000 +1300
@@ -181,7 +181,7 @@
 public:
     std::string query;
     AsyncCall::Pointer callback;
-    SSL *ssl;
+    Security::SessionPointer ssl;
 };
 CBDATA_CLASS_INIT(submitData);
 
@@ -193,7 +193,7 @@
     std::string error;
 
     submitData *crtdvdData = static_cast<submitData *>(data);
-    STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(crtdvdData->ssl);
+    STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(crtdvdData->ssl.get());
     if (reply.result == ::Helper::BrokenHelper) {
         debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content());
         validationResponse->resultCode = ::Helper::BrokenHelper;
@@ -220,7 +220,6 @@
             delete item;
     }
 
-    SSL_free(crtdvdData->ssl);
     delete crtdvdData;
 }
 
@@ -237,8 +236,7 @@
     crtdvdData->query = message.compose();
     crtdvdData->query += '\n';
     crtdvdData->callback = callback;
-    crtdvdData->ssl = request.ssl;
-    CRYPTO_add(&crtdvdData->ssl->references,1,CRYPTO_LOCK_SSL);
+    crtdvdData->ssl.resetAndLock(request.ssl);
     Ssl::CertValidationResponse::Pointer const*validationResponse;
 
     if (CertValidationHelper::HelperCache &&
@@ -248,7 +246,6 @@
         Must(dialer);
         dialer->arg1 = *validationResponse;
         ScheduleCallHere(callback);
-        SSL_free(crtdvdData->ssl);
         delete crtdvdData;
         return;
     }
@@ -260,8 +257,6 @@
         Must(dialer);
         dialer->arg1 = resp;
         ScheduleCallHere(callback);
-
-        SSL_free(crtdvdData->ssl);
         delete crtdvdData;
         return;
     }
diff -u -r -N squid-4.0.16/src/ssl/PeekingPeerConnector.cc squid-4.0.17/src/ssl/PeekingPeerConnector.cc
--- squid-4.0.16/src/ssl/PeekingPeerConnector.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/PeekingPeerConnector.cc	2016-12-16 23:06:20.000000000 +1300
@@ -65,7 +65,7 @@
     acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpServerFirst));
     Security::SessionPointer session(fd_table[serverConn->fd].ssl);
     BIO *b = SSL_get_rbio(session.get());
-    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
     if (!srvBio->canSplice())
         acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpSplice));
     if (!srvBio->canBump())
@@ -78,7 +78,7 @@
 {
     Security::SessionPointer session(fd_table[serverConn->fd].ssl);
     BIO *b = SSL_get_rbio(session.get());
-    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
     debugs(83,5, "Will check for peek and splice on FD " << serverConn->fd);
 
     Ssl::BumpMode finalAction = action;
@@ -169,14 +169,14 @@
             auto clientSession = fd_table[clientConn->fd].ssl.get();
             Must(clientSession);
             BIO *bc = SSL_get_rbio(clientSession);
-            Ssl::ClientBio *cltBio = static_cast<Ssl::ClientBio *>(bc->ptr);
+            Ssl::ClientBio *cltBio = static_cast<Ssl::ClientBio *>(BIO_get_data(bc));
             Must(cltBio);
             if (details && details->tlsVersion.protocol != AnyP::PROTO_NONE) {
                 applyTlsDetailsToSSL(serverSession.get(), details, csd->sslBumpMode);
                 // Should we allow it for all protocols?
                 if (details->tlsVersion.protocol == AnyP::PROTO_TLS || details->tlsVersion == AnyP::ProtocolVersion(AnyP::PROTO_SSL, 3, 0)) {
                     BIO *b = SSL_get_rbio(serverSession.get());
-                    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+                    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
                     // Inherite client features, like SSL version, SNI and other
                     srvBio->setClientFeatures(details, cltBio->rBufData());
                     srvBio->recordInput(true);
@@ -201,10 +201,10 @@
         }
 
         if (Ssl::ServerBump *serverBump = csd->serverBump()) {
-            serverBump->attachServerSSL(serverSession.get());
+            serverBump->attachServerSession(serverSession);
             // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
             if (X509 *peeked_cert = serverBump->serverCert.get()) {
-                CRYPTO_add(&(peeked_cert->references),1,CRYPTO_LOCK_X509);
+                X509_up_ref(peeked_cert);
                 SSL_set_ex_data(serverSession.get(), ssl_ex_index_ssl_peeked_cert, peeked_cert);
             }
         }
@@ -262,7 +262,7 @@
     const int fd = serverConnection()->fd;
     Security::SessionPointer session(fd_table[fd].ssl);
     BIO *b = SSL_get_rbio(session.get());
-    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
 
     if ((srvBio->bumpMode() == Ssl::bumpPeek || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
         debugs(81, 3, "hold write on SSL connection on FD " << fd);
@@ -279,7 +279,7 @@
     const int fd = serverConnection()->fd;
     Security::SessionPointer session(fd_table[fd].ssl);
     BIO *b = SSL_get_rbio(session.get());
-    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
 
     // In Peek mode, the ClientHello message sent to the server. If the
     // server resuming a previous (spliced) SSL session with the client,
@@ -308,7 +308,7 @@
             (srvBio->bumpMode() == Ssl::bumpPeek  || srvBio->bumpMode() == Ssl::bumpStare) && srvBio->holdWrite()) {
         Security::CertPointer serverCert(SSL_get_peer_certificate(session.get()));
         if (serverCert) {
-            debugs(81, 3, "Error ("  << ERR_error_string(ssl_lib_error, NULL) <<  ") but, hold write on SSL connection on FD " << fd);
+            debugs(81, 3, "Error ("  << Security::ErrorString(ssl_lib_error) <<  ") but, hold write on SSL connection on FD " << fd);
             checkForPeekAndSplice();
             return;
         }
diff -u -r -N squid-4.0.16/src/ssl/ServerBump.cc squid-4.0.17/src/ssl/ServerBump.cc
--- squid-4.0.16/src/ssl/ServerBump.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/ServerBump.cc	2016-12-16 23:06:20.000000000 +1300
@@ -53,21 +53,21 @@
 }
 
 void
-Ssl::ServerBump::attachServerSSL(SSL *ssl)
+Ssl::ServerBump::attachServerSession(const Security::SessionPointer &s)
 {
-    if (serverSSL.get())
+    if (serverSession)
         return;
 
-    serverSSL.resetAndLock(ssl);
+    serverSession = s;
 }
 
 const Security::CertErrors *
 Ssl::ServerBump::sslErrors() const
 {
-    if (!serverSSL.get())
+    if (!serverSession)
         return NULL;
 
-    const Security::CertErrors *errs = static_cast<const Security::CertErrors*>(SSL_get_ex_data(serverSSL.get(), ssl_ex_index_ssl_errors));
+    const Security::CertErrors *errs = static_cast<const Security::CertErrors*>(SSL_get_ex_data(serverSession.get(), ssl_ex_index_ssl_errors));
     return errs;
 }
 
diff -u -r -N squid-4.0.16/src/ssl/ServerBump.h squid-4.0.17/src/ssl/ServerBump.h
--- squid-4.0.16/src/ssl/ServerBump.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/ServerBump.h	2016-12-16 23:06:20.000000000 +1300
@@ -32,14 +32,14 @@
 public:
     explicit ServerBump(HttpRequest *fakeRequest, StoreEntry *e = NULL, Ssl::BumpMode mode = Ssl::bumpServerFirst);
     ~ServerBump();
-    void attachServerSSL(SSL *); ///< Sets the server SSL object
+    void attachServerSession(const Security::SessionPointer &); ///< Sets the server TLS session object
     const Security::CertErrors *sslErrors() const; ///< SSL [certificate validation] errors
 
     /// faked, minimal request; required by Client API
     HttpRequest::Pointer request;
     StoreEntry *entry; ///< for receiving Squid-generated error messages
     /// HTTPS server certificate. Maybe it is different than the one
-    /// it is stored in serverSSL object (error SQUID_X509_V_ERR_CERT_CHANGE)
+    /// it is stored in serverSession object (error SQUID_X509_V_ERR_CERT_CHANGE)
     Security::CertPointer serverCert;
     struct {
         Ssl::BumpMode step1; ///< The SSL bump mode at step1
@@ -48,9 +48,9 @@
     } act; ///< bumping actions at various bumping steps
     Ssl::BumpStep step; ///< The SSL bumping step
     SBuf clientSni; ///< the SSL client SNI name
-    Security::SessionPointer serverSSL; ///< The SSL object on server side.
 
 private:
+    Security::SessionPointer serverSession; ///< The TLS session object on server side.
     store_client *sc; ///< dummy client to prevent entry trimming
 };
 
diff -u -r -N squid-4.0.16/src/ssl/support.cc squid-4.0.17/src/ssl/support.cc
--- squid-4.0.16/src/ssl/support.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/support.cc	2016-12-16 23:06:20.000000000 +1300
@@ -44,7 +44,7 @@
 
 const EVP_MD *Ssl::DefaultSignHash = NULL;
 
-const char *Ssl::BumpModeStr[] = {
+std::vector<const char *> Ssl::BumpModeStr = {
     "none",
     "client-first",
     "server-first",
@@ -52,9 +52,8 @@
     "stare",
     "bump",
     "splice",
-    "terminate",
-    /*"err",*/
-    NULL
+    "terminate"
+    /*,"err"*/
 };
 
 /**
@@ -455,7 +454,7 @@
 
         if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
             const int ssl_error = ERR_get_error();
-            fatalf("Failed to initialise SSL engine: %s\n", ERR_error_string(ssl_error, NULL));
+            fatalf("Failed to initialise SSL engine: %s\n", Security::ErrorString(ssl_error));
         }
     }
 #else
@@ -519,7 +518,7 @@
 
         if (!SSL_CTX_set_cipher_list(ctx.get(), port.secure.sslCipher.c_str())) {
             ssl_error = ERR_get_error();
-            debugs(83, DBG_CRITICAL, "ERROR: Failed to set SSL cipher suite '" << port.secure.sslCipher << "': " << ERR_error_string(ssl_error, NULL));
+            debugs(83, DBG_CRITICAL, "ERROR: Failed to set SSL cipher suite '" << port.secure.sslCipher << "': " << Security::ErrorString(ssl_error));
             return false;
         }
     }
@@ -536,7 +535,7 @@
             SSL_CTX_set_client_CA_list(ctx.get(), clientca);
         } else {
             ssl_error = ERR_get_error();
-            debugs(83, DBG_CRITICAL, "ERROR: Failed to dupe the client CA list: " << ERR_error_string(ssl_error, NULL));
+            debugs(83, DBG_CRITICAL, "ERROR: Failed to dupe the client CA list: " << Security::ErrorString(ssl_error));
             return false;
         }
 
@@ -572,14 +571,14 @@
     if (!SSL_CTX_use_certificate(ctx.get(), port.signingCert.get())) {
         const int ssl_error = ERR_get_error();
         const auto &keys = port.secure.certs.front();
-        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS certificate '" << keys.certFile << "': " << ERR_error_string(ssl_error, NULL));
+        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS certificate '" << keys.certFile << "': " << Security::ErrorString(ssl_error));
         return false;
     }
 
     if (!SSL_CTX_use_PrivateKey(ctx.get(), port.signPkey.get())) {
         const int ssl_error = ERR_get_error();
         const auto &keys = port.secure.certs.front();
-        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS private key '" << keys.privateKeyFile << "': " << ERR_error_string(ssl_error, NULL));
+        debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS private key '" << keys.privateKeyFile << "': " << Security::ErrorString(ssl_error));
         return false;
     }
 
@@ -590,7 +589,7 @@
 
         if (!SSL_CTX_use_certificate_chain_file(ctx.get(), certfile)) {
             ssl_error = ERR_get_error();
-            debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL certificate '" << certfile << "': " << ERR_error_string(ssl_error, NULL));
+            debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL certificate '" << certfile << "': " << Security::ErrorString(ssl_error));
             return false;
         }
 
@@ -599,7 +598,7 @@
 
         if (!SSL_CTX_use_PrivateKey_file(ctx.get(), keyfile, SSL_FILETYPE_PEM)) {
             ssl_error = ERR_get_error();
-            debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL private key '" << keyfile << "': " << ERR_error_string(ssl_error, NULL));
+            debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire SSL private key '" << keyfile << "': " << Security::ErrorString(ssl_error));
             return false;
         }
 
@@ -608,7 +607,7 @@
         if (!SSL_CTX_check_private_key(ctx.get())) {
             ssl_error = ERR_get_error();
             debugs(83, DBG_CRITICAL, "ERROR: SSL private key '" << certfile << "' does not match public key '" <<
-                   keyfile << "': " << ERR_error_string(ssl_error, NULL));
+                   keyfile << "': " << Security::ErrorString(ssl_error));
             return false;
         }
     */
@@ -640,7 +639,7 @@
         if (!SSL_CTX_set_cipher_list(ctx.get(), cipher)) {
             const int ssl_error = ERR_get_error();
             fatalf("Failed to set SSL cipher suite '%s': %s\n",
-                   cipher, ERR_error_string(ssl_error, NULL));
+                   cipher, Security::ErrorString(ssl_error));
         }
     }
 
@@ -654,7 +653,7 @@
             if (!SSL_CTX_use_certificate_chain_file(ctx.get(), certfile)) {
                 const int ssl_error = ERR_get_error();
                 fatalf("Failed to acquire SSL certificate '%s': %s\n",
-                       certfile, ERR_error_string(ssl_error, NULL));
+                       certfile, Security::ErrorString(ssl_error));
             }
 
             debugs(83, DBG_IMPORTANT, "Using private key in " << keys.privateKeyFile);
@@ -664,7 +663,7 @@
             if (!SSL_CTX_use_PrivateKey_file(ctx.get(), keyfile, SSL_FILETYPE_PEM)) {
                 const int ssl_error = ERR_get_error();
                 fatalf("Failed to acquire SSL private key '%s': %s\n",
-                       keyfile, ERR_error_string(ssl_error, NULL));
+                       keyfile, Security::ErrorString(ssl_error));
             }
 
             debugs(83, 5, "Comparing private and public SSL keys.");
@@ -672,7 +671,7 @@
             if (!SSL_CTX_check_private_key(ctx.get())) {
                 const int ssl_error = ERR_get_error();
                 fatalf("SSL private key '%s' does not match public key '%s': %s\n",
-                       certfile, keyfile, ERR_error_string(ssl_error, NULL));
+                       certfile, keyfile, Security::ErrorString(ssl_error));
             }
         }
     }
@@ -975,10 +974,10 @@
     X509 *signingCert = port.signingCert.get();
     if (SSL_CTX_add_extra_chain_cert(ctx.get(), signingCert)) {
         // increase the certificate lock
-        CRYPTO_add(&(signingCert->references),1,CRYPTO_LOCK_X509);
+        X509_up_ref(signingCert);
     } else {
         const int ssl_error = ERR_get_error();
-        debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain: " << ERR_error_string(ssl_error, NULL));
+        debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain: " << Security::ErrorString(ssl_error));
     }
     Ssl::addChainToSslContext(ctx, port.certsToChain.get());
 }
@@ -1069,7 +1068,7 @@
     if (!SSL_set_tlsext_host_name(ssl, fqdn)) {
         const int ssl_error = ERR_get_error();
         debugs(83, 3,  "WARNING: unable to set TLS servername extension (SNI): " <<
-               ERR_error_string(ssl_error, NULL) << "\n");
+               Security::ErrorString(ssl_error) << "\n");
         return false;
     }
     return true;
@@ -1089,10 +1088,10 @@
         X509 *cert = sk_X509_value(chain, i);
         if (SSL_CTX_add_extra_chain_cert(ctx.get(), cert)) {
             // increase the certificate lock
-            CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
+            X509_up_ref(cert);
         } else {
             const int ssl_error = ERR_get_error();
-            debugs(83, DBG_IMPORTANT, "WARNING: can not add certificate to SSL context chain: " << ERR_error_string(ssl_error, NULL));
+            debugs(83, DBG_IMPORTANT, "WARNING: can not add certificate to SSL context chain: " << Security::ErrorString(ssl_error));
         }
     }
 }
@@ -1330,7 +1329,7 @@
 {
     if (!certFilename)
         return NULL;
-    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
     if (!bio)
         return NULL;
     if (!BIO_read_filename(bio.get(), certFilename))
@@ -1372,10 +1371,17 @@
     pem_password_cb *cb = ::Config.Program.ssl_password ? &ssl_ask_password_cb : NULL;
     pkey.resetWithoutLocking(readSslPrivateKey(keyFilename, cb));
     cert.resetWithoutLocking(readSslX509CertificatesChain(certFilename, chain.get()));
-    if (!pkey || !cert || !X509_check_private_key(cert.get(), pkey.get())) {
-        pkey.reset();
-        cert.reset();
-    }
+    if (!cert) {
+        debugs(83, DBG_IMPORTANT, "WARNING: missing cert in '" << certFilename << "'");
+    } else if (!pkey) {
+        debugs(83, DBG_IMPORTANT, "WARNING: missing private key in '" << keyFilename << "'");
+    } else if (!X509_check_private_key(cert.get(), pkey.get())) {
+        debugs(83, DBG_IMPORTANT, "WARNING: X509_check_private_key() failed to verify signing cert");
+    } else
+        return; // everything is okay
+
+    pkey.reset();
+    cert.reset();
 }
 
 bool Ssl::generateUntrustedCert(Security::CertPointer &untrustedCert, EVP_PKEY_Pointer &untrustedPkey, Security::CertPointer const  &cert, EVP_PKEY_Pointer const & pkey)
@@ -1432,7 +1438,7 @@
     }
 
     debugs(83, DBG_IMPORTANT, "ERROR: " << squidCtx << ' ' << errAction <<
-           ": " << ERR_error_string(errCode, NULL));
+           ": " << Security::ErrorString(errCode));
     return false;
 }
 
diff -u -r -N squid-4.0.16/src/ssl/support.h squid-4.0.17/src/ssl/support.h
--- squid-4.0.16/src/ssl/support.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/ssl/support.h	2016-12-16 23:06:20.000000000 +1300
@@ -91,6 +91,12 @@
 /// initialize a TLS client context with OpenSSL specific settings
 bool InitClientContext(Security::ContextPointer &, Security::PeerOptions &, long options, long flags);
 
+#if defined(CRYPTO_LOCK_X509)
+// portability wrapper for OpenSSL 1.0 vs 1.1
+// use Security::CertPointer instead where possible
+inline int X509_up_ref(X509 *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_X509); return 0;}
+#endif
+
 } //namespace Ssl
 
 /// \ingroup ServerProtocolSSLAPI
@@ -145,7 +151,7 @@
  \ingroup  ServerProtocolSSLAPI
  * Short names for ssl-bump modes
  */
-extern const char *BumpModeStr[];
+extern std::vector<const char *>BumpModeStr;
 
 /**
  \ingroup ServerProtocolSSLAPI
@@ -153,7 +159,7 @@
  */
 inline const char *bumpMode(int bm)
 {
-    return (0 <= bm && bm < Ssl::bumpEnd) ? Ssl::BumpModeStr[bm] : NULL;
+    return (0 <= bm && bm < Ssl::bumpEnd) ? Ssl::BumpModeStr.at(bm) : NULL;
 }
 
 /// certificates indexed by issuer name
diff -u -r -N squid-4.0.16/src/store/id_rewriters/file/storeid_file_rewrite.8 squid-4.0.17/src/store/id_rewriters/file/storeid_file_rewrite.8
--- squid-4.0.16/src/store/id_rewriters/file/storeid_file_rewrite.8	2016-10-31 03:19:23.000000000 +1300
+++ squid-4.0.17/src/store/id_rewriters/file/storeid_file_rewrite.8	2016-12-17 07:22:45.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "STOREID_FILE_REWRITE 8"
-.TH STOREID_FILE_REWRITE 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH STOREID_FILE_REWRITE 8 "2016-12-16" "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-4.0.16/src/tests/stub_HttpControlMsg.cc squid-4.0.17/src/tests/stub_HttpControlMsg.cc
--- squid-4.0.16/src/tests/stub_HttpControlMsg.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/tests/stub_HttpControlMsg.cc	2016-12-16 23:06:20.000000000 +1300
@@ -13,4 +13,5 @@
 
 #include "HttpControlMsg.h"
 void HttpControlMsgSink::wroteControlMsg(CommIoCbParams const&) STUB
+void HttpControlMsgSink::doneWithControlMsg() STUB
 
diff -u -r -N squid-4.0.16/src/tests/stub_libsslsquid.cc squid-4.0.17/src/tests/stub_libsslsquid.cc
--- squid-4.0.16/src/tests/stub_libsslsquid.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/tests/stub_libsslsquid.cc	2016-12-16 23:06:20.000000000 +1300
@@ -66,7 +66,7 @@
 //GETX509ATTRIBUTE GetX509UserAttribute;
 //GETX509ATTRIBUTE GetX509CAAttribute;
 //GETX509ATTRIBUTE GetX509Fingerprint;
-const char *BumpModeStr[] = {""};
+std::vector<const char *> BumpModeStr = {""};
 bool generateUntrustedCert(Security::CertPointer & untrustedCert, EVP_PKEY_Pointer & untrustedPkey, Security::CertPointer const & cert, EVP_PKEY_Pointer const & pkey) STUB_RETVAL(false)
 Security::ContextPointer generateSslContext(CertificateProperties const &, AnyP::PortCfg &) STUB_RETVAL(Security::ContextPointer())
 bool verifySslCertificate(Security::ContextPointer &, CertificateProperties const &) STUB_RETVAL(false)
diff -u -r -N squid-4.0.16/src/tests/stub_wordlist.cc squid-4.0.17/src/tests/stub_wordlist.cc
--- squid-4.0.16/src/tests/stub_wordlist.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/tests/stub_wordlist.cc	2016-12-16 23:06:20.000000000 +1300
@@ -13,7 +13,5 @@
 #include "tests/STUB.h"
 
 const char *wordlistAdd(wordlist **, const char *) STUB_RETVAL(NULL)
-void wordlistAddWl(wordlist **, wordlist *) STUB
-void wordlistJoin(wordlist **, wordlist **) STUB
 void wordlistDestroy(wordlist **) STUB
 
diff -u -r -N squid-4.0.16/src/tests/testSBuf.cc squid-4.0.17/src/tests/testSBuf.cc
--- squid-4.0.16/src/tests/testSBuf.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/tests/testSBuf.cc	2016-12-16 23:06:20.000000000 +1300
@@ -151,9 +151,22 @@
 void
 testSBuf::testAppendSBuf()
 {
-    SBuf s1(fox1),s2(fox2);
-    s1.append(s2);
-    CPPUNIT_ASSERT_EQUAL(s1,literal);
+    const SBuf appendix(fox1);
+    const char * const rawAppendix = appendix.rawContent();
+
+    // check whether the optimization that prevents copying when append()ing to
+    // default-constructed SBuf actually works
+    SBuf s0;
+    s0.append(appendix);
+    CPPUNIT_ASSERT_EQUAL(s0.rawContent(), appendix.rawContent());
+    CPPUNIT_ASSERT_EQUAL(s0, appendix);
+
+    // paranoid: check that the above code can actually detect copies
+    SBuf s1(fox1);
+    s1.append(appendix);
+    CPPUNIT_ASSERT(s1.rawContent() != appendix.rawContent());
+    CPPUNIT_ASSERT(s1 != appendix);
+    CPPUNIT_ASSERT_EQUAL(rawAppendix, appendix.rawContent());
 }
 
 void
@@ -828,6 +841,15 @@
                 b.append('X');
         }
     }
+
+    // the minimal space requirement should overwrite idealSpace preferences
+    requirements.minSpace = 10;
+    for (const int delta: {-1,0,+1}) {
+        requirements.idealSpace = requirements.minSpace + delta;
+        SBuf buffer;
+        buffer.reserve(requirements);
+        CPPUNIT_ASSERT(buffer.spaceSize() >= requirements.minSpace);
+    }
 }
 
 void
diff -u -r -N squid-4.0.16/src/tools.cc squid-4.0.17/src/tools.cc
--- squid-4.0.16/src/tools.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/tools.cc	2016-12-16 23:06:20.000000000 +1300
@@ -1014,7 +1014,7 @@
 
     if (!fp) {
         int xerrno = errno;
-        debugs(1, DBG_IMPORTANT, "parseEtcHosts: " << Config.etcHostsPath << ": " << xstrerr(xerrno));
+        debugs(1, DBG_IMPORTANT, "parseEtcHosts: '" << Config.etcHostsPath << "' : " << xstrerr(xerrno));
         return;
     }
 
@@ -1023,8 +1023,6 @@
 #endif
 
     while (fgets(buf, 1024, fp)) {  /* for each line */
-        wordlist *hosts = NULL;
-        char *addr;
 
         if (buf[0] == '#')  /* MS-windows likes to add comments */
             continue;
@@ -1033,7 +1031,7 @@
 
         lt = buf;
 
-        addr = buf;
+        char *addr = buf;
 
         debugs(1, 5, "etc_hosts: line is '" << buf << "'");
 
@@ -1048,6 +1046,8 @@
 
         lt = nt + 1;
 
+        SBufList hosts;
+
         while ((nt = strpbrk(lt, w_space))) {
             char *host = NULL;
 
@@ -1073,19 +1073,16 @@
 
             if (ipcacheAddEntryFromHosts(host, addr) != 0) {
                 /* invalid address, continuing is useless */
-                wordlistDestroy(&hosts);
-                hosts = NULL;
+                hosts.clear();
                 break;
             }
-            wordlistAdd(&hosts, host);
+            hosts.emplace_back(SBuf(host));
 
             lt = nt + 1;
         }
 
-        if (hosts) {
+        if (!hosts.empty())
             fqdncacheAddEntryFromHosts(addr, hosts);
-            wordlistDestroy(&hosts);
-        }
     }
 
     fclose (fp);
diff -u -r -N squid-4.0.16/src/tunnel.cc squid-4.0.17/src/tunnel.cc
--- squid-4.0.16/src/tunnel.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/tunnel.cc	2016-12-16 23:06:20.000000000 +1300
@@ -28,7 +28,6 @@
 #include "http.h"
 #include "http/Stream.h"
 #include "HttpRequest.h"
-#include "HttpStateFlags.h"
 #include "ip/QosConfig.h"
 #include "LogTags.h"
 #include "MemBuf.h"
@@ -68,7 +67,7 @@
     CBDATA_CLASS(TunnelStateData);
 
 public:
-    TunnelStateData();
+    TunnelStateData(ClientHttpRequest *);
     ~TunnelStateData();
     TunnelStateData(const TunnelStateData &); // do not implement
     TunnelStateData &operator =(const TunnelStateData &); // do not implement
@@ -105,6 +104,10 @@
 
     /// Whether the client sent a CONNECT request to us.
     bool clientExpectsConnectResponse() const {
+        // If we are forcing a tunnel after receiving a client CONNECT, then we
+        // have already responded to that CONNECT before tunnel.cc started.
+        if (request && request->flags.forceTunnel)
+            return false;
 #if USE_OPENSSL
         // We are bumping and we had already send "OK CONNECTED"
         if (http.valid() && http->getConn() && http->getConn()->serverBump() && http->getConn()->serverBump()->step > Ssl::bumpStep1)
@@ -285,12 +288,7 @@
     }
 }
 
-TunnelStateData::TunnelStateData() :
-    url(NULL),
-    http(),
-    request(NULL),
-    status_ptr(NULL),
-    logTag_ptr(NULL),
+TunnelStateData::TunnelStateData(ClientHttpRequest *clientRequest) :
     connectRespBuf(NULL),
     connectReqWriting(false),
     started(squid_curtime)
@@ -298,6 +296,23 @@
     debugs(26, 3, "TunnelStateData constructed this=" << this);
     client.readPendingFunc = &tunnelDelayedClientRead;
     server.readPendingFunc = &tunnelDelayedServerRead;
+
+    assert(clientRequest);
+    url = xstrdup(clientRequest->uri);
+    request = clientRequest->request;
+    server.size_ptr = &clientRequest->out.size;
+    client.size_ptr = &clientRequest->al->http.clientRequestSz.payloadData;
+    status_ptr = &clientRequest->al->http.code;
+    logTag_ptr = &clientRequest->logType;
+    al = clientRequest->al;
+    http = clientRequest;
+
+    client.conn = clientRequest->getConn()->clientConnection;
+    comm_add_close_handler(client.conn->fd, tunnelClientClosed, this);
+
+    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
+                                     CommTimeoutCbPtrFun(tunnelTimeout, this));
+    commSetConnTimeout(client.conn, Config.Timeout.lifetime, timeoutCall);
 }
 
 TunnelStateData::~TunnelStateData()
@@ -498,7 +513,8 @@
     *status_ptr = rep.sline.status();
 
     // we need to relay the 401/407 responses when login=PASS(THRU)
-    const char *pwd = server.conn->getPeer()->login;
+    const CachePeer *peer = server.conn->getPeer();
+    const char *pwd = (peer ? peer->login : nullptr);
     const bool relay = pwd && (strcmp(pwd, "PASS") == 0 || strcmp(pwd, "PASSTHRU") == 0) &&
                        (*status_ptr == Http::scProxyAuthenticationRequired ||
                         *status_ptr == Http::scUnauthorized);
@@ -1075,28 +1091,10 @@
     ++statCounter.server.all.requests;
     ++statCounter.server.other.requests;
 
-    tunnelState = new TunnelStateData;
+    tunnelState = new TunnelStateData(http);
 #if USE_DELAY_POOLS
-    tunnelState->server.setDelayId(DelayId::DelayClient(http));
+    //server.setDelayId called from tunnelConnectDone after server side connection established
 #endif
-    tunnelState->url = xstrdup(url);
-    tunnelState->request = request;
-    tunnelState->server.size_ptr = &http->out.size;
-    tunnelState->client.size_ptr = &http->al->http.clientRequestSz.payloadData;
-    tunnelState->status_ptr = &http->al->http.code;
-    tunnelState->logTag_ptr = &http->logType;
-    tunnelState->client.conn = http->getConn()->clientConnection;
-    tunnelState->http = http;
-    tunnelState->al = http->al;
-    //tunnelState->started is set in TunnelStateData ctor
-
-    comm_add_close_handler(tunnelState->client.conn->fd,
-                           tunnelClientClosed,
-                           tunnelState);
-
-    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
-                                     CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
-    commSetConnTimeout(tunnelState->client.conn, Config.Timeout.lifetime, timeoutCall);
 
     peerSelect(&(tunnelState->serverDestinations), request, http->al,
                NULL,
@@ -1143,7 +1141,7 @@
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     assert(!tunnelState->waitingForConnectExchange());
     HttpHeader hdr_out(hoRequest);
-    HttpStateFlags flags;
+    Http::StateFlags flags;
     debugs(26, 3, HERE << srv << ", tunnelState=" << tunnelState);
     memset(&flags, '\0', sizeof(flags));
     flags.proxying = tunnelState->request->flags.proxying;
@@ -1183,13 +1181,40 @@
     commSetConnTimeout(srv, Config.Timeout.read, timeoutCall);
 }
 
+static Comm::ConnectionPointer
+borrowPinnedConnection(HttpRequest *request, Comm::ConnectionPointer &serverDestination)
+{
+    // pinned_connection may become nil after a pconn race
+    if (ConnStateData *pinned_connection = request ? request->pinnedConnection() : nullptr) {
+        Comm::ConnectionPointer serverConn = pinned_connection->borrowPinnedConnection(request, serverDestination->getPeer());
+        return serverConn;
+    }
+
+    return nullptr;
+}
+
 static void
 tunnelPeerSelectComplete(Comm::ConnectionList *peer_paths, ErrorState *err, void *data)
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
 
-    if (peer_paths == NULL || peer_paths->size() < 1) {
+    bool bail = false;
+    if (!peer_paths || peer_paths->empty()) {
         debugs(26, 3, HERE << "No paths found. Aborting CONNECT");
+        bail = true;
+    }
+
+    if (!bail && tunnelState->serverDestinations[0]->peerType == PINNED) {
+        Comm::ConnectionPointer serverConn = borrowPinnedConnection(tunnelState->request.getRaw(), tunnelState->serverDestinations[0]);
+        debugs(26,7, "pinned peer connection: " << serverConn);
+        if (Comm::IsConnOpen(serverConn)) {
+            tunnelConnectDone(serverConn, Comm::OK, 0, (void *)tunnelState);
+            return;
+        }
+        bail = true;
+    }
+
+    if (bail) {
         if (!err) {
             err = new ErrorState(ERR_CANNOT_FORWARD, Http::scServiceUnavailable, tunnelState->request.getRaw());
         }
@@ -1237,51 +1262,33 @@
 switchToTunnel(HttpRequest *request, Comm::ConnectionPointer &clientConn, Comm::ConnectionPointer &srvConn)
 {
     debugs(26,5, "Revert to tunnel FD " << clientConn->fd << " with FD " << srvConn->fd);
-    /* Create state structure. */
-    const SBuf url(request->effectiveRequestUri());
 
-    debugs(26, 3, request->method << " " << url << " " << request->http_ver);
+    /* Create state structure. */
     ++statCounter.server.all.requests;
     ++statCounter.server.other.requests;
 
-    TunnelStateData *tunnelState = new TunnelStateData;
-    tunnelState->url = SBufToCstring(url);
-    tunnelState->request = request;
-    tunnelState->server.size_ptr = NULL; //Set later if Http::Stream is available
-
-    // Temporary static variable to store the unneeded for our case status code
-    static int status_code = 0;
-    tunnelState->status_ptr = &status_code;
-    tunnelState->client.conn = clientConn;
-
-    if (auto conn = request->clientConnectionManager.get()) {
-        Http::StreamPointer context = conn->pipeline.front();
-        if (context && context->http) {
-            tunnelState->logTag_ptr = &context->http->logType;
-            tunnelState->server.size_ptr = &context->http->out.size;
-            tunnelState->al = context->http->al;
+    auto conn = request->clientConnectionManager.get();
+    Must(conn);
+    Http::StreamPointer context = conn->pipeline.front();
+    Must(context && context->http);
 
-#if USE_DELAY_POOLS
-            /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
-            if (srvConn->getPeer() && srvConn->getPeer()->options.no_delay)
-                tunnelState->server.setDelayId(DelayId::DelayClient(context->http));
-#endif
-        }
-    }
+    debugs(26, 3, request->method << " " << context->http->uri << " " << request->http_ver);
 
-    comm_add_close_handler(tunnelState->client.conn->fd,
-                           tunnelClientClosed,
-                           tunnelState);
+    TunnelStateData *tunnelState = new TunnelStateData(context->http);
 
-    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
-                                     CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
-    commSetConnTimeout(tunnelState->client.conn, Config.Timeout.lifetime, timeoutCall);
     fd_table[clientConn->fd].read_method = &default_read_method;
     fd_table[clientConn->fd].write_method = &default_write_method;
 
     request->hier.note(srvConn, tunnelState->getHost());
 
     tunnelState->server.conn = srvConn;
+
+#if USE_DELAY_POOLS
+    /* no point using the delayIsNoDelay stuff since tunnel is nice and simple */
+    if (srvConn->getPeer() && srvConn->getPeer()->options.no_delay)
+        tunnelState->server.setDelayId(DelayId::DelayClient(context->http));
+#endif
+
     request->peer_host = srvConn->getPeer() ? srvConn->getPeer()->host : nullptr;
     comm_add_close_handler(srvConn->fd, tunnelServerClosed, tunnelState);
 
@@ -1298,8 +1305,8 @@
         request->flags.proxying = false;
     }
 
-    timeoutCall = commCbCall(5, 4, "tunnelTimeout",
-                             CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
+    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
+                                     CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
     commSetConnTimeout(srvConn, Config.Timeout.read, timeoutCall);
     fd_table[srvConn->fd].read_method = &default_read_method;
     fd_table[srvConn->fd].write_method = &default_write_method;
diff -u -r -N squid-4.0.16/src/wordlist.cc squid-4.0.17/src/wordlist.cc
--- squid-4.0.16/src/wordlist.cc	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/wordlist.cc	2016-12-16 23:06:20.000000000 +1300
@@ -32,28 +32,6 @@
 }
 
 void
-wordlistJoin(wordlist ** list, wordlist ** wl)
-{
-    while (*list)
-        list = &(*list)->next;
-
-    *list = *wl;
-
-    *wl = NULL;
-}
-
-void
-wordlistAddWl(wordlist ** list, wordlist * wl)
-{
-    while (*list)
-        list = &(*list)->next;
-
-    for (; wl; wl = wl->next, list = &(*list)->next) {
-        *list = new wordlist(wl->key);
-    }
-}
-
-void
 wordlistCat(const wordlist * w, MemBuf * mb)
 {
     while (NULL != w) {
diff -u -r -N squid-4.0.16/src/wordlist.h squid-4.0.17/src/wordlist.h
--- squid-4.0.16/src/wordlist.h	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/src/wordlist.h	2016-12-16 23:06:20.000000000 +1300
@@ -52,18 +52,6 @@
  */
 void wordlistCat(const wordlist *, MemBuf *);
 
-/** append a wordlist to another
- *
- * \deprecated use SBufList.merge(otherwordlist) instead
- */
-void wordlistAddWl(wordlist **, wordlist *);
-
-/** Concatenate the words in a wordlist
- *
- * \deprecated use SBufListContainerJoin(SBuf()) from sbuf/Algorithms.h instead
- */
-void wordlistJoin(wordlist **, wordlist **);
-
 /// destroy a wordlist
 void wordlistDestroy(wordlist **);
 
diff -u -r -N squid-4.0.16/test-suite/squidconf/mgr_passwd squid-4.0.17/test-suite/squidconf/mgr_passwd
--- squid-4.0.16/test-suite/squidconf/mgr_passwd	1970-01-01 12:00:00.000000000 +1200
+++ squid-4.0.17/test-suite/squidconf/mgr_passwd	2016-12-16 23:06:20.000000000 +1300
@@ -0,0 +1,22 @@
+#
+# Tests for cachemgr_passwd directive
+
+# accept passwords
+cachemgr_passwd password1 utilization
+cachemgr_passwd password1 store_io
+
+# accept list of actions for one password
+cachemgr_passwd password2 io counters
+
+# specaial case 'none'
+cachemgr_passwd none menu
+cachemgr_passwd none index
+
+# special case 'disable'
+cachemgr_passwd disable
+
+# repeating actions should produce a warning
+cachemgr_passwd none menu
+
+# unknown action ??
+cachemgr_passwd none blah_blah_blah
diff -u -r -N squid-4.0.16/tools/helper-mux/helper-mux.8 squid-4.0.17/tools/helper-mux/helper-mux.8
--- squid-4.0.16/tools/helper-mux/helper-mux.8	2016-10-31 03:22:52.000000000 +1300
+++ squid-4.0.17/tools/helper-mux/helper-mux.8	2016-12-17 07:27:19.000000000 +1300
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "HELPER-MUX 8"
-.TH HELPER-MUX 8 "2016-10-30" "perl v5.24.1" "User Contributed Perl Documentation"
+.TH HELPER-MUX 8 "2016-12-16" "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-4.0.16/tools/purge/purge.1 squid-4.0.17/tools/purge/purge.1
--- squid-4.0.16/tools/purge/purge.1	2016-10-31 01:24:50.000000000 +1300
+++ squid-4.0.17/tools/purge/purge.1	2016-12-16 23:06:20.000000000 +1300
@@ -19,7 +19,7 @@
 .B squid
 does not seem to know about any longer.
 .PP
-This is a tool for expert usage only, use it under your own responsability.
+This is a tool for expert usage only, use it under your own responsibility.
 .
 .SH OPTIONS
 .if !'po4a'hide' .TP 12
@@ -58,7 +58,7 @@
 .
 .if !'po4a'hide' .TP
 .if !'po4a'hide' .B \-d l
-lets you specify a debug level. Differents bits are reserved for
+lets you specify a debug level. Different bits are reserved for
 different output.
 .br
 default: 0
