You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@apache.org on 2016/12/23 05:19:21 UTC

svn commit: r1775787 [1/2] - in /httpd/httpd/branches/2.2.x: ./ docs/manual/mod/ include/ modules/http/ server/

Author: wrowe
Date: Fri Dec 23 05:19:21 2016
New Revision: 1775787

URL: http://svn.apache.org/viewvc?rev=1775787&view=rev
Log:
Resigning my first attempt to get patches through the 2.2.x process, and
revoking my ratification of a list of patches (e.g. -1 as had been applied,
including my own submissions - I will revert in any case, where misordered.)

Proposing that we start with the same branch model as used on 2.4.x to get
through too many many-year-old patches to idly browse through; replay these
in mostly-sequential order, and bring 2.2.x up to 2.4.x in the affected areas
of code, and finally this proposal suggests the same merge as was applied to
2.4.25 GA release, modulo all our new crazy APLOGNO fun.

There is not much to see here, other than to compare rev no's of what had
been applied/proposed reverts to the list of patches on the 2.2.x merge
branch... the interesting data is on that merge branch. But extensive testing
against the resulting code is critical to our hope of offering a last 2.2.x
release to close that chapter. TIA to each and everyone who assists!



Modified:
    httpd/httpd/branches/2.2.x/   (props changed)
    httpd/httpd/branches/2.2.x/CHANGES
    httpd/httpd/branches/2.2.x/STATUS
    httpd/httpd/branches/2.2.x/docs/manual/mod/core.xml
    httpd/httpd/branches/2.2.x/include/ap_mmn.h
    httpd/httpd/branches/2.2.x/include/http_core.h
    httpd/httpd/branches/2.2.x/include/http_protocol.h
    httpd/httpd/branches/2.2.x/include/httpd.h
    httpd/httpd/branches/2.2.x/modules/http/http_filters.c
    httpd/httpd/branches/2.2.x/server/core.c
    httpd/httpd/branches/2.2.x/server/gen_test_char.c
    httpd/httpd/branches/2.2.x/server/protocol.c
    httpd/httpd/branches/2.2.x/server/util.c
    httpd/httpd/branches/2.2.x/server/vhost.c

Propchange: httpd/httpd/branches/2.2.x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Dec 23 05:19:21 2016
@@ -1,2 +1,4 @@
+/httpd/httpd/branches/2.2.x-merge-http-strict:1775686-1775717,1775749-1775780
 /httpd/httpd/branches/2.4.x:1555538,1555559,1648845,1649003,1681034,1682929,1682939,1707123,1722573,1726087
-/httpd/httpd/trunk:290940,395552,417988,451572,501364,583817,583830,611483,630858,639005,639010,647395,657354,657459,660461,660566,664330,678761,680082,681190,682369,683626,685112,686805,686809,687099,687754,693120,693392,693727-693728,696006,697093,706318,707163,708902,711421,713575,719357,720250,729316-729317,729586,732414,732504,732816,732832,733127,733134,733218-733219,734710,743589,755190,756671,756675,756678,756683,757741,761329,763394,764239,768535,769809,771587,771610,776325,777042,777091,778438-778439,778531,778942,780648,780655,780692,780697,780699,785457,785661,790587,803704,819480,823536,823563,834378,835046,891282,892678,892808,900022,932791,942209,952823,953311,955966,979120,981084,992625,1026743,1031551,1040304,1040373,1058192,1070096,1082189,1082196,1090645,1100511,1102124,1172732,1200040,1200372,1200374,1213380,1213391,1222335,1223048,1231446,1244211,1294306,1299738,1300171,1301111,1308862,1327036,1327080,1328133,1328325-1328326,1345319,1348656,1349905,1352912,13631
 83,1363186,1366344,1367778,1368131,1368396,1369568,1395225,1398066,1400700,1407004,1407088,1407528,1408402,1410681,1413732,1414094,1416889,1418752,1422234,1422253,1435178,1447426,1470940,1475878,1476604,1476621,1476642,1476644-1476645,1477530,1484852,1485409,1485668,1490994,1493330,1496429,1500323,1504276,1506714,1509872,1509875,1514215,1524192,1524770,1526168,1526189,1527291,1527295,1527925,1528718,1529559,1529988,1529991,1531505,1532816,1551685,1551714,1552227,1553204,1554276,1554281,1555240,1555555,1556428,1563420,1572092,1572198,1572543,1572611,1572630,1572655,1572663,1572668-1572671,1572896,1572911,1572967,1573224,1573229,1575400,1585090,1586745,1587594,1587639,1588851,1590509,1597352,1603156,1604353,1610207,1610311,1610491,1610501,1611165,1611169,1620932,1621419,1621453,1643537,1643543,1648840,1649001,1649043,1650310,1650320,1652929,1653997,1657897,1658765,1663647,1664205,1665215,1665218,1665625,1665721,1666363,1674056,1675533,1676654,1677462,1679182,1679470,1680895,1680900,16
 80942,1681037,1682923,1682937,1684513,1684900,1685345,1685347,1685349-1685350,1687539,1687680,1688274,1688331,1688339-1688340,1688343,1688536,1688538,1697013,1697015,1706989,1710095,1722572,1723567,1726086,1727544,1745767,1748448,1753228
+/httpd/httpd/branches/2.4.x-merge-http-strict:1767913-1775776
+/httpd/httpd/trunk:290940,395552,417988,451572,501364,583817,583830,611483,630858,639005,639010,647395,657354,657459,660461,660566,664330,678761,680082,681190,682369,683626,685112,686805,686809,687099,687754,693120,693392,693727-693728,696006,697093,706318,707163,708902,711421,713575,719357,720250,729316-729317,729586,732414,732504,732816,732832,733127,733134,733218-733219,734710,743589,755190,756671,756675,756678,756683,757741,761329,763394,764239,768535,769809,771587,771610,776325,777042,777091,778438-778439,778531,778942,780648,780655,780692,780697,780699,785457,785661,790587,803704,819480,823536,823563,834378,835046,891282,892678,892808,900022,932791,942209,952823,953311,955966,979120,981084,992625,1026743,1031551,1040304,1040373,1057372,1058192,1070096,1082189,1082196,1090645,1100511,1172732,1178566,1185385,1188745,1200040,1200372,1200374,1213380,1213391,1222335,1223048,1231446,1237407,1244211,1294306,1299738,1300171,1301111,1308862,1327036,1327080,1328133,1328325-1328326,13453
 19,1348656,1349905,1352911-1352912,1363183,1363186,1366344,1367778,1368131,1368396,1369568,1392347,1395225,1398066,1400700,1406719,1407004,1407088,1407528,1407599,1407643,1408402,1410681,1413732,1414094,1416889,1418752,1422234,1422253,1425366,1426827,1426877,1426879,1426988,1426992,1428145,1433613,1435178,1436457,1446421,1447426,1470940,1475878,1476604,1476621,1476642,1476644-1476645,1477530,1483005,1484852,1485409,1485668,1490994,1493330,1496429,1500323,1504276,1506714,1509872,1509875,1514215,1524192,1524770,1526168,1526189,1527291,1527295,1527925,1528718,1529559,1529988,1529991,1531505,1532816,1551685,1551714,1552227,1553204,1554276,1554281,1555240,1555555,1556428,1563420,1572092,1572198,1572543,1572611,1572630,1572655,1572663,1572668-1572671,1572896,1572911,1572967,1573224,1573229,1575400,1585090,1586745,1587594,1587639,1588851,1590509,1597352,1603156,1604353,1610207,1610311,1610383,1610491,1610501,1611165,1611169,1620932,1621453,1635762,1643537,1643543,1648840,1649001,1649043,16
 50310,1650320,1652929,1653997,1657897,1658765,1663647,1664205,1664576,1665215,1665218,1665625,1665721,1666363,1674056,1675533,1676654,1677462,1679182,1679470,1680895,1680900,1680942,1681037,1682923,1682937,1683123,1684513,1684900,1685345,1685347,1685349-1685350,1687539,1687642-1687643,1687680,1688274,1688331,1688339-1688340,1688343,1688536,1688538,1697013,1697015,1706989,1710095,1722572,1723567,1726086,1727544,1745767,1748448,1753228,1754536,1754538-1754541,1754544,1754547-1754548,1754555-1754556,1754568-1754570,1754577,1754579,1755123-1755126,1755233-1755236,1755263-1755264,1755343,1755744,1756540,1756555,1756649,1756729,1756821,1756823-1756824,1756847,1756849,1756862,1756934,1756937,1756946,1756959,1756978,1757062,1757065,1757589,1757593,1757711,1757920-1757921,1757924,1758226,1758263,1758265-1758266,1758304-1758305,1758313,1760444,1764961,1765112-1765115,1765451,1769965,1770786,1770817,1770867,1770869,1771690,1772418,1773159,1773162,1773293,1773346,1773761,1773779,1773812,1773861
 -1773862,1773865,1774286

Modified: httpd/httpd/branches/2.2.x/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/CHANGES?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/CHANGES [utf-8] (original)
+++ httpd/httpd/branches/2.2.x/CHANGES [utf-8] Fri Dec 23 05:19:21 2016
@@ -1,17 +1,46 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.2.32
 
-  *) core: CVE-2016-5387: Mitigate [f]cgi "httpoxy" issues.
+  *) SECURITY: CVE-2016-8743 (cve.mitre.org)
+     Enforce HTTP request grammar corresponding to RFC7230 for request lines
+     and request headers, to prevent response splitting and cache pollution by
+     malicious clients or downstream proxies. [William Rowe, Stefan Fritsch]
+
+  *) CVE-2016-5387: core: Mitigate [f]cgi "httpoxy" issues.
      [Dominic Scheirlinck <dominic vendhq.com>, Yann Ylavic]
 
+  *) Validate HTTP response header grammar defined by RFC7230, resulting
+     in a 500 error in the event that invalid response header contents are
+     detected when serving the response, to avoid response splitting and cache
+     pollution by malicious clients, upstream servers or faulty modules.
+     [Stefan Fritsch, Eric Covener, Yann Ylavic]
+
+  *) core: Enforce LimitRequestFieldSize after multiple headers with the same
+     name have been merged. [Stefan Fritsch]
+
+  *) core: Drop Content-Length header and message-body from HTTP 204 responses.
+     PR 51350 [Luca Toscano]
+
+  *) core: Permit unencoded ';' characters to appear in proxy requests and
+     Location: response headers. Corresponds to modern browser behavior.
+     [William Rowe]
+
+  *) core: ap_rgetline_core now pulls from r->proto_input_filters.
+
+  *) core: Correctly parse an IPv6 literal host specification in an absolute
+     URL in the request line. [Stefan Fritsch]
+
+  *) core: New directive RegisterHttpMethod for registering non-standard
+     HTTP methods. [Stefan Fritsch]
+
   *) core: Limit to ten the number of tolerated empty lines between request.
      [Yann Ylavic]
 
-  *) Core: reject NULLs in request line or request headers.
+  *) core: reject NULLs in request line or request headers.
      PR 43039 [Nick Kew]
 
-  *) mod_ssl: Fix a possible memory leak on restart for custom [EC]DH params.
-     [Jan Kaluza, Yann Ylavic]
+  *) core: Avoid a possible truncation of the faulty header included in the
+     HTML response when LimitRequestFieldSize is reached.  [Yann Ylavic]
 
   *) mod_proxy: Fix a regression with 2.2.31 that caused inherited workers to
      use a different scoreboard slot then the original one.  PR 58267.
@@ -42,6 +71,12 @@ Changes with Apache 2.2.32
      failures under Visual Studio 2015 and other mismatched MSVCRT flavors.
      PR59630 [Jan Ehrhardt <phpdev ehrhardt.nl>]
 
+  *) mod_ssl: Fix a possible memory leak on restart for custom [EC]DH params.
+     [Jan Kaluza, Yann Ylavic]
+
+  *) core: Support custom ErrorDocuments for HTTP 501 and 414 status codes.
+     PR 57167 [Edward Lu <Chaosed0 gmail.com>]
+
 Changes with Apache 2.2.31
 
   *) Correct win32 build issues for mod_proxy exports, OpenSSL 1.0.x headers.

