trafficserver (8.0.2+ds-1+deb10u7) buster-security; urgency=medium
authorAbhijith PA <abhijith@debian.org>
Sat, 29 Oct 2022 12:33:47 +0000 (13:33 +0100)
committerAbhijith PA <abhijith@debian.org>
Sat, 29 Oct 2022 12:33:47 +0000 (13:33 +0100)
  * Non-maintainer upload by the Debian LTS Team.
  * Multiple CVE fixes
    + CVE-2021-37150: Protocol vs scheme mismatch
    + CVE-2022-25763 Improper input validation on HTTP/2 headers
    + CVE-2022-28129  Insufficient Validation of HTTP/1.x Headers
    + CVE-2022-31780 HTTP/2 framing vulnerabilities

[dgit import unpatched trafficserver 8.0.2+ds-1+deb10u7]

61 files changed:
1  2 
debian/CONFIGURATION.Debian
debian/NEWS
debian/README.Debian
debian/README.conf-remap.Debian
debian/change_config.pl
debian/changelog
debian/compat
debian/control
debian/copyright
debian/docs
debian/gbp.conf
debian/gitlab-ci.yml
debian/not-installed
debian/patches/0001-Use-mcx16-on-x86-platforms-only.patch
debian/patches/0003-reproductible-build.patch
debian/patches/0006-fix-doc-build.patch
debian/patches/0008-fix-python-check-unused-dependencies.patch
debian/patches/0009-fix-mysql-8-build.patch
debian/patches/0011-fix-segfault.patch
debian/patches/0012-fix-spelling-checks.patch
debian/patches/0013-fix-perl-interpreter-path.patch
debian/patches/0014-use_system_yaml-cpp.patch
debian/patches/0015-8.0.4-CVE-backport.patch
debian/patches/0015-8.0.5-CVE-backport.patch
debian/patches/0016-CVE-2019-17559.patch
debian/patches/0016-CVE-2019-17565.patch
debian/patches/0016-CVE-2020-1944.patch
debian/patches/0016-CVE-2020-9481.patch
debian/patches/0017-CVE-2020-9494.patch
debian/patches/0018-CVE-2020-17508.patch
debian/patches/0018-CVE-2020-17509.patch
debian/patches/0019-CVE-2021-35474_32567_32566_32565_27577.patch
debian/patches/0020-CVE-2021-37147.patch
debian/patches/0020-CVE-2021-37148.patch
debian/patches/0020-CVE-2021-37149.patch
debian/patches/0020-CVE-2021-38161.patch
debian/patches/0021-CVE_2021_44040.patch
debian/patches/0021-CVE_2021_44759.patch
debian/patches/CVE-2021-37150.patch
debian/patches/CVE-2022-25763.patch
debian/patches/series
debian/rules
debian/source/format
debian/source/options
debian/trafficserver-dev.examples
debian/trafficserver-dev.install
debian/trafficserver-dev.manpages
debian/trafficserver-experimental-plugins.install
debian/trafficserver-experimental-plugins.lintian-overrides
debian/trafficserver.default
debian/trafficserver.dirs
debian/trafficserver.example
debian/trafficserver.init
debian/trafficserver.install
debian/trafficserver.maintscript
debian/trafficserver.manpages
debian/trafficserver.postinst
debian/trafficserver.service
debian/trafficserver.tmpfile
debian/upstream/signing-key.asc
debian/watch

index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..206614a7ca27162ad094dbc6fa644f35cd23065a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,99 @@@
++trafficserver for Debian
++------------------------
++
++Here are some configuration hints related to Traffic Server. Unless you already
++noted, configuration files are located in /etc/trafficserver. Traffic Server
++has some uncommon behaviour here: Please note, those files are generated auto-
++matically and some may not even be intended to be edited manually. Moreover it
++will rotate and synchronize configuration files if you run a cluster. Hence be
++careful when editing them, chances are Traffic Server WILL OVERWRITE them. This
++is especially true for comments which are not kept when using `traffic_line'
++(as explained below).
++
++* To get a general idea about traffic server and its architecture, read [4]. It
++  is crucial to understand the internal architecture before trying to start.
++
++* The main configuration file is `records.config'. You may either edit the main
++  configuration file by hand, but it is suggested to use the `traffic_line'
++  command instead. More on editing configuration files can be found on [1]. The
++  `traffic_line' command is used like this:
++
++      traffic_line -s proxy.config.proxy_name -v www.example.com
++
++  You might need to tell Traffic Server about your changes. If it is already
++  running use `traffic_line -x' to inform it about your changes. Please note
++  that you must restart your `traffic_server' daemon for some changes.
++
++* If you used Squid before you might find [2] helpful which lists Traffic Serv-
++  er configuration directives along their Squid equivalent. Use this as resour-
++  ce to understand Traffic Server.
++
++* To help you to get started, I am going to introduce you very briefly to Traf-
++  fic Server. Note, lines starting with `#' are remarks:
++
++    Open `records.config'.
++
++    # Configure the cluster interface
++    CONFIG proxy.config.cluster.ethernet_interface STRING eth0
++
++
++    # You may also want to bind your proxy server to a specific IP:
++    # Please note the security warning below as well
++    CONFIG proxy.local.incoming_ip_to_bind 1.2.3.4
++
++    # Next configure the listening port for incoming connections
++    CONFIG proxy.config.http.server_port INT 80
++
++    # Now tell Traffic Server how much Memory it is allowed to consume
++    # Set this value to -1 for best results which will instruct Traffic Server
++    # to consume roughly 1 MB for each GB of your disk backend storage size
++    # for memory caching. If you can afford it, go on and trash as much as
++    # you want and put here any positive value indicating the memory cache
++    # in bytes.
++    CONFIG proxy.config.cache.ram_cache.size LLONG 131072 # (128 MB)
++
++    Next edit `storage.config'. Debian ships with a default configuration poin-
++    ting to a cache file in `/var/cache/trafficserver' sized 256MB. This is fair-
++    ly ok for testing, otherwise feel free to change (value is in KB):
++
++    /var/cache/trafficserver 262144
++
++    Finally open `remap.config' where you need to tell Traffic Server about
++    your origin server(s):
++
++    map http://www.example.com/ http://www.example.com:8080/
++    #   ----------------------  ----------------------------
++    #   ^-  This is your client      This is your origin  -^
++    #       Host: header that                    server.
++    #       is to be mappend
++
++* For a more comprehensive documentation about Traffic Server and its configu-
++  ration directives consult [3].
++
++  Yet the documentation on the web page can not be considered up to date for some
++  parts. Feel free to ask for help on the Apache Traffic Server mailing list:
++
++  E-Mail <users@trafficserver.apache.org>
++  URL: <http://mail-archives.apache.org/mod_mbox/trafficserver-users/>
++
++  More information may be found on either resource among those:
++  Project Website: <http://trafficserver.apache.org/>
++  IRC: #traffic-server on irc.freenode.net.
++  Project Wiki: https://cwiki.apache.org/TS/traffic-server.html
++
++* Traffic Server can basically act in two modes: As forward proxy and as a rever-
++  se proxy. Traffic Servers primary purpose is to act as reverse proxy (e.g.
++  different to Squid). Nonetheless you can Traffic Server still instruct to act
++  as forward proxy as well. If you know what you are doing, you need to set
++
++     CONFIG proxy.config.url_remap.remap_required INT 0
++
++  for that purpose.
++
++
++[1] https://docs.trafficserver.apache.org/en/latest/admin-guide/configuring-traffic-server.en.html
++[2] https://cwiki.apache.org/confluence/display/TS/SquidConfigTranslation
++[3] https://docs.trafficserver.apache.org/en/latest/admin-guide/files/records.config.en.html
++[4] https://docs.trafficserver.apache.org/en/latest/admin-guide/introduction.en.html
++
++ -- Arno Töll <debian@toell.net>  Wed, 21 Mar 2012 14:10:21 +0100
diff --cc debian/NEWS
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d4253168a32da89e7d52c3129870720651488e40
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++trafficserver (6.0.0-3) unstable; urgency=medium
++
++  If you are using 32-bits package, please not that upstream has
++  discontinued 32-bit support.
++  32-bits Debian package will still be built, but is subject to removal
++  whenever 32-bit fails.
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Mon, 14 Dec 2015 10:29:12 +0100
++
++trafficserver (3.0.0-1) unstable; urgency=low
++
++  If you are upgrading from v2.1.8 or earlier to ATS 3.0 you need to migrate
++  to the new `records.config' configuration file. If possible, let Debian
++  overwrite your `records.config' configuration file.
++
++  Moreover you should clear all caches after upgrading (e.g. do "traffic_server
++  -Cclear").
++
++  The host.db data abse isn't required anymore. Therefore you can delete
++  `/etc/trafficserver/internal/hostdb.config' and `/var/cache/trafficserver/
++  host.db'.
++
++ -- Arno Töll <debian@toell.net>  Wed, 15 Jun 2011 16:23:13 +0200
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd6a3caef64f29889f04673722d3f51d13fdccca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++trafficserver for Debian
++------------------------
++
++* ATS 8.0.0 comes with slight changes in config files.
++    Following ones has been removed:
++    - /etc/trafficserver/vaddrs.config
++    - /etc/trafficserver/metrics.config
++    - /etc/trafficserver/logging.config
++    - /etc/trafficserver/log_hosts.config
++    - /etc/trafficserver/congestion.config
++    - /etc/trafficserver/cluster.config
++    - /etc/trafficserver/body_factory/default/congestion#retryAfter
++
++* New config files have been added and now use yaml format.
++    More files will be migrated in the future:
++    - /etc/trafficserver/logging.yaml
++    - /etc/trafficserver/ssl_server_name.yaml
++
++* gzip pluging has been renamed into compress.
++    Existing configuration won't be updated
++
++* traffic_cop has been removed. Now, init and systemd files use traffic_manager
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Fri, 21 Sep 2018 09:04:07 +0200
++
++* If you are using 32-bits package, please note that upstream has
++    discontinued 32-bit support.
++    32-bits Debian package will still be built, but is subject to removal
++    whenever 32-bit fails.
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Mon, 14 Dec 2015 10:29:12 +0100
++
++* Please read the CONFIGURATION.Debian file to get started with Traffic Server.
++
++* Currently Apache Traffic Server lacks man pages of binaries shipped along the
++  package. That's a known problem which is being worked on. Please read the
++  upstream documentation on the website instead.
++
++* If you read upstream's documentation about Traffic Server you will sometimes
++  find references to a program `/usr/bin/trafficserver`. Debian does not ship
++  this script. This is intentional. Use Debian's /etc/init.d/trafficserver
++  script instead.
++
++ -- Arno Töll <debian@toell.net>  Tue, 31 Dec 2011 13:54:18 +0100
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28d9e4dcf248d6a63831f161bc97962189940e0b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++Trafficserver-plugin-conf-remap for Debian
++-----------------------------------------
++
++* The conf_remap plug-in allows you to override configuration directives 
++  dependent on actual remapping rules. 
++* This plug-in replaces older legacy functionality which allowed to achieve a 
++  similar behaviour previously by configuring remap rules for example with
++  switches like "@pristine_host_hdr=1" in your remap configuration.
++* If you want to achieve this behaviour now, configure a remap rule like this:
++ 
++    map http://cdn.example.com/  http://some-server.example.com \
++         @plugin=conf_remap.so @pparam=/etc/trafficserver/cdn.conf
++  
++  where cdn.conf would look like records.config, e.g.
++    
++    CONFIG proxy.config.url_remap.pristine_host_hdr INT 1
++
++  Doing this, you will override your global default configuration on a per map-
++  ping rule.
++
++* You may want to look on 
++  <https://cwiki.apache.org/confluence/display/TS/conf_remap+Plug-In> 
++  for a full list of options which are allowed to be overridden and perhaps
++  some updated information.
++
++ -- Arno Toell <debian@toell.net>  Tue, 12 Jan 2011 19:30:18 +0100
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01072dfb68f06138ef27e655d5b4d6b215a5fb7d
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++#! /usr/bin/perl
++
++use strict;
++
++our @config_lines;
++our %defaults = (
++    "proxy.config.alarm_email" => "trafficserver",
++    "proxy.config.admin.user_id" => "trafficserver",
++    "proxy.config.log.max_space_mb_for_logs" => 2000,
++);
++
++die("Usage: $0 <file name>") unless $#ARGV == 0;
++
++open(F, "+<", $ARGV[0]) || die("Cannot open $ARGV[0]: $!");
++while(my $line = <F>)
++{
++        if ($line =~ /^CONFIG/)
++        {
++                foreach my $pattern (keys %defaults)
++                {
++                    $line =~ s/(?<=CONFIG $pattern)(\s+[A-Z]+)\s+.+$/$1 $defaults{$pattern}/;
++                }
++        }
++        push(@config_lines, $line);
++}
++seek(F, 0, 0);
++foreach my $line (@config_lines)
++{
++        print F $line;
++}
++print F "CONFIG proxy.config.admin.user_id STRING trafficserver\n";
++close(F);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6910cf99bbcdfaae011a9dfcc7bbc926ddb21e7a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,834 @@@
++trafficserver (8.0.2+ds-1+deb10u7) buster-security; urgency=medium
++
++  * Non-maintainer upload by the Debian LTS Team.
++  * Multiple CVE fixes 
++    + CVE-2021-37150: Protocol vs scheme mismatch
++    + CVE-2022-25763 Improper input validation on HTTP/2 headers
++    + CVE-2022-28129  Insufficient Validation of HTTP/1.x Headers
++    + CVE-2022-31780 HTTP/2 framing vulnerabilities
++
++ -- Abhijith PA <abhijith@debian.org>  Sat, 29 Oct 2022 18:03:47 +0530
++
++trafficserver (8.0.2+ds-1+deb10u6) buster-security; urgency=high
++
++  * Multiple CVE fixes for 8.0.x
++    + CVE-2021-37147: Improper input validation vulnerability
++    + CVE-2021-37148: Improper input validation vulnerability
++    + CVE-2021-37149: Improper Input Validation vulnerability
++    + CVE-2021-38161: Improper Authentication vulnerability in TLS origin verification
++    + CVE-2021-44040: Improper Input Validation vulnerability in request line parsing
++    + CVE-2021-44759: Improper Authentication vulnerability in TLS origin validation
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Sat, 21 May 2022 21:14:28 +0200
++
++trafficserver (8.0.2+ds-1+deb10u5) buster-security; urgency=medium
++
++  * CVE-2021-35474 CVE-2021-32567 CVE-2021-32566 CVE_2021-32565
++    CVE-2021-27577 (Closes: #990303)
++
++ -- Moritz Mühlenhoff <jmm@debian.org>  Mon, 26 Jul 2021 22:59:59 +0200
++
++trafficserver (8.0.2+ds-1+deb10u4) buster-security; urgency=high
++
++  * Add fix from upstream for CVE-2020-17508
++  * Add fix from upstream for CVE-2020-17509
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Sun, 06 Dec 2020 17:00:17 +0100
++
++trafficserver (8.0.2+ds-1+deb10u3) buster-security; urgency=high
++
++  * Add fix from upstream for CVE-2020-9494 (Closes: #963629)
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Thu, 25 Jun 2020 19:58:34 +0200
++
++trafficserver (8.0.2+ds-1+deb10u2) buster-security; urgency=medium
++
++  * Add fix from upstream for CVE-2019-17559
++  * Add fix from upstream for CVE-2019-17565
++  * Add fix from upstream for CVE-2020-1944
++  * Add fix from upstream for CVE-2020-9481
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Thu, 16 Apr 2020 21:36:40 +0200
++
++trafficserver (8.0.2+ds-1+deb10u1) buster-security; urgency=high
++
++  * Add patch for security backport from 8.0.4 for CVE-2019-9512,
++       CVE-2019-9514, and CVE-2019-9515. (Closes: #934887)
++  * Add patch for security backport from 8.0.5 for fixes
++       CVE-2019-9518 (Closes: #935314)
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Mon, 26 Aug 2019 13:55:33 +0200
++
++trafficserver (8.0.2+ds-1) unstable; urgency=medium
++
++  * Disable testing build in d/gitlab-ci.yml
++  * Update d/copyright to remove unneeded files when importing upstream release
++  * Update upstream GPG keys list
++  * New upstream version 8.0.2+ds
++  * Patches refresh for 8.0.2
++  * Update patch. Add dep3 headers
++  * Update d/control. Mark trafficserver-dev architecture all
++  * Remove signature from upstream keys. Fix lintian info
++  * Move dpkg-maintscript-helper commands to d/trafficserver.maintscript
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Wed, 30 Jan 2019 14:45:09 +0100
++
++trafficserver (8.0.1-4) unstable; urgency=medium
++
++  * Add patch and update d/control, d/rules to use system provided yaml-cpp
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Fri, 11 Jan 2019 19:07:28 +0100
++
++trafficserver (8.0.1-3) unstable; urgency=medium
++
++  * Add missed experimental plugin certifier
++  * Add man pages paths to d/not-installed to avoid too verbose dh_missing list.
++    Manpages are compressed before being install. Non-compressed version is in
++    turn erroneously detected as not installed.
++  * Update patch which has been merged upstream
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Mon, 07 Jan 2019 15:24:38 +0100
++
++trafficserver (8.0.1-2) unstable; urgency=medium
++
++  * Update Standards Version in d/control. No other changes needed
++  * Update trafficserver-experimental-plugins package descriptions
++    in d/control to fix lintian warnings
++  * Add patch 0012-fix-spelling-checks to fix spellings. Forwarded upstream
++  * Update patch 0006-fix-doc-build to include missing manpages
++  * Remove d/trafficserver.lintian-overrides
++  * Add patch 0013-fix-perl-interpreter-path. Fix lintian error which used
++    to be overridden in d/trafficserver.lintian-overrides
++  * Update d/trafficserver-dev.lintian-overrides
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Thu, 03 Jan 2019 10:52:44 +0100
++
++trafficserver (8.0.1-1) unstable; urgency=medium
++
++  * New upstream version 8.0.1
++  * Patches refresh for 8.0.1
++  * Removed patches which have been merged upstream:
++    - 0002-add-mips64-support
++    - 0004-fix_arm_build
++    - 0005-fix_build_kfreebsd
++    - 0007-fix-uri_signing
++    - 0010-fix-32bits-build
++    - 0012-fix-traffic_via
++  * Fix lintian warning public-upstream-key-not-minimal
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Mon, 03 Dec 2018 14:15:07 +0100
++
++trafficserver (8.0.0-4) unstable; urgency=medium
++
++  [ Emanuele Rocca ]
++  * Update descriptions in d/control
++
++  [ Jean Baptiste Favre ]
++  * Update patch to fix kfreebsd FTBFS
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Mon, 05 Nov 2018 10:13:03 +0100
++
++trafficserver (8.0.0-3) unstable; urgency=medium
++
++  * Remove webptransform experimental plugin
++  * Update patch to fix kfreebsd FTBFS
++  * Update d/copyright to fix lintian error
++  * Update d/rules to fix FTBFS for mips & mipsel archs
++  * Update d/rules to spare call to dpkg-parsechangelog
++  * Update d/copyright after 8.0.0 file moves
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Fri, 02 Nov 2018 14:24:38 +0100
++
++trafficserver (8.0.0-2) unstable; urgency=medium
++
++  * Add a patch to fix FTBFS with MySQL 8 (LP: #1795362)
++  * Update 0009-fix-mysql-8-build.patch which has been merged upstream
++  * Bump Standards-Version in d/control
++  * Add patch to fix 32bits arm & mips arch build
++  * Backport upstream patch to fix segmentation fault
++  * Backport upstream patch to fix traffic_via
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Wed, 17 Oct 2018 20:40:51 +0200
++
++trafficserver (8.0.0-1) unstable; urgency=medium
++
++  [ Aron Xu ]
++  * Remove .vscode directory
++  * systemd: After=network-online.target
++
++  [ Jean Baptiste Favre ]
++  * Update d/changelog for 8.0.0
++  * Update init and service file after traffic_cop removal
++  * Update d/README.Debian to reflect configuration changes
++  * Add d/gitlab-ci.yml to use Salsa CI
++  * Update d/control
++      * Add build-deps against graphviz, python3-sphinxcontrib.plantuml
++        and plantuml to build documentation & manpages
++      * Add build-deps against libcrypto++-dev, libjansson-dev, libcjose-dev to
++        build uri_signing plugin
++      * Update Breaks to manage plugins transition from experimental to stable
++  * Update postinst maintainer script, add postrm & preinst scripts to remove
++    obsolete config files
++    - /etc/trafficserver/vaddrs.config,
++    - /etc/trafficserver/metrics.config,
++    - /etc/trafficserver/logging.config,
++    - /etc/trafficserver/log_hosts.config,
++    - /etc/trafficserver/congestion.config,
++    - /etc/trafficserver/cluster.config,
++    - /etc/trafficserver/body_factory/default/congestion#retryAfter
++  * Update d/trafficserver and d/trafficserver-experimental-plugins install
++    files
++    - Stable plugin gzip renamed to compress
++    - Promoted stable plugins:
++      - cachekey,
++      - cache_promote,
++      - escalate,
++      - test_cppapi,
++      - tslua
++    - Added new experimental plugins:
++      - access_control,
++      - fq_pacing,
++      - prefetch,
++      - server_push_preload,
++      - system_stats,
++      - tls_bridge,
++      - traffic_dump,
++      - uri_signing
++    - Remove deprecated plugins collapsed_connection, epic
++  * Remove obsolete config option --enable-system-luajit from d/rules
++  * Add --with-build-version option to d/rule to have a deterministic
++    build-version
++  * Update d/copyright to remove .vscode during import at repack stage
++  * Update patches for 8.0.0
++    - Refresh patches for 8.0.0
++    - Removed patches:
++      - 0004-force-use-luajit-system*, updated upstream
++      - 0005-fix_documentation_build_option, updated upstream
++      - 0008-fix_build_lua, updated upstream
++      - 0009-fix-doc-python3, update upstream
++      - 0010-Remove-custom-memory-barriers-from-header_rewrite-an patches,
++        updated upstream
++    - Renamed remaining patches:
++      - 0006-fix_arm_build -> 0004-fix_arm_build,
++      - 0007-fix_build_kfreebsd -> 0005-fix_build_kfreebsd,
++      - 0009-fix-doc-git -> 0006-fix-doc-build
++    - Add patch to fix uri_signing experimental plugin build
++    - Add patch to fix check-unused-dependencies
++  * New upstream version 8.0.0
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Sat, 06 Oct 2018 12:43:13 +0200
++
++trafficserver (7.1.4+ds-1) unstable; urgency=medium
++
++  [ Aron Xu ]
++  * Use libunwind for mips*
++
++  [ Jean Baptiste Favre ]
++  * Update Debian standards version
++  * New upstream version 7.1.4+ds
++  * Patches refresh for 7.1.4
++  * Add lintian override for trafficserver package
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Mon, 06 Aug 2018 10:56:14 +0200
++
++trafficserver (7.1.3+ds-4) unstable; urgency=medium
++
++  * Fix FTBS on armel (Closes: #902112)
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Fri, 22 Jun 2018 17:13:53 +0200
++
++trafficserver (7.1.3+ds-3) unstable; urgency=medium
++
++  * Add systemd tmpfile support (LP: #1756207)
++  * Update Build-Depends (Closes: #887503)
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Wed, 13 Jun 2018 22:39:58 +0200
++
++trafficserver (7.1.3+ds-2) unstable; urgency=medium
++
++  * Update build-deps to libncurses6
++  * Update patch to fix arm* builds
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Fri, 18 May 2018 17:25:22 +0200
++
++trafficserver (7.1.3+ds-1) unstable; urgency=medium
++
++  * Update Vcs URLs. Replace alioth with salsa
++  * Add patch to make documentation build with python3. Fix lintian warning
++  * New upstream version 7.1.3+ds
++  * Update d/gbp.conf
++  * Patches refresh for 7.1.3
++  * Add a patch to fix doc build outside of git repository
++  * Update standards version
++  * Remove chown command from postinst script. Fix lintian warning
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Tue, 24 Apr 2018 10:17:21 +0200
++
++trafficserver (7.1.2+ds-3) unstable; urgency=medium
++
++  * Add upstream changelog file to trafficserver package
++  * Add systemd units file
++  * Fix documentation. Update links
++  * Update d/rules to remove examples binary files from package
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Fri, 16 Feb 2018 15:05:07 +0100
++
++trafficserver (7.1.2+ds-2) unstable; urgency=medium
++
++  * Drop patch for lua source code removal (Handled with repack)
++  * Drop patch for documentation build (merged upstream)
++  * Add a patch to make man pages being built
++  * Update d/compat
++  * Fix lintian warnings
++  * Update d/rule to fix lintian warning.
++  * Disable auto_test (again)
++  * Install CHANGELOG file in /usr/share/doc
++  * Enable documentation built (if not, man pages aren't either)
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Thu, 25 Jan 2018 23:38:51 +0100
++
++trafficserver (7.1.2+ds-1) unstable; urgency=medium
++
++  * New upstream version 7.1.2+ds
++  * Update d/patches for 7.1.2+ds
++  * Update build dependencies
++  * Update compilation flags
++  * Enable experimental plugin cache_key_genid
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Tue, 16 Jan 2018 18:18:40 +0100
++
++trafficserver (7.1.1-1) unstable; urgency=medium
++
++  * Fix trafficserver-dev dependencies. (Closes: #877457)
++  * Fix d/gbp.conf. Remove duplicate filter option
++  * Update standards version in d/control
++  * Fix debian-rules-sets-dpkg-architecture-variable lintian warning
++  * Fix debian-watch-uses-insecure-uri lintian info
++  * Update d/patches
++  * Update d/rules to reflect healthcheck being managed as a stable plugin
++  * Add a patch to fix kfreebsd build
++  * Add a patch to fix arm build
++  * New upstream version 7.1.0
++  * Remove broken 0008-fix_build_armel patch
++  * Patches refresh for 7.1.0
++  * Add new patch to fix build with luajit 2.1 (Closes: #873328)
++  * Update experimental modules list
++  * Update Debian Standards-Version & d/compat
++  * Update Vcs-* fields to use secure communication
++  * Lintian fix for d/NEWS
++  * Add new build option to use system luajit
++  * Update build dependencies (Closes: #859750)
++  * Fix lintian warning in d/copyright
++  * New upstream version 7.1.1
++  * Patches refresh for 7.1.1
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Tue, 02 Jan 2018 13:30:43 +0100
++
++trafficserver (7.0.0-5) unstable; urgency=medium
++
++  * Add patch to fix arm* build. (Closes: #857389)
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Wed, 22 Mar 2017 09:31:14 +0100
++
++trafficserver (7.0.0-4) unstable; urgency=medium
++
++  * Add Conflicts for file overwrites (Closes: #850775)
++
++ -- Aron Xu <aron@debian.org>  Wed, 11 Jan 2017 14:49:15 +0800
++
++trafficserver (7.0.0-3) unstable; urgency=medium
++
++  * Fix documentation build for  docutils >= 0.13 (Closes: #848800)
++  * Update LuaJIT patches serie to fallback using Lua if LuaJIT not found
++  * Update luajit patch.
++    - Remove hardcoded value
++    - Add luajit dynamic detection
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Wed, 04 Jan 2017 15:13:43 +0100
++
++trafficserver (7.0.0-2) unstable; urgency=medium
++
++  * Add patches to use system luajit and not the internal one any more
++  * Update d/rules to remove luajit exception.
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Fri, 25 Nov 2016 17:51:25 +0100
++
++trafficserver (7.0.0-1) unstable; urgency=medium
++
++  [ Arno Töll ]
++  * Remove myself as maintainer. Thanks to Aron and Jean Baptiste for stepping
++    in on my place and keeping the package up to date. It makes the confident
++    the package is in good hands now.
++
++  [ Jean Baptiste Favre ]
++  * New upstream release 7.0.0
++  * Patches refresh for 7.0.0
++  * Update plugins list
++  * Update build dependencies
++  * Add pkgconfig .pc file in trafficserver-dev package
++  * Enable experimental plugins webp_transform
++  * Enable traffic_top build (Closes: #836126)
++  * Remove Linux AIO support (Closes: #803661, #836124)
++  * Fix FTBFS for mips64el (Closes: #830856)
++  * Add patch to make the build reproductible (Closes: #833176)
++
++  [ Aron Xu ]
++  * Set myself as Maintainer at the moment
++  * Build-Depends on default-libmysqlclient-dev
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Wed, 16 Nov 2016 09:48:10 +0100
++
++trafficserver (6.2.0-1) unstable; urgency=medium
++
++  * Add upstream gpg keys for uscan pgpsigurlmangle option
++  * Imported Upstream version 6.2.0
++  * Patch refresh for 6.2.0
++  * Update trafficserver manpages list
++  * Update trafficserver install list
++  * Update trafficserver-experimental-plugins install list
++  * Update build dependencies (Add libmariadbclient-dev in experimental)
++  * Bump standards version
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Sat, 23 Jul 2016 19:15:58 +0200
++
++trafficserver (6.1.1-1) unstable; urgency=medium
++
++  * Imported Upstream version 6.1.1
++  * Update d/watch for gpg signature check
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Sat, 06 Feb 2016 10:37:41 +0100
++
++trafficserver (6.1.0-1) unstable; urgency=medium
++
++  * New upstream release
++  * Use -mcx16 on x86 platforms only (Closes: #805124)
++
++ -- Aron Xu <aron@debian.org>  Tue, 02 Feb 2016 12:18:10 +0800
++
++trafficserver (6.0.0-3) unstable; urgency=medium
++
++  * Update d/NEWS & d/README.Debian with 32-bit upstream support drop
++
++ -- Jean Baptiste Favre <debian@jbfavre.org>  Mon, 14 Dec 2015 10:48:20 +0100
++
++trafficserver (6.0.0-2) unstable; urgency=medium
++
++  * Re-enable 32-bit builds
++  * Add some optional build dependencies
++  * Use -O3 by default
++
++ -- Aron Xu <aron@debian.org>  Sat, 31 Oct 2015 03:05:20 +0800
++
++trafficserver (6.0.0-1) unstable; urgency=medium
++
++  [ Aron Xu ]
++  * Avoid possible-missing-colon-in-closes
++
++  [ Jean Baptiste Favre ]
++  * Imported Upstream version 6.0.0
++  * Update experimental plugins list
++    - Remove channel_stats
++    - Add cache promote cache_promote.so
++    - Add cache ranage request cache_range_requests.so
++    - Add mp4 streaming media mp4.so
++    - Add Stream editor stream_editor.so
++
++ -- Aron Xu <aron@debian.org>  Sat, 10 Oct 2015 11:19:19 +0200
++
++trafficserver (5.3.1-1) unstable; urgency=medium
++
++  [ Arno Töll ]
++  * Add proxy.config.admin.user_id setting
++
++  [ Aron Xu ]
++  * Add myself back to Uploaders
++  * Use dh compat 9
++
++  [ Jean Baptiste Favre ]
++  * Imported Upstream version 5.3.1
++  * Removed patch for TS-3632 (merged upstream)
++  * Upstream changes allow ftbfs with GCC-5 (Closes: #778148)
++
++ --  Jean Baptiste Favre <debian@jbfavre.org>  Sat, 04 Jul 2015 23:20:49 +0200
++
++trafficserver (5.3.0-2) unstable; urgency=medium
++
++  * Avoid installing tslua.so on archs built without LuaJIT
++    (Closes: #770353)
++
++ -- Aron Xu <aron@debian.org>  Thu, 11 Jun 2015 15:28:39 +0800
++
++trafficserver (5.3.0-1) unstable; urgency=medium
++
++  * Imported Upstream version 5.3.0
++    - CVE-2014-10022: remote DoS
++  * Remove fix_TS3316_i386_build patch
++  * Fix traffic_wccp build, missing OpenSSL symbol
++  * Add experimental plugin generator
++  * Format patch for TS-3632
++  * Update Uploader field in d/control
++
++ -- Jean Baptiste Favre <jbfavre@debian.org>  Wed, 03 Jun 2015 20:06:13 +0200
++
++trafficserver (5.2.0-2) unstable; urgency=medium
++
++  * Disable LuaJIT for arm64
++
++ -- Aron Xu <aron@debian.org>  Sat, 28 Feb 2015 23:51:00 +0800
++
++trafficserver (5.2.0-1) unstable; urgency=medium
++
++  [ Jean Baptiste Favre ]
++  * New upstream release.
++  * Add support for following experimental modules:
++    - epic
++    - header_normalize
++    - mysql_remap
++    - ssl_cert_loader
++    - sslheaders
++  * Remove missing file related to removed traffic_shell
++  * Backport patch for TS-3316 to solve i386 build issue
++  * Fix misconfigured call to dh_makeshlibs (used system dir instead
++    of temporary build one)
++
++  [ Aron Xu ]
++  * Enable all hardening features.
++  * Update copyright file.
++
++ -- Aron Xu <aron@debian.org>  Thu, 12 Feb 2015 22:22:44 +0100
++
++trafficserver (5.1.1-1) unstable; urgency=medium
++
++  * New upstream release. This release includes fixes for these security
++    related vulnerabilities:
++    - CVE-2014-3566: Do not enable SSL3 by default
++    - CVE-2014-3624: Ensure remap requests are properly tunneled using CONNECT
++      requests to avoid an open relay
++  * Add support for ppc64el, patch merged upstream (Closes: #754134, #754808)
++  * Fix "ftbfs on kfreebsd" by conditionally building healthchecks.so on Linux
++    only. Patch supplied by Steven Chamberlain, thanks (Closes: #767287)
++
++ -- Arno Töll <arno@debian.org>  Sun, 02 Nov 2014 12:40:44 -1100
++
++trafficserver (5.1.0-1) unstable; urgency=medium
++
++  * New upstream release
++  * Bump standards version to 3.9.6 (no changes needed)
++
++  [Jean Baptiste Favre]
++  * Add a debug package with debug symbols for those who need it.
++  * Split out experimental trafficserver plugins into a separate package so
++    that users are aware of their experimental character before using them.
++  * Add a libhwloc b-d so that ATS has a better idea about the underlying
++    hardware it runs at to improve the runtime performance
++
++ -- Arno Töll <arno@debian.org>  Wed, 08 Oct 2014 23:50:49 -1100
++
++trafficserver (5.0.1-1) unstable; urgency=medium
++
++  * New upstream release including a fix for CVE-2014-3525 that allowed
++    attackers by special crafted packets to obtain privileges for services bound
++    to localhost
++
++ -- Arno Töll <arno@debian.org>  Wed, 23 Jul 2014 04:43:00 -1100
++
++trafficserver (5.0.0-1) unstable; urgency=medium
++
++  * Acknowledge previous NMUs, thanks to Anibal Monsalve Salazar for
++    coordination.
++  * New upstream version. Patch changes:
++    + drop 0001-TS-1821.patch: released upstream in 5.0.0
++    + drop 0001-TS-2454-Fix-undefined-reference-to-__sync_fetch_and_.patch:
++      released upstream in 4.2
++    + drop add-mips-support.patch: released upstream in 5.0.0
++    + drop pthread_setname_np.patch: This was a Debian specific issue which is
++      being worked around in eglibc's commit r5460.
++  * Add "support for mips64": merged upstream (Closes: #750807)
++  * Build with dh-autoreconf to avoid build time issues with Automake 1.13
++    which is not in Debian yet.
++
++ -- Arno Töll <arno@debian.org>  Sun, 06 Jul 2014 00:20:59 -1100
++
++trafficserver (4.1.2-1.2) unstable; urgency=medium
++
++  * Non-maintainer upload.
++  * Add missing prototype for pthread_setname_np()
++    Add pthread_setname_np.patch
++    Patch by Petr Salinger
++    Closes: #743584
++
++ -- Anibal Monsalve Salazar <anibal@debian.org>  Fri, 04 Apr 2014 08:59:48 +0100
++
++trafficserver (4.1.2-1.1) unstable; urgency=medium
++
++  * Non-maintainer upload.
++  * Fix undefined reference to `__sync_fetch_and_sub_8' on ARM 32bit
++    Add 0001-TS-2454-Fix-undefined-reference-to-__sync_fetch_and_.patch
++    from https://issues.apache.org/jira/browse/TS-2454
++    Patch by Yunkai Zhang
++  * Add support for MIPS
++    Add add-mips-support.patch
++    Submitted: https://issues.apache.org/jira/browse/TS-2687
++    Merged: https://git-wip-us.apache.org/repos/asf?p=trafficserver.git;h=2f81790
++    Patch by Dejan Latinovic
++    Closes: 743395
++  * Build-depend on libboost-dev
++    Patch by Dejan Latinovic
++    Closes: #737510
++
++ -- Anibal Monsalve Salazar <anibal@debian.org>  Thu, 03 Apr 2014 04:18:23 +0100
++
++trafficserver (4.1.2-1) unstable; urgency=medium
++
++  * Merge the experimental branch of trafficserver to unstable
++  * New upstram release (Closes: #711530, #733377)
++    + Refresh 0001-TS-1821.patch until it is fixed upstream
++    + Don't run autoreconf anymore, we do not need it anymore
++    + build depend on libaio-dev to support AIO on Linux systems
++  * Push standards version
++
++ -- Arno Töll <arno@debian.org>  Wed, 29 Jan 2014 03:01:12 -1100
++
++trafficserver (3.3.2-1) experimental; urgency=low
++
++  [ Arno Töll ]
++  * Drop --with-arg-max from configure
++  * Update changelog in view of the new upstream version
++
++  [ Aron Xu ]
++  * Imported Upstream version 3.3.2
++  * Drop patch for enabling experimental plugins
++  * Add liblua5.1-dev and liboost1.53-dev to B-D
++  * Try on all archs for experimental builds
++  * Permit parallel building
++  * Fix typo in dep5 copyright file
++  * Do not install staticly linked library
++  * Enable Linux native AIO support for linux-any
++  * Enable reclaimable freelist
++
++ -- Aron Xu <aron@debian.org>  Thu, 09 May 2013 01:00:04 +0800
++
++trafficserver (3.3.0+git20121208-0exp1) experimental; urgency=low
++
++  * Upstream git snapshot.
++  * Enable most of experimental plugins, install related libraries.
++  * Run dh_autoreconf.
++  * Make dh_auto_test errors non-fatal.
++
++ -- Aron Xu <aron@debian.org>  Thu, 06 Dec 2012 23:32:25 +0800
++
++trafficserver (3.3.0-1) experimental; urgency=low
++
++  * Upload upstream development release to experimental.
++
++ -- Aron Xu <aron@debian.org>  Thu, 29 Nov 2012 22:13:55 +0800
++
++trafficserver (3.2.5-1) unstable; urgency=low
++
++  * New upstream release
++    + Fix FTBFS on ARM (Closes: #691179)
++    + Fix FTBS with gcc 4.8 (Closes: #701427)
++  * Promote trafficserver to depend for trafficserver-dev to fix a broken
++    library symlink. The library is not required for all users, but those who
++    need it don't need to install it manually anymore (Closes: #715134)
++
++ -- Arno Töll <arno@debian.org>  Sun, 21 Jul 2013 11:55:38 +0200
++
++trafficserver (3.2.4-1) unstable; urgency=low
++
++  * New upstream release
++    + Delete upstream's .gitignore file in our source tree
++  * Switch packaging repository to Git.
++    + Add gbp.conf file for those using git-buildpackage
++  * Fix "Upgrade fails if purging of cache fails" by not dying in a fire when
++    the postinst fails to purge the cache (Closes: #687698)
++  * Drop --with-arg-max from ./configure, it's not needed anymore.
++
++ -- Arno Töll <arno@debian.org>  Tue, 29 Jan 2013 23:54:44 +0100
++
++trafficserver (3.2.0-1) unstable; urgency=low
++
++  * New upstream release
++    + If you are using SSL or HTTP filtering, please update your configuration.
++      proxy.config.http.quick_filter.mask  and
++      proxy.config.ssl.server.cert.filename is not recognized anymore. Please
++      use ip_allow.config and ssl_multicert.config respectively instead.
++      There is no automated migration for this in Debian, as this affects your
++      site-specific configuration files.
++    + See https://cwiki.apache.org/confluence/display/TS/Upgrading+to+3.2 for
++      full upgrade instructions.
++  * Upstream decided to ship more plug-ins with the trafficserver core
++    distribution. These are all bundled into the main package now. Therefore,
++    the trafficserver-plugin-conf-remap package is not provided anymore.
++  * Update the default configuration file to ship with more moderate values
++    for the log configuration.
++  * Now do start ATS by default for fresh installations. The default
++    out-of-the box configuration is much more secure than past defaults.
++  * Purge the host and data cache on upgrades
++  * Let's welcome Aron Xu to the Uploaders of Trafficserver. Hi Aron! :)
++
++ -- Arno Töll <arno@debian.org>  Fri, 14 Sep 2012 22:56:29 +0200
++
++trafficserver (3.0.5-1) unstable; urgency=low
++
++  * New upstream release.
++  * No kudos for the previous hostile NMU, but include the changelog to denote
++    this upload does not introduce a regression.
++  * Update my maintainer address
++  * Make the init script look much better when using fancy outputs.
++  * Fix "status" output of the init script
++  * Remove "DM-Upload-Allowed". I don't need that flag anymore.
++
++
++ -- Arno Töll <arno@debian.org>  Sat, 09 Jun 2012 18:48:23 +0200
++
++trafficserver (3.0.4-1.1) unstable; urgency=low
++
++   * Non maintainer upload
++   * Fix build failure with GCC 4.7. Closes: #667396.
++
++ -- Matthias Klose <doko@debian.org>  Wed, 30 May 2012 04:40:28 +0000
++
++trafficserver (3.0.4-1) unstable; urgency=high
++
++  * New upstream release
++    + Fix CVE-2012-0256: A request with a very large Host header caused ATS to
++      crash.
++  * Setting urgency to high because of security updates
++  * Push standards to 3.9.3 - no further changes
++  * Stilistic adaptions in debian/copyright, but not content changes
++  * Remove cluster interface warning from README.Configuration. ATS now binds on
++    lo by default
++
++ -- Arno Töll <debian@toell.net>  Wed, 21 Mar 2012 12:34:35 +0100
++
++trafficserver (3.0.2-1) unstable; urgency=low
++
++  * New upstream release
++    + Includes former Debian specific patch which makes sure the upstream
++      configure script does not override any -O flags passed by the user
++      anymore.
++  * Adapt to dpkg 1.16.1 API changes regarding build flags. This enables
++    hardening build flags. This means, trafficserver is now being built with
++    -fstack-protector and other security related build flags.
++  * Add dpkg-dev (>= 1.16.1~) to build-depends to make sure our buildflags are
++    properly supported. That's guaranteed for Testing, but might be helpful to
++    know for backporters.
++  * Fix several issues in the DEP-5 syntax. Unfortunately there is no way to
++    express that a file is subject to different license agreements so far.
++  * Do not install the upstream changelog twice anymore
++  * Finally run regression checks again, now as build failures are sorted out.
++
++ -- Arno Töll <debian@toell.net>  Sun, 11 Dec 2011 00:45:45 +0100
++
++trafficserver (3.0.1-2) unstable; urgency=low
++
++  * Fix "please add armhf to the arch list" - add armhf to the list of supported
++    architectures. Thanks Konstantinos Margaritis for the hint (Closes: #636338)
++  * Remove IA64 from the list of supported architectures. The upgrade to the gcc
++    4.6 toolchain disclosed portability issues with it, which caused the resul-
++    ting binary package to produce no-op code in some functions.
++
++ -- Arno Töll <debian@toell.net>  Tue, 02 Aug 2011 22:58:37 +0200
++
++trafficserver (3.0.1-1) unstable; urgency=low
++
++  * New upstream release. Fixes several important issues which caused
++    `traffic_cop' and `traffic_server' to crash.
++  * Fix "FTBFS with ld --as-needed" re-order libraries upon linkage, patch
++    committed upstream. Thanks Ilya Barygin (Closes: #632546)
++  * Fix "trafficserver: Getting rid of unneeded *.la / emptying
++    dependency_libs", remove *.la files from the installation target completely
++    (Closes: #633192)
++  * Set "DM-Upload-Allowed: yes" in agreement with Asheesh Laroia
++    <paulproteus{at}debian.org>
++
++ -- Arno Töll <debian@toell.net>  Mon, 01 Aug 2011 19:29:58 +0200
++
++trafficserver (3.0.0-1) unstable; urgency=low
++
++  * New upstream release. Major changes (since 2.1.9):
++    + `traffic_server' won't crash anymore when using non-existent plugin in
++      remap rule
++    + Don't cache HTTP 401, 303 and 407 error responses anymore, when negative
++      caching is enabled.
++  * Re-enable kfreebsd support, it was accidentally not available in
++    2.1.9-unstable-1 because of non installable dependencies, as libcap-dev is
++    installable (and required) on Linux only
++  * debian/rules:
++    + Simplify dh_auto_configure flags (upstream incorporated our build layout)
++    + Enable WCCP (Web Cache Communication Protocol; Linux only)
++  * debian/control:
++    + Add flex and bison to build dependencies, both are required for WCCP
++      (Linux only)
++
++ -- Arno Töll <debian@toell.net>  Wed, 15 Jun 2011 15:56:29 +0200
++
++trafficserver (2.1.9-unstable-1) unstable; urgency=low
++
++  * New upstream release. Major features (since 2.1.8):
++    + Bring back support for $DESTDIR and "make check" which makes Debian
++      patches obsolete
++    + Allow larger working sets than 512G
++    + Disable cluster autodiscovery when cluster mode is disabled
++    + Cleanup `records.config'
++    + Disable SSLv2 by default
++  * debian/control: Add build dependency to libcap-dev, because when running
++    traffic_server standalone, it is unable to bind restricted ports otherwise
++    (Upstream: TS-804)
++  * debian/rules:
++    + Remove override for dh_clean, but put options to debian/source/options
++      instead
++    + Remove DH_OPTIONS (unused anyway)
++  * Source package: Minor change to improving package quality and usability
++    (i.e. grammar, verbosity of comments)
++  * Make the init script more robust
++  * Base the origin of the package source on the untouched upstream tarball,
++    instead of the versioned SVN branch.
++  * Bring back IA64 support, this time actually working (upstream merged my
++    patch TS-783)
++  * Remove patch `build-quirks.patch'. Changes have been committed upstream
++    by now.
++
++ -- Arno Töll <debian@toell.net>  Tue, 31 May 2011 21:56:12 +0200
++
++trafficserver (2.1.8-unstable-1) unstable; urgency=low
++
++  * New upstream release. Major features (since 2.1.5):
++    + Many bug fixes (none reported in Debian's BTS)
++    + Set source address for origin Servers
++    + Major API changes for the SDK
++    + Provide traffic_logstats
++    + traffic_shell does not hang anymore on any command
++  * Fix "FTBFS on architectures not supported upstream": (Closes: #622800)
++    + Don't execute regression checks for now (fixes x86)
++    + Upstream merges a Debian patch originally for 2.1.7 which enables
++      kFreeBSD support (originally provided by myself)
++    + Restrict Architectures (drop S390, IA64, MIPS[EL], PPC, SPARC)
++  * Remove ts-ui-disable-conf.patch (applied upstream)
++  * Update `build-quirks.patch' to make TS handle $DESTDIR correctly
++    (upstream: TS-759)
++  * Remove .deps from SDK binary package examples (they were incidentally
++    included before).
++  * Fix permissions for /var/cache/trafficserver in postinst
++  * Bump standards to 3.9.2, depend on debhelper 8.0, adapt VCS links
++  * Simplify debian/rules
++
++ -- Arno Töll <debian@toell.net>  Thu, 05 May 2011 21:49:52 +0200
++
++trafficserver (2.1.5-unstable-1) unstable; urgency=low
++
++  * Initial release (Closes: #609285)
++  * Added some configuration and informational hints
++  * New upstream release. Major features:
++    + Better AMD64 support
++    + Override configuration per transaction
++    + IPv6
++    + Support ARM architectures
++    + SDK-API changes
++  * Differences to upstream version:
++    + Ship some documentation. Well, really a few hints
++    + Split source into three packages (core, plug-in, SDK)
++    + Ship our own init script
++
++ -- Arno Toell <debian@toell.net>  Tue, 13 Jan 2011 11:49:18 +0100
diff --cc debian/compat
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b4de3947675361a7770d29b8982c407b0ec6b2a0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++11
diff --cc debian/control
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2765cb093bfb2762bc577cea9b12561614677f42
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++Source: trafficserver
++Section: web
++Priority: optional
++Maintainer: Aron Xu <aron@debian.org>
++Uploaders: Jean Baptiste Favre <debian@jbfavre.org>
++Build-Depends: debhelper (>= 11), libssl-dev, tcl-dev, libexpat1-dev,
++  libpcre3-dev, libtool, libcap-dev [linux-any], graphviz,
++  bison [linux-any], flex [linux-any], dpkg-dev (>= 1.16.1~), pkg-config, libgeoip-dev,
++  libluajit-5.1-dev, libboost-dev, libhwloc-dev, default-libmysqlclient-dev,
++  python3-sphinx [!kfreebsd-any], python-sphinx [kfreebsd-any], plantuml,
++  python3-sphinxcontrib.plantuml [!kfreebsd-any], python-sphinxcontrib.plantuml [kfreebsd-any],
++  libxml2-dev, libncurses-dev, libcurl4-openssl-dev,
++  libkyotocabinet-dev, libmemcached-dev, libbrotli-dev,
++  libcrypto++-dev, libjansson-dev, libcjose-dev, libyaml-cpp-dev (>= 0.6.2~),
++  libunwind-dev [i386 amd64 ppc64el armhf arm64 mipsel mips64el mips]
++Standards-Version: 4.3.0
++Homepage: http://trafficserver.apache.org/
++Vcs-Git: https://salsa.debian.org/debian/trafficserver.git
++Vcs-Browser: https://salsa.debian.org/debian/trafficserver
++
++Package: trafficserver
++Architecture: any
++Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base (>= 3.2-14), adduser,
++  ${perl:Depends}
++Provides: trafficserver-plugin-conf-remap
++Replaces: trafficserver-plugin-conf-remap (<< 3.2~),
++ trafficserver-experimental-plugins (<< 8.0.0~)
++Suggests: trafficserver-experimental-plugins (= ${binary:Version})
++Breaks: trafficserver-plugin-conf-remap (<< 3.2~),
++ trafficserver-experimental-plugins (<< 8.0.0~)
++Description: fast, scalable and extensible HTTP/1.1 and HTTP/2.0 caching proxy server
++ This package provides the Apache Traffic Server, a fast, scalable reverse
++ proxy server which may operate as forward proxy as well. Apache Traffic Server
++ supports:
++ .
++   * Caching: improve response time while reducing server load and bandwidth
++     needs by caching and reusing frequently-requested web pages, images, and
++     web service calls.
++   * Proxying: add keep-alive, filter or anonymize content requests, or add
++     load balancing by adding a proxy layer.
++   * Scaling: handle 10s of thousands of requests per second on modern SMP
++     hardware.
++   * Extensions: use the API to do anything from modifying HTTP headers to
++     handling ESI requests to writing your own cache algorithm.
++
++Package: trafficserver-experimental-plugins
++Architecture: any
++Depends: ${shlibs:Depends}, ${misc:Depends}
++Pre-Depends: trafficserver (= ${binary:Version})
++Breaks: trafficserver (<< 8.0.0~)
++Description: experimental plugins for Apache Traffic Server
++ This package provides the Apache Traffic Server plugins marked as
++ experimental.
++ .
++ Please note that these plugins can be removed without priori notice, or
++ promoted as stable plugin. In the last case, they'll be moved from
++ trafficserver-experimental-plugins to trafficserver package.
++
++Package: trafficserver-dev
++Architecture: any
++Depends: ${misc:Depends}, trafficserver (= ${binary:Version})
++Description: Apache Traffic Server Software Developers Kit (SDK)
++ This package provides the Apache Traffic Server Software Developers Kit, which
++ consists of: a collection of development header and bindings for the C
++ programming language, the tsxs linking helper and examples to write your own
++ plug-ins for the Apache Traffic Server.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eddb9830339367d2d7529f100e97da745c033c35
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
++Upstream-Name: Traffic Server
++Upstream-Contact: dev@trafficserver.apache.org
++Source: http://trafficserver.apache.org/
++Files-Excluded:
++    .vscode
++    lib/yamlcpp
++Comment:
++    The upstream repository provides libraries which are also available
++    in Debian. Because we'll use libraries packaged in Debian, we don't
++    need embedded ones.
++
++Files: *
++Copyright: 2010 - 2011 The Apache Software Foundation
++           2009 Yahoo! Inc.
++License: Apache-2.0
++
++Files: debian/*
++Copyright: 2011 Arno Töll
++License: Apache-2.0
++Comment: Packaging for Debian was done by Arno Toell, and I hereby grant
++  distribution of it under the same terms as Apache Traffic Server itself.
++
++License: Apache-2.0
++   Licensed under the Apache License, Version 2.0 (the "License");
++   you may not use this file except in compliance with the License.
++   You may obtain a copy of the License at
++   .
++       http://www.apache.org/licenses/LICENSE-2.0
++   .
++   Unless required by applicable law or agreed to in writing, software
++   distributed under the License is distributed on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++   See the License for the specific language governing permissions and
++   limitations under the License.
++   .
++   On Debian systems, the full text of the e Apache License, Version 2.0
++   can be found in the file `/usr/share/common-licenses/Apache-2.0'.
++
++Files: include/tscore/ink_rand.*
++Copyright: 1997 - 2002, Makoto Matsumoto and Takuji Nishimura
++License: BSD-3-clause
++
++Files: include/tscore/ink_resolver.h src/tscore/ink_res_init.cc src/tscore/ink_res_mkquery.cc
++Copyright:  1983, 1987, 1989  The Regents of the University of California
++License: BSD-3-clause
++
++License: BSD-3-clause
++ For the University of California part:
++ .
++ Redistribution and use in source and binary forms, with or without
++ modification, are permitted provided that the following conditions
++ are met:
++ 1. Redistributions of source code must retain the above copyright
++    notice, this list of conditions and the following disclaimer.
++ 2. Redistributions in binary form must reproduce the above copyright
++    notice, this list of conditions and the following disclaimer in the
++    documentation and/or other materials provided with the distribution.
++ 3. Neither the name of the University nor the names of its contributors
++    may be used to endorse or promote products derived from this software
++    without specific prior written permission.
++ .
++ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ SUCH DAMAGE.
++ .
++ For the Internet Systems Consortium, Inc. part:
++ See Apache-2.0
++ .
++ For the Digital Equipment Corporation part:
++ Permission to use, copy, modify, and distribute this software for any
++ purpose with or without fee is hereby granted, provided that the above
++ copyright notice and this permission notice appear in all copies, and that
++ the name of Digital Equipment Corporation not be used in advertising or
++ publicity pertaining to distribution of the document or software without
++ specific, written prior permission.
++ .
++ THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
++ WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
++ OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
++ CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
++ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
++ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
++ ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
++ SOFTWARE.
++
++Files: src/tscore/ink_string.cc
++Comment: For the strlcat, strlcpy in inktomi++/ink_string.cc
++Copyright:  1998 Todd C. Miller <Todd.Miller@courtesan.com>
++License: ISC
++ Permission to use, copy, modify, and distribute this software for any
++ purpose with or without fee is hereby granted, provided that the above
++ copyright notice and this permission notice appear in all copies.
++ .
++ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++Files: include/tscore/fastlz.h src/tscore/fastlz.c
++Copyright: 2005-2007 Ariya Hidayat (ariya@kde.org)
++License: Expat
++ Permission is hereby granted, free of charge, to any person obtaining a copy
++ of this software and associated documentation files (the "Software"), to deal
++ in the Software without restriction, including without limitation the rights
++ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ copies of the Software, and to permit persons to whom the Software is
++ furnished to do so, subject to the following conditions:
++ .
++ The above copyright notice and this permission notice shall be included in
++ all copies or substantial portions of the Software.
++ .
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ THE SOFTWARE.
diff --cc debian/docs
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..356cb60e49dfe2d3c9de786b32581af4174d4c20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++LAYOUT
++NOTICE
++STATUS
++REVIEWERS
++README
++README-EC2
++debian/README.Debian
++debian/CONFIGURATION.Debian
++debian/README.conf-remap.Debian
diff --cc debian/gbp.conf
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b0d19e7ade3380a5bad8ebaddee7f7f3f8f93ba2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++[DEFAULT]
++pristine-tar = True
++builder=dpkg-buildpackage -i\.git -I.git
++#cleaner=true
++
++[import-orig]
++filter = [ '.gitignore', '.git', '.vscode' ]
++merge = True
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a4e7143ec727f27b0f5f8aa873b05b68c0fc857
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,102 @@@
++stages:
++  - build
++  - lintian
++  - piuparts
++#  - reprotest
++
++.build: &build
++  before_script:
++    - apt-get update
++    - apt-get -qy upgrade
++    - apt-get -qy install devscripts autoconf automake adduser fakeroot sudo
++    - mk-build-deps -t "apt-get -y -o Debug::pkgProblemResolver=yes --no-install-recommends" -i -r
++    - adduser --disabled-password --gecos "" builduser
++    - chown -R builduser:builduser .
++    - chown builduser:builduser ..
++  stage: build
++  script:
++    - sudo -u builduser dpkg-buildpackage -b -rfakeroot
++
++.lintian: &lintian
++  before_script:
++    - apt-get update
++    - apt-get -y install lintian
++  stage: lintian
++  script:
++    - echo "performing general and experimental lintian checks"
++    - lintian --color always -EviIL +pedantic built/*.changes || true
++    - echo "performing ftp-master-rejects lintian checks"
++    - lintian --color always -viF built/*.changes
++
++.piuparts: &piuparts
++  stage: piuparts
++  services:
++    - docker:dind
++  before_script:
++    - export CHROOT_PATH=/tmp/debian-sid
++    - CONTAINER_ID=$(docker run --rm -d debian:sid sleep infinity)
++    - docker exec ${CONTAINER_ID} bash -c "apt-get update && apt-get -qy dist-upgrade && apt-get -qy install eatmydata"
++    - mkdir -p ${CHROOT_PATH}
++    - docker export ${CONTAINER_ID} | tar -C ${CHROOT_PATH} -xf -
++    - mknod -m 666 ${CHROOT_PATH}/dev/urandom c 1 9
++  artifacts:
++    name: "$CI_BUILD_NAME"
++    expire_in: 180 day
++    paths:
++      - ./piuparts.log
++    when: always
++  script:
++    - piuparts --hard-link -e ${CHROOT_PATH} -l piuparts.log built/*.changes
++
++#.reprotest: &reprotest
++#  stage: reprotest
++#  before_script:
++#    - apt-get update
++#    - apt-get -qy dist-upgrade
++#    - eatmydata apt-get build-dep -y .
++#  artifacts:
++#    name: "$CI_BUILD_NAME"
++#    expire_in: 180 day
++#    paths:
++#      - ./reprotest.log
++#    when: always
++#  script:
++#    - export DEB_BUILD_OPTIONS=nocheck
++#    - eatmydata reprotest --min-cpus $(nproc --all) . -- null &> reprotest.log
++
++### End of generic stage declaration
++
++#build:testing:
++#  <<: *build
++#  image: debian:testing
++
++build:unstable:
++  <<: *build
++  image: debian:sid
++  artifacts:
++    name: "$CI_BUILD_NAME"
++    expire_in: 180 day
++    paths:
++      - built
++    when: always
++  after_script:
++    - mkdir built
++    - dcmd mv ../*.changes built/
++
++lintian:unstable:
++  <<: *lintian
++  dependencies:
++    - build:unstable
++  image: debian:sid
++
++piuparts:unstable:
++  <<: *piuparts
++  dependencies:
++    - build:unstable
++  image: registry.salsa.debian.org/salsa-ci-team/images/piuparts
++
++#reprotest:unstable:
++#  <<: *reprotest
++#  dependencies:
++#    - build:unstable
++#  image: registry.salsa.debian.org/salsa-ci-team/images/reprotest
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b97cd0e718b887f3f615e1e291702e830af84cbb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++usr/man/man3
++usr/share/man
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4852f89c40912e3196a9fab2179b5f6e4929702f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++From: Aron Xu <aron@debian.org>
++Date: Tue, 2 Feb 2016 12:17:24 +0800
++Subject: Use -mcx16 on x86 platforms only
++
++---
++ configure.ac | 3 +++
++ 1 file changed, 3 insertions(+)
++
++--- a/configure.ac
+++++ b/configure.ac
++@@ -1495,12 +1495,15 @@ CFLAGS="${__saved_CFLAGS}"
++ AC_LANG_POP
++ AC_SUBST(has_128bit_cas)
++ 
+++case "$host_cpu" in
+++    amd64|x86_64|i*86)
++ AS_IF([test "x$has_128bit_cas" = "x1"], [
++   AS_IF([test "x$ax_cv_c_compiler_vendor" != "xintel"], [
++     TS_ADDTO(AM_CFLAGS, [-mcx16])
++     TS_ADDTO(AM_CXXFLAGS, [-mcx16])
++   ])
++ ])
+++esac
++ 
++ # Check for POSIX capabilities library.
++ # If we don't find it, disable checking for header.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc2cc8d90c66dafbf4fe13b0e56d8f8d098ea494
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++Description: make the build reproducible
++Author: Reiner Herrmann <reiner@reiner-h.de>
++Origin: other, https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833176
++Reviewed-by: Jean Baptiste Favre <debian@jbfavre.org>
++Last-Update: 2016-11-18
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/configure.ac
+++++ b/configure.ac
++@@ -144,9 +144,9 @@ AC_ARG_WITH([build-number],
++ #
++ # Build environment
++ #
++-build_person="`id -nu | sed -e 's/\\\\/\\\\\\\\/g'`"
++-build_group="`id -ng | sed -e 's/\\\\/\\\\\\\\/g'`"
++-build_machine="`uname -n | sed -e 's/\\\\/\\\\\\\\/g'`"
+++build_person="root"
+++build_group="root"
+++build_machine="localhost"
++ AC_SUBST([build_machine])
++ AC_SUBST([build_person])
++ AC_SUBST([build_group])
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29e76561b8225deb267efe761f25537545f76810
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++Description: Make documentation build works outside of git repository
++ Current documentation build requires git and curl to get some stuff from the internet
++ This patch aims to delete those dependencies, forcing git branch to master,
++ and using Debian provided plantuml instead of downloading it from apache mirror
++Author: Jean Baptiste Favre <debian@jbfavre.org>
++Origin: other
++Last-Update: 2019-01-03
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/doc/ext/traffic-server.py
+++++ b/doc/ext/traffic-server.py
++@@ -380,7 +380,7 @@ with open('../configure.ac', 'r') as f:
++     autoconf_version = '.'.join(match.group(1).split('.', 2)[:2] + ['x'])
++ 
++ # get the current branch the local repository is on
++-git_branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
+++git_branch = "master"
++ 
++ 
++ def make_github_link(name, rawtext, text, lineno, inliner, options={}, content=[]):
++--- a/doc/Makefile.am
+++++ b/doc/Makefile.am
++@@ -61,7 +61,7 @@ I18NSPHINXOPTS  = $(SPHINXOPTS)
++ # The PAPER setting variables requires recursive make variable expansion, which automake
++ # detects as non-portable. We bounce this through a shell script and do the expansion there.
++ SBUILD = PAPEROPT_a4="$(PAPEROPT_a4)" PAPEROPT_letter="$(PAPEROPT_letter)" PAPER="$(PAPER)" \
++-     PLANTUML_JAR="$(shell ext/plantuml_fetch.sh | tail -1)" \
+++     PLANTUML_JAR="/usr/share/plantuml/plantuml.jar" \
++      $(srcdir)/sbuild $(SPHINXBUILD) \
++      -c $(srcdir) \
++      $(ALLSPHINXOPTS)
++--- a/doc/uml/Makefile.am
+++++ b/doc/uml/Makefile.am
++@@ -18,7 +18,7 @@
++ 
++ if BUILD_DOCS
++ images := $(patsubst %.uml,images/%.svg,$(wildcard *.uml))
++-PLANTUML_JAR := $(shell ../ext/plantuml_fetch.sh | tail -1)
+++PLANTUML_JAR := /usr/share/plantuml/plantuml.jar
++ 
++ all-am: jar-check $(images)
++ endif
++@@ -39,7 +39,7 @@ latex: all-am
++ man: all-am
++ 
++ images/%.svg : %.uml
++-     $(JAVA) -jar $(PLANTUML_JAR) -o images -tsvg $<
+++     $(JAVA) -jar $(PLANTUML_JAR) -graphvizdot /usr/bin/dot -o images -tsvg $<
++ 
++ clean-local:
++      rm -f images/*.svg
++--- a/doc/manpages.py
+++++ b/doc/manpages.py
++@@ -33,6 +33,9 @@ man_pages = [
++     ('appendices/command-line/traffic_top.en', 'traffic_top', u'Display Traffic Server statistics', None, '1'),
++     ('appendices/command-line/tsxs.en', 'tsxs', u'Traffic Server plugin tool', None, '1'),
++     ('appendices/command-line/traffic_via.en', 'traffic_via', u'Traffic Server Via header decoder', None, '1'),
+++    ('appendices/command-line/traffic_layout.en', 'traffic_layout', u'Traffic Server sandbox management tool', None, '1'),
+++    ('appendices/command-line/traffic_cache_tool.en', 'traffic_cache_tool', u'Traffic Server cache management tool', None, '1'),
+++    ('appendices/command-line/traffic_wccp.en', 'traffic_wccp', u'Traffic Server WCCP client', None, '1'),
++ 
++     ('admin-guide/files/cache.config.en', 'cache.config', u'Traffic Server cache configuration file', None, '5'),
++     ('admin-guide/files/hosting.config.en', 'hosting.config', u'Traffic Server domain hosting configuration file', None, '5'),
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ff1000041130ba63df3a75d7862249203639bd5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++Description: Force python3 usage, add libfakeroot-sysv to blacklist
++Author: Jean Baptiste Favre <debian@jbfavre.org>
++Origin: other
++Last-Update: 2018-09-24
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/tools/check-unused-dependencies
+++++ b/tools/check-unused-dependencies
++@@ -1,4 +1,4 @@
++-#!/usr/bin/env python
+++#!/usr/bin/env python3
++ 
++ # Licensed to the Apache Software Foundation (ASF) under one or more
++ # contributor license agreements. See the NOTICE file distributed with
++@@ -53,7 +53,8 @@ def get_dependencies(program):
++                     'libdl.so.',      # Because we add -ldl to LIBS
++                     'libgcc_s.so.',
++                     'libm.so.',       # Why does Libtool call ld with -lm?
++-                    'libpthread.so.'  # Because we add -lpthread to LIBS
+++                    'libpthread.so.',  # Because we add -lpthread to LIBS
+++                    'libfakeroot-sysv.so'
++                 ])):
++             continue
++ 
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b2615e37220680dcb8fa2c0adc80acc6d399e215
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++Description: Fix build issue with MySQL 8
++ The my_bool type is no longer used in MySQL source code.
++ Any third-party code that used this type to represent C
++ boolean variables should use the bool or int C type instead.
++Author: Jean Baptiste Favre <debian@jbfavre.org>
++Origin: other, https://bugs.launchpad.net/ubuntu/+source/trafficserver/+bug/1795362
++Forwarded: https://github.com/apache/trafficserver/pull/4360
++Applied-Upstream: https://github.com/apache/trafficserver/commit/05b30527974416768515506f69da338652c23260
++Reviewed-by: Jean Baptiste Favre <debian@jbfavre.org>
++Last-Update: 2018-10-06
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/plugins/experimental/mysql_remap/mysql_remap.cc
+++++ b/plugins/experimental/mysql_remap/mysql_remap.cc
++@@ -187,7 +187,7 @@ TSPluginInit(int argc, const char *argv[
++   my_data *data = (my_data *)malloc(1 * sizeof(my_data));
++ 
++   TSPluginRegistrationInfo info;
++-  my_bool reconnect = 1;
+++  bool reconnect = 1;
++ 
++   info.plugin_name   = const_cast<char *>(PLUGIN_NAME);
++   info.vendor_name   = const_cast<char *>("Apache Software Foundation");
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d3db7fff96b0ae18e819860ab84d04eb362970f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++Description: Fix Segmentation fault in ShowCache::handleCacheEvent
++Author: zhang <15535135608@163.com>
++Origin: upstream
++Bug: https://github.com/apache/trafficserver/issues/4328
++Applied-Upstream: https://github.com/apache/trafficserver/commit/616eb10bfc35599a2c93ff30879d584a05ddf83e
++Reviewed-by: Jean Baptiste Favre <debian@jbfavre.org>
++Last-Update: 2018-10-17
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/iocore/cache/CachePages.cc
+++++ b/iocore/cache/CachePages.cc
++@@ -337,6 +337,13 @@ ShowCache::handleCacheEvent(int event, E
++     CacheHTTPInfoVector *vec = &(cache_vc->vector);
++     int alt_count            = vec->count();
++     if (alt_count) {
+++      // check cache_vc->first_buf is NULL, response cache lookup busy.
+++      if (cache_vc->first_buf == nullptr) {
+++        cache_vc->do_io_close(-1);
+++        CHECK_SHOW(show("<H3>Cache Lookup Busy, please try again</H3>\n"));
+++        return complete(event, e);
+++      }
+++
++       Doc *d = (Doc *)(cache_vc->first_buf->data());
++       time_t t;
++       char tmpstr[4096];
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..db36cc6ced252be792cbbf2c79bbcb4365a067ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,867 @@@
++Description: Fix various speeling issues
++Author: Jean Baptiste Favre <debian@jbfavre.org>
++Forwarded: https://github.com/apache/trafficserver/pull/4750
++Applied-Upstream: https://github.com/apache/trafficserver/commit/af0ad4a1880a21743e98331855bb78e15d5406ef
++Last-Update: 2019-01-03
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/doc/admin-guide/files/ip_allow.config.en.rst
+++++ b/doc/admin-guide/files/ip_allow.config.en.rst
++@@ -55,7 +55,7 @@ range with the lower and upper values eq
++ The value of ``method`` is a string which must consist of either HTTP method names separated by the
++ character '|' or the keyword literal ``ALL``. This keyword may omitted in which case it is treated
++ as if it were ``method=ALL``. Methods can also be specified by having multiple instances of the
++-``method`` keyword, each specifiying a single method. E.g., ``method=GET|HEAD`` is the same as
+++``method`` keyword, each specifying a single method. E.g., ``method=GET|HEAD`` is the same as
++ ``method=GET method=HEAD``. The method names are not validated which means non-standard method names
++ can be specified.
++ 
++@@ -104,7 +104,7 @@ If the entire subnet were to be denied,
++ 
++    src_ip=123.45.6.0/24 action=ip_deny
++ 
++-The following example allows to any upstream servers::
+++The following example allows one to any upstream servers::
++ 
++    dest_ip=0.0.0.0-255.255.255.255 action=ip_allow
++ 
++--- a/doc/admin-guide/files/parent.config.en.rst
+++++ b/doc/admin-guide/files/parent.config.en.rst
++@@ -189,7 +189,7 @@ The following list shows the possible ac
++     - ``simple_retry`` - If the parent origin server returns a 404 response on a request
++       a new parent is selected and the request is retried.  The number of retries is controlled
++       by ``max_simple_retries`` which is set to 1 by default.
++-    - ``unavailable_server_retry`` - If the parent returns a 503 response or if the reponse matches
+++    - ``unavailable_server_retry`` - If the parent returns a 503 response or if the response matches
++       a list of http 5xx responses defined in ``unavailable_server_retry_responses``, the currently selected
++       parent is marked down and a new parent is selected to retry the request.  The number of
++       retries is controlled by ``max_unavailable_server_retries`` which is set to 1 by default.
++@@ -207,7 +207,7 @@ The following list shows the possible ac
++ 
++ ``max_simple_retries``
++   By default the value for ``max_simple_retries`` is 1.  It may be set to any value in the range 1 to 5.
++-  If ``parent_retry`` is set to ``simple_retry`` or ``both`` a 404 reponse
+++  If ``parent_retry`` is set to ``simple_retry`` or ``both`` a 404 response
++   from a parent origin server will cause the request to be retried using a new parent at most 1 to 5
++   times as configured by ``max_simple_retries``.
++ 
++@@ -215,7 +215,7 @@ The following list shows the possible ac
++ 
++ ``max_unavailable_server_retries``
++   By default the value for ``max_unavailable_server_retries`` is 1.  It may be set to any value in the range 1 to 5.
++-  If ``parent_retry`` is set to ``unavailable_server_retries`` or ``both`` a 503 reponse
+++  If ``parent_retry`` is set to ``unavailable_server_retries`` or ``both`` a 503 response
++   by default or any http 5xx response listed in the list ``unavailable_server_retry_responses`` from a parent origin server will
++   cause the request to be retried using a new parent after first marking the current parent down.  The request
++   will be retried at most 1 to 5 times as configured by ``max_unavailable_server_retries``.
++--- a/doc/admin-guide/files/records.config.en.rst
+++++ b/doc/admin-guide/files/records.config.en.rst
++@@ -802,7 +802,7 @@ ip-resolve
++    ===== ======================================================================
++    Value Description
++    ===== ======================================================================
++-   ``0`` |TS| will buffer the request until the post body has been recieved and
+++   ``0`` |TS| will buffer the request until the post body has been received and
++          then send the request to the origin server.
++    ``1`` Immediately return a ``100 Continue`` from |TS| without waiting for
++          the post body.
++@@ -1608,7 +1608,7 @@ Proxy User Variables
++    connection=full     Full user agent connection :ref:`protocol tags <protocol_tags>`
++    ==================  ===============================================================
++ 
++-   Each paramater in the list must be separated by ``|`` or ``:``.  For example, ``for|by=uuid|proto`` is
+++   Each parameter in the list must be separated by ``|`` or ``:``.  For example, ``for|by=uuid|proto`` is
++    a valid value for this variable.  Note that the ``connection`` parameter is a non-standard extension to
++    RFC 7239.  Also note that, while Traffic Server allows multiple ``by`` parameters for the same proxy, this
++    is prohibited by RFC 7239. Currently, for the ``host`` parameter to provide the original host from the
++@@ -1636,7 +1636,7 @@ Proxy User Variables
++        information.
++        See :ts:cv:`proxy.config.http.server_ports` for information on how to enable Proxy Protocol on a port.
++ 
++-   See :ref:`proxy-protocol` for more discussion on how |TS| tranforms the `Forwarded: header.
+++   See :ref:`proxy-protocol` for more discussion on how |TS| transforms the `Forwarded: header.
++ 
++ .. ts:cv:: CONFIG proxy.config.http.normalize_ae INT 1
++    :reloadable:
++@@ -2386,7 +2386,7 @@ DNS
++ 
++ .. ts:cv:: CONFIG proxy.config.dns.resolv_conf STRING /etc/resolv.conf
++ 
++-   Allows to specify which ``resolv.conf`` file to use for finding resolvers. While the format of this file must be the same as the
+++   Allows one to specify which ``resolv.conf`` file to use for finding resolvers. While the format of this file must be the same as the
++    standard ``resolv.conf`` file, this option allows an administrator to manage the set of resolvers in an external configuration file,
++    without affecting how the rest of the operating system uses DNS.
++ 
++@@ -2404,7 +2404,7 @@ DNS
++    :reloadable:
++    :overridable:
++ 
++-   Indicates whether to use SRV records for orgin server lookup.
+++   Indicates whether to use SRV records for origin server lookup.
++ 
++ .. ts:cv:: CONFIG proxy.config.dns.dedicated_thread INT 0
++ 
++@@ -2637,7 +2637,7 @@ HostDB
++    Set the frequency (in seconds) to sync hostdb to disk.
++ 
++    Note: hostdb is syncd to disk on a per-partition basis (of which there are 64).
++-   This means that the minumum time to sync all data to disk is :ts:cv:`proxy.config.cache.hostdb.sync_frequency` * 64
+++   This means that the minimum time to sync all data to disk is :ts:cv:`proxy.config.cache.hostdb.sync_frequency` * 64
++ 
++ Logging Configuration
++ =====================
++@@ -2951,7 +2951,7 @@ Diagnostic Logging Configuration
++ 
++ .. ts:cv:: CONFIG proxy.config.diags.debug.tags STRING http|dns
++ 
++-   Each |TS| `diag` and `debug` level message is annotated with a subsytem tag.  This configuration
+++   Each |TS| `diag` and `debug` level message is annotated with a subsystem tag.  This configuration
++    contains an anchored regular expression that filters the messages based on the tag. The
++    expressions are prefix matched which creates an implicit ``.*`` at the end. Therefore the default
++    value ``http|dns`` will match tags such as ``http``, ``http_hdrs``, ``dns``, and ``dns_recv``.
++@@ -2959,7 +2959,7 @@ Diagnostic Logging Configuration
++    Some commonly used debug tags are:
++ 
++    ============  =====================================================
++-   Tag           Subsytem usage
+++   Tag           Subsystem usage
++    ============  =====================================================
++    dns           DNS query resolution
++    http_hdrs     Logs the headers for HTTP requests and responses
++@@ -3115,7 +3115,7 @@ SSL Termination
++ .. ts:cv:: CONFIG proxy.config.ssl.client.groups_list STRING <See notes under proxy.config.ssl.server.groups_list.>
++ 
++    Configures the list of supported groups provided by OpenSSL which
++-   |TS| will use for the "key_share" and "supported groups" extention
+++   |TS| will use for the "key_share" and "supported groups" extension
++    of TLSv1.3 connections. The value is a colon separated list of
++    group NIDs or names, for example "P-521:P-384:P-256". For
++    instructions, see "Groups" section of `TLS1.3 - OpenSSLWiki <https://wiki.openssl.org/index.php/TLS1.3#Groups>`_.
++@@ -3254,7 +3254,7 @@ SSL Termination
++    ``0`` Disables the session cache entirely.
++    ``1`` Enables the session cache using OpenSSL's implementation.
++    ``2`` Default. Enables the session cache using |TS|'s implementation. This
++-         implentation should perform much better than the OpenSSL
+++         implementation should perform much better than the OpenSSL
++          implementation.
++    ===== ======================================================================
++ 
++--- a/doc/admin-guide/files/remap.config.en.rst
+++++ b/doc/admin-guide/files/remap.config.en.rst
++@@ -415,7 +415,7 @@ Acl Filters
++ 
++ Acl filters can be created to control access of specific remap lines. The markup
++ is very similar to that of :file:`ip_allow.config`, with slight changes to
++-accomodate remap markup
+++accommodate remap markup
++ 
++ Examples
++ --------
++--- a/doc/admin-guide/files/storage.config.en.rst
+++++ b/doc/admin-guide/files/storage.config.en.rst
++@@ -92,7 +92,7 @@ which will effectively clear most of the
++ reboot causes the path names to change.
++ 
++ The :arg:`id` option can be used to create a fixed string that an administrator can use to keep the
++-assignment table consistent by maintaing the mapping from physical device to base string even in the presence of hardware changes and failures.
+++assignment table consistent by maintaining the mapping from physical device to base string even in the presence of hardware changes and failures.
++ 
++ Examples
++ ========
++--- a/doc/appendices/command-line/traffic_ctl.en.rst
+++++ b/doc/appendices/command-line/traffic_ctl.en.rst
++@@ -255,7 +255,7 @@ traffic_ctl host
++ .. program:: traffic_ctl host
++ .. option:: status HOSTNAME [HOSTNAME ...]
++ 
++-    Get the current status of the hosts used in parent.config as a next hop in a multi-tiered cache heirarchy.  The value 0 or 1 is returned indicating that the host is marked as down '0' or marked as up '1'.  If a host is marked as down, it will not be used as the next hop parent, another host marked as up will be chosen.
+++    Get the current status of the hosts used in parent.config as a next hop in a multi-tiered cache hierarchy.  The value 0 or 1 is returned indicating that the host is marked as down '0' or marked as up '1'.  If a host is marked as down, it will not be used as the next hop parent, another host marked as up will be chosen.
++ 
++ .. program:: traffic_ctl host
++ .. option:: down --time seconds HOSTNAME [HOSTNAME ...]
++--- a/doc/developer-guide/api/functions/TSCacheRemove.en.rst
+++++ b/doc/developer-guide/api/functions/TSCacheRemove.en.rst
++@@ -41,4 +41,4 @@ the cache calls :arg:`contp` back with t
++ In both of these callbacks, the user (:arg:`contp`) does not have to do
++ anything.  The user does not get any vconnection from the cache, since
++ no data needs to be transferred.  When the cache calls :arg:`contp` back with
++-:data:`TS_EVENT_CACHE_REMOVE`, the remove has already been commited.
+++:data:`TS_EVENT_CACHE_REMOVE`, the remove has already been committed.
++--- a/doc/developer-guide/api/functions/TSContSchedule.en.rst
+++++ b/doc/developer-guide/api/functions/TSContSchedule.en.rst
++@@ -32,7 +32,7 @@ Description
++ ===========
++ 
++ Schedules :arg:`contp` to run :arg:`delay` milliseconds in the future. This is approximate. The delay
++-will be at least :arg:`delay` but possibly more. Resultions finer than roughly 5 milliseconds will
+++will be at least :arg:`delay` but possibly more. Resolutions finer than roughly 5 milliseconds will
++ not be effective. :arg:`contp` is required to have a mutex, which is provided to
++ :func:`TSContCreate`.
++ 
++--- a/doc/developer-guide/api/functions/TSHttpConnectWithPluginId.en.rst
+++++ b/doc/developer-guide/api/functions/TSHttpConnectWithPluginId.en.rst
++@@ -79,7 +79,7 @@ virtual connection.
++ 
++ The combination of :arg:`tag` and :arg:`id` is intended to enable correlation
++ in log post processing. The :arg:`tag` identifies the connection as related
++-to the plugin and the :arg:`id` can be used in conjuction with plugin
+++to the plugin and the :arg:`id` can be used in conjunction with plugin
++ generated logs to correlate the log records.
++ 
++ Notes
++--- a/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst
+++++ b/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst
++@@ -43,7 +43,7 @@ Description
++ 
++ Some of the values that are set in :file:`records.config` can be changed for a
++ specific transaction. It is important to note that these functions change the
++-configuration values stored for the transation, which is not quite the same as
+++configuration values stored for the transaction, which is not quite the same as
++ changing the actual operating values of the transaction. The critical effect is
++ the value must be changed before it is used by the transaction - after that,
++ changes will not have any effect.
++--- a/doc/developer-guide/api/functions/TSHttpTxnErrorBodySet.en.rst
+++++ b/doc/developer-guide/api/functions/TSHttpTxnErrorBodySet.en.rst
++@@ -36,4 +36,4 @@ Description
++ Note that both string arguments must be allocated with :c:func:`TSmalloc` or
++ :c:func:`TSstrdup`.  The :arg:`mimetype` is optional, and if not provided it
++ defaults to :literal:`text/html`. Sending an empty string would prevent setting
++-a content type header (but that is not adviced).
+++a content type header (but that is not advised).
++--- a/doc/developer-guide/api/functions/TSHttpTxnMilestoneGet.en.rst
+++++ b/doc/developer-guide/api/functions/TSHttpTxnMilestoneGet.en.rst
++@@ -138,7 +138,7 @@ is successful.
++ 
++      .. macro:: TS_MILESTONE_LAST_ENTRY
++ 
++-             A psuedo index which is set to be one more than the last valid index. This is useful for looping over the data.
+++             A pseudo index which is set to be one more than the last valid index. This is useful for looping over the data.
++ 
++ 
++ *  The server connect times predate the transmission of the :literal:`SYN`
++--- a/doc/developer-guide/api/functions/TSHttpTxnServerIntercept.en.rst
+++++ b/doc/developer-guide/api/functions/TSHttpTxnServerIntercept.en.rst
++@@ -53,7 +53,7 @@ The response from the plugin is cached s
++ caching rules. Should the plugin wish the response not be cached, the plugin
++ must use appropriate HTTP response headers to prevent caching. The primary
++ purpose of :func:`TSHttpTxnServerIntercept` is allow plugins to provide gateways
++-to other protocols or to allow to plugin to its own transport for the next hop
+++to other protocols or to allow one to plugin to its own transport for the next hop
++ to the server. :func:`TSHttpTxnServerIntercept` overrides parent cache
++ configuration.
++ 
++--- a/doc/developer-guide/api/functions/TSIOBufferReader.en.rst
+++++ b/doc/developer-guide/api/functions/TSIOBufferReader.en.rst
++@@ -59,7 +59,7 @@ has two very important consequences --
++ *  Conversely keeping a reader around unused will pin the buffer data in memory. This can be useful or harmful.
++ 
++ A buffer has a fixed amount of possible readers (currently 5) which is determined at compile
++-time. Reader allocation is fast and cheap until this maxium is reached at which point it fails.
+++time. Reader allocation is fast and cheap until this maximum is reached at which point it fails.
++ 
++ :func:`TSIOBufferReaderAlloc` allocates a reader for the IO buffer :arg:`bufp`. This should only be
++       called on a newly allocated buffer. If not the location of the reader in the buffer will be
++--- a/doc/developer-guide/api/functions/TSSslContext.en.rst
+++++ b/doc/developer-guide/api/functions/TSSslContext.en.rst
++@@ -36,11 +36,11 @@ Description
++ ===========
++ 
++ :func:`TSSslContextFindByName` searches for a SSL server context
++-created from :file:`ssl_multicert.config`, matching against the
+++created from :file:`ssl_multicert.config`, matchingg against the
++ server :arg:`name`.
++ 
++ :func:`TSSslContextFindByAddr` searches for a SSL server context
++-created from :file:`ssl_multicert.config` matchin against the server
+++created from :file:`ssl_multicert.config` matching against the server
++ :arg:`address`.
++ 
++ 
++--- a/doc/developer-guide/api/functions/TSSslSession.en.rst
+++++ b/doc/developer-guide/api/functions/TSSslSession.en.rst
++@@ -46,7 +46,7 @@ The functions also work with the :type:`
++ 
++ These functions perform the appropriate locking on the session cache to avoid errors.
++ 
++-The :func:`TSSslSessionGet` and :func:`TSSslSessionGetBuffer` functions retreive the :type:`TSSslSession` object that is identifed by the
+++The :func:`TSSslSessionGet` and :func:`TSSslSessionGetBuffer` functions retrieve the :type:`TSSslSession` object that is identifed by the
++ :type:`TSSslSessionID` object.  If there is no matching sesion object, :func:`TSSslSessionGet` returns NULL and :func:`TSSslSessionGetBuffer`
++ returns 0.
++ 
++--- a/doc/developer-guide/api/functions/TSStat.en.rst
+++++ b/doc/developer-guide/api/functions/TSStat.en.rst
++@@ -46,9 +46,9 @@ Description
++ 
++ A plugin statistic is created by :func:`TSStatCreate`. The :arg:`name` must be globally unique and
++ should follow the standard dotted tag form. To avoid collisions and for easy of use the first tag
++-should be the plugin name or something easily derived from it. Currently only integers are suppored
+++should be the plugin name or something easily derived from it. Currently only integers are supported
++ therefore :arg:`type` must be :macro:`TS_RECORDDATATYPE_INT`. The return value is the index of the
++-statistic. In general thsi should work but if it doesn't it will :code:`assert`. In particular,
+++statistic. In general this should work but if it doesn't it will :code:`assert`. In particular,
++ creating the same statistic twice will fail in this way, which can happen if statistics are created
++ as part of or based on configuration files and |TS| is reloaded.
++ 
++--- a/doc/developer-guide/api/functions/TSVConnReenable.en.rst
+++++ b/doc/developer-guide/api/functions/TSVConnReenable.en.rst
++@@ -32,7 +32,7 @@ Description
++ ===========
++ 
++ Reenable the SSL connection :arg:`svc`. If a plugin hook is called, ATS
++-processing on that connnection will not resume until this is invoked for that
+++processing on that connection will not resume until this is invoked for that
++ connection.
++ 
++ If the server is running OpenSSL 1.0.1 with the appropraite patch installed or
++--- a/doc/developer-guide/api/functions/TSfwrite.en.rst
+++++ b/doc/developer-guide/api/functions/TSfwrite.en.rst
++@@ -44,4 +44,4 @@ The behavior is undefined if length is g
++ Return Value
++ ============
++ 
++-Returns the number of bytes actually written, or -1 if an error occured.
+++Returns the number of bytes actually written, or -1 if an error occurred.
++--- a/iocore/cache/CacheHosting.cc
+++++ b/iocore/cache/CacheHosting.cc
++@@ -715,7 +715,7 @@ ConfigVolumes::BuildListFromString(char
++           // added by YTS Team, yamsat for bug id 59632
++           total += size;
++           if (size > 100 || total > 100) {
++-            err = "Total volume size added upto more than 100 percent, No volumes created";
+++            err = "Total volume size added up to more than 100 percent, No volumes created";
++             break;
++           }
++           // ends here
++--- a/iocore/eventsystem/IOBuffer.cc
+++++ b/iocore/eventsystem/IOBuffer.cc
++@@ -179,7 +179,7 @@ MIOBuffer::puts(char *s, int64_t len)
++     }
++     if (!*pb || *pb == '\n') {
++       int64_t n = (int64_t)(pb - s);
++-      memcpy(end(), s, n + 1); // Upto and including '\n'
+++      memcpy(end(), s, n + 1); // Up to and including '\n'
++       end()[n + 1] = 0;
++       fill(n + 1);
++       return n + 1;
++--- a/plugins/esi/lib/EsiProcessor.cc
+++++ b/plugins/esi/lib/EsiProcessor.cc
++@@ -305,7 +305,7 @@ EsiProcessor::process(const char *&data,
++ 
++     /* FAILURE CACHE */
++     FailureData *data = static_cast<FailureData *>(pthread_getspecific(threadKey));
++-    _debugLog("plugin_esi_failureInfo", "[%s]Fetched data related to thread specfic %p", __FUNCTION__, data);
+++    _debugLog("plugin_esi_failureInfo", "[%s]Fetched data related to thread specific %p", __FUNCTION__, data);
++ 
++     for (iter = try_iter->attempt_nodes.begin(); iter != try_iter->attempt_nodes.end(); ++iter) {
++       if ((iter->type == DocNode::TYPE_INCLUDE) || iter->type == DocNode::TYPE_SPECIAL_INCLUDE) {
++@@ -342,7 +342,7 @@ EsiProcessor::process(const char *&data,
++       }
++     }
++     if (attempt_succeeded) {
++-      _debugLog(_debug_tag, "[%s] attempt section succeded; using attempt section", __FUNCTION__);
+++      _debugLog(_debug_tag, "[%s] attempt section succeeded; using attempt section", __FUNCTION__);
++       _node_list.splice(try_iter->pos, try_iter->attempt_nodes);
++     } else {
++       _debugLog(_debug_tag, "[%s] attempt section errored; trying except section", __FUNCTION__);
++@@ -436,7 +436,7 @@ EsiProcessor::flush(string &data, int &o
++ 
++     /* FAILURE CACHE */
++     FailureData *fdata = static_cast<FailureData *>(pthread_getspecific(threadKey));
++-    _debugLog("plugin_esi_failureInfo", "[%s]Fetched data related to thread specfic %p", __FUNCTION__, fdata);
+++    _debugLog("plugin_esi_failureInfo", "[%s]Fetched data related to thread specific %p", __FUNCTION__, fdata);
++ 
++     for (iter = try_iter->attempt_nodes.begin(); iter != try_iter->attempt_nodes.end(); ++iter) {
++       if ((iter->type == DocNode::TYPE_INCLUDE) || iter->type == DocNode::TYPE_SPECIAL_INCLUDE) {
++@@ -473,7 +473,7 @@ EsiProcessor::flush(string &data, int &o
++       }
++     }
++     if (attempt_succeeded) {
++-      _debugLog(_debug_tag, "[%s] attempt section succeded; using attempt section", __FUNCTION__);
+++      _debugLog(_debug_tag, "[%s] attempt section succeeded; using attempt section", __FUNCTION__);
++       _n_prescanned_nodes = _n_prescanned_nodes + try_iter->attempt_nodes.size();
++       _node_list.splice(try_iter->pos, try_iter->attempt_nodes);
++     } else {
++--- a/plugins/esi/lib/Variables.cc
+++++ b/plugins/esi/lib/Variables.cc
++@@ -437,18 +437,18 @@ Variables::_parseDictVariable(const std:
++   for (int i = 0; i < (var_size - 1); ++i) {
++     if (variable[i] == '{') {
++       if (paranth_index != -1) {
++-        _debugLog(_debug_tag, "[%s] Cannot have multiple paranthesis in dict variable [%.*s]", __FUNCTION__, var_size, var_ptr);
+++        _debugLog(_debug_tag, "[%s] Cannot have multiple parenthesis in dict variable [%.*s]", __FUNCTION__, var_size, var_ptr);
++         return false;
++       }
++       paranth_index = i;
++     }
++     if (variable[i] == '}') {
++-      _debugLog(_debug_tag, "[%s] Cannot have multiple paranthesis in dict variable [%.*s]", __FUNCTION__, var_size, var_ptr);
+++      _debugLog(_debug_tag, "[%s] Cannot have multiple parenthesis in dict variable [%.*s]", __FUNCTION__, var_size, var_ptr);
++       return false;
++     }
++   }
++   if (paranth_index == -1) {
++-    _debugLog(_debug_tag, "[%s] Could not find opening paranthesis in variable [%.*s]", __FUNCTION__, var_size, var_ptr);
+++    _debugLog(_debug_tag, "[%s] Could not find opening parenthesis in variable [%.*s]", __FUNCTION__, var_size, var_ptr);
++     return false;
++   }
++   if (paranth_index == 0) {
++--- a/plugins/experimental/collapsed_forwarding/collapsed_forwarding.cc
+++++ b/plugins/experimental/collapsed_forwarding/collapsed_forwarding.cc
++@@ -353,7 +353,7 @@ TSRemapInit(TSRemapInterface * /* api_in
++     TSError("Cannot initialize %s as both global and remap plugin", DEBUG_TAG);
++     return TS_ERROR;
++   } else {
++-    TSDebug(DEBUG_TAG, "plugin is succesfully initialized for remap");
+++    TSDebug(DEBUG_TAG, "plugin is successfully initialized for remap");
++     return TS_SUCCESS;
++   }
++ }
++--- a/plugins/experimental/fq_pacing/fq_pacing.c
+++++ b/plugins/experimental/fq_pacing/fq_pacing.c
++@@ -118,7 +118,7 @@ TSRemapInit(TSRemapInterface *api_info,
++     return TS_ERROR;
++   }
++ 
++-  TSDebug(PLUGIN_NAME, "plugin is succesfully initialized");
+++  TSDebug(PLUGIN_NAME, "plugin is successfully initialized");
++   return TS_SUCCESS;
++ }
++ 
++--- a/plugins/experimental/header_normalize/header_normalize.cc
+++++ b/plugins/experimental/header_normalize/header_normalize.cc
++@@ -158,7 +158,7 @@ TSRemapInit(TSRemapInterface *api_info,
++     return TS_ERROR;
++   }
++   buildHdrMap();
++-  TSDebug(PLUGIN_NAME, "plugin is succesfully initialized");
+++  TSDebug(PLUGIN_NAME, "plugin is successfully initialized");
++   return TS_SUCCESS;
++ }
++ 
++--- a/plugins/experimental/prefetch/plugin.cc
+++++ b/plugins/experimental/prefetch/plugin.cc
++@@ -202,7 +202,7 @@ evaluate(const String &v)
++   } else {
++     stmt.assign(v);
++   }
++-  PrefetchDebug("statement: '%s', formating length: %zu", stmt.c_str(), len);
+++  PrefetchDebug("statement: '%s', formatting length: %zu", stmt.c_str(), len);
++ 
++   int result = 0;
++   pos        = stmt.find_first_of("+-");
++--- a/plugins/experimental/uri_signing/parse.c
+++++ b/plugins/experimental/uri_signing/parse.c
++@@ -142,7 +142,7 @@ validate_jws(cjose_jws_t *jws, struct co
++     PluginDebug("Initial validation of JWT failed for %16p", jws);
++     goto jwt_fail;
++   }
++-  TimerDebug("inital validation of jwt");
+++  TimerDebug("initial validation of jwt");
++ 
++   cjose_header_t *hdr = cjose_jws_get_protected(jws);
++   TimerDebug("getting header of jws");
++--- a/plugins/experimental/uri_signing/uri_signing.c
+++++ b/plugins/experimental/uri_signing/uri_signing.c
++@@ -46,7 +46,7 @@ TSRemapInit(TSRemapInterface *api_info,
++     return TS_ERROR;
++   }
++ 
++-  TSDebug(PLUGIN_NAME, "plugin is succesfully initialized");
+++  TSDebug(PLUGIN_NAME, "plugin is successfully initialized");
++   return TS_SUCCESS;
++ }
++ 
++--- a/plugins/experimental/url_sig/url_sig.c
+++++ b/plugins/experimental/url_sig/url_sig.c
++@@ -98,7 +98,7 @@ TSRemapInit(TSRemapInterface *api_info,
++     return TS_ERROR;
++   }
++ 
++-  TSDebug(PLUGIN_NAME, "plugin is succesfully initialized");
+++  TSDebug(PLUGIN_NAME, "plugin is successfully initialized");
++   return TS_SUCCESS;
++ }
++ 
++--- a/plugins/generator/generator.cc
+++++ b/plugins/generator/generator.cc
++@@ -609,7 +609,7 @@ GeneratorTxnHook(TSCont contp, TSEvent e
++     TSReleaseAssert(TSHttpTxnCacheLookupStatusGet(arg.txn, &status) == TS_SUCCESS);
++     if (status != TS_CACHE_LOOKUP_HIT_FRESH) {
++       // This transaction is going to be a cache miss, so intercept it.
++-      VDEBUG("intercepting orgin server request for txn=%p", arg.txn);
+++      VDEBUG("intercepting origin server request for txn=%p", arg.txn);
++       TSHttpTxnServerIntercept(TSContCreate(GeneratorInterceptionHook, TSMutexCreate()), arg.txn);
++     }
++ 
++--- a/plugins/header_rewrite/header_rewrite.cc
+++++ b/plugins/header_rewrite/header_rewrite.cc
++@@ -330,7 +330,7 @@ TSPluginInit(int argc, const char *argv[
++     // just appended to the configurations.
++     TSDebug(PLUGIN_NAME, "Loading global configuration file %s", argv[i]);
++     if (conf->parse_config(argv[i], TS_HTTP_READ_RESPONSE_HDR_HOOK)) {
++-      TSDebug(PLUGIN_NAME, "Succesfully loaded global config file %s", argv[i]);
+++      TSDebug(PLUGIN_NAME, "Successfully loaded global config file %s", argv[i]);
++       got_config = true;
++     } else {
++       TSError("[header_rewrite] failed to parse configuration file %s", argv[i]);
++@@ -401,7 +401,7 @@ TSRemapNewInstance(int argc, char *argv[
++       delete conf;
++       return TS_ERROR;
++     } else {
++-      TSDebug(PLUGIN_NAME, "Succesfully loaded remap config file %s", argv[i]);
+++      TSDebug(PLUGIN_NAME, "Successfully loaded remap config file %s", argv[i]);
++     }
++   }
++ 
++@@ -470,6 +470,6 @@ TSRemapDoRemap(void *ih, TSHttpTxn rh, T
++     rule = rule->next;
++   }
++ 
++-  TSDebug(PLUGIN_NAME_DBG, "Returing from TSRemapDoRemap with status: %d", rval);
+++  TSDebug(PLUGIN_NAME_DBG, "Returning from TSRemapDoRemap with status: %d", rval);
++   return rval;
++ }
++--- a/plugins/s3_auth/s3_auth.cc
+++++ b/plugins/s3_auth/s3_auth.cc
++@@ -871,7 +871,7 @@ event_handler(TSCont cont, TSEvent event
++     }
++ 
++     if (TS_HTTP_STATUS_OK == status) {
++-      TSDebug(PLUGIN_NAME, "Succesfully signed the AWS S3 URL");
+++      TSDebug(PLUGIN_NAME, "Successfully signed the AWS S3 URL");
++     } else {
++       TSDebug(PLUGIN_NAME, "Failed to sign the AWS S3 URL, status = %d", status);
++       TSHttpTxnStatusSet(txnp, status);
++--- a/src/traffic_cache_tool/CacheTool.cc
+++++ b/src/traffic_cache_tool/CacheTool.cc
++@@ -867,7 +867,7 @@ Span::updateHeader()
++       zret.push(0, errno, "Failed to update span - ", strerror(errno));
++     }
++   } else {
++-    std::cout << "Writing not enabled, no updates perfomed" << std::endl;
+++    std::cout << "Writing not enabled, no updates performed" << std::endl;
++   }
++   return zret;
++ }
++--- a/src/traffic_crashlog/traffic_crashlog.cc
+++++ b/src/traffic_crashlog/traffic_crashlog.cc
++@@ -198,7 +198,7 @@ main(int /* argc ATS_UNUSED */, const ch
++   mgmterr = TSInit(nullptr, (TSInitOptionT)(TS_MGMT_OPT_NO_EVENTS | TS_MGMT_OPT_NO_SOCK_TESTS));
++   if (mgmterr != TS_ERR_OKAY) {
++     char *msg = TSGetErrorMessage(mgmterr);
++-    Warning("failed to intialize management API: %s", msg);
+++    Warning("failed to initialize management API: %s", msg);
++     TSfree(msg);
++   }
++ 
++--- a/src/traffic_logstats/logstats.cc
+++++ b/src/traffic_logstats/logstats.cc
++@@ -1828,7 +1828,7 @@ process_file(int in_fd, off_t offset, un
++     unsigned second_read_size = sizeof(LogBufferHeader) - first_read_size;
++     nread                     = read(in_fd, &buffer[first_read_size], second_read_size);
++     if (!nread || EOF == nread) {
++-      Debug("logstats", "Second read of header failed (attemped %d bytes at offset %d, got nothing), errno=%d.", second_read_size,
+++      Debug("logstats", "Second read of header failed (attempted %d bytes at offset %d, got nothing), errno=%d.", second_read_size,
++             first_read_size, errno);
++       return 1;
++     }
++--- a/src/traffic_manager/traffic_manager.cc
+++++ b/src/traffic_manager/traffic_manager.cc
++@@ -142,7 +142,7 @@ rotateLogs()
++     if (kill(tspid, SIGUSR2) != 0) {
++       mgmt_log("Could not send SIGUSR2 to TS: %s", strerror(errno));
++     } else {
++-      mgmt_log("Succesfully sent SIGUSR2 to TS!");
+++      mgmt_log("Successfully sent SIGUSR2 to TS!");
++     }
++   }
++ }
++--- a/src/traffic_server/CoreUtils.h
+++++ b/src/traffic_server/CoreUtils.h
++@@ -44,7 +44,7 @@
++ #define PC_REGNUM 12 /* Contains program counter EIP */
++ #define FP_REGNUM 5  /* Virtual frame pointer EBP */
++ #define NO_OF_ARGS                                             \
++-  10 /* The argument depth upto which we would be looking into \
+++  10 /* The argument depth up to which we would be looking into \
++         the stack */
++ 
++ // contains local and in registers, frame pointer, and stack base
++@@ -63,7 +63,7 @@ struct core_stack_state {
++ #include <assert.h>
++ 
++ #define NO_OF_ARGS                                             \
++-  10 /* The argument depth upto which we would be looking into \
+++  10 /* The argument depth up to which we would be looking into \
++         the stack */
++ 
++ // contains local and in registers, frame pointer, and stack base
++--- a/src/traffic_server/InkAPITest.cc
+++++ b/src/traffic_server/InkAPITest.cc
++@@ -4225,7 +4225,7 @@ REGRESSION_TEST(SDK_API_TSHttpHdr)(Regre
++         SDK_RPRINT(test, "TSHttpHdrUrlSet&Get", "TestCase1", TC_FAIL, "TSHttpHdrUrlSet returns TS_ERROR");
++       } else {
++         if (TSHttpHdrUrlGet(bufp1, hdr_loc1, &url_loc_Get) != TS_SUCCESS) {
++-          SDK_RPRINT(test, "TSHttpHdrUrlSet&Get", "TestCase1", TC_FAIL, "TSHttpHdrUrlGet retuns TS_ERROR");
+++          SDK_RPRINT(test, "TSHttpHdrUrlSet&Get", "TestCase1", TC_FAIL, "TSHttpHdrUrlGet returns TS_ERROR");
++         } else {
++           if (url_loc == url_loc_Get) {
++             SDK_RPRINT(test, "TSHttpHdrUrlSet&Get", "TestCase1", TC_PASS, "ok");
++--- a/src/tscore/ArgParser.cc
+++++ b/src/tscore/ArgParser.cc
++@@ -154,7 +154,7 @@ ArgParser::parse(const char **argv)
++   };
++   // if there is anything left, then output usage
++   if (!args.empty()) {
++-    std::string msg = "Unkown command, option or args:";
+++    std::string msg = "Unknown command, option or args:";
++     for (auto it : args) {
++       msg = msg + " '" + it + "'";
++     }
++--- a/iocore/net/Socks.cc
+++++ b/iocore/net/Socks.cc
++@@ -694,7 +694,7 @@ socks5PasswdAuthHandler(int event, unsig
++     // NEC thinks it is 5 RFC seems to indicate 1.
++     switch (p[1]) {
++     case 0:
++-      Debug("Socks", "Username/Passwd succeded");
+++      Debug("Socks", "Username/Passwd succeeded");
++       *h_ptr = nullptr;
++       break;
++ 
++--- a/iocore/net/UnixUDPNet.cc
+++++ b/iocore/net/UnixUDPNet.cc
++@@ -628,7 +628,7 @@ UDPNetProcessor::CreateUDPSocket(int *re
++     }
++ 
++     if ((res = safe_getsockname(fd, &local_addr.sa, &local_addr_len)) < 0) {
++-      Debug("udpnet", "CreateUdpsocket: getsockname didnt' work");
+++      Debug("udpnet", "CreateUdpsocket: getsockname didn't work");
++       goto HardError;
++     }
++   }
++--- a/mgmt/Rollback.cc
+++++ b/mgmt/Rollback.cc
++@@ -133,7 +133,7 @@ Rollback::Rollback(const char *fileName_
++           mgmt_log("[RollBack::Rollback] Automatic Rollback to prior version failed for %s : %s\n", fileName, strerror(errno));
++           needZeroLength = true;
++         } else {
++-          mgmt_log("[RollBack::Rollback] Automatic Rollback to version succeded for %s\n", fileName, strerror(errno));
+++          mgmt_log("[RollBack::Rollback] Automatic Rollback to version succeeded for %s\n", fileName, strerror(errno));
++           needZeroLength = false;
++           highestSeen--;
++           // Since we've made the highestVersion active
++--- a/proxy/logging/LogBuffer.cc
+++++ b/proxy/logging/LogBuffer.cc
++@@ -270,7 +270,7 @@ LogBuffer::checkout_write(size_t *write_
++       }
++ 
++       if (switch_state(old_s, new_s)) {
++-        // we succeded in setting the new state
+++        // we succeeded in setting the new state
++         break;
++       }
++     }
++--- a/proxy/logging/LogObject.cc
+++++ b/proxy/logging/LogObject.cc
++@@ -409,7 +409,7 @@ LogObject::_checkout_write(size_t *write
++ 
++     switch (result_code) {
++     case LogBuffer::LB_OK:
++-      // checkout succeded
+++      // checkout succeeded
++       retry = false;
++       break;
++ 
++--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++++ b/doc/admin-guide/plugins/header_rewrite.en.rst
++@@ -330,7 +330,7 @@ The data that can be checked is ::
++    %{INBOUND:REMOTE-PORT}     The client port for the connection.
++    %{INBOUND:TLS}             The TLS protocol if the connection is over TLS, otherwise the empty string.
++    %{INBOUND:H2}              The string "h2" if the connection is HTTP/2, otherwise the empty string.
++-   %{INBOUND:IPV4}            The string "ipv4" if the connection is IPv4, otherwise the emtpy string.
+++   %{INBOUND:IPV4}            The string "ipv4" if the connection is IPv4, otherwise the empty string.
++    %{INBOUND:IPV6}            The string "ipv6" if the connection is IPv6, otherwise the empty string.
++    %{INBOUND:IP-FAMILY}       The IP family, either "ipv4" or "ipv6".
++    %{INBOUND:STACK}           The full protocol stack separated by ','.
++@@ -827,7 +827,7 @@ Variable                Description
++ %<INBOUND:TLS>          The TLS protocol for the inbound connection if it is over TLS, otherwise the
++                         empty string.
++ %<INBOUND:H2>           The string "h2" if the inbound connection is HTTP/2, otherwise the empty string.
++-%<INBOUND:IPV4>         The string "ipv4" if the inbound connection is IPv4, otherwise the emtpy string.
+++%<INBOUND:IPV4>         The string "ipv4" if the inbound connection is IPv4, otherwise the empty string.
++ %<INBOUND:IPV6>         The string "ipv6" if the inbound connection is IPv6, otherwise the empty string.
++ %<INBOUND:IP-FAMILY>    The IP family of the inbound connection (either "ipv4" or "ipv6").
++ %<INBOUND:STACK>        The full protocol stack of the inbound connection separated by ','.
++--- a/iocore/dns/SplitDNS.cc
+++++ b/iocore/dns/SplitDNS.cc
++@@ -341,7 +341,7 @@ SplitDNSRecord::ProcessDNSHosts(char *va
++       if (tmp - current > (MAXDNAME - 1)) {
++         return "DNS server name (ip) is too long";
++       } else if (tmp - current == 0) {
++-        return "server string is emtpy";
+++        return "server string is empty";
++       }
++       *tmp = 0;
++     }
++--- a/lib/records/RecHttp.cc
+++++ b/lib/records/RecHttp.cc
++@@ -410,7 +410,7 @@ HttpProxyPort::processOptions(const char
++     if (in_ip_set_p && m_family != m_inbound_ip.family()) {
++       std::string_view iname{ats_ip_family_name(m_inbound_ip.family())};
++       std::string_view fname{ats_ip_family_name(m_family)};
++-      Warning("Invalid port descriptor '%s' - the inbound adddress family [%.*s] is not the same type as the explicit family value "
+++      Warning("Invalid port descriptor '%s' - the inbound address family [%.*s] is not the same type as the explicit family value "
++               "[%.*s]",
++               opts, static_cast<int>(iname.size()), iname.data(), static_cast<int>(fname.size()), fname.data());
++       zret = false;
++--- a/proxy/ParentSelection.cc
+++++ b/proxy/ParentSelection.cc
++@@ -491,7 +491,7 @@ ParentRecord::ProcessParents(char *val,
++       errPtr = "Parent hostname is too long";
++       goto MERROR;
++     } else if (tmp - current == 0) {
++-      errPtr = "Parent string is emtpy";
+++      errPtr = "Parent string is empty";
++       goto MERROR;
++     }
++     // Update the pRecords
++--- a/src/tscore/HostLookup.cc
+++++ b/src/tscore/HostLookup.cc
++@@ -50,7 +50,7 @@ domaincmp(const char *hostname, const ch
++   const char *host_cur   = hostname + strlen(hostname);
++   const char *domain_cur = domain + strlen(domain);
++ 
++-  // Check to see if were passed emtpy stings for either
+++  // Check to see if were passed empty stings for either
++   //  argument.  Empty strings do not match anything
++   //
++   if (domain_cur == domain || host_cur == hostname) {
++@@ -380,7 +380,7 @@ charIndex::Insert(const char *match_data
++ 
++     // Check to see if are at the level we supposed be at
++     if (*(match_data + 1) == '\0') {
++-      // The slot should always be emtpy, no duplicate
+++      // The slot should always be empty, no duplicate
++       //   keys are allowed
++       ink_assert(cur->branch_array[index] == nullptr);
++       cur->branch_array[index] = toInsert;
++--- a/build/pkg.m4
+++++ b/build/pkg.m4
++@@ -53,7 +53,7 @@ fi[]dnl
++ # to PKG_CHECK_MODULES(), but does not set variables or print errors.
++ #
++ # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
++-# only at the first occurence in configure.ac, so if the first place
+++# only at the first occurrence in configure.ac, so if the first place
++ # it's called might be skipped (such as if it is within an "if", you
++ # have to call PKG_CHECK_EXISTS manually
++ # --------------------------------------------------------------
++--- a/doc/admin-guide/files/logging.yaml.en.rst
+++++ b/doc/admin-guide/files/logging.yaml.en.rst
++@@ -258,7 +258,7 @@ inconsequentially variable parameters th
++ reporting from log analyzers.
++ 
++ Multiple query parameters may be listed, separated by spaces, though only the
++-first occurence of each will be wiped from the query string if any individual
+++first occurrence of each will be wiped from the query string if any individual
++ parameter appears more than once in the URL.
++ 
++ .. _admin-custom-logs-logs:
++--- a/include/tscpp/util/TextView.h
+++++ b/include/tscpp/util/TextView.h
++@@ -264,7 +264,7 @@ public:
++   self_type prefix(size_t n) const;
++   /// Convenience overload to avoid ambiguity for literal numbers.
++   self_type prefix(int n) const;
++-  /** Get the prefix delimited by the first occurence of the character @a c.
+++  /** Get the prefix delimited by the first occurrence of the character @a c.
++ 
++       If @a c is not found the entire view is returned.
++       The delimiter character is not included in the returned view.
++@@ -272,7 +272,7 @@ public:
++       @return A view of the prefix.
++   */
++   self_type prefix(char c) const;
++-  /** Get the prefix delimited by the first occurence of a character in @a delimiters.
+++  /** Get the prefix delimited by the first occurrence of a character in @a delimiters.
++ 
++       If no such character is found the entire view is returned.
++       The delimiter character is not included in the returned view.
++--- a/iocore/cache/CacheVol.cc
+++++ b/iocore/cache/CacheVol.cc
++@@ -400,7 +400,7 @@ CacheVC::scanOpenWrite(int /* event ATS_
++   // get volume lock
++   if (writer_lock_retry > SCAN_WRITER_LOCK_MAX_RETRY) {
++     int r = _action.continuation->handleEvent(CACHE_EVENT_SCAN_OPERATION_BLOCKED, nullptr);
++-    Debug("cache_scan", "still havent got the writer lock, asking user..");
+++    Debug("cache_scan", "still haven't got the writer lock, asking user..");
++     switch (r) {
++     case CACHE_SCAN_RESULT_RETRY:
++       writer_lock_retry = 0;
++--- a/iocore/eventsystem/I_IOBuffer.h
+++++ b/iocore/eventsystem/I_IOBuffer.h
++@@ -697,7 +697,7 @@ public:
++   /**
++     Perform a memchr() across the list of IOBufferBlocks. Returns the
++     offset from the current start point of the reader to the first
++-    occurence of character 'c' in the buffer.
+++    occurrence of character 'c' in the buffer.
++ 
++     @param c character to look for.
++     @param len number of characters to check. If len exceeds the number
++--- a/iocore/net/OCSPStapling.cc
+++++ b/iocore/net/OCSPStapling.cc
++@@ -89,7 +89,7 @@ stapling_get_issuer(SSL_CTX *ssl_ctx, X5
++ 
++ #ifdef SSL_CTX_select_current_cert
++   if (!SSL_CTX_select_current_cert(ssl_ctx, x)) {
++-    Warning("OCSP: could not select current certifcate chain %p", x);
+++    Warning("OCSP: could not select current certificate chain %p", x);
++   }
++ #endif
++ 
++--- a/mgmt/Alarms.cc
+++++ b/mgmt/Alarms.cc
++@@ -297,7 +297,7 @@ Alarms::signalAlarm(alarm_t a, const cha
++     (*(func))(a, ip, desc);
++   }
++ 
++-  /* Priority 2 alarms get signalled if they are the first unsolved occurence. */
+++  /* Priority 2 alarms get signalled if they are the first unsolved occurrence. */
++   if (priority == 2 && !ip) {
++     execAlarmBin(desc);
++   }
++--- a/proxy/http/HttpSessionManager.cc
+++++ b/proxy/http/HttpSessionManager.cc
++@@ -204,7 +204,7 @@ ServerSessionPool::eventHandler(int even
++         if (connection_count_below_min) {
++           Debug("http_ss",
++                 "[%" PRId64 "] [session_bucket] session received io notice [%s], "
++-                "reseting timeout to maintain minimum number of connections",
+++                "resetting timeout to maintain minimum number of connections",
++                 s->con_id, HttpDebugNames::get_event_name(event));
++           s->get_netvc()->set_inactivity_timeout(s->get_netvc()->get_inactivity_timeout());
++           s->get_netvc()->set_active_timeout(s->get_netvc()->get_active_timeout());
++--- a/plugins/lua/ts_lua_transform.c
+++++ b/plugins/lua/ts_lua_transform.c
++@@ -89,7 +89,7 @@ ts_lua_transform_handler(TSCont contp, t
++   empty_input = 0;
++   if (!TSVIOBufferGet(input_vio)) {
++     if (transform_ctx->output.vio) {
++-      TSDebug(TS_LUA_DEBUG_TAG, "[%s] reenabling ouput VIO after input VIO does not exist", __FUNCTION__);
+++      TSDebug(TS_LUA_DEBUG_TAG, "[%s] reenabling output VIO after input VIO does not exist", __FUNCTION__);
++       TSVIONBytesSet(transform_ctx->output.vio, transform_ctx->total);
++       TSVIOReenable(transform_ctx->output.vio);
++       return 0;
++--- a/proxy/hdrs/HdrHeap.cc
+++++ b/proxy/hdrs/HdrHeap.cc
++@@ -950,7 +950,7 @@ HdrHeap::unmarshal(int buf_length, int o
++       // Nothing to do
++       break;
++     default:
++-      fprintf(stderr, "WARNING: Unmarshal failed due to unknow obj type %d after %d bytes", (int)obj->m_type,
+++      fprintf(stderr, "WARNING: Unmarshal failed due to unknown obj type %d after %d bytes", (int)obj->m_type,
++               (int)(obj_data - (char *)this));
++       dump_heap(unmarshal_size);
++       return -1;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4866f34b38f53045c69f9b97e1a18adbe7203af2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++Description: Fix Perl interpreter path
++Author: Jean Baptiste Favre <debian@jbfavre.org>
++Reviewed-by: Jean Baptiste Favre <debian@jbfavre.org>
++Last-Update: 2019-01-03
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/tools/tspush
+++++ b/tools/tspush
++@@ -1,4 +1,4 @@
++-#!/usr/bin/env perl
+++#!/usr/bin/perl
++ 
++ #
++ # Licensed to the Apache Software Foundation (ASF) under one
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..058c052c8d9b24d0afb5dbdd3b0a61d5af9bf15b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++Description: Update compilation chain after embedded libyamlcpp removal
++Author: Jean Baptiste Favre <debian@jbfavre.org>
++Origin: other
++Reviewed-by: Jean Baptiste Favre <debian@jbfavre.org>
++Last-Update: 2019-01-30
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/CMakeLists.txt
+++++ b/CMakeLists.txt
++@@ -41,7 +41,6 @@ include_directories(lib
++         proxy/shared
++         lib/records
++         include/wccp
++-        lib/yamlcpp/include
++         iocore/eventsystem
++         iocore/net
++         iocore/dns
++--- a/configure.ac
+++++ b/configure.ac
++@@ -2041,7 +2041,6 @@ AC_CONFIG_FILES([
++   include/tscore/ink_config.h
++   lib/tsconfig/Makefile
++   src/wccp/Makefile
++-  lib/yamlcpp/Makefile
++   mgmt/Makefile
++   mgmt/api/Makefile
++   mgmt/api/include/Makefile
++--- a/lib/Makefile.am
+++++ b/lib/Makefile.am
++@@ -25,12 +25,6 @@ endif
++ # to prevent Clang Analyzer warning
++ LOCAL =
++ 
++-if BUILD_YAML_CPP
++-LOCAL += yamlcpp
++-endif
++-
++ all-local:   $(LOCAL)
++-     $(MAKE) -C yamlcpp
++ 
++ clean-local:
++-     $(MAKE) -C yamlcpp clean
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5e02de5314d9370f4acdb9e4535e0fdbc6a6504
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,905 @@@
++Description: HTTP/2 rate limiting
++  Fix for CVE-2019-9512, CVE-2019-9514, CVE-2019-9515, CVE-2019-10079
++Author: Bryan Call <bcall@apache.org>
++Origin: backport, https://github.com/apache/trafficserver/pull/5822
++Reviewed-by: Jean Baptiste Favre <debian@jbfavre.org>
++Last-Update: 2019-08-26
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/doc/admin-guide/files/records.config.en.rst
+++++ b/doc/admin-guide/files/records.config.en.rst
++@@ -3564,6 +3564,34 @@ HTTP/2 Configuration
++    HTTP/2 connection to avoid duplicate pushes on the same connection. If the
++    maximum number is reached, new entries are not remembered.
++ 
+++.. ts:cv:: CONFIG proxy.config.http2.max_settings_frames_per_minute INT 14
+++   :reloadable:
+++
+++   Specifies how many SETTINGS frames |TS| receives for a minute at maximum.
+++   Clients exceeded this limit will be immediately disconnected with an error
+++   code of ENHANCE_YOUR_CALM.
+++
+++.. ts:cv:: CONFIG proxy.config.http2.max_ping_frames_per_minute INT 60
+++   :reloadable:
+++
+++   Specifies how many number of PING frames |TS| receives for a minute at maximum.
+++   Clients exceeded this limit will be immediately disconnected with an error
+++   code of ENHANCE_YOUR_CALM.
+++
+++.. ts:cv:: CONFIG proxy.config.http2.max_priority_frames_per_minute INT 120
+++   :reloadable:
+++
+++   Specifies how many number of PRIORITY frames |TS| receives for a minute at maximum.
+++   Clients exceeded this limit will be immediately disconnected with an error
+++   code of ENHANCE_YOUR_CALM.
+++
+++.. ts:cv:: CONFIG proxy.config.http2.min_avg_window_update FLOAT 2560.0
+++   :reloadable:
+++
+++   Specifies the minimum average window increment |TS| allows. The average will be calculated based on the last 5 WINDOW_UPDATE frames.
+++   Clients that send smaller window increments lower than this limit will be immediately disconnected with an error
+++   code of ENHANCE_YOUR_CALM.
+++
++ Plug-in Configuration
++ =====================
++ 
++--- a/iocore/eventsystem/I_VIO.h
+++++ b/iocore/eventsystem/I_VIO.h
++@@ -139,6 +139,9 @@ public:
++   */
++   inkcoreapi void reenable_re();
++ 
+++  void disable();
+++  bool is_disabled();
+++
++   VIO(int aop);
++   VIO();
++ 
++@@ -218,6 +221,9 @@ public:
++ 
++   */
++   Ptr<ProxyMutex> mutex;
+++
+++private:
+++  bool _disabled = false;
++ };
++ 
++ #include "I_VConnection.h"
++--- a/iocore/eventsystem/P_VIO.h
+++++ b/iocore/eventsystem/P_VIO.h
++@@ -104,6 +104,7 @@ VIO::set_continuation(Continuation *acon
++ TS_INLINE void
++ VIO::reenable()
++ {
+++  this->_disabled = false;
++   if (vc_server) {
++     vc_server->reenable(this);
++   }
++@@ -117,7 +118,20 @@ VIO::reenable()
++ TS_INLINE void
++ VIO::reenable_re()
++ {
+++  this->_disabled = false;
++   if (vc_server) {
++     vc_server->reenable_re(this);
++   }
++ }
+++
+++TS_INLINE void
+++VIO::disable()
+++{
+++  this->_disabled = true;
+++}
+++
+++TS_INLINE bool
+++VIO::is_disabled()
+++{
+++  return this->_disabled;
+++}
++--- a/iocore/net/SSLNetVConnection.cc
+++++ b/iocore/net/SSLNetVConnection.cc
++@@ -526,7 +526,7 @@ SSLNetVConnection::net_read_io(NetHandle
++   // If it is not enabled, lower its priority.  This allows
++   // a fast connection to speed match a slower connection by
++   // shifting down in priority even if it could read.
++-  if (!s->enabled || s->vio.op != VIO::READ) {
+++  if (!s->enabled || s->vio.op != VIO::READ || s->vio.is_disabled()) {
++     read_disable(nh, this);
++     return;
++   }
++@@ -632,7 +632,7 @@ SSLNetVConnection::net_read_io(NetHandle
++   }
++ 
++   // If there is nothing to do or no space available, disable connection
++-  if (ntodo <= 0 || !buf.writer()->write_avail()) {
+++  if (ntodo <= 0 || !buf.writer()->write_avail() || s->vio.is_disabled()) {
++     read_disable(nh, this);
++     return;
++   }
++@@ -1577,6 +1577,7 @@ SSLNetVConnection::reenable(NetHandler *
++     }
++     Debug("ssl", "iterate from reenable curHook=%p %d", curHook, sslHandshakeHookState);
++   }
+++
++   this->readReschedule(nh);
++ }
++ 
++--- a/iocore/net/UnixNetVConnection.cc
+++++ b/iocore/net/UnixNetVConnection.cc
++@@ -201,7 +201,7 @@ read_from_net(NetHandler *nh, UnixNetVCo
++     return;
++   }
++   // if it is not enabled.
++-  if (!s->enabled || s->vio.op != VIO::READ) {
+++  if (!s->enabled || s->vio.op != VIO::READ || s->vio.is_disabled()) {
++     read_disable(nh, vc);
++     return;
++   }
++--- a/mgmt/RecordsConfig.cc
+++++ b/mgmt/RecordsConfig.cc
++@@ -1330,6 +1330,14 @@ static const RecordElement RecordsConfig
++   ,
++   {RECT_LOCAL, "proxy.local.log.collation_mode", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-4]", RECA_NULL}
++   ,
+++  {RECT_CONFIG, "proxy.config.http2.max_settings_frames_per_minute", RECD_INT, "14", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+++  ,
+++  {RECT_CONFIG, "proxy.config.http2.max_ping_frames_per_minute", RECD_INT, "60", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+++  ,
+++  {RECT_CONFIG, "proxy.config.http2.max_priority_frames_per_minute", RECD_INT, "120", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+++  ,
+++  {RECT_CONFIG, "proxy.config.http2.min_avg_window_update", RECD_FLOAT, "2560.0", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
+++  ,
++ 
++   //# Librecords based stats system (new as of v2.1.3)
++   {RECT_CONFIG, "proxy.config.stat_api.max_stats_allowed", RECD_INT, "256", RECU_RESTART_TS, RR_NULL, RECC_INT, "[256-1000]", RECA_NULL}
++--- a/proxy/http2/HTTP2.cc
+++++ b/proxy/http2/HTTP2.cc
++@@ -717,21 +717,28 @@ http2_decode_header_blocks(HTTPHdr *hdr,
++ }
++ 
++ // Initialize this subsystem with librecords configs (for now)
++-uint32_t Http2::max_concurrent_streams_in  = 100;
++-uint32_t Http2::min_concurrent_streams_in  = 10;
++-uint32_t Http2::max_active_streams_in      = 0;
++-bool Http2::throttling                     = false;
++-uint32_t Http2::stream_priority_enabled    = 0;
++-uint32_t Http2::initial_window_size        = 1048576;
++-uint32_t Http2::max_frame_size             = 16384;
++-uint32_t Http2::header_table_size          = 4096;
++-uint32_t Http2::max_header_list_size       = 4294967295;
++-uint32_t Http2::max_request_header_size    = 131072;
++-uint32_t Http2::accept_no_activity_timeout = 120;
++-uint32_t Http2::no_activity_timeout_in     = 120;
++-uint32_t Http2::active_timeout_in          = 0;
++-uint32_t Http2::push_diary_size            = 256;
++-uint32_t Http2::zombie_timeout_in          = 0;
+++uint32_t Http2::max_concurrent_streams_in      = 100;
+++uint32_t Http2::min_concurrent_streams_in      = 10;
+++uint32_t Http2::max_active_streams_in          = 0;
+++bool Http2::throttling                         = false;
+++uint32_t Http2::stream_priority_enabled        = 0;
+++uint32_t Http2::initial_window_size            = 1048576;
+++uint32_t Http2::max_frame_size                 = 16384;
+++uint32_t Http2::header_table_size              = 4096;
+++uint32_t Http2::max_header_list_size           = 4294967295;
+++uint32_t Http2::max_request_header_size        = 131072;
+++uint32_t Http2::accept_no_activity_timeout     = 120;
+++uint32_t Http2::no_activity_timeout_in         = 120;
+++uint32_t Http2::active_timeout_in              = 0;
+++uint32_t Http2::push_diary_size                = 256;
+++uint32_t Http2::zombie_timeout_in              = 0;
+++float Http2::stream_error_rate_threshold       = 0.1;
+++uint32_t Http2::max_settings_per_frame         = 7;
+++uint32_t Http2::max_settings_per_minute        = 14;
+++uint32_t Http2::max_settings_frames_per_minute = 14;
+++uint32_t Http2::max_ping_frames_per_minute     = 60;
+++uint32_t Http2::max_priority_frames_per_minute = 120;
+++float Http2::min_avg_window_update             = 2560.0;
++ 
++ void
++ Http2::init()
++--- a/proxy/http2/HTTP2.h
+++++ b/proxy/http2/HTTP2.h
++@@ -378,6 +378,13 @@ public:
++   static uint32_t active_timeout_in;
++   static uint32_t push_diary_size;
++   static uint32_t zombie_timeout_in;
+++  static float stream_error_rate_threshold;
+++  static uint32_t max_settings_per_frame;
+++  static uint32_t max_settings_per_minute;
+++  static uint32_t max_settings_frames_per_minute;
+++  static uint32_t max_ping_frames_per_minute;
+++  static uint32_t max_priority_frames_per_minute;
+++  static float min_avg_window_update;
++ 
++   static void init();
++ };
++--- a/proxy/http2/Http2ClientSession.cc
+++++ b/proxy/http2/Http2ClientSession.cc
++@@ -74,6 +74,11 @@ Http2ClientSession::destroy()
++ void
++ Http2ClientSession::free()
++ {
+++  if (this->_reenable_event) {
+++    this->_reenable_event->cancel();
+++    this->_reenable_event = nullptr;
+++  }
+++
++   if (h2_pushed_urls) {
++     this->h2_pushed_urls = ink_hash_table_destroy(this->h2_pushed_urls);
++   }
++@@ -326,6 +331,13 @@ Http2ClientSession::main_event_handler(i
++     break;
++   }
++ 
+++  case HTTP2_SESSION_EVENT_REENABLE:
+++    // VIO will be reenableed in this handler
+++    retval = (this->*session_handler)(VC_EVENT_READ_READY, static_cast<VIO *>(e->cookie));
+++    // Clear the event after calling session_handler to not reschedule REENABLE in it
+++    this->_reenable_event = nullptr;
+++    break;
+++
++   case VC_EVENT_ACTIVE_TIMEOUT:
++   case VC_EVENT_INACTIVITY_TIMEOUT:
++   case VC_EVENT_ERROR:
++@@ -478,7 +490,16 @@ Http2ClientSession::state_complete_frame
++   STATE_ENTER(&Http2ClientSession::state_complete_frame_read, event);
++   ink_assert(event == VC_EVENT_READ_COMPLETE || event == VC_EVENT_READ_READY);
++   if (this->sm_reader->read_avail() < this->current_hdr.length) {
++-    vio->reenable();
+++    if (this->_should_do_something_else()) {
+++      if (this->_reenable_event == nullptr) {
+++        vio->disable();
+++        this->_reenable_event = mutex->thread_holding->schedule_in(this, HRTIME_MSECONDS(1), HTTP2_SESSION_EVENT_REENABLE, vio);
+++      } else {
+++        vio->reenable();
+++      }
+++    } else {
+++      vio->reenable();
+++    }
++     return 0;
++   }
++   Http2SsnDebug("completed frame read, %" PRId64 " bytes available", this->sm_reader->read_avail());
++@@ -495,6 +516,7 @@ Http2ClientSession::do_complete_frame_re
++   Http2Frame frame(this->current_hdr, this->sm_reader);
++   send_connection_event(&this->connection_state, HTTP2_SESSION_EVENT_RECV, &frame);
++   this->sm_reader->consume(this->current_hdr.length);
+++  ++(this->_n_frame_read);
++ 
++   // Set the event handler if there is no more data to process a new frame
++   HTTP2_SET_SESSION_HANDLER(&Http2ClientSession::state_start_frame_read);
++@@ -510,9 +532,19 @@ Http2ClientSession::state_process_frame_
++   }
++ 
++   while (this->sm_reader->read_avail() >= (int64_t)HTTP2_FRAME_HEADER_LEN) {
+++
+++    Http2ErrorCode err = Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+++    if (this->connection_state.get_stream_error_rate() > std::min(1.0, Http2::stream_error_rate_threshold * 2.0)) {
+++      ip_port_text_buffer ipb;
+++      const char *client_ip = ats_ip_ntop(get_client_addr(), ipb, sizeof(ipb));
+++      Error("HTTP/2 session error client_ip=%s session_id=%" PRId64
+++            " closing a connection, because its stream error rate (%f) is too high",
+++            client_ip, connection_id(), this->connection_state.get_stream_error_rate());
+++      err = Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM;
+++    }
+++
++     // Return if there was an error
++-    Http2ErrorCode err;
++-    if (do_start_frame_read(err) < 0) {
+++    if (err > Http2ErrorCode::HTTP2_ERROR_NO_ERROR || do_start_frame_read(err) < 0) {
++       // send an error if specified.  Otherwise, just go away
++       if (err > Http2ErrorCode::HTTP2_ERROR_NO_ERROR) {
++         SCOPED_MUTEX_LOCK(lock, this->connection_state.mutex, this_ethread());
++@@ -531,6 +563,14 @@ Http2ClientSession::state_process_frame_
++       break;
++     }
++     do_complete_frame_read();
+++
+++    if (this->_should_do_something_else()) {
+++      if (this->_reenable_event == nullptr) {
+++        vio->disable();
+++        this->_reenable_event = mutex->thread_holding->schedule_in(this, HRTIME_MSECONDS(1), HTTP2_SESSION_EVENT_REENABLE, vio);
+++        return 0;
+++      }
+++    }
++   }
++ 
++   // If the client hasn't shut us down, reenable
++@@ -539,3 +579,10 @@ Http2ClientSession::state_process_frame_
++   }
++   return 0;
++ }
+++
+++bool
+++Http2ClientSession::_should_do_something_else()
+++{
+++  // Do something else every 128 incoming frames
+++  return (this->_n_frame_read & 0x7F) == 0;
+++}
++--- a/proxy/http2/Http2ClientSession.h
+++++ b/proxy/http2/Http2ClientSession.h
++@@ -42,6 +42,7 @@
++ #define HTTP2_SESSION_EVENT_XMIT (HTTP2_SESSION_EVENTS_START + 4)
++ #define HTTP2_SESSION_EVENT_SHUTDOWN_INIT (HTTP2_SESSION_EVENTS_START + 5)
++ #define HTTP2_SESSION_EVENT_SHUTDOWN_CONT (HTTP2_SESSION_EVENTS_START + 6)
+++#define HTTP2_SESSION_EVENT_REENABLE (HTTP2_SESSION_EVENTS_START + 7)
++ 
++ size_t const HTTP2_HEADER_BUFFER_SIZE_INDEX = CLIENT_CONNECTION_FIRST_READ_BUFFER_SIZE_INDEX;
++ 
++@@ -335,6 +336,8 @@ private:
++   // if there are multiple frames ready on the wire
++   int state_process_frame_read(int event, VIO *vio, bool inside_frame);
++ 
+++  bool _should_do_something_else();
+++
++   int64_t total_write_len        = 0;
++   SessionHandler session_handler = nullptr;
++   NetVConnection *client_vc      = nullptr;
++@@ -358,6 +361,9 @@ private:
++ 
++   InkHashTable *h2_pushed_urls = nullptr;
++   uint32_t h2_pushed_urls_size = 0;
+++
+++  Event *_reenable_event = nullptr;
+++  int _n_frame_read      = 0;
++ };
++ 
++ extern ClassAllocator<Http2ClientSession> http2ClientSessionAllocator;
++--- a/proxy/http2/Http2ConnectionState.cc
+++++ b/proxy/http2/Http2ConnectionState.cc
++@@ -27,6 +27,7 @@
++ #include "Http2Stream.h"
++ #include "Http2DebugNames.h"
++ #include <sstream>
+++#include <numeric>
++ 
++ #define Http2ConDebug(ua_session, fmt, ...) \
++   SsnDebug(ua_session, "http2_con", "[%" PRId64 "] " fmt, ua_session->connection_id(), ##__VA_ARGS__);
++@@ -137,18 +138,18 @@ rcv_data_frame(Http2ConnectionState &cst
++   }
++ 
++   // Check whether Window Size is acceptable
++-  if (cstate.server_rwnd < payload_length) {
+++  if (cstate.server_rwnd() < payload_length) {
++     return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_FLOW_CONTROL_ERROR,
++                       "recv data cstate.server_rwnd < payload_length");
++   }
++-  if (stream->server_rwnd < payload_length) {
+++  if (stream->server_rwnd() < payload_length) {
++     return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_FLOW_CONTROL_ERROR,
++                       "recv data stream->server_rwnd < payload_length");
++   }
++ 
++   // Update Window size
++-  cstate.server_rwnd -= payload_length;
++-  stream->server_rwnd -= payload_length;
+++  cstate.decrement_server_rwnd(payload_length);
+++  stream->decrement_server_rwnd(payload_length);
++ 
++   const uint32_t unpadded_length = payload_length - pad_length;
++   // If we call write() multiple times, we must keep the same reader, so we can
++@@ -174,15 +175,15 @@ rcv_data_frame(Http2ConnectionState &cst
++   uint32_t initial_rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
++   uint32_t min_rwnd     = std::min(initial_rwnd, cstate.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE));
++   // Connection level WINDOW UPDATE
++-  if (cstate.server_rwnd <= min_rwnd) {
++-    Http2WindowSize diff_size = initial_rwnd - cstate.server_rwnd;
++-    cstate.server_rwnd += diff_size;
+++  if (cstate.server_rwnd() <= min_rwnd) {
+++    Http2WindowSize diff_size = initial_rwnd - cstate.server_rwnd();
+++    cstate.increment_server_rwnd(diff_size);
++     cstate.send_window_update_frame(0, diff_size);
++   }
++   // Stream level WINDOW UPDATE
++-  if (stream->server_rwnd <= min_rwnd) {
++-    Http2WindowSize diff_size = initial_rwnd - stream->server_rwnd;
++-    stream->server_rwnd += diff_size;
+++  if (stream->server_rwnd() <= min_rwnd) {
+++    Http2WindowSize diff_size = initial_rwnd - stream->server_rwnd();
+++    stream->increment_server_rwnd(diff_size);
++     cstate.send_window_update_frame(stream->get_id(), diff_size);
++   }
++ 
++@@ -406,6 +407,17 @@ rcv_priority_frame(Http2ConnectionState
++                       "PRIORITY frame depends on itself");
++   }
++ 
+++  // Update PRIORITY frame count per minute
+++  cstate.increment_received_priority_frame_count();
+++  // Close this conection if its priority frame count received exceeds a limit
+++  if (cstate.get_received_priority_frame_count() > Http2::max_priority_frames_per_minute) {
+++    Http2StreamDebug(cstate.ua_session, stream_id,
+++                     "Observed too frequent priority changes: %u priority changes within a last minute",
+++                     cstate.get_received_priority_frame_count());
+++    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+++                      "recv priority too frequent priority changes");
+++  }
+++
++   if (!Http2::stream_priority_enabled) {
++     return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE);
++   }
++@@ -511,6 +523,16 @@ rcv_settings_frame(Http2ConnectionState
++     Warning("Setting frame for zombied sessoin %" PRId64, cstate.ua_session->connection_id());
++   }
++ 
+++  // Update SETTIGNS frame count per minute
+++  cstate.increment_received_settings_frame_count();
+++  // Close this conection if its SETTINGS frame count exceeds a limit
+++  if (cstate.get_received_settings_frame_count() > Http2::max_settings_frames_per_minute) {
+++    Http2StreamDebug(cstate.ua_session, stream_id, "Observed too frequent SETTINGS frames: %u frames within a last minute",
+++                     cstate.get_received_settings_frame_count());
+++    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+++                      "recv settings too frequent SETTINGS frames");
+++  }
+++
++   // [RFC 7540] 6.5. The stream identifier for a SETTINGS frame MUST be zero.
++   // If an endpoint receives a SETTINGS frame whose stream identifier field is
++   // anything other than 0x0, the endpoint MUST respond with a connection
++@@ -614,6 +636,16 @@ rcv_ping_frame(Http2ConnectionState &cst
++                       "ping bad length");
++   }
++ 
+++  // Update PING frame count per minute
+++  cstate.increment_received_ping_frame_count();
+++  // Close this conection if its ping count received exceeds a limit
+++  if (cstate.get_received_ping_frame_count() > Http2::max_ping_frames_per_minute) {
+++    Http2StreamDebug(cstate.ua_session, stream_id, "Observed too frequent PING frames: %u PING frames within a last minute",
+++                     cstate.get_received_ping_frame_count());
+++    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+++                      "recv ping too frequent PING frame");
+++  }
+++
++   // An endpoint MUST NOT respond to PING frames containing this flag.
++   if (frame.header().flags & HTTP2_FLAGS_PING_ACK) {
++     return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE);
++@@ -695,7 +727,7 @@ rcv_window_update_frame(Http2ConnectionS
++   if (stream_id == 0) {
++     // Connection level window update
++     Http2StreamDebug(cstate.ua_session, stream_id, "Received WINDOW_UPDATE frame - updated to: %zd delta: %u",
++-                     (cstate.client_rwnd + size), size);
+++                     (cstate.client_rwnd() + size), size);
++ 
++     // A sender MUST NOT allow a flow-control window to exceed 2^31-1
++     // octets.  If a sender receives a WINDOW_UPDATE that causes a flow-
++@@ -704,12 +736,16 @@ rcv_window_update_frame(Http2ConnectionS
++     // sends a RST_STREAM with an error code of FLOW_CONTROL_ERROR; for the
++     // connection, a GOAWAY frame with an error code of FLOW_CONTROL_ERROR
++     // is sent.
++-    if (size > HTTP2_MAX_WINDOW_SIZE - cstate.client_rwnd) {
+++    if (size > HTTP2_MAX_WINDOW_SIZE - cstate.client_rwnd()) {
++       return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_FLOW_CONTROL_ERROR,
++                         "window update too big");
++     }
++ 
++-    cstate.client_rwnd += size;
+++    auto error = cstate.increment_client_rwnd(size);
+++    if (error != Http2ErrorCode::HTTP2_ERROR_NO_ERROR) {
+++      return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, error);
+++    }
+++
++     cstate.restart_streams();
++   } else {
++     // Stream level window update
++@@ -725,7 +761,7 @@ rcv_window_update_frame(Http2ConnectionS
++     }
++ 
++     Http2StreamDebug(cstate.ua_session, stream_id, "Received WINDOW_UPDATE frame - updated to: %zd delta: %u",
++-                     (stream->client_rwnd + size), size);
+++                     (stream->client_rwnd() + size), size);
++ 
++     // A sender MUST NOT allow a flow-control window to exceed 2^31-1
++     // octets.  If a sender receives a WINDOW_UPDATE that causes a flow-
++@@ -734,14 +770,17 @@ rcv_window_update_frame(Http2ConnectionS
++     // sends a RST_STREAM with an error code of FLOW_CONTROL_ERROR; for the
++     // connection, a GOAWAY frame with an error code of FLOW_CONTROL_ERROR
++     // is sent.
++-    if (size > HTTP2_MAX_WINDOW_SIZE - stream->client_rwnd) {
+++    if (size > HTTP2_MAX_WINDOW_SIZE - stream->client_rwnd()) {
++       return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_FLOW_CONTROL_ERROR,
++                         "window update too big 2");
++     }
++ 
++-    stream->client_rwnd += size;
++-    ssize_t wnd = std::min(cstate.client_rwnd, stream->client_rwnd);
+++    auto error = stream->increment_client_rwnd(size);
+++    if (error != Http2ErrorCode::HTTP2_ERROR_NO_ERROR) {
+++      return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, error);
+++    }
++ 
+++    ssize_t wnd = std::min(cstate.client_rwnd(), stream->client_rwnd());
++     if (!stream->is_closed() && stream->get_state() == Http2StreamState::HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE && wnd > 0) {
++       SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread());
++       stream->restart_sending();
++@@ -1137,7 +1176,7 @@ Http2ConnectionState::restart_streams()
++     while (s != end) {
++       Http2Stream *next = static_cast<Http2Stream *>(s->link.next ? s->link.next : stream_list.head);
++       if (!s->is_closed() && s->get_state() == Http2StreamState::HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE &&
++-          std::min(this->client_rwnd, s->client_rwnd) > 0) {
+++          std::min(this->client_rwnd(), s->client_rwnd()) > 0) {
++         SCOPED_MUTEX_LOCK(lock, s->mutex, this_ethread());
++         s->restart_sending();
++       }
++@@ -1145,7 +1184,7 @@ Http2ConnectionState::restart_streams()
++       s = next;
++     }
++     if (!s->is_closed() && s->get_state() == Http2StreamState::HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE &&
++-        std::min(this->client_rwnd, s->client_rwnd) > 0) {
+++        std::min(this->client_rwnd(), s->client_rwnd()) > 0) {
++       SCOPED_MUTEX_LOCK(lock, s->mutex, this_ethread());
++       s->restart_sending();
++     }
++@@ -1284,7 +1323,7 @@ Http2ConnectionState::update_initial_rwn
++   // Update stream level window sizes
++   for (Http2Stream *s = stream_list.head; s; s = static_cast<Http2Stream *>(s->link.next)) {
++     SCOPED_MUTEX_LOCK(lock, s->mutex, this_ethread());
++-    s->client_rwnd = new_size - (client_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE) - s->client_rwnd);
+++    s->update_initial_rwnd(new_size - (client_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE) - s->client_rwnd()));
++   }
++ }
++ 
++@@ -1313,7 +1352,7 @@ Http2ConnectionState::send_data_frames_d
++   Http2DependencyTree::Node *node = dependency_tree->top();
++ 
++   // No node to send or no connection level window left
++-  if (node == nullptr || client_rwnd <= 0) {
+++  if (node == nullptr || _client_rwnd <= 0) {
++     return;
++   }
++ 
++@@ -1355,7 +1394,7 @@ Http2ConnectionState::send_data_frames_d
++ Http2SendDataFrameResult
++ Http2ConnectionState::send_a_data_frame(Http2Stream *stream, size_t &payload_length)
++ {
++-  const ssize_t window_size         = std::min(this->client_rwnd, stream->client_rwnd);
+++  const ssize_t window_size         = std::min(this->client_rwnd(), stream->client_rwnd());
++   const size_t buf_len              = BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_DATA]);
++   const size_t write_available_size = std::min(buf_len, static_cast<size_t>(window_size));
++   size_t read_available_size        = 0;
++@@ -1401,12 +1440,12 @@ Http2ConnectionState::send_a_data_frame(
++   }
++ 
++   // Update window size
++-  this->client_rwnd -= payload_length;
++-  stream->client_rwnd -= payload_length;
+++  this->decrement_client_rwnd(payload_length);
+++  stream->decrement_client_rwnd(payload_length);
++ 
++   // Create frame
++   Http2StreamDebug(ua_session, stream->get_id(), "Send a DATA frame - client window con: %5zd stream: %5zd payload: %5zd",
++-                   client_rwnd, stream->client_rwnd, payload_length);
+++                   _client_rwnd, stream->client_rwnd(), payload_length);
++ 
++   Http2Frame data(HTTP2_FRAME_TYPE_DATA, stream->get_id(), flags);
++   data.alloc(buffer_size_index[HTTP2_FRAME_TYPE_DATA]);
++@@ -1817,6 +1856,72 @@ Http2ConnectionState::send_window_update
++   this->ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &window_update);
++ }
++ 
+++void
+++Http2ConnectionState::increment_received_settings_frame_count()
+++{
+++  ink_hrtime hrtime_sec = Thread::get_hrtime() / HRTIME_SECOND;
+++  uint8_t counter_index = ((hrtime_sec % 60) >= 30);
+++
+++  if ((hrtime_sec - 60) > this->settings_frame_count_last_update) {
+++    this->settings_frame_count[0] = 0;
+++    this->settings_frame_count[1] = 0;
+++  } else if (counter_index != ((this->settings_frame_count_last_update % 60) >= 30)) {
+++    this->settings_frame_count[counter_index] = 0;
+++  }
+++  ++this->settings_frame_count[counter_index];
+++  this->settings_frame_count_last_update = hrtime_sec;
+++}
+++
+++uint32_t
+++Http2ConnectionState::get_received_settings_frame_count()
+++{
+++  return this->settings_frame_count[0] + this->settings_frame_count[1];
+++}
+++
+++void
+++Http2ConnectionState::increment_received_ping_frame_count()
+++{
+++  ink_hrtime hrtime_sec = Thread::get_hrtime() / HRTIME_SECOND;
+++  uint8_t counter_index = ((hrtime_sec % 60) >= 30);
+++
+++  if ((hrtime_sec - 60) > this->ping_frame_count_last_update) {
+++    this->ping_frame_count[0] = 0;
+++    this->ping_frame_count[1] = 0;
+++  } else if (counter_index != ((this->ping_frame_count_last_update % 60) >= 30)) {
+++    this->ping_frame_count[counter_index] = 0;
+++  }
+++  ++this->ping_frame_count[counter_index];
+++  this->ping_frame_count_last_update = hrtime_sec;
+++}
+++
+++uint32_t
+++Http2ConnectionState::get_received_ping_frame_count()
+++{
+++  return this->ping_frame_count[0] + this->ping_frame_count[1];
+++}
+++
+++void
+++Http2ConnectionState::increment_received_priority_frame_count()
+++{
+++  ink_hrtime hrtime_sec = Thread::get_hrtime() / HRTIME_SECOND;
+++  uint8_t counter_index = ((hrtime_sec % 60) >= 30);
+++
+++  if ((hrtime_sec - 60) > this->priority_frame_count_last_update) {
+++    this->priority_frame_count[0] = 0;
+++    this->priority_frame_count[1] = 0;
+++  } else if (counter_index != ((this->priority_frame_count_last_update % 60) >= 30)) {
+++    this->priority_frame_count[counter_index] = 0;
+++  }
+++  ++this->priority_frame_count[counter_index];
+++  this->priority_frame_count_last_update = hrtime_sec;
+++}
+++
+++uint32_t
+++Http2ConnectionState::get_received_priority_frame_count()
+++{
+++  return this->priority_frame_count[0] + this->priority_frame_count[1];
+++}
+++
++ // Return min_concurrent_streams_in when current client streams number is larger than max_active_streams_in.
++ // Main purpose of this is preventing DDoS Attacks.
++ unsigned
++@@ -1849,3 +1954,52 @@ Http2ConnectionState::_adjust_concurrent
++ 
++   return Http2::max_concurrent_streams_in;
++ }
+++
+++ssize_t
+++Http2ConnectionState::client_rwnd() const
+++{
+++  return this->_client_rwnd;
+++}
+++
+++Http2ErrorCode
+++Http2ConnectionState::increment_client_rwnd(size_t amount)
+++{
+++  this->_client_rwnd += amount;
+++
+++  this->_recent_rwnd_increment[this->_recent_rwnd_increment_index] = amount;
+++  ++this->_recent_rwnd_increment_index;
+++  this->_recent_rwnd_increment_index %= this->_recent_rwnd_increment.size();
+++  double sum = std::accumulate(this->_recent_rwnd_increment.begin(), this->_recent_rwnd_increment.end(), 0.0);
+++  double avg = sum / this->_recent_rwnd_increment.size();
+++  if (avg < Http2::min_avg_window_update) {
+++    return Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM;
+++  }
+++  return Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+++}
+++
+++Http2ErrorCode
+++Http2ConnectionState::decrement_client_rwnd(size_t amount)
+++{
+++  this->_client_rwnd -= amount;
+++  return Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+++}
+++
+++ssize_t
+++Http2ConnectionState::server_rwnd() const
+++{
+++  return this->_server_rwnd;
+++}
+++
+++Http2ErrorCode
+++Http2ConnectionState::increment_server_rwnd(size_t amount)
+++{
+++  this->_server_rwnd += amount;
+++  return Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+++}
+++
+++Http2ErrorCode
+++Http2ConnectionState::decrement_server_rwnd(size_t amount)
+++{
+++  this->_server_rwnd -= amount;
+++  return Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+++}
++--- a/proxy/http2/Http2ConnectionState.h
+++++ b/proxy/http2/Http2ConnectionState.h
++@@ -220,9 +220,16 @@ public:
++     return client_streams_in_count;
++   }
++ 
++-  // Connection level window size
++-  ssize_t client_rwnd = HTTP2_INITIAL_WINDOW_SIZE;
++-  ssize_t server_rwnd = Http2::initial_window_size;
+++  double
+++  get_stream_error_rate() const
+++  {
+++    int total = get_stream_requests();
+++    if (total > 0) {
+++      return (double)stream_error_count / (double)total;
+++    } else {
+++      return 0;
+++    }
+++  }
++ 
++   // HTTP/2 frame sender
++   void schedule_stream(Http2Stream *stream);
++@@ -292,6 +299,20 @@ public:
++     }
++   }
++ 
+++  void increment_received_settings_frame_count();
+++  uint32_t get_received_settings_frame_count();
+++  void increment_received_ping_frame_count();
+++  uint32_t get_received_ping_frame_count();
+++  void increment_received_priority_frame_count();
+++  uint32_t get_received_priority_frame_count();
+++
+++  ssize_t client_rwnd() const;
+++  Http2ErrorCode increment_client_rwnd(size_t amount);
+++  Http2ErrorCode decrement_client_rwnd(size_t amount);
+++  ssize_t server_rwnd() const;
+++  Http2ErrorCode increment_server_rwnd(size_t amount);
+++  Http2ErrorCode decrement_server_rwnd(size_t amount);
+++
++ private:
++   unsigned _adjust_concurrent_stream();
++ 
++@@ -330,4 +351,24 @@ private:
++   Event *shutdown_cont_event        = nullptr;
++   Event *fini_event                 = nullptr;
++   Event *zombie_event               = nullptr;
+++
+++  // Counter for stream errors ATS sent
+++  uint32_t stream_error_count;
+++
+++  // Connection level window size
+++  ssize_t _client_rwnd = HTTP2_INITIAL_WINDOW_SIZE;
+++  ssize_t _server_rwnd = Http2::initial_window_size;
+++
+++  std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX};
+++  int _recent_rwnd_increment_index           = 0;
+++
+++  // Counters for frames received within last 60 seconds
+++  // Each item in an array holds a count for 30 seconds.
+++  uint16_t settings_frame_count[2]            = {0};
+++  ink_hrtime settings_frame_count_last_update = 0;
+++  uint16_t ping_frame_count[2]                = {0};
+++  ink_hrtime ping_frame_count_last_update     = 0;
+++  uint16_t priority_frame_count[2]            = {0};
+++  ink_hrtime priority_frame_count_last_update = 0;
+++
++ };
++--- a/proxy/http2/Http2Stream.cc
+++++ b/proxy/http2/Http2Stream.cc
++@@ -26,6 +26,8 @@
++ #include "Http2ClientSession.h"
++ #include "../http/HttpSM.h"
++ 
+++#include <numeric>
+++
++ #define Http2StreamDebug(fmt, ...) \
++   SsnDebug(parent, "http2_stream", "[%" PRId64 "] [%u] " fmt, parent->connection_id(), this->get_id(), ##__VA_ARGS__);
++ 
++@@ -812,6 +814,63 @@ Http2Stream::response_process_data(bool
++   }
++ }
++ 
+++ssize_t
+++Http2Stream::client_rwnd() const
+++{
+++  return this->_client_rwnd;
+++}
+++
+++Http2ErrorCode
+++Http2Stream::increment_client_rwnd(size_t amount)
+++{
+++  this->_client_rwnd += amount;
+++
+++  this->_recent_rwnd_increment[this->_recent_rwnd_increment_index] = amount;
+++  ++this->_recent_rwnd_increment_index;
+++  this->_recent_rwnd_increment_index %= this->_recent_rwnd_increment.size();
+++  double sum = std::accumulate(this->_recent_rwnd_increment.begin(), this->_recent_rwnd_increment.end(), 0.0);
+++  double avg = sum / this->_recent_rwnd_increment.size();
+++  if (avg < Http2::min_avg_window_update) {
+++    return Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM;
+++  }
+++  return Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+++}
+++
+++Http2ErrorCode
+++Http2Stream::decrement_client_rwnd(size_t amount)
+++{
+++  this->_client_rwnd -= amount;
+++  if (this->_client_rwnd < 0) {
+++    return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
+++  } else {
+++    return Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+++  }
+++}
+++
+++ssize_t
+++Http2Stream::server_rwnd() const
+++{
+++  return this->_server_rwnd;
+++}
+++
+++Http2ErrorCode
+++Http2Stream::increment_server_rwnd(size_t amount)
+++{
+++  this->_server_rwnd += amount;
+++  return Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+++}
+++
+++Http2ErrorCode
+++Http2Stream::decrement_server_rwnd(size_t amount)
+++{
+++  this->_server_rwnd -= amount;
+++  if (this->_server_rwnd < 0) {
+++    return Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR;
+++  } else {
+++    return Http2ErrorCode::HTTP2_ERROR_NO_ERROR;
+++  }
+++}
+++
++ bool
++ Http2Stream::response_is_data_available() const
++ {
++--- a/proxy/http2/Http2Stream.h
+++++ b/proxy/http2/Http2Stream.h
++@@ -38,7 +38,7 @@ class Http2Stream : public ProxyClientTr
++ {
++ public:
++   typedef ProxyClientTransaction super; ///< Parent type.
++-  Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size) : client_rwnd(initial_rwnd), _id(sid)
+++  Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size) : _id(sid), _client_rwnd(initial_rwnd)
++   {
++     SET_HANDLER(&Http2Stream::main_event_handler);
++   }
++@@ -46,10 +46,10 @@ public:
++   void
++   init(Http2StreamId sid, ssize_t initial_rwnd)
++   {
++-    _id               = sid;
++-    _start_time       = Thread::get_hrtime();
++-    _thread           = this_ethread();
++-    this->client_rwnd = initial_rwnd;
+++    _id                = sid;
+++    _start_time        = Thread::get_hrtime();
+++    _thread            = this_ethread();
+++    this->_client_rwnd = initial_rwnd;
++     HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT, _thread);
++     HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_STREAM_COUNT, _thread);
++     sm_reader = request_reader = request_buffer.alloc_reader();
++@@ -117,7 +117,7 @@ public:
++   void
++   update_initial_rwnd(Http2WindowSize new_size)
++   {
++-    client_rwnd = new_size;
+++    this->_client_rwnd = new_size;
++   }
++ 
++   bool
++@@ -171,8 +171,12 @@ public:
++   void push_promise(URL &url, const MIMEField *accept_encoding);
++ 
++   // Stream level window size
++-  ssize_t client_rwnd;
++-  ssize_t server_rwnd = Http2::initial_window_size;
+++  ssize_t client_rwnd() const;
+++  Http2ErrorCode increment_client_rwnd(size_t amount);
+++  Http2ErrorCode decrement_client_rwnd(size_t amount);
+++  ssize_t server_rwnd() const;
+++  Http2ErrorCode increment_server_rwnd(size_t amount);
+++  Http2ErrorCode decrement_server_rwnd(size_t amount);
++ 
++   uint8_t *header_blocks        = nullptr;
++   uint32_t header_blocks_length = 0;  // total length of header blocks (not include
++@@ -288,6 +292,12 @@ private:
++   uint64_t data_length = 0;
++   uint64_t bytes_sent  = 0;
++ 
+++  ssize_t _client_rwnd;
+++  ssize_t _server_rwnd = Http2::initial_window_size;
+++
+++  std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX};
+++  int _recent_rwnd_increment_index           = 0;
+++
++   ChunkedHandler chunked_handler;
++   Event *cross_thread_event      = nullptr;
++   Event *buffer_full_write_event = nullptr;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b252d5bcdceb4c5d67e82dd3ce809bc7f0b520b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++Description: HTTP/2 fix with realloc (CVE-2019-9518)
++Author: Bryan Call <bcall@apache.org>
++Origin: backport, https://github.com/apache/trafficserver/pull/5850
++Reviewed-by: Jean Baptiste Favre <debian@jbfavre.org>
++Last-Update: 2019-08-26
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/proxy/http2/Http2ConnectionState.cc
+++++ b/proxy/http2/Http2ConnectionState.cc
++@@ -304,10 +304,12 @@ rcv_headers_frame(Http2ConnectionState &
++     }
++   }
++ 
++-  stream->header_blocks = static_cast<uint8_t *>(ats_malloc(header_block_fragment_length));
++-  frame.reader()->memcpy(stream->header_blocks, header_block_fragment_length, header_block_fragment_offset);
+++  if (header_block_fragment_length > 0) {
+++    stream->header_blocks = static_cast<uint8_t *>(ats_malloc(header_block_fragment_length));
+++    frame.reader()->memcpy(stream->header_blocks, header_block_fragment_length, header_block_fragment_offset);
++ 
++-  stream->header_blocks_length = header_block_fragment_length;
+++    stream->header_blocks_length = header_block_fragment_length;
+++  }
++ 
++   if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
++     // NOTE: If there are END_HEADERS flag, decode stored Header Blocks.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81040df5424b6dd3a9bea78184eb68e011557a99
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,213 @@@
++Description: Fix for CVE-2019-17559
++Author: Bryan Call <bcall@apache.org>
++Origin: backport
++Applied-Upstream: https://github.com/apache/trafficserver/pull/6389
++Last-Update: 2020-04-16
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- trafficserver.orig/proxy/hdrs/HdrTest.cc  2020-04-16 19:03:21.522369124 +0200
+++++ trafficserver/proxy/hdrs/HdrTest.cc       2020-04-16 19:03:21.522369124 +0200
++@@ -343,44 +343,100 @@
++     // Start with an easy one...
++     "http://trafficserver.apache.org/index.html",
++ 
++-    // "cheese://bogosity",         This fails, but it's not clear it should work...
+++    "cheese://bogosity",
++ 
++-    "some.place", "some.place/", "http://some.place", "http://some.place/", "http://some.place/path",
++-    "http://some.place/path;params", "http://some.place/path;params?query", "http://some.place/path;params?query#fragment",
++-    "http://some.place/path?query#fragment", "http://some.place/path#fragment",
++-
++-    "some.place:80", "some.place:80/", "http://some.place:80", "http://some.place:80/",
++-
++-    "foo@some.place:80", "foo@some.place:80/", "http://foo@some.place:80", "http://foo@some.place:80/",
++-
++-    "foo:bar@some.place:80", "foo:bar@some.place:80/", "http://foo:bar@some.place:80", "http://foo:bar@some.place:80/",
+++    "some.place",
+++    "some.place/",
+++    "http://some.place",
+++    "http://some.place/",
+++    "http://some.place/path",
+++    "http://some.place/path;params",
+++    "http://some.place/path;params?query",
+++    "http://some.place/path;params?query#fragment",
+++    "http://some.place/path?query#fragment",
+++    "http://some.place/path#fragment",
+++
+++    "some.place:80",
+++    "some.place:80/",
+++    "http://some.place:80",
+++    "http://some.place:80/",
+++
+++    "foo@some.place:80",
+++    "foo@some.place:80/",
+++    "http://foo@some.place:80",
+++    "http://foo@some.place:80/",
+++
+++    "foo:bar@some.place:80",
+++    "foo:bar@some.place:80/",
+++    "http://foo:bar@some.place:80",
+++    "http://foo:bar@some.place:80/",
++ 
++     // Some address stuff
++-    "http://172.16.28.101", "http://172.16.28.101:8080", "http://[::]", "http://[::1]", "http://[fc01:172:16:28::101]",
++-    "http://[fc01:172:16:28::101]:80", "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]",
++-    "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080", "http://172.16.28.101/some/path", "http://172.16.28.101:8080/some/path",
++-    "http://[::1]/some/path", "http://[fc01:172:16:28::101]/some/path", "http://[fc01:172:16:28::101]:80/some/path",
++-    "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]/some/path", "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/some/path",
++-    "http://172.16.28.101/", "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/",
+++    "http://172.16.28.101",
+++    "http://172.16.28.101:8080",
+++    "http://[::]",
+++    "http://[::1]",
+++    "http://[fc01:172:16:28::101]",
+++    "http://[fc01:172:16:28::101]:80",
+++    "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]",
+++    "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080",
+++    "http://172.16.28.101/some/path",
+++    "http://172.16.28.101:8080/some/path",
+++    "http://[::1]/some/path",
+++    "http://[fc01:172:16:28::101]/some/path",
+++    "http://[fc01:172:16:28::101]:80/some/path",
+++    "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]/some/path",
+++    "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/some/path",
+++    "http://172.16.28.101/",
+++    "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/",
+++
+++    // "foo:@some.place", TODO - foo:@some.place is change to foo@some.place in the test
+++    "foo:bar@some.place",
+++    "foo:bar@some.place/",
+++    "http://foo:bar@some.place",
+++    "http://foo:bar@some.place/",
+++    "http://foo:bar@[::1]:8080/",
+++    "http://foo@[::1]",
+++
+++    "mms://sm02.tsqa.example.com/0102rally.asf",
+++    "pnm://foo:bar@some.place:80/path;params?query#fragment",
+++    "rtsp://foo:bar@some.place:80/path;params?query#fragment",
+++    "rtspu://foo:bar@some.place:80/path;params?query#fragment",
+++    "/finance/external/cbsm/*http://cbs.marketwatch.com/archive/19990713/news/current/net.htx?source=blq/yhoo&dist=yhoo",
+++    "http://a.b.com/xx.jpg?newpath=http://bob.dave.com",
++ 
++-    "foo:bar@some.place", "foo:bar@some.place/", "http://foo:bar@some.place", "http://foo:bar@some.place/",
++-    "http://foo:bar@[::1]:8080/", "http://foo@[::1]",
+++    "ht-tp://a.b.com",
+++    "ht+tp://a.b.com",
+++    "ht.tp://a.b.com",
++ 
++-    "mms://sm02.tsqa.example.com/0102rally.asf", "pnm://foo:bar@some.place:80/path;params?query#fragment",
++-    "rtsp://foo:bar@some.place:80/path;params?query#fragment", "rtspu://foo:bar@some.place:80/path;params?query#fragment",
++-    "/finance/external/cbsm/*http://cbs.marketwatch.com/archive/19990713/news/current/net.htx?source=blq/yhoo&dist=yhoo",
++-    "http://a.b.com/xx.jpg?newpath=http://bob.dave.com"};
+++    "h1ttp://a.b.com",
+++    "http1://a.b.com",
+++  };
++ 
++   static const char *bad[] = {
++     "http://[1:2:3:4:5:6:7:8:9]",
++     "http://1:2:3:4:5:6:7:8:A:B",
++     "http://bob.com[::1]",
++     "http://[::1].com",
+++
++     "http://foo:bar:baz@bob.com/",
++     "http://foo:bar:baz@[::1]:8080/",
+++
++     "http://]",
++     "http://:",
+++
++     "http:/",
+++    "http:/foo.bar.com/",
+++    "~http://invalid.char.in.scheme/foo",
+++    "http~://invalid.char.in.scheme/foo",
+++    "ht~tp://invalid.char.in.scheme/foo",
+++    "1http://first.char.not.alpha",
+++    "some.domain.com/http://invalid.domain/foo",
+++    ":",
+++    "://",
+++
+++    // maybe this should be a valid URL
+++    "a.b.com/xx.jpg?newpath=http://bob.dave.com",
++   };
++ 
++   int err, failed;
++@@ -400,6 +456,7 @@
++     url.create(nullptr);
++     err = url.parse(&start, end);
++     if (err < 0) {
+++      printf("Failed to parse url '%s'\n", start);
++       failed = 1;
++       break;
++     }
++@@ -448,6 +505,8 @@
++       failed = 1;
++       printf("Successfully parsed invalid url '%s'", x);
++       break;
+++    } else {
+++      printf("   bad URL - PARSE FAILED: '%s'\n", bad[i]);
++     }
++   }
++ 
++--- trafficserver.orig/proxy/hdrs/URL.cc      2020-04-16 19:03:21.522369124 +0200
+++++ trafficserver/proxy/hdrs/URL.cc   2020-04-16 19:03:21.522369124 +0200
++@@ -1120,34 +1120,41 @@
++   const char *scheme_end   = nullptr;
++   int scheme_wks_idx;
++ 
+++  // Skip over spaces
++   while (' ' == *cur && ++cur < end) {
++-    ;
++   }
+++
++   if (cur < end) {
++     scheme_start = scheme_end = cur;
++-    // special case 'http:' for performance
++-    if ((end - cur >= 5) && (((cur[0] ^ 'h') | (cur[1] ^ 't') | (cur[2] ^ 't') | (cur[3] ^ 'p') | (cur[4] ^ ':')) == 0)) {
++-      scheme_end = cur + 4; // point to colon
++-      url_scheme_set(heap, url, scheme_start, URL_WKSIDX_HTTP, 4, copy_strings_p);
++-    } else if ('/' != *cur) {
++-      // For forward transparent mode, the URL for the method can just be a path,
++-      // so don't scan that for a scheme, as we could find a false positive if there
++-      // is a URL in the parameters (which is legal).
+++
+++    // If the URL is more complex then a path, parse to see if there is a scheme
+++    if ('/' != *cur) {
+++      // Search for a : it could be part of a scheme or a username:password
++       while (':' != *cur && ++cur < end) {
++-        ;
++       }
++-      if (cur < end) { // found a colon
++-        scheme_wks_idx = hdrtoken_tokenize(scheme_start, cur - scheme_start, &scheme_wks);
++ 
++-        /*  Distinguish between a scheme only and a username by looking past the colon. If it is missing
++-            or it's a slash, presume scheme. Otherwise it's a username with a password.
++-        */
++-        if ((scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME) || // known scheme
++-            (cur >= end - 1 || cur[1] == '/')) // no more data or slash past colon
++-        {
++-          scheme_end = cur;
++-          url_scheme_set(heap, url, scheme_start, scheme_wks_idx, scheme_end - scheme_start, copy_strings_p);
+++      // If there is a :// then there is a scheme
+++      if (cur + 2 < end && cur[1] == '/' && cur[2] == '/') { // found "://"
+++        scheme_end     = cur;
+++        scheme_wks_idx = hdrtoken_tokenize(scheme_start, scheme_end - scheme_start, &scheme_wks);
+++
+++        if (!(scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME)) {
+++          // Unknown scheme, validate the scheme
+++
+++          // RFC 3986 Section 3.1
+++          // These are the valid characters in a scheme:
+++          //   scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+++          // return an error if there is another character in the scheme
+++          if (!ParseRules::is_alpha(*scheme_start)) {
+++            return PARSE_RESULT_ERROR;
+++          }
+++          for (cur = scheme_start + 1; cur < scheme_end; ++cur) {
+++            if (!(ParseRules::is_alnum(*cur) != 0 || *cur == '+' || *cur == '-' || *cur == '.')) {
+++              return PARSE_RESULT_ERROR;
+++            }
+++          }
++         }
+++        url_scheme_set(heap, url, scheme_start, scheme_wks_idx, scheme_end - scheme_start, copy_strings_p);
++       }
++     }
++     *start = scheme_end;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ca4ce750b3938f09951cb4b4dd10ba81fa2af95e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,262 @@@
++Description: Fix for CVE-2019-17565
++Author: Bryan Call <bcall@apache.org>
++Origin: backport
++Applied-Upstream: https://github.com/apache/trafficserver/pull/6398
++Last-Update: 2020-04-16
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- trafficserver.orig/iocore/net/UnixNetVConnection.cc       2020-04-16 19:12:51.968334236 +0200
+++++ trafficserver/iocore/net/UnixNetVConnection.cc    2020-04-16 19:12:51.964334223 +0200
++@@ -933,7 +933,7 @@
++     try_to_write  = 0;
++ 
++     while (niov < NET_MAX_IOV) {
++-      int64_t wavail = towrite - total_written;
+++      int64_t wavail = towrite - total_written - try_to_write;
++       int64_t len    = tmp_reader->block_read_avail();
++ 
++       // Check if we have done this block.
++--- trafficserver.orig/proxy/http/HttpTransact.cc     2020-04-16 19:12:51.968334236 +0200
+++++ trafficserver/proxy/http/HttpTransact.cc  2020-04-16 19:12:51.964334223 +0200
++@@ -6785,9 +6785,8 @@
++ 
++     // check that the client is HTTP 1.1 and the conf allows chunking or the client
++     // protocol unchunks before returning to the user agent (i.e. is http/2)
++-    if (s->client_info.http_version == HTTPVersion(1, 1) &&
++-        (s->txn_conf->chunking_enabled == 1 ||
++-         (s->state_machine->plugin_tag && (!strncmp(s->state_machine->plugin_tag, "http/2", 6)))) &&
+++    if (s->client_info.http_version == HTTPVersion(1, 1) && s->txn_conf->chunking_enabled == 1 &&
+++        s->state_machine->ua_txn->is_chunked_encoding_supported() &&
++         // if we're not sending a body, don't set a chunked header regardless of server response
++         !is_response_body_precluded(s->hdr_info.client_response.status_get(), s->method) &&
++         // we do not need chunked encoding for internal error messages
++--- trafficserver.orig/proxy/http/HttpTunnel.cc       2020-04-16 19:12:51.968334236 +0200
+++++ trafficserver/proxy/http/HttpTunnel.cc    2020-04-16 19:12:51.968334236 +0200
++@@ -871,10 +871,9 @@
++     consumer_n = (producer_n = INT64_MAX);
++   }
++ 
++-  // Do the IO on the consumers first so
++-  //  data doesn't disappear out from
++-  //  under the tunnel
++-  for (c = p->consumer_list.head; c;) {
+++  // At least set up the consumer readers first so the data
+++  // doesn't disappear out from under the tunnel
+++  for (c = p->consumer_list.head; c; c = c->link.next) {
++     // Create a reader for each consumer.  The reader allows
++     // us to implement skip bytes
++     if (c->vc_type == HT_CACHE_WRITE) {
++@@ -905,45 +904,6 @@
++       ink_assert(c->skip_bytes <= c->buffer_reader->read_avail());
++       c->buffer_reader->consume(c->skip_bytes);
++     }
++-    int64_t c_write = consumer_n;
++-
++-    // INKqa05109 - if we don't know the length leave it at
++-    //  INT64_MAX or else the cache may bounce the write
++-    //  because it thinks the document is too big.  INT64_MAX
++-    //  is a special case for the max document size code
++-    //  in the cache
++-    if (c_write != INT64_MAX) {
++-      c_write -= c->skip_bytes;
++-    }
++-    // Fix for problems with not chunked content being chunked and
++-    // not sending the entire data.  The content length grows when
++-    // it is being chunked.
++-    if (p->do_chunking == true) {
++-      c_write = INT64_MAX;
++-    }
++-
++-    if (c_write == 0) {
++-      // Nothing to do, call back the cleanup handlers
++-      c->write_vio = nullptr;
++-      consumer_handler(VC_EVENT_WRITE_COMPLETE, c);
++-    } else {
++-      // In the client half close case, all the data that will be sent
++-      // from the client is already in the buffer.  Go ahead and set
++-      // the amount to read since we know it.  We will forward the FIN
++-      // to the server on VC_EVENT_WRITE_COMPLETE.
++-      if (p->vc_type == HT_HTTP_CLIENT) {
++-        ProxyClientTransaction *ua_vc = static_cast<ProxyClientTransaction *>(p->vc);
++-        if (ua_vc->get_half_close_flag()) {
++-          c_write          = c->buffer_reader->read_avail();
++-          p->alive         = false;
++-          p->handler_state = HTTP_SM_POST_SUCCESS;
++-        }
++-      }
++-      c->write_vio = c->vc->do_io_write(this, c_write, c->buffer_reader);
++-      ink_assert(c_write > 0);
++-    }
++-
++-    c = c->link.next;
++   }
++ 
++   // YTS Team, yamsat Plugin
++@@ -1003,7 +963,56 @@
++       producer_n = 0;
++     }
++   }
+++  for (c = p->consumer_list.head; c; c = c->link.next) {
+++    int64_t c_write = consumer_n;
++ 
+++    if (!p->alive) {
+++      // Adjust the amount of chunked data to write if the only data was in the initial read
+++      // The amount to write in some cases is dependent on the type of the consumer, so this
+++      // value must be computed for each consumer
+++      c_write = final_consumer_bytes_to_write(p, c);
+++    } else {
+++      // INKqa05109 - if we don't know the length leave it at
+++      //  INT64_MAX or else the cache may bounce the write
+++      //  because it thinks the document is too big.  INT64_MAX
+++      //  is a special case for the max document size code
+++      //  in the cache
+++      if (c_write != INT64_MAX) {
+++        c_write -= c->skip_bytes;
+++      }
+++      // Fix for problems with not chunked content being chunked and
+++      // not sending the entire data.  The content length grows when
+++      // it is being chunked.
+++      if (p->do_chunking == true) {
+++        c_write = INT64_MAX;
+++      }
+++    }
+++
+++    if (c_write == 0) {
+++      // Nothing to do, call back the cleanup handlers
+++      c->write_vio = nullptr;
+++      consumer_handler(VC_EVENT_WRITE_COMPLETE, c);
+++    } else {
+++      // In the client half close case, all the data that will be sent
+++      // from the client is already in the buffer.  Go ahead and set
+++      // the amount to read since we know it.  We will forward the FIN
+++      // to the server on VC_EVENT_WRITE_COMPLETE.
+++      if (p->vc_type == HT_HTTP_CLIENT) {
+++        ProxyClientTransaction *ua_vc = static_cast<ProxyClientTransaction *>(p->vc);
+++        if (ua_vc->get_half_close_flag()) {
+++          int tmp = c->buffer_reader->read_avail();
+++          if (tmp < c_write) {
+++            c_write = tmp;
+++          }
+++          p->alive         = false;
+++          p->handler_state = HTTP_SM_POST_SUCCESS;
+++        }
+++      }
+++      // Start the writes now that we know we will consume all the initial data
+++      c->write_vio = c->vc->do_io_write(this, c_write, c->buffer_reader);
+++      ink_assert(c_write > 0);
+++    }
+++  }
++   if (p->alive) {
++     ink_assert(producer_n >= 0);
++ 
++@@ -1184,8 +1193,8 @@
++     }
++   } // end of added logic for partial copy of POST
++ 
++-  Debug("http_redirect", "[HttpTunnel::producer_handler] enable_redirection: [%d %d %d] event: %d", p->alive == true,
++-        sm->enable_redirection, (p->self_consumer && p->self_consumer->alive == true), event);
+++  Debug("http_redirect", "[HttpTunnel::producer_handler] enable_redirection: [%d %d %d] event: %d, state: %d", p->alive == true,
+++        sm->enable_redirection, (p->self_consumer && p->self_consumer->alive == true), event, p->chunked_handler.state);
++ 
++   switch (event) {
++   case VC_EVENT_READ_READY:
++@@ -1470,21 +1479,18 @@
++   }
++ }
++ 
++-// void HttpTunnel::chain_finish_internal(HttpTunnelProducer* p)
++ //
++-//    Internal function for finishing all consumers.  Takes
++-//       chain argument about where to finish just immediate
++-//       consumer or all those downstream
+++// Determine the number of bytes a consumer should read from a producer
++ //
++-void
++-HttpTunnel::finish_all_internal(HttpTunnelProducer *p, bool chain)
+++int64_t
+++HttpTunnel::final_consumer_bytes_to_write(HttpTunnelProducer *p, HttpTunnelConsumer *c)
++ {
++-  ink_assert(p->alive == false);
++-  HttpTunnelConsumer *c         = p->consumer_list.head;
++-  int64_t total_bytes           = 0;
++-  TunnelChunkingAction_t action = p->chunking_action;
++-
++-  while (c) {
+++  int64_t total_bytes = 0;
+++  int64_t consumer_n  = 0;
+++  if (p->alive) {
+++    consumer_n = INT64_MAX;
+++  } else {
+++    TunnelChunkingAction_t action = p->chunking_action;
++     if (c->alive) {
++       if (c->vc_type == HT_CACHE_WRITE) {
++         switch (action) {
++@@ -1503,12 +1509,48 @@
++         total_bytes = p->chunked_handler.skip_bytes + p->chunked_handler.chunked_size;
++       } else if (action == TCA_DECHUNK_CONTENT) {
++         total_bytes = p->chunked_handler.skip_bytes + p->chunked_handler.dechunked_size;
+++      } else if (action == TCA_PASSTHRU_CHUNKED_CONTENT) {
+++        total_bytes = p->bytes_read + p->init_bytes_done;
++       } else {
++         total_bytes = p->bytes_read + p->init_bytes_done;
++       }
+++      consumer_n = total_bytes - c->skip_bytes;
+++    }
+++  }
+++  return consumer_n;
+++}
+++
+++//
+++// void HttpTunnel::finish_all_internal(HttpTunnelProducer* p)
+++//
+++//    Internal function for finishing all consumers.  Takes
+++//       chain argument about where to finish just immediate
+++//       consumer or all those downstream
+++//
+++void
+++HttpTunnel::finish_all_internal(HttpTunnelProducer *p, bool chain)
+++{
+++  ink_assert(p->alive == false);
+++  HttpTunnelConsumer *c         = p->consumer_list.head;
+++  int64_t total_bytes           = 0;
+++  TunnelChunkingAction_t action = p->chunking_action;
+++
+++  if (action == TCA_PASSTHRU_CHUNKED_CONTENT) {
+++    // if the only chunked data was in the initial read, make sure we don't consume too much
+++    if (p->bytes_read == 0) {
+++      int num_read = p->buffer_start->read_avail() - p->chunked_handler.chunked_reader->read_avail();
+++      if (num_read < p->init_bytes_done) {
+++        p->init_bytes_done = num_read;
+++      }
+++    }
+++  }
++ 
+++  while (c) {
+++    if (c->alive) {
++       if (c->write_vio) {
++-        c->write_vio->nbytes = total_bytes - c->skip_bytes;
+++        // Adjust the number of bytes to write in the case of
+++        // a completed unlimited producer
+++        c->write_vio->nbytes = final_consumer_bytes_to_write(p, c);
++         ink_assert(c->write_vio->nbytes >= 0);
++ 
++         if (c->write_vio->nbytes < 0) {
++@@ -1525,7 +1567,7 @@
++       //   is nothing to do.  Check to see if there is
++       //   nothing to do and take the appripriate
++       //   action
++-      if (c->write_vio && c->write_vio->nbytes == c->write_vio->ndone) {
+++      if (c->write_vio && c->alive && c->write_vio->nbytes == c->write_vio->ndone) {
++         consumer_handler(VC_EVENT_WRITE_COMPLETE, c);
++       }
++     }
++--- trafficserver.orig/proxy/http/HttpTunnel.h        2020-04-16 19:12:51.968334236 +0200
+++++ trafficserver/proxy/http/HttpTunnel.h     2020-04-16 19:12:51.968334236 +0200
++@@ -312,6 +312,7 @@
++   void chain_abort_all(HttpTunnelProducer *p);
++   void abort_cache_write_finish_others(HttpTunnelProducer *p);
++   void append_message_to_producer_buffer(HttpTunnelProducer *p, const char *msg, int64_t msg_len);
+++  int64_t final_consumer_bytes_to_write(HttpTunnelProducer *p, HttpTunnelConsumer *c);
++ 
++   /** Mark a producer and consumer as the same underlying object.
++ 
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..19d93e7cbbba27f590803a492bca989d9ee2a015
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++Description: Fix for CVE-2020-1944
++Author: Bryan Call <bcall@apache.org>
++Origin: backport
++Applied-Upstream: https://github.com/apache/trafficserver/pull/6390
++Last-Update: 2020-04-16
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- trafficserver.orig/proxy/hdrs/HTTP.cc     2020-04-16 19:20:19.621738649 +0200
+++++ trafficserver/proxy/hdrs/HTTP.cc  2020-04-16 19:23:55.166369632 +0200
++@@ -1125,19 +1125,18 @@
++ 
++     end                    = real_end;
++     parser->m_parsing_http = false;
++-
++-    ParseResult ret = mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof);
++-    // If we're done with the main parse do some validation
++-    if (ret == PARSE_RESULT_DONE) {
++-      ret = validate_hdr_host(hh); // check HOST header
++-    }
++-    if (ret == PARSE_RESULT_DONE) {
++-      ret = validate_hdr_content_length(heap, hh);
++-    }
++-    return ret;
++   }
++ 
++-  return mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof);
+++  ParseResult ret =
+++    mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof);
+++  // If we're done with the main parse do some validation
+++  if (ret == PARSE_RESULT_DONE) {
+++    ret = validate_hdr_host(hh); // check HOST header
+++  }
+++  if (ret == PARSE_RESULT_DONE) {
+++    ret = validate_hdr_content_length(heap, hh);
+++  }
+++  return ret;
++ }
++ 
++ ParseResult
++@@ -1189,7 +1188,7 @@
++     if (mime_hdr_field_find(hh->m_fields_impl, MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING) != nullptr) {
++       // Delete all Content-Length headers
++       Debug("http", "Transfer-Encoding header and Content-Length headers the request, removing all Content-Length headers");
++-      mime_hdr_field_delete(heap, hh->m_fields_impl, content_length_field);
+++      mime_hdr_field_delete(heap, hh->m_fields_impl, content_length_field, true);
++       return PARSE_RESULT_DONE;
++     }
++ 
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f865a71263877e8940b01dc7feaf3a51dcfee199
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,318 @@@
++Index: trafficserver/doc/admin-guide/files/records.config.en.rst
++===================================================================
++--- trafficserver.orig/doc/admin-guide/files/records.config.en.rst    2020-04-16 21:35:49.127162696 +0200
+++++ trafficserver/doc/admin-guide/files/records.config.en.rst 2020-04-16 21:35:49.123162682 +0200
++@@ -3493,7 +3493,7 @@
++    :ts:cv:`proxy.config.http2.min_concurrent_streams_in`.
++    To disable, set to zero (``0``).
++ 
++-.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 1048576
+++.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 65535
++    :reloadable:
++ 
++    The initial window size for inbound connections.
++Index: trafficserver/mgmt/RecordsConfig.cc
++===================================================================
++--- trafficserver.orig/mgmt/RecordsConfig.cc  2020-04-16 21:35:49.127162696 +0200
+++++ trafficserver/mgmt/RecordsConfig.cc       2020-04-16 21:35:49.123162682 +0200
++@@ -1304,7 +1304,7 @@
++   ,
++   {RECT_CONFIG, "proxy.config.http2.max_active_streams_in", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
++   ,
++-  {RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "1048576", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
+++  {RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "65535", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
++   ,
++   {RECT_CONFIG, "proxy.config.http2.max_frame_size", RECD_INT, "16384", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL}
++   ,
++Index: trafficserver/proxy/http2/HTTP2.cc
++===================================================================
++--- trafficserver.orig/proxy/http2/HTTP2.cc   2020-04-16 21:35:49.127162696 +0200
+++++ trafficserver/proxy/http2/HTTP2.cc        2020-04-16 21:35:49.123162682 +0200
++@@ -722,7 +722,7 @@
++ uint32_t Http2::max_active_streams_in          = 0;
++ bool Http2::throttling                         = false;
++ uint32_t Http2::stream_priority_enabled        = 0;
++-uint32_t Http2::initial_window_size            = 1048576;
+++uint32_t Http2::initial_window_size            = 65535;
++ uint32_t Http2::max_frame_size                 = 16384;
++ uint32_t Http2::header_table_size              = 4096;
++ uint32_t Http2::max_header_list_size           = 4294967295;
++Index: trafficserver/proxy/http2/Http2ClientSession.cc
++===================================================================
++--- trafficserver.orig/proxy/http2/Http2ClientSession.cc      2020-04-16 21:35:49.127162696 +0200
+++++ trafficserver/proxy/http2/Http2ClientSession.cc   2020-04-16 21:35:49.123162682 +0200
++@@ -348,11 +348,9 @@
++     break;
++ 
++   case VC_EVENT_WRITE_READY:
++-    retval = 0;
++-    break;
++-
++   case VC_EVENT_WRITE_COMPLETE:
++-    // Seems as this is being closed already
+++    this->connection_state.restart_streams();
+++
++     retval = 0;
++     break;
++ 
++@@ -586,3 +584,9 @@
++   // Do something else every 128 incoming frames
++   return (this->_n_frame_read & 0x7F) == 0;
++ }
+++
+++int64_t
+++Http2ClientSession::write_avail()
+++{
+++  return this->write_buffer->write_avail();
+++}
++Index: trafficserver/proxy/http2/Http2ClientSession.h
++===================================================================
++--- trafficserver.orig/proxy/http2/Http2ClientSession.h       2020-04-16 21:35:49.127162696 +0200
+++++ trafficserver/proxy/http2/Http2ClientSession.h    2020-04-16 21:35:49.123162682 +0200
++@@ -319,6 +319,8 @@
++     return write_buffer->max_read_avail();
++   }
++ 
+++  int64_t write_avail();
+++
++   // noncopyable
++   Http2ClientSession(Http2ClientSession &) = delete;
++   Http2ClientSession &operator=(const Http2ClientSession &) = delete;
++Index: trafficserver/proxy/http2/Http2ConnectionState.cc
++===================================================================
++--- trafficserver.orig/proxy/http2/Http2ConnectionState.cc    2020-04-16 21:35:49.127162696 +0200
+++++ trafficserver/proxy/http2/Http2ConnectionState.cc 2020-04-16 21:35:49.127162696 +0200
++@@ -151,6 +151,12 @@
++   cstate.decrement_server_rwnd(payload_length);
++   stream->decrement_server_rwnd(payload_length);
++ 
+++  if (is_debug_tag_set("http2_con")) {
+++    uint32_t rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
+++    Http2StreamDebug(cstate.ua_session, stream->get_id(), "Received DATA frame: rwnd con=%zd/%" PRId32 " stream=%zd/%" PRId32,
+++                     cstate.server_rwnd(), rwnd, stream->server_rwnd(), rwnd);
+++  }
+++
++   const uint32_t unpadded_length = payload_length - pad_length;
++   // If we call write() multiple times, we must keep the same reader, so we can
++   // update its offset via consume.  Otherwise, we will read the same data on the
++@@ -172,21 +178,6 @@
++   }
++   myreader->writer()->dealloc_reader(myreader);
++ 
++-  uint32_t initial_rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
++-  uint32_t min_rwnd     = std::min(initial_rwnd, cstate.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE));
++-  // Connection level WINDOW UPDATE
++-  if (cstate.server_rwnd() <= min_rwnd) {
++-    Http2WindowSize diff_size = initial_rwnd - cstate.server_rwnd();
++-    cstate.increment_server_rwnd(diff_size);
++-    cstate.send_window_update_frame(0, diff_size);
++-  }
++-  // Stream level WINDOW UPDATE
++-  if (stream->server_rwnd() <= min_rwnd) {
++-    Http2WindowSize diff_size = initial_rwnd - stream->server_rwnd();
++-    stream->increment_server_rwnd(diff_size);
++-    cstate.send_window_update_frame(stream->get_id(), diff_size);
++-  }
++-
++   return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE);
++ }
++ 
++@@ -1196,6 +1187,35 @@
++ }
++ 
++ void
+++Http2ConnectionState::restart_receiving(Http2Stream *stream)
+++{
+++  uint32_t initial_rwnd = this->server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
+++  uint32_t min_rwnd     = std::min(initial_rwnd, this->server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE));
+++
+++  // Connection level WINDOW UPDATE
+++  if (this->server_rwnd() < min_rwnd) {
+++    Http2WindowSize diff_size = initial_rwnd - this->server_rwnd();
+++    this->increment_server_rwnd(diff_size);
+++    this->send_window_update_frame(0, diff_size);
+++  }
+++
+++  // Stream level WINDOW UPDATE
+++  if (stream == nullptr || stream->server_rwnd() >= min_rwnd) {
+++    return;
+++  }
+++
+++  // If read_vio is buffering data, do not fully update window
+++  int64_t data_size = stream->read_vio_read_avail();
+++  if (data_size >= initial_rwnd) {
+++    return;
+++  }
+++
+++  Http2WindowSize diff_size = initial_rwnd - std::max(static_cast<int64_t>(stream->server_rwnd()), data_size);
+++  stream->increment_server_rwnd(diff_size);
+++  this->send_window_update_frame(stream->get_id(), diff_size);
+++}
+++
+++void
++ Http2ConnectionState::cleanup_streams()
++ {
++   Http2Stream *s = stream_list.head;
++@@ -1415,6 +1435,12 @@
++     return Http2SendDataFrameResult::ERROR;
++   }
++ 
+++  SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread());
+++  if (this->ua_session->write_avail() == 0) {
+++    Http2StreamDebug(this->ua_session, stream->get_id(), "Not write avail");
+++    return Http2SendDataFrameResult::NOT_WRITE_AVAIL;
+++  }
+++
++   // Select appropriate payload length
++   if (read_available_size > 0) {
++     // We only need to check for window size when there is a payload
++@@ -1458,7 +1484,6 @@
++   current_reader->consume(payload_length);
++ 
++   // xmit event
++-  SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread());
++   this->ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &data);
++ 
++   if (flags & HTTP2_FLAGS_DATA_END_STREAM) {
++@@ -1845,7 +1870,7 @@
++ void
++ Http2ConnectionState::send_window_update_frame(Http2StreamId id, uint32_t size)
++ {
++-  Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame");
+++  Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame: size=%" PRIu32, size);
++ 
++   // Create WINDOW_UPDATE frame
++   Http2Frame window_update(HTTP2_FRAME_TYPE_WINDOW_UPDATE, id, 0x0);
++Index: trafficserver/proxy/http2/Http2ConnectionState.h
++===================================================================
++--- trafficserver.orig/proxy/http2/Http2ConnectionState.h     2020-04-16 21:35:49.127162696 +0200
+++++ trafficserver/proxy/http2/Http2ConnectionState.h  2020-04-16 21:35:49.127162696 +0200
++@@ -34,6 +34,7 @@
++   NO_ERROR = 0,
++   NO_WINDOW,
++   NO_PAYLOAD,
+++  NOT_WRITE_AVAIL,
++   ERROR,
++   DONE,
++ };
++@@ -128,6 +129,8 @@
++   void
++   init()
++   {
+++    this->_server_rwnd = Http2::initial_window_size;
+++
++     local_hpack_handle  = new HpackHandle(HTTP2_HEADER_TABLE_SIZE);
++     remote_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE);
++     dependency_tree     = new DependencyTree(Http2::max_concurrent_streams_in);
++@@ -171,6 +174,7 @@
++   void release_stream(Http2Stream *stream);
++   void cleanup_streams();
++ 
+++  void restart_receiving(Http2Stream *stream);
++   void update_initial_rwnd(Http2WindowSize new_size);
++ 
++   Http2StreamId
++@@ -357,7 +361,7 @@
++ 
++   // Connection level window size
++   ssize_t _client_rwnd = HTTP2_INITIAL_WINDOW_SIZE;
++-  ssize_t _server_rwnd = Http2::initial_window_size;
+++  ssize_t _server_rwnd = 0;
++ 
++   std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX};
++   int _recent_rwnd_increment_index           = 0;
++Index: trafficserver/proxy/http2/Http2Stream.cc
++===================================================================
++--- trafficserver.orig/proxy/http2/Http2Stream.cc     2020-04-16 21:35:49.127162696 +0200
+++++ trafficserver/proxy/http2/Http2Stream.cc  2020-04-16 21:35:49.127162696 +0200
++@@ -524,6 +524,19 @@
++ void
++ Http2Stream::restart_sending()
++ {
+++  if (!this->response_header_done) {
+++    return;
+++  }
+++
+++  IOBufferReader *reader = this->response_get_data_reader();
+++  if (reader && !reader->is_read_avail_more_than(0)) {
+++    return;
+++  }
+++
+++  if (this->write_vio.mutex && this->write_vio.ntodo() == 0) {
+++    return;
+++  }
+++
++   this->send_response_body(true);
++ }
++ 
++@@ -705,6 +718,12 @@
++       SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
++       update_write_request(vio->get_reader(), INT64_MAX, true);
++     } else if (vio->op == VIO::READ) {
+++      Http2ClientSession *h2_proxy_ssn = static_cast<Http2ClientSession *>(this->get_parent());
+++      {
+++        SCOPED_MUTEX_LOCK(lock, h2_proxy_ssn->connection_state.mutex, this_ethread());
+++        h2_proxy_ssn->connection_state.restart_receiving(this);
+++      }
+++
++       SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread());
++       update_read_request(INT64_MAX, true);
++     }
++@@ -964,3 +983,14 @@
++   current_reader = nullptr; // State machine is on its own way down.
++   this->do_io_close();
++ }
+++
+++int64_t
+++Http2Stream::read_vio_read_avail()
+++{
+++  MIOBuffer *writer = this->read_vio.get_writer();
+++  if (writer) {
+++    return writer->max_read_avail();
+++  }
+++
+++  return 0;
+++}
++Index: trafficserver/proxy/http2/Http2Stream.h
++===================================================================
++--- trafficserver.orig/proxy/http2/Http2Stream.h      2020-04-16 21:35:49.127162696 +0200
+++++ trafficserver/proxy/http2/Http2Stream.h   2020-04-16 21:35:49.127162696 +0200
++@@ -38,10 +38,7 @@
++ {
++ public:
++   typedef ProxyClientTransaction super; ///< Parent type.
++-  Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size) : _id(sid), _client_rwnd(initial_rwnd)
++-  {
++-    SET_HANDLER(&Http2Stream::main_event_handler);
++-  }
+++  Http2Stream() { SET_HANDLER(&Http2Stream::main_event_handler); }
++ 
++   void
++   init(Http2StreamId sid, ssize_t initial_rwnd)
++@@ -50,6 +47,7 @@
++     _start_time        = Thread::get_hrtime();
++     _thread            = this_ethread();
++     this->_client_rwnd = initial_rwnd;
+++    this->_server_rwnd = Http2::initial_window_size;
++     HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT, _thread);
++     HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_STREAM_COUNT, _thread);
++     sm_reader = request_reader = request_buffer.alloc_reader();
++@@ -177,6 +175,7 @@
++   ssize_t server_rwnd() const;
++   Http2ErrorCode increment_server_rwnd(size_t amount);
++   Http2ErrorCode decrement_server_rwnd(size_t amount);
+++  int64_t read_vio_read_avail();
++ 
++   uint8_t *header_blocks        = nullptr;
++   uint32_t header_blocks_length = 0;  // total length of header blocks (not include
++@@ -292,8 +291,8 @@
++   uint64_t data_length = 0;
++   uint64_t bytes_sent  = 0;
++ 
++-  ssize_t _client_rwnd;
++-  ssize_t _server_rwnd = Http2::initial_window_size;
+++  ssize_t _client_rwnd = 0;
+++  ssize_t _server_rwnd = 0;
++ 
++   std::vector<size_t> _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX};
++   int _recent_rwnd_increment_index           = 0;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fb0aa443695d90a2c3e4abeb2145e5e2ff86bdf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,131 @@@
++Author: Bryan Call <bcall@apache.org>
++Origin: backport
++Applied-Upstream: https://github.com/apache/trafficserver/pull/6922
++Last-Update: 2020-06-25
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/proxy/http2/HPACK.cc
+++++ b/proxy/http2/HPACK.cc
++@@ -944,7 +944,11 @@ hpack_decode_header_block(HpackIndexingT
++ 
++     field->name_get(&name_len);
++     field->value_get(&value_len);
++-    total_header_size += name_len + value_len;
+++
+++    // [RFC 7540] 6.5.2. SETTINGS_MAX_HEADER_LIST_SIZE:
+++    // The value is based on the uncompressed size of header fields, including the length of the name and value in octets plus an
+++    // overhead of 32 octets for each header field.
+++    total_header_size += name_len + value_len + ADDITIONAL_OCTETS;
++ 
++     if (total_header_size > max_header_size) {
++       return HPACK_ERROR_SIZE_EXCEEDED_ERROR;
++--- a/proxy/http2/Http2ConnectionState.cc
+++++ b/proxy/http2/Http2ConnectionState.cc
++@@ -227,13 +227,6 @@ rcv_headers_frame(Http2ConnectionState &
++     return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE);
++   }
++ 
++-  // keep track of how many bytes we get in the frame
++-  stream->request_header_length += payload_length;
++-  if (stream->request_header_length > Http2::max_request_header_size) {
++-    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
++-                      "recv headers payload for headers greater than header length");
++-  }
++-
++   Http2HeadersParameter params;
++   uint32_t header_block_fragment_offset = 0;
++   uint32_t header_block_fragment_length = payload_length;
++@@ -252,7 +245,8 @@ rcv_headers_frame(Http2ConnectionState &
++                         "recv headers failed to parse");
++     }
++ 
++-    if (params.pad_length > payload_length) {
+++    // Payload length can't be smaller than the pad length
+++    if ((params.pad_length + HTTP2_HEADERS_PADLEN_LEN) > header_block_fragment_length) {
++       return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
++                         "recv headers pad > payload length");
++     }
++@@ -268,7 +262,7 @@ rcv_headers_frame(Http2ConnectionState &
++     frame.reader()->memcpy(buf, HTTP2_PRIORITY_LEN, header_block_fragment_offset);
++     if (!http2_parse_priority_parameter(make_iovec(buf, HTTP2_PRIORITY_LEN), params.priority)) {
++       return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
++-                        "recv headers prioirity parameters failed parse");
+++                        "recv headers priority parameters failed parse");
++     }
++     // Protocol error if the stream depends on itself
++     if (stream_id == params.priority.stream_dependency) {
++@@ -276,6 +270,12 @@ rcv_headers_frame(Http2ConnectionState &
++                         "recv headers self dependency");
++     }
++ 
+++    // Payload length can't be smaller than the priority length
+++    if (HTTP2_PRIORITY_LEN > header_block_fragment_length) {
+++      return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
+++                        "recv priority length > payload length");
+++    }
+++
++     header_block_fragment_offset += HTTP2_PRIORITY_LEN;
++     header_block_fragment_length -= HTTP2_PRIORITY_LEN;
++   }
++@@ -295,11 +295,20 @@ rcv_headers_frame(Http2ConnectionState &
++     }
++   }
++ 
+++  stream->header_blocks_length = header_block_fragment_length;
+++
+++  // ATS advertises SETTINGS_MAX_HEADER_LIST_SIZE as a limit of total header blocks length. (Details in [RFC 7560] 10.5.1.)
+++  // Make it double to relax the limit in cases of 1) HPACK is used naively, or 2) Huffman Encoding generates large header blocks.
+++  // The total "decoded" header length is strictly checked by hpack_decode_header_block().
+++  if (stream->header_blocks_length > std::max(Http2::max_header_list_size, Http2::max_header_list_size * 2)) {
+++    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+++                      "header blocks too large");
+++  }
+++
++   if (header_block_fragment_length > 0) {
++     stream->header_blocks = static_cast<uint8_t *>(ats_malloc(header_block_fragment_length));
++     frame.reader()->memcpy(stream->header_blocks, header_block_fragment_length, header_block_fragment_offset);
++ 
++-    stream->header_blocks_length = header_block_fragment_length;
++   }
++ 
++   if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
++@@ -830,16 +839,17 @@ rcv_continuation_frame(Http2ConnectionSt
++     }
++   }
++ 
++-  // keep track of how many bytes we get in the frame
++-  stream->request_header_length += payload_length;
++-  if (stream->request_header_length > Http2::max_request_header_size) {
++-    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
++-                      "continuation payload for headers exceeded");
++-  }
++-
++   uint32_t header_blocks_offset = stream->header_blocks_length;
++   stream->header_blocks_length += payload_length;
++ 
+++  // ATS advertises SETTINGS_MAX_HEADER_LIST_SIZE as a limit of total header blocks length. (Details in [RFC 7560] 10.5.1.)
+++  // Make it double to relax the limit in cases of 1) HPACK is used naively, or 2) Huffman Encoding generates large header blocks.
+++  // The total "decoded" header length is strictly checked by hpack_decode_header_block().
+++  if (stream->header_blocks_length > std::max(Http2::max_header_list_size, Http2::max_header_list_size * 2)) {
+++    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+++                      "header blocks too large");
+++  }
+++
++   stream->header_blocks = static_cast<uint8_t *>(ats_realloc(stream->header_blocks, stream->header_blocks_length));
++   frame.reader()->memcpy(stream->header_blocks + header_blocks_offset, payload_length);
++ 
++--- a/proxy/http2/Http2Stream.h
+++++ b/proxy/http2/Http2Stream.h
++@@ -178,10 +178,8 @@ public:
++   int64_t read_vio_read_avail();
++ 
++   uint8_t *header_blocks        = nullptr;
++-  uint32_t header_blocks_length = 0;  // total length of header blocks (not include
++-                                      // Padding or other fields)
++-  uint32_t request_header_length = 0; // total length of payload (include Padding
++-                                      // and other fields)
+++  uint32_t header_blocks_length = 0; // total length of header blocks (not include Padding or other fields)
+++
++   bool recv_end_stream = false;
++   bool send_end_stream = false;
++ 
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..921678f629d2b8092ef307beaa06dc703b7a7f34
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,529 @@@
++Author: Brian Neradt <brian.neradt@gmail.com>
++Origin: backport
++Applied-upstream: https://github.com/apache/trafficserver/pull/7358
++Last-Update: 2020-12-06
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/doc/admin-guide/plugins/esi.en.rst
+++++ b/doc/admin-guide/plugins/esi.en.rst
++@@ -80,11 +80,11 @@ Enabling ESI
++ 
++ 2. There are four options you can add to the above.
++ 
++-- "--private-response" will add private cache control and expires header to the processed ESI document.
++-- "--packed-node-support" will enable the support for using packed node, which will improve the performance of parsing
+++- ``--private-response`` will add private cache control and expires header to the processed ESI document.
+++- ``--packed-node-support`` will enable the support for using packed node, which will improve the performance of parsing
++   cached ESI document.
++-- "--disable-gzip-output" will disable gzipped output, which will NOT gzip the output anyway.
++-- "--first-byte-flush" will enable the first byte flush feature, which will flush content to users as soon as the entire
+++- ``--disable-gzip-output`` will disable gzipped output, which will NOT gzip the output anyway.
+++- ``--first-byte-flush`` will enable the first byte flush feature, which will flush content to users as soon as the entire
++   ESI document is received and parsed without all ESI includes fetched (the flushing will stop at the ESI include markup
++   till that include is fetched).
++ 
++--- a/plugins/esi/esi.cc
+++++ b/plugins/esi/esi.cc
++@@ -776,7 +776,7 @@ transformData(TSCont contp)
++             out_data_len = 0;
++             out_data     = "";
++           } else {
++-            TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes", __FUNCTION__, out_data_len,
+++            TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes via gzip", __FUNCTION__, out_data_len,
++                     (int)cdata.size());
++             out_data_len = cdata.size();
++             out_data     = cdata.data();
++@@ -841,12 +841,9 @@ transformData(TSCont contp)
++         if (!cont_data->esi_gzip->stream_encode(out_data, cdata)) {
++           TSError("[esi][%s] Error while gzipping content", __FUNCTION__);
++         } else {
++-          TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes", __FUNCTION__, (int)out_data.size(),
++-                  (int)cdata.size());
+++          TSDebug(cont_data->debug_tag, "[%s] Compressed document from size %d to %d bytes via EsiGzip", __FUNCTION__,
+++                  (int)out_data.size(), (int)cdata.size());
++         }
++-      }
++-
++-      if (cont_data->gzip_output) {
++         if (TSIOBufferWrite(TSVIOBufferGet(cont_data->output_vio), cdata.data(), cdata.size()) == TS_ERROR) {
++           TSError("[esi][%s] Error while writing bytes to downstream VC", __FUNCTION__);
++           return 0;
++--- a/plugins/esi/lib/EsiGzip.cc
+++++ b/plugins/esi/lib/EsiGzip.cc
++@@ -30,7 +30,7 @@ using std::string;
++ using namespace EsiLib;
++ 
++ EsiGzip::EsiGzip(const char *debug_tag, ComponentBase::Debug debug_func, ComponentBase::Error error_func)
++-  : ComponentBase(debug_tag, debug_func, error_func), _downstream_length(0), _total_data_length(0)
+++  : ComponentBase(debug_tag, debug_func, error_func), _downstream_length(0), _total_data_length(0), _crc(0)
++ {
++   // Zlib _zstrm varibles are initialized when they are required in runDeflateLoop
++   // coverity[uninit_member]
++@@ -71,6 +71,7 @@ runDeflateLoop(z_stream &zstrm, int flus
++ bool
++ EsiGzip::stream_encode(const char *data, int data_len, std::string &cdata)
++ {
+++  const auto initial_cdata_size = cdata.size();
++   if (_downstream_length == 0) {
++     cdata.assign(GZIP_HEADER_SIZE, 0); // reserving space for the header
++     cdata[0] = MAGIC_BYTE_1;
++@@ -111,10 +112,9 @@ EsiGzip::stream_encode(const char *data,
++       return false;
++     }
++     _crc = crc32(_crc, reinterpret_cast<const Bytef *>(data), data_len);
++-    _downstream_length += cdata.size();
++     _total_data_length += data_len;
++   }
++-
+++  _downstream_length += cdata.size() - initial_cdata_size;
++   deflateEnd(&_zstrm);
++ 
++   return true;
++@@ -123,6 +123,17 @@ EsiGzip::stream_encode(const char *data,
++ bool
++ EsiGzip::stream_finish(std::string &cdata, int &downstream_length)
++ {
+++  if (_downstream_length == 0) {
+++    // We need to run encode first to get the gzip header inserted.
+++    if (!stream_encode(nullptr, 0, cdata)) {
+++      return false;
+++    }
+++  }
+++  // Note that a call to stream_encode will update cdata to apply the gzip
+++  // header and that call itself will update _downstream_length. Since we don't
+++  // want to double count the gzip header bytes, we capture initial_cdata_size
+++  // here after any possible call to stream_encode above.
+++  const auto initial_cdata_size = cdata.size();
++   char buf[BUF_SIZE];
++ 
++   _zstrm.zalloc = Z_NULL;
++@@ -145,7 +156,7 @@ EsiGzip::stream_finish(std::string &cdat
++   }
++   append(cdata, static_cast<uint32_t>(_crc));
++   append(cdata, static_cast<int32_t>(_total_data_length));
++-  _downstream_length += cdata.size();
+++  _downstream_length += cdata.size() - initial_cdata_size;
++   downstream_length = _downstream_length;
++   return true;
++ }
++--- a/plugins/esi/lib/EsiGzip.h
+++++ b/plugins/esi/lib/EsiGzip.h
++@@ -26,6 +26,7 @@
++ #include "ComponentBase.h"
++ #include <zlib.h>
++ #include <string>
+++#include <string_view>
++ 
++ class EsiGzip : private EsiLib::ComponentBase
++ {
++@@ -34,19 +35,41 @@ public:
++ 
++   ~EsiGzip() override;
++ 
+++  /** Compress the provided content.
+++   *
+++   * @param[in] data The input data to compress.
+++   * @param[in] data_len The length of the input data to compress.
+++   * @param[in,out] The result of compressing the input data will be appended
+++   *    to cdata.
+++   *
+++   * @return True if the compression succeeded, false otherwise.
+++   */
++   bool stream_encode(const char *data, int data_len, std::string &cdata);
++ 
+++  /** A string_view overload of stream_encode. */
++   inline bool
++-  stream_encode(std::string data, std::string &cdata)
+++  stream_encode(std::string_view data, std::string &cdata)
++   {
++     return stream_encode(data.data(), data.size(), cdata);
++   }
++ 
+++  /** Finish the compression stream.
+++   *
+++   * @param[out] cdata The compressed data is appended to this.
+++   * @param[out] downstream_length The total number of compressed stream bytes
+++   *    across all calls to stream_encode and stream_finish.
+++   *
+++   * @return True if the compression succeeded, false otherwise.
+++   */
++   bool stream_finish(std::string &cdata, int &downstream_length);
++ 
++ private:
++-  // int runDeflateLoop(z_stream &zstrm, int flush, std::string &cdata);
+++  /** The cumulative total number of bytes for the compressed stream. */
++   int _downstream_length;
+++
+++  /** The cumulative total number of uncompressed bytes that have been
+++   * compressed.
+++   */
++   int _total_data_length;
++   z_stream _zstrm;
++   uLong _crc;
++--- /dev/null
+++++ b/tests/gold_tests/pluginTest/esi/esi.test.py
++@@ -0,0 +1,310 @@
+++'''
+++Test the ESI plugin.
+++'''
+++#  Licensed to the Apache Software Foundation (ASF) under one
+++#  or more contributor license agreements.  See the NOTICE file
+++#  distributed with this work for additional information
+++#  regarding copyright ownership.  The ASF licenses this file
+++#  to you under the Apache License, Version 2.0 (the
+++#  "License"); you may not use this file except in compliance
+++#  with the License.  You may obtain a copy of the License at
+++#
+++#      http://www.apache.org/licenses/LICENSE-2.0
+++#
+++#  Unless required by applicable law or agreed to in writing, software
+++#  distributed under the License is distributed on an "AS IS" BASIS,
+++#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+++#  See the License for the specific language governing permissions and
+++#  limitations under the License.
+++
+++import os
+++
+++Test.Summary = '''
+++Test the ESI plugin.
+++'''
+++
+++Test.SkipUnless(
+++    Condition.PluginExists('esi.so'),
+++)
+++
+++
+++class EsiTest():
+++    """
+++    A class that encapsulates the configuration and execution of a set of EPI
+++    test cases.
+++    """
+++
+++    """ static: The same server Process is used across all tests. """
+++    _server = None
+++
+++    """ static: A counter to keep the ATS process names unique across tests. """
+++    _ts_counter = 0
+++
+++    """ static: A counter to keep any output file names unique across tests. """
+++    _output_counter = 0
+++
+++    """ The ATS process for this set of test cases. """
+++    _ts = None
+++
+++    def __init__(self, plugin_config):
+++        """
+++        Args:
+++            plugin_config (str): The config line to place in plugin.config for
+++                the ATS process.
+++        """
+++        if EsiTest._server is None:
+++            EsiTest._server = EsiTest._create_server()
+++
+++        self._ts = EsiTest._create_ats(self, plugin_config)
+++
+++    @staticmethod
+++    def _create_server():
+++        """
+++        Create and start a server process.
+++        """
+++        # Configure our server.
+++        server = Test.MakeOriginServer("server")
+++
+++        # Generate the set of ESI responses derived right from our ESI docs.
+++        # See:
+++        #   doc/admin-guide/plugins/esi.en.rst
+++        request_header = {
+++            "headers": (
+++                "GET /esi.php HTTP/1.1\r\n"
+++                "Host: www.example.com\r\n"
+++                "Content-Length: 0\r\n\r\n"),
+++            "timestamp": "1469733493.993",
+++            "body": ""
+++        }
+++        esi_body = r'''<?php   header('X-Esi: 1'); ?>
+++<html>
+++<body>
+++Hello, <esi:include src="http://www.example.com/date.php"/>
+++</body>
+++</html>
+++'''
+++        response_header = {
+++            "headers": (
+++                "HTTP/1.1 200 OK\r\n"
+++                "Content-Type: text/html\r\n"
+++                "X-Esi: 1\r\n"
+++                "Connection: close\r\n"
+++                "Content-Length: {}\r\n"
+++                "Cache-Control: max-age=300\r\n"
+++                "\r\n".format(len(esi_body))),
+++            "timestamp": "1469733493.993",
+++            "body": esi_body
+++        }
+++        server.addResponse("sessionfile.log", request_header, response_header)
+++        request_header = {
+++            "headers": (
+++                "GET /date.php HTTP/1.1\r\n"
+++                "Host: www.example.com\r\n"
+++                "Content-Length: 0\r\n\r\n"),
+++            "timestamp": "1469733493.993",
+++            "body": ""
+++        }
+++        date_body = r'''<?php
+++header ("Cache-control: no-cache");
+++echo date('l jS \of F Y h:i:s A');
+++?>
+++'''
+++        response_header = {
+++            "headers": (
+++                "HTTP/1.1 200 OK\r\n"
+++                "Content-Type: text/html\r\n"
+++                "Connection: close\r\n"
+++                "Content-Length: {}\r\n"
+++                "Cache-Control: max-age=300\r\n"
+++                "\r\n".format(len(date_body))),
+++            "timestamp": "1469733493.993",
+++            "body": date_body
+++        }
+++        server.addResponse("sessionfile.log", request_header, response_header)
+++        # Verify correct functionality with an empty body.
+++        request_header = {
+++            "headers": (
+++                "GET /expect_empty_body HTTP/1.1\r\n"
+++                "Host: www.example.com\r\n"
+++                "Content-Length: 0\r\n\r\n"),
+++            "timestamp": "1469733493.993",
+++            "body": ""
+++        }
+++        response_header = {
+++            "headers": (
+++                "HTTP/1.1 200 OK\r\n"
+++                "X-ESI: On\r\n"
+++                "Content-Length: 0\r\n"
+++                "Connection: close\r\n"
+++                "Content-Type: text/html; charset=UTF-8\r\n"
+++                "\r\n"),
+++            "timestamp": "1469733493.993",
+++            "body": ""
+++        }
+++        server.addResponse("sessionfile.log", request_header, response_header)
+++
+++        # Create a run to start the server.
+++        tr = Test.AddTestRun("Start the server.")
+++        tr.Processes.Default.StartBefore(server)
+++        tr.Processes.Default.Command = "echo starting the server"
+++        tr.Processes.Default.ReturnCode = 0
+++        tr.StillRunningAfter = server
+++
+++        return server
+++
+++    @staticmethod
+++    def _create_ats(self, plugin_config):
+++        """
+++        Create and start an ATS process.
+++        """
+++        EsiTest._ts_counter += 1
+++
+++        # Configure ATS with a vanilla ESI plugin configuration.
+++        ts = Test.MakeATSProcess("ts{}".format(EsiTest._ts_counter))
+++        ts.Disk.records_config.update({
+++            'proxy.config.diags.debug.enabled': 1,
+++            'proxy.config.diags.debug.tags': 'http|plugin_esi',
+++        })
+++        ts.Disk.remap_config.AddLine(
+++            'map http://www.example.com/ http://127.0.0.1:{0}'.format(EsiTest._server.Variables.Port)
+++        )
+++        ts.Disk.plugin_config.AddLine(plugin_config)
+++
+++        # Create a run to start the ATS process.
+++        tr = Test.AddTestRun("Start the ATS process.")
+++        tr.Processes.Default.StartBefore(ts)
+++        tr.Processes.Default.Command = "echo starting ATS"
+++        tr.Processes.Default.ReturnCode = 0
+++        tr.StillRunningAfter = ts
+++        return ts
+++
+++    def run_cases_expecting_gzip(self):
+++        # Test 1: Verify basic ESI functionality.
+++        tr = Test.AddTestRun("First request for esi.php: not cached")
+++        tr.Processes.Default.Command = \
+++            ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" '
+++             '-H"Accept: */*" --verbose'.format(
+++                 self._ts.Variables.port))
+++        tr.Processes.Default.ReturnCode = 0
+++        tr.Processes.Default.Streams.stderr = "gold/esi_headers.gold"
+++        tr.Processes.Default.Streams.stdout = "gold/esi_body.gold"
+++        tr.StillRunningAfter = self._server
+++        tr.StillRunningAfter = self._ts
+++
+++        # Test 2: Repeat the above, should now be cached.
+++        tr = Test.AddTestRun("Second request for esi.php: will be cached")
+++        tr.Processes.Default.Command = \
+++            ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" '
+++             '-H"Accept: */*" --verbose'.format(
+++                 self._ts.Variables.port))
+++        tr.Processes.Default.ReturnCode = 0
+++        tr.Processes.Default.Streams.stderr = "gold/esi_headers.gold"
+++        tr.Processes.Default.Streams.stdout = "gold/esi_body.gold"
+++        tr.StillRunningAfter = self._server
+++        tr.StillRunningAfter = self._ts
+++
+++        # Test 3: Verify the ESI plugin can gzip a response when the client accepts it.
+++        tr = Test.AddTestRun("Verify the ESI plugin can gzip a response")
+++        EsiTest._output_counter += 1
+++        unzipped_body_file = os.path.join(
+++                tr.RunDirectory,
+++                "non_empty_curl_output_{}".format(EsiTest._output_counter))
+++        gzipped_body_file = unzipped_body_file + ".gz"
+++        tr.Processes.Default.Command = \
+++            ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" '
+++             '-H "Accept-Encoding: gzip" -H"Accept: */*" --verbose --output {1}'.format(
+++                 self._ts.Variables.port, gzipped_body_file))
+++        tr.Processes.Default.ReturnCode = 0
+++        tr.Processes.Default.Ready = When.FileExists(gzipped_body_file)
+++        tr.Processes.Default.Streams.stderr = "gold/esi_gzipped.gold"
+++        tr.StillRunningAfter = self._server
+++        tr.StillRunningAfter = self._ts
+++        zipped_body_disk_file = tr.Disk.File(gzipped_body_file)
+++        zipped_body_disk_file.Exists = True
+++
+++        # Now, unzip the file and make sure its size is the expected body.
+++        tr = Test.AddTestRun("Verify the file uzips to the expected body.")
+++        tr.Processes.Default.Command = "gunzip {}".format(gzipped_body_file)
+++        tr.Processes.Default.Ready = When.FileExists(unzipped_body_file)
+++        tr.Processes.Default.ReturnCode = 0
+++        unzipped_body_disk_file = tr.Disk.File(unzipped_body_file)
+++        unzipped_body_disk_file.Content = "gold/esi_body.gold"
+++
+++        # Test 4: Verify correct handling of a gzipped empty response body.
+++        tr = Test.AddTestRun("Verify we can handle an empty response.")
+++        EsiTest._output_counter += 1
+++        empty_body_file = os.path.join(
+++                tr.RunDirectory,
+++                "empty_curl_output_{}".format(EsiTest._output_counter))
+++        gzipped_empty_body = empty_body_file + ".gz"
+++        tr.Processes.Default.Command = \
+++            ('curl http://127.0.0.1:{0}/expect_empty_body -H"Host: www.example.com" '
+++             '-H"Accept-Encoding: gzip" -H"Accept: */*" --verbose --output {1}'.format(
+++                 self._ts.Variables.port, gzipped_empty_body))
+++        tr.Processes.Default.ReturnCode = 0
+++        tr.Processes.Default.Ready = When.FileExists(gzipped_empty_body)
+++        tr.Processes.Default.Streams.stderr = "gold/empty_response_body.gold"
+++        tr.StillRunningAfter = self._server
+++        tr.StillRunningAfter = self._ts
+++        # The gzipped output file should be greater than 0, even though 0 bytes are
+++        # compressed.
+++        gz_disk_file = tr.Disk.File(gzipped_empty_body)
+++        gz_disk_file.Size = Testers.GreaterThan(0)
+++
+++        # Now, unzip the file and make sure its size is the original 0 size body.
+++        tr = Test.AddTestRun("Verify the file uzips to a zero sized file.")
+++        tr.Processes.Default.Command = "gunzip {}".format(gzipped_empty_body)
+++        tr.Processes.Default.Ready = When.FileExists(empty_body_file)
+++        tr.Processes.Default.ReturnCode = 0
+++        unzipped_disk_file = tr.Disk.File(empty_body_file)
+++        unzipped_disk_file.Size = 0
+++
+++    def run_cases_expecting_no_gzip(self):
+++        # Test 1: Run an ESI test where the client does not accept gzip.
+++        tr = Test.AddTestRun("First request for esi.php: gzip not accepted.")
+++        tr.Processes.Default.Command = \
+++            ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" '
+++             '-H"Accept: */*" --verbose'.format(
+++                 self._ts.Variables.port))
+++        tr.Processes.Default.ReturnCode = 0
+++        tr.Processes.Default.Streams.stderr = "gold/esi_headers.gold"
+++        tr.Processes.Default.Streams.stdout = "gold/esi_body.gold"
+++        tr.StillRunningAfter = self._server
+++        tr.StillRunningAfter = self._ts
+++
+++        # Test 2: Verify the ESI plugin does not gzip the response even if the
+++        # client accepts the gzip encoding.
+++        tr = Test.AddTestRun("Verify the ESI plugin refuses to gzip responses with -–disable-gzip-output")
+++        tr.Processes.Default.Command = \
+++            ('curl http://127.0.0.1:{0}/esi.php -H"Host: www.example.com" '
+++             '-H "Accept-Encoding: gzip" -H"Accept: */*" --verbose'.format(
+++                 self._ts.Variables.port))
+++        tr.Processes.Default.ReturnCode = 0
+++        tr.Processes.Default.Streams.stderr = "gold/esi_headers.gold"
+++        tr.Processes.Default.Streams.stdout = "gold/esi_body.gold"
+++        tr.StillRunningAfter = self._server
+++        tr.StillRunningAfter = self._ts
+++
+++
+++#
+++# Configure and run the test cases.
+++#
+++
+++# Run the tests with ESI configured with no parameters.
+++vanilla_test = EsiTest(plugin_config='esi.so')
+++vanilla_test.run_cases_expecting_gzip()
+++
+++# For these test cases, the behavior should remain the same with
+++# --first-byte-flush set.
+++first_byte_flush_test = EsiTest(plugin_config='esi.so --first-byte-flush')
+++first_byte_flush_test.run_cases_expecting_gzip()
+++
+++# For these test cases, the behavior should remain the same with
+++# --packed-node-support set.
+++packed_node_support_test = EsiTest(plugin_config='esi.so --packed-node-support')
+++packed_node_support_test.run_cases_expecting_gzip()
+++
+++# Run a set of cases verifying that the plugin does not zip content if
+++# --disable-gzip-output is set.
+++gzip_disabled_test = EsiTest(plugin_config='esi.so --disable-gzip-output')
+++gzip_disabled_test.run_cases_expecting_no_gzip()
++--- /dev/null
+++++ b/tests/gold_tests/pluginTest/esi/gold/empty_response_body.gold
++@@ -0,0 +1,14 @@
+++``
+++> GET /expect_empty_body HTTP/1.1
+++> Host: www.example.com
+++> User-Agent: ``
+++> Accept-Encoding: gzip
+++> Accept: */*
+++``
+++< HTTP/1.1 200 OK
+++< Content-Type: text/html; charset=UTF-8
+++``
+++< Content-Length: 20
+++``
+++< Content-Encoding: gzip
+++``
++--- /dev/null
+++++ b/tests/gold_tests/pluginTest/esi/gold/esi_body.gold
++@@ -0,0 +1,12 @@
+++``
+++<?php   header('X-Esi: 1'); ?>
+++<html>
+++<body>
+++Hello, <?php
+++header ("Cache-control: no-cache");
+++echo date('l jS \of F Y h:i:s A');
+++?>
+++
+++</body>
+++</html>
+++``
++--- /dev/null
+++++ b/tests/gold_tests/pluginTest/esi/gold/esi_gzipped.gold
++@@ -0,0 +1,10 @@
+++``
+++> GET /esi.php HTTP/1.1
+++``
+++> Accept-Encoding: gzip
+++> Accept: */*
+++``
+++< HTTP/1.1 200 OK
+++``
+++< Content-Encoding: gzip
+++``
++--- /dev/null
+++++ b/tests/gold_tests/pluginTest/esi/gold/esi_headers.gold
++@@ -0,0 +1,6 @@
+++``
+++> GET /esi.php HTTP/1.1
+++``
+++< HTTP/1.1 200 OK
+++< Content-Type: text/html
+++``
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4556d60faaa9c3d5c893941f071c2eab4094bc50
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,218 @@@
++Author: Brian Neradt <brian.neradt@gmail.com>
++Origin: backport
++Applied-upstream: https://github.com/apache/trafficserver/pull/7359
++Last-Update: 2020-06-25
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++--- a/doc/admin-guide/files/records.config.en.rst
+++++ b/doc/admin-guide/files/records.config.en.rst
++@@ -1482,11 +1482,9 @@ Negative Response Caching
++    ====================== =====================================================
++    ``204``                No Content
++    ``305``                Use Proxy
++-   ``400``                Bad Request
++    ``403``                Forbidden
++    ``404``                Not Found
++    ``414``                URI Too Long
++-   ``405``                Method Not Allowed
++    ``500``                Internal Server Error
++    ``501``                Not Implemented
++    ``502``                Bad Gateway
++@@ -1504,7 +1502,7 @@ Negative Response Caching
++    How long (in seconds) Traffic Server keeps the negative responses  valid in cache. This value only affects negative
++    responses that do NOT have explicit ``Expires:`` or ``Cache-Control:`` lifetimes set by the server.
++ 
++-.. ts:cv:: CONFIG proxy.config.http.negative_caching_list STRING 204 305 403 404 405 414 500 501 502 503 504
+++.. ts:cv:: CONFIG proxy.config.http.negative_caching_list STRING 204 305 403 404 414 500 501 502 503 504
++    :reloadable:
++ 
++    The HTTP status code for negative caching. Default values are mentioned above. The unwanted status codes can be
++--- a/doc/admin-guide/performance/index.en.rst
+++++ b/doc/admin-guide/performance/index.en.rst
++@@ -493,7 +493,7 @@ Error responses from origins are coniste
++ If error responses are costly for your origin server to generate, you may elect
++ to have |TS| cache these responses for a period of time. The default behavior is
++ to consider all of these responses to be uncacheable, which will lead to every
++-client request to result in an origin request.
+++client request resulting in an origin request.
++ 
++ This behavior is controlled by both enabling the feature via
++ :ts:cv:`proxy.config.http.negative_caching_enabled` and setting the cache time
++@@ -502,7 +502,7 @@ status code for negative caching can be
++ 
++     CONFIG proxy.config.http.negative_caching_enabled INT 1
++     CONFIG proxy.config.http.negative_caching_lifetime INT 10
++-    CONFIG proxy.config.http.negative_caching_list STRING 204 305 403 404 405 414 500 501 502 503 504
+++    CONFIG proxy.config.http.negative_caching_list STRING 204 305 403 404 414 500 501 502 503 504
++ 
++ SSL-Specific Options
++ ~~~~~~~~~~~~~~~~~~~~
++--- a/mgmt/RecordsConfig.cc
+++++ b/mgmt/RecordsConfig.cc
++@@ -499,7 +499,7 @@ static const RecordElement RecordsConfig
++   ,
++   {RECT_CONFIG, "proxy.config.http.negative_caching_lifetime", RECD_INT, "1800", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
++   ,
++-  {RECT_CONFIG, "proxy.config.http.negative_caching_list", RECD_STRING, "204 305 403 404 405 414 500 501 502 503 504", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
+++  {RECT_CONFIG, "proxy.config.http.negative_caching_list", RECD_STRING, "204 305 403 404 414 500 501 502 503 504", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
++   ,
++ 
++   //        #########################
++--- a/proxy/http/HttpConfig.cc
+++++ b/proxy/http/HttpConfig.cc
++@@ -910,7 +910,7 @@ set_negative_caching_list(const char *na
++   HttpStatusBitset set;
++   // values from proxy.config.http.negative_caching_list
++   if (0 == strcasecmp("proxy.config.http.negative_caching_list", name) && RECD_STRING == dtype && data.rec_string) {
++-    // parse the list of status code
+++    // parse the list of status codes
++     ts::TextView status_list(data.rec_string, strlen(data.rec_string));
++     auto is_sep{[](char c) { return isspace(c) || ',' == c || ';' == c; }};
++     while (!status_list.ltrim_if(is_sep).empty()) {
++--- a/proxy/http/HttpSM.cc
+++++ b/proxy/http/HttpSM.cc
++@@ -2922,7 +2922,7 @@ HttpSM::tunnel_handler_server(int event,
++       // the reason string being written to the client and a bad CL when reading from cache.
++       // I didn't find anywhere this appended reason is being used, so commenting it out.
++       /*
++-        if (t_state.negative_caching && p->bytes_read == 0) {
+++        if (t_state.is_cacheable_and_negative_caching_is_enabled && p->bytes_read == 0) {
++         int reason_len;
++         const char *reason = t_state.hdr_info.server_response.reason_get(&reason_len);
++         if (reason == NULL)
++@@ -2977,8 +2977,8 @@ HttpSM::tunnel_handler_server(int event,
++   }
++ 
++   // turn off negative caching in case there are multiple server contacts
++-  if (t_state.negative_caching) {
++-    t_state.negative_caching = false;
+++  if (t_state.is_cacheable_and_negative_caching_is_enabled) {
+++    t_state.is_cacheable_and_negative_caching_is_enabled = false;
++   }
++ 
++   // If we had a ground fill, check update our status
++@@ -6531,7 +6531,8 @@ HttpSM::setup_server_transfer()
++ 
++   nbytes = server_transfer_init(buf, hdr_size);
++ 
++-  if (t_state.negative_caching && t_state.hdr_info.server_response.status_get() == HTTP_STATUS_NO_CONTENT) {
+++  if (t_state.is_cacheable_and_negative_caching_is_enabled &&
+++      t_state.hdr_info.server_response.status_get() == HTTP_STATUS_NO_CONTENT) {
++     int s = sizeof("No Content") - 1;
++     buf->write("No Content", s);
++     nbytes += s;
++--- a/proxy/http/HttpTransact.cc
+++++ b/proxy/http/HttpTransact.cc
++@@ -4150,7 +4150,7 @@ HttpTransact::handle_cache_operation_on_
++     client_response_code = server_response_code;
++     base_response        = &s->hdr_info.server_response;
++ 
++-    s->negative_caching = is_negative_caching_appropriate(s) && cacheable;
+++    s->is_cacheable_and_negative_caching_is_enabled = cacheable && s->txn_conf->negative_caching_enabled;
++ 
++     // determine the correct cache action given the original cache action,
++     // cacheability of server response, and request method
++@@ -4185,7 +4185,7 @@ HttpTransact::handle_cache_operation_on_
++       }
++ 
++     } else if (s->cache_info.action == CACHE_DO_WRITE) {
++-      if (!cacheable && !s->negative_caching) {
+++      if (!cacheable) {
++         s->cache_info.action = CACHE_DO_NO_ACTION;
++       } else if (s->method == HTTP_WKSIDX_HEAD) {
++         s->cache_info.action = CACHE_DO_NO_ACTION;
++@@ -4212,7 +4212,7 @@ HttpTransact::handle_cache_operation_on_
++     //   before issuing a 304
++     if (s->cache_info.action == CACHE_DO_WRITE || s->cache_info.action == CACHE_DO_NO_ACTION ||
++         s->cache_info.action == CACHE_DO_REPLACE) {
++-      if (s->negative_caching) {
+++      if (s->is_cacheable_and_negative_caching_is_enabled) {
++         HTTPHdr *resp;
++         s->cache_info.object_store.create();
++         s->cache_info.object_store.request_set(&s->hdr_info.client_request);
++@@ -4248,8 +4248,8 @@ HttpTransact::handle_cache_operation_on_
++           SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVER_REVALIDATED);
++         }
++       }
++-    } else if (s->negative_caching) {
++-      s->negative_caching = false;
+++    } else if (s->is_cacheable_and_negative_caching_is_enabled) {
+++      s->is_cacheable_and_negative_caching_is_enabled = false;
++     }
++ 
++     break;
++@@ -4659,7 +4659,7 @@ HttpTransact::set_headers_for_cache_writ
++      sites yields no insight. So the assert is removed and we keep the behavior that if the response
++      in @a cache_info is already set, we don't override it.
++   */
++-  if (!s->negative_caching || !cache_info->response_get()->valid()) {
+++  if (!s->is_cacheable_and_negative_caching_is_enabled || !cache_info->response_get()->valid()) {
++     cache_info->response_set(response);
++   }
++ 
++@@ -6111,24 +6111,24 @@ HttpTransact::is_response_cacheable(Stat
++     }
++   }
++ 
++-  // default cacheability
++-  if (!s->txn_conf->negative_caching_enabled) {
++-    if ((response_code == HTTP_STATUS_OK) || (response_code == HTTP_STATUS_NOT_MODIFIED) ||
++-        (response_code == HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION) || (response_code == HTTP_STATUS_MOVED_PERMANENTLY) ||
++-        (response_code == HTTP_STATUS_MULTIPLE_CHOICES) || (response_code == HTTP_STATUS_GONE)) {
++-      TxnDebug("http_trans", "[is_response_cacheable] YES by default ");
++-      return true;
++-    } else {
++-      TxnDebug("http_trans", "[is_response_cacheable] NO by default");
++-      return false;
++-    }
+++  if ((response_code == HTTP_STATUS_OK) || (response_code == HTTP_STATUS_NOT_MODIFIED) ||
+++      (response_code == HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION) || (response_code == HTTP_STATUS_MOVED_PERMANENTLY) ||
+++      (response_code == HTTP_STATUS_MULTIPLE_CHOICES) || (response_code == HTTP_STATUS_GONE)) {
+++    TxnDebug("http_trans", "[is_response_cacheable] YES response code seems fine");
+++    return true;
++   }
+++  // Notice that the following are not overridable by negative caching.
++   if (response_code == HTTP_STATUS_SEE_OTHER || response_code == HTTP_STATUS_UNAUTHORIZED ||
++       response_code == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
++     return false;
++   }
++-  // let is_negative_caching_approriate decide what to do
++-  return true;
+++  // The response code does not look appropriate for caching. Check, however,
+++  // whether the user has specified it should be cached via negative response
+++  // caching configuration.
+++  if (is_negative_caching_appropriate(s)) {
+++    return true;
+++  }
+++  return false;
++   /* Since we weren't caching response obtained with
++      Authorization (the cache control stuff was commented out previously)
++      I've moved this check to is_request_cache_lookupable().
++--- a/proxy/http/HttpTransact.h
+++++ b/proxy/http/HttpTransact.h
++@@ -761,8 +761,23 @@ public:
++     bool client_connection_enabled = true;
++     bool acl_filtering_performed   = false;
++ 
++-    // for negative caching
++-    bool negative_caching = false;
+++    /// True if negative caching is enabled and the response is cacheable.
+++    ///
+++    /// Note carefully that this being true does not necessarily imply that the
+++    /// response code was negative. It means that (a) the response was
+++    /// cacheable apart from response code considerations, and (b) concerning
+++    /// the response code one of the following was true:
+++    ///
+++    ///   * The response was a negative response code configured cacheable
+++    ///   by the user via negative response caching configuration, or ...
+++    ///
+++    ///   * The response code was an otherwise cacheable positive repsonse
+++    ///   value (such as a 200 response, for example).
+++    ///
+++    /// TODO: We should consider refactoring this variable and its use. For now
+++    /// I'm giving it an awkwardly long name to make sure the meaning of it is
+++    /// clear in its various contexts.
+++    bool is_cacheable_and_negative_caching_is_enabled = false;
++     // for authenticated content caching
++     CacheAuth_t www_auth_content = CACHE_AUTH_NONE;
++ 
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a0a28f8feff1b78c3868f57428bb2261336cfbb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,120 @@@
++From b82a3d192f995fb9d78e1c44d51d9acca4783277 Mon Sep 17 00:00:00 2001
++From: Evan Zelkowitz <eze@apache.org>
++Date: Tue, 22 Jun 2021 14:32:55 -0700
++Subject: [PATCH] Fixes (#7971)
++
++* String the url fragment for outgoing requests (#7966)
++
++Co-authored-by: Susan Hinrichs <shinrich@verizonmedia.com>
++(cherry picked from commit 2b13eb33794574e62249997b4ba654d943a10f2d)
++
++* Ensure that the content-length value is only digits (#7964)
++
++Co-authored-by: Susan Hinrichs <shinrich@verizonmedia.com>
++(cherry picked from commit 668d0f8668fec1cd350b0ceba3f7f8e4020ae3ca)
++
++* Schedule H2 reenable event only if it's necessary
++
++Co-authored-by: Katsutoshi Ikenoya <kikenoya@yahoo-corp.jp>
++
++* Fix dynamic-stack-buffer-overflow of cachekey plugin (#7945)
++
++* Fix dynamic-stack-buffer-overflow of cachekey plugin
++
++* Check dst_size include null termination
++
++(cherry picked from commit 5a9339d7bc65e1c2d8d2a0fc80bb051daf3cdb0b)
++
++Co-authored-by: Bryan Call <bcall@apache.org>
++Co-authored-by: Masakazu Kitajo <maskit@apache.org>
++Co-authored-by: Katsutoshi Ikenoya <kikenoya@yahoo-corp.jp>
++Co-authored-by: Masaori Koshiba <masaori@apache.org>
++---
++ plugins/cachekey/cachekey.cc      |  2 +-
++ proxy/hdrs/HTTP.cc                | 11 +++++++++++
++ proxy/http/HttpTransact.cc        |  5 ++++-
++ proxy/http2/Http2ClientSession.cc | 14 +++++++-------
++ proxy/logging/LogUtils.cc         |  2 +-
++ 5 files changed, 24 insertions(+), 10 deletions(-)
++
++diff --git a/plugins/cachekey/cachekey.cc b/plugins/cachekey/cachekey.cc
++index 5f128894bfa..44925b3db28 100644
++--- a/plugins/cachekey/cachekey.cc
+++++ b/plugins/cachekey/cachekey.cc
++@@ -41,7 +41,7 @@ appendEncoded(String &target, const char *s, size_t len)
++     return;
++   }
++ 
++-  char tmp[len * 2];
+++  char tmp[len * 3 + 1];
++   size_t written;
++ 
++   /* The default table does not encode the comma, so we need to use our own table here. */
++diff --git a/proxy/hdrs/HTTP.cc b/proxy/hdrs/HTTP.cc
++index 6a2ecc41d3a..48032dd9ddf 100644
++--- a/proxy/hdrs/HTTP.cc
+++++ b/proxy/hdrs/HTTP.cc
++@@ -1202,6 +1202,17 @@ validate_hdr_content_length(HdrHeap *heap, HTTPHdrImpl *hh)
++     int content_length_len         = 0;
++     const char *content_length_val = content_length_field->value_get(&content_length_len);
++ 
+++    // RFC 7230 section 3.3.2
+++    // Content-Length = 1*DIGIT
+++    //
+++    // If the content-length value contains a non-numeric value, the header is invalid
+++    for (int i = 0; i < content_length_len; i++) {
+++      if (!isdigit(content_length_val[i])) {
+++        Debug("http", "Content-Length value contains non-digit, returning parse error");
+++        return PARSE_RESULT_ERROR;
+++      }
+++    }
+++
++     while (content_length_field->has_dups()) {
++       int content_length_len_2         = 0;
++       const char *content_length_val_2 = content_length_field->m_next_dup->value_get(&content_length_len_2);
++diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
++index c3d135b98e8..2de29a8d08d 100644
++--- a/proxy/http/HttpTransact.cc
+++++ b/proxy/http/HttpTransact.cc
++@@ -7619,9 +7619,12 @@ HttpTransact::build_request(State *s, HTTPHdr *base_request, HTTPHdr *outgoing_r
++ 
++   // HttpTransactHeaders::convert_request(outgoing_version, outgoing_request); // commented out this idea
++ 
+++  URL *url = outgoing_request->url_get();
+++  // Remove fragment from upstream URL
+++  url->fragment_set(NULL, 0);
+++
++   // Check whether a Host header field is missing from a 1.0 or 1.1 request.
++   if (outgoing_version != HTTPVersion(0, 9) && !outgoing_request->presence(MIME_PRESENCE_HOST)) {
++-    URL *url = outgoing_request->url_get();
++     int host_len;
++     const char *host = url->host_get(&host_len);
++ 
++diff --git a/proxy/http2/Http2ClientSession.cc b/proxy/http2/Http2ClientSession.cc
++index 6d7d3de7992..ee952b8a275 100644
++--- a/proxy/http2/Http2ClientSession.cc
+++++ b/proxy/http2/Http2ClientSession.cc
++@@ -653,8 +653,8 @@ Http2ClientSession::remember(const SourceLocation &location, int event, int reen
++ bool
++ Http2ClientSession::_should_do_something_else()
++ {
++-  // Do something else every 128 incoming frames
++-  return (this->_n_frame_read & 0x7F) == 0;
+++  // Do something else every 128 incoming frames if connection state isn't closed
+++  return (this->_n_frame_read & 0x7F) == 0 && !connection_state.is_state_closed();
++ }
++ 
++ int64_t
++diff --git a/proxy/logging/LogUtils.cc b/proxy/logging/LogUtils.cc
++index 94becf250ac..475bee87cad 100644
++--- a/proxy/logging/LogUtils.cc
+++++ b/proxy/logging/LogUtils.cc
++@@ -343,7 +343,7 @@ escapify_url_common(Arena *arena, char *url, size_t len_in, int *len_out, char *
++   //
++   size_t out_len = len_in + 2 * count;
++ 
++-  if (dst && out_len > dst_size) {
+++  if (dst && (out_len + 1) > dst_size) {
++     *len_out = 0;
++     return nullptr;
++   }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..089f6ae2742a67d38f84df3a3e334a1dd4d828fa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++commit 5cad961c87cb07fbb8fa6890685d9878a169378d
++Author: Brian Neradt <brian.neradt@gmail.com>
++Date:   Wed Oct 27 11:29:43 2021 -0500
++
++    Fix output '\n' HTTP field line endings (#8460)
++    
++    This is another attempt to fix what was initially addressed in #8096 but
++    got backed out via #8305. That more extensive patch was considered too
++    invasive and potentially risky.  This more targeted patch will fix
++    clients that only send the \n endings but it will force the \r\n line
++    ending on output.
++    
++    This was mostly in place except for header lines that get
++    m_n_v_raw_printable set, which seems to be most header lines. The
++    addition checks to see if the header line ends in \r\n. If it does not
++    the m_n_v_raw_printable flag gets cleared and the logic that explicitly
++    adds the line endings while be invoked on output.
++
++Index: trafficserver/proxy/hdrs/MIME.cc
++===================================================================
++--- trafficserver.orig/proxy/hdrs/MIME.cc     2022-05-21 21:12:19.187391663 +0200
+++++ trafficserver/proxy/hdrs/MIME.cc  2022-05-21 21:12:19.187391663 +0200
++@@ -2638,8 +2638,17 @@
++ 
++     // find_value_last
++     field_value_last = line_e - 1;
+++    int suffix_count = 0;
++     while ((field_value_last >= field_value_first) && ParseRules::is_wslfcr(*field_value_last)) {
++       --field_value_last;
+++      ++suffix_count;
+++    }
+++
+++    // Make sure the field ends in CRLF. If not, we'll fix the field via the n_v_raw_printable
+++    // flag.
+++    bool raw_print_field = true;
+++    if (suffix_count < 2 || *(line_e - 2) != '\r' || *(line_e - 1) != '\n') {
+++      raw_print_field = false;
++     }
++ 
++     field_name_length  = (int)(field_name_last - field_name_first + 1);
++@@ -2676,7 +2685,7 @@
++ 
++     MIMEField *field = mime_field_create(heap, mh);
++     mime_field_name_value_set(heap, mh, field, field_name_wks_idx, field_name_first, field_name_length, field_value_first,
++-                              field_value_length, true, total_line_length, false);
+++                              field_value_length, raw_print_field, total_line_length, false);
++     mime_hdr_field_attach(mh, field, 1, nullptr);
++   }
++ }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b2f2b8d8f2983e964d3e0b36cad96d88c664b195
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++commit e2c9ac217f24dc3e91ff2c9f52b52093e8fb32d5
++Author: Brian Neradt <brian.neradt@verizonmedia.com>
++Date:   Wed Oct 27 11:30:32 2021 -0500
++
++    8.1.x: Reject Transfer-Encoding in pre-HTTP/1.1 requests (#8457)
++    
++    Per spec, Transfer-Encoding is only supported in HTTP/1.1. For earlier
++    versions, we must reject Transfer-Encoding rather than interpret it
++    since downstream proxies may ignore the chunk header and rely upon the
++    Content-Length, or interpret the body some other way.  These differences
++    in interpretation may open up the door to compatibility issues. To
++    protect against this, we reply with a 4xx if the client uses
++    Transfer-Encoding with HTTP versions that do not support it.
++
++Index: trafficserver/proxy/http/HttpTransact.cc
++===================================================================
++--- trafficserver.orig/proxy/http/HttpTransact.cc     2022-05-21 21:12:19.271391152 +0200
+++++ trafficserver/proxy/http/HttpTransact.cc  2022-05-21 21:12:19.271391152 +0200
++@@ -5122,6 +5122,17 @@
++       return BAD_CONNECT_PORT;
++     }
++ 
+++    if (s->client_info.transfer_encoding == CHUNKED_ENCODING && incoming_hdr->version_get() < HTTPVersion(1, 1)) {
+++      // Per spec, Transfer-Encoding is only supported in HTTP/1.1. For earlier
+++      // versions, we must reject Transfer-Encoding rather than interpret it
+++      // since downstream proxies may ignore the chunk header and rely upon the
+++      // Content-Length, or interpret the body some other way. These
+++      // differences in interpretation may open up the door to compatibility
+++      // issues. To protect against this, we reply with a 4xx if the client
+++      // uses Transfer-Encoding with HTTP versions that do not support it.
+++      return UNACCEPTABLE_TE_REQUIRED;
+++    }
+++
++     // Require Content-Length/Transfer-Encoding for POST/PUSH/PUT
++     if ((scheme == URL_WKSIDX_HTTP || scheme == URL_WKSIDX_HTTPS) &&
++         (method == HTTP_WKSIDX_POST || method == HTTP_WKSIDX_PUSH || method == HTTP_WKSIDX_PUT) &&
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..090ba8374eb9853569a5c4476e21ea2243ef4776
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,234 @@@
++commit 2addc8ca71449ceac0d5b80172460ee09c938f5e
++Author: Brian Neradt <brian.neradt@gmail.com>
++Date:   Wed Oct 27 11:30:07 2021 -0500
++
++    Detect and handle chunk header size truncation (#8458)
++    
++    This detects if a chunk header size is too large and, if so, closes the
++    connection.
++
++Index: trafficserver/include/tscore/ink_memory.h
++===================================================================
++--- trafficserver.orig/include/tscore/ink_memory.h    2022-05-21 21:12:19.371390543 +0200
+++++ trafficserver/include/tscore/ink_memory.h 2022-05-21 21:12:19.367390568 +0200
++@@ -26,6 +26,7 @@
++ #include <cstring>
++ #include <strings.h>
++ #include <cinttypes>
+++#include <limits>
++ #include <string>
++ #include <string_view>
++ 
++@@ -204,6 +205,24 @@
++   memset(static_cast<void *>(&t), 0, sizeof(t));
++ }
++ 
+++/** Verify that we can safely shift value num_places places left.
+++ *
+++ * This checks that the shift will not cause the variable to overflow and that
+++ * the value will not become negative.
+++ *
+++ * @param[in] value The value against which to check whether the shift is safe.
+++ *
+++ * @param[in] num_places The number of places to check that shifting left is safe.
+++ *
+++ */
+++template <typename T>
+++inline constexpr bool
+++can_safely_shift_left(T value, int num_places)
+++{
+++  constexpr auto max_value = std::numeric_limits<T>::max();
+++  return value >= 0 && value <= (max_value >> num_places);
+++}
+++
++ /** Scoped resources.
++ 
++     An instance of this class is used to hold a contingent resource. When this object goes out of scope
++Index: trafficserver/proxy/http/HttpTunnel.cc
++===================================================================
++--- trafficserver.orig/proxy/http/HttpTunnel.cc       2022-05-21 21:12:19.371390543 +0200
+++++ trafficserver/proxy/http/HttpTunnel.cc    2022-05-21 21:12:19.371390543 +0200
++@@ -36,6 +36,7 @@
++ #include "HttpSM.h"
++ #include "HttpDebugNames.h"
++ #include "tscore/ParseRules.h"
+++#include "tscore/ink_memory.h"
++ 
++ static const int min_block_transfer_bytes = 256;
++ static const char *const CHUNK_HEADER_FMT = "%" PRIx64 "\r\n";
++@@ -153,8 +154,16 @@
++       if (state == CHUNK_READ_SIZE) {
++         // The http spec says the chunked size is always in hex
++         if (ParseRules::is_hex(*tmp)) {
+++          // Make sure we will not overflow running_sum with our shift.
+++          if (!can_safely_shift_left(running_sum, 4)) {
+++            // We have no more space in our variable for the shift.
+++            state = CHUNK_READ_ERROR;
+++            done  = true;
+++            break;
+++          }
++           num_digits++;
++-          running_sum *= 16;
+++          // Shift over one hex value.
+++          running_sum <<= 4;
++ 
++           if (ParseRules::is_digit(*tmp)) {
++             running_sum += *tmp - '0';
++Index: trafficserver/src/tscore/Makefile.am
++===================================================================
++--- trafficserver.orig/src/tscore/Makefile.am 2022-05-21 21:12:19.371390543 +0200
+++++ trafficserver/src/tscore/Makefile.am      2022-05-21 21:12:19.371390543 +0200
++@@ -258,6 +258,7 @@
++      unit_tests/test_BufferWriter.cc \
++      unit_tests/test_BufferWriterFormat.cc \
++      unit_tests/test_ink_inet.cc \
+++     unit_tests/test_ink_memory.cc \
++      unit_tests/test_IntrusivePtr.cc \
++      unit_tests/test_IpMap.cc \
++      unit_tests/test_layout.cc \
++Index: trafficserver/src/tscore/unit_tests/test_ink_memory.cc
++===================================================================
++--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++++ trafficserver/src/tscore/unit_tests/test_ink_memory.cc    2022-05-21 21:12:19.371390543 +0200
++@@ -0,0 +1,141 @@
+++/** @file
+++
+++    ink_memory unit tests.
+++
+++    @section license License
+++
+++    Licensed to the Apache Software Foundation (ASF) under one
+++    or more contributor license agreements.  See the NOTICE file
+++    distributed with this work for additional information
+++    regarding copyright ownership.  The ASF licenses this file
+++    to you under the Apache License, Version 2.0 (the
+++    "License"); you may not use this file except in compliance
+++    with the License.  You may obtain a copy of the License at
+++
+++    http://www.apache.org/licenses/LICENSE-2.0
+++
+++    Unless required by applicable law or agreed to in writing, software
+++    distributed under the License is distributed on an "AS IS" BASIS,
+++    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+++    See the License for the specific language governing permissions and
+++    limitations under the License.
+++*/
+++
+++#include <catch.hpp>
+++#include <cstdint>
+++#include "tscore/ink_memory.h"
+++
+++constexpr void
+++test_can_safely_shift_int8_t()
+++{
+++  constexpr int8_t a = 0;
+++  static_assert(can_safely_shift_left(a, 0) == true, "shifting 0 is safe");
+++  static_assert(can_safely_shift_left(a, 4) == true, "shifting 0 is safe");
+++  static_assert(can_safely_shift_left(a, 8) == true, "shifting 0 is safe");
+++
+++  constexpr int8_t b = 1;
+++  static_assert(can_safely_shift_left(b, 0) == true, "shifting int8_t 1 0 places is safe");
+++  static_assert(can_safely_shift_left(b, 1) == true, "shifting int8_t 1 1 places is safe");
+++  static_assert(can_safely_shift_left(b, 6) == true, "shifting int8_t 1 6 places is safe");
+++  static_assert(can_safely_shift_left(b, 7) == false, "shifting int8_t 1 7 places becomes negative");
+++  static_assert(can_safely_shift_left(b, 8) == false, "shifting int8_t 1 8 places overflows");
+++
+++  constexpr int8_t c = 0xff;
+++  static_assert(can_safely_shift_left(c, 0) == false, "int8_t 0xff is already negative");
+++  static_assert(can_safely_shift_left(c, 1) == false, "shifting int8_t 0xff 1 place overflows");
+++}
+++
+++constexpr void
+++test_can_safely_shift_uint8_t()
+++{
+++  constexpr uint8_t a = 0;
+++  static_assert(can_safely_shift_left(a, 0) == true, "shifting 0 is safe");
+++  static_assert(can_safely_shift_left(a, 4) == true, "shifting 0 is safe");
+++  static_assert(can_safely_shift_left(a, 8) == true, "shifting 0 is safe");
+++
+++  constexpr uint8_t b = 1;
+++  static_assert(can_safely_shift_left(b, 0) == true, "shifting uint8_t 1 0 places is safe");
+++  static_assert(can_safely_shift_left(b, 1) == true, "shifting uint8_t 1 1 places is safe");
+++  static_assert(can_safely_shift_left(b, 6) == true, "shifting uint8_t 1 6 places is safe");
+++  static_assert(can_safely_shift_left(b, 7) == true, "shifting uint8_t 1 7 is safe");
+++  static_assert(can_safely_shift_left(b, 8) == false, "shifting uint8_t 1 8 places overflows");
+++
+++  constexpr uint8_t c = 0xff;
+++  static_assert(can_safely_shift_left(c, 0) == true, "shifting int8_t 0xff 0 places is safe");
+++  static_assert(can_safely_shift_left(c, 1) == false, "shifting int8_t 0xff 1 place overflows");
+++}
+++
+++constexpr void
+++test_can_safely_shift_int32_t()
+++{
+++  constexpr int32_t a = 0;
+++  static_assert(can_safely_shift_left(a, 4) == true, "shifting 0 is safe");
+++
+++  constexpr int32_t b = 1;
+++  static_assert(can_safely_shift_left(b, 4) == true, "shifting 1 is safe");
+++
+++  constexpr int32_t c = 0x00ff'ffff;
+++  static_assert(can_safely_shift_left(c, 4) == true, "shifting 0x00ff'ffff is safe");
+++
+++  constexpr int32_t d = 0x07ff'ffff;
+++  static_assert(can_safely_shift_left(d, 4) == true, "shifting 0x07ff'ffff is safe");
+++
+++  constexpr int32_t e = -1;
+++  static_assert(can_safely_shift_left(e, 4) == false, "shifting -1 will result in truncation");
+++
+++  constexpr int32_t f = 0x0800'0000;
+++  static_assert(can_safely_shift_left(f, 4) == false, "shifting 0x0801'0000 will become negative");
+++
+++  constexpr int32_t g = 0x0fff'ffff;
+++  static_assert(can_safely_shift_left(g, 4) == false, "shifting 0x0fff'ffff will become negative");
+++
+++  constexpr int32_t h = 0x1000'0000;
+++  static_assert(can_safely_shift_left(h, 4) == false, "shifting 0x1000'0000 will overflow");
+++
+++  constexpr int32_t i = 0xf000'0000;
+++  static_assert(can_safely_shift_left(i, 4) == false, "shifting 0xf000'0000 will overflow");
+++
+++  constexpr int32_t j = 0xf800'0000;
+++  static_assert(can_safely_shift_left(j, 4) == false, "shifting 0xf800'0000 will become negative");
+++}
+++
+++constexpr void
+++test_can_safely_shift_uint32_t()
+++{
+++  constexpr uint32_t a = 0;
+++  static_assert(can_safely_shift_left(a, 4) == true, "shifting 0 is safe");
+++
+++  constexpr uint32_t b = 1;
+++  static_assert(can_safely_shift_left(b, 4) == true, "shifting 1 is safe");
+++
+++  constexpr uint32_t c = 0x00ff'ffff;
+++  static_assert(can_safely_shift_left(c, 4) == true, "shifting 0x00ff'ffff is safe");
+++
+++  constexpr uint32_t d = 0x07ff'ffff;
+++  static_assert(can_safely_shift_left(d, 4) == true, "shifting 0x07ff'ffff is safe");
+++
+++  constexpr uint32_t e = 0x0800'0000;
+++  static_assert(can_safely_shift_left(e, 4) == true, "shifting unisgned 0x0800'0000 is safe");
+++
+++  constexpr uint32_t f = 0x0fff'ffff;
+++  static_assert(can_safely_shift_left(f, 4) == true, "shifting unsigned 0x0fff'ffff is safe");
+++
+++  constexpr uint32_t g = 0x1000'0000;
+++  static_assert(can_safely_shift_left(g, 4) == false, "shifting 0x1000'0000 will overflow");
+++
+++  constexpr uint32_t h = 0xf000'0000;
+++  static_assert(can_safely_shift_left(h, 4) == false, "shifting 0xf000'0000 will overflow");
+++
+++  constexpr uint32_t i = 0xf800'0000;
+++  static_assert(can_safely_shift_left(i, 4) == false, "shifting 0xf800'0000 will become negative");
+++}
+++
+++TEST_CASE("can_safely_shift", "[libts][ink_inet][memory]")
+++{
+++  // can_safely_shift_left is a constexpr function, therefore all these checks are
+++  // done at compile time and REQUIRES calls are not necessary.
+++  test_can_safely_shift_int8_t();
+++  test_can_safely_shift_uint8_t();
+++  test_can_safely_shift_int32_t();
+++  test_can_safely_shift_uint32_t();
+++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..baae619625e2b8d8a981399dc803688f0c199af8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++commit feefc5e4abc5011dfad5dcfef3f22998faf6e2d4
++Author: Alan M. Carroll <amc@apache.org>
++Date:   Wed Oct 27 13:41:47 2021 -0500
++
++    Add some checking to validate the scheme matches the wire protocol. (#8464)
++
++Index: trafficserver/proxy/http/HttpSM.cc
++===================================================================
++--- trafficserver.orig/proxy/http/HttpSM.cc   2022-05-21 21:46:27.407477781 +0200
+++++ trafficserver/proxy/http/HttpSM.cc        2022-05-21 21:48:18.978834781 +0200
++@@ -443,6 +443,9 @@
++   // Collect log & stats information
++   client_tcp_reused         = !(ua_txn->is_first_transaction());
++   SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(netvc);
+++
+++  is_internal       = netvc->get_is_internal_request();
+++
++   if (ssl_vc != nullptr) {
++     client_connection_is_ssl = true;
++     client_ssl_reused        = ssl_vc->getSSLSessionCacheHit();
++@@ -724,6 +727,17 @@
++   case PARSE_RESULT_DONE:
++     SMDebug("http", "[%" PRId64 "] done parsing client request header", sm_id);
++ 
+++    if (!is_internal) {
+++      auto scheme = t_state.hdr_info.client_request.url_get()->scheme_get_wksidx();
+++      if ((client_connection_is_ssl && (scheme == URL_WKSIDX_HTTP || scheme == URL_WKSIDX_WS)) ||
+++          (!client_connection_is_ssl && (scheme == URL_WKSIDX_HTTPS || scheme == URL_WKSIDX_WSS))) {
+++        SMDebug("http", "scheme [%s] vs. protocol [%s] mismatch", hdrtoken_index_to_wks(scheme),
+++                client_connection_is_ssl ? "tls" : "plaintext");
+++        t_state.http_return_code = HTTP_STATUS_BAD_REQUEST;
+++        call_transact_and_set_next_state(HttpTransact::BadRequest);
+++        break;
+++      }
+++    }
++     ua_txn->set_session_active();
++ 
++     if (t_state.hdr_info.client_request.version_get() == HTTPVersion(1, 1) &&
++Index: trafficserver/proxy/http/HttpSM.h
++===================================================================
++--- trafficserver.orig/proxy/http/HttpSM.h    2022-05-21 21:47:00.135289347 +0200
+++++ trafficserver/proxy/http/HttpSM.h 2022-05-21 21:47:14.131208716 +0200
++@@ -541,6 +541,7 @@
++   int64_t pushed_response_body_bytes = 0;
++   bool client_tcp_reused             = false;
++   // Info about client's SSL connection.
+++  bool is_internal                = false;
++   bool client_ssl_reused          = false;
++   bool client_connection_is_ssl   = false;
++   const char *client_protocol     = "-";
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57a33473998ddda6273e4aeb03e2550f192ff912
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,513 @@@
++Description: Improper Input Validation vulnerability in request line parsing
++Author: <name and email of author, optional>
++Origin: upstream
++Applied-Upstream: 85c319a7f7c0537bee408ea25df6f1a5ed0a4071, c4e6661a5a205b1f60279f0e66aa496023185967, 8c6f2ed84ba0d8e6255baceb99ee891ebe1ce473
++Reviewed-by: Jean Baptiste Favre <debian@jbfavre.org>
++Last-Update: 2022-05-21
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++Index: trafficserver/doc/admin-guide/files/records.config.en.rst
++===================================================================
++--- trafficserver.orig/doc/admin-guide/files/records.config.en.rst    2022-05-21 21:12:19.571389326 +0200
+++++ trafficserver/doc/admin-guide/files/records.config.en.rst 2022-05-21 21:12:19.567389350 +0200
++@@ -1112,10 +1112,12 @@
++    An arbitrary string value that, if set, will be used to replace any request
++    ``User-Agent`` header.
++ 
++-.. ts:cv:: CONFIG proxy.config.http.strict_uri_parsing INT 0
+++.. ts:cv:: CONFIG proxy.config.http.strict_uri_parsing INT 2
++ 
++-   Enables (``1``) or disables (``0``) Traffic Server to return a 400 Bad Request
++-   if client's request URI includes character which is not RFC 3986 compliant
+++   Takes a value between 0 and 2.  ``0`` disables strict_uri_parsing.  Any character can appears
+++   in the URI.  ``1`` causes |TS| to return 400 Bad Request
+++   if client's request URI includes character which is not RFC 3986 compliant. ``2`` directs |TS|
+++   to reject the clients request if it contains whitespace or non-printable characters.
++ 
++ .. ts:cv:: CONFIG proxy.config.http.errors.log_error_pages INT 1
++    :reloadable:
++Index: trafficserver/mgmt/RecordsConfig.cc
++===================================================================
++--- trafficserver.orig/mgmt/RecordsConfig.cc  2022-05-21 21:12:19.571389326 +0200
+++++ trafficserver/mgmt/RecordsConfig.cc       2022-05-21 21:12:19.567389350 +0200
++@@ -354,7 +354,7 @@
++   ,
++   {RECT_CONFIG, "proxy.config.http.post.check.content_length.enabled", RECD_INT, "1", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
++   ,
++-  {RECT_CONFIG, "proxy.config.http.strict_uri_parsing", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
+++  {RECT_CONFIG, "proxy.config.http.strict_uri_parsing", RECD_INT, "2", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-2]", RECA_NULL}
++   ,
++   //       # Send http11 requests
++   //       #
++Index: trafficserver/proxy/hdrs/HTTP.cc
++===================================================================
++--- trafficserver.orig/proxy/hdrs/HTTP.cc     2022-05-21 21:12:19.571389326 +0200
+++++ trafficserver/proxy/hdrs/HTTP.cc  2022-05-21 21:12:19.567389350 +0200
++@@ -885,7 +885,7 @@
++ 
++ ParseResult
++ http_parser_parse_req(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end,
++-                      bool must_copy_strings, bool eof, bool strict_uri_parsing)
+++                      bool must_copy_strings, bool eof, int strict_uri_parsing)
++ {
++   if (parser->m_parsing_http) {
++     MIMEScanner *scanner = &parser->m_mime_parser.m_scanner;
++Index: trafficserver/proxy/hdrs/HTTP.h
++===================================================================
++--- trafficserver.orig/proxy/hdrs/HTTP.h      2022-05-21 21:12:19.571389326 +0200
+++++ trafficserver/proxy/hdrs/HTTP.h   2022-05-21 21:12:19.567389350 +0200
++@@ -445,7 +445,7 @@
++ void http_parser_init(HTTPParser *parser);
++ void http_parser_clear(HTTPParser *parser);
++ ParseResult http_parser_parse_req(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end,
++-                                  bool must_copy_strings, bool eof, bool strict_uri_parsing);
+++                                  bool must_copy_strings, bool eof, int strict_uri_parsing);
++ ParseResult validate_hdr_host(HTTPHdrImpl *hh);
++ ParseResult validate_hdr_content_length(HdrHeap *heap, HTTPHdrImpl *hh);
++ ParseResult http_parser_parse_resp(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end,
++@@ -623,10 +623,10 @@
++   const char *reason_get(int *length);
++   void reason_set(const char *value, int length);
++ 
++-  ParseResult parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, bool strict_uri_parsing = false);
+++  ParseResult parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, int strict_uri_parsing = 0);
++   ParseResult parse_resp(HTTPParser *parser, const char **start, const char *end, bool eof);
++ 
++-  ParseResult parse_req(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof, bool strict_uri_parsing = false);
+++  ParseResult parse_req(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof, int strict_uri_parsing = 0);
++   ParseResult parse_resp(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof);
++ 
++ public:
++@@ -1221,7 +1221,7 @@
++   -------------------------------------------------------------------------*/
++ 
++ inline ParseResult
++-HTTPHdr::parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, bool strict_uri_parsing)
+++HTTPHdr::parse_req(HTTPParser *parser, const char **start, const char *end, bool eof, int strict_uri_parsing)
++ {
++   ink_assert(valid());
++   ink_assert(m_http->m_polarity == HTTP_TYPE_REQUEST);
++Index: trafficserver/proxy/hdrs/HdrTSOnly.cc
++===================================================================
++--- trafficserver.orig/proxy/hdrs/HdrTSOnly.cc        2022-05-21 21:12:19.571389326 +0200
+++++ trafficserver/proxy/hdrs/HdrTSOnly.cc     2022-05-21 21:12:19.567389350 +0200
++@@ -45,7 +45,7 @@
++   -------------------------------------------------------------------------*/
++ 
++ ParseResult
++-HTTPHdr::parse_req(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof, bool strict_uri_parsing)
+++HTTPHdr::parse_req(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof, int strict_uri_parsing)
++ {
++   const char *start;
++   const char *tmp;
++Index: trafficserver/proxy/hdrs/URL.cc
++===================================================================
++--- trafficserver.orig/proxy/hdrs/URL.cc      2022-05-21 21:12:19.571389326 +0200
+++++ trafficserver/proxy/hdrs/URL.cc   2022-05-21 21:12:19.567389350 +0200
++@@ -1179,10 +1179,34 @@
++   return true;
++ }
++ 
+++/**
+++ *  This method will return TRUE if the uri is mostly compliant with
+++ *  RFC 3986 and it will return FALSE if not. Specifically denying white
+++ *  space an unprintable characters
+++ */
+++static bool
+++url_is_mostly_compliant(const char *start, const char *end)
+++{
+++  for (const char *i = start; i < end; ++i) {
+++    if (isspace(*i)) {
+++      Debug("http", "Whitespace character [0x%.2X] found in URL", (unsigned char)*i);
+++      return false;
+++    }
+++    if (!isprint(*i)) {
+++      Debug("http", "Non-printable character [0x%.2X] found in URL", (unsigned char)*i);
+++      return false;
+++    }
+++  }
+++  return true;
+++}
+++
++ ParseResult
++-url_parse(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings_p, bool strict_uri_parsing)
+++url_parse(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings_p, int strict_uri_parsing)
++ {
++-  if (strict_uri_parsing && !url_is_strictly_compliant(*start, end)) {
+++  if (strict_uri_parsing == 1 && !url_is_strictly_compliant(*start, end)) {
+++    return PARSE_RESULT_ERROR;
+++  }
+++  if (strict_uri_parsing == 2 && !url_is_mostly_compliant(*start, end)) {
++     return PARSE_RESULT_ERROR;
++   }
++ 
++Index: trafficserver/proxy/hdrs/URL.h
++===================================================================
++--- trafficserver.orig/proxy/hdrs/URL.h       2022-05-21 21:12:19.571389326 +0200
+++++ trafficserver/proxy/hdrs/URL.h    2022-05-21 21:12:19.567389350 +0200
++@@ -198,14 +198,13 @@
++ void url_fragment_set(HdrHeap *heap, URLImpl *url, const char *value, int length, bool copy_string);
++ 
++ ParseResult url_parse(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings,
++-                      bool strict_uri_parsing = false);
+++                      int strict_uri_parsing = false);
++ ParseResult url_parse_no_path_component_breakdown(HdrHeap *heap, URLImpl *url, const char **start, const char *end,
++                                                   bool copy_strings);
++ ParseResult url_parse_internet(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings);
++ ParseResult url_parse_http(HdrHeap *heap, URLImpl *url, const char **start, const char *end, bool copy_strings);
++ ParseResult url_parse_http_no_path_component_breakdown(HdrHeap *heap, URLImpl *url, const char **start, const char *end,
++                                                        bool copy_strings);
++-
++ char *url_unescapify(Arena *arena, const char *str, int length);
++ 
++ void unescape_str(char *&buf, char *buf_e, const char *&str, const char *str_e, int &state);
++Index: trafficserver/proxy/http/HttpConfig.cc
++===================================================================
++--- trafficserver.orig/proxy/http/HttpConfig.cc       2022-05-21 21:12:19.571389326 +0200
+++++ trafficserver/proxy/http/HttpConfig.cc    2022-05-21 21:12:19.567389350 +0200
++@@ -1475,7 +1475,7 @@
++   params->referer_filter_enabled  = INT_TO_BOOL(m_master.referer_filter_enabled);
++   params->referer_format_redirect = INT_TO_BOOL(m_master.referer_format_redirect);
++ 
++-  params->strict_uri_parsing = INT_TO_BOOL(m_master.strict_uri_parsing);
+++  params->strict_uri_parsing = m_master.strict_uri_parsing;
++ 
++   params->oride.down_server_timeout    = m_master.oride.down_server_timeout;
++   params->oride.client_abort_threshold = m_master.oride.client_abort_threshold;
++Index: trafficserver/proxy/http/HttpConfig.h
++===================================================================
++--- trafficserver.orig/proxy/http/HttpConfig.h        2022-05-21 21:12:19.571389326 +0200
+++++ trafficserver/proxy/http/HttpConfig.h     2022-05-21 21:12:19.567389350 +0200
++@@ -847,7 +847,7 @@
++   MgmtByte referer_filter_enabled  = 0;
++   MgmtByte referer_format_redirect = 0;
++ 
++-  MgmtByte strict_uri_parsing = 0;
+++  MgmtByte strict_uri_parsing = 2;
++ 
++   MgmtByte reverse_proxy_enabled = 0;
++   MgmtByte url_remap_required    = 1;
++Index: trafficserver/tests/gold_tests/headers/gold/bad_good_request.gold
++===================================================================
++--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++++ trafficserver/tests/gold_tests/headers/gold/bad_good_request.gold 2022-05-21 21:12:19.567389350 +0200
++@@ -0,0 +1,9 @@
+++``HTTP/1.1 400 Invalid HTTP Request
+++``Connection: close
+++``Server: ATS/``
+++``Content-Length: 219
+++``
+++<TITLE>Bad Request</TITLE>
+++``<H1>Bad Request</H1>
+++``Description: Could not process this request.
+++``
++Index: trafficserver/tests/gold_tests/headers/gold/bad_good_request_header.gold
++===================================================================
++--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++++ trafficserver/tests/gold_tests/headers/gold/bad_good_request_header.gold  2022-05-21 21:12:19.567389350 +0200
++@@ -0,0 +1,5 @@
+++``HTTP/1.1 400 Invalid HTTP Request
+++``Connection: close
+++``Server: ATS/``
+++``Content-Length: 219
+++``
++Index: trafficserver/tests/gold_tests/headers/gold/bad_good_request_http1.gold
++===================================================================
++--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++++ trafficserver/tests/gold_tests/headers/gold/bad_good_request_http1.gold   2022-05-21 21:12:19.567389350 +0200
++@@ -0,0 +1,8 @@
+++``HTTP/1.0 400 Invalid HTTP Request
+++``Server: ATS/``
+++``Content-Length: 219
+++``
+++<TITLE>Bad Request</TITLE>
+++``<H1>Bad Request</H1>
+++``Description: Could not process this request.
+++``
++Index: trafficserver/tests/gold_tests/headers/gold/bad_method.gold
++===================================================================
++--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++++ trafficserver/tests/gold_tests/headers/gold/bad_method.gold       2022-05-21 21:12:19.567389350 +0200
++@@ -0,0 +1,24 @@
+++HTTP/1.1 501 Unsupported method ('gET')
+++Content-Type: text/html;charset=utf-8
+++Content-Length: 496
+++Date: ``
+++Age: 0
+++Connection: keep-alive
+++Server: ATS/``
+++
+++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+++        "http://www.w3.org/TR/html4/strict.dtd">
+++<html>
+++    <head>
+++        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+++        <title>Error response</title>
+++    </head>
+++    <body>
+++        <h1>Error response</h1>
+++        <p>Error code: 501</p>
+++        <p>Message: Unsupported method ('gET').</p>
+++        <p>Error code explanation: HTTPStatus.NOT_IMPLEMENTED - Server does not support this operation.</p>
+++    </body>
+++</html>
+++HTTP/1.1 200 OK
+++``
++Index: trafficserver/tests/gold_tests/headers/gold/bad_protocol_number.gold
++===================================================================
++--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++++ trafficserver/tests/gold_tests/headers/gold/bad_protocol_number.gold      2022-05-21 21:12:19.567389350 +0200
++@@ -0,0 +1,22 @@
+++HTTP/1.1 505 Unsupported HTTP Version
+++Date: ``
+++Server: ATS/``
+++Cache-Control: no-store
+++Content-Type: text/html
+++Content-Language: en
+++Content-Length: 219
+++
+++<HTML>
+++<HEAD>
+++<TITLE>Bad Request</TITLE>
+++</HEAD>
+++
+++<BODY BGCOLOR="white" FGCOLOR="black">
+++<H1>Bad Request</H1>
+++<HR>
+++
+++<FONT FACE="Helvetica,Arial"><B>
+++Description: Could not process this request.
+++</B></FONT>
+++<HR>
+++</BODY>
++Index: trafficserver/tests/gold_tests/headers/gold/bad_te_value.gold
++===================================================================
++--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++++ trafficserver/tests/gold_tests/headers/gold/bad_te_value.gold     2022-05-21 21:12:19.567389350 +0200
++@@ -0,0 +1,25 @@
+++HTTP/1.1 501 Field not implemented
+++Date: ``
+++Connection: keep-alive
+++Server: ATS/``
+++Cache-Control: no-store
+++Content-Type: text/html
+++Content-Language: en
+++Content-Length: 289
+++
+++<HTML>
+++<HEAD>
+++<TITLE>Transcoding Not Available</TITLE>
+++</HEAD>
+++
+++<BODY BGCOLOR="white" FGCOLOR="black">
+++<H1>Transcoding Not Available</H1>
+++<HR>
+++
+++<FONT FACE="Helvetica,Arial">
+++
+++<B> Description: Unable to provide the document in the
+++format requested by your browser.
+++</B></FONT>
+++<HR>
+++</BODY>
++Index: trafficserver/tests/gold_tests/headers/good_request_after_bad.test.py
++===================================================================
++--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++++ trafficserver/tests/gold_tests/headers/good_request_after_bad.test.py     2022-05-21 21:12:19.567389350 +0200
++@@ -0,0 +1,196 @@
+++'''
+++Verify that request following a ill-formed request is not processed
+++'''
+++#  Licensed to the Apache Software Foundation (ASF) under one
+++#  or more contributor license agreements.  See the NOTICE file
+++#  distributed with this work for additional information
+++#  regarding copyright ownership.  The ASF licenses this file
+++#  to you under the Apache License, Version 2.0 (the
+++#  "License"); you may not use this file except in compliance
+++#  with the License.  You may obtain a copy of the License at
+++#
+++#      http://www.apache.org/licenses/LICENSE-2.0
+++#
+++#  Unless required by applicable law or agreed to in writing, software
+++#  distributed under the License is distributed on an "AS IS" BASIS,
+++#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+++#  See the License for the specific language governing permissions and
+++#  limitations under the License.
+++
+++import os
+++
+++Test.Summary = '''
+++Verify that request following a ill-formed request is not processed
+++'''
+++Test.ContinueOnFail = True
+++ts = Test.MakeATSProcess("ts")
+++Test.ContinueOnFail = True
+++ts.Disk.records_config.update({'proxy.config.diags.debug.tags': 'http',
+++                               'proxy.config.diags.debug.enabled': 0,
+++                               'proxy.config.http.strict_uri_parsing': 1
+++                               })
+++
+++ts2 = Test.MakeATSProcess("ts2")
+++
+++ts2.Disk.records_config.update({'proxy.config.diags.debug.tags': 'http',
+++                                'proxy.config.diags.debug.enabled': 0,
+++                                'proxy.config.http.strict_uri_parsing': 2
+++                                })
+++
+++
+++server = Test.MakeOriginServer("server")
+++request_header = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+++response_header = {
+++    "headers": "HTTP/1.1 200 OK\r\nConnection: close\r\nLast-Modified: Tue, 08 May 2018 15:49:41 GMT\r\nCache-Control: max-age=1000\r\n\r\n",
+++    "timestamp": "1469733493.993",
+++    "body": "xxx"}
+++server.addResponse("sessionlog.json", request_header, response_header)
+++
+++ts.Disk.remap_config.AddLine(
+++    'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
+++)
+++ts.Disk.remap_config.AddLine(
+++    'map /bob<> http://127.0.0.1:{0}'.format(server.Variables.Port)
+++)
+++ts2.Disk.remap_config.AddLine(
+++    'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
+++)
+++ts2.Disk.remap_config.AddLine(
+++    'map /bob<> http://127.0.0.1:{0}'.format(server.Variables.Port)
+++)
+++
+++trace_out = Test.Disk.File("trace_curl.txt")
+++
+++# Make a good request to get item in the cache for later tests
+++tr = Test.AddTestRun("Good control")
+++tr.Processes.Default.StartBefore(server)
+++tr.Processes.Default.StartBefore(Test.Processes.ts)
+++tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nHost: bob\r\n\r\n" | nc  127.0.0.1 {}'.format(ts.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++
+++tr = Test.AddTestRun("Good control")
+++tr.Processes.Default.StartBefore(server)
+++tr.Processes.Default.StartBefore(Test.Processes.ts2)
+++tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nHost: bob\r\n\r\n" | nc  127.0.0.1 {}'.format(ts2.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++
+++tr = Test.AddTestRun("space after header name")
+++tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nHost : bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++    ts.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold'
+++
+++# Commenting out a bunch of tests on master whose fixes are not in 8.1.x.
+++#tr = Test.AddTestRun("Bad protocol number")
+++#tr.Processes.Default.Command = 'printf "GET / HTTP/11.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.stdout = 'gold/bad_protocol_number.gold'
+++#
+++#tr = Test.AddTestRun("Unsupported Transfer Encoding value")
+++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ntransfer-encoding: random\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.stdout = 'gold/bad_te_value.gold'
+++#
+++#tr = Test.AddTestRun("Another unsupported Transfer Encoding value")
+++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ntransfer-encoding: \x08chunked\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.stdout = 'gold/bad_te_value.gold'
+++#
+++#tr = Test.AddTestRun("Extra characters in content-length")
+++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ncontent-length:+3\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_header.gold'
+++#
+++#tr = Test.AddTestRun("Different extra characters in content-length")
+++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ncontent-length:\x0c3\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_header.gold'
+++#
+++#
+++## TRACE request with a body
+++#tr = Test.AddTestRun("Trace request with a body")
+++#tr.Processes.Default.Command = 'printf "TRACE /foo HTTP/1.1\r\nHost: bob\r\nContent-length:2\r\n\r\nokGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold'
+++#
+++#tr = Test.AddTestRun("Trace request with a chunked body")
+++#tr.Processes.Default.Command = 'printf "TRACE /foo HTTP/1.1\r\nHost: bob\r\ntransfer-encoding: chunked\r\n\r\n2\r\nokGGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold'
+++#
+++#tr = Test.AddTestRun("Trace request with a chunked body via curl")
+++#tr.Processes.Default.Command = 'curl -v --http1.1 --header "Transfer-Encoding: chunked" -d aaa -X TRACE -o trace_curl.txt -k http://127.0.0.1:{}/foo'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.All = 'gold/bad_good_request_header.gold'
+++#trace_out.Content = Testers.ContainsExpression("<TITLE>Bad Request</TITLE>", "ATS error msg")
+++#trace_out.Content += Testers.ContainsExpression("Description: Could not process this request.", "ATS error msg")
+++#
+++#tr = Test.AddTestRun("Trace request via curl")
+++#tr.Processes.Default.Command = 'curl -v --http1.1 -X TRACE -k http://127.0.0.1:{}/bar'.format(ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.All = Testers.ContainsExpression(
+++#    r"HTTP/1.1 501 Unsupported method \('TRACE'\)",
+++#    "microserver does not support TRACE")
+++#
+++## Methods are case sensitive. Verify that "gET" is not confused with "GET".
+++#tr = Test.AddTestRun("mixed case method")
+++#tr.Processes.Default.Command = 'printf "gET / HTTP/1.1\r\nHost:bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.stdout = 'gold/bad_method.gold'
+++#
+++## mangled termination
+++#tr = Test.AddTestRun("mangled line termination")
+++#tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nHost:bob\r\n \r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++#    ts.Variables.port)
+++#tr.Processes.Default.ReturnCode = 0
+++#tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold'
+++
+++tr = Test.AddTestRun("Catch bad URL characters")
+++tr.Processes.Default.Command = 'printf "GET /bob<> HTTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++    ts.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++# Since the request line is messsed up ATS will reply with HTTP/1.0
+++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold'
+++
+++tr = Test.AddTestRun("Catch whitespace in URL")
+++tr.Processes.Default.Command = 'printf "GET /bob foo HTTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++    ts.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++# Since the request line is messsed up ATS will reply with HTTP/1.0
+++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold'
+++
+++tr = Test.AddTestRun("Extra characters in protocol")
+++tr.Processes.Default.Command = 'printf "GET / HTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++    ts.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++# Since the request line is messsed up ATS will reply with HTTP/1.0
+++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold'
+++
+++tr = Test.AddTestRun("Characters that are strict but not case 2 bad")
+++tr.Processes.Default.Command = 'printf "GET /bob<> HTTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++    ts2.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++tr.Processes.Default.Streams.All = Testers.ContainsExpression("HTTP/1.1 200 OK", "Success")
+++
+++tr = Test.AddTestRun("Catch whitespace in URL")
+++tr.Processes.Default.Command = 'printf "GET /bob foo HTTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++    ts2.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++# Since the request line is messsed up ATS will reply with HTTP/1.0
+++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold'
+++
+++tr = Test.AddTestRun("Extra characters in protocol")
+++tr.Processes.Default.Command = 'printf "GET / HTP/1.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++    ts2.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++# Since the request line is messsed up ATS will reply with HTTP/1.0
+++tr.Processes.Default.Streams.stdout = 'gold/bad_good_request_http1.gold'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c7eed12643569174a7d300c8c33507992ff7dadc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++Description: Bug fix in origin connection handling
++Origin: upstream
++Applied-Upstream: https://github.com/apache/trafficserver/commit/d3f36f79820ea10c26573c742b1bbc370c351716
++Reviewed-by: Jean Baptiste Favre <debian@jbfavre.org>
++Last-Update: 2022-05-21
++---
++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
++Index: trafficserver/iocore/net/SSLNetVConnection.cc
++===================================================================
++--- trafficserver.orig/iocore/net/SSLNetVConnection.cc        2022-05-21 21:12:19.763388158 +0200
+++++ trafficserver/iocore/net/SSLNetVConnection.cc     2022-05-21 21:12:19.763388158 +0200
++@@ -1036,8 +1036,7 @@
++       // Making the check here instead of later, so we only
++       // do this setting immediately after we create the SSL object
++       SNIConfig::scoped_config sniParam;
++-      int8_t clientVerify = 0;
++-      cchar *serverKey    = this->options.sni_servername;
+++      cchar *serverKey = this->options.sni_servername;
++       if (!serverKey) {
++         ats_ip_ntop(this->get_remote_addr(), buff, INET6_ADDRSTRLEN);
++         serverKey = buff;
++@@ -1046,24 +1045,30 @@
++       SSL_CTX *clientCTX = nullptr;
++ 
++       if (nps) {
++-        clientCTX    = nps->ctx;
++-        clientVerify = nps->verifyLevel;
+++        clientCTX = nps->ctx;
++       } else {
++-        clientCTX    = params->client_ctx;
++-        clientVerify = params->clientVerify;
+++        clientCTX = params->client_ctx;
++       }
+++
++       if (!clientCTX) {
++         SSLErrorVC(this, "failed to create SSL client session");
++         return EVENT_ERROR;
++       }
++ 
+++      if (nps && nps->verifyLevel != static_cast<uint8_t>(YamlSNIConfig::Level::UNSET)) {
+++        this->options.clientVerificationFlag = nps->verifyLevel;
+++      } else {
+++        // Keeping backwards compatibility on the proxy.config.ssl.client.verify.server setting
+++        this->options.clientVerificationFlag = params->clientVerify ? (params->clientVerify == 1 ? 2 : 1) : 0;
+++      }
+++
++       this->ssl = make_ssl_connection(clientCTX, this);
++       if (this->ssl == nullptr) {
++         SSLErrorVC(this, "failed to create SSL client session");
++         return EVENT_ERROR;
++       }
++       int verify_op;
++-      if (clientVerify) {
+++      if (this->options.clientVerificationFlag) {
++         verify_op = SSL_VERIFY_PEER;
++         SSL_set_verify(this->ssl, verify_op, verify_callback);
++       } else {
++Index: trafficserver/iocore/net/YamlSNIConfig.h
++===================================================================
++--- trafficserver.orig/iocore/net/YamlSNIConfig.h     2022-05-21 21:12:19.763388158 +0200
+++++ trafficserver/iocore/net/YamlSNIConfig.h  2022-05-21 21:12:19.763388158 +0200
++@@ -44,7 +44,7 @@
++     client_cert
++ 
++   };
++-  enum class Level { NONE = 0, MODERATE, STRICT };
+++  enum class Level { NONE = 0, MODERATE, STRICT, UNSET };
++ 
++   YamlSNIConfig() {}
++ 
++@@ -53,7 +53,7 @@
++     bool disable_h2             = false;
++     uint8_t verify_client_level = 0;
++     std::string tunnel_destination;
++-    uint8_t verify_origin_server = 0;
+++    uint8_t verify_origin_server = static_cast<uint8_t>(Level::UNSET);
++     std::string client_cert;
++     std::string ip_allow;
++   };
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d015fe06d8e36d58c24f3bc236ec54a50d1fe11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++From 4da63a69cbce10a6cd4d103de9f9b01d9c9be908 Mon Sep 17 00:00:00 2001
++From: Brian Neradt <brian.neradt@gmail.com>
++Date: Mon, 8 Aug 2022 22:18:49 -0500
++Subject: [PATCH] Add back validatation that the scheme matches the wire
++ protocol (#9007)
++
++This adds back in the scheme and wire protocol check (see #8465) along
++with a configuration to be able to disable the check if the verification
++is not desired.
++---
++ doc/admin-guide/files/records.config.en.rst | 23 +++++++++++++++++++++
++ mgmt/RecordsConfig.cc                       |  2 ++
++ proxy/http/HttpConfig.cc                    |  4 +++-
++ proxy/http/HttpConfig.h                     |  1 +
++ proxy/http/HttpSM.cc                        | 14 +++++++++++++
++ 5 files changed, 43 insertions(+), 1 deletion(-)
++
++--- a/doc/admin-guide/files/records.config.en.rst
+++++ b/doc/admin-guide/files/records.config.en.rst
++@@ -3413,6 +3413,29 @@ Client-Related Configuration
++ 
++    Enables (``1``) or disables (``0``) TLSv1_1 in the ATS client context. If not specified, enabled by default
++ 
+++.. ts:cv:: CONFIG proxy.config.ssl.client.scheme_proto_mismatch_policy INT 2
+++   :overridable:
+++
+++   This option controls how |TS| behaves when the client side connection
+++   protocol and the client request's scheme do not match. For example, if
+++   enforcement is enabled by setting this value to ``2`` and the client
+++   connection is a cleartext HTTP connection but the scheme of the URL is
+++   ``https://``, then |TS| will emit a warning and return an immediate 400 HTTP
+++   response without proxying the request to the origin.
+++
+++   The default value is ``2``, meaning that |TS| will enforce that the protocol
+++   matches the scheme.
+++
+++   ===== ======================================================================
+++   Value Description
+++   ===== ======================================================================
+++   ``0`` Disable verification that the protocol and scheme match.
+++   ``1`` Check that the protocol and scheme match, but only emit a warning if
+++         they do not.
+++   ``2`` Check that the protocol and scheme match and, if they do not, emit a
+++         warning and return an immediate HTTP 400 response.
+++   ===== ======================================================================
+++
++ .. ts:cv:: CONFIG proxy.config.ssl.client.TLSv1_2 INT 1
++ 
++    Enables (``1``) or disables (``0``) TLSv1_2 in the ATS client context. If not specified, enabled by default
++--- a/mgmt/RecordsConfig.cc
+++++ b/mgmt/RecordsConfig.cc
++@@ -1137,6 +1137,8 @@ static const RecordElement RecordsConfig
++   ,
++   {RECT_CONFIG, "proxy.config.ssl.client.CA.cert.path", RECD_STRING, TS_BUILD_SYSCONFDIR, RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
++   ,
+++  {RECT_CONFIG, "proxy.config.ssl.client.scheme_proto_mismatch_policy", RECD_INT, "2", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
+++  ,
++   {RECT_CONFIG, "proxy.config.ssl.session_cache", RECD_INT, "2", RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
++   ,
++   {RECT_CONFIG, "proxy.config.ssl.session_cache.size", RECD_INT, "102400", RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
++--- a/proxy/http/HttpConfig.cc
+++++ b/proxy/http/HttpConfig.cc
++@@ -1225,7 +1225,7 @@ HttpConfig::startup()
++   HttpEstablishStaticConfigByte(c.redirection_host_no_port, "proxy.config.http.redirect_host_no_port");
++   HttpEstablishStaticConfigLongLong(c.oride.number_of_redirections, "proxy.config.http.number_of_redirections");
++   HttpEstablishStaticConfigLongLong(c.post_copy_size, "proxy.config.http.post_copy_size");
++-
+++  HttpEstablishStaticConfigByte(c.scheme_proto_mismatch_policy, "proxy.config.ssl.client.scheme_proto_mismatch_policy");
++   http_config_cont->handleEvent(EVENT_NONE, nullptr);
++ 
++   return;
++@@ -1492,6 +1492,8 @@ HttpConfig::reconfigure()
++   params->oride.client_cert_filename        = ats_strdup(m_master.oride.client_cert_filename);
++   params->oride.client_cert_filepath        = ats_strdup(m_master.oride.client_cert_filepath);
++ 
+++  params->scheme_proto_mismatch_policy = m_master.scheme_proto_mismatch_policy;
+++
++   params->negative_caching_list = m_master.negative_caching_list;
++ 
++   m_id = configProcessor.set(m_id, params);
++--- a/proxy/http/HttpConfig.h
+++++ b/proxy/http/HttpConfig.h
++@@ -873,6 +873,7 @@ public:
++ 
++   MgmtInt body_factory_response_max_size = 8192;
++ 
+++  MgmtByte scheme_proto_mismatch_policy = 2;
++   // noncopyable
++   /////////////////////////////////////
++   // operator = and copy constructor //
++--- a/proxy/http/HttpSM.cc
+++++ b/proxy/http/HttpSM.cc
++@@ -727,17 +727,20 @@ HttpSM::state_read_client_request_header
++   case PARSE_RESULT_DONE:
++     SMDebug("http", "[%" PRId64 "] done parsing client request header", sm_id);
++ 
++-    if (!is_internal) {
+++        if (!is_internal && t_state.http_config_param->scheme_proto_mismatch_policy != 0) {
++       auto scheme = t_state.hdr_info.client_request.url_get()->scheme_get_wksidx();
++       if ((client_connection_is_ssl && (scheme == URL_WKSIDX_HTTP || scheme == URL_WKSIDX_WS)) ||
++           (!client_connection_is_ssl && (scheme == URL_WKSIDX_HTTPS || scheme == URL_WKSIDX_WSS))) {
++-        SMDebug("http", "scheme [%s] vs. protocol [%s] mismatch", hdrtoken_index_to_wks(scheme),
+++        Warning("scheme [%s] vs. protocol [%s] mismatch", hdrtoken_index_to_wks(scheme),
++                 client_connection_is_ssl ? "tls" : "plaintext");
++-        t_state.http_return_code = HTTP_STATUS_BAD_REQUEST;
++-        call_transact_and_set_next_state(HttpTransact::BadRequest);
++-        break;
+++        if (t_state.http_config_param->scheme_proto_mismatch_policy == 2) {
+++          t_state.http_return_code = HTTP_STATUS_BAD_REQUEST;
+++          call_transact_and_set_next_state(HttpTransact::BadRequest);
+++          break;
+++        }
++       }
++     }
+++
++     ua_txn->set_session_active();
++ 
++     if (t_state.hdr_info.client_request.version_get() == HTTPVersion(1, 1) &&
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4e6e75cd851c8c8008d77beb2064d05653c325ca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,619 @@@
++From 0ca9ef5abc8a535d05150ebc7c16bbfa4e982d16 Mon Sep 17 00:00:00 2001
++From: Masaori Koshiba <masaori@apache.org>
++Date: Tue, 9 Aug 2022 12:19:13 +0900
++Subject: [PATCH] [8.1.x] Backport HTTP Validations (#9015)
++
++---
++ .gitignore                                    |  1 +
++ include/tscore/ParseRules.h                   | 14 +++-
++ proxy/hdrs/MIME.cc                            | 15 ++++
++ proxy/hdrs/MIME.h                             | 12 +--
++ proxy/hdrs/Makefile.am                        | 21 +++++
++ proxy/hdrs/URL.cc                             | 42 +++++++---
++ proxy/hdrs/URL.h                              |  2 +
++ proxy/hdrs/unit_tests/test_Hdrs.cc            | 80 +++++++++++++++++++
++ proxy/hdrs/unit_tests/test_URL.cc             | 46 +++++++++++
++ proxy/hdrs/unit_tests/unit_test_main.cc       | 44 ++++++++++
++ proxy/http/HttpTunnel.h                       | 12 +--
++ proxy/http2/HTTP2.cc                          | 25 ++++--
++ proxy/http2/Http2ConnectionState.cc           | 14 +++-
++ proxy/http2/Http2Stream.cc                    |  6 +-
++ .../gold/invalid_character_in_te_value.gold   | 23 ++++++
++ .../headers/good_request_after_bad.test.py    |  6 ++
++ 16 files changed, 332 insertions(+), 31 deletions(-)
++ create mode 100644 proxy/hdrs/unit_tests/test_Hdrs.cc
++ create mode 100644 proxy/hdrs/unit_tests/test_URL.cc
++ create mode 100644 proxy/hdrs/unit_tests/unit_test_main.cc
++ create mode 100644 tests/gold_tests/headers/gold/invalid_character_in_te_value.gold
++
++--- a/include/tscore/ParseRules.h
+++++ b/include/tscore/ParseRules.h
++@@ -126,7 +126,7 @@ public:
++   static CTypeResult is_empty(char c);            // wslfcr,#
++   static CTypeResult is_alnum(char c);            // 0-9,A-Z,a-z
++   static CTypeResult is_space(char c);            // ' ' HT,VT,NP,CR,LF
++-  static CTypeResult is_control(char c);          // 0-31 127
+++  static CTypeResult is_control(char c);          // 0x00-0x08, 0x0a-0x1f, 0x7f
++   static CTypeResult is_mime_sep(char c);         // ()<>,;\"/[]?{} \t
++   static CTypeResult is_http_field_name(char c);  // not : or mime_sep except for @
++   static CTypeResult is_http_field_value(char c); // not CR, LF, comma, or "
++@@ -665,14 +665,24 @@ ParseRules::is_space(char c)
++ #endif
++ }
++ 
+++/**
+++   Return true if @c is a control char except HTAB(0x09) and SP(0x20).
+++   If you need to check @c is HTAB or SP, use `ParseRules::is_ws`.
+++ */
++ inline CTypeResult
++ ParseRules::is_control(char c)
++ {
++ #ifndef COMPILE_PARSE_RULES
++   return (parseRulesCType[(unsigned char)c] & is_control_BIT);
++ #else
++-  if (((unsigned char)c) < 32 || ((unsigned char)c) == 127)
+++  if (c == CHAR_HT || c == CHAR_SP) {
+++    return false;
+++  }
+++
+++  if (((unsigned char)c) < 0x20 || c == 0x7f) {
++     return true;
+++  }
+++
++   return false;
++ #endif
++ }
++--- a/proxy/hdrs/MIME.cc
+++++ b/proxy/hdrs/MIME.cc
++@@ -2679,6 +2679,21 @@ mime_parser_parse(MIMEParser *parser, Hd
++ 
++     int field_name_wks_idx = hdrtoken_tokenize(field_name_first, field_name_length);
++ 
+++    if (field_name_wks_idx < 0) {
+++      for (int i = 0; i < field_name_length; ++i) {
+++        if (ParseRules::is_control(field_name_first[i])) {
+++          return PARSE_RESULT_ERROR;
+++        }
+++      }
+++    }
+++
+++    // RFC 9110 Section 5.5. Field Values
+++    for (int i = 0; i < field_value_length; ++i) {
+++      if (ParseRules::is_control(field_value_first[i])) {
+++        return PARSE_RESULT_ERROR;
+++      }
+++    }
+++
++     ///////////////////////////////////////////
++     // build and insert the new field object //
++     ///////////////////////////////////////////
++--- a/proxy/hdrs/MIME.h
+++++ b/proxy/hdrs/MIME.h
++@@ -164,7 +164,7 @@ struct MIMEField {
++   int value_get_comma_list(StrList *list) const;
++ 
++   void name_set(HdrHeap *heap, MIMEHdrImpl *mh, const char *name, int length);
++-  bool name_is_valid() const;
+++  bool name_is_valid(uint32_t invalid_char_bits = is_control_BIT) const;
++ 
++   void value_set(HdrHeap *heap, MIMEHdrImpl *mh, const char *value, int length);
++   void value_set_int(HdrHeap *heap, MIMEHdrImpl *mh, int32_t value);
++@@ -176,7 +176,7 @@ struct MIMEField {
++   // Other separators (e.g. ';' in Set-cookie/Cookie) are also possible
++   void value_append(HdrHeap *heap, MIMEHdrImpl *mh, const char *value, int length, bool prepend_comma = false,
++                     const char separator = ',');
++-  bool value_is_valid() const;
+++  bool value_is_valid(uint32_t invalid_char_bits = is_control_BIT) const;
++   int has_dups() const;
++ };
++ 
++@@ -768,13 +768,13 @@ MIMEField::name_set(HdrHeap *heap, MIMEH
++   -------------------------------------------------------------------------*/
++ 
++ inline bool
++-MIMEField::name_is_valid() const
+++MIMEField::name_is_valid(uint32_t invalid_char_bits) const
++ {
++   const char *name;
++   int length;
++ 
++   for (name = name_get(&length); length > 0; length--) {
++-    if (ParseRules::is_control(name[length - 1])) {
+++    if (ParseRules::is_type(name[length - 1], invalid_char_bits)) {
++       return false;
++     }
++   }
++@@ -875,13 +875,13 @@ MIMEField::value_append(HdrHeap *heap, M
++   -------------------------------------------------------------------------*/
++ 
++ inline bool
++-MIMEField::value_is_valid() const
+++MIMEField::value_is_valid(uint32_t invalid_char_bits) const
++ {
++   const char *value;
++   int length;
++ 
++   for (value = value_get(&length); length > 0; length--) {
++-    if (ParseRules::is_control(value[length - 1])) {
+++    if (ParseRules::is_type(value[length - 1], invalid_char_bits)) {
++       return false;
++     }
++   }
++--- a/proxy/hdrs/Makefile.am
+++++ b/proxy/hdrs/Makefile.am
++@@ -62,9 +62,31 @@ load_http_hdr_LDADD = -L. -lhdrs \
++      $(top_builddir)/src/tscpp/util/libtscpputil.la \
++      @LIBTCL@
++ 
++-check_PROGRAMS = test_mime
+++check_PROGRAMS = test_proxy_hdrs \
+++                 test_mime
+++
+++TESTS = $(check_PROGRAMS)
+++
+++test_proxy_hdrs_CPPFLAGS = $(AM_CPPFLAGS) \
+++     -I$(abs_top_srcdir)/tests/include
+++
+++test_proxy_hdrs_SOURCES = \
+++     unit_tests/unit_test_main.cc \
+++     unit_tests/test_URL.cc \
+++     unit_tests/test_Hdrs.cc
+++
+++test_proxy_hdrs_LDADD = \
+++     $(top_builddir)/src/tscore/libtscore.la \
+++     -L. -lhdrs \
+++     $(top_builddir)/src/tscore/libtscore.la \
+++     $(top_builddir)/src/tscpp/util/libtscpputil.la \
+++     $(top_builddir)/iocore/eventsystem/libinkevent.a \
+++     $(top_builddir)/lib/records/librecords_p.a \
+++     $(top_builddir)/mgmt/libmgmt_p.la \
+++     $(top_builddir)/proxy/shared/libUglyLogStubs.a \
+++     @HWLOC_LIBS@ \
+++     @LIBCAP@
++ 
++-TESTS = test_mime
++ 
++ test_mime_LDADD = -L. -lhdrs \
++      $(top_builddir)/src/tscore/libtscore.la \
++--- a/proxy/hdrs/URL.cc
+++++ b/proxy/hdrs/URL.cc
++@@ -114,6 +114,36 @@ validate_host_name(std::string_view addr
++   return std::all_of(addr.begin(), addr.end(), &is_host_char);
++ }
++ 
+++/**
+++   Checks if the (un-well-known) scheme is valid
+++
+++   RFC 3986 Section 3.1
+++   These are the valid characters in a scheme:
+++     scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+++   return an error if there is another character in the scheme
+++*/
+++bool
+++validate_scheme(std::string_view scheme)
+++{
+++  if (scheme.empty()) {
+++    return false;
+++  }
+++
+++  if (!ParseRules::is_alpha(scheme[0])) {
+++    return false;
+++  }
+++
+++  for (size_t i = 0; i < scheme.size(); ++i) {
+++    const char &c = scheme[i];
+++
+++    if (!(ParseRules::is_alnum(c) != 0 || c == '+' || c == '-' || c == '.')) {
+++      return false;
+++    }
+++  }
+++
+++  return true;
+++}
+++
++ /*-------------------------------------------------------------------------
++   -------------------------------------------------------------------------*/
++ 
++@@ -1140,19 +1170,9 @@ url_parse_scheme(HdrHeap *heap, URLImpl
++ 
++         if (!(scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME)) {
++           // Unknown scheme, validate the scheme
++-
++-          // RFC 3986 Section 3.1
++-          // These are the valid characters in a scheme:
++-          //   scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
++-          // return an error if there is another character in the scheme
++-          if (!ParseRules::is_alpha(*scheme_start)) {
+++          if (!validate_scheme({scheme_start, static_cast<size_t>(scheme_end - scheme_start)})) {
++             return PARSE_RESULT_ERROR;
++           }
++-          for (cur = scheme_start + 1; cur < scheme_end; ++cur) {
++-            if (!(ParseRules::is_alnum(*cur) != 0 || *cur == '+' || *cur == '-' || *cur == '.')) {
++-              return PARSE_RESULT_ERROR;
++-            }
++-          }
++         }
++         url_scheme_set(heap, url, scheme_start, scheme_wks_idx, scheme_end - scheme_start, copy_strings_p);
++       }
++--- a/proxy/hdrs/URL.h
+++++ b/proxy/hdrs/URL.h
++@@ -156,6 +156,8 @@ void url_adjust(MarshalXlate *str_xlate,
++ 
++ /* Public */
++ bool validate_host_name(std::string_view addr);
+++bool validate_scheme(std::string_view scheme);
+++
++ void url_init();
++ 
++ URLImpl *url_create(HdrHeap *heap);
++--- /dev/null
+++++ b/proxy/hdrs/unit_tests/test_Hdrs.cc
++@@ -0,0 +1,80 @@
+++/** @file
+++
+++   Catch-based unit tests for various header logic.
+++   This replaces the old regression tests in HdrTest.cc.
+++
+++   @section license License
+++
+++   Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.
+++   See the NOTICE file distributed with this work for additional information regarding copyright
+++   ownership.  The ASF licenses this file to you under the Apache License, Version 2.0 (the
+++   "License"); you may not use this file except in compliance with the License.  You may obtain a
+++   copy of the License at
+++
+++      http://www.apache.org/licenses/LICENSE-2.0
+++
+++   Unless required by applicable law or agreed to in writing, software distributed under the License
+++   is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+++   or implied. See the License for the specific language governing permissions and limitations under
+++   the License.
+++ */
+++
+++#include <cstdio>
+++
+++#include "catch.hpp"
+++
+++#include "MIME.h"
+++
+++TEST_CASE("HdrTest", "[proxy][hdrtest]")
+++{
+++  mime_init();
+++
+++  SECTION("Field Char Check")
+++  {
+++    static struct {
+++      std::string_view line;
+++      ParseResult expected;
+++    } test_cases[] = {
+++      ////
+++      // Field Name
+++      {"Content-Length: 10\r\n", PARSE_RESULT_CONT},
+++      {"Content-Length\x0b: 10\r\n", PARSE_RESULT_ERROR},
+++      ////
+++      // Field Value
+++      // SP
+++      {"Content-Length: 10\r\n", PARSE_RESULT_CONT},
+++      {"Foo: ab cd\r\n", PARSE_RESULT_CONT},
+++      // HTAB
+++      {"Foo: ab\td/cd\r\n", PARSE_RESULT_CONT},
+++      // VCHAR
+++      {"Foo: ab\x21/cd\r\n", PARSE_RESULT_CONT},
+++      {"Foo: ab\x7e/cd\r\n", PARSE_RESULT_CONT},
+++      // DEL
+++      {"Foo: ab\x7f/cd\r\n", PARSE_RESULT_ERROR},
+++      // obs-text
+++      {"Foo: ab\x80/cd\r\n", PARSE_RESULT_CONT},
+++      {"Foo: ab\xff/cd\r\n", PARSE_RESULT_CONT},
+++      // control char
+++      {"Content-Length: 10\x0b\r\n", PARSE_RESULT_ERROR},
+++      {"Content-Length:\x0b 10\r\n", PARSE_RESULT_ERROR},
+++      {"Foo: ab\x1d/cd\r\n", PARSE_RESULT_ERROR},
+++    };
+++
+++    MIMEHdr hdr;
+++    MIMEParser parser;
+++    mime_parser_init(&parser);
+++
+++    for (const auto &t : test_cases) {
+++      mime_parser_clear(&parser);
+++
+++      const char *start = t.line.data();
+++      const char *end   = start + t.line.size();
+++
+++      int r = hdr.parse(&parser, &start, end, false, false);
+++      if (r != t.expected) {
+++        std::printf("Expected %s is %s, but not", t.line.data(), t.expected == PARSE_RESULT_ERROR ? "invalid" : "valid");
+++        CHECK(false);
+++      }
+++    }
+++  }
+++}
++--- /dev/null
+++++ b/proxy/hdrs/unit_tests/test_URL.cc
++@@ -0,0 +1,46 @@
+++/** @file
+++
+++   Catch-based unit tests for URL
+++
+++   @section license License
+++
+++   Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.
+++   See the NOTICE file distributed with this work for additional information regarding copyright
+++   ownership.  The ASF licenses this file to you under the Apache License, Version 2.0 (the
+++   "License"); you may not use this file except in compliance with the License.  You may obtain a
+++   copy of the License at
+++
+++      http://www.apache.org/licenses/LICENSE-2.0
+++
+++   Unless required by applicable law or agreed to in writing, software distributed under the License
+++   is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+++   or implied. See the License for the specific language governing permissions and limitations under
+++   the License.
+++ */
+++
+++#include <cstdio>
+++
+++#include "catch.hpp"
+++
+++#include "URL.h"
+++
+++TEST_CASE("Validate Scheme", "[proxy][validscheme]")
+++{
+++  static const struct {
+++    std::string_view text;
+++    bool valid;
+++  } scheme_test_cases[] = {{"http", true},      {"https", true},      {"example", true},    {"example.", true},
+++                           {"example++", true}, {"example--.", true}, {"++example", false}, {"--example", false},
+++                           {".example", false}, {"example://", false}};
+++
+++  for (auto i : scheme_test_cases) {
+++    // it's pretty hard to debug with
+++    //     CHECK(validate_scheme(i.text) == i.valid);
+++
+++    std::string_view text = i.text;
+++    if (validate_scheme(text) != i.valid) {
+++      std::printf("Validation of scheme: \"%s\", expected %s, but not\n", text.data(), (i.valid ? "true" : "false"));
+++      CHECK(false);
+++    }
+++  }
+++}
++--- /dev/null
+++++ b/proxy/hdrs/unit_tests/unit_test_main.cc
++@@ -0,0 +1,44 @@
+++/** @file
+++
+++  This file used for catch based tests. It is the main() stub.
+++
+++  @section license License
+++
+++  Licensed to the Apache Software Foundation (ASF) under one
+++  or more contributor license agreements.  See the NOTICE file
+++  distributed with this work for additional information
+++  regarding copyright ownership.  The ASF licenses this file
+++  to you under the Apache License, Version 2.0 (the
+++  "License"); you may not use this file except in compliance
+++  with the License.  You may obtain a copy of the License at
+++
+++      http://www.apache.org/licenses/LICENSE-2.0
+++
+++  Unless required by applicable law or agreed to in writing, software
+++  distributed under the License is distributed on an "AS IS" BASIS,
+++  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+++  See the License for the specific language governing permissions and
+++  limitations under the License.
+++ */
+++
+++#include "HTTP.h"
+++
+++#define CATCH_CONFIG_RUNNER
+++#include "catch.hpp"
+++
+++extern int cmd_disable_pfreelist;
+++
+++int
+++main(int argc, char *argv[])
+++{
+++  // No thread setup, forbid use of thread local allocators.
+++  cmd_disable_pfreelist = true;
+++  // Get all of the HTTP WKS items populated.
+++  http_init();
+++
+++  int result = Catch::Session().run(argc, argv);
+++
+++  // global clean-up...
+++
+++  return result;
+++}
++--- a/proxy/http/HttpTunnel.h
+++++ b/proxy/http/HttpTunnel.h
++@@ -282,6 +282,7 @@ public:
++   }
++   bool is_tunnel_alive() const;
++   bool has_cache_writer() const;
+++  bool has_consumer_besides_client() const;
++ 
++   HttpTunnelProducer *add_producer(VConnection *vc, int64_t nbytes, IOBufferReader *reader_start, HttpProducerHandler sm_handler,
++                                    HttpTunnelType_t vc_type, const char *name);
++@@ -515,6 +516,30 @@ HttpTunnel::has_cache_writer() const
++ }
++ 
++ inline bool
+++HttpTunnel::has_consumer_besides_client() const
+++{
+++  bool res = false; // case of no consumers
+++
+++  for (const auto &consumer : consumers) {
+++    if (!consumer.alive) {
+++      continue;
+++    }
+++
+++    switch (consumer.vc_type) {
+++    case HT_HTTP_CLIENT:
+++      continue;
+++    case HT_HTTP_SERVER:
+++      // ignore uploading data to servers
+++      continue;
+++    default:
+++      return true;
+++    }
+++  }
+++
+++  return res;
+++}
+++
+++inline bool
++ HttpTunnelConsumer::is_downstream_from(VConnection *vc)
++ {
++   HttpTunnelProducer *p = producer;
++--- a/proxy/http2/HTTP2.cc
+++++ b/proxy/http2/HTTP2.cc
++@@ -420,19 +420,33 @@ http2_convert_header_from_2_to_1_1(HTTPH
++     int scheme_len, authority_len, path_len, method_len;
++ 
++     // Get values of :scheme, :authority and :path to assemble requested URL
++-    if ((field = headers->field_find(HTTP2_VALUE_SCHEME, HTTP2_LEN_SCHEME)) != nullptr && field->value_is_valid()) {
+++    if ((field = headers->field_find(HTTP2_VALUE_SCHEME, HTTP2_LEN_SCHEME)) != nullptr &&
+++        field->value_is_valid(is_control_BIT | is_ws_BIT)) {
++       scheme = field->value_get(&scheme_len);
+++
+++      const char *scheme_wks;
+++
+++      int scheme_wks_idx = hdrtoken_tokenize(scheme, scheme_len, &scheme_wks);
+++
+++      if (!(scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME)) {
+++        // unkown scheme, validate the scheme
+++        if (!validate_scheme({scheme, static_cast<size_t>(scheme_len)})) {
+++          return PARSE_RESULT_ERROR;
+++        }
+++      }
++     } else {
++       return PARSE_RESULT_ERROR;
++     }
++ 
++-    if ((field = headers->field_find(HTTP2_VALUE_AUTHORITY, HTTP2_LEN_AUTHORITY)) != nullptr && field->value_is_valid()) {
+++    if ((field = headers->field_find(HTTP2_VALUE_AUTHORITY, HTTP2_LEN_AUTHORITY)) != nullptr &&
+++        field->value_is_valid(is_control_BIT | is_ws_BIT)) {
++       authority = field->value_get(&authority_len);
++     } else {
++       return PARSE_RESULT_ERROR;
++     }
++ 
++-    if ((field = headers->field_find(HTTP2_VALUE_PATH, HTTP2_LEN_PATH)) != nullptr && field->value_is_valid()) {
+++    if ((field = headers->field_find(HTTP2_VALUE_PATH, HTTP2_LEN_PATH)) != nullptr &&
+++        field->value_is_valid(is_control_BIT | is_ws_BIT)) {
++       path = field->value_get(&path_len);
++     } else {
++       return PARSE_RESULT_ERROR;
++@@ -452,7 +466,8 @@ http2_convert_header_from_2_to_1_1(HTTPH
++     arena.str_free(url);
++ 
++     // Get value of :method
++-    if ((field = headers->field_find(HTTP2_VALUE_METHOD, HTTP2_LEN_METHOD)) != nullptr && field->value_is_valid()) {
+++    if ((field = headers->field_find(HTTP2_VALUE_METHOD, HTTP2_LEN_METHOD)) != nullptr &&
+++        field->value_is_valid(is_control_BIT | is_ws_BIT)) {
++       method = field->value_get(&method_len);
++ 
++       int method_wks_idx = hdrtoken_tokenize(method, method_len);
++@@ -494,7 +509,7 @@ http2_convert_header_from_2_to_1_1(HTTPH
++   // Check validity of all names and values
++   MIMEFieldIter iter;
++   for (const MIMEField *field = headers->iter_get_first(&iter); field != nullptr; field = headers->iter_get_next(&iter)) {
++-    if (!field->name_is_valid() || !field->value_is_valid()) {
+++    if (!field->name_is_valid(is_control_BIT | is_ws_BIT) || !field->value_is_valid()) {
++       return PARSE_RESULT_ERROR;
++     }
++   }
++--- a/proxy/http2/Http2ConnectionState.cc
+++++ b/proxy/http2/Http2ConnectionState.cc
++@@ -127,7 +127,7 @@ rcv_data_frame(Http2ConnectionState &cst
++       return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE);
++     }
++     if (!stream->payload_length_is_valid()) {
++-      return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
+++      return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
++                         "recv data bad payload length");
++     }
++   }
++@@ -347,6 +347,12 @@ rcv_headers_frame(Http2ConnectionState &
++       }
++     }
++ 
+++    // Check Content-Length & payload length when END_STREAM flag is true
+++    if (stream->recv_end_stream && !stream->payload_length_is_valid()) {
+++      return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
+++                        "recv data bad payload length");
+++    }
+++
++     // Set up the State Machine
++     if (!empty_request) {
++       SCOPED_MUTEX_LOCK(stream_lock, stream->mutex, this_ethread());
++@@ -878,6 +884,12 @@ rcv_continuation_frame(Http2ConnectionSt
++       }
++     }
++ 
+++    // Check Content-Length & payload length when END_STREAM flag is true
+++    if (stream->recv_end_stream && !stream->payload_length_is_valid()) {
+++      return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
+++                        "recv data bad payload length");
+++    }
+++
++     // Set up the State Machine
++     SCOPED_MUTEX_LOCK(stream_lock, stream->mutex, this_ethread());
++     stream->new_transaction();
++--- a/proxy/http2/Http2Stream.cc
+++++ b/proxy/http2/Http2Stream.cc
++@@ -146,7 +146,11 @@ void
++ Http2Stream::send_request(Http2ConnectionState &cstate)
++ {
++   // Convert header to HTTP/1.1 format
++-  http2_convert_header_from_2_to_1_1(&_req_header);
+++  if (http2_convert_header_from_2_to_1_1(&_req_header) == PARSE_RESULT_ERROR) {
+++    // There's no way to cause Bad Request directly at this time.
+++    // Set an invalid method so it causes an error later.
+++    _req_header.method_set("\xffVOID", 1);
+++  }
++ 
++   // Write header to a buffer.  Borrowing logic from HttpSM::write_header_into_buffer.
++   // Seems like a function like this ought to be in HTTPHdr directly
++--- /dev/null
+++++ b/tests/gold_tests/headers/gold/invalid_character_in_te_value.gold
++@@ -0,0 +1,23 @@
+++HTTP/1.1 400 Invalid HTTP Request
+++Date:``
+++Connection: close
+++Server:``
+++Cache-Control: no-store
+++Content-Type: text/html
+++Content-Language: en
+++Content-Length:``
+++
+++<HTML>
+++<HEAD>
+++<TITLE>Bad Request</TITLE>
+++</HEAD>
+++
+++<BODY BGCOLOR="white" FGCOLOR="black">
+++<H1>Bad Request</H1>
+++<HR>
+++
+++<FONT FACE="Helvetica,Arial"><B>
+++Description: Could not process this request.
+++</B></FONT>
+++<HR>
+++</BODY>
++--- a/tests/gold_tests/headers/good_request_after_bad.test.py
+++++ b/tests/gold_tests/headers/good_request_after_bad.test.py
++@@ -80,6 +80,12 @@ tr.Processes.Default.Command = 'printf "
++ tr.Processes.Default.ReturnCode = 0
++ tr.Processes.Default.Streams.stdout = 'gold/bad_good_request.gold'
++ 
+++tr = Test.AddTestRun("Another unsupported Transfer Encoding value")
+++tr.Processes.Default.Command = 'printf "GET / HTTP/1.1\r\nhost: bob\r\ntransfer-encoding: \x08chunked\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
+++    ts.Variables.port)
+++tr.Processes.Default.ReturnCode = 0
+++tr.Processes.Default.Streams.stdout = 'gold/invalid_character_in_te_value.gold'
+++
++ # Commenting out a bunch of tests on master whose fixes are not in 8.1.x.
++ #tr = Test.AddTestRun("Bad protocol number")
++ #tr.Processes.Default.Command = 'printf "GET / HTTP/11.1\r\nhost: bob\r\n\r\nGET / HTTP/1.1\r\nHost: boa\r\n\r\n" | nc  127.0.0.1 {}'.format(
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d0f4236d8a261ead18ddbe698dcb600f6df1f2e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++0001-Use-mcx16-on-x86-platforms-only.patch
++0003-reproductible-build.patch
++0006-fix-doc-build.patch
++0008-fix-python-check-unused-dependencies.patch
++0009-fix-mysql-8-build.patch
++0011-fix-segfault.patch
++0012-fix-spelling-checks.patch
++0013-fix-perl-interpreter-path.patch
++0014-use_system_yaml-cpp.patch
++0015-8.0.4-CVE-backport.patch
++0015-8.0.5-CVE-backport.patch
++0016-CVE-2019-17559.patch
++0016-CVE-2019-17565.patch
++0016-CVE-2020-1944.patch
++0016-CVE-2020-9481.patch
++0017-CVE-2020-9494.patch
++0018-CVE-2020-17508.patch
++0018-CVE-2020-17509.patch
++0019-CVE-2021-35474_32567_32566_32565_27577.patch
++0020-CVE-2021-37147.patch
++0020-CVE-2021-37148.patch
++0020-CVE-2021-37149.patch
++0020-CVE-2021-38161.patch
++0021-CVE_2021_44040.patch
++0021-CVE_2021_44759.patch
++CVE-2022-25763.patch
++CVE-2021-37150.patch
diff --cc debian/rules
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..127eaae450d37486104a242e72d2b1d7c30a6d9e
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++#!/usr/bin/make -f
++# -*- makefile -*-
++
++# Uncomment this to turn on verbose mode.
++#export DH_VERBOSE=1
++
++export DEB_BUILD_MAINT_OPTIONS = hardening=+all
++export DEB_CFLAGS_MAINT_APPEND = -O3
++export DEB_CXXFLAGS_MAINT_APPEND = -O3
++# mips & mipsel need explicit libatomic link
++ifeq ($(DEB_HOST_ARCH),$(filter $(DEB_HOST_ARCH),mips mipsel))
++  export DEB_LDFLAGS_MAINT_APPEND += -latomic
++endif
++
++# Get build architecture. ./configure is different on Linux
++include /usr/share/dpkg/architecture.mk
++# Get rid f dpkg-parsechangelog
++include /usr/share/dpkg/pkg-info.mk
++
++configure_flags = \
++      --enable-layout=Debian \
++      --sysconfdir=/etc/trafficserver --libdir=/usr/lib/trafficserver \
++      --libexecdir=/usr/lib/trafficserver/modules \
++      --with-user=root --with-group=root --disable-silent-rules \
++      --enable-experimental-plugins --enable-32bit-build \
++      --enable-mime-sanity-check --enable-docs \
++      --with-build-version=$(DEB_VERSION) \
++      --with-yaml-cpp=/usr \
++        $(shell dpkg-buildflags --export=configure)
++
++ifeq ($(DEB_HOST_ARCH_OS),linux)
++      configure_flags += --enable-wccp
++endif
++
++%:
++      dh $@ --with autoreconf
++
++override_dh_auto_configure:
++      dh_auto_configure -- $(configure_flags)
++
++override_dh_auto_install:
++      dh_auto_install -- INSTALLDIRS=vendor
++      rm -f debian/tmp/usr/bin/trafficserver # We install our own
++      # Satisfy §10.2 (http://wiki.debian.org/ReleaseGoals/LAFileRemoval)
++      rm -f debian/tmp/usr/lib/trafficserver/lib*.la
++      rm -f debian/tmp/usr/lib/trafficserver/modules/*.la
++      rm -f debian/tmp/usr/lib/trafficserver/lib*.a
++ifneq ($(DEB_HOST_ARCH_OS),linux)
++      # Remove Linux-specific plugin
++      sed -i '/\/healthchecks\.so$$/d' \
++              debian/trafficserver.install
++endif
++
++override_dh_install:
++      dh_install
++      dh_missing --list-missing
++      ./debian/change_config.pl debian/trafficserver/etc/trafficserver/records.config
++      # Lintian fixes
++      mkdir -p debian/trafficserver/usr/share/doc/trafficserver
++      cat CHANGELOG-* >> debian/trafficserver/usr/share/doc/trafficserver/changelog
++      #rm debian/trafficserver-dev/usr/share/doc/trafficserver-dev/examples/ssl-preaccept/ssl_preaccept.config
++      #(cd debian/trafficserver-dev/usr/share/doc/trafficserver-dev/examples/ssl-preaccept/; \
++      #ln -s ssl_preaccept.config ssl_sni.config)
++
++override_dh_fixperms:
++      dh_fixperms -Xvar/lib/trafficserver \
++                  -Xvar/log/trafficserver \
++                  -Xvar/cache/trafficserver \
++                  -Xvar/run/trafficserver
++      # Fix lintian warning
++      chmod -x debian/trafficserver-dev/usr/share/doc/trafficserver-dev/examples/remap_header_add/build.txt
++
++override_dh_installexamples:
++      dh_installexamples -XMakefile -X.libs -X.dirstamp -X.deps -X.la -X.lo
++
++override_dh_makeshlibs:
++      dh_makeshlibs -Xdebian/tmp/usr/lib/trafficserver
++
++override_dh_auto_test:
++      -dh_auto_test
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..163aaf8d82b6c54f23c45f32895dbdfdcc27b047
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++3.0 (quilt)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9693b2ab1d8ec10a1aef374dd91f03ddf582589a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++--extend-diff-ignore='examples/|diags.log|lib/perl/'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0bbe99ef554bf54b9b6b84033113941dca478ca9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++example/*
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..973eea9a68e11d187360069e8e5fe8c85e108fff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++debian/tmp/usr/include/*
++debian/tmp/usr/bin/tsxs
++debian/tmp/usr/lib/trafficserver/lib*.so
++debian/tmp/usr/lib/trafficserver/pkgconfig/trafficserver.pc
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c1687793a2c7d120df0b0c2c375683a006e91222
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++debian/tmp/usr/share/man/man1/tsxs.1
++debian/tmp/usr/share/man/man3/*
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d8c3707abacbf51969b890d48b409ba128822f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++usr/lib/trafficserver/modules/access_control.so
++usr/lib/trafficserver/modules/acme.so
++usr/lib/trafficserver/modules/balancer.so
++usr/lib/trafficserver/modules/buffer_upload.so
++usr/lib/trafficserver/modules/cache_key_genid.so
++usr/lib/trafficserver/modules/cache_range_requests.so
++usr/lib/trafficserver/modules/certifier.so
++usr/lib/trafficserver/modules/collapsed_forwarding.so
++usr/lib/trafficserver/modules/custom_redirect.so
++usr/lib/trafficserver/modules/fq_pacing.so
++usr/lib/trafficserver/modules/geoip_acl.so
++usr/lib/trafficserver/modules/header_freq.so
++usr/lib/trafficserver/modules/header_normalize.so
++usr/lib/trafficserver/modules/hipes.so
++usr/lib/trafficserver/modules/hook-trace.so
++usr/lib/trafficserver/modules/inliner.so
++usr/lib/trafficserver/modules/tsmemcache.so
++usr/lib/trafficserver/modules/memcached_remap.so
++usr/lib/trafficserver/modules/metalink.so
++usr/lib/trafficserver/modules/money_trace.so
++usr/lib/trafficserver/modules/mp4.so
++usr/lib/trafficserver/modules/multiplexer.so
++usr/lib/trafficserver/modules/mysql_remap.so
++usr/lib/trafficserver/modules/prefetch.so
++usr/lib/trafficserver/modules/remap_purge.so
++usr/lib/trafficserver/modules/remap_stats.so
++usr/lib/trafficserver/modules/server_push_preload.so
++usr/lib/trafficserver/modules/ssl_cert_loader.so
++usr/lib/trafficserver/modules/sslheaders.so
++usr/lib/trafficserver/modules/stale_while_revalidate.so
++usr/lib/trafficserver/modules/stream_editor.so
++usr/lib/trafficserver/modules/system_stats.so
++usr/lib/trafficserver/modules/tls_bridge.so
++usr/lib/trafficserver/modules/traffic_dump.so
++usr/lib/trafficserver/modules/uri_signing.so
++usr/lib/trafficserver/modules/url_sig.so
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77c80dbfdd04aaaebab53a3376eb0ba97def8733
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++# changelog is already provided in trafficserver main package.
++# no need to duplicate it in each package
++no-upstream-changelog
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ca86af353a7dce49ebdbe872b34aea31e82f6d5f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++## Defaults for trafficserver initscript
++## sourced by /etc/init.d/trafficserver
++## installed at /etc/default/trafficserver by the maintainer scripts
++
++##
++## This is a POSIX shell fragment
++##
++
++## Variable: RUNDIR
++## Default: /var/run/trafficserver
++## Description: Set this to the directory where runtime data is stored. The
++##              default value should work fine for almost all users.
++# RUNDIR=/var/run/trafficserver
++
++
++## Configuration for `traffic_manager'.
++## Meaning of variables is analogous to traffic_cop above, but for the
++## `traffic_manager' binary.
++
++TM_START=yes
++# TM_DAEMON_ARGS=""
++# TM_PIDFILE=$RUNDIR/manager.lock
++
++##
++## NOTICE:
++##        Typically you do not want to configure anything below. Note, generally Traffic
++##        Server is started through `traffic_cop' which is a watchdog to control any local
++##        Traffic Server instances. It starts both, traffic_manager and traffic_server, as
++##        does it monitor these processes. While it is generally not advised, you can
++##        choose to manage both processes yourself. In such cases do not set TC_START to
++##        "yes" and enable any service you want below.
++##
++##        Choose either alternative, but do not mix up both.
++
++## Configuration for `traffic_server'.
++## Meaning of variables is analogous to traffic_cop above, but for the
++## `traffic_server' binary.
++
++# TS_START=no
++# TS_DAEMON_ARGS=""
++# TS_PIDFILE=$RUNDIR/server.lock
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..91e6eaf4bee55ee4275058f787c012db8bc4df31
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++/var/cache/trafficserver
++/var/log/trafficserver
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b9d32e1e9272b3825b14a48b11def0b223deeb35
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++plugins/experimental/cacheurl/*.example
++plugins/experimental/mysql_remap/sample.ini
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cc1179b0bfc5b7dd606764f0dc630ad12d5dc56b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,257 @@@
++#! /bin/sh
++
++### BEGIN INIT INFO
++# Provides:          trafficserver
++# Required-Start:    $remote_fs $syslog
++# Required-Stop:     $remote_fs $syslog
++# Default-Start:     2 3 4 5
++# Default-Stop:      0 1 6
++# Short-Description: init script for the Apache Traffic Server
++# Description:       Apache Traffic Server is fast, scalable and extensible
++#                    HTTP/1.1 compliant caching proxy server.
++### END INIT INFO
++
++# Author: Arno Töll <debian@toell.net>
++#
++# This init script is derived from the source package's version shipped
++# along the source tarball as rc/trafficserver. Therefore it is a derivative
++# work and licensed as follows:
++#
++#
++# Licensed to the Apache Software Foundation (ASF) under one or more
++# contributor license agreements.  See the NOTICE file distributed with
++# this work for additional information regarding copyright ownership.
++# The ASF licenses this file to You under the Apache License, Version 2.0
++# (the "License"); you may not use this file except in compliance with
++# the License.  You may obtain a copy of the License at
++#
++#     http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
++PATH=/sbin:/usr/sbin:/bin:/usr/bin
++DESC="Apache Traffic Server"
++NAME=trafficserver
++SCRIPTNAME=/etc/init.d/$NAME
++
++
++# Please do not touch TS_ROOT and TS_BASE. Traffic Server uses them
++# They are used to determine location of ATS components on the file
++# system.
++# According to DPM § 9.9 a program must not depend on the existance of
++# environment variables to work properly. Please report any errors if
++# you experience such a problem, for me it seems to work just fine with-
++# out
++ESED=/usr/bin/sed
++test -x $ESED || ESED=sed
++TS_PREFIX="/usr"
++TS_ROOT=${TS_ROOT:-$TS_PREFIX}
++
++# TS_BASE is offset inside the file system from where the layout starts
++# For standard installations TS_BASE will be empty
++eval TS_BASE="`echo $TS_ROOT | ${ESED} -e 's;/usr$;;'`"
++
++# Set some safe defaults. So not change values here, override them in
++# in /etc/default/trafficserver instead.
++# See there for a documentation as well 
++
++RUNDIR=${RUNDIR:-$TS_BASE/var/run/trafficserver}
++
++TM_START=${TM_START:-no}
++TM_NAME=${TM_NAME:-traffic_manager}
++TM_DAEMON=${TM_DAEMON:-$TS_BASE/usr/bin/traffic_manager}
++TM_DAEMON_ARGS=""
++TM_PIDFILE=${TM_PIDFILE:-$RUNDIR/manager.lock}
++
++TS_START=${TS_START:-no}
++TS_NAME=${TS_NAME:-traffic_server}
++TS_DAEMON=${TS_DAEMON:-$TS_BASE/usr/bin/traffic_server}
++TS_DAEMON_ARGS=""
++TS_PIDFILE=${TS_PIDFILE:-$RUNDIR/server.lock}
++
++# Exit if the package is not installed
++[ -x "$TM_DAEMON" ] || exit 0
++
++
++# Read configuration variable file if it is present
++[ -r /etc/default/$NAME ] && . /etc/default/$NAME
++
++# Load the VERBOSE setting and other rcS variables
++. /lib/init/vars.sh
++
++# Define LSB log_* functions.
++. /lib/lsb/init-functions
++
++# Check permissions of /etc/trafficserver.
++# Traffic Server needs write permissions, so warn the user if we suppose it
++# wouldn't.
++# The sysadmin is welcome to change the user ID that ATS uses. However to do that
++# in a clean and supported way, the administrator should overwrite the `stat override'
++# Debian  installs by default in Trafficserver's postinst maintainer script.
++# Print a warning only.
++# Since this can't be safely determined by this script print a warning only, but
++# don't fail.
++CONF_DIR='/etc/trafficserver'
++USER=$(dpkg-statoverride --list "$CONF_DIR" | awk '{print $1}')
++OWNER=$(env stat -c '%U' "$CONF_DIR")
++if [ -d "$CONF_DIR" ] && [ ! "x$OWNER" = "x$USER" ] ; then
++      log_warning_msg "Configuration directory '$CONF_DIR' is not owned by user '$USER'. " \
++              "However Traffic Server needs write permissions to it."
++fi
++
++
++# Make sure $RUNDIR exists as the underlying file system
++# may be volatile (see § 9.3.2 from DPM)
++install -d -o trafficserver -g trafficserver -m 0755 "$RUNDIR"
++
++
++# A helper function, its purpose is to start a daemon.
++# Arguments are interpreted in order as follows:
++# 1) The executable path
++# 2) A string containing optional daemon arguments
++# 3) A (valid) path containing the PID file for the daemon
++# Returns:
++#   0 if daemon has been started
++#   1 if daemon was already running
++#   2 if daemon could not be started
++start_cmd()
++{
++      # Args
++      DAEMON=$1
++      DAEMON_ARGS=$2
++      PID=$3
++
++      #echo "\n\n"
++      #echo "d:" $DAEMON
++      #echo "da:" $DAEMON_ARGS
++      #echo "pid:" $PID
++
++      start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \
++              || return 1
++      start-stop-daemon --start --quiet --background --pidfile $PID --exec $DAEMON -- \
++              $DAEMON_ARGS \
++              || return 2
++
++      return 0
++}
++
++
++# A helper function, its purpose is to stop a daemon.
++# Arguments are interpreted in order as follows:
++# 1) The daemon name (i.e. the binary name)
++# 2) The executable path
++# 3) A (valid) path containing the PID file for the daemon
++# Returns:
++#   0 if daemon has been stopped
++#   1 if daemon was already stopped
++#   2 if daemon could not be stopped
++#   Another value if a failure occurred
++stop_cmd()
++{
++      NAME=$1
++      DAEMON=$2
++      PID=$3
++
++      start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PID --name $NAME
++      RETVAL="$?"
++      [ "$RETVAL" = 2 ] && return 2
++
++      start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
++      [ "$?" = 2 ] && return 2
++
++      # Many daemons don't delete their pidfiles when they exit.
++      rm -f $PID
++      return "$RETVAL"
++}
++
++# The start function
++# This function does everything required to bring up the service
++# at boot time.
++# It does not accept any arguments
++do_start() {
++      if [ "x$TM_START" != "xno" ]; then
++                [ "$VERBOSE" != no ] && log_daemon_msg "Starting $TM_NAME"
++              start_cmd "$TM_DAEMON" "$TM_DAEMON_ARGS" "$TM_PIDFILE"
++              case "$?" in
++                      0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
++                        2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
++              esac
++      fi
++
++      if [ "x$TS_START" != "xno" ]; then
++                [ "$VERBOSE" != no ] && log_daemon_msg "Starting $TS_NAME"
++              start_cmd "$TS_DAEMON" "$TS_DAEMON_ARGS" "$TS_PIDFILE"
++              case "$?" in
++                      0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
++                        2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
++              esac
++      fi
++}
++
++
++# The stop function
++# This function does everything required to stop the service.
++# It does not accept any arguments
++do_stop() {
++        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $TM_NAME"
++      stop_cmd "$TM_NAME" "$TM_DAEMON" "$TM_PIDFILE"
++      case "$?" in
++              0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
++              2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
++      esac
++
++        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $TS_NAME"
++      stop_cmd "$TS_NAME" "$TS_DAEMON" "$TS_PIDFILE"
++      case "$?" in
++              0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
++              2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
++      esac
++}
++
++case "$1" in
++  start)
++      if [ "x$TM_START" = "xno" ] && [ "x$TS_START" = "xno" ]; then
++              [ "$VERBOSE" != no ] && log_warning_msg "Not starting $DESC"
++      else
++              do_start
++      fi
++      ;;
++  stop)
++      do_stop
++      ;;
++  status)
++       if [ "x$TM_START" != "xno" ] ; then
++               status_of_proc "$TM_DAEMON" "$TM_NAME" -p "$TM_PIDFILE" && exit 0 || exit $?
++       else
++               status_of_proc "$TS_DAEMON" "$TS_NAME" -p "$TS_PIDFILE" || exit $?
++       fi
++       ;;
++  restart|force-reload)
++      log_daemon_msg "Restarting $DESC" "$NAME\n"
++      do_stop
++      case "$?" in
++        0|1)
++              do_start
++              case "$?" in
++                      0) log_end_msg 0 ;;
++                      1) log_end_msg 1 ;; # Old process is still running
++                      *) log_end_msg 1 ;; # Failed to start
++              esac
++              ;;
++        *)
++              # Failed to stop
++              log_end_msg 1
++              ;;
++      esac
++      ;;
++  *)
++      echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
++      exit 3
++      ;;
++esac
++
++:
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa971f524741c16400bc55880eb6a22c1807b6cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++usr/bin/traffic_*
++usr/bin/tspush
++etc/trafficserver/*
++usr/lib/trafficserver/lib*.so.*
++usr/lib/trafficserver/modules/authproxy.so
++usr/lib/trafficserver/modules/background_fetch.so
++usr/lib/trafficserver/modules/cachekey.so
++usr/lib/trafficserver/modules/cache_promote.so
++usr/lib/trafficserver/modules/combo_handler.so
++usr/lib/trafficserver/modules/compress.so
++usr/lib/trafficserver/modules/conf_remap.so
++usr/lib/trafficserver/modules/escalate.so
++usr/lib/trafficserver/modules/esi.so
++usr/lib/trafficserver/modules/generator.so
++usr/lib/trafficserver/modules/header_rewrite.so
++usr/lib/trafficserver/modules/healthchecks.so
++usr/lib/trafficserver/modules/libloader.so
++usr/lib/trafficserver/modules/regex_remap.so
++usr/lib/trafficserver/modules/regex_revalidate.so
++usr/lib/trafficserver/modules/s3_auth.so
++usr/lib/trafficserver/modules/stats_over_http.so
++usr/lib/trafficserver/modules/tcpinfo.so
++usr/lib/trafficserver/modules/test_cppapi.so
++usr/lib/trafficserver/modules/tslua.so
++usr/lib/trafficserver/modules/xdebug.so
++usr/lib/perl5/* usr/share/perl5/
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51810a2e36dcf9a85492652ce9f0b716f58e6749
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++rm_conffile /etc/trafficserver/vaddrs.config 8.0.0~ trafficserver
++rm_conffile /etc/trafficserver/metrics.config 8.0.0~ trafficserver
++rm_conffile /etc/trafficserver/logging.config 8.0.0~ trafficserver
++rm_conffile /etc/trafficserver/log_hosts.config 8.0.0~ trafficserver
++rm_conffile /etc/trafficserver/congestion.config 8.0.0~ trafficserver
++rm_conffile /etc/trafficserver/cluster.config 8.0.0~ trafficserver
++rm_conffile /etc/trafficserver/body_factory/default/congestion#retryAfter 8.0.0~ trafficserver
++rm_conffile /etc/trafficserver/icp.config 8.0.0~ trafficserver
++rm_conffile /etc/trafficserver/snapshosts 8.0.0~ trafficserver
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b2bfc3aae69551474ed7bde33b41eb69431474a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++debian/tmp/usr/share/man/man1/traffic_*
++debian/tmp/usr/share/man/man1/tspush.1
++debian/tmp/usr/share/man/man8/traffic_*
++debian/tmp/usr/share/man/man5/*
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..23764905e64ac2e15f54f57be1aaf56b75ba84d4
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,127 @@@
++#! /bin/sh
++# postinst script for trafficserver
++#
++# see: dh_installdeb(1)
++#
++#  Copyright 2011 Arno Toell <debian@toell.net>
++#
++#   Licensed under the Apache License, Version 2.0 (the "License");
++#   you may not use this file except in compliance with the License.
++#   You may obtain a copy of the License at
++#
++#       http://www.apache.org/licenses/LICENSE-2.0
++#
++#   Unless required by applicable law or agreed to in writing, software
++#   distributed under the License is distributed on an "AS IS" BASIS,
++#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++#   See the License for the specific language governing permissions and
++#   limitations under the License.
++
++
++set -e
++
++USER='trafficserver'
++GROUP='trafficserver'
++USER_HOME='/var/run/trafficserver'
++OWNER=$(env stat -c '%U' /etc/trafficserver)
++OWNER_CACHE_DIR=$(env stat -c '%U' /var/cache/trafficserver)
++
++# summary of how this script can be called:
++#        * <postinst> `configure' <most-recently-configured-version>
++#        * <old-postinst> `abort-upgrade' <new version>
++#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
++#          <new-version>
++#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
++#          <failed-install-package> <version> `removing'
++#          <conflicting-package> <version>
++# for details, see /usr/share/doc/packaging-manual/
++#
++# quoting from the policy:
++#     Any necessary prompting should almost always be confined to the
++#     post-installation script, and should be protected with a conditional
++#     so that unnecessary prompting doesn't happen if a package's
++#     installation fails and the `postinst' is called with `abort-upgrade',
++#     `abort-remove' or `abort-deconfigure'.
++
++case "$1" in
++configure)
++
++      if ! getent passwd -- "$USER" >/dev/null 2>&1 ; then
++      adduser --home "$USER_HOME" \
++              --group \
++              --system \
++              --disabled-password \
++              --no-create-home \
++              --gecos "Debian Traffic Server user" \
++              $USER
++      fi
++
++      if [ -d /etc/trafficserver ] && [ "x$OWNER" = "xroot" ] ; then
++              # Ok, I admit I am lazy. I don't check every permission
++              # the user may have changed. If he didn't for /etc I can
++              # safely assume he neither has for other directories (I 
++              # hope, since /etc requires write permissions by ATS). 
++              echo 'Fixing permissions ...'
++
++              if ! dpkg-statoverride --list /etc/trafficserver >/dev/null 2>&1; then
++                  dpkg-statoverride --update --add "$USER" "$GROUP" 0755 /etc/trafficserver
++              fi
++
++              if ! dpkg-statoverride --list /var/log/trafficserver >/dev/null 2>&1; then
++                      dpkg-statoverride --update --add "$USER" adm 0750 /var/log/trafficserver
++              fi
++
++              if [ -d /var/cache/trafficserver ] && [ "x$OWNER_CACHE_DIR" = "xroot" ] ; then
++                      if ! dpkg-statoverride --list /var/cache/trafficserver  >/dev/null 2>&1; then
++                              dpkg-statoverride --update --add "$USER" adm 0750 /var/cache/trafficserver
++                      fi
++              fi
++      fi
++
++        if [ -n "$2" ] && dpkg --compare-versions "$2" 'le' '3.2~' ; then
++                RET=0
++                invoke-rc.d trafficserver status > /dev/null 2>&1 || RET=$?
++                # 0 => ATS is running
++                # 4 => Status is unknown
++                # 1,2,3 => ATS is not running
++
++                # using /bin/echo to make sure -e is supported
++                ECHO=`which echo`
++                if [ "$RET" -gt 0 ] && [ "$RET" -ne 4 ] && [ -f /var/cache/trafficserver/host.db ] ; then
++                        echo "Purging TrafficServer cache upon upgrade."
++                        RET=0
++                        traffic_server -Cclear > /dev/null 2>&1 || RET=$?
++                        if [ "$RET" -ne 0 ] ; then
++                                $ECHO "======================================================================="
++                                $ECHO -e "WARNING: Apache TrafficServer's cache couldn't be purged during the upgrade.\n" \
++                                        "Please inspect the situation manually and call 'traffic_server -Cclear'\n" \
++                                        "afterwards to purge the caches."
++                                $ECHO "======================================================================="
++                        fi
++                else
++                    $ECHO "======================================================================="
++                    $ECHO -e "WARNING: Apache TrafficServer is not running or its state couldn't be\n" \
++                        "determined. Please inspect the situation manually and call\n" \
++                        "'traffic_server -Cclear' afterwards to purge the caches.\n"
++                    $ECHO "======================================================================="
++
++               fi
++        fi
++;;
++
++abort-upgrade|abort-remove|abort-deconfigure)
++
++;;
++
++*)
++echo "postinst called with unknown argument \`$1'" >&2
++exit 0
++;;
++esac
++
++# dh_installdeb will replace this with shell code automatically
++# generated by other debhelper scripts.
++
++#DEBHELPER#
++
++exit 0
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3fb0f534082b497a8c47d21b949e9511e00f80c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++#
++#  Licensed to the Apache Software Foundation (ASF) under one
++#  or more contributor license agreements.  See the NOTICE file
++#  distributed with this work for additional information
++#  regarding copyright ownership.  The ASF licenses this file
++#  to you under the Apache License, Version 2.0 (the
++#  "License"); you may not use this file except in compliance
++#  with the License.  You may obtain a copy of the License at
++#
++#      http://www.apache.org/licenses/LICENSE-2.0
++#
++#  Unless required by applicable law or agreed to in writing, software
++#  distributed under the License is distributed on an "AS IS" BASIS,
++#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++#  See the License for the specific language governing permissions and
++#  limitations under the License.
++#
++[Unit]
++Description=Apache Traffic Server is a fast, scalable and extensible caching proxy server.
++Documentation=man:traffic_server(8)
++After=network-online.target
++
++[Service]
++Type=simple
++EnvironmentFile=-/etc/default/trafficserver
++PIDFile=/run/trafficserver/manager.lock
++ExecStart=/usr/bin/traffic_manager $TM_DAEMON_ARGS
++ExecReload=/usr/bin/traffic_ctl config reload
++
++[Install]
++WantedBy=multi-user.target
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..231e3171b33e4a1e3b92d583618c0b8ab4e023d9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++d /run/trafficserver 0755 trafficserver trafficserver
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d485de24fa392a4fdad41a8241df4b453861ba97
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,686 @@@
++-----BEGIN PGP PUBLIC KEY BLOCK-----
++
++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
++=VvDM
++-----END PGP PUBLIC KEY BLOCK-----
++-----BEGIN PGP PUBLIC KEY BLOCK-----
++
++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
++yMLkPeLfh5LLCwL80fgxSp7Z+UB1
++=MC/H
++-----END PGP PUBLIC KEY BLOCK-----
++-----BEGIN PGP PUBLIC KEY BLOCK-----
++
++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
++=3xB/
++-----END PGP PUBLIC KEY BLOCK-----
++-----BEGIN PGP PUBLIC KEY BLOCK-----
++
++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
++DOI2xbN2Mhjbj0mkwaQ=
++=CIzB
++-----END PGP PUBLIC KEY BLOCK-----
++-----BEGIN PGP PUBLIC KEY BLOCK-----
++
++mQINBEtfajQBEADAmWqIJkt2MPjmZJ9RlymAAuOcvImB5E3cvpWFqVH8h5ycS3NA
++TTcvomSnILsiq+NM0h/J3Bd3cchCNA0uuXW1CIDY7mGrkPdXNxQVygYY2L/ubAE8
++Ed1u1ghzPUaMYtFf7QAk4WCpglBaL2frJmfS93AIYOAIuV0zw8+ce8uvVYSt7aAk
++Ww8JKnpW91nMZr3GBTibhthCmLcDWIyntR2gYyPX9NdAAViSfkmW8ea4dtbXfu8j
++nkr7DnbsQu+vl29nwLmI3h2jcgV6QkHu40breY5NbFSTVmyyBgM5yH3cVk7xVIGv
++1rt0Q9DvoftaSiWn3TcXcrnHnWVPTo1VM+kYHT9J9p6JSxZdrcAySR48YZvgDXTI
++BC2sNCtW/gHC4iNdxWvOYKDO7xv9fLweqiUNXi/BxcwcCapYN5xZEONFKVVRvd5X
++pcr0hxviNjkD/GkWTYEt3EqhnAGDOwQc60DtyuY1w82bhtIhCmNcnLAUEXYlJMXN
++iPy1owKuY0EBt3VO3KsI5RqZTGgvYHtB2XpnyRLutnwAsMQceUl3r3CMRoNagTGw
+++gfY18819b5dgMSYp1+Im0TtImMv/LOPjZY44Br24JisScaqQXc+DETEfafoIKsW
++BBj1gfz5GsuUxdlnZHwwIM4MwtWvoU8/OHp3zVlDJZFLNBReTimV1La0DQARAQAB
++tCBCcnlhbiBXLiBDYWxsIDxiY2FsbEBhcGFjaGUub3JnPokCOAQTAQIAIgUCS19q
++NAIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQTRVBELhFCOyuyxAArYgT
++qrMxveqgKEEUrdnYF+hw1weoA5I4vJPUoAdsUr52WUZ2P8AnKxsESdGefA0XwG2v
++t6qyo0fFce04YZ5gYjg5au0sYndN84vwysjuo5PKBR4mt3Ij097gMnotRzFDReJD
++TB5Yk4oj0/jHMUkkS2dw3gM2DFfI2ILAaCRNbFapejwk4jCxt5JAGVEr+z8BEyOg
++NcwL0X5G1qci+OR9cv0AzfChxaKNwW/oGpW/h5gM4jBQuElMmCxqDcyAlIHLaGoM
++H73iGnD9U4bI05kvpIdqABQrfDoN1QyQkiqG640LnP4WVHycNDIyOeeQcDVenK5a
++IOm/4gnErOyzQkPS0v5J4jD4df2lvxXnoSvjLySCeA1um/HV/gF02NufyoMHDJFD
++QeEIjQu6KLYPNOT3dMeuioqtIsncylmAXiSqnwrP7zfp2RWRaWzAgP1SSZMzF/80
++a+6W5uQt8j2ok/z0NPCKQflS+oU4Gc5C+/KHUrdDCY5GcwwgFBv1i41jXdjh0nkz
++VzX/asUIO9ptW8TR7aIcS3lm847YV4isNvWlroeGhmVfIk8M8RqsDfq7HPLvqipI
++V2phFiUEiON8OgD0x8+2fTnveE/lY1jpjYUg73XOcHG6wna4D6ECTzlG3s4QtBrI
++gcUFY97nfHgt6sT/ix7jB746wVfKgRzRyCuCwxK5Ag0ES19qNAEQAL3rbob4n3jy
++cUhDIqg7bzReb4rfoS5JWKVCu169q8aGys2HJ4tCKtPE2ldwHKo55nTZsQQTbaLM
++WxznmGWxESV4OJKDDNXOhctISaiwOSNEj58QvDGmcmK38ZlrSsuoUNgpbnFfxlwx
++DBu0Nh5ocKiNJcBx5BwhrD9hzqaSW/HjQU52EUrLfT5gYnT25ZtmTIEzonbT1AGb
++vMWPQeNVHwUZmy7foFIp68Hw4Z54SWWB6wOs3sPg5PfptfrTe2TTCe1CfcUCCzAJ
++IEeNK8u2tYwK1u02pmYJ2nXxCo0op0bP4Bo40USbqI40qf37WAjGYIik31upNOU7
++Ku6vYopQMV8kB7i5HBuYzTvxjCz+dC8P2TuALwmMCpOdBQ5c+lT4gRr5kt0OlD1k
++IpL/vrlTFsmhILe5KbawfM2ZwM6KbJyX0er38XNK4HPpHiyaNrLS2bQSDgBU8PC5
++sOHKpOXq404owDybkOfiB0voCJm/e7RkOI6AyeokCobXzcO6ZrWmwiRwXz9K5EuK
++IfA8gWs5i3pK3X9cBMnjQ/uMSL0w7SY5khUAMFwECM73YW53E+hqaB7LG26ATjql
++esM3uhaVd3INNNp+haoHSsRUY7DZh2s/ghX5t/T/pkRzfNimVdejYOUxAHHX4x8S
++fcXsB1I9r81wolbUqaddHdZLYS4Z7ZBNABEBAAGJAh8EGAECAAkFAktfajQCGwwA
++CgkQTRVBELhFCOyNpw//fF6pKllSPv9yvNAMvt+Ly0hk6GwOCRYCMe9uQuk7EDTI
++idiG6DbzEKsqGIugvcyMrOGJxGDfSc+I8KgOhsuY31aWllSLyMo2fVFPECztPPFt
++7IovwXe1sSA5d278KG2Xi+2Z0W0XMnCqVLLoQoFqkVvbgaLKWrvgk93yozsYYcJd
++2iDIseyL3YzuqSNeLoXf0DWlMDX8oXtemlD87oWJWOnfHTVMLmGM45qAvmcDf96b
++ZiDjdo4Sn6LSu5Xn/fCNrZOtpTDXJPJ9fb9APz4n2tdTS4UrzCQdtYEaNxb5LeP7
++MySMOITpZ2xrmC3SzL67STXn4POTxXa8lnY1DCpGUL8uxYej8cXkPDSC9sX836vJ
++DbalYPHlgb5Fyn9pb+LoZWhUKxtfp6dp8N2kVKJ1yLlTwVdRFEicF5hqiMa6Curk
++TVIVb9VQxEzw9bnBmoi9k4XLJAHz35cFvLNjJVSt0naNqjpy6fxHllJ6I1s6dJe9
++jGfECEc1sw1vLjS4f+NIGTSjciuxWkOfZ3Ulw3RPuUbeMHMC6FEfv35M1dDRv3ec
++xIBKO0t7qvJ8Q6b/DzwW+IhqzLTK5bXEXVKS1zAJ3Iyd1QOmGOLdP/caoKV313J5
++YVz+bqtclFVJQ20cqAPhWEf7UNZTFCwa1CAKbPIGKYoDSCId4sqsNyRCmbRqY8A=
++=Ycol
++-----END PGP PUBLIC KEY BLOCK-----
diff --cc debian/watch
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b39b7a76bc2daf0b5ee4f60d2933325a7f5b495d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++version=4
++opts="pgpsigurlmangle=s/$/.asc/,dversionmangle=s/\+ds\d*$//,repacksuffix=+ds,repack,compression=xz" \
++   https://www.apache.org/dist/trafficserver \
++   trafficserver-(\d+\.\d+.\d+)\.tar\.bz2 \
++   debian uupdate