You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2020/06/23 20:31:37 UTC

[trafficserver] 01/02: Charge Proxy Header Regression tests into Catch unit tests.

This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 629c72f08714730c9c3a4b93d7b5fa10f9f120ff
Author: Walter Karas <wk...@verizonmedia.com>
AuthorDate: Tue Mar 10 18:43:12 2020 -0500

    Charge Proxy Header Regression tests into Catch unit tests.
    
    (cherry picked from commit 4228868a365d9774740cd153ed107768239352d3)
---
 .gitignore                           |    1 +
 include/tscore/Regression.h          |    2 -
 proxy/hdrs/HdrTest.cc                | 2191 ----------------------------------
 proxy/hdrs/HdrTest.h                 |   77 --
 proxy/hdrs/Makefile.am               |    6 -
 proxy/hdrs/unit_tests/test_Hdrs.cc   | 1920 ++++++++++++++++++++++++++++-
 src/traffic_server/traffic_server.cc |   15 -
 7 files changed, 1920 insertions(+), 2292 deletions(-)

diff --git a/.gitignore b/.gitignore
index 20c2bfa..b9563ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,6 +123,7 @@ proxy/http3/test_libhttp3
 proxy/http3/test_qpack
 proxy/logging/test_LogUtils
 proxy/logging/test_LogUtils2
+proxy/logging/test_RolledLogDeleter
 
 plugins/header_rewrite/header_rewrite_test
 plugins/experimental/cookie_remap/test_cookiejar
diff --git a/include/tscore/Regression.h b/include/tscore/Regression.h
index e34a497..8ebbe8c 100644
--- a/include/tscore/Regression.h
+++ b/include/tscore/Regression.h
@@ -54,8 +54,6 @@
 #define REGRESSION_TEST_QUICK 1
 #define REGRESSION_TEST_NIGHTLY 2
 #define REGRESSION_TEST_EXTENDED 3
-// use only for testing TS error handling!
-#define REGRESSION_TEST_FATAL 4
 
 // regression options
 #define REGRESSION_OPT_EXCLUSIVE (1 << 0)