Modified: httpd/httpd/branches/2.2.x/STATUS
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/STATUS?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/STATUS (original)
+++ httpd/httpd/branches/2.2.x/STATUS Fri Dec 23 05:19:21 2016
@@ -99,6 +99,32 @@ CURRENT RELEASE NOTES:
 
 RELEASE SHOWSTOPPERS:
 
+  *) Rather than odds-and-ends applied out of order, proposing we revert
+     r1757240, r1757256, r1757295, r1758671, r1758672, r1775232, all of
+     which is now recorded in the 2.2.x-merge-http-strict branch, and
+     bring that branch back into 2.2.x for 2.4.32 release.
+     Merges;
+       -c-1775232 .
+       -c-1757672 .
+       -c-1757671 .
+       -c-1757295 .
+       -c-1757256 .
+       -c-1757240 .
+     [here we are back at 2.2.32-dev bump]
+       -r1775685:1775780 https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x-merge-http-strict/
+     Roll-up patch of the above (not recommended for casual reading, these
+     would be committed individually as noted above... but for only for sanity
+     testing the end result. Due to intervening CHANGES/ap_mmn changes, there
+     is small delta after reverting the above...)
+       https://raw.githubusercontent.com/wrowe/patches/master/httpd-2.2-HEAD-http-protocol-strict.patch
+       This patch above does *NOT* apply to the 2.2.31 release, c.f. the delta
+       of the 2.2.x-merge-http-strict branch for that information. This is for
+       folks who are testing rollbacks plus 2.4.x activity against 2.2.x HEAD!
+       Sorry to start from scratch, but yann's correct observation was correct,
+       that nothing will apply out-of-order, and everything on 2.2 branch had
+       already become disordered.
+     +1: wrowe
+
 
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
@@ -152,44 +178,6 @@ PATCHES PROPOSED TO BACKPORT FROM TRUNK:
          http://home.apache.org/~ylavic/patches/httpd-2.2.x-r1753592.patch
       +1: ylavic
 
-  *) Enforce LimitRequestFieldSize after multiple headers with the same
-     name have been merged, Ensure LimitRequestFieldSize is always logged.
-     Downgrade some more log messages indicating client errors from level error
-     to info. Add log messages for various reasons to return HTTP_BAD_REQUEST.
-     Correctly return a 400 (Bad request) in case of a HTTP/0.9 request like
-     "GET @example.org/foo".
-     Add some trace logging to core (using AP_DEBUG_THE_REQUEST macro, because
-     the TRACE5 facilities aren't in 2.2.x branch).
-     Improve error message (PR 54384).
-     Submitted by: sf, rpluem, jailletc36
-     [Note: everything in this patch is modifying logging and brings in the
-     LimitRequestFieldSize logic used for the lifespan of 2.4.x]
-     Trunk version of patch
-         http://svn.apache.org/r951900 (server/protocol.c alone)
-         http://svn.apache.org/r1178566
-         http://svn.apache.org/r1185385
-         http://svn.apache.org/r1188745
-         http://svn.apache.org/r1352911
-         http://svn.apache.org/r1433613
-     Backport: (Adjustments dodging 2.4'isms such as APLOGNO's)
-         https://raw.githubusercontent.com/wrowe/patches/master/backport-2.2.x-r951900-r1178566-r1185385-r1188745-r1352911-r1433613.patch 
-     +1: wrowe, covener
-     ylavic: the patch does not apply cleanly? (I tried both w/ and w/o
-             backport-2.2.x-r892678.patch first, conflicts in protocol.c)
-
-  *) core: ErrorDocument now works for requests without a Host header.
-     Support custom ErrorDocuments for HTTP 501 and 414 status codes.
-     PR: 48357, 57167
-     Submitted by: trawick,  [Edward Lu <Chaosed0 gmail.com>]
-     Trunk version of patch
-         http://svn.apache.org/r1392347
-         http://svn.apache.org/r1635762
-     Backport:
-         https://raw.githubusercontent.com/wrowe/patches/master/backport-2.2.x-r1392347-r1635762.patch 
-     +1: wrowe, covener
-     ylavic: same here (hunk + access_status = HTTP_BAD_REQUEST;)
-             depends on the previous one?
-
   *) core: potential rejection of valid MaxMemFree and ThreadStackSize directives
      trunk patch: https://svn.apache.org/r1542338
      2.4.x patch: https://svn.apache.org/r1542549

Modified: httpd/httpd/branches/2.2.x/docs/manual/mod/core.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/docs/manual/mod/core.xml?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/docs/manual/mod/core.xml (original)
+++ httpd/httpd/branches/2.2.x/docs/manual/mod/core.xml Fri Dec 23 05:19:21 2016
@@ -1440,6 +1440,82 @@ MIME content-type</description>
 </usage>
 </directivesynopsis>
 
+<directivesynopsis>
+<name>HttpProtocolOptions</name>
+<description>Modify restrictions on HTTP Request Messages</description>
+<syntax>HttpProtocolOptions [Strict|Unsafe] [RegisteredMethods|LenientMethods]
+ [Allow0.9|Require1.0]</syntax>
+<default>HttpProtocolOptions Strict LenientMethods Allow0.9</default>
+<contextlist><context>server config</context>
+<context>virtual host</context></contextlist>
+<compatibility>2.2.32 or 2.4.24 and later</compatibility>
+
+<usage>
+    <p>This directive changes the rules applied to the HTTP Request Line
+    (<a href="https://tools.ietf.org/html/rfc7230#section-3.1.1"
+      >RFC 7230 &sect;3.1.1</a>) and the HTTP Request Header Fields
+    (<a href="https://tools.ietf.org/html/rfc7230#section-3.2"
+      >RFC 7230 &sect;3.2</a>), which are now applied by default or using
+    the <code>Strict</code> option. Due to legacy modules, applications or
+    custom user-agents which must be deperecated the <code>Unsafe</code>
+    option has been added to revert to the legacy behaviors. These rules
+    are applied prior to request processing, so must be configured at the
+    global or default (first) matching virtual host section, by IP/port
+    interface (and not by name) to be honored.</p>
+
+    <p>Prior to the introduction of this directive, the Apache HTTP Server
+    request message parsers were tolerant of a number of forms of input
+    which did not conform to the protocol.
+    <a href="https://tools.ietf.org/html/rfc7230#section-9.4"
+      >RFC 7230 &sect;9.4 Request Splitting</a> and
+    <a href="https://tools.ietf.org/html/rfc7230#section-9.5"
+      >&sect;9.5 Response Smuggling</a> call out only two of the potential
+    risks of accepting non-conformant request messages, while
+    <a href="https://tools.ietf.org/html/rfc7230#section-3.5"
+         >RFC 7230 &sect;3.5</a> "Message Parsing Robustness" identify the
+    risks of accepting obscure whitespace and request message formatting. 
+    As of the introduction of this directive, all grammer rules of the
+    specification are enforced in the default <code>Strict</code> operating
+    mode, and the strict whitespace suggested by section 3.5 is enforced
+    and cannot be relaxed.</p>
+
+    <p>Users are strongly cautioned against toggling the <code>Unsafe</code>
+    mode of operation, particularly on outward-facing, publicly accessible
+    server deployments.  If an interface is required for faulty monitoring
+    or other custom service consumers running on an intranet, users should
+    toggle the Unsafe option only on a specific virtual host configured
+    to service their internal private network.</p>
+
+    <p>Reviewing the messages logged to the <directive>ErrorLog</directive>,
+    configured with <directive>LogLevel</directive> <code>debug</code> level,
+    can help identify such faulty requests along with their origin.
+    Users should pay particular attention to the 400 responses in the access
+    log for invalid requests which were unexpectedly rejected.</p>
+
+    <p><a href="https://tools.ietf.org/html/rfc7231#section-4.1"
+         >RFC 7231 &sect;4.1</a> "Request Methods" "Overview" requires that
+    origin servers shall respond with an error when an unsupported method
+    is encountered in the request line. This already happens when the
+    <code>LenientMethods</code> option is used, but administrators may wish
+    to toggle the <code>RegisteredMethods</code> option and register any
+    non-standard methods using the <directive>RegisterHttpMethod</directive>
+    directive, particularly if the <code>Unsafe</code> option has been toggled.
+    The <code>RegisteredMethods</code> option should <strong>not</strong>
+    be toggled for forward proxy hosts, as the methods supported by the
+    origin servers are unknown to the proxy server.</p>
+
+    <p><a href="https://tools.ietf.org/html/rfc2616#section-19.6"
+         >RFC 2616 &sect;19.6</a> "Compatibility With Previous Versions" had
+    encouraged HTTP servers to support legacy HTTP/0.9 requests. RFC 7230
+    superceeds this with "The expectation to support HTTP/0.9 requests has
+    been removed" and offers additional comments in 
+    <a href="https://tools.ietf.org/html/rfc7230#appendix-A"
+      >RFC 7230 Appendix A</a>. The <code>Require1.0</code> option allows
+    the user to remove support of the default <code>Allow0.9</code> option's
+    behavior.</p>
+</usage>
+</directivesynopsis>
+
 <directivesynopsis type="section">
 <name>IfDefine</name>
 <description>Encloses directives that will be processed only
@@ -3681,5 +3757,19 @@ hostname or IP address</description>
 </usage>
 </directivesynopsis>
 
+<directivesynopsis>
+<name>RegisterHttpMethod</name>
+<description>Register non-standard HTTP methods</description>
+<syntax>RegisterHttpMethod <var>method</var> [<var>method</var> [...]]</syntax>
+<contextlist><context>server config</context></contextlist>
+<usage>
+<p>HTTP Methods that are not conforming to the relvant RFCs are normally
+rejected by request processing in Apache HTTPD. To avoid this, modules
+can register non-standard HTTP methods they support.
+The <directive>RegisterHttpMethod</directive> allows to register such
+methods manually. This can be useful for if such methods are forwared
+for external processing, e.g. to a CGI script.</p>
+</usage>
+</directivesynopsis>
 
 </modulesynopsis>

Modified: httpd/httpd/branches/2.2.x/include/ap_mmn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/include/ap_mmn.h?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/include/ap_mmn.h (original)
+++ httpd/httpd/branches/2.2.x/include/ap_mmn.h Fri Dec 23 05:19:21 2016
@@ -160,6 +160,13 @@
  * 20051115.40 (2.2.30) Add ap_map_http_request_error()
  * 20051115.41 (2.2.32) Add s member to proxy_server_conf struct and server
  *                      member to proxy_worker struct.
+ * 20151115.42 (2.2.32) Add http09_enable, http_conformance, and
+ *                      http_methods to core_server_config
+ *                      Add ap_scan_http_field_token(),
+ *                      ap_scan_http_field_content(),
+ *                      and ap_scan_vchar_obstext()
+ *                      Replaced fold boolean with with multiple bit flags
+ *                      to ap_[r]getline()
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503232UL /* "AP22" */
@@ -167,7 +174,7 @@
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20051115
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 41                    /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 42                    /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a

Modified: httpd/httpd/branches/2.2.x/include/http_core.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/include/http_core.h?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/include/http_core.h (original)
+++ httpd/httpd/branches/2.2.x/include/http_core.h Fri Dec 23 05:19:21 2016
@@ -627,6 +627,21 @@ typedef struct {
 #define AP_MERGE_TRAILERS_DISABLE  2
     int merge_trailers;
 
+#define AP_HTTP09_UNSET   0
+#define AP_HTTP09_ENABLE  1
+#define AP_HTTP09_DISABLE 2
+    char http09_enable;
+
+#define AP_HTTP_CONFORMANCE_UNSET     0
+#define AP_HTTP_CONFORMANCE_UNSAFE    1
+#define AP_HTTP_CONFORMANCE_STRICT    2
+    char http_conformance;
+
+#define AP_HTTP_METHODS_UNSET         0
+#define AP_HTTP_METHODS_LENIENT       1
+#define AP_HTTP_METHODS_REGISTERED    2
+    char http_methods;
+
 } core_server_config;
 
 /* for AddOutputFiltersByType in core.c */