diff --git a/proxy/hdrs/HdrTest.cc b/proxy/hdrs/HdrTest.cc
deleted file mode 100644
index 4678416..0000000
--- a/proxy/hdrs/HdrTest.cc
+++ /dev/null
@@ -1,2191 +0,0 @@
-/** @file
-
-  A brief file description
-
-  @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.
- */
-
-/****************************************************************************
-
-   HdrTest.cc
-
-   Description:
-       Unit test code for sanity checking the header system is functioning
-         properly
-
-
- ****************************************************************************/
-
-#include "tscore/ink_platform.h"
-#include "tscore/ink_memory.h"
-#include "tscore/ink_time.h"
-
-#include "tscore/Arena.h"
-#include "HTTP.h"
-#include "MIME.h"
-#include "tscore/Regex.h"
-#include "URL.h"
-#include "HttpCompat.h"
-
-#include "HdrTest.h"
-
-//////////////////////
-// Main Test Driver //
-//////////////////////
-
-int
-HdrTest::go(RegressionTest *t, int /* atype ATS_UNUSED */)
-{
-  HdrTest::rtest = t;
-  int status     = 1;
-
-  hdrtoken_init();
-  url_init();
-  mime_init();
-  http_init();
-
-  status = status & test_http_hdr_print_and_copy();
-  status = status & test_comma_vals();
-  status = status & test_parse_comma_list();
-  status = status & test_set_comma_vals();
-  status = status & test_delete_comma_vals();
-  status = status & test_extend_comma_vals();
-  status = status & test_insert_comma_vals();
-  status = status & test_accept_language_match();
-  status = status & test_accept_charset_match();
-  status = status & test_parse_date();
-  status = status & test_format_date();
-  status = status & test_url();
-  status = status & test_arena();
-  status = status & test_regex();
-  status = status & test_http_mutation();
-  status = status & test_mime();
-  status = status & test_http();
-
-  return (status ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
-}
-
-////////////////////////////////////////////////////////////
-// Individual Tests --- return 1 on success, 0 on failure //
-////////////////////////////////////////////////////////////
-
-int
-HdrTest::test_parse_date()
-{
-  static struct {
-    const char *fast;
-    const char *slow;
-  } dates[] = {
-    {"Sun, 06 Nov 1994 08:49:37 GMT", "Sunday, 06-Nov-1994 08:49:37 GMT"},
-    {"Mon, 07 Nov 1994 08:49:37 GMT", "Monday, 07-Nov-1994 08:49:37 GMT"},
-    {"Tue, 08 Nov 1994 08:49:37 GMT", "Tuesday, 08-Nov-1994 08:49:37 GMT"},
-    {"Wed, 09 Nov 1994 08:49:37 GMT", "Wednesday, 09-Nov-1994 08:49:37 GMT"},
-    {"Thu, 10 Nov 1994 08:49:37 GMT", "Thursday, 10-Nov-1994 08:49:37 GMT"},
-    {"Fri, 11 Nov 1994 08:49:37 GMT", "Friday, 11-Nov-1994 08:49:37 GMT"},
-    {"Sat, 11 Nov 1994 08:49:37 GMT", "Saturday, 11-Nov-1994 08:49:37 GMT"},
-    {"Sun, 03 Jan 1999 08:49:37 GMT", "Sunday, 03-Jan-1999 08:49:37 GMT"},
-    {"Sun, 07 Feb 1999 08:49:37 GMT", "Sunday, 07-Feb-1999 08:49:37 GMT"},
-    {"Sun, 07 Mar 1999 08:49:37 GMT", "Sunday, 07-Mar-1999 08:49:37 GMT"},
-    {"Sun, 04 Apr 1999 08:49:37 GMT", "Sunday, 04-Apr-1999 08:49:37 GMT"},
-    {"Sun, 02 May 1999 08:49:37 GMT", "Sunday, 02-May-1999 08:49:37 GMT"},
-    {"Sun, 06 Jun 1999 08:49:37 GMT", "Sunday, 06-Jun-1999 08:49:37 GMT"},
-    {"Sun, 04 Jul 1999 08:49:37 GMT", "Sunday, 04-Jul-1999 08:49:37 GMT"},
-    {"Sun, 01 Aug 1999 08:49:37 GMT", "Sunday, 01-Aug-1999 08:49:37 GMT"},
-    {"Sun, 05 Sep 1999 08:49:37 GMT", "Sunday, 05-Sep-1999 08:49:37 GMT"},
-    {"Sun, 03 Oct 1999 08:49:37 GMT", "Sunday, 03-Oct-1999 08:49:37 GMT"},
-    {"Sun, 07 Nov 1999 08:49:37 GMT", "Sunday, 07-Nov-1999 08:49:37 GMT"},
-    {"Sun, 05 Dec 1999 08:49:37 GMT", "Sunday, 05-Dec-1999 08:49:37 GMT"},
-    {nullptr, nullptr},
-  };
-
-  int i;
-  int failures = 0;
-  time_t fast_t, slow_t;
-
-  bri_box("test_parse_date");
-
-  for (i = 0; dates[i].fast; i++) {
-    fast_t = mime_parse_date(dates[i].fast, dates[i].fast + static_cast<int>(strlen(dates[i].fast)));
-    slow_t = mime_parse_date(dates[i].slow, dates[i].slow + static_cast<int>(strlen(dates[i].slow)));
-    // compare with strptime here!
-    if (fast_t != slow_t) {
-      printf("FAILED: date %lu (%s) != %lu (%s)\n", static_cast<unsigned long>(fast_t), dates[i].fast,
-             static_cast<unsigned long>(slow_t), dates[i].slow);
-      ++failures;
-    }
-  }
-
-  return (failures_to_status("test_parse_date", failures));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_format_date()
-{
-  static const char *dates[] = {
-    "Sun, 06 Nov 1994 08:49:37 GMT",
-    "Sun, 03 Jan 1999 08:49:37 GMT",
-    "Sun, 05 Dec 1999 08:49:37 GMT",
-    "Tue, 25 Apr 2000 20:29:53 GMT",
-    nullptr,
-  };
-
-  bri_box("test_format_date");
-
-  // (1) Test a few hand-created dates
-
-  int i;
-  time_t t, t2, t3;
-  char buffer[128], buffer2[128];
-  static const char *envstr = "TZ=GMT0";
-  int failures              = 0;
-
-  // shift into GMT timezone for cftime conversions
-  putenv(const_cast<char *>(envstr));
-  tzset();
-
-  for (i = 0; dates[i]; i++) {
-    t = mime_parse_date(dates[i], dates[i] + static_cast<int>(strlen(dates[i])));
-
-    cftime_replacement(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", &t);
-    if (memcmp(dates[i], buffer, 29) != 0) {
-      printf("FAILED: original date doesn't match cftime date\n");
-      printf("  input date:  %s\n", dates[i]);
-      printf("  cftime date: %s\n", buffer);
-      ++failures;
-    }
-
-    mime_format_date(buffer, t);
-    if (memcmp(dates[i], buffer, 29) != 0) {
-      printf("FAILED: original date doesn't match mime_format_date date\n");
-      printf("  input date:  %s\n", dates[i]);
-      printf("  cftime date: %s\n", buffer);
-      ++failures;
-    }
-  }
-
-  // (2) test a few times per day from 1/1/1970 to past 2010
-
-  // coverity[dont_call]
-  for (t = 0; t < 40 * 366 * (24 * 60 * 60); t += static_cast<int>(drand48() * (24 * 60 * 60))) {
-    cftime_replacement(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", &t);
-    t2 = mime_parse_date(buffer, buffer + static_cast<int>(strlen(buffer)));
-    if (t2 != t) {
-      printf("FAILED: parsed time_t doesn't match original time_t\n");
-      printf("  input time_t:  %d (%s)\n", static_cast<int>(t), buffer);
-      printf("  parsed time_t: %d\n", static_cast<int>(t2));
-      ++failures;
-    }
-    mime_format_date(buffer2, t);
-    if (memcmp(buffer, buffer2, 29) != 0) {
-      printf("FAILED: formatted date doesn't match original date\n");
-      printf("  original date:  %s\n", buffer);
-      printf("  formatted date: %s\n", buffer2);
-      ++failures;
-    }
-    t3 = mime_parse_date(buffer2, buffer2 + static_cast<int>(strlen(buffer2)));
-    if (t != t3) {
-      printf("FAILED: parsed time_t doesn't match original time_t\n");
-      printf("  input time_t:  %d (%s)\n", static_cast<int>(t), buffer2);
-      printf("  parsed time_t: %d\n", static_cast<int>(t3));
-      ++failures;
-    }
-
-    if (failures > 20) {
-      // Already too many failures, don't need to continue
-      break;
-    }
-  }
-
-  return (failures_to_status("test_format_date", failures));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_url()
-{
-  static const char *strs[] = {
-    "http://some.place/path;params?query#fragment",
-
-    // Start with an easy one...
-    "http://trafficserver.apache.org/index.html",
-
-    "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 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/",
-
-    // "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",
-
-    "ht-tp://a.b.com",
-    "ht+tp://a.b.com",
-    "ht.tp://a.b.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;
-  URL url;
-  const char *start;
-  const char *end;
-  int old_length, new_length;
-
-  bri_box("test_url");
-
-  failed = 0;
-  for (unsigned i = 0; i < countof(strs); i++) {
-    old_length = static_cast<int>(strlen(strs[i]));
-    start      = strs[i];
-    end        = start + old_length;
-
-    url.create(nullptr);
-    err = url.parse(&start, end);
-    if (err < 0) {
-      printf("Failed to parse url '%s'\n", start);
-      failed = 1;
-      break;
-    }
-
-    char print_buf[1024];
-    new_length = 0;
-    int offset = 0;
-    url.print(print_buf, 1024, &new_length, &offset);
-    print_buf[new_length] = '\0';
-
-    const char *fail_text = nullptr;
-
-    if (old_length == new_length) {
-      if (memcmp(print_buf, strs[i], new_length) != 0) {
-        fail_text = "URLS DIFFER";
-      }
-    } else if (old_length == new_length - 1) {
-      // Check to see if the difference is the trailing
-      //   slash we add
-      if (memcmp(print_buf, strs[i], old_length) != 0 || print_buf[new_length - 1] != '/' || (strs[i])[old_length - 1] == '/') {
-        fail_text = "TRAILING SLASH";
-      }
-    } else {
-      fail_text = "LENGTHS DIFFER";
-    }
-
-    if (fail_text) {
-      failed = 1;
-      printf("%16s: OLD: (%4d) %s\n", fail_text, old_length, strs[i]);
-      printf("%16s: NEW: (%4d) %s\n", "", new_length, print_buf);
-      obj_describe(url.m_url_impl, true);
-    } else {
-      printf("%16s: '%s'\n", "PARSE SUCCESS", strs[i]);
-    }
-
-    url.destroy();
-  }
-
-  for (unsigned i = 0; i < countof(bad); ++i) {
-    const char *x = bad[i];
-
-    url.create(nullptr);
-    err = url.parse(x, strlen(x));
-    url.destroy();
-    if (err == PARSE_RESULT_DONE) {
-      failed = 1;
-      printf("Successfully parsed invalid url '%s'", x);
-      break;
-    } else {
-      printf("   bad URL - PARSE FAILED: '%s'\n", bad[i]);
-    }
-  }
-
-  return (failures_to_status("test_url", failed));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_mime()
-{
-  // This can not be a static string (any more) since we unfold the headers
-  // in place.
-  char mime[] = {
-    //        "Date: Tuesday, 08-Dec-98 20:32:17 GMT\r\n"
-    "Date: 6 Nov 1994 08:49:37 GMT\r\n"
-    "Max-Forwards: 65535\r\n"
-    "Cache-Control: private\r\n"
-    "accept: foo\r\n"
-    "accept: bar\n"
-    ": (null) field name\r\n"
-    "aCCept: \n"
-    "ACCEPT\r\n"
-    "foo: bar\r\n"
-    "foo: argh\r\n"
-    "foo: three, four\r\n"
-    "word word: word \r\n"
-    "accept: \"fazzle, dazzle\"\r\n"
-    "accept: 1, 2, 3, 4, 5, 6, 7, 8\r\n"
-    "continuation: part1\r\n"
-    " part2\r\n"
-    "scooby: doo\r\n"
-    " scooby: doo\r\n"
-    "bar: foo\r\n"
-    "\r\n",
-  };
-
-  int err;
-  MIMEHdr hdr;
-  MIMEParser parser;
-  const char *start;
-  const char *end;
-
-  bri_box("test_mime");
-
-  printf("   <<< MUST BE HAND-VERIFIED FOR FULL-BENEFIT>>>\n\n");
-
-  start = mime;
-  end   = start + strlen(start);
-
-  mime_parser_init(&parser);
-
-  bool must_copy_strs = false;
-
-  hdr.create(nullptr);
-  err = hdr.parse(&parser, &start, end, must_copy_strs, false);
-
-  if (err < 0) {
-    return (failures_to_status("test_mime", 1));
-  }
-
-  // Test the (new) continuation line folding to be correct. This should replace the
-  // \r\n with two spaces (so a total of three between "part1" and "part2").
-  int length               = 0;
-  const char *continuation = hdr.value_get("continuation", 12, &length);
-
-  if ((13 != length)) {
-    printf("FAILED: continue header folded line was too short\n");
-    return (failures_to_status("test_mime", 1));
-  }
-
-  if (strncmp(continuation + 5, "   ", 3)) {
-    printf("FAILED: continue header unfolding did not produce correct WS's\n");
-    return (failures_to_status("test_mime", 1));
-  }
-
-  if (strncmp(continuation, "part1   part2", 13)) {
-    printf("FAILED: continue header unfolding was not correct\n");
-    return (failures_to_status("test_mime", 1));
-  }
-
-  hdr.field_delete("not_there", 9);
-  hdr.field_delete("accept", 6);
-  hdr.field_delete("scooby", 6);
-  hdr.field_delete("scooby", 6);
-  hdr.field_delete("bar", 3);
-  hdr.field_delete("continuation", 12);
-
-  int count = hdr.fields_count();
-  printf("hdr.fields_count() = %d\n", count);
-
-  int i_max_forwards = hdr.value_get_int("Max-Forwards", 12);
-  int u_max_forwards = hdr.value_get_uint("Max-Forwards", 12);
-  printf("i_max_forwards = %d   u_max_forwards = %d\n", i_max_forwards, u_max_forwards);
-
-  hdr.set_age(9999);
-
-  length = hdr.length_get();
-  printf("hdr.length_get() = %d\n", length);
-
-  time_t t0, t1, t2;
-
-  t0 = hdr.get_date();
-  if (t0 == 0) {
-    printf("FAILED: Initial date is zero but shouldn't be\n");
-    return (failures_to_status("test_mime", 1));
-  }
-
-  t1 = time(nullptr);
-  hdr.set_date(t1);
-  t2 = hdr.get_date();
-  if (t1 != t2) {
-    printf("FAILED: set_date(%" PRId64 ") ... get_date = %" PRId64 "\n\n", static_cast<int64_t>(t1), static_cast<int64_t>(t2));
-    return (failures_to_status("test_mime", 1));
-  }
-
-  hdr.value_append("Cache-Control", 13, "no-cache", 8, true);
-
-  MIMEField *cc_field;
-  StrList slist;
-
-  cc_field = hdr.field_find("Cache-Control", 13);
-
-  if (cc_field == nullptr) {
-    printf("FAILED: missing Cache-Control header\n\n");
-    return (failures_to_status("test_mime", 1));
-  }
-
-  // TODO: Do we need to check the "count" returned?
-  cc_field->value_get_comma_list(&slist); // FIX: correct usage?
-
-  if (cc_field->value_get_index("Private", 7) < 0) {
-    printf("Failed: value_get_index of Cache-Control did not find private");
-    return (failures_to_status("test_mime", 1));
-  }
-  if (cc_field->value_get_index("Bogus", 5) >= 0) {
-    printf("Failed: value_get_index of Cache-Control incorrectly found bogus");
-    return (failures_to_status("test_mime", 1));
-  }
-  if (hdr.value_get_index("foo", 3, "three", 5) < 0) {
-    printf("Failed: value_get_index of foo did not find three");
-    return (failures_to_status("test_mime", 1));
-  }
-  if (hdr.value_get_index("foo", 3, "bar", 3) < 0) {
-    printf("Failed: value_get_index of foo did not find bar");
-    return (failures_to_status("test_mime", 1));
-  }
-  if (hdr.value_get_index("foo", 3, "Bogus", 5) >= 0) {
-    printf("Failed: value_get_index of foo incorrectly found bogus");
-    return (failures_to_status("test_mime", 1));
-  }
-
-  mime_parser_clear(&parser);
-
-  hdr.print(nullptr, 0, nullptr, nullptr);
-  printf("\n");
-
-  obj_describe((HdrHeapObjImpl *)(hdr.m_mime), true);
-
-  const char *field_name = "Test_heap_reuse";
-
-  MIMEField *f = hdr.field_create(field_name, static_cast<int>(strlen(field_name)));
-  ink_release_assert(f->m_ptr_value == nullptr);
-
-  hdr.field_attach(f);
-  ink_release_assert(f->m_ptr_value == nullptr);
-
-  const char *test_value = "mytest";
-
-  printf("Testing Heap Reuse..\n");
-  hdr.field_value_set(f, "orig_value", strlen("orig_value"));
-  const char *m_ptr_value_orig = f->m_ptr_value;
-  hdr.field_value_set(f, test_value, strlen(test_value), true);
-  ink_release_assert(f->m_ptr_value != test_value);       // should be copied
-  ink_release_assert(f->m_ptr_value == m_ptr_value_orig); // heap doesn't change
-  ink_release_assert(f->m_len_value == strlen(test_value));
-  ink_release_assert(memcmp(f->m_ptr_value, test_value, f->m_len_value) == 0);
-
-  m_ptr_value_orig           = f->m_ptr_value;
-  const char *new_test_value = "myTest";
-  hdr.field_value_set(f, new_test_value, strlen(new_test_value), false);
-  ink_release_assert(f->m_ptr_value != new_test_value);   // should be copied
-  ink_release_assert(f->m_ptr_value != m_ptr_value_orig); // new heap
-  ink_release_assert(f->m_len_value == strlen(new_test_value));
-  ink_release_assert(memcmp(f->m_ptr_value, new_test_value, f->m_len_value) == 0);
-
-  hdr.fields_clear();
-
-  hdr.destroy();
-
-  return (failures_to_status("test_mime", 0));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_http_aux(const char *request, const char *response)
-{
-  int err;
-  HTTPHdr req_hdr, rsp_hdr;
-  HTTPParser parser;
-  const char *start;
-  const char *end;
-
-  printf("   <<< MUST BE HAND-VERIFIED FOR FULL BENEFIT >>>\n\n");
-
-  /*** (1) parse the request string into req_hdr ***/
-
-  start = request;
-  end   = start + strlen(start); // 1 character past end of string
-
-  http_parser_init(&parser);
-
-  req_hdr.create(HTTP_TYPE_REQUEST);
-  rsp_hdr.create(HTTP_TYPE_RESPONSE);
-
-  printf("======== parsing\n\n");
-  while (true) {
-    err = req_hdr.parse_req(&parser, &start, end, true);
-    if (err != PARSE_RESULT_CONT) {
-      break;
-    }
-  }
-  if (err == PARSE_RESULT_ERROR) {
-    req_hdr.destroy();
-    rsp_hdr.destroy();
-    return (failures_to_status("test_http_aux", false));
-  }
-
-  /*** useless copy to exercise copy function ***/
-
-  HTTPHdr new_hdr;
-  new_hdr.create(HTTP_TYPE_REQUEST);
-  new_hdr.copy(&req_hdr);
-  new_hdr.destroy();
-
-  /*** (2) print out the request ***/
-
-  printf("======== real request (length=%d)\n\n", static_cast<int>(strlen(request)));
-  printf("%s\n", request);
-
-  printf("\n[");
-  req_hdr.print(nullptr, 0, nullptr, nullptr);
-  printf("]\n\n");
-
-  obj_describe(req_hdr.m_http, true);
-
-  // req_hdr.destroy ();
-  // ink_release_assert(!"req_hdr.destroy() not defined");
-
-  /*** (3) parse the response string into rsp_hdr ***/
-
-  start = response;
-  end   = start + strlen(start);
-
-  http_parser_clear(&parser);
-  http_parser_init(&parser);
-
-  while (true) {
-    err = rsp_hdr.parse_resp(&parser, &start, end, true);
-    if (err != PARSE_RESULT_CONT) {
-      break;
-    }
-  }
-  if (err == PARSE_RESULT_ERROR) {
-    req_hdr.destroy();
-    rsp_hdr.destroy();
-    return (failures_to_status("test_http_aux", false));
-  }
-
-  http_parser_clear(&parser);
-
-  /*** (4) print out the response ***/
-
-  printf("\n======== real response (length=%d)\n\n", static_cast<int>(strlen(response)));
-  printf("%s\n", response);
-
-  printf("\n[");
-  rsp_hdr.print(nullptr, 0, nullptr, nullptr);
-  printf("]\n\n");
-
-  obj_describe(rsp_hdr.m_http, true);
-
-#define NNN 1000
-  {
-    char buf[NNN];
-    int bufindex, last_bufindex;
-    int tmp;
-    int i;
-
-    bufindex = 0;
-
-    do {
-      last_bufindex = bufindex;
-      tmp           = bufindex;
-      buf[0]        = '#'; // make it obvious if hdr.print doesn't print anything
-      err           = rsp_hdr.print(buf, NNN, &bufindex, &tmp);
-
-      // printf("test_header: tmp = %d  err = %d  bufindex = %d\n", tmp, err, bufindex);
-      putchar('{');
-      for (i = 0; i < bufindex - last_bufindex; i++) {
-        if (!iscntrl(buf[i])) {
-          putchar(buf[i]);
-        } else {
-          printf("\\%o", buf[i]);
-        }
-      }
-      putchar('}');
-    } while (!err);
-  }
-
-  // rsp_hdr.print (NULL, 0, NULL, NULL);
-
-  req_hdr.destroy();
-  rsp_hdr.destroy();
-
-  return (failures_to_status("test_http_aux", false));
-}
-
-int
-HdrTest::test_http_req_parse_error(const char *request, const char *response)
-{
-  int err;
-  HTTPHdr req_hdr, rsp_hdr;
-  HTTPParser parser;
-  const char *start;
-  const char *end;
-
-  int status = 1;
-
-  /*** (1) parse the request string into req_hdr ***/
-
-  start = request;
-  end   = start + strlen(start); // 1 character past end of string
-
-  http_parser_init(&parser);
-
-  req_hdr.create(HTTP_TYPE_REQUEST);
-  rsp_hdr.create(HTTP_TYPE_RESPONSE);
-
-  printf("======== test_http_req_parse_error parsing\n\n");
-  err = req_hdr.parse_req(&parser, &start, end, true, true, 1);
-  if (err != PARSE_RESULT_ERROR) {
-    status = 0;
-  }
-
-  http_parser_clear(&parser);
-
-  /*** (4) print out the response ***/
-
-  printf("\n======== real response (length=%d)\n\n", static_cast<int>(strlen(response)));
-  printf("%s\n", response);
-
-  obj_describe(rsp_hdr.m_http, true);
-
-  req_hdr.destroy();
-  rsp_hdr.destroy();
-
-  return (failures_to_status("test_http_req_parse_error", (status == 0)));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_http_hdr_print_and_copy()
-{
-  static struct {
-    const char *req;
-    const char *req_tgt;
-    const char *rsp;
-    const char *rsp_tgt;
-  } tests[] = {
-    {"GET http://foo.com/bar.txt HTTP/1.0\r\n"
-     "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa\r\n"
-     "\r\n",
-     "GET http://foo.com/bar.txt HTTP/1.0\r\n"
-     "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "\r\n"},
-    {"GET http://foo.com/bar.txt HTTP/1.0\r\n"
-     "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa fjfj dslkfjdslk fjsdafkl dsajfkldsa jfkldsafj "
-     "klsafjs lkafjdsalk fsdjakfl sdjaflkdsaj flksdjflsd ;ffd salfdjs lf;sdaf ;dsaf jdsal;fdjsaflkjsda \r\n"
-     "\r\n",
-     "GET http://foo.com/bar.txt HTTP/1.0\r\n"
-     "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa fjfj dslkfjdslk fjsdafkl dsajfkldsa jfkldsafj "
-     "klsafjs lkafjdsalk fsdjakfl sdjaflkdsaj flksdjflsd ;ffd salfdjs lf;sdaf ;dsaf jdsal;fdjsaflkjsda \r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "\r\n"},
-    {"GET http://foo.com/bar.txt HTTP/1.0\r\n"
-     "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa fjfj dslkfjdslk fjsdafkl dsajfkldsa jfkldsafj "
-     "klsafjs lkafjdsalk fsdjakfl sdjaflkdsaj flksdjflsd ;ffd salfdjs lf;sdaf ;dsaf jdsal;fdjsaflkjsda kfl; fsdajfl; "
-     "sdjafl;dsajlsjfl;sdafjsdal;fjds al;fdjslaf ;slajdk;f\r\n"
-     "\r\n",
-     "GET http://foo.com/bar.txt HTTP/1.0\r\n"
-     "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa fjfj dslkfjdslk fjsdafkl dsajfkldsa jfkldsafj "
-     "klsafjs lkafjdsalk fsdjakfl sdjaflkdsaj flksdjflsd ;ffd salfdjs lf;sdaf ;dsaf jdsal;fdjsaflkjsda kfl; fsdajfl; "
-     "sdjafl;dsajlsjfl;sdafjsdal;fjds al;fdjslaf ;slajdk;f\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "\r\n"},
-    {"GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-     "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
-     "Referer: chocolate fribble\r\n", // missing final CRLF
-     "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-     "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
-     "Referer: chocolate fribble\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "MIME-Version: 1.0\r\n"
-     "Server: WebSTAR/2.1 ID/30013\r\n"
-     "Content-Type: text/html\r\n"
-     "Content-Length: 939\r\n"
-     "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n", // missing final CRLF
-     "HTTP/1.0 200 OK\r\n"
-     "MIME-Version: 1.0\r\n"
-     "Server: WebSTAR/2.1 ID/30013\r\n"
-     "Content-Type: text/html\r\n"
-     "Content-Length: 939\r\n"
-     "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n"
-     "\r\n"},
-    {"GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-     "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
-     "Referer: \r\n", // missing final CRLF
-     "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-     "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
-     "Referer: \r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "MIME-Version: 1.0\r\n"
-     "Server: WebSTAR/2.1 ID/30013\r\n"
-     "Content-Type: text/html\r\n"
-     "Content-Length: 939\r\n"
-     "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "MIME-Version: 1.0\r\n"
-     "Server: WebSTAR/2.1 ID/30013\r\n"
-     "Content-Type: text/html\r\n"
-     "Content-Length: 939\r\n"
-     "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n"
-     "\r\n"},
-    {"GET http://www.news.com:80/ HTTP/1.0\r\n"
-     "Proxy-Connection: Keep-Alive\r\n"
-     "User-Agent: Mozilla/4.04 [en] (X11; I; Linux 2.0.33 i586)\r\n"
-     "Pragma: no-cache\r\n"
-     "Host: www.news.com\r\n"
-     "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n"
-     "Accept-Language: en\r\n"
-     "Accept-Charset: iso-8859-1, *, utf-8\r\n"
-     "Client-ip: D1012148\r\n"
-     "Foo: abcdefghijklmnopqrtu\r\n"
-     "\r\n",
-     "GET http://www.news.com:80/ HTTP/1.0\r\n"
-     "Proxy-Connection: Keep-Alive\r\n"
-     "User-Agent: Mozilla/4.04 [en] (X11; I; Linux 2.0.33 i586)\r\n"
-     "Pragma: no-cache\r\n"
-     "Host: www.news.com\r\n"
-     "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n"
-     "Accept-Language: en\r\n"
-     "Accept-Charset: iso-8859-1, *, utf-8\r\n"
-     "Client-ip: D1012148\r\n"
-     "Foo: abcdefghijklmnopqrtu\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "Content-Length: 16428\r\n"
-     "Content-Type: text/html\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "Content-Length: 16428\r\n"
-     "Content-Type: text/html\r\n"
-     "\r\n"},
-    {"GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-     "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
-     "Referer: http://people.netscape.com/jwz/index.html\r\n"
-     "Proxy-Connection: Keep-Alive\r\n"
-     "User-Agent:  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
-     "Pragma: no-cache\r\n"
-     "Host: people.netscape.com\r\n"
-     "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
-     "\r\n",
-     "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-     "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
-     "Referer: http://people.netscape.com/jwz/index.html\r\n"
-     "Proxy-Connection: Keep-Alive\r\n"
-     "User-Agent:  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
-     "Pragma: no-cache\r\n"
-     "Host: people.netscape.com\r\n"
-     "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "Content-Length: 16428\r\n"
-     "Content-Type: text/html\r\n"
-     "\r\n",
-     "HTTP/1.0 200 OK\r\n"
-     "Content-Length: 16428\r\n"
-     "Content-Type: text/html\r\n"
-     "\r\n"},
-  };
-
-  int ntests = sizeof(tests) / sizeof(tests[0]);
-  int i, failures;
-
-  failures = 0;
-
-  bri_box("test_http_hdr_print_and_copy");
-
-  for (i = 0; i < ntests; i++) {
-    int status = test_http_hdr_print_and_copy_aux(i + 1, tests[i].req, tests[i].req_tgt, tests[i].rsp, tests[i].rsp_tgt);
-    if (status == 0) {
-      ++failures;
-    }
-
-    // Test for expected failures
-    // parse with a '\0' in the header.  Should fail
-    status = test_http_hdr_null_char(i + 1, tests[i].req, tests[i].req_tgt);
-    if (status == 0) {
-      ++failures;
-    }
-
-    // Parse with a CTL character in the method name.  Should fail
-    status = test_http_hdr_ctl_char(i + 1, tests[i].req, tests[i].req_tgt);
-    if (status == 0) {
-      ++failures;
-    }
-  }
-
-  return (failures_to_status("test_http_hdr_print_and_copy", failures));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-static const char *
-comp_http_hdr(HTTPHdr *h1, HTTPHdr *h2)
-{
-  int h1_len, h2_len;
-  int p_index, p_dumpoffset, rval;
-  char *h1_pbuf, *h2_pbuf;
-
-  h1_len = h1->length_get();
-  h2_len = h2->length_get();
-
-  if (h1_len != h2_len) {
-    return "length mismatch";
-  }
-
-  h1_pbuf = static_cast<char *>(ats_malloc(h1_len + 1));
-  h2_pbuf = static_cast<char *>(ats_malloc(h2_len + 1));
-
-  p_index = p_dumpoffset = 0;
-  rval                   = h1->print(h1_pbuf, h1_len, &p_index, &p_dumpoffset);
-  if (rval != 1) {
-    ats_free(h1_pbuf);
-    ats_free(h2_pbuf);
-    return "hdr print failed";
-  }
-
-  p_index = p_dumpoffset = 0;
-  rval                   = h2->print(h2_pbuf, h2_len, &p_index, &p_dumpoffset);
-  if (rval != 1) {
-    ats_free(h1_pbuf);
-    ats_free(h2_pbuf);
-    return "hdr print failed";
-  }
-
-  rval = memcmp(h1_pbuf, h2_pbuf, h1_len);
-  ats_free(h1_pbuf);
-  ats_free(h2_pbuf);
-
-  if (rval != 0) {
-    return "compare failed";
-  } else {
-    return nullptr;
-  }
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_http_hdr_copy_over_aux(int testnum, const char *request, const char *response)
-{
-  int err;
-  HTTPHdr req_hdr;
-  HTTPHdr resp_hdr;
-  HTTPHdr copy1;
-  HTTPHdr copy2;
-
-  HTTPParser parser;
-  const char *start;
-  const char *end;
-  const char *comp_str = nullptr;
-
-  /*** (1) parse the request string into hdr ***/
-
-  req_hdr.create(HTTP_TYPE_REQUEST);
-
-  start = request;
-  end   = start + strlen(start); // 1 character past end of string
-
-  http_parser_init(&parser);
-
-  while (true) {
-    err = req_hdr.parse_req(&parser, &start, end, true);
-    if (err != PARSE_RESULT_CONT) {
-      break;
-    }
-  }
-
-  if (err == PARSE_RESULT_ERROR) {
-    printf("FAILED: (test #%d) parse error parsing request hdr\n", testnum);
-    return (0);
-  }
-  http_parser_clear(&parser);
-
-  /*** (2) parse the response string into hdr ***/
-
-  resp_hdr.create(HTTP_TYPE_RESPONSE);
-
-  start = response;
-  end   = start + strlen(start); // 1 character past end of string
-
-  http_parser_init(&parser);
-
-  while (true) {
-    err = resp_hdr.parse_resp(&parser, &start, end, true);
-    if (err != PARSE_RESULT_CONT) {
-      break;
-    }
-  }
-
-  if (err == PARSE_RESULT_ERROR) {
-    printf("FAILED: (test #%d) parse error parsing response hdr\n", testnum);
-    return (0);
-  }
-
-  /*** (3) Basic copy testing ***/
-  copy1.create(HTTP_TYPE_REQUEST);
-  copy1.copy(&req_hdr);
-  comp_str = comp_http_hdr(&req_hdr, &copy1);
-  if (comp_str) {
-    goto done;
-  }
-
-  copy2.create(HTTP_TYPE_RESPONSE);
-  copy2.copy(&resp_hdr);
-  comp_str = comp_http_hdr(&resp_hdr, &copy2);
-  if (comp_str) {
-    goto done;
-  }
-
-  // The APIs for copying headers uses memcpy() which can be unsafe for
-  // overlapping memory areas. It's unclear to me why these tests were
-  // created in the first place honestly, since nothing else does this.
-
-  /*** (4) Gender bending copying ***/
-  copy1.copy(&resp_hdr);
-  comp_str = comp_http_hdr(&resp_hdr, &copy1);
-  if (comp_str) {
-    goto done;
-  }
-
-  copy2.copy(&req_hdr);
-  comp_str = comp_http_hdr(&req_hdr, &copy2);
-  if (comp_str) {
-    goto done;
-  }
-
-done:
-  req_hdr.destroy();
-  resp_hdr.destroy();
-  copy1.destroy();
-  copy2.destroy();
-
-  if (comp_str) {
-    printf("FAILED: (test #%d) copy & compare: %s\n", testnum, comp_str);
-    printf("REQ:\n[%.*s]\n", static_cast<int>(strlen(request)), request);
-    printf("RESP  :\n[%.*s]\n", static_cast<int>(strlen(response)), response);
-    return (0);
-  } else {
-    return (1);
-  }
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-int
-HdrTest::test_http_hdr_null_char(int testnum, const char *request, const char * /*request_tgt*/)
-{
-  int err;
-  HTTPHdr hdr;
-  HTTPParser parser;
-  const char *start;
-  char cpy_buf[2048];
-  const char *cpy_buf_ptr = cpy_buf;
-
-  /*** (1) parse the request string into hdr ***/
-
-  hdr.create(HTTP_TYPE_REQUEST);
-
-  start = request;
-
-  if (strlen(start) > sizeof(cpy_buf)) {
-    printf("FAILED: (test #%d) Internal buffer too small for null char test\n", testnum);
-    return (0);
-  }
-  strcpy(cpy_buf, start);
-
-  // Put a null character somewhere in the header
-  int length          = strlen(start);
-  cpy_buf[length / 2] = '\0';
-  http_parser_init(&parser);
-
-  while (true) {
-    err = hdr.parse_req(&parser, &cpy_buf_ptr, cpy_buf_ptr + length, true);
-    if (err != PARSE_RESULT_CONT) {
-      break;
-    }
-  }
-  if (err != PARSE_RESULT_ERROR) {
-    printf("FAILED: (test #%d) no parse error parsing request with null char\n", testnum);
-    return (0);
-  }
-  return 1;
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-int
-HdrTest::test_http_hdr_ctl_char(int testnum, const char *request, const char * /*request_tgt */)
-{
-  int err;
-  HTTPHdr hdr;
-  HTTPParser parser;
-  const char *start;
-  char cpy_buf[2048];
-  const char *cpy_buf_ptr = cpy_buf;
-
-  /*** (1) parse the request string into hdr ***/
-
-  hdr.create(HTTP_TYPE_REQUEST);
-
-  start = request;
-
-  if (strlen(start) > sizeof(cpy_buf)) {
-    printf("FAILED: (test #%d) Internal buffer too small for ctl char test\n", testnum);
-    return (0);
-  }
-  strcpy(cpy_buf, start);
-
-  // Replace a character in the method
-  cpy_buf[1] = 16;
-
-  http_parser_init(&parser);
-
-  while (true) {
-    err = hdr.parse_req(&parser, &cpy_buf_ptr, cpy_buf_ptr + strlen(start), true);
-    if (err != PARSE_RESULT_CONT) {
-      break;
-    }
-  }
-
-  if (err != PARSE_RESULT_ERROR) {
-    printf("FAILED: (test #%d) no parse error parsing method with ctl char\n", testnum);
-    return (0);
-  }
-  return 1;
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *request_tgt, const char *response,
-                                          const char *response_tgt)
-{
-  int err;
-  HTTPHdr hdr;
-  HTTPParser parser;
-  const char *start;
-  const char *end;
-
-  char prt_buf[2048];
-  int prt_bufsize = sizeof(prt_buf);
-  int prt_bufindex, prt_dumpoffset, prt_ret;
-
-  char cpy_buf[2048];
-  int cpy_bufsize = sizeof(cpy_buf);
-  int cpy_bufindex, cpy_dumpoffset, cpy_ret;
-
-  char *marshal_buf   = static_cast<char *>(ats_malloc(2048));
-  int marshal_bufsize = sizeof(cpy_buf);
-
-  /*** (1) parse the request string into hdr ***/
-
-  hdr.create(HTTP_TYPE_REQUEST);
-
-  start = request;
-  end   = start + strlen(start); // 1 character past end of string
-
-  http_parser_init(&parser);
-
-  while (true) {
-    err = hdr.parse_req(&parser, &start, end, true);
-    if (err != PARSE_RESULT_CONT) {
-      break;
-    }
-  }
-
-  if (err == PARSE_RESULT_ERROR) {
-    printf("FAILED: (test #%d) parse error parsing request hdr\n", testnum);
-    ats_free(marshal_buf);
-    return (0);
-  }
-
-  /*** (2) copy the request header ***/
-  HTTPHdr new_hdr, marshal_hdr;
-  RefCountObj ref;
-
-  // Pretend to pin this object with a refcount.
-  ref.refcount_inc();
-
-  int marshal_len = hdr.m_heap->marshal(marshal_buf, marshal_bufsize);
-  marshal_hdr.create(HTTP_TYPE_REQUEST);
-  marshal_hdr.unmarshal(marshal_buf, marshal_len, &ref);
-  new_hdr.create(HTTP_TYPE_REQUEST);
-  new_hdr.copy(&marshal_hdr);
-
-  /*** (3) print the request header and copy to buffers ***/
-
-  prt_bufindex = prt_dumpoffset = 0;
-  prt_ret                       = hdr.print(prt_buf, prt_bufsize, &prt_bufindex, &prt_dumpoffset);
-
-  cpy_bufindex = cpy_dumpoffset = 0;
-  cpy_ret                       = new_hdr.print(cpy_buf, cpy_bufsize, &cpy_bufindex, &cpy_dumpoffset);
-
-  ats_free(marshal_buf);
-
-  if ((prt_ret != 1) || (cpy_ret != 1)) {
-    printf("FAILED: (test #%d) couldn't print req hdr or copy --- prt_ret=%d, cpy_ret=%d\n", testnum, prt_ret, cpy_ret);
-    return (0);
-  }
-
-  if ((static_cast<size_t>(prt_bufindex) != strlen(request_tgt)) || (static_cast<size_t>(cpy_bufindex) != strlen(request_tgt))) {
-    printf("FAILED: (test #%d) print req output size mismatch --- tgt=%d, prt_bufsize=%d, cpy_bufsize=%d\n", testnum,
-           static_cast<int>(strlen(request_tgt)), prt_bufindex, cpy_bufindex);
-
-    printf("ORIGINAL:\n[%.*s]\n", static_cast<int>(strlen(request)), request);
-    printf("TARGET  :\n[%.*s]\n", static_cast<int>(strlen(request_tgt)), request_tgt);
-    printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf);
-    printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf);
-    return (0);
-  }
-
-  if ((strncasecmp(request_tgt, prt_buf, strlen(request_tgt)) != 0) ||
-      (strncasecmp(request_tgt, cpy_buf, strlen(request_tgt)) != 0)) {
-    printf("FAILED: (test #%d) print req output mismatch\n", testnum);
-    printf("ORIGINAL:\n[%.*s]\n", static_cast<int>(strlen(request)), request);
-    printf("TARGET  :\n[%.*s]\n", static_cast<int>(strlen(request_tgt)), request_tgt);
-    printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf);
-    printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf);
-    return (0);
-  }
-
-  hdr.destroy();
-  new_hdr.destroy();
-
-  /*** (4) parse the response string into hdr ***/
-
-  hdr.create(HTTP_TYPE_RESPONSE);
-
-  start = response;
-  end   = start + strlen(start); // 1 character past end of string
-
-  http_parser_init(&parser);
-
-  while (true) {
-    err = hdr.parse_resp(&parser, &start, end, true);
-    if (err != PARSE_RESULT_CONT) {
-      break;
-    }
-  }
-
-  if (err == PARSE_RESULT_ERROR) {
-    printf("FAILED: (test #%d) parse error parsing response hdr\n", testnum);
-    return (0);
-  }
-
-  /*** (2) copy the response header ***/
-
-  new_hdr.create(HTTP_TYPE_RESPONSE);
-  new_hdr.copy(&hdr);
-
-  /*** (3) print the response header and copy to buffers ***/
-
-  prt_bufindex = prt_dumpoffset = 0;
-  prt_ret                       = hdr.print(prt_buf, prt_bufsize, &prt_bufindex, &prt_dumpoffset);
-
-  cpy_bufindex = cpy_dumpoffset = 0;
-  cpy_ret                       = new_hdr.print(cpy_buf, cpy_bufsize, &cpy_bufindex, &cpy_dumpoffset);
-
-  if ((prt_ret != 1) || (cpy_ret != 1)) {
-    printf("FAILED: (test #%d) couldn't print rsp hdr or copy --- prt_ret=%d, cpy_ret=%d\n", testnum, prt_ret, cpy_ret);
-    return (0);
-  }
-
-  if ((static_cast<size_t>(prt_bufindex) != strlen(response_tgt)) || (static_cast<size_t>(cpy_bufindex) != strlen(response_tgt))) {
-    printf("FAILED: (test #%d) print rsp output size mismatch --- tgt=%d, prt_bufsize=%d, cpy_bufsize=%d\n", testnum,
-           static_cast<int>(strlen(response_tgt)), prt_bufindex, cpy_bufindex);
-    printf("ORIGINAL:\n[%.*s]\n", static_cast<int>(strlen(response)), response);
-    printf("TARGET  :\n[%.*s]\n", static_cast<int>(strlen(response_tgt)), response_tgt);
-    printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf);
-    printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf);
-    return (0);
-  }
-
-  if ((strncasecmp(response_tgt, prt_buf, strlen(response_tgt)) != 0) ||
-      (strncasecmp(response_tgt, cpy_buf, strlen(response_tgt)) != 0)) {
-    printf("FAILED: (test #%d) print rsp output mismatch\n", testnum);
-    printf("ORIGINAL:\n[%.*s]\n", static_cast<int>(strlen(response)), response);
-    printf("TARGET  :\n[%.*s]\n", static_cast<int>(strlen(response_tgt)), response_tgt);
-    printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf);
-    printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf);
-    return (0);
-  }
-
-  hdr.destroy();
-  new_hdr.destroy();
-
-  if (test_http_hdr_copy_over_aux(testnum, request, response) == 0) {
-    return 0;
-  }
-
-  return (1);
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_http()
-{
-  int status = 1;
-
-  static const char request0[] = {
-    "GET http://www.news.com:80/ HTTP/1.0\r\n"
-    "Proxy-Connection: Keep-Alive\r\n"
-    "User-Agent: Mozilla/4.04 [en] (X11; I; Linux 2.0.33 i586)\r\n"
-    "Pragma: no-cache\r\n"
-    "Host: www.news.com\r\n"
-    "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n"
-    "Accept-Language: en\r\n"
-    "Accept-Charset: iso-8859-1, *, utf-8\r\n"
-    "Cookie: u_vid_0_0=00031ba3; "
-    "s_cur_0_0=0101sisi091314775496e7d3Jx4+POyJakrMybmNOsq6XOn5bVn5Z6a4Ln5crU5M7Rxq2lm5aWpqupo20=; "
-    "SC_Cnet001=Sampled; SC_Cnet002=Sampled\r\n"
-    "Client-ip: D1012148\r\n"
-    "Foo: abcdefghijklmnopqrtu\r\n"
-    "\r\n",
-  };
-
-  static const char request09[] = {
-    "GET /index.html\r\n"
-    "\r\n",
-  };
-
-  static const char request1[] = {
-    "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-    "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
-    "Referer: http://people.netscape.com/jwz/index.html\r\n"
-    "Proxy-Connection: Keep-Alive\r\n"
-    "User-Agent:  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
-    "Pragma: no-cache\r\n"
-    "Host: people.netscape.com\r\n"
-    "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
-    "\r\n",
-  };
-
-  static const char request_no_colon[] = {
-    "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-    "If-Modified-Since Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
-    "Referer http://people.netscape.com/jwz/index.html\r\n"
-    "Proxy-Connection Keep-Alive\r\n"
-    "User-Agent  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
-    "Pragma no-cache\r\n"
-    "Host people.netscape.com\r\n"
-    "Accept image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
-    "\r\n",
-  };
-
-  static const char request_no_val[] = {
-    "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-    "If-Modified-Since:\r\n"
-    "Referer:     "
-    "Proxy-Connection:\r\n"
-    "User-Agent:     \r\n"
-    "Host:::\r\n"
-    "\r\n",
-  };
-
-  static const char request_multi_fblock[] = {
-    "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
-    "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
-    "Referer: http://people.netscape.com/jwz/index.html\r\n"
-    "Proxy-Connection: Keep-Alive\r\n"
-    "User-Agent:  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
-    "Pragma: no-cache\r\n"
-    "Host: people.netscape.com\r\n"
-    "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
-    "X-1: blah\r\n"
-    "X-2: blah\r\n"
-    "X-3: blah\r\n"
-    "X-4: blah\r\n"
-    "X-5: blah\r\n"
-    "X-6: blah\r\n"
-    "X-7: blah\r\n"
-    "X-8: blah\r\n"
-    "X-9: blah\r\n"
-    "Pragma: no-cache\r\n"
-    "X-X-1: blah\r\n"
-    "X-X-2: blah\r\n"
-    "X-X-3: blah\r\n"
-    "X-X-4: blah\r\n"
-    "X-X-5: blah\r\n"
-    "X-X-6: blah\r\n"
-    "X-X-7: blah\r\n"
-    "X-X-8: blah\r\n"
-    "X-X-9: blah\r\n"
-    "\r\n",
-  };
-
-  static const char request_leading_space[] = {
-    " GET http://www.news.com:80/ HTTP/1.0\r\n"
-    "Proxy-Connection: Keep-Alive\r\n"
-    "User-Agent: Mozilla/4.04 [en] (X11; I; Linux 2.0.33 i586)\r\n"
-    "\r\n",
-  };
-
-  static const char request_padding[] = {
-    "GET http://www.padding.com:80/ HTTP/1.0\r\n"
-    "X-1: blah1\r\n"
-    //       "X-2:  blah2\r\n"
-    "X-3:   blah3\r\n"
-    //       "X-4:    blah4\r\n"
-    "X-5:     blah5\r\n"
-    //       "X-6:      blah6\r\n"
-    "X-7:       blah7\r\n"
-    //       "X-8:        blah8\r\n"
-    "X-9:         blah9\r\n"
-    "\r\n",
-  };
-
-  static const char request_09p[] = {
-    "GET http://www.news09.com/\r\n"
-    "\r\n",
-  };
-
-  static const char request_09ht[] = {
-    "GET http://www.news09.com/ HT\r\n"
-    "\r\n",
-  };
-
-  static const char request_11[] = {
-    "GET http://www.news.com/ HTTP/1.1\r\n"
-    "Connection: close\r\n"
-    "\r\n",
-  };
-
-  static const char request_too_long[] = {
-    "GET http://www.news.com/i/am/too/long HTTP/1.1\r\n"
-    "Connection: close\r\n"
-    "\r\n",
-  };
-
-  static const char request_unterminated[] = {
-    "GET http://www.unterminated.com/ HTTP/1.1",
-  };
-
-  static const char request_blank[] = {
-    "\r\n",
-  };
-
-  static const char request_blank2[] = {
-    "\r\n"
-    "\r\n",
-  };
-
-  static const char request_blank3[] = {
-    "     "
-    "\r\n",
-  };
-
-  ////////////////////////////////////////////////////
-
-  static const char response0[] = {
-    "HTTP/1.0 200 OK\r\n"
-    "MIME-Version: 1.0\r\n"
-    "Server: WebSTAR/2.1 ID/30013\r\n"
-    "Content-Type: text/html\r\n"
-    "Content-Length: 939\r\n"
-    "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n"
-    "\r\n",
-  };
-
-  static const char response1[] = {
-    "HTTP/1.0 200 OK\r\n"
-    "Server: Netscape-Communications/1.12\r\n"
-    "Date: Tuesday, 08-Dec-98 20:32:17 GMT\r\n"
-    "Content-Type: text/html\r\n"
-    "\r\n",
-  };
-
-  static const char response_no_colon[] = {
-    "HTTP/1.0 200 OK\r\n"
-    "Server Netscape-Communications/1.12\r\n"
-    "Date: Tuesday, 08-Dec-98 20:32:17 GMT\r\n"
-    "Content-Type: text/html\r\n"
-    "\r\n",
-  };
-
-  static const char response_unterminated[] = {
-    "HTTP/1.0 200 OK",
-  };
-
-  static const char response09[] = {
-    "",
-  };
-
-  static const char response_blank[] = {
-    "\r\n",
-  };
-
-  static const char response_blank2[] = {
-    "\r\n"
-    "\r\n",
-  };
-
-  static const char response_blank3[] = {
-    "     "
-    "\r\n",
-  };
-
-  static const char response_too_long_req[] = {
-    "HTTP/1.0 414 URI Too Long\r\n"
-    "\r\n",
-  };
-
-  status = status & test_http_aux(request0, response0);
-  status = status & test_http_aux(request09, response09);
-  status = status & test_http_aux(request1, response1);
-  status = status & test_http_aux(request_no_colon, response_no_colon);
-  status = status & test_http_aux(request_no_val, response_no_colon);
-  status = status & test_http_aux(request_leading_space, response0);
-  status = status & test_http_aux(request_multi_fblock, response0);
-  status = status & test_http_aux(request_padding, response0);
-  status = status & test_http_aux(request_09p, response0);
-  status = status & test_http_aux(request_09ht, response0);
-  status = status & test_http_aux(request_11, response0);
-  status = status & test_http_aux(request_unterminated, response_unterminated);
-  status = status & test_http_aux(request_blank, response_blank);
-  status = status & test_http_aux(request_blank2, response_blank2);
-  status = status & test_http_aux(request_blank3, response_blank3);
-  status = status & test_http_req_parse_error(request_too_long, response_too_long_req);
-
-  return (failures_to_status("test_http", (status == 0)));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_http_mutation()
-{
-  bri_box("test_http_mutation");
-
-  printf("   <<< MUST BE HAND-VERIFIED FOR FULL BENEFIT>>>\n\n");
-
-  HTTPHdr resp_hdr;
-  int err, i;
-  HTTPParser parser;
-  const char base_resp[] = "HTTP/1.0 200 OK\r\n\r\n";
-  const char *start, *end;
-
-  /*** (1) parse the response string into req_hdr ***/
-
-  start = base_resp;
-  end   = start + strlen(start);
-
-  http_parser_init(&parser);
-
-  resp_hdr.create(HTTP_TYPE_RESPONSE);
-
-  while (true) {
-    err = resp_hdr.parse_resp(&parser, &start, end, true);
-    if (err != PARSE_RESULT_CONT) {
-      break;
-    }
-  }
-
-  printf("\n======== before mutation ==========\n\n");
-  printf("\n[");
-  resp_hdr.print(nullptr, 0, nullptr, nullptr);
-  printf("]\n\n");
-
-  /*** (2) add in a bunch of header fields ****/
-  char field_name[1024];
-  char field_value[1024];
-  for (i = 1; i <= 100; i++) {
-    snprintf(field_name, sizeof(field_name), "Test%d", i);
-    snprintf(field_value, sizeof(field_value), "%d %d %d %d %d", i, i, i, i, i);
-    resp_hdr.value_set(field_name, static_cast<int>(strlen(field_name)), field_value, static_cast<int>(strlen(field_value)));
-  }
-
-  /**** (3) delete all the even numbered fields *****/
-  for (i = 2; i <= 100; i += 2) {
-    snprintf(field_name, sizeof(field_name), "Test%d", i);
-    resp_hdr.field_delete(field_name, static_cast<int>(strlen(field_name)));
-  }
-
-  /***** (4) add in secondary fields for all multiples of 3 ***/
-  for (i = 3; i <= 100; i += 3) {
-    snprintf(field_name, sizeof(field_name), "Test%d", i);
-    MIMEField *f = resp_hdr.field_create(field_name, static_cast<int>(strlen(field_name)));
-    resp_hdr.field_attach(f);
-    snprintf(field_value, sizeof(field_value), "d %d %d %d %d %d", i, i, i, i, i);
-    f->value_set(resp_hdr.m_heap, resp_hdr.m_mime, field_value, static_cast<int>(strlen(field_value)));
-  }
-
-  /***** (5) append all fields with multiples of 5 ***/
-  for (i = 5; i <= 100; i += 5) {
-    snprintf(field_name, sizeof(field_name), "Test%d", i);
-    snprintf(field_value, sizeof(field_value), "a %d", i);
-
-    resp_hdr.value_append(field_name, static_cast<int>(strlen(field_name)), field_value, static_cast<int>(strlen(field_value)),
-                          true);
-  }
-
-  /**** (6) delete all multiples of nine *****/
-  for (i = 9; i <= 100; i += 9) {
-    snprintf(field_name, sizeof(field_name), "Test%d", i);
-    resp_hdr.field_delete(field_name, static_cast<int>(strlen(field_name)));
-  }
-
-  printf("\n======== mutated response ==========\n\n");
-  printf("\n[");
-  resp_hdr.print(nullptr, 0, nullptr, nullptr);
-  printf("]\n\n");
-
-  resp_hdr.destroy();
-
-  return (failures_to_status("test_http_mutation", false));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_arena_aux(Arena *arena, int len)
-{
-  char *str      = arena->str_alloc(len);
-  int verify_len = static_cast<int>(arena->str_length(str));
-
-  if (len != verify_len) {
-    printf("FAILED: requested %d, got %d bytes\n", len, verify_len);
-    return (1); // 1 error (different return convention)
-  } else {
-    return (0); // no errors (different return convention)
-  }
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_arena()
-{
-  bri_box("test_arena");
-
-  Arena *arena;
-  int failures = 0;
-
-  arena = new Arena;
-
-  failures += test_arena_aux(arena, 1);
-  failures += test_arena_aux(arena, 127);
-  failures += test_arena_aux(arena, 128);
-  failures += test_arena_aux(arena, 129);
-  failures += test_arena_aux(arena, 255);
-  failures += test_arena_aux(arena, 256);
-  failures += test_arena_aux(arena, 16384);
-  failures += test_arena_aux(arena, 16385);
-  failures += test_arena_aux(arena, 16511);
-  failures += test_arena_aux(arena, 16512);
-  failures += test_arena_aux(arena, 2097152);
-  failures += test_arena_aux(arena, 2097153);
-  failures += test_arena_aux(arena, 2097279);
-  failures += test_arena_aux(arena, 2097280);
-
-  delete arena;
-
-  return (failures_to_status("test_arena", failures));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_regex()
-{
-  DFA dfa;
-  int status = 1;
-
-  const char *test_harness[] = {"foo", "(.*\\.apache\\.org)", "(.*\\.example\\.com)"};
-
-  bri_box("test_regex");
-
-  dfa.compile(test_harness, SIZEOF(test_harness));
-  status = status & (dfa.match("trafficserver.apache.org") == 1);
-  status = status & (dfa.match("www.example.com") == 2);
-  status = status & (dfa.match("aaaaaafooooooooinktomi....com.org") == -1);
-  status = status & (dfa.match("foo") == 0);
-
-  return (failures_to_status("test_regex", (status != 1)));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_accept_language_match()
-{
-  bri_box("test_accept_language_match");
-
-  struct {
-    const char *content_language;
-    const char *accept_language;
-    float Q;
-    int L;
-    int I;
-  } test_cases[] = {
-    {"en", "*", 1.0, 1, 1},
-    {"en", "fr", 0.0, 0, 0},
-    {"en", "de, fr, en;q=0.7", 0.7, 2, 3},
-    {"en-cockney", "de, fr, en;q=0.7", 0.7, 2, 3},
-    {"en-cockney", "de, fr, en-foobar;q=0.8, en;q=0.7", 0.7, 2, 4},
-    {"en-cockney", "de, fr, en-cockney;q=0.8, en;q=0.7", 0.8, 10, 3},
-    {"en-cockney", "de, fr, en;q=0.8, en;q=0.7", 0.8, 2, 3},
-    {"en-cockney", "de, fr, en;q=0.7, en;q=0.8", 0.8, 2, 4},
-    {"en-cockney", "de, fr, en;q=0.8, en;q=0.8", 0.8, 2, 3},
-    {"en-cockney", "de, fr, en-cockney;q=0.7, en;q=0.8", 0.7, 10, 3},
-    {"en-cockney", "de, fr, en;q=0.8, en-cockney;q=0.7", 0.7, 10, 4},
-    {"en-cockney", "de, fr, en-cockney;q=0.8, en;q=0.8", 0.8, 10, 3},
-    {"en-cockney", "de, fr, en-cockney;q=0.8, en;q=0.7", 0.8, 10, 3},
-    {"en-cockney", "de, fr, en-american", 0.0, 0, 0},
-    {"en-cockney", "de, fr, en;q=0.8, en;q=0.8, *", 0.8, 2, 3},
-    {"en-cockney", "de, fr, en;q=0.8, en;q=0.8, *;q=0.9", 0.8, 2, 3},
-    {"en-foobar", "de, fr, en;q=0.8, en;q=0.8, *;q=0.9", 0.8, 2, 3},
-    {"oo-foobar", "de, fr, en;q=0.8, en;q=0.8, *;q=0.9", 0.9, 1, 5},
-    {"oo-foobar", "de, fr, en;q=0.8, en;q=0.8, *;q=0.9, *", 1.0, 1, 6},
-    {"oo-foobar", "de, fr, en;q=0.8, en;q=0.8, *, *;q=0.9", 1.0, 1, 5},
-    {"fr-belgian", "de, fr;hi-there;q=0.9, fr;q=0.8, en", 0.9, 2, 2},
-    {"fr-belgian", "de, fr;q=0.8, fr;hi-there;q=0.9, en", 0.9, 2, 3},
-    {nullptr, nullptr, 0.0, 0, 0},
-  };
-
-  int i, I, L;
-  float Q;
-  int failures = 0;
-
-  for (i = 0; test_cases[i].accept_language; i++) {
-    StrList acpt_lang_list(false);
-    HttpCompat::parse_comma_list(&acpt_lang_list, test_cases[i].accept_language,
-                                 static_cast<int>(strlen(test_cases[i].accept_language)));
-
-    Q = HttpCompat::match_accept_language(test_cases[i].content_language, static_cast<int>(strlen(test_cases[i].content_language)),
-                                          &acpt_lang_list, &L, &I);
-
-    if ((Q != test_cases[i].Q) || (L != test_cases[i].L) || (I != test_cases[i].I)) {
-      printf("FAILED: (#%d) got { Q = %.3f; L = %d; I = %d; }, expected { Q = %.3f; L = %d; I = %d; }, from matching\n  '%s' "
-             "against '%s'\n",
-             i, Q, L, I, test_cases[i].Q, test_cases[i].L, test_cases[i].I, test_cases[i].content_language,
-             test_cases[i].accept_language);
-      ++failures;
-    }
-  }
-
-  return (failures_to_status("test_accept_language_match", failures));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_accept_charset_match()
-{
-  bri_box("test_accept_charset_match");
-
-  struct {
-    const char *content_charset;
-    const char *accept_charset;
-    float Q;
-    int I;
-  } test_cases[] = {
-    {"iso-8859-1", "*", 1.0, 1},
-    {"iso-8859-1", "iso-8859-2", 0.0, 0},
-    {"iso-8859-1", "iso-8859", 0.0, 0},
-    {"iso-8859-1", "iso-8859-12", 0.0, 0},
-    {"iso-8859-1", "koi-8-r", 0.0, 0},
-    {"euc-jp", "shift_jis, iso-2022-jp, euc-jp;q=0.7", 0.7, 3},
-    {"euc-jp", "shift_jis, iso-2022-jp, euc-jp;q=0.7", 0.7, 3},
-    {"euc-jp", "shift_jis, iso-2022-jp, euc-jp;q=0.8, euc-jp;q=0.7", 0.8, 3},
-    {"euc-jp", "shift_jis, iso-2022-jp, euc-jp;q=0.7, euc-jp;q=0.8", 0.8, 4},
-    {"euc-jp", "euc-jp;q=0.9, shift_jis, iso-2022-jp, euc-jp;q=0.7, euc-jp;q=0.8", 0.9, 1},
-    {"EUC-JP", "euc-jp;q=0.9, shift_jis, iso-2022-jp, euc-jp, euc-jp;q=0.8", 1.0, 4},
-    {"euc-jp", "euc-jp;q=0.9, shift_jis, iso-2022-jp, EUC-JP, euc-jp;q=0.8", 1.0, 4},
-    {"euc-jp", "shift_jis, iso-2022-jp, euc-jp-foobar", 0.0, 0},
-    {"euc-jp", "shift_jis, iso-2022-jp, euc-jp-foobar, *", 1.0, 4},
-    {"euc-jp", "shift_jis, iso-2022-jp, euc-jp-foobar, *;q=0.543", 0.543, 4},
-    {"euc-jp", "shift_jis, iso-2022-jp, euc-jp-foobar, *;q=0.0", 0.0, 4},
-    {"euc-jp", "shift_jis, iso-2022-jp, *;q=0.0, euc-jp-foobar, *;q=0.0", 0.0, 3},
-    {"euc-jp", "shift_jis, iso-2022-jp, *;q=0.0, euc-jp-foobar, *;q=0.5", 0.5, 5},
-    {"euc-jp", "shift_jis, iso-2022-jp, *;q=0.5, euc-jp-foobar, *;q=0.0", 0.5, 3},
-    {"euc-jp", "shift_jis, iso-2022-jp, *;q=0.5, euc-jp-foobar, *, *;q=0.0", 1.0, 5},
-    {"euc-jp", "shift_jis, euc-jp;hi-there;q=0.5, iso-2022-jp", 0.5, 2},
-    {"euc-jp", "shift_jis, euc-jp;hi-there;q= 0.5, iso-2022-jp", 0.5, 2},
-    {"euc-jp", "shift_jis, euc-jp;hi-there;q = 0.5, iso-2022-jp", 0.5, 2},
-    {"euc-jp", "shift_jis, euc-jp;hi-there ; q = 0.5, iso-2022-jp", 0.5, 2},
-    {"euc-jp", "shift_jis, euc-jp;hi-there ;; q = 0.5, iso-2022-jp", 0.5, 2},
-    {"euc-jp", "shift_jis, euc-jp;hi-there ;; Q = 0.5, iso-2022-jp", 0.5, 2},
-    {nullptr, nullptr, 0.0, 0},
-  };
-
-  int i, I;
-  float Q;
-  int failures = 0;
-
-  for (i = 0; test_cases[i].accept_charset; i++) {
-    StrList acpt_lang_list(false);
-    HttpCompat::parse_comma_list(&acpt_lang_list, test_cases[i].accept_charset,
-                                 static_cast<int>(strlen(test_cases[i].accept_charset)));
-
-    Q = HttpCompat::match_accept_charset(test_cases[i].content_charset, static_cast<int>(strlen(test_cases[i].content_charset)),
-                                         &acpt_lang_list, &I);
-
-    if ((Q != test_cases[i].Q) || (I != test_cases[i].I)) {
-      printf("FAILED: (#%d) got { Q = %.3f; I = %d; }, expected { Q = %.3f; I = %d; }, from matching\n  '%s' against '%s'\n", i, Q,
-             I, test_cases[i].Q, test_cases[i].I, test_cases[i].content_charset, test_cases[i].accept_charset);
-      ++failures;
-    }
-  }
-
-  return (failures_to_status("test_accept_charset_match", failures));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_comma_vals()
-{
-  static struct {
-    const char *value;
-    int value_count;
-    struct {
-      int offset;
-      int len;
-    } pieces[4];
-  } tests[] = {
-    {",", 2, {{0, 0}, {1, 0}, {-1, 0}, {-1, 0}}},
-    {"", 1, {{0, 0}, {-1, 0}, {-1, 0}, {-1, 0}}},
-    {" ", 1, {{0, 0}, {-1, 0}, {-1, 0}, {-1, 0}}},
-    {", ", 2, {{0, 0}, {1, 0}, {-1, 0}, {-1, 0}}},
-    {",,", 3, {{0, 0}, {1, 0}, {2, 0}, {-1, 0}}},
-    {" ,", 2, {{0, 0}, {2, 0}, {-1, 0}, {-1, 0}}},
-    {" , ", 2, {{0, 0}, {2, 0}, {-1, 0}, {-1, 0}}},
-    {"a, ", 2, {{0, 1}, {2, 0}, {-1, 0}, {-1, 0}}},
-    {" a, ", 2, {{1, 1}, {3, 0}, {-1, 0}, {-1, 0}}},
-    {" ,a", 2, {{0, 0}, {2, 1}, {-1, 0}, {-1, 0}}},
-    {" , a", 2, {{0, 0}, {3, 1}, {-1, 0}, {-1, 0}}},
-    {"a,a", 2, {{0, 1}, {2, 1}, {-1, 0}, {-1, 0}}},
-    {"foo", 1, {{0, 3}, {-1, 0}, {-1, 0}, {-1, 0}}},
-    {"foo,", 2, {{0, 3}, {4, 0}, {-1, 0}, {-1, 0}}},
-    {"foo, ", 2, {{0, 3}, {4, 0}, {-1, 0}, {-1, 0}}},
-    {"foo, bar", 2, {{0, 3}, {5, 3}, {-1, 0}, {-1, 0}}},
-    {"foo, bar,", 3, {{0, 3}, {5, 3}, {9, 0}, {-1, 0}}},
-    {"foo, bar, ", 3, {{0, 3}, {5, 3}, {9, 0}, {-1, 0}}},
-    {
-      ",foo,bar,",
-      4,
-      {{0, 0}, {1, 3}, {5, 3}, {9, 0}},
-    },
-  };
-
-  bri_box("test_comma_vals");
-
-  HTTPHdr hdr;
-  char field_name[32];
-  int i, j, len, failures, ntests, ncommavals;
-
-  failures = 0;
-  ntests   = sizeof(tests) / sizeof(tests[0]);
-
-  hdr.create(HTTP_TYPE_REQUEST);
-
-  for (i = 0; i < ntests; i++) {
-    snprintf(field_name, sizeof(field_name), "Test%d", i);
-
-    MIMEField *f = hdr.field_create(field_name, static_cast<int>(strlen(field_name)));
-    ink_release_assert(f->m_ptr_value == nullptr);
-
-    hdr.field_attach(f);
-    ink_release_assert(f->m_ptr_value == nullptr);
-
-    hdr.field_value_set(f, tests[i].value, strlen(tests[i].value));
-    ink_release_assert(f->m_ptr_value != tests[i].value); // should be copied
-    ink_release_assert(f->m_len_value == strlen(tests[i].value));
-    ink_release_assert(memcmp(f->m_ptr_value, tests[i].value, f->m_len_value) == 0);
-
-    ncommavals = mime_field_value_get_comma_val_count(f);
-    if (ncommavals != tests[i].value_count) {
-      ++failures;
-      printf("FAILED: test #%d (field value '%s') expected val count %d, got %d\n", i + 1, tests[i].value, tests[i].value_count,
-             ncommavals);
-    }
-
-    for (j = 0; j < tests[i].value_count; j++) {
-      const char *val = mime_field_value_get_comma_val(f, &len, j);
-      int offset      = ((val == nullptr) ? -1 : (val - f->m_ptr_value));
-
-      if ((offset != tests[i].pieces[j].offset) || (len != tests[i].pieces[j].len)) {
-        ++failures;
-        printf("FAILED: test #%d (field value '%s', commaval idx %d) expected [offset %d, len %d], got [offset %d, len %d]\n",
-               i + 1, tests[i].value, j, tests[i].pieces[j].offset, tests[i].pieces[j].len, offset, len);
-      }
-    }
-  }
-
-  hdr.destroy();
-  return (failures_to_status("test_comma_vals", failures));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_set_comma_vals()
-{
-  static struct {
-    const char *old_raw;
-    int idx;
-    const char *slice;
-    const char *new_raw;
-  } tests[] = {
-    {"a,b,c", 0, "fred", "fred, b, c"},
-    {"a,b,c", 1, "fred", "a, fred, c"},
-    {"a,b,c", 2, "fred", "a, b, fred"},
-    {"a,b,c", 3, "fred", "a,b,c"},
-    {"", 0, "", ""},
-    {"", 0, "foo", "foo"},
-    {"", 1, "foo", ""},
-    {" ", 0, "", ""},
-    {" ", 0, "foo", "foo"},
-    {" ", 1, "foo", " "},
-    {",", 0, "foo", "foo, "},
-    {",", 1, "foo", ", foo"},
-    {",,", 0, "foo", "foo, , "},
-    {",,", 1, "foo", ", foo, "},
-    {",,", 2, "foo", ", , foo"},
-    {"foo", 0, "abc", "abc"},
-    {"foo", 1, "abc", "foo"},
-    {"foo", 0, "abc,", "abc,"},
-    {"foo", 0, ",abc", ",abc"},
-    {",,", 1, ",,,", ", ,,,, "},
-    {" a , b , c", 0, "fred", "fred, b, c"},
-    {" a , b , c", 1, "fred", "a, fred, c"},
-    {" a , b , c", 2, "fred", "a, b, fred"},
-    {" a , b , c", 3, "fred", " a , b , c"},
-    {"    a   ,   b ", 0, "fred", "fred, b"},
-    {"    a   ,   b ", 1, "fred", "a, fred"},
-    {"    a   , b ", 1, "fred", "a, fred"},
-    {"    a   ,b ", 1, "fred", "a, fred"},
-    {"a, , , , e, , g,", 0, "fred", "fred, , , , e, , g, "},
-    {"a, , , , e, , g,", 1, "fred", "a, fred, , , e, , g, "},
-    {"a, , , , e, , g,", 2, "fred", "a, , fred, , e, , g, "},
-    {"a, , , , e, , g,", 5, "fred", "a, , , , e, fred, g, "},
-    {"a, , , , e, , g,", 7, "fred", "a, , , , e, , g, fred"},
-    {"a, , , , e, , g,", 8, "fred", "a, , , , e, , g,"},
-    {"a, \"boo,foo\", c", 0, "wawa", "wawa, \"boo,foo\", c"},
-    {"a, \"boo,foo\", c", 1, "wawa", "a, wawa, c"},
-    {"a, \"boo,foo\", c", 2, "wawa", "a, \"boo,foo\", wawa"},
-  };
-
-  bri_box("test_set_comma_vals");
-
-  HTTPHdr hdr;
-  char field_name[32];
-  int i, failures, ntests;
-
-  failures = 0;
-  ntests   = sizeof(tests) / sizeof(tests[0]);
-
-  hdr.create(HTTP_TYPE_REQUEST);
-
-  for (i = 0; i < ntests; i++) {
-    snprintf(field_name, sizeof(field_name), "Test%d", i);
-
-    MIMEField *f = hdr.field_create(field_name, static_cast<int>(strlen(field_name)));
-    hdr.field_value_set(f, tests[i].old_raw, strlen(tests[i].old_raw));
-    mime_field_value_set_comma_val(hdr.m_heap, hdr.m_mime, f, tests[i].idx, tests[i].slice, strlen(tests[i].slice));
-    ink_release_assert(f->m_ptr_value != nullptr);
-
-    if ((f->m_len_value != strlen(tests[i].new_raw)) || (memcmp(f->m_ptr_value, tests[i].new_raw, f->m_len_value) != 0)) {
-      ++failures;
-      printf("FAILED:  test #%d (setting idx %d of '%s' to '%s') expected '%s' len %d, got '%.*s' len %d\n", i + 1, tests[i].idx,
-             tests[i].old_raw, tests[i].slice, tests[i].new_raw, static_cast<int>(strlen(tests[i].new_raw)), f->m_len_value,
-             f->m_ptr_value, f->m_len_value);
-    }
-  }
-
-  hdr.destroy();
-  return (failures_to_status("test_set_comma_vals", failures));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_delete_comma_vals()
-{
-  bri_box("test_delete_comma_vals");
-  rprintf(rtest, "  HdrTest test_delete_comma_vals: TEST NOT IMPLEMENTED\n");
-  return (1);
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_extend_comma_vals()
-{
-  bri_box("test_extend_comma_vals");
-  rprintf(rtest, "  HdrTest test_extend_comma_vals: TEST NOT IMPLEMENTED\n");
-  return (1);
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_insert_comma_vals()
-{
-  bri_box("test_insert_comma_vals");
-  rprintf(rtest, "  HdrTest test_insert_comma_vals: TEST NOT IMPLEMENTED\n");
-  return (1);
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::test_parse_comma_list()
-{
-  static struct {
-    const char *value;
-    int count;
-    struct {
-      int offset;
-      int len;
-    } pieces[3];
-  } tests[] = {
-    {"", 1, {{0, 0}, {-1, 0}, {-1, 0}}},
-    {",", 2, {{0, 0}, {1, 0}, {-1, 0}}},
-    {" ,", 2, {{0, 0}, {2, 0}, {-1, 0}}},
-    {", ", 2, {{0, 0}, {1, 0}, {-1, 0}}},
-    {" , ", 2, {{0, 0}, {2, 0}, {-1, 0}}},
-    {"abc,", 2, {{0, 3}, {4, 0}, {-1, 0}}},
-    {"abc, ", 2, {{0, 3}, {4, 0}, {-1, 0}}},
-    {"", 1, {{0, 0}, {-1, 0}, {-1, 0}}},
-    {" ", 1, {{0, 0}, {-1, 0}, {-1, 0}}},
-    {"  ", 1, {{0, 0}, {-1, 0}, {-1, 0}}},
-    {"a", 1, {{0, 1}, {-1, 0}, {-1, 0}}},
-    {" a", 1, {{1, 1}, {-1, 0}, {-1, 0}}},
-    {"  a  ", 1, {{2, 1}, {-1, 0}, {-1, 0}}},
-    {"abc,defg", 2, {{0, 3}, {4, 4}, {-1, 0}}},
-    {" abc,defg", 2, {{1, 3}, {5, 4}, {-1, 0}}},
-    {" abc, defg", 2, {{1, 3}, {6, 4}, {-1, 0}}},
-    {" abc , defg", 2, {{1, 3}, {7, 4}, {-1, 0}}},
-    {" abc , defg ", 2, {{1, 3}, {7, 4}, {-1, 0}}},
-    {" abc , defg, ", 3, {{1, 3}, {7, 4}, {12, 0}}},
-    {" abc , defg ,", 3, {{1, 3}, {7, 4}, {13, 0}}},
-    {", abc , defg ", 3, {{0, 0}, {2, 3}, {8, 4}}},
-    {" ,abc , defg ", 3, {{0, 0}, {2, 3}, {8, 4}}},
-    {"a,b", 2, {{0, 1}, {2, 1}, {-1, 0}}},
-    {"a,,b", 3, {{0, 1}, {2, 0}, {3, 1}}},
-    {"a, ,b", 3, {{0, 1}, {2, 0}, {4, 1}}},
-    {"a ,,b", 3, {{0, 1}, {3, 0}, {4, 1}}},
-    {",", 2, {{0, 0}, {1, 0}, {-1, 0}}},
-    {" ,", 2, {{0, 0}, {2, 0}, {-1, 0}}},
-    {", ", 2, {{0, 0}, {1, 0}, {-1, 0}}},
-    {" , ", 2, {{0, 0}, {2, 0}, {-1, 0}}},
-    {"a,b,", 3, {{0, 1}, {2, 1}, {4, 0}}},
-    {"a,b, ", 3, {{0, 1}, {2, 1}, {4, 0}}},
-    {"a,b,  ", 3, {{0, 1}, {2, 1}, {4, 0}}},
-    {"a,b,  c", 3, {{0, 1}, {2, 1}, {6, 1}}},
-    {"a,b,  c ", 3, {{0, 1}, {2, 1}, {6, 1}}},
-    {"a,\"b,c\",d", 3, {{0, 1}, {3, 3}, {8, 1}}},
-  };
-
-  bri_box("test_parse_comma_list");
-
-  int i, j, failures, ntests, offset;
-
-  failures = (offset = 0);
-  ntests   = sizeof(tests) / sizeof(tests[0]);
-
-  for (i = 0; i < ntests; i++) {
-    StrList list(false);
-    HttpCompat::parse_comma_list(&list, tests[i].value);
-    if (list.count != tests[i].count) {
-      ++failures;
-      printf("FAILED: test #%d (string '%s') expected list count %d, got %d\n", i + 1, tests[i].value, tests[i].count, list.count);
-    }
-
-    for (j = 0; j < tests[i].count; j++) {
-      Str *cell = list.get_idx(j);
-      if (cell != nullptr) {
-        offset = cell->str - tests[i].value;
-      }
-
-      if (tests[i].pieces[j].offset == -1) // should not have a piece
-      {
-        if (cell != nullptr) {
-          ++failures;
-          printf("FAILED: test #%d (string '%s', idx %d) expected NULL piece, got [offset %d len %d]\n", i + 1, tests[i].value, j,
-                 offset, static_cast<int>(cell->len));
-        }
-      } else // should have a piece
-      {
-        if (cell == nullptr) {
-          ++failures;
-          printf("FAILED: test #%d (string '%s', idx %d) expected [offset %d len %d], got NULL piece\n", i + 1, tests[i].value, j,
-                 tests[i].pieces[j].offset, tests[i].pieces[j].len);
-        } else if ((offset != tests[i].pieces[j].offset) || (cell->len != static_cast<size_t>(tests[i].pieces[j].len))) {
-          ++failures;
-          printf("FAILED: test #%d (string '%s', idx %d) expected [offset %d len %d], got [offset %d len %d]\n", i + 1,
-                 tests[i].value, j, tests[i].pieces[j].offset, tests[i].pieces[j].len, offset, static_cast<int>(cell->len));
-        }
-      }
-    }
-  }
-
-  return (failures_to_status("test_parse_comma_list", failures));
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-void
-HdrTest::bri_box(const char *s)
-{
-  int i, len;
-
-  len = static_cast<int>(strlen(s));
-  printf("\n+-");
-  for (i = 0; i < len; i++) {
-    putchar('-');
-  }
-  printf("-+\n");
-  printf("| %s |\n", s);
-  printf("+-");
-  for (i = 0; i < len; i++) {
-    putchar('-');
-  }
-  printf("-+\n\n");
-}
-
-/*-------------------------------------------------------------------------
-  -------------------------------------------------------------------------*/
-
-int
-HdrTest::failures_to_status(const char *testname, int nfail)
-{
-  rprintf(rtest, "  HdrTest %s: %s\n", testname, ((nfail > 0) ? "FAILED" : "PASSED"));
-  return ((nfail > 0) ? 0 : 1);
-}
diff --git a/proxy/hdrs/HdrTest.h b/proxy/hdrs/HdrTest.h
deleted file mode 100644
index 76128fa..0000000
--- a/proxy/hdrs/HdrTest.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/** @file
-
-  A brief file description
-
-  @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.
- */
-
-/****************************************************************************
-
-   HdrTest.cc
-
-   Description:
-       Unit test code for sanity checking the header system is functioning
-         properly
-
-
- ****************************************************************************/
-
-#pragma once
-
-#include "tscore/Regression.h"
-
-class HdrTest
-{
-public:
-  RegressionTest *rtest = nullptr;
-
-  HdrTest(){};
-  ~HdrTest(){};
-
-  int go(RegressionTest *t, int atype);
-
-private:
-  int test_http_hdr_print_and_copy();
-  int test_parse_date();
-  int test_format_date();
-  int test_url();
-  int test_arena();
-  int test_regex();
-  int test_accept_language_match();
-  int test_accept_charset_match();
-  int test_comma_vals();
-  int test_set_comma_vals();
-  int test_delete_comma_vals();
-  int test_extend_comma_vals();
-  int test_insert_comma_vals();
-  int test_parse_comma_list();
-  int test_mime();
-  int test_http();
-  int test_http_mutation();
-
-  int test_http_hdr_print_and_copy_aux(int testnum, const char *req, const char *req_tgt, const char *rsp, const char *rsp_tgt);
-  int test_http_hdr_null_char(int testnum, const char *req, const char *req_tgt);
-  int test_http_hdr_ctl_char(int testnum, const char *req, const char *req_tgt);
-  int test_http_hdr_copy_over_aux(int testnum, const char *request, const char *response);
-  int test_http_aux(const char *request, const char *response);
-  int test_http_req_parse_error(const char *request, const char *response);
-  int test_arena_aux(Arena *arena, int len);
-  void bri_box(const char *s);
-  int failures_to_status(const char *testname, int nfail);
-};
diff --git a/proxy/hdrs/Makefile.am b/proxy/hdrs/Makefile.am
index 7067f55..950dd2b 100644
--- a/proxy/hdrs/Makefile.am
+++ b/proxy/hdrs/Makefile.am
@@ -49,12 +49,6 @@ libhdrs_a_SOURCES = \
 	XPACK.cc \
 	XPACK.h
 
-if BUILD_TESTS
-libhdrs_a_SOURCES += \
-	HdrTest.cc \
-	HdrTest.h
-endif
-
 load_http_hdr_SOURCES = \
 	HTTP.h \
 	HdrHeap.h \
diff --git a/proxy/hdrs/unit_tests/test_Hdrs.cc b/proxy/hdrs/unit_tests/test_Hdrs.cc
index a471339..dcbbd83 100644
--- a/proxy/hdrs/unit_tests/test_Hdrs.cc
+++ b/proxy/hdrs/unit_tests/test_Hdrs.cc
@@ -26,13 +26,19 @@
 #include <initializer_list>
 #include <array>
 #include <new>
+#include <cstdio>
+#include <memory>
+
+#include "tscore/Regex.h"
+#include "tscore/ink_time.h"
 
 #include "catch.hpp"
 
 #include "HTTP.h"
+#include "HttpCompat.h"
 
 // replaces test_http_parser_eos_boundary_cases
-TEST_CASE("HdrTest", "[proxy][hdrtest]")
+TEST_CASE("HdrTestHttpParse", "[proxy][hdrtest]")
 {
   struct Test {
     ts::TextView msg;
@@ -115,3 +121,1915 @@ TEST_CASE("MIMEScanner_fragments", "[proxy][mimescanner_fragments]")
 
   REQUIRE(message == output);
 }
+
+namespace
+{
+static const char *
+comp_http_hdr(HTTPHdr *h1, HTTPHdr *h2)
+{
+  int h1_len = h1->length_get();
+  int h2_len = h2->length_get();
+
+  if (h1_len != h2_len) {
+    return "length mismatch";
+  }
+
+  std::unique_ptr<char[]> h1_pbuf(new char[h1_len + 1]);
+  std::unique_ptr<char[]> h2_pbuf(new char[h2_len + 1]);
+
+  int p_index = 0, p_dumpoffset = 0;
+
+  int rval = h1->print(h1_pbuf.get(), h1_len, &p_index, &p_dumpoffset);
+  if (rval != 1) {
+    return "hdr print failed";
+  }
+
+  p_index = p_dumpoffset = 0;
+  rval                   = h2->print(h2_pbuf.get(), h2_len, &p_index, &p_dumpoffset);
+  if (rval != 1) {
+    return "hdr print failed";
+  }
+
+  rval = memcmp(h1_pbuf.get(), h2_pbuf.get(), h1_len);
+
+  if (rval != 0) {
+    return "compare failed";
+  } else {
+    return nullptr;
+  }
+}
+
+int
+test_http_hdr_copy_over_aux(int testnum, const char *request, const char *response)
+{
+  int err;
+  HTTPHdr req_hdr;
+  HTTPHdr resp_hdr;
+  HTTPHdr copy1;
+  HTTPHdr copy2;
+
+  HTTPParser parser;
+  const char *start;
+  const char *end;
+  const char *comp_str = nullptr;
+
+  /*** (1) parse the request string into hdr ***/
+
+  req_hdr.create(HTTP_TYPE_REQUEST);
+
+  start = request;
+  end   = start + strlen(start); // 1 character past end of string
+
+  http_parser_init(&parser);
+
+  while (true) {
+    err = req_hdr.parse_req(&parser, &start, end, true);
+    if (err != PARSE_RESULT_CONT) {
+      break;
+    }
+  }
+
+  if (err == PARSE_RESULT_ERROR) {
+    std::printf("FAILED: (test #%d) parse error parsing request hdr\n", testnum);
+    return (0);
+  }
+  http_parser_clear(&parser);
+
+  /*** (2) parse the response string into hdr ***/
+
+  resp_hdr.create(HTTP_TYPE_RESPONSE);
+
+  start = response;
+  end   = start + strlen(start); // 1 character past end of string
+
+  http_parser_init(&parser);
+
+  while (true) {
+    err = resp_hdr.parse_resp(&parser, &start, end, true);
+    if (err != PARSE_RESULT_CONT) {
+      break;
+    }
+  }
+
+  if (err == PARSE_RESULT_ERROR) {
+    printf("FAILED: (test #%d) parse error parsing response hdr\n", testnum);
+    return (0);
+  }
+
+  /*** (3) Basic copy testing ***/
+  copy1.create(HTTP_TYPE_REQUEST);
+  copy1.copy(&req_hdr);
+  comp_str = comp_http_hdr(&req_hdr, &copy1);
+  if (comp_str) {
+    goto done;
+  }
+
+  copy2.create(HTTP_TYPE_RESPONSE);
+  copy2.copy(&resp_hdr);
+  comp_str = comp_http_hdr(&resp_hdr, &copy2);
+  if (comp_str) {
+    goto done;
+  }
+
+  // The APIs for copying headers uses memcpy() which can be unsafe for
+  // overlapping memory areas. It's unclear to me why these tests were
+  // created in the first place honestly, since nothing else does this.
+
+  /*** (4) Gender bending copying ***/
+  copy1.copy(&resp_hdr);
+  comp_str = comp_http_hdr(&resp_hdr, &copy1);
+  if (comp_str) {
+    goto done;
+  }
+
+  copy2.copy(&req_hdr);
+  comp_str = comp_http_hdr(&req_hdr, &copy2);
+  if (comp_str) {
+    goto done;
+  }
+
+done:
+  req_hdr.destroy();
+  resp_hdr.destroy();
+  copy1.destroy();
+  copy2.destroy();
+
+  if (comp_str) {
+    printf("FAILED: (test #%d) copy & compare: %s\n", testnum, comp_str);
+    printf("REQ:\n[%.*s]\n", static_cast<int>(strlen(request)), request);
+    printf("RESP  :\n[%.*s]\n", static_cast<int>(strlen(response)), response);
+    return (0);
+  } else {
+    return (1);
+  }
+}
+
+int
+test_http_hdr_null_char(int testnum, const char *request, const char * /*request_tgt*/)
+{
+  int err;
+  HTTPHdr hdr;
+  HTTPParser parser;
+  const char *start;
+  char cpy_buf[2048];
+  const char *cpy_buf_ptr = cpy_buf;
+
+  /*** (1) parse the request string into hdr ***/
+
+  hdr.create(HTTP_TYPE_REQUEST);
+
+  start = request;
+
+  if (strlen(start) > sizeof(cpy_buf)) {
+    std::printf("FAILED: (test #%d) Internal buffer too small for null char test\n", testnum);
+    return (0);
+  }
+  strcpy(cpy_buf, start);
+
+  // Put a null character somewhere in the header
+  int length          = strlen(start);
+  cpy_buf[length / 2] = '\0';
+  http_parser_init(&parser);
+
+  while (true) {
+    err = hdr.parse_req(&parser, &cpy_buf_ptr, cpy_buf_ptr + length, true);
+    if (err != PARSE_RESULT_CONT) {
+      break;
+    }
+  }
+  if (err != PARSE_RESULT_ERROR) {
+    std::printf("FAILED: (test #%d) no parse error parsing request with null char\n", testnum);
+    return (0);
+  }
+  return 1;
+}
+
+int
+test_http_hdr_ctl_char(int testnum, const char *request, const char * /*request_tgt */)
+{
+  int err;
+  HTTPHdr hdr;
+  HTTPParser parser;
+  const char *start;
+  char cpy_buf[2048];
+  const char *cpy_buf_ptr = cpy_buf;
+
+  /*** (1) parse the request string into hdr ***/
+
+  hdr.create(HTTP_TYPE_REQUEST);
+
+  start = request;
+
+  if (strlen(start) > sizeof(cpy_buf)) {
+    std::printf("FAILED: (test #%d) Internal buffer too small for ctl char test\n", testnum);
+    return (0);
+  }
+  strcpy(cpy_buf, start);
+
+  // Replace a character in the method
+  cpy_buf[1] = 16;
+
+  http_parser_init(&parser);
+
+  while (true) {
+    err = hdr.parse_req(&parser, &cpy_buf_ptr, cpy_buf_ptr + strlen(start), true);
+    if (err != PARSE_RESULT_CONT) {
+      break;
+    }
+  }
+
+  if (err != PARSE_RESULT_ERROR) {
+    std::printf("FAILED: (test #%d) no parse error parsing method with ctl char\n", testnum);
+    return (0);
+  }
+  return 1;
+}
+
+int
+test_http_hdr_print_and_copy_aux(int testnum, const char *request, const char *request_tgt, const char *response,
+                                 const char *response_tgt)
+{
+  int err;
+  HTTPHdr hdr;
+  HTTPParser parser;
+  const char *start;
+  const char *end;
+
+  char prt_buf[2048];
+  int prt_bufsize = sizeof(prt_buf);
+  int prt_bufindex, prt_dumpoffset, prt_ret;
+
+  char cpy_buf[2048];
+  int cpy_bufsize = sizeof(cpy_buf);
+  int cpy_bufindex, cpy_dumpoffset, cpy_ret;
+
+  std::unique_ptr<char[]> marshal_buf(new char[2048]);
+  int marshal_bufsize = sizeof(cpy_buf);
+
+  /*** (1) parse the request string into hdr ***/
+
+  hdr.create(HTTP_TYPE_REQUEST);
+
+  start = request;
+  end   = start + strlen(start); // 1 character past end of string
+
+  http_parser_init(&parser);
+
+  while (true) {
+    err = hdr.parse_req(&parser, &start, end, true);
+    if (err != PARSE_RESULT_CONT) {
+      break;
+    }
+  }
+
+  if (err == PARSE_RESULT_ERROR) {
+    std::printf("FAILED: (test #%d) parse error parsing request hdr\n", testnum);
+    return (0);
+  }
+
+  /*** (2) copy the request header ***/
+  HTTPHdr new_hdr, marshal_hdr;
+  RefCountObj ref;
+
+  // Pretend to pin this object with a refcount.
+  ref.refcount_inc();
+
+  int marshal_len = hdr.m_heap->marshal(marshal_buf.get(), marshal_bufsize);
+  marshal_hdr.create(HTTP_TYPE_REQUEST);
+  marshal_hdr.unmarshal(marshal_buf.get(), marshal_len, &ref);
+  new_hdr.create(HTTP_TYPE_REQUEST);
+  new_hdr.copy(&marshal_hdr);
+
+  /*** (3) print the request header and copy to buffers ***/
+
+  prt_bufindex = prt_dumpoffset = 0;
+  prt_ret                       = hdr.print(prt_buf, prt_bufsize, &prt_bufindex, &prt_dumpoffset);
+
+  cpy_bufindex = cpy_dumpoffset = 0;
+  cpy_ret                       = new_hdr.print(cpy_buf, cpy_bufsize, &cpy_bufindex, &cpy_dumpoffset);
+
+  if ((prt_ret != 1) || (cpy_ret != 1)) {
+    std::printf("FAILED: (test #%d) couldn't print req hdr or copy --- prt_ret=%d, cpy_ret=%d\n", testnum, prt_ret, cpy_ret);
+    return (0);
+  }
+
+  if ((static_cast<size_t>(prt_bufindex) != strlen(request_tgt)) || (static_cast<size_t>(cpy_bufindex) != strlen(request_tgt))) {
+    std::printf("FAILED: (test #%d) print req output size mismatch --- tgt=%d, prt_bufsize=%d, cpy_bufsize=%d\n", testnum,
+                static_cast<int>(strlen(request_tgt)), prt_bufindex, cpy_bufindex);
+
+    std::printf("ORIGINAL:\n[%.*s]\n", static_cast<int>(strlen(request)), request);
+    std::printf("TARGET  :\n[%.*s]\n", static_cast<int>(strlen(request_tgt)), request_tgt);
+    std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf);
+    std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf);
+    return (0);
+  }
+
+  if ((strncasecmp(request_tgt, prt_buf, strlen(request_tgt)) != 0) ||
+      (strncasecmp(request_tgt, cpy_buf, strlen(request_tgt)) != 0)) {
+    std::printf("FAILED: (test #%d) print req output mismatch\n", testnum);
+    std::printf("ORIGINAL:\n[%.*s]\n", static_cast<int>(strlen(request)), request);
+    std::printf("TARGET  :\n[%.*s]\n", static_cast<int>(strlen(request_tgt)), request_tgt);
+    std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf);
+    std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf);
+    return (0);
+  }
+
+  hdr.destroy();
+  new_hdr.destroy();
+
+  /*** (4) parse the response string into hdr ***/
+
+  hdr.create(HTTP_TYPE_RESPONSE);
+
+  start = response;
+  end   = start + strlen(start); // 1 character past end of string
+
+  http_parser_init(&parser);
+
+  while (true) {
+    err = hdr.parse_resp(&parser, &start, end, true);
+    if (err != PARSE_RESULT_CONT) {
+      break;
+    }
+  }
+
+  if (err == PARSE_RESULT_ERROR) {
+    std::printf("FAILED: (test #%d) parse error parsing response hdr\n", testnum);
+    return (0);
+  }
+
+  /*** (2) copy the response header ***/
+
+  new_hdr.create(HTTP_TYPE_RESPONSE);
+  new_hdr.copy(&hdr);
+
+  /*** (3) print the response header and copy to buffers ***/
+
+  prt_bufindex = prt_dumpoffset = 0;
+  prt_ret                       = hdr.print(prt_buf, prt_bufsize, &prt_bufindex, &prt_dumpoffset);
+
+  cpy_bufindex = cpy_dumpoffset = 0;
+  cpy_ret                       = new_hdr.print(cpy_buf, cpy_bufsize, &cpy_bufindex, &cpy_dumpoffset);
+
+  if ((prt_ret != 1) || (cpy_ret != 1)) {
+    std::printf("FAILED: (test #%d) couldn't print rsp hdr or copy --- prt_ret=%d, cpy_ret=%d\n", testnum, prt_ret, cpy_ret);
+    return (0);
+  }
+
+  if ((static_cast<size_t>(prt_bufindex) != strlen(response_tgt)) || (static_cast<size_t>(cpy_bufindex) != strlen(response_tgt))) {
+    std::printf("FAILED: (test #%d) print rsp output size mismatch --- tgt=%d, prt_bufsize=%d, cpy_bufsize=%d\n", testnum,
+                static_cast<int>(strlen(response_tgt)), prt_bufindex, cpy_bufindex);
+    std::printf("ORIGINAL:\n[%.*s]\n", static_cast<int>(strlen(response)), response);
+    std::printf("TARGET  :\n[%.*s]\n", static_cast<int>(strlen(response_tgt)), response_tgt);
+    std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf);
+    std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf);
+    return (0);
+  }
+
+  if ((strncasecmp(response_tgt, prt_buf, strlen(response_tgt)) != 0) ||
+      (strncasecmp(response_tgt, cpy_buf, strlen(response_tgt)) != 0)) {
+    std::printf("FAILED: (test #%d) print rsp output mismatch\n", testnum);
+    std::printf("ORIGINAL:\n[%.*s]\n", static_cast<int>(strlen(response)), response);
+    std::printf("TARGET  :\n[%.*s]\n", static_cast<int>(strlen(response_tgt)), response_tgt);
+    std::printf("PRT_BUFF:\n[%.*s]\n", prt_bufindex, prt_buf);
+    std::printf("CPY_BUFF:\n[%.*s]\n", cpy_bufindex, cpy_buf);
+    return (0);
+  }
+
+  hdr.destroy();
+  new_hdr.destroy();
+
+  if (test_http_hdr_copy_over_aux(testnum, request, response) == 0) {
+    return 0;
+  }
+
+  return (1);
+}
+
+int
+test_arena_aux(Arena *arena, int len)
+{
+  char *str      = arena->str_alloc(len);
+  int verify_len = static_cast<int>(arena->str_length(str));
+
+  if (len != verify_len) {
+    std::printf("FAILED: requested %d, got %d bytes\n", len, verify_len);
+    return (1); // 1 error (different return convention)
+  } else {
+    return (0); // no errors (different return convention)
+  }
+}
+
+} // end anonymous namespace
+
+TEST_CASE("HdrTest", "[proxy][hdrtest]")
+{
+  hdrtoken_init();
+  url_init();
+  mime_init();
+  http_init();
+
+  SECTION("Test parse date")
+  {
+    static struct {
+      const char *fast;
+      const char *slow;
+    } dates[] = {
+      {"Sun, 06 Nov 1994 08:49:37 GMT", "Sunday, 06-Nov-1994 08:49:37 GMT"},
+      {"Mon, 07 Nov 1994 08:49:37 GMT", "Monday, 07-Nov-1994 08:49:37 GMT"},
+      {"Tue, 08 Nov 1994 08:49:37 GMT", "Tuesday, 08-Nov-1994 08:49:37 GMT"},
+      {"Wed, 09 Nov 1994 08:49:37 GMT", "Wednesday, 09-Nov-1994 08:49:37 GMT"},
+      {"Thu, 10 Nov 1994 08:49:37 GMT", "Thursday, 10-Nov-1994 08:49:37 GMT"},
+      {"Fri, 11 Nov 1994 08:49:37 GMT", "Friday, 11-Nov-1994 08:49:37 GMT"},
+      {"Sat, 11 Nov 1994 08:49:37 GMT", "Saturday, 11-Nov-1994 08:49:37 GMT"},
+      {"Sun, 03 Jan 1999 08:49:37 GMT", "Sunday, 03-Jan-1999 08:49:37 GMT"},
+      {"Sun, 07 Feb 1999 08:49:37 GMT", "Sunday, 07-Feb-1999 08:49:37 GMT"},
+      {"Sun, 07 Mar 1999 08:49:37 GMT", "Sunday, 07-Mar-1999 08:49:37 GMT"},
+      {"Sun, 04 Apr 1999 08:49:37 GMT", "Sunday, 04-Apr-1999 08:49:37 GMT"},
+      {"Sun, 02 May 1999 08:49:37 GMT", "Sunday, 02-May-1999 08:49:37 GMT"},
+      {"Sun, 06 Jun 1999 08:49:37 GMT", "Sunday, 06-Jun-1999 08:49:37 GMT"},
+      {"Sun, 04 Jul 1999 08:49:37 GMT", "Sunday, 04-Jul-1999 08:49:37 GMT"},
+      {"Sun, 01 Aug 1999 08:49:37 GMT", "Sunday, 01-Aug-1999 08:49:37 GMT"},
+      {"Sun, 05 Sep 1999 08:49:37 GMT", "Sunday, 05-Sep-1999 08:49:37 GMT"},
+      {"Sun, 03 Oct 1999 08:49:37 GMT", "Sunday, 03-Oct-1999 08:49:37 GMT"},
+      {"Sun, 07 Nov 1999 08:49:37 GMT", "Sunday, 07-Nov-1999 08:49:37 GMT"},
+      {"Sun, 05 Dec 1999 08:49:37 GMT", "Sunday, 05-Dec-1999 08:49:37 GMT"},
+      {nullptr, nullptr},
+    };
+
+    int i;
+    time_t fast_t, slow_t;
+
+    for (i = 0; dates[i].fast; i++) {
+      fast_t = mime_parse_date(dates[i].fast, dates[i].fast + static_cast<int>(strlen(dates[i].fast)));
+      slow_t = mime_parse_date(dates[i].slow, dates[i].slow + static_cast<int>(strlen(dates[i].slow)));
+      // compare with strptime here!
+      if (fast_t != slow_t) {
+        std::printf("FAILED: date %lu (%s) != %lu (%s)\n", static_cast<unsigned long>(fast_t), dates[i].fast,
+                    static_cast<unsigned long>(slow_t), dates[i].slow);
+        CHECK(false);
+      }
+    }
+  }
+
+  SECTION("Test format date")
+  {
+    static const char *dates[] = {
+      "Sun, 06 Nov 1994 08:49:37 GMT",
+      "Sun, 03 Jan 1999 08:49:37 GMT",
+      "Sun, 05 Dec 1999 08:49:37 GMT",
+      "Tue, 25 Apr 2000 20:29:53 GMT",
+      nullptr,
+    };
+
+    // (1) Test a few hand-created dates
+
+    int i;
+    time_t t, t2, t3;
+    char buffer[128], buffer2[128];
+    static const char *envstr = "TZ=GMT0";
+
+    // shift into GMT timezone for cftime conversions
+    putenv(const_cast<char *>(envstr));
+    tzset();
+
+    for (i = 0; dates[i]; i++) {
+      t = mime_parse_date(dates[i], dates[i] + static_cast<int>(strlen(dates[i])));
+
+      cftime_replacement(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", &t);
+      if (memcmp(dates[i], buffer, 29) != 0) {
+        std::printf("FAILED: original date doesn't match cftime date\n");
+        std::printf("  input date:  %s\n", dates[i]);
+        std::printf("  cftime date: %s\n", buffer);
+        CHECK(false);
+      }
+
+      mime_format_date(buffer, t);
+      if (memcmp(dates[i], buffer, 29) != 0) {
+        std::printf("FAILED: original date doesn't match mime_format_date date\n");
+        std::printf("  input date:  %s\n", dates[i]);
+        std::printf("  cftime date: %s\n", buffer);
+        CHECK(false);
+      }
+    }
+
+    // (2) test a few times per day from 1/1/1970 to past 2010
+
+    // coverity[dont_call]
+    for (t = 0; t < 40 * 366 * (24 * 60 * 60); t += static_cast<int>(drand48() * (24 * 60 * 60))) {
+      cftime_replacement(buffer, sizeof(buffer), "%a, %d %b %Y %T %Z", &t);
+      t2 = mime_parse_date(buffer, buffer + static_cast<int>(strlen(buffer)));
+      if (t2 != t) {
+        std::printf("FAILED: parsed time_t doesn't match original time_t\n");
+        std::printf("  input time_t:  %d (%s)\n", static_cast<int>(t), buffer);
+        std::printf("  parsed time_t: %d\n", static_cast<int>(t2));
+        CHECK(false);
+      }
+      mime_format_date(buffer2, t);
+      if (memcmp(buffer, buffer2, 29) != 0) {
+        std::printf("FAILED: formatted date doesn't match original date\n");
+        std::printf("  original date:  %s\n", buffer);
+        std::printf("  formatted date: %s\n", buffer2);
+        CHECK(false);
+      }
+      t3 = mime_parse_date(buffer2, buffer2 + static_cast<int>(strlen(buffer2)));
+      if (t != t3) {
+        std::printf("FAILED: parsed time_t doesn't match original time_t\n");
+        std::printf("  input time_t:  %d (%s)\n", static_cast<int>(t), buffer2);
+        std::printf("  parsed time_t: %d\n", static_cast<int>(t3));
+        CHECK(false);
+      }
+    }
+  }
+
+  SECTION("Test url")
+  {
+    static const char *strs[] = {
+      "http://some.place/path;params?query#fragment",
+
+      // Start with an easy one...
+      "http://trafficserver.apache.org/index.html",
+
+      "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 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/",
+
+      // "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",
+
+      "ht-tp://a.b.com",
+      "ht+tp://a.b.com",
+      "ht.tp://a.b.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 = 0;
+    URL url;
+    const char *start;
+    const char *end;
+    int old_length, new_length;
+
+    for (unsigned i = 0; i < countof(strs); i++) {
+      old_length = static_cast<int>(strlen(strs[i]));
+      start      = strs[i];
+      end        = start + old_length;
+
+      url.create(nullptr);
+      err = url.parse(&start, end);
+      if (err < 0) {
+        std::printf("Failed to parse url '%s'\n", start);
+        failed = 1;
+        break;
+      }
+
+      char print_buf[1024];
+      new_length = 0;
+      int offset = 0;
+      url.print(print_buf, 1024, &new_length, &offset);
+      print_buf[new_length] = '\0';
+
+      const char *fail_text = nullptr;
+
+      if (old_length == new_length) {
+        if (memcmp(print_buf, strs[i], new_length) != 0) {
+          fail_text = "URLS DIFFER";
+        }
+      } else if (old_length == new_length - 1) {
+        // Check to see if the difference is the trailing
+        //   slash we add
+        if (memcmp(print_buf, strs[i], old_length) != 0 || print_buf[new_length - 1] != '/' || (strs[i])[old_length - 1] == '/') {
+          fail_text = "TRAILING SLASH";
+        }
+      } else {
+        fail_text = "LENGTHS DIFFER";
+      }
+
+      if (fail_text) {
+        failed = 1;
+        std::printf("%16s: OLD: (%4d) %s\n", fail_text, old_length, strs[i]);
+        std::printf("%16s: NEW: (%4d) %s\n", "", new_length, print_buf);
+        obj_describe(url.m_url_impl, true);
+      } else {
+        std::printf("%16s: '%s'\n", "PARSE SUCCESS", strs[i]);
+      }
+
+      url.destroy();
+    }
+
+    for (unsigned i = 0; i < countof(bad); ++i) {
+      const char *x = bad[i];
+
+      url.create(nullptr);
+      err = url.parse(x, strlen(x));
+      url.destroy();
+      if (err == PARSE_RESULT_DONE) {
+        failed = 1;
+        std::printf("Successfully parsed invalid url '%s'", x);
+        break;
+      } else {
+        std::printf("   bad URL - PARSE FAILED: '%s'\n", bad[i]);
+      }
+    }
+
+    CHECK(failed == 0);
+  }
+
+  SECTION("Test mime")
+  {
+    // This can not be a static string (any more) since we unfold the headers
+    // in place.
+    char mime[] = {
+      //        "Date: Tuesday, 08-Dec-98 20:32:17 GMT\r\n"
+      "Date: 6 Nov 1994 08:49:37 GMT\r\n"
+      "Max-Forwards: 65535\r\n"
+      "Cache-Control: private\r\n"
+      "accept: foo\r\n"
+      "accept: bar\n"
+      ": (null) field name\r\n"
+      "aCCept: \n"
+      "ACCEPT\r\n"
+      "foo: bar\r\n"
+      "foo: argh\r\n"
+      "foo: three, four\r\n"
+      "word word: word \r\n"
+      "accept: \"fazzle, dazzle\"\r\n"
+      "accept: 1, 2, 3, 4, 5, 6, 7, 8\r\n"
+      "continuation: part1\r\n"
+      " part2\r\n"
+      "scooby: doo\r\n"
+      " scooby: doo\r\n"
+      "bar: foo\r\n"
+      "\r\n",
+    };
+
+    int err;
+    MIMEHdr hdr;
+    MIMEParser parser;
+    const char *start;
+    const char *end;
+
+    std::printf("   <<< MUST BE HAND-VERIFIED FOR FULL-BENEFIT>>>\n\n");
+
+    start = mime;
+    end   = start + strlen(start);
+
+    mime_parser_init(&parser);
+
+    bool must_copy_strs = false;
+
+    hdr.create(nullptr);
+    err = hdr.parse(&parser, &start, end, must_copy_strs, false);
+
+    REQUIRE(err >= 0);
+
+    // Test the (new) continuation line folding to be correct. This should replace the
+    // \r\n with two spaces (so a total of three between "part1" and "part2").
+    int length               = 0;
+    const char *continuation = hdr.value_get("continuation", 12, &length);
+
+    if ((13 != length)) {
+      std::printf("FAILED: continue header folded line was too short\n");
+      REQUIRE(false);
+    }
+
+    if (strncmp(continuation + 5, "   ", 3)) {
+      std::printf("FAILED: continue header unfolding did not produce correct WS's\n");
+      REQUIRE(false);
+    }
+
+    if (strncmp(continuation, "part1   part2", 13)) {
+      std::printf("FAILED: continue header unfolding was not correct\n");
+      REQUIRE(false);
+    }
+
+    hdr.field_delete("not_there", 9);
+    hdr.field_delete("accept", 6);
+    hdr.field_delete("scooby", 6);
+    hdr.field_delete("scooby", 6);
+    hdr.field_delete("bar", 3);
+    hdr.field_delete("continuation", 12);
+
+    int count = hdr.fields_count();
+    std::printf("hdr.fields_count() = %d\n", count);
+
+    int i_max_forwards = hdr.value_get_int("Max-Forwards", 12);
+    int u_max_forwards = hdr.value_get_uint("Max-Forwards", 12);
+    std::printf("i_max_forwards = %d   u_max_forwards = %d\n", i_max_forwards, u_max_forwards);
+
+    hdr.set_age(9999);
+
+    length = hdr.length_get();
+    std::printf("hdr.length_get() = %d\n", length);
+
+    time_t t0, t1, t2;
+
+    t0 = hdr.get_date();
+    if (t0 == 0) {
+      std::printf("FAILED: Initial date is zero but shouldn't be\n");
+      REQUIRE(false);
+    }
+
+    t1 = time(nullptr);
+    hdr.set_date(t1);
+    t2 = hdr.get_date();
+    if (t1 != t2) {
+      std::printf("FAILED: set_date(%" PRId64 ") ... get_date = %" PRId64 "\n\n", static_cast<int64_t>(t1),
+                  static_cast<int64_t>(t2));
+      REQUIRE(false);
+    }
+
+    hdr.value_append("Cache-Control", 13, "no-cache", 8, true);
+
+    MIMEField *cc_field;
+    StrList slist;
+
+    cc_field = hdr.field_find("Cache-Control", 13);
+
+    if (cc_field == nullptr) {
+      std::printf("FAILED: missing Cache-Control header\n\n");
+      REQUIRE(false);
+    }
+
+    // TODO: Do we need to check the "count" returned?
+    cc_field->value_get_comma_list(&slist); // FIX: correct usage?
+
+    if (cc_field->value_get_index("Private", 7) < 0) {
+      std::printf("Failed: value_get_index of Cache-Control did not find private");
+      REQUIRE(false);
+    }
+    if (cc_field->value_get_index("Bogus", 5) >= 0) {
+      std::printf("Failed: value_get_index of Cache-Control incorrectly found bogus");
+      REQUIRE(false);
+    }
+    if (hdr.value_get_index("foo", 3, "three", 5) < 0) {
+      std::printf("Failed: value_get_index of foo did not find three");
+      REQUIRE(false);
+    }
+    if (hdr.value_get_index("foo", 3, "bar", 3) < 0) {
+      std::printf("Failed: value_get_index of foo did not find bar");
+      REQUIRE(false);
+    }
+    if (hdr.value_get_index("foo", 3, "Bogus", 5) >= 0) {
+      std::printf("Failed: value_get_index of foo incorrectly found bogus");
+      REQUIRE(false);
+    }
+
+    mime_parser_clear(&parser);
+
+    hdr.print(nullptr, 0, nullptr, nullptr);
+    std::printf("\n");
+
+    obj_describe((HdrHeapObjImpl *)(hdr.m_mime), true);
+
+    const char *field_name = "Test_heap_reuse";
+
+    MIMEField *f = hdr.field_create(field_name, static_cast<int>(strlen(field_name)));
+    REQUIRE(f->m_ptr_value == nullptr);
+
+    hdr.field_attach(f);
+    REQUIRE(f->m_ptr_value == nullptr);
+
+    const char *test_value = "mytest";
+
+    std::printf("Testing Heap Reuse..\n");
+    hdr.field_value_set(f, "orig_value", strlen("orig_value"));
+    const char *m_ptr_value_orig = f->m_ptr_value;
+    hdr.field_value_set(f, test_value, strlen(test_value), true);
+    REQUIRE(f->m_ptr_value != test_value);       // should be copied
+    REQUIRE(f->m_ptr_value == m_ptr_value_orig); // heap doesn't change
+    REQUIRE(f->m_len_value == strlen(test_value));
+    REQUIRE(memcmp(f->m_ptr_value, test_value, f->m_len_value) == 0);
+
+    m_ptr_value_orig           = f->m_ptr_value;
+    const char *new_test_value = "myTest";
+    hdr.field_value_set(f, new_test_value, strlen(new_test_value), false);
+    REQUIRE(f->m_ptr_value != new_test_value);   // should be copied
+    REQUIRE(f->m_ptr_value != m_ptr_value_orig); // new heap
+    REQUIRE(f->m_len_value == strlen(new_test_value));
+    REQUIRE(memcmp(f->m_ptr_value, new_test_value, f->m_len_value) == 0);
+
+    hdr.fields_clear();
+
+    hdr.destroy();
+  }
+
+  SECTION("Test http hdr print and copy")
+  {
+    static struct {
+      const char *req;
+      const char *req_tgt;
+      const char *rsp;
+      const char *rsp_tgt;
+    } tests[] = {
+      {"GET http://foo.com/bar.txt HTTP/1.0\r\n"
+       "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa\r\n"
+       "\r\n",
+       "GET http://foo.com/bar.txt HTTP/1.0\r\n"
+       "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "\r\n"},
+      {"GET http://foo.com/bar.txt HTTP/1.0\r\n"
+       "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa fjfj dslkfjdslk fjsdafkl dsajfkldsa jfkldsafj "
+       "klsafjs lkafjdsalk fsdjakfl sdjaflkdsaj flksdjflsd ;ffd salfdjs lf;sdaf ;dsaf jdsal;fdjsaflkjsda \r\n"
+       "\r\n",
+       "GET http://foo.com/bar.txt HTTP/1.0\r\n"
+       "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa fjfj dslkfjdslk fjsdafkl dsajfkldsa jfkldsafj "
+       "klsafjs lkafjdsalk fsdjakfl sdjaflkdsaj flksdjflsd ;ffd salfdjs lf;sdaf ;dsaf jdsal;fdjsaflkjsda \r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "\r\n"},
+      {"GET http://foo.com/bar.txt HTTP/1.0\r\n"
+       "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa fjfj dslkfjdslk fjsdafkl dsajfkldsa jfkldsafj "
+       "klsafjs lkafjdsalk fsdjakfl sdjaflkdsaj flksdjflsd ;ffd salfdjs lf;sdaf ;dsaf jdsal;fdjsaflkjsda kfl; fsdajfl; "
+       "sdjafl;dsajlsjfl;sdafjsdal;fjds al;fdjslaf ;slajdk;f\r\n"
+       "\r\n",
+       "GET http://foo.com/bar.txt HTTP/1.0\r\n"
+       "Accept-Language: fjdfjdslkf dsjkfdj flkdsfjlk sjfdlk ajfdlksa fjfj dslkfjdslk fjsdafkl dsajfkldsa jfkldsafj "
+       "klsafjs lkafjdsalk fsdjakfl sdjaflkdsaj flksdjflsd ;ffd salfdjs lf;sdaf ;dsaf jdsal;fdjsaflkjsda kfl; fsdajfl; "
+       "sdjafl;dsajlsjfl;sdafjsdal;fjds al;fdjslaf ;slajdk;f\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "\r\n"},
+      {"GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+       "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
+       "Referer: chocolate fribble\r\n", // missing final CRLF
+       "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+       "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
+       "Referer: chocolate fribble\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "MIME-Version: 1.0\r\n"
+       "Server: WebSTAR/2.1 ID/30013\r\n"
+       "Content-Type: text/html\r\n"
+       "Content-Length: 939\r\n"
+       "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n", // missing final CRLF
+       "HTTP/1.0 200 OK\r\n"
+       "MIME-Version: 1.0\r\n"
+       "Server: WebSTAR/2.1 ID/30013\r\n"
+       "Content-Type: text/html\r\n"
+       "Content-Length: 939\r\n"
+       "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n"
+       "\r\n"},
+      {"GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+       "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
+       "Referer: \r\n", // missing final CRLF
+       "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+       "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
+       "Referer: \r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "MIME-Version: 1.0\r\n"
+       "Server: WebSTAR/2.1 ID/30013\r\n"
+       "Content-Type: text/html\r\n"
+       "Content-Length: 939\r\n"
+       "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "MIME-Version: 1.0\r\n"
+       "Server: WebSTAR/2.1 ID/30013\r\n"
+       "Content-Type: text/html\r\n"
+       "Content-Length: 939\r\n"
+       "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n"
+       "\r\n"},
+      {"GET http://www.news.com:80/ HTTP/1.0\r\n"
+       "Proxy-Connection: Keep-Alive\r\n"
+       "User-Agent: Mozilla/4.04 [en] (X11; I; Linux 2.0.33 i586)\r\n"
+       "Pragma: no-cache\r\n"
+       "Host: www.news.com\r\n"
+       "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n"
+       "Accept-Language: en\r\n"
+       "Accept-Charset: iso-8859-1, *, utf-8\r\n"
+       "Client-ip: D1012148\r\n"
+       "Foo: abcdefghijklmnopqrtu\r\n"
+       "\r\n",
+       "GET http://www.news.com:80/ HTTP/1.0\r\n"
+       "Proxy-Connection: Keep-Alive\r\n"
+       "User-Agent: Mozilla/4.04 [en] (X11; I; Linux 2.0.33 i586)\r\n"
+       "Pragma: no-cache\r\n"
+       "Host: www.news.com\r\n"
+       "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n"
+       "Accept-Language: en\r\n"
+       "Accept-Charset: iso-8859-1, *, utf-8\r\n"
+       "Client-ip: D1012148\r\n"
+       "Foo: abcdefghijklmnopqrtu\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "Content-Length: 16428\r\n"
+       "Content-Type: text/html\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "Content-Length: 16428\r\n"
+       "Content-Type: text/html\r\n"
+       "\r\n"},
+      {"GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+       "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
+       "Referer: http://people.netscape.com/jwz/index.html\r\n"
+       "Proxy-Connection: Keep-Alive\r\n"
+       "User-Agent:  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
+       "Pragma: no-cache\r\n"
+       "Host: people.netscape.com\r\n"
+       "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
+       "\r\n",
+       "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+       "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
+       "Referer: http://people.netscape.com/jwz/index.html\r\n"
+       "Proxy-Connection: Keep-Alive\r\n"
+       "User-Agent:  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
+       "Pragma: no-cache\r\n"
+       "Host: people.netscape.com\r\n"
+       "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "Content-Length: 16428\r\n"
+       "Content-Type: text/html\r\n"
+       "\r\n",
+       "HTTP/1.0 200 OK\r\n"
+       "Content-Length: 16428\r\n"
+       "Content-Type: text/html\r\n"
+       "\r\n"},
+    };
+
+    int ntests = sizeof(tests) / sizeof(tests[0]);
+    int i;
+
+    for (i = 0; i < ntests; i++) {
+      int status = test_http_hdr_print_and_copy_aux(i + 1, tests[i].req, tests[i].req_tgt, tests[i].rsp, tests[i].rsp_tgt);
+      CHECK(status != 0);
+
+      // Test for expected failures
+      // parse with a '\0' in the header.  Should fail
+      status = test_http_hdr_null_char(i + 1, tests[i].req, tests[i].req_tgt);
+      CHECK(status != 0);
+
+      // Parse with a CTL character in the method name.  Should fail
+      status = test_http_hdr_ctl_char(i + 1, tests[i].req, tests[i].req_tgt);
+      CHECK(status != 0);
+    }
+  }
+
+  SECTION("Test http")
+  {
+    static const char request0[] = {
+      "GET http://www.news.com:80/ HTTP/1.0\r\n"
+      "Proxy-Connection: Keep-Alive\r\n"
+      "User-Agent: Mozilla/4.04 [en] (X11; I; Linux 2.0.33 i586)\r\n"
+      "Pragma: no-cache\r\n"
+      "Host: www.news.com\r\n"
+      "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*\r\n"
+      "Accept-Language: en\r\n"
+      "Accept-Charset: iso-8859-1, *, utf-8\r\n"
+      "Cookie: u_vid_0_0=00031ba3; "
+      "s_cur_0_0=0101sisi091314775496e7d3Jx4+POyJakrMybmNOsq6XOn5bVn5Z6a4Ln5crU5M7Rxq2lm5aWpqupo20=; "
+      "SC_Cnet001=Sampled; SC_Cnet002=Sampled\r\n"
+      "Client-ip: D1012148\r\n"
+      "Foo: abcdefghijklmnopqrtu\r\n"
+      "\r\n",
+    };
+
+    static const char request09[] = {
+      "GET /index.html\r\n"
+      "\r\n",
+    };
+
+    static const char request1[] = {
+      "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+      "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
+      "Referer: http://people.netscape.com/jwz/index.html\r\n"
+      "Proxy-Connection: Keep-Alive\r\n"
+      "User-Agent:  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
+      "Pragma: no-cache\r\n"
+      "Host: people.netscape.com\r\n"
+      "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
+      "\r\n",
+    };
+
+    static const char request_no_colon[] = {
+      "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+      "If-Modified-Since Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
+      "Referer http://people.netscape.com/jwz/index.html\r\n"
+      "Proxy-Connection Keep-Alive\r\n"
+      "User-Agent  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
+      "Pragma no-cache\r\n"
+      "Host people.netscape.com\r\n"
+      "Accept image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
+      "\r\n",
+    };
+
+    static const char request_no_val[] = {
+      "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+      "If-Modified-Since:\r\n"
+      "Referer:     "
+      "Proxy-Connection:\r\n"
+      "User-Agent:     \r\n"
+      "Host:::\r\n"
+      "\r\n",
+    };
+
+    static const char request_multi_fblock[] = {
+      "GET http://people.netscape.com/jwz/hacks-1.gif HTTP/1.0\r\n"
+      "If-Modified-Since: Wednesday, 26-Feb-97 06:58:17 GMT; length=842\r\n"
+      "Referer: http://people.netscape.com/jwz/index.html\r\n"
+      "Proxy-Connection: Keep-Alive\r\n"
+      "User-Agent:  Mozilla/3.01 (X11; I; Linux 2.0.28 i586)\r\n"
+      "Pragma: no-cache\r\n"
+      "Host: people.netscape.com\r\n"
+      "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"
+      "X-1: blah\r\n"
+      "X-2: blah\r\n"
+      "X-3: blah\r\n"
+      "X-4: blah\r\n"
+      "X-5: blah\r\n"
+      "X-6: blah\r\n"
+      "X-7: blah\r\n"
+      "X-8: blah\r\n"
+      "X-9: blah\r\n"
+      "Pragma: no-cache\r\n"
+      "X-X-1: blah\r\n"
+      "X-X-2: blah\r\n"
+      "X-X-3: blah\r\n"
+      "X-X-4: blah\r\n"
+      "X-X-5: blah\r\n"
+      "X-X-6: blah\r\n"
+      "X-X-7: blah\r\n"
+      "X-X-8: blah\r\n"
+      "X-X-9: blah\r\n"
+      "\r\n",
+    };
+
+    static const char request_leading_space[] = {
+      " GET http://www.news.com:80/ HTTP/1.0\r\n"
+      "Proxy-Connection: Keep-Alive\r\n"
+      "User-Agent: Mozilla/4.04 [en] (X11; I; Linux 2.0.33 i586)\r\n"
+      "\r\n",
+    };
+
+    static const char request_padding[] = {
+      "GET http://www.padding.com:80/ HTTP/1.0\r\n"
+      "X-1: blah1\r\n"
+      //       "X-2:  blah2\r\n"
+      "X-3:   blah3\r\n"
+      //       "X-4:    blah4\r\n"
+      "X-5:     blah5\r\n"
+      //       "X-6:      blah6\r\n"
+      "X-7:       blah7\r\n"
+      //       "X-8:        blah8\r\n"
+      "X-9:         blah9\r\n"
+      "\r\n",
+    };
+
+    static const char request_09p[] = {
+      "GET http://www.news09.com/\r\n"
+      "\r\n",
+    };
+
+    static const char request_09ht[] = {
+      "GET http://www.news09.com/ HT\r\n"
+      "\r\n",
+    };
+
+    static const char request_11[] = {
+      "GET http://www.news.com/ HTTP/1.1\r\n"
+      "Connection: close\r\n"
+      "\r\n",
+    };
+
+    static const char request_too_long[] = {
+      "GET http://www.news.com/i/am/too/long HTTP/1.1\r\n"
+      "Connection: close\r\n"
+      "\r\n",
+    };
+
+    static const char request_unterminated[] = {
+      "GET http://www.unterminated.com/ HTTP/1.1",
+    };
+
+    static const char request_blank[] = {
+      "\r\n",
+    };
+
+    static const char request_blank2[] = {
+      "\r\n"
+      "\r\n",
+    };
+
+    static const char request_blank3[] = {
+      "     "
+      "\r\n",
+    };
+
+    ////////////////////////////////////////////////////
+
+    static const char response0[] = {
+      "HTTP/1.0 200 OK\r\n"
+      "MIME-Version: 1.0\r\n"
+      "Server: WebSTAR/2.1 ID/30013\r\n"
+      "Content-Type: text/html\r\n"
+      "Content-Length: 939\r\n"
+      "Last-Modified: Thursday, 01-Jan-04 05:00:00 GMT\r\n"
+      "\r\n",
+    };
+
+    static const char response1[] = {
+      "HTTP/1.0 200 OK\r\n"
+      "Server: Netscape-Communications/1.12\r\n"
+      "Date: Tuesday, 08-Dec-98 20:32:17 GMT\r\n"
+      "Content-Type: text/html\r\n"
+      "\r\n",
+    };
+
+    static const char response_no_colon[] = {
+      "HTTP/1.0 200 OK\r\n"
+      "Server Netscape-Communications/1.12\r\n"
+      "Date: Tuesday, 08-Dec-98 20:32:17 GMT\r\n"
+      "Content-Type: text/html\r\n"
+      "\r\n",
+    };
+
+    static const char response_unterminated[] = {
+      "HTTP/1.0 200 OK",
+    };
+
+    static const char response09[] = {
+      "",
+    };
+
+    static const char response_blank[] = {
+      "\r\n",
+    };
+
+    static const char response_blank2[] = {
+      "\r\n"
+      "\r\n",
+    };
+
+    static const char response_blank3[] = {
+      "     "
+      "\r\n",
+    };
+
+    static const char response_too_long_req[] = {
+      "HTTP/1.0 414 URI Too Long\r\n"
+      "\r\n",
+    };
+
+    struct RequestResponse {
+      char const *request;
+      char const *response;
+    };
+
+    RequestResponse rr[] = {{request0, response0},
+                            {request09, response09},
+                            {request1, response1},
+                            {request_no_colon, response_no_colon},
+                            {request_no_val, response_no_colon},
+                            {request_leading_space, response0},
+                            {request_multi_fblock, response0},
+                            {request_padding, response0},
+                            {request_09p, response0},
+                            {request_09ht, response0},
+                            {request_11, response0},
+                            {request_unterminated, response_unterminated},
+                            {request_blank, response_blank},
+                            {request_blank2, response_blank2},
+                            {request_blank3, response_blank3}};
+
+    int err;
+    HTTPHdr req_hdr, rsp_hdr;
+    HTTPParser parser;
+    const char *start;
+    const char *end;
+    const char *request;
+    const char *response;
+
+    for (unsigned idx = 0; idx < (sizeof(rr) / sizeof(*rr)); ++idx) {
+      request  = rr[idx].request;
+      response = rr[idx].response;
+
+      std::printf("   <<< MUST BE HAND-VERIFIED FOR FULL BENEFIT >>>\n\n");
+
+      /*** (1) parse the request string into req_hdr ***/
+
+      start = request;
+      end   = start + strlen(start); // 1 character past end of string
+
+      http_parser_init(&parser);
+
+      req_hdr.create(HTTP_TYPE_REQUEST);
+      rsp_hdr.create(HTTP_TYPE_RESPONSE);
+
+      std::printf("======== parsing\n\n");
+      while (true) {
+        err = req_hdr.parse_req(&parser, &start, end, true);
+        if (err != PARSE_RESULT_CONT) {
+          break;
+        }
+      }
+      if (err == PARSE_RESULT_ERROR) {
+        req_hdr.destroy();
+        rsp_hdr.destroy();
+        break;
+      }
+
+      /*** useless copy to exercise copy function ***/
+
+      HTTPHdr new_hdr;
+      new_hdr.create(HTTP_TYPE_REQUEST);
+      new_hdr.copy(&req_hdr);
+      new_hdr.destroy();
+
+      /*** (2) print out the request ***/
+
+      std::printf("======== real request (length=%d)\n\n", static_cast<int>(strlen(request)));
+      std::printf("%s\n", request);
+
+      std::printf("\n[");
+      req_hdr.print(nullptr, 0, nullptr, nullptr);
+      std::printf("]\n\n");
+
+      obj_describe(req_hdr.m_http, true);
+
+      // req_hdr.destroy ();
+      // REQUIRE(!"req_hdr.destroy() not defined");
+
+      /*** (3) parse the response string into rsp_hdr ***/
+
+      start = response;
+      end   = start + strlen(start);
+
+      http_parser_clear(&parser);
+      http_parser_init(&parser);
+
+      while (true) {
+        err = rsp_hdr.parse_resp(&parser, &start, end, true);
+        if (err != PARSE_RESULT_CONT) {
+          break;
+        }
+      }
+      if (err == PARSE_RESULT_ERROR) {
+        req_hdr.destroy();
+        rsp_hdr.destroy();
+        break;
+      }
+
+      http_parser_clear(&parser);
+
+      /*** (4) print out the response ***/
+
+      std::printf("\n======== real response (length=%d)\n\n", static_cast<int>(strlen(response)));
+      std::printf("%s\n", response);
+
+      std::printf("\n[");
+      rsp_hdr.print(nullptr, 0, nullptr, nullptr);
+      std::printf("]\n\n");
+
+      obj_describe(rsp_hdr.m_http, true);
+
+      const int NNN = 1000;
+      {
+        char buf[NNN];
+        int bufindex, last_bufindex;
+        int tmp;
+        int i;
+
+        bufindex = 0;
+
+        do {
+          last_bufindex = bufindex;
+          tmp           = bufindex;
+          buf[0]        = '#'; // make it obvious if hdr.print doesn't print anything
+          err           = rsp_hdr.print(buf, NNN, &bufindex, &tmp);
+
+          // std::printf("test_header: tmp = %d  err = %d  bufindex = %d\n", tmp, err, bufindex);
+          putchar('{');
+          for (i = 0; i < bufindex - last_bufindex; i++) {
+            if (!iscntrl(buf[i])) {
+              putchar(buf[i]);
+            } else {
+              std::printf("\\%o", buf[i]);
+            }
+          }
+          putchar('}');
+        } while (!err);
+      }
+
+      // rsp_hdr.print (NULL, 0, NULL, NULL);
+
+      req_hdr.destroy();
+      rsp_hdr.destroy();
+    }
+
+    {
+      request  = request_too_long;
+      response = response_too_long_req;
+
+      int status = 1;
+
+      /*** (1) parse the request string into req_hdr ***/
+
+      start = request;
+      end   = start + strlen(start); // 1 character past end of string
+
+      http_parser_init(&parser);
+
+      req_hdr.create(HTTP_TYPE_REQUEST);
+      rsp_hdr.create(HTTP_TYPE_RESPONSE);
+
+      std::printf("======== test_http_req_parse_error parsing\n\n");
+      err = req_hdr.parse_req(&parser, &start, end, true, true, 1);
+      if (err != PARSE_RESULT_ERROR) {
+        status = 0;
+      }
+
+      http_parser_clear(&parser);
+
+      /*** (4) print out the response ***/
+
+      std::printf("\n======== real response (length=%d)\n\n", static_cast<int>(strlen(response)));
+      std::printf("%s\n", response);
+
+      obj_describe(rsp_hdr.m_http, true);
+
+      req_hdr.destroy();
+      rsp_hdr.destroy();
+
+      CHECK(status != 0);
+    }
+  }
+
+  SECTION("Test http mutation")
+  {
+    std::printf("   <<< MUST BE HAND-VERIFIED FOR FULL BENEFIT>>>\n\n");
+
+    HTTPHdr resp_hdr;
+    int err, i;
+    HTTPParser parser;
+    const char base_resp[] = "HTTP/1.0 200 OK\r\n\r\n";
+    const char *start, *end;
+
+    /*** (1) parse the response string into req_hdr ***/
+
+    start = base_resp;
+    end   = start + strlen(start);
+
+    http_parser_init(&parser);
+
+    resp_hdr.create(HTTP_TYPE_RESPONSE);
+
+    while (true) {
+      err = resp_hdr.parse_resp(&parser, &start, end, true);
+      if (err != PARSE_RESULT_CONT) {
+        break;
+      }
+    }
+
+    std::printf("\n======== before mutation ==========\n\n");
+    std::printf("\n[");
+    resp_hdr.print(nullptr, 0, nullptr, nullptr);
+    std::printf("]\n\n");
+
+    /*** (2) add in a bunch of header fields ****/
+    char field_name[1024];
+    char field_value[1024];
+    for (i = 1; i <= 100; i++) {
+      snprintf(field_name, sizeof(field_name), "Test%d", i);
+      snprintf(field_value, sizeof(field_value), "%d %d %d %d %d", i, i, i, i, i);
+      resp_hdr.value_set(field_name, static_cast<int>(strlen(field_name)), field_value, static_cast<int>(strlen(field_value)));
+    }
+
+    /**** (3) delete all the even numbered fields *****/
+    for (i = 2; i <= 100; i += 2) {
+      snprintf(field_name, sizeof(field_name), "Test%d", i);
+      resp_hdr.field_delete(field_name, static_cast<int>(strlen(field_name)));
+    }
+
+    /***** (4) add in secondary fields for all multiples of 3 ***/
+    for (i = 3; i <= 100; i += 3) {
+      snprintf(field_name, sizeof(field_name), "Test%d", i);
+      MIMEField *f = resp_hdr.field_create(field_name, static_cast<int>(strlen(field_name)));
+      resp_hdr.field_attach(f);
+      snprintf(field_value, sizeof(field_value), "d %d %d %d %d %d", i, i, i, i, i);
+      f->value_set(resp_hdr.m_heap, resp_hdr.m_mime, field_value, static_cast<int>(strlen(field_value)));
+    }
+
+    /***** (5) append all fields with multiples of 5 ***/
+    for (i = 5; i <= 100; i += 5) {
+      snprintf(field_name, sizeof(field_name), "Test%d", i);
+      snprintf(field_value, sizeof(field_value), "a %d", i);
+
+      resp_hdr.value_append(field_name, static_cast<int>(strlen(field_name)), field_value, static_cast<int>(strlen(field_value)),
+                            true);
+    }
+
+    /**** (6) delete all multiples of nine *****/
+    for (i = 9; i <= 100; i += 9) {
+      snprintf(field_name, sizeof(field_name), "Test%d", i);
+      resp_hdr.field_delete(field_name, static_cast<int>(strlen(field_name)));
+    }
+
+    std::printf("\n======== mutated response ==========\n\n");
+    std::printf("\n[");
+    resp_hdr.print(nullptr, 0, nullptr, nullptr);
+    std::printf("]\n\n");
+
+    resp_hdr.destroy();
+  }
+
+  SECTION("Test arena")
+  {
+    Arena *arena;
+
+    arena = new Arena;
+
+    CHECK(test_arena_aux(arena, 1) != 1);
+    CHECK(test_arena_aux(arena, 127) != 1);
+    CHECK(test_arena_aux(arena, 128) != 1);
+    CHECK(test_arena_aux(arena, 129) != 1);
+    CHECK(test_arena_aux(arena, 255) != 1);
+    CHECK(test_arena_aux(arena, 256) != 1);
+    CHECK(test_arena_aux(arena, 16384) != 1);
+    CHECK(test_arena_aux(arena, 16385) != 1);
+    CHECK(test_arena_aux(arena, 16511) != 1);
+    CHECK(test_arena_aux(arena, 16512) != 1);
+    CHECK(test_arena_aux(arena, 2097152) != 1);
+    CHECK(test_arena_aux(arena, 2097153) != 1);
+    CHECK(test_arena_aux(arena, 2097279) != 1);
+    CHECK(test_arena_aux(arena, 2097280) != 1);
+
+    delete arena;
+  }
+
+  SECTION("Test regex")
+  {
+    DFA dfa;
+
+    const char *test_harness[] = {"foo", "(.*\\.apache\\.org)", "(.*\\.example\\.com)"};
+
+    dfa.compile(test_harness, SIZEOF(test_harness));
+    CHECK(dfa.match("trafficserver.apache.org") == 1);
+    CHECK(dfa.match("www.example.com") == 2);
+    CHECK(dfa.match("aaaaaafooooooooinktomi....com.org") == -1);
+    CHECK(dfa.match("foo") == 0);
+  }
+
+  SECTION("Test accept language match")
+  {
+    struct {
+      const char *content_language;
+      const char *accept_language;
+      float Q;
+      int L;
+      int I;
+    } test_cases[] = {
+      {"en", "*", 1.0, 1, 1},
+      {"en", "fr", 0.0, 0, 0},
+      {"en", "de, fr, en;q=0.7", 0.7, 2, 3},
+      {"en-cockney", "de, fr, en;q=0.7", 0.7, 2, 3},
+      {"en-cockney", "de, fr, en-foobar;q=0.8, en;q=0.7", 0.7, 2, 4},
+      {"en-cockney", "de, fr, en-cockney;q=0.8, en;q=0.7", 0.8, 10, 3},
+      {"en-cockney", "de, fr, en;q=0.8, en;q=0.7", 0.8, 2, 3},
+      {"en-cockney", "de, fr, en;q=0.7, en;q=0.8", 0.8, 2, 4},
+      {"en-cockney", "de, fr, en;q=0.8, en;q=0.8", 0.8, 2, 3},
+      {"en-cockney", "de, fr, en-cockney;q=0.7, en;q=0.8", 0.7, 10, 3},
+      {"en-cockney", "de, fr, en;q=0.8, en-cockney;q=0.7", 0.7, 10, 4},
+      {"en-cockney", "de, fr, en-cockney;q=0.8, en;q=0.8", 0.8, 10, 3},
+      {"en-cockney", "de, fr, en-cockney;q=0.8, en;q=0.7", 0.8, 10, 3},
+      {"en-cockney", "de, fr, en-american", 0.0, 0, 0},
+      {"en-cockney", "de, fr, en;q=0.8, en;q=0.8, *", 0.8, 2, 3},
+      {"en-cockney", "de, fr, en;q=0.8, en;q=0.8, *;q=0.9", 0.8, 2, 3},
+      {"en-foobar", "de, fr, en;q=0.8, en;q=0.8, *;q=0.9", 0.8, 2, 3},
+      {"oo-foobar", "de, fr, en;q=0.8, en;q=0.8, *;q=0.9", 0.9, 1, 5},
+      {"oo-foobar", "de, fr, en;q=0.8, en;q=0.8, *;q=0.9, *", 1.0, 1, 6},
+      {"oo-foobar", "de, fr, en;q=0.8, en;q=0.8, *, *;q=0.9", 1.0, 1, 5},
+      {"fr-belgian", "de, fr;hi-there;q=0.9, fr;q=0.8, en", 0.9, 2, 2},
+      {"fr-belgian", "de, fr;q=0.8, fr;hi-there;q=0.9, en", 0.9, 2, 3},
+      {nullptr, nullptr, 0.0, 0, 0},
+    };
+
+    int i, I, L;
+    float Q;
+
+    for (i = 0; test_cases[i].accept_language; i++) {
+      StrList acpt_lang_list(false);
+      HttpCompat::parse_comma_list(&acpt_lang_list, test_cases[i].accept_language,
+                                   static_cast<int>(strlen(test_cases[i].accept_language)));
+
+      Q = HttpCompat::match_accept_language(test_cases[i].content_language,
+                                            static_cast<int>(strlen(test_cases[i].content_language)), &acpt_lang_list, &L, &I);
+
+      if ((Q != test_cases[i].Q) || (L != test_cases[i].L) || (I != test_cases[i].I)) {
+        std::printf(
+          "FAILED: (#%d) got { Q = %.3f; L = %d; I = %d; }, expected { Q = %.3f; L = %d; I = %d; }, from matching\n  '%s' "
+          "against '%s'\n",
+          i, Q, L, I, test_cases[i].Q, test_cases[i].L, test_cases[i].I, test_cases[i].content_language,
+          test_cases[i].accept_language);
+        CHECK(false);
+      }
+    }
+  }
+
+  SECTION("Test accept charset match")
+  {
+    struct {
+      const char *content_charset;
+      const char *accept_charset;
+      float Q;
+      int I;
+    } test_cases[] = {
+      {"iso-8859-1", "*", 1.0, 1},
+      {"iso-8859-1", "iso-8859-2", 0.0, 0},
+      {"iso-8859-1", "iso-8859", 0.0, 0},
+      {"iso-8859-1", "iso-8859-12", 0.0, 0},
+      {"iso-8859-1", "koi-8-r", 0.0, 0},
+      {"euc-jp", "shift_jis, iso-2022-jp, euc-jp;q=0.7", 0.7, 3},
+      {"euc-jp", "shift_jis, iso-2022-jp, euc-jp;q=0.7", 0.7, 3},
+      {"euc-jp", "shift_jis, iso-2022-jp, euc-jp;q=0.8, euc-jp;q=0.7", 0.8, 3},
+      {"euc-jp", "shift_jis, iso-2022-jp, euc-jp;q=0.7, euc-jp;q=0.8", 0.8, 4},
+      {"euc-jp", "euc-jp;q=0.9, shift_jis, iso-2022-jp, euc-jp;q=0.7, euc-jp;q=0.8", 0.9, 1},
+      {"EUC-JP", "euc-jp;q=0.9, shift_jis, iso-2022-jp, euc-jp, euc-jp;q=0.8", 1.0, 4},
+      {"euc-jp", "euc-jp;q=0.9, shift_jis, iso-2022-jp, EUC-JP, euc-jp;q=0.8", 1.0, 4},
+      {"euc-jp", "shift_jis, iso-2022-jp, euc-jp-foobar", 0.0, 0},
+      {"euc-jp", "shift_jis, iso-2022-jp, euc-jp-foobar, *", 1.0, 4},
+      {"euc-jp", "shift_jis, iso-2022-jp, euc-jp-foobar, *;q=0.543", 0.543, 4},
+      {"euc-jp", "shift_jis, iso-2022-jp, euc-jp-foobar, *;q=0.0", 0.0, 4},
+      {"euc-jp", "shift_jis, iso-2022-jp, *;q=0.0, euc-jp-foobar, *;q=0.0", 0.0, 3},
+      {"euc-jp", "shift_jis, iso-2022-jp, *;q=0.0, euc-jp-foobar, *;q=0.5", 0.5, 5},
+      {"euc-jp", "shift_jis, iso-2022-jp, *;q=0.5, euc-jp-foobar, *;q=0.0", 0.5, 3},
+      {"euc-jp", "shift_jis, iso-2022-jp, *;q=0.5, euc-jp-foobar, *, *;q=0.0", 1.0, 5},
+      {"euc-jp", "shift_jis, euc-jp;hi-there;q=0.5, iso-2022-jp", 0.5, 2},
+      {"euc-jp", "shift_jis, euc-jp;hi-there;q= 0.5, iso-2022-jp", 0.5, 2},
+      {"euc-jp", "shift_jis, euc-jp;hi-there;q = 0.5, iso-2022-jp", 0.5, 2},
+      {"euc-jp", "shift_jis, euc-jp;hi-there ; q = 0.5, iso-2022-jp", 0.5, 2},
+      {"euc-jp", "shift_jis, euc-jp;hi-there ;; q = 0.5, iso-2022-jp", 0.5, 2},
+      {"euc-jp", "shift_jis, euc-jp;hi-there ;; Q = 0.5, iso-2022-jp", 0.5, 2},
+      {nullptr, nullptr, 0.0, 0},
+    };
+
+    int i, I;
+    float Q;
+
+    for (i = 0; test_cases[i].accept_charset; i++) {
+      StrList acpt_lang_list(false);
+      HttpCompat::parse_comma_list(&acpt_lang_list, test_cases[i].accept_charset,
+                                   static_cast<int>(strlen(test_cases[i].accept_charset)));
+
+      Q = HttpCompat::match_accept_charset(test_cases[i].content_charset, static_cast<int>(strlen(test_cases[i].content_charset)),
+                                           &acpt_lang_list, &I);
+
+      if ((Q != test_cases[i].Q) || (I != test_cases[i].I)) {
+        std::printf("FAILED: (#%d) got { Q = %.3f; I = %d; }, expected { Q = %.3f; I = %d; }, from matching\n  '%s' against '%s'\n",
+                    i, Q, I, test_cases[i].Q, test_cases[i].I, test_cases[i].content_charset, test_cases[i].accept_charset);
+        CHECK(false);
+      }
+    }
+  }
+
+  SECTION("Test comma vals")
+  {
+    static struct {
+      const char *value;
+      int value_count;
+      struct {
+        int offset;
+        int len;
+      } pieces[4];
+    } tests[] = {
+      {",", 2, {{0, 0}, {1, 0}, {-1, 0}, {-1, 0}}},
+      {"", 1, {{0, 0}, {-1, 0}, {-1, 0}, {-1, 0}}},
+      {" ", 1, {{0, 0}, {-1, 0}, {-1, 0}, {-1, 0}}},
+      {", ", 2, {{0, 0}, {1, 0}, {-1, 0}, {-1, 0}}},
+      {",,", 3, {{0, 0}, {1, 0}, {2, 0}, {-1, 0}}},
+      {" ,", 2, {{0, 0}, {2, 0}, {-1, 0}, {-1, 0}}},
+      {" , ", 2, {{0, 0}, {2, 0}, {-1, 0}, {-1, 0}}},
+      {"a, ", 2, {{0, 1}, {2, 0}, {-1, 0}, {-1, 0}}},
+      {" a, ", 2, {{1, 1}, {3, 0}, {-1, 0}, {-1, 0}}},
+      {" ,a", 2, {{0, 0}, {2, 1}, {-1, 0}, {-1, 0}}},
+      {" , a", 2, {{0, 0}, {3, 1}, {-1, 0}, {-1, 0}}},
+      {"a,a", 2, {{0, 1}, {2, 1}, {-1, 0}, {-1, 0}}},
+      {"foo", 1, {{0, 3}, {-1, 0}, {-1, 0}, {-1, 0}}},
+      {"foo,", 2, {{0, 3}, {4, 0}, {-1, 0}, {-1, 0}}},
+      {"foo, ", 2, {{0, 3}, {4, 0}, {-1, 0}, {-1, 0}}},
+      {"foo, bar", 2, {{0, 3}, {5, 3}, {-1, 0}, {-1, 0}}},
+      {"foo, bar,", 3, {{0, 3}, {5, 3}, {9, 0}, {-1, 0}}},
+      {"foo, bar, ", 3, {{0, 3}, {5, 3}, {9, 0}, {-1, 0}}},
+      {
+        ",foo,bar,",
+        4,
+        {{0, 0}, {1, 3}, {5, 3}, {9, 0}},
+      },
+    };
+
+    HTTPHdr hdr;
+    char field_name[32];
+    int i, j, len, ntests, ncommavals;
+
+    ntests = sizeof(tests) / sizeof(tests[0]);
+
+    hdr.create(HTTP_TYPE_REQUEST);
+
+    for (i = 0; i < ntests; i++) {
+      snprintf(field_name, sizeof(field_name), "Test%d", i);
+
+      MIMEField *f = hdr.field_create(field_name, static_cast<int>(strlen(field_name)));
+      REQUIRE(f->m_ptr_value == nullptr);
+
+      hdr.field_attach(f);
+      REQUIRE(f->m_ptr_value == nullptr);
+
+      hdr.field_value_set(f, tests[i].value, strlen(tests[i].value));
+      REQUIRE(f->m_ptr_value != tests[i].value); // should be copied
+      REQUIRE(f->m_len_value == strlen(tests[i].value));
+      REQUIRE(memcmp(f->m_ptr_value, tests[i].value, f->m_len_value) == 0);
+
+      ncommavals = mime_field_value_get_comma_val_count(f);
+      if (ncommavals != tests[i].value_count) {
+        std::printf("FAILED: test #%d (field value '%s') expected val count %d, got %d\n", i + 1, tests[i].value,
+                    tests[i].value_count, ncommavals);
+        CHECK(false);
+      }
+
+      for (j = 0; j < tests[i].value_count; j++) {
+        const char *val = mime_field_value_get_comma_val(f, &len, j);
+        int offset      = ((val == nullptr) ? -1 : (val - f->m_ptr_value));
+
+        if ((offset != tests[i].pieces[j].offset) || (len != tests[i].pieces[j].len)) {
+          std::printf(
+            "FAILED: test #%d (field value '%s', commaval idx %d) expected [offset %d, len %d], got [offset %d, len %d]\n", i + 1,
+            tests[i].value, j, tests[i].pieces[j].offset, tests[i].pieces[j].len, offset, len);
+          CHECK(false);
+        }
+      }
+    }
+
+    hdr.destroy();
+  }
+
+  SECTION("Test set comma vals")
+  {
+    static struct {
+      const char *old_raw;
+      int idx;
+      const char *slice;
+      const char *new_raw;
+    } tests[] = {
+      {"a,b,c", 0, "fred", "fred, b, c"},
+      {"a,b,c", 1, "fred", "a, fred, c"},
+      {"a,b,c", 2, "fred", "a, b, fred"},
+      {"a,b,c", 3, "fred", "a,b,c"},
+      {"", 0, "", ""},
+      {"", 0, "foo", "foo"},
+      {"", 1, "foo", ""},
+      {" ", 0, "", ""},
+      {" ", 0, "foo", "foo"},
+      {" ", 1, "foo", " "},
+      {",", 0, "foo", "foo, "},
+      {",", 1, "foo", ", foo"},
+      {",,", 0, "foo", "foo, , "},
+      {",,", 1, "foo", ", foo, "},
+      {",,", 2, "foo", ", , foo"},
+      {"foo", 0, "abc", "abc"},
+      {"foo", 1, "abc", "foo"},
+      {"foo", 0, "abc,", "abc,"},
+      {"foo", 0, ",abc", ",abc"},
+      {",,", 1, ",,,", ", ,,,, "},
+      {" a , b , c", 0, "fred", "fred, b, c"},
+      {" a , b , c", 1, "fred", "a, fred, c"},
+      {" a , b , c", 2, "fred", "a, b, fred"},
+      {" a , b , c", 3, "fred", " a , b , c"},
+      {"    a   ,   b ", 0, "fred", "fred, b"},
+      {"    a   ,   b ", 1, "fred", "a, fred"},
+      {"    a   , b ", 1, "fred", "a, fred"},
+      {"    a   ,b ", 1, "fred", "a, fred"},
+      {"a, , , , e, , g,", 0, "fred", "fred, , , , e, , g, "},
+      {"a, , , , e, , g,", 1, "fred", "a, fred, , , e, , g, "},
+      {"a, , , , e, , g,", 2, "fred", "a, , fred, , e, , g, "},
+      {"a, , , , e, , g,", 5, "fred", "a, , , , e, fred, g, "},
+      {"a, , , , e, , g,", 7, "fred", "a, , , , e, , g, fred"},
+      {"a, , , , e, , g,", 8, "fred", "a, , , , e, , g,"},
+      {"a, \"boo,foo\", c", 0, "wawa", "wawa, \"boo,foo\", c"},
+      {"a, \"boo,foo\", c", 1, "wawa", "a, wawa, c"},
+      {"a, \"boo,foo\", c", 2, "wawa", "a, \"boo,foo\", wawa"},
+    };
+
+    HTTPHdr hdr;
+    char field_name[32];
+    int i, ntests;
+
+    ntests = sizeof(tests) / sizeof(tests[0]);
+
+    hdr.create(HTTP_TYPE_REQUEST);
+
+    for (i = 0; i < ntests; i++) {
+      snprintf(field_name, sizeof(field_name), "Test%d", i);
+
+      MIMEField *f = hdr.field_create(field_name, static_cast<int>(strlen(field_name)));
+      hdr.field_value_set(f, tests[i].old_raw, strlen(tests[i].old_raw));
+      mime_field_value_set_comma_val(hdr.m_heap, hdr.m_mime, f, tests[i].idx, tests[i].slice, strlen(tests[i].slice));
+      REQUIRE(f->m_ptr_value != nullptr);
+
+      if ((f->m_len_value != strlen(tests[i].new_raw)) || (memcmp(f->m_ptr_value, tests[i].new_raw, f->m_len_value) != 0)) {
+        std::printf("FAILED:  test #%d (setting idx %d of '%s' to '%s') expected '%s' len %d, got '%.*s' len %d\n", i + 1,
+                    tests[i].idx, tests[i].old_raw, tests[i].slice, tests[i].new_raw, static_cast<int>(strlen(tests[i].new_raw)),
+                    f->m_len_value, f->m_ptr_value, f->m_len_value);
+        CHECK(false);
+      }
+    }
+
+    hdr.destroy();
+  }
+
+  SECTION("Test delete comma vals")
+  {
+    // TEST NOT IMPLEMENTED
+  }
+
+  SECTION("Test extend comma vals")
+  {
+    // TEST NOT IMPLEMENTED
+  }
+
+  SECTION("Test insert comma vals")
+  {
+    // TEST NOT IMPLEMENTED
+  }
+
+  SECTION("Test parse comma list")
+  {
+    static struct {
+      const char *value;
+      int count;
+      struct {
+        int offset;
+        int len;
+      } pieces[3];
+    } tests[] = {
+      {"", 1, {{0, 0}, {-1, 0}, {-1, 0}}},
+      {",", 2, {{0, 0}, {1, 0}, {-1, 0}}},
+      {" ,", 2, {{0, 0}, {2, 0}, {-1, 0}}},
+      {", ", 2, {{0, 0}, {1, 0}, {-1, 0}}},
+      {" , ", 2, {{0, 0}, {2, 0}, {-1, 0}}},
+      {"abc,", 2, {{0, 3}, {4, 0}, {-1, 0}}},
+      {"abc, ", 2, {{0, 3}, {4, 0}, {-1, 0}}},
+      {"", 1, {{0, 0}, {-1, 0}, {-1, 0}}},
+      {" ", 1, {{0, 0}, {-1, 0}, {-1, 0}}},
+      {"  ", 1, {{0, 0}, {-1, 0}, {-1, 0}}},
+      {"a", 1, {{0, 1}, {-1, 0}, {-1, 0}}},
+      {" a", 1, {{1, 1}, {-1, 0}, {-1, 0}}},
+      {"  a  ", 1, {{2, 1}, {-1, 0}, {-1, 0}}},
+      {"abc,defg", 2, {{0, 3}, {4, 4}, {-1, 0}}},
+      {" abc,defg", 2, {{1, 3}, {5, 4}, {-1, 0}}},
+      {" abc, defg", 2, {{1, 3}, {6, 4}, {-1, 0}}},
+      {" abc , defg", 2, {{1, 3}, {7, 4}, {-1, 0}}},
+      {" abc , defg ", 2, {{1, 3}, {7, 4}, {-1, 0}}},
+      {" abc , defg, ", 3, {{1, 3}, {7, 4}, {12, 0}}},
+      {" abc , defg ,", 3, {{1, 3}, {7, 4}, {13, 0}}},
+      {", abc , defg ", 3, {{0, 0}, {2, 3}, {8, 4}}},
+      {" ,abc , defg ", 3, {{0, 0}, {2, 3}, {8, 4}}},
+      {"a,b", 2, {{0, 1}, {2, 1}, {-1, 0}}},
+      {"a,,b", 3, {{0, 1}, {2, 0}, {3, 1}}},
+      {"a, ,b", 3, {{0, 1}, {2, 0}, {4, 1}}},
+      {"a ,,b", 3, {{0, 1}, {3, 0}, {4, 1}}},
+      {",", 2, {{0, 0}, {1, 0}, {-1, 0}}},
+      {" ,", 2, {{0, 0}, {2, 0}, {-1, 0}}},
+      {", ", 2, {{0, 0}, {1, 0}, {-1, 0}}},
+      {" , ", 2, {{0, 0}, {2, 0}, {-1, 0}}},
+      {"a,b,", 3, {{0, 1}, {2, 1}, {4, 0}}},
+      {"a,b, ", 3, {{0, 1}, {2, 1}, {4, 0}}},
+      {"a,b,  ", 3, {{0, 1}, {2, 1}, {4, 0}}},
+      {"a,b,  c", 3, {{0, 1}, {2, 1}, {6, 1}}},
+      {"a,b,  c ", 3, {{0, 1}, {2, 1}, {6, 1}}},
+      {"a,\"b,c\",d", 3, {{0, 1}, {3, 3}, {8, 1}}},
+    };
+
+    int i, j, ntests, offset;
+
+    offset = 0;
+    ntests = sizeof(tests) / sizeof(tests[0]);
+
+    for (i = 0; i < ntests; i++) {
+      StrList list(false);
+      HttpCompat::parse_comma_list(&list, tests[i].value);
+      if (list.count != tests[i].count) {
+        std::printf("FAILED: test #%d (string '%s') expected list count %d, got %d\n", i + 1, tests[i].value, tests[i].count,
+                    list.count);
+        CHECK(false);
+      }
+
+      for (j = 0; j < tests[i].count; j++) {
+        Str *cell = list.get_idx(j);
+        if (cell != nullptr) {
+          offset = cell->str - tests[i].value;
+        }
+
+        if (tests[i].pieces[j].offset == -1) // should not have a piece
+        {
+          if (cell != nullptr) {
+            std::printf("FAILED: test #%d (string '%s', idx %d) expected NULL piece, got [offset %d len %d]\n", i + 1,
+                        tests[i].value, j, offset, static_cast<int>(cell->len));
+            CHECK(false);
+          }
+        } else // should have a piece
+        {
+          if (cell == nullptr) {
+            std::printf("FAILED: test #%d (string '%s', idx %d) expected [offset %d len %d], got NULL piece\n", i + 1,
+                        tests[i].value, j, tests[i].pieces[j].offset, tests[i].pieces[j].len);
+            CHECK(false);
+          } else if ((offset != tests[i].pieces[j].offset) || (cell->len != static_cast<size_t>(tests[i].pieces[j].len))) {
+            std::printf("FAILED: test #%d (string '%s', idx %d) expected [offset %d len %d], got [offset %d len %d]\n", i + 1,
+                        tests[i].value, j, tests[i].pieces[j].offset, tests[i].pieces[j].len, offset, static_cast<int>(cell->len));
+            CHECK(false);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/traffic_server/traffic_server.cc b/src/traffic_server/traffic_server.cc
index afb72fc..6153edd 100644
--- a/src/traffic_server/traffic_server.cc
+++ b/src/traffic_server/traffic_server.cc
@@ -2151,21 +2151,6 @@ main(int /* argc ATS_UNUSED */, const char **argv)
   delete main_thread;
 }
 
-#if TS_HAS_TESTS
-//////////////////////////////
-// Unit Regression Test Hook //
-//////////////////////////////
-
-#include "HdrTest.h"
-
-REGRESSION_TEST(Hdrs)(RegressionTest *t, int atype, int *pstatus)
-{
-  HdrTest ht;
-  *pstatus = ht.go(t, atype);
-  return;
-}
-#endif
-
 static void mgmt_restart_shutdown_callback(ts::MemSpan<void>)
 {
   sync_cache_dir_on_shutdown();