Modified: httpd/httpd/branches/2.2.x/include/http_protocol.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/include/http_protocol.h?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/include/http_protocol.h (original)
+++ httpd/httpd/branches/2.2.x/include/http_protocol.h Fri Dec 23 05:19:21 2016
@@ -510,17 +510,22 @@ AP_DECLARE(int) ap_get_basic_auth_pw(req
  */
 AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri);
 
+#define AP_GETLINE_FOLD 1 /* Whether to merge continuation lines */
+#define AP_GETLINE_CRLF 2 /*Whether line ends must be in the form CR LF */
+
 /**
  * Get the next line of input for the request
  * @param s The buffer into which to read the line
  * @param n The size of the buffer
  * @param r The request
- * @param fold Whether to merge continuation lines
+ * @param flags Bit flag of multiple parsing options
+ *              AP_GETLINE_FOLD Whether to merge continuation lines
+ *              AP_GETLINE_CRLF Whether line ends must be in the form CR LF
  * @return The length of the line, if successful
  *         n, if the line is too big to fit in the buffer
  *         -1 for miscellaneous errors
  */
-AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold);
+AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int flags);
 
 /**
  * Get the next line of input for the request
@@ -538,7 +543,9 @@ AP_DECLARE(int) ap_getline(char *s, int
  * @param n The size of the buffer
  * @param read The length of the line.
  * @param r The request
- * @param fold Whether to merge continuation lines
+ * @param flags Bit flag of multiple parsing options
+ *              AP_GETLINE_FOLD Whether to merge continuation lines
+ *              AP_GETLINE_CRLF Whether line ends must be in the form CR LF
  * @param bb Working brigade to use when reading buckets
  * @return APR_SUCCESS, if successful
  *         APR_ENOSPC, if the line is too big to fit in the buffer
@@ -547,7 +554,7 @@ AP_DECLARE(int) ap_getline(char *s, int
 #if APR_CHARSET_EBCDIC
 AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, 
                                      apr_size_t *read,
-                                     request_rec *r, int fold,
+                                     request_rec *r, int flags,
                                      apr_bucket_brigade *bb);
 #else /* ASCII box */
 #define ap_rgetline(s, n, read, r, fold, bb) \
@@ -557,7 +564,7 @@ AP_DECLARE(apr_status_t) ap_rgetline(cha
 /** @see ap_rgetline */
 AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, 
                                           apr_size_t *read,
-                                          request_rec *r, int fold,
+                                          request_rec *r, int flags,
                                           apr_bucket_brigade *bb);
 
 /**

Modified: httpd/httpd/branches/2.2.x/include/httpd.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/include/httpd.h?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/include/httpd.h (original)
+++ httpd/httpd/branches/2.2.x/include/httpd.h Fri Dec 23 05:19:21 2016
@@ -1414,6 +1414,28 @@ AP_DECLARE(char *) ap_get_list_item(apr_
  */
 AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line, const char *tok);
 
+/* Scan a string for field content chars, as defined by RFC7230 section 3.2
+ * including VCHAR/obs-text, as well as HT and SP
+ * @param ptr The string to scan
+ * @return A pointer to the first (non-HT) ASCII ctrl character.
+ * @note lws and trailing whitespace are scanned, the caller is responsible
+ * for trimming leading and trailing whitespace
+ */
+AP_DECLARE(const char *) ap_scan_http_field_content(const char *ptr);
+
+/* Scan a string for token characters, as defined by RFC7230 section 3.2.6 
+ * @param ptr The string to scan
+ * @return A pointer to the first non-token character.
+ */
+AP_DECLARE(const char *) ap_scan_http_token(const char *ptr);
+
+/* Scan a string for visible ASCII (0x21-0x7E) or obstext (0x80+)
+ * and return a pointer to the first SP/CTL/NUL character encountered.
+ * @param ptr The string to scan
+ * @return A pointer to the first SP/CTL character.
+ */
+AP_DECLARE(const char *) ap_scan_vchar_obstext(const char *ptr);
+
 /**
  * Retrieve a token, spacing over it and adjusting the pointer to
  * the first non-white byte afterwards.  Note that these tokens

Modified: httpd/httpd/branches/2.2.x/modules/http/http_filters.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/modules/http/http_filters.c?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/modules/http/http_filters.c (original)
+++ httpd/httpd/branches/2.2.x/modules/http/http_filters.c Fri Dec 23 05:19:21 2016
@@ -125,14 +125,15 @@ static apr_status_t bail_out_on_error(ht
 
 /**
  * Parse a chunk line with optional extension, detect overflow.
- * There are two error cases:
- *  1) If the conversion would require too many bits, APR_EGENERAL is returned.
- *  2) If the conversion used the correct number of bits, but an overflow
+ * There are several error cases:
+ *  1) If the chunk link is misformatted, APR_EINVAL is returned.
+ *  2) If the conversion would require too many bits, APR_EGENERAL is returned.
+ *  3) If the conversion used the correct number of bits, but an overflow
  *     caused only the sign bit to flip, then APR_ENOSPC is returned.
- * In general, any negative number can be considered an overflow error.
+ * A negative chunk length always indicates an overflow error.
  */
 static apr_status_t parse_chunk_size(http_ctx_t *ctx, const char *buffer,
-                                     apr_size_t len, int linelimit)
+                                     apr_size_t len, int linelimit, int strict)
 {
     apr_size_t i = 0;
 
@@ -145,6 +146,12 @@ static apr_status_t parse_chunk_size(htt
         if (ctx->state == BODY_CHUNK_END
                 || ctx->state == BODY_CHUNK_END_LF) {
             if (c == LF) {
+                if (strict && (ctx->state != BODY_CHUNK_END_LF)) {
+                    /*
+                     * CR missing before LF.
+                     */
+                    return APR_EINVAL;
+                }
                 ctx->state = BODY_CHUNK;
             }
             else if (c == CR && ctx->state == BODY_CHUNK_END) {
@@ -152,7 +159,7 @@ static apr_status_t parse_chunk_size(htt
             }
             else {
                 /*
-                 * LF expected.
+                 * CRLF expected.
                  */
                 return APR_EINVAL;
             }
@@ -179,6 +186,12 @@ static apr_status_t parse_chunk_size(htt
         }
 
         if (c == LF) {
+            if (strict && (ctx->state != BODY_CHUNK_LF)) {
+                /*
+                 * CR missing before LF.
+                 */
+                return APR_EINVAL;
+            }
             if (ctx->remaining) {
                 ctx->state = BODY_CHUNK_DATA;
             }
@@ -200,14 +213,17 @@ static apr_status_t parse_chunk_size(htt
         }
         else if (ctx->state == BODY_CHUNK_EXT) {
             /*
-             * Control chars (but tabs) are invalid.
+             * Control chars (excluding tabs) are invalid.
+             * TODO: more precisely limit input
              */
             if (c != '\t' && apr_iscntrl(c)) {
                 return APR_EINVAL;
             }
         }
         else if (c == ' ' || c == '\t') {
-            /* Be lenient up to 10 BWS (term from rfc7230 - 3.2.3).
+            /* Be lenient up to 10 implied *LWS, a legacy of RFC 2616,
+             * and noted as errata to RFC7230;
+             * https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
              */
             ctx->state = BODY_CHUNK_CR;
             if (++ctx->chunk_bws > 10) {
@@ -323,7 +339,10 @@ apr_status_t ap_http_filter(ap_filter_t
                             ap_input_mode_t mode, apr_read_type_e block,
                             apr_off_t readbytes)
 {
-    core_server_config *conf;
+    core_server_config *conf =
+        (core_server_config *)ap_get_module_config(f->r->server->module_config,
+                                                   &core_module);
+    int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
     apr_bucket *e;
     http_ctx_t *ctx = f->ctx;
     apr_status_t rv;
@@ -331,9 +350,6 @@ apr_status_t ap_http_filter(ap_filter_t
     apr_bucket_brigade *bb;
     int again;
 
-    conf = (core_server_config *)
-        ap_get_module_config(f->r->server->module_config, &core_module);
-
     /* just get out of the way of things we don't want. */
     if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
         return ap_get_brigade(f->next, b, mode, block, readbytes);
@@ -525,7 +541,7 @@ apr_status_t ap_http_filter(ap_filter_t
                     if (rv == APR_SUCCESS) {
                         parsing = 1;
                         rv = parse_chunk_size(ctx, buffer, len,
-                                f->r->server->limit_req_fieldsize);
+                                f->r->server->limit_req_fieldsize, strict);
                     }
                     if (rv != APR_SUCCESS) {
                         ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r,
@@ -667,14 +683,83 @@ apr_status_t ap_http_filter(ap_filter_t
     return APR_SUCCESS;
 }
 
+struct check_header_ctx {
+    request_rec *r;
+    int strict;
+};
+
+/* check a single header, to be used with apr_table_do() */
+static int check_header(void *arg, const char *name, const char *val)
+{
+    struct check_header_ctx *ctx = arg;
+    const char *test;
+
+    if (name[0] == '\0') {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r,
+                      "Empty response header name, aborting request");
+        return 0;
+    }
+
+    if (ctx->strict) { 
+        test = ap_scan_http_token(name);
+    }
+    else {
+        test = ap_scan_vchar_obstext(name);
+    }
+    if (*test) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r,
+                      "Response header name '%s' contains invalid "
+                      "characters, aborting request",
+                      name);
+        return 0;
+    }
+
+    test = ap_scan_http_field_content(val);
+    if (*test) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r,
+                      "Response header '%s' value of '%s' contains invalid "
+                      "characters, aborting request",
+                      name, val);
+        return 0;
+    }
+    return 1;
+}
+
+/**
+ * Check headers for HTTP conformance
+ * @return 1 if ok, 0 if bad
+ */
+static APR_INLINE int check_headers(request_rec *r)
+{
+    struct check_header_ctx ctx;
+    core_server_config *conf =
+        (core_server_config *)ap_get_module_config(r->server->module_config,
+                                                   &core_module);
+
+    ctx.r = r;
+    ctx.strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
+    return apr_table_do(check_header, &ctx, r->headers_out, NULL) &&
+           apr_table_do(check_header, &ctx, r->err_headers_out, NULL);
+}
+
+static int check_headers_recursion(request_rec *r)
+{
+    void *check = NULL;
+    apr_pool_userdata_get(&check, "check_headers_recursion", r->pool);
+    if (check) {
+        return 1;
+    }
+    apr_pool_userdata_setn("true", "check_headers_recursion", NULL, r->pool);
+    return 0;
+}
+
 typedef struct header_struct {
     apr_pool_t *pool;
     apr_bucket_brigade *bb;
 } header_struct;
 
 /* Send a single HTTP header field to the client.  Note that this function
- * is used in calls to table_do(), so their interfaces are co-dependent.
- * In other words, don't change this one without checking table_do in alloc.c.
+ * is used in calls to apr_table_do(), so don't change its interface.
  * It returns true unless there was a write error of some kind.
  */
 static int form_header_field(header_struct *h,
@@ -1146,6 +1231,7 @@ AP_DECLARE_NONSTD(int) ap_send_http_trac
 
 typedef struct header_filter_ctx {
     int headers_sent;
+    int headers_error;
 } header_filter_ctx;
 
 AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
@@ -1161,19 +1247,23 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_
     header_filter_ctx *ctx = f->ctx;
     const char *ctype;
     ap_bucket_error *eb = NULL;
+    apr_bucket *eos = NULL;
 
     AP_DEBUG_ASSERT(!r->main);
 
-    if (r->header_only) {
-        if (!ctx) {
-            ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
-        }
-        else if (ctx->headers_sent) {
+    if (!ctx) {
+        ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
+    }
+    if (ctx->headers_sent) {
+        /* Eat body if response must not have one. */
+        if (r->header_only || r->status == HTTP_NO_CONTENT) {
             apr_brigade_cleanup(b);
-            return OK;
+            return APR_SUCCESS;
         }
     }
-
+    else if (!ctx->headers_error && !check_headers(r)) {
+        ctx->headers_error = 1;
+    }
     for (e = APR_BRIGADE_FIRST(b);
          e != APR_BRIGADE_SENTINEL(b);
          e = APR_BUCKET_NEXT(e))
@@ -1190,10 +1280,44 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_
             ap_remove_output_filter(f);
             return ap_pass_brigade(f->next, b);
         }
+        if (ctx->headers_error && APR_BUCKET_IS_EOS(e)) {
+            eos = e;
+        }
     }
-    if (eb) {
-        int status;
+    if (ctx->headers_error) {
+        if (!eos) {
+            /* Eat body until EOS */
+            apr_brigade_cleanup(b);
+            return APR_SUCCESS;
+        }
 
+        /* We may come back here from ap_die() below,
+         * so clear anything from this response.
+         */
+        ctx->headers_error = 0;
+        apr_table_clear(r->headers_out);
+        apr_table_clear(r->err_headers_out);
+
+        /* Don't recall ap_die() if we come back here (from its own internal
+         * redirect or error response), otherwise we can end up in infinite
+         * recursion; better fall through with 500, minimal headers and an
+         * empty body (EOS only).
+         */
+        if (!check_headers_recursion(r)) {
+            apr_brigade_cleanup(b);
+            ap_die(HTTP_INTERNAL_SERVER_ERROR, r);
+            return AP_FILTER_ERROR;
+        }
+        APR_BUCKET_REMOVE(eos);
+        apr_brigade_cleanup(b);
+        APR_BRIGADE_INSERT_TAIL(b, eos);
+        r->status = HTTP_INTERNAL_SERVER_ERROR;
+        r->content_type = r->content_encoding = NULL;
+        r->content_languages = NULL;
+        ap_set_content_length(r, 0);
+    }
+    else if (eb) {
+        int status;
         status = eb->status;
         apr_brigade_cleanup(b);
         ap_die(status, r);
@@ -1250,6 +1374,10 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_
         apr_table_unset(r->headers_out, "Content-Length");
     }
 
+    if (r->status == HTTP_NO_CONTENT) {
+        apr_table_unset(r->headers_out, "Content-Length");
+    }
+
     ctype = ap_make_content_type(r, r->content_type);
     if (strcasecmp(ctype, NO_CONTENT_TYPE)) {
         apr_table_setn(r->headers_out, "Content-Type", ctype);
@@ -1338,11 +1466,11 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_
     terminate_header(b2);
 
     ap_pass_brigade(f->next, b2);
+    ctx->headers_sent = 1;
 
-    if (r->header_only) {
+    if (r->header_only || r->status == HTTP_NO_CONTENT) {
         apr_brigade_cleanup(b);
-        ctx->headers_sent = 1;
-        return OK;
+        return APR_SUCCESS;
     }
 
     r->sent_bodyct = 1;         /* Whatever follows is real body stuff... */

Modified: httpd/httpd/branches/2.2.x/server/core.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/server/core.c?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/core.c (original)
+++ httpd/httpd/branches/2.2.x/server/core.c Fri Dec 23 05:19:21 2016
@@ -546,6 +546,15 @@ static void *merge_core_server_configs(a
                            ? virt->merge_trailers
                            : base->merge_trailers;
 
+    if (virt->http09_enable != AP_HTTP09_UNSET)
+        conf->http09_enable = virt->http09_enable;
+
+    if (virt->http_conformance != AP_HTTP_CONFORMANCE_UNSET)
+        conf->http_conformance = virt->http_conformance;
+
+    if (virt->http_methods != AP_HTTP_METHODS_UNSET)
+        conf->http_methods = virt->http_methods;
+
     return conf;
 }
 
@@ -3241,6 +3250,57 @@ static const char *add_ct_output_filters
 
     return NULL;
 }
+
+static const char *set_http_protocol_options(cmd_parms *cmd, void *dummy,
+                                             const char *arg)
+{
+    core_server_config *conf = ap_get_module_config(cmd->server->module_config,
+                                                    &core_module);
+    if (strcasecmp(arg, "allow0.9") == 0)
+        conf->http09_enable |= AP_HTTP09_ENABLE;
+    else if (strcasecmp(arg, "require1.0") == 0)
+        conf->http09_enable |= AP_HTTP09_DISABLE;
+    else if (strcasecmp(arg, "strict") == 0)
+        conf->http_conformance |= AP_HTTP_CONFORMANCE_STRICT;
+    else if (strcasecmp(arg, "unsafe") == 0)
+        conf->http_conformance |= AP_HTTP_CONFORMANCE_UNSAFE;
+    else if (strcasecmp(arg, "registeredmethods") == 0)
+        conf->http_methods |= AP_HTTP_METHODS_REGISTERED;
+    else if (strcasecmp(arg, "lenientmethods") == 0)
+        conf->http_methods |= AP_HTTP_METHODS_LENIENT;
+    else
+        return "HttpProtocolOptions accepts "
+               "'Unsafe' or 'Strict' (default), "
+               "'RegisteredMethods' or 'LenientMethods' (default), and "
+               "'Require1.0' or 'Allow0.9' (default)";
+
+    if ((conf->http09_enable & AP_HTTP09_ENABLE)
+            && (conf->http09_enable & AP_HTTP09_DISABLE))
+        return "HttpProtocolOptions 'Allow0.9' and 'Require1.0'"
+               " are mutually exclusive";
+
+    if ((conf->http_conformance & AP_HTTP_CONFORMANCE_STRICT)
+            && (conf->http_conformance & AP_HTTP_CONFORMANCE_UNSAFE))
+        return "HttpProtocolOptions 'Strict' and 'Unsafe'"
+               " are mutually exclusive";
+
+    if ((conf->http_methods & AP_HTTP_METHODS_REGISTERED)
+            && (conf->http_methods & AP_HTTP_METHODS_LENIENT))
+        return "HttpProtocolOptions 'RegisteredMethods' and 'LenientMethods'"
+               " are mutually exclusive";
+
+    return NULL;
+}
+
+static const char *set_http_method(cmd_parms *cmd, void *conf, const char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL)
+        return err;
+    ap_method_register(cmd->pool, arg);
+    return NULL;
+}
+
 /*
  * Insert filters requested by the AddOutputFilterByType
  * configuration directive. We cannot add filters based
@@ -3550,6 +3610,12 @@ AP_INIT_FLAG("Suexec", unixd_set_suexec,
 #endif
 AP_INIT_FLAG("MergeTrailers", set_merge_trailers, NULL, RSRC_CONF,
               "merge request trailers into request headers or not"),
+AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CONF,
+                "'Allow0.9' or 'Require1.0' (default); "
+                "'RegisteredMethods' or 'LenientMethods' (default); "
+                "'Unsafe' or 'Strict' (default). Sets HTTP acceptance rules"),
+AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF,
+                "Registers non-standard HTTP methods"),
 { NULL }
 };
 

Modified: httpd/httpd/branches/2.2.x/server/gen_test_char.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/server/gen_test_char.c?rev=1775787&r1=1775786&r2=1775787&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/server/gen_test_char.c (original)
+++ httpd/httpd/branches/2.2.x/server/gen_test_char.c Fri Dec 23 05:19:21 2016
@@ -16,11 +16,11 @@
 
 #ifdef CROSS_COMPILE
 
+#include <ctype.h>
 #define apr_isalnum(c) (isalnum(((unsigned char)(c))))
 #define apr_isalpha(c) (isalpha(((unsigned char)(c))))
 #define apr_iscntrl(c) (iscntrl(((unsigned char)(c))))
 #define apr_isprint(c) (isprint(((unsigned char)(c))))
-#include <ctype.h>
 #define APR_HAVE_STDIO_H 1
 #define APR_HAVE_STRING_H 1
 
@@ -51,11 +51,13 @@
 #define T_HTTP_TOKEN_STOP     (0x08)
 #define T_ESCAPE_LOGITEM      (0x10)
 #define T_ESCAPE_FORENSIC     (0x20)
+#define T_HTTP_CTRLS          (0x80)
+#define T_VCHAR_OBSTEXT      (0x100)
 
 int main(int argc, char *argv[])
 {
     unsigned c;
-    unsigned char flags;
+    unsigned short flags;
 
     printf("/* this file is automatically generated by gen_test_char, "
            "do not edit */\n"
@@ -65,18 +67,22 @@ int main(int argc, char *argv[])
            "#define T_HTTP_TOKEN_STOP      (%u)\n"
            "#define T_ESCAPE_LOGITEM       (%u)\n"
            "#define T_ESCAPE_FORENSIC      (%u)\n"
+           "#define T_HTTP_CTRLS           (%u)\n"
+           "#define T_VCHAR_OBSTEXT        (%u)\n"
            "\n"
-           "static const unsigned char test_char_table[256] = {",
+           "static const unsigned short test_char_table[256] = {",
            T_ESCAPE_SHELL_CMD,
            T_ESCAPE_PATH_SEGMENT,
            T_OS_ESCAPE_PATH,
            T_HTTP_TOKEN_STOP,
            T_ESCAPE_LOGITEM,
-           T_ESCAPE_FORENSIC);
+           T_ESCAPE_FORENSIC,
+           T_HTTP_CTRLS,
+           T_VCHAR_OBSTEXT);
 
     for (c = 0; c < 256; ++c) {
         flags = 0;
-        if (c % 20 == 0)
+        if (c % 8 == 0)
             printf("\n    ");
 
         /* escape_shell_cmd */
@@ -104,15 +110,36 @@ int main(int argc, char *argv[])
             flags |= T_ESCAPE_PATH_SEGMENT;
         }
 
-        if (!apr_isalnum(c) && !strchr("$-_.+!*'(),:@&=/~", c)) {
+        if (!apr_isalnum(c) && !strchr("$-_.+!*'(),:;@&=/~", c)) {
             flags |= T_OS_ESCAPE_PATH;
         }
 
-        /* these are the "tspecials" (RFC2068) or "separators" (RFC2616) */
-        if (c && (apr_iscntrl(c) || strchr(" \t()<>@,;:\\\"/[]?={}", c))) {
+        /* Stop for any non-'token' character, including ctrls, obs-text,
+         * and "tspecials" (RFC2068) a.k.a. "separators" (RFC2616), which
+         * is easer to express as characters remaining in the ASCII token set
+         */
+        if (!c || !(apr_isalnum(c) || strchr("!#$%&'*+-.^_`|~", c))) {
             flags |= T_HTTP_TOKEN_STOP;
         }
 
+        /* Catch CTRLs other than VCHAR, HT and SP, and obs-text (RFC7230 3.2)
+         * This includes only the C0 plane, not C1 (which is obs-text itself.)
+         * XXX: We should verify that all ASCII C0 ctrls/DEL corresponding to
+         * the current EBCDIC translation are captured, and ASCII C1 ctrls
+         * corresponding are all permitted (as they fall under obs-text rule)
+         */
+        if (!c || (apr_iscntrl(c) && c != '\t')) {
+            flags |= T_HTTP_CTRLS;
+        }
+
+        /* From RFC3986, the specific sets of gen-delims, sub-delims (2.2),
+         * and unreserved (2.3) that are possible somewhere within a URI.
+         * Spec requires all others to be %XX encoded, including obs-text.
+         */
+        if (c && !apr_iscntrl(c) && c != ' ') {
+            flags |= T_VCHAR_OBSTEXT;
+        }
+
         /* For logging, escape all control characters,
          * double quotes (because they delimit the request in the log file)
          * backslashes (because we use backslash for escaping)
@@ -130,7 +157,7 @@ int main(int argc, char *argv[])
             flags |= T_ESCAPE_FORENSIC;
         }
 
-        printf("%u%c", flags, (c < 255) ? ',' : ' ');
+        printf("0x%03x%c", flags, (c < 255) ? ',' : ' ');
     }
 
     printf("\n};\n");