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 2017/03/05 00:22:50 UTC

[trafficserver] branch master updated (adf7b1a -> f778a8f)

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

zwoop pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git.

      from  adf7b1a   MemView: Add constructor with 'int' size for convenience.
       new  8c96e13   Add new testing infra to ATS
       new  80f3652   Doc for microserver in AuTest
       new  6b49012   update document
       new  a2847b3   add execute permission to boostrap.py script
       new  8614424   address bootstrap issue with rhel and centos
       new  a4fb5bc   better fix for RHEL and CentOS systems
       new  f6b284c   change -x to -r as 'enable' is not executable
       new  a79347d   more clean up to bootstrap.py
       new  4e6120b   tweak typo
       new  8b9888f   tweak test to be more stable
       new  43fde5f   Update getting_started.md
       new  2779fae   Fix custom log test Added 10 second delay to get AST to write out the log file Added a verbose message to help clarify a conf file was written as expected
       new  0b383f4   tweak gold files to not look for proxy-connection Curl adds Proxy-connection on it own in some cases, and or duplicates it more than once
       new  f778a8f   fix issue with virtualenv on Fedora 25

The 14 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "adds" were already present in the repository and have only
been added to this reference.


Summary of changes:
 NOTICE                                             |   6 +
 tests/autest.sh                                    |  35 ++
 tests/bootstrap.py                                 | 232 ++++++++
 tests/getting_started.md                           | 197 +++++++
 tests/gold_tests/autest-site/copy_config.test.ext  |  49 ++
 tests/gold_tests/autest-site/init.cli.ext          |  27 +
 tests/gold_tests/autest-site/microserver.test.ext  | 118 ++++
 tests/gold_tests/autest-site/min_cfg/cache.config  |   1 +
 .../gold_tests/autest-site/min_cfg/hosting.config  |   1 +
 .../gold_tests/autest-site/min_cfg/ip_allow.config |   4 +
 tests/gold_tests/autest-site/min_cfg/parent.config |   1 +
 tests/gold_tests/autest-site/min_cfg/readme.txt    |   3 +
 .../gold_tests/autest-site/min_cfg/records.config  |   1 +
 tests/gold_tests/autest-site/min_cfg/remap.config  |   1 +
 .../autest-site/min_cfg/ssl_multicert.config       |   1 +
 .../gold_tests/autest-site/min_cfg/storage.config  |   4 +
 tests/gold_tests/autest-site/min_cfg/volume.config |   1 +
 tests/gold_tests/autest-site/ports.py              | 107 ++++
 tests/gold_tests/autest-site/setup.cli.ext         |  49 ++
 .../gold_tests/autest-site/trafficserver.test.ext  | 325 +++++++++++
 tests/gold_tests/basic/basic-cop.test.py           |  33 ++
 tests/gold_tests/basic/basic-manager.test.py       |  33 ++
 tests/gold_tests/basic/basic.test.py               |  33 ++
 tests/gold_tests/basic/config.test.py              |  33 ++
 tests/gold_tests/basic/config/records_8090.config  |   2 +
 tests/gold_tests/basic/config/records_8091.config  |   2 +
 tests/gold_tests/basic/config/remap.config         |   1 +
 tests/gold_tests/basic/copy_config.test.py         |  49 ++
 tests/gold_tests/basic/copy_config2.test.py        |  46 ++
 tests/gold_tests/body_factory/config/remap.config  |   1 +
 .../body_factory/custom_response.test.py           |  64 +++
 .../cache/cache-generation-clear.test.py           |  90 +++
 .../cache/cache-generation-disjoint.test.py        |  88 +++
 .../cache/disjoint-wait-for-cache.test.py          |  88 +++
 tests/gold_tests/cache/gold/hit_default-1.gold     |   8 +
 tests/gold_tests/cache/gold/hit_default77.gold     |   8 +
 tests/gold_tests/cache/gold/hit_gen1.gold          |   8 +
 tests/gold_tests/cache/gold/hit_gen2.gold          |   8 +
 tests/gold_tests/cache/gold/miss_default-1.gold    |   8 +
 tests/gold_tests/cache/gold/miss_default77.gold    |   8 +
 tests/gold_tests/cache/gold/miss_gen1.gold         |   8 +
 tests/gold_tests/cache/gold/miss_gen2.gold         |   8 +
 tests/gold_tests/h2/gold/remap-200.gold            |   4 +
 tests/gold_tests/h2/h2client.py                    |  63 +++
 tests/gold_tests/h2/http2.test.py                  |  70 +++
 tests/gold_tests/h2/ssl/server.key                 |  15 +
 tests/gold_tests/h2/ssl/server.pem                 |  32 ++
 tests/gold_tests/logging/custom-log.test.py        | 103 ++++
 tests/gold_tests/logging/gold/custom.gold          |   8 +
 .../header_rewrite/gold/header_rewrite-303.gold    |  14 +
 .../header_rewrite/gold/header_rewrite-tag.gold    |   1 +
 .../header_rewrite/header_rewrite.test.py          |  68 +++
 .../pluginTest/header_rewrite/rules/rule.conf      |   2 +
 tests/gold_tests/remap/gold/remap-200.gold         |  14 +
 tests/gold_tests/remap/gold/remap-404.gold         |  12 +
 tests/gold_tests/remap/gold/remap-hitATS-404.gold  |  11 +
 tests/gold_tests/remap/gold/remap-https-200.gold   |  13 +
 tests/gold_tests/remap/remap_http.test.py          |  91 +++
 tests/gold_tests/remap/remap_https.test.py         | 112 ++++
 tests/gold_tests/remap/ssl/server.key              |  15 +
 tests/gold_tests/remap/ssl/server.pem              |  32 ++
 tests/tools/microServer/ssl/server.crt             |  17 +
 tests/tools/microServer/ssl/server.pem             |  32 ++
 tests/tools/microServer/uWServer.py                | 627 +++++++++++++++++++++
 tests/tools/readme.txt                             |  20 +
 tests/tools/sessionvalidation/__init__.py          |  17 +
 tests/tools/sessionvalidation/badsession.py        |  33 ++
 tests/tools/sessionvalidation/request.py           |  47 ++
 tests/tools/sessionvalidation/response.py          |  38 ++
 tests/tools/sessionvalidation/session.py           |  41 ++
 tests/tools/sessionvalidation/sessionvalidation.py | 269 +++++++++
 tests/tools/sessionvalidation/transaction.py       |  39 ++
 72 files changed, 3650 insertions(+)
 create mode 100755 tests/autest.sh
 create mode 100755 tests/bootstrap.py
 create mode 100644 tests/getting_started.md
 create mode 100644 tests/gold_tests/autest-site/copy_config.test.ext
 create mode 100644 tests/gold_tests/autest-site/init.cli.ext
 create mode 100644 tests/gold_tests/autest-site/microserver.test.ext
 create mode 100644 tests/gold_tests/autest-site/min_cfg/cache.config
 create mode 100644 tests/gold_tests/autest-site/min_cfg/hosting.config
 create mode 100644 tests/gold_tests/autest-site/min_cfg/ip_allow.config
 create mode 100644 tests/gold_tests/autest-site/min_cfg/parent.config
 create mode 100644 tests/gold_tests/autest-site/min_cfg/readme.txt
 create mode 100644 tests/gold_tests/autest-site/min_cfg/records.config
 create mode 100644 tests/gold_tests/autest-site/min_cfg/remap.config
 create mode 100644 tests/gold_tests/autest-site/min_cfg/ssl_multicert.config
 create mode 100644 tests/gold_tests/autest-site/min_cfg/storage.config
 create mode 100644 tests/gold_tests/autest-site/min_cfg/volume.config
 create mode 100644 tests/gold_tests/autest-site/ports.py
 create mode 100644 tests/gold_tests/autest-site/setup.cli.ext
 create mode 100644 tests/gold_tests/autest-site/trafficserver.test.ext
 create mode 100644 tests/gold_tests/basic/basic-cop.test.py
 create mode 100644 tests/gold_tests/basic/basic-manager.test.py
 create mode 100644 tests/gold_tests/basic/basic.test.py
 create mode 100644 tests/gold_tests/basic/config.test.py
 create mode 100644 tests/gold_tests/basic/config/records_8090.config
 create mode 100644 tests/gold_tests/basic/config/records_8091.config
 create mode 100644 tests/gold_tests/basic/config/remap.config
 create mode 100644 tests/gold_tests/basic/copy_config.test.py
 create mode 100644 tests/gold_tests/basic/copy_config2.test.py
 create mode 100644 tests/gold_tests/body_factory/config/remap.config
 create mode 100644 tests/gold_tests/body_factory/custom_response.test.py
 create mode 100644 tests/gold_tests/cache/cache-generation-clear.test.py
 create mode 100644 tests/gold_tests/cache/cache-generation-disjoint.test.py
 create mode 100644 tests/gold_tests/cache/disjoint-wait-for-cache.test.py
 create mode 100644 tests/gold_tests/cache/gold/hit_default-1.gold
 create mode 100644 tests/gold_tests/cache/gold/hit_default77.gold
 create mode 100644 tests/gold_tests/cache/gold/hit_gen1.gold
 create mode 100644 tests/gold_tests/cache/gold/hit_gen2.gold
 create mode 100644 tests/gold_tests/cache/gold/miss_default-1.gold
 create mode 100644 tests/gold_tests/cache/gold/miss_default77.gold
 create mode 100644 tests/gold_tests/cache/gold/miss_gen1.gold
 create mode 100644 tests/gold_tests/cache/gold/miss_gen2.gold
 create mode 100644 tests/gold_tests/h2/gold/remap-200.gold
 create mode 100644 tests/gold_tests/h2/h2client.py
 create mode 100644 tests/gold_tests/h2/http2.test.py
 create mode 100644 tests/gold_tests/h2/ssl/server.key
 create mode 100644 tests/gold_tests/h2/ssl/server.pem
 create mode 100644 tests/gold_tests/logging/custom-log.test.py
 create mode 100644 tests/gold_tests/logging/gold/custom.gold
 create mode 100644 tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold
 create mode 100644 tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-tag.gold
 create mode 100644 tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py
 create mode 100644 tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf
 create mode 100644 tests/gold_tests/remap/gold/remap-200.gold
 create mode 100644 tests/gold_tests/remap/gold/remap-404.gold
 create mode 100644 tests/gold_tests/remap/gold/remap-hitATS-404.gold
 create mode 100644 tests/gold_tests/remap/gold/remap-https-200.gold
 create mode 100644 tests/gold_tests/remap/remap_http.test.py
 create mode 100644 tests/gold_tests/remap/remap_https.test.py
 create mode 100644 tests/gold_tests/remap/ssl/server.key
 create mode 100644 tests/gold_tests/remap/ssl/server.pem
 create mode 100644 tests/tools/microServer/ssl/server.crt
 create mode 100644 tests/tools/microServer/ssl/server.pem
 create mode 100644 tests/tools/microServer/uWServer.py
 create mode 100644 tests/tools/readme.txt
 create mode 100644 tests/tools/sessionvalidation/__init__.py
 create mode 100644 tests/tools/sessionvalidation/badsession.py
 create mode 100644 tests/tools/sessionvalidation/request.py
 create mode 100644 tests/tools/sessionvalidation/response.py
 create mode 100644 tests/tools/sessionvalidation/session.py
 create mode 100644 tests/tools/sessionvalidation/sessionvalidation.py
 create mode 100644 tests/tools/sessionvalidation/transaction.py

-- 
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].

[trafficserver] 10/14: tweak test to be more stable

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8b9888ffcf9f90e3504ef7c3a22c2dc9684f3646
Author: Jason <dr...@live.com>
AuthorDate: Wed Mar 1 09:50:34 2017 -0600

    tweak test to be more stable
---
 .../pluginTest/header_rewrite/gold/header_rewrite-303.gold     |  2 +-
 .../pluginTest/header_rewrite/header_rewrite.test.py           |  2 +-
 tests/gold_tests/remap/gold/remap-200.gold                     |  2 +-
 tests/gold_tests/remap/remap_http.test.py                      | 10 +++++-----
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold
index 5a0be7f..5d55f9e 100644
--- a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold
+++ b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold
@@ -3,7 +3,7 @@
 > Host: www.example.com``
 > User-Agent: curl/``
 > Accept: */*
-> Proxy-Connection: Keep-Alive
+> Proxy-Connection: keep-alive
 ``
 < HTTP/1.1 303 See Other
 < Date: ``
diff --git a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py
index 5c4590f..c9d6d73 100644
--- a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py
+++ b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py
@@ -54,7 +54,7 @@ ts.Disk.remap_config.AddLine(
 
 # call localhost straight
 tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl --proxy 127.0.0.1:{0} "http://www.example.com" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl --proxy 127.0.0.1:{0} "http://www.example.com" -H "Proxy-Connection: keep-alive" --verbose'.format(ts.Variables.port)
 tr.Processes.Default.ReturnCode=0
 # time delay as proxy.config.http.wait_for_cache could be broken
 tr.Processes.Default.StartBefore(server,ready=When.PortOpen(server.Variables.Port))
diff --git a/tests/gold_tests/remap/gold/remap-200.gold b/tests/gold_tests/remap/gold/remap-200.gold
index e4f5eb9..ec2f310 100644
--- a/tests/gold_tests/remap/gold/remap-200.gold
+++ b/tests/gold_tests/remap/gold/remap-200.gold
@@ -3,7 +3,7 @@
 > Host: www.example.com``
 > User-Agent: curl/``
 > Accept: */*
-> Proxy-Connection: Keep-Alive
+> Proxy-Connection: keep-alive
 ``
 < HTTP/1.1 200 OK
 < Date: ``
diff --git a/tests/gold_tests/remap/remap_http.test.py b/tests/gold_tests/remap/remap_http.test.py
index 2f6e007..a6fa9cb 100644
--- a/tests/gold_tests/remap/remap_http.test.py
+++ b/tests/gold_tests/remap/remap_http.test.py
@@ -60,31 +60,31 @@ tr.StillRunningAfter=server
 
 # www.example.com host
 tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl --proxy 127.0.0.1:{0} "http://www.example.com" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl --proxy 127.0.0.1:{0} "http://www.example.com"  -H "Proxy-Connection: keep-alive" --verbose'.format(ts.Variables.port)
 tr.Processes.Default.ReturnCode=0
 tr.Processes.Default.Streams.stderr="gold/remap-200.gold"
 
 # www.example.com:80 host
 tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.example.com:80/" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.example.com:80/"  -H "Proxy-Connection: keep-alive" --verbose'.format(ts.Variables.port)
 tr.Processes.Default.ReturnCode=0
 tr.Processes.Default.Streams.stderr="gold/remap-200.gold"
 
 # www.example.com:8080 host
 tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.example.com:8080" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.example.com:8080"  -H "Proxy-Connection: keep-alive" --verbose'.format(ts.Variables.port)
 tr.Processes.Default.ReturnCode=0
 tr.Processes.Default.Streams.stderr="gold/remap-200.gold"
 
 # no rule for this
 tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.test.com/" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.test.com/"  -H "Proxy-Connection: keep-alive" --verbose'.format(ts.Variables.port)
 tr.Processes.Default.ReturnCode=0
 tr.Processes.Default.Streams.stderr="gold/remap-404.gold"
 
 # bad port
 tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.example.com:1234/" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.example.com:1234/"  -H "Proxy-Connection: keep-alive" --verbose'.format(ts.Variables.port)
 tr.Processes.Default.ReturnCode=0
 tr.Processes.Default.Streams.stderr="gold/remap-404.gold"
 

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 13/14: tweak gold files to not look for proxy-connection Curl adds Proxy-connection on it own in some cases, and or duplicates it more than once

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0b383f461edb17534c5076171619bd59779782a1
Author: Jason <dr...@live.com>
AuthorDate: Fri Mar 3 10:44:29 2017 -0600

    tweak gold files to not look for proxy-connection
    Curl adds Proxy-connection on it own in some cases, and or duplicates it more than once
---
 tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold | 1 -
 tests/gold_tests/remap/gold/remap-200.gold                              | 1 -
 2 files changed, 2 deletions(-)

diff --git a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold
index 5d55f9e..94a42ad 100644
--- a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold
+++ b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold
@@ -3,7 +3,6 @@
 > Host: www.example.com``
 > User-Agent: curl/``
 > Accept: */*
-> Proxy-Connection: keep-alive
 ``
 < HTTP/1.1 303 See Other
 < Date: ``
diff --git a/tests/gold_tests/remap/gold/remap-200.gold b/tests/gold_tests/remap/gold/remap-200.gold
index ec2f310..619066a 100644
--- a/tests/gold_tests/remap/gold/remap-200.gold
+++ b/tests/gold_tests/remap/gold/remap-200.gold
@@ -3,7 +3,6 @@
 > Host: www.example.com``
 > User-Agent: curl/``
 > Accept: */*
-> Proxy-Connection: keep-alive
 ``
 < HTTP/1.1 200 OK
 < Date: ``

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 11/14: Update getting_started.md

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 43fde5f80d52cc2a428462960c38bb058d9a3589
Author: dragon512 <dr...@live.com>
AuthorDate: Thu Mar 2 09:33:51 2017 -0600

    Update getting_started.md
    
    Fix path to / to ./
---
 tests/getting_started.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/getting_started.md b/tests/getting_started.md
index b152348..3d36a9d 100644
--- a/tests/getting_started.md
+++ b/tests/getting_started.md
@@ -25,7 +25,7 @@ This script should try to install python35 or better on the system, and needed p
 # Advance setup
 
 AuTest can be install manually instead of using the wrapper script. The advange of this is that it is often easier to debug issues with the testing system, or the tests. There are two ways this can be done.
-1. run the bootstrap script then source the path with a "source /env-test/bin/activate" command. At this point autest command should run without the wrapper script
+1. run the bootstrap script then source the path with a "source ./env-test/bin/activate" command. At this point autest command should run without the wrapper script
 2. The other way is to make sure you install python 3.5 or better on your system. From there install these python packages ( ie pip install ):
   - hyper
   - git+https://bitbucket.org/dragon512/reusable-gold-testing-system.git 

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 09/14: tweak typo

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4e6120ba9f19e1b7c3c3ef3ae7a62a53472882c8
Author: Jason <dr...@live.com>
AuthorDate: Fri Feb 24 15:53:29 2017 -0600

    tweak typo
---
 tests/bootstrap.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/bootstrap.py b/tests/bootstrap.py
index 7980928..30c5026 100755
--- a/tests/bootstrap.py
+++ b/tests/bootstrap.py
@@ -170,7 +170,7 @@ def venv_cmds(path):
     to the path being used for the script
     '''
 
-    return = [
+    return [
         # first command only needed for rhel and centos systems at this time
         extra + " virtualenv --python=python3 {0}".format(path),
         extra +" {0}/bin/pip install pip --upgrade".format(path)

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 14/14: fix issue with virtualenv on Fedora 25

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f778a8f654c089adf10298c4451663fe5d198b1d
Author: Jason Kenny <dr...@live.com>
AuthorDate: Fri Mar 3 11:57:12 2017 -0500

    fix issue with virtualenv on Fedora 25
---
 tests/bootstrap.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/bootstrap.py b/tests/bootstrap.py
index 30c5026..285be0a 100755
--- a/tests/bootstrap.py
+++ b/tests/bootstrap.py
@@ -40,6 +40,7 @@ distro_packages = {
     "Fedora": [
         "install python3",
         "install python3-virtualenv",
+        "install python-virtualenv",
     ],
     "Ubuntu": [
         "install python3",

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 05/14: address bootstrap issue with rhel and centos

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 86144248d5f7cae292a5e60a74619b55ca992f01
Author: Jason Kenny <dr...@live.com>
AuthorDate: Sat Feb 25 01:56:58 2017 -0600

    address bootstrap issue with rhel and centos
---
 tests/autest.sh    |  2 ++
 tests/bootstrap.py | 10 +++++-----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/tests/autest.sh b/tests/autest.sh
index 05656b6..2efc491 100755
--- a/tests/autest.sh
+++ b/tests/autest.sh
@@ -26,6 +26,8 @@ if [ ! -f ./env-test/bin/autest ]; then\
 		./bootstrap.py;\
         echo -e "${GREEN}Done!${NC}";\
 	fi
+# this is for rhel or centos systems
+. /opt/rh/rh-python35/enable || true ;
 . env-test/bin/activate
 ./env-test/bin/autest -D gold_tests $*
 ret=$?
diff --git a/tests/bootstrap.py b/tests/bootstrap.py
index cde71b8..babe621 100755
--- a/tests/bootstrap.py
+++ b/tests/bootstrap.py
@@ -48,8 +48,7 @@ distro_packages = {
     ],
     "CentOS": [
         "install epel-release",
-        "install python34",
-        "install rh-python34-python-virtualenv"
+        "install rh-python35-python-virtualenv"
     ]
 }
 
@@ -167,8 +166,9 @@ def venv_cmds(path):
     to the path being used for the script
     '''
     return [
-        "virtualenv --python=python3 {0}".format(path),
-        "{0}/bin/pip install pip --upgrade".format(path)
+        # first command only needed for rhel and centos systems at this time
+        ". /opt/rh/rh-python35/enable || true ; virtualenv --python=python3 {0}".format(path),
+        ". /opt/rh/rh-python35/enable || true ; {0}/bin/pip install pip --upgrade".format(path)
     ]
 
 
@@ -217,7 +217,7 @@ def main():
             path_to_pip = os.path.join(args.venv_path, "bin", args.use_pip)
 
     # install pip packages
-    cmds += ["{0} install {1}".format(path_to_pip, " ".join(pip_packages))]
+    cmds += [". /opt/rh/rh-python35/enable || true ;{0} install {1}".format(path_to_pip, " ".join(pip_packages))]
 
     run_cmds(cmds)
 

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 08/14: more clean up to bootstrap.py

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a79347d85c4168511de8cb52f1108dc44d5e4c5f
Author: Jason <dr...@live.com>
AuthorDate: Fri Feb 24 15:51:39 2017 -0600

    more clean up to bootstrap.py
---
 tests/bootstrap.py | 31 ++++++++++++-------------------
 1 file changed, 12 insertions(+), 19 deletions(-)

diff --git a/tests/bootstrap.py b/tests/bootstrap.py
index 480a90b..7980928 100755
--- a/tests/bootstrap.py
+++ b/tests/bootstrap.py
@@ -160,25 +160,22 @@ def gen_package_cmds(packages):
     return ret
 
 
+extra=''
+if distro() == 'RHEL' or distro() == 'CentOS':
+    extra = ". /opt/rh/rh-python35/enable ;"
+
 def venv_cmds(path):
     '''
     Create virtual environment and add it
     to the path being used for the script
     '''
 
-    if isRedHatBased():
-        ret = [
-            # first command only needed for rhel and centos systems at this time
-            ". /opt/rh/rh-python35/enable || true ; virtualenv --python=python3 {0}".format(path),
-            ". /opt/rh/rh-python35/enable || true ; {0}/bin/pip install pip --upgrade".format(path)
-        ]
-    else:
-        ret = [
-            # first command only needed for rhel and centos systems at this time
-            "virtualenv --python=python3 {0}".format(path),
-            "{0}/bin/pip install pip --upgrade".format(path)
-        ]
-    return ret
+    return = [
+        # first command only needed for rhel and centos systems at this time
+        extra + " virtualenv --python=python3 {0}".format(path),
+        extra +" {0}/bin/pip install pip --upgrade".format(path)
+    ]
+    
 
 
 def main():
@@ -224,12 +221,8 @@ def main():
         cmds += venv_cmds(args.venv_path)
         if path_to_pip is None:
             path_to_pip = os.path.join(args.venv_path, "bin", args.use_pip)
-
-    # install pip packages
-    if isRedHatBased():
-        cmds += [". /opt/rh/rh-python35/enable || true ;{0} install {1}".format(path_to_pip, " ".join(pip_packages))]
-    else:
-        cmds += ["{0} install {1}".format(path_to_pip, " ".join(pip_packages))]
+    
+    cmds += [extra + "{0} install {1}".format(path_to_pip, " ".join(pip_packages))]
 
     run_cmds(cmds)
 

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 03/14: update document

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 6b490121d09f1d1ab02d526e45fee80a2b7dfc75
Author: Jason Kenny <dr...@live.com>
AuthorDate: Wed Feb 22 15:01:53 2017 -0600

    update document
---
 tests/getting_started.md | 74 +++++++++++++++++++++++++++++-------------------
 1 file changed, 45 insertions(+), 29 deletions(-)

diff --git a/tests/getting_started.md b/tests/getting_started.md
index f3df186..b152348 100644
--- a/tests/getting_started.md
+++ b/tests/getting_started.md
@@ -40,12 +40,28 @@ Autest allows for the creation of extension to help specilaize and simplify test
 
 This command line argument will point to your build of ATS you want to test. At this time v6.0 or newer of Trafficserver should work.
 
-###MakeATSProcess(name,command=[traffic_server],select_ports=[True])
+### MakeATSProcess(name,command=[traffic_server],select_ports=[True])
  * name - A name for this instance of ATS
  * command - optional argument defining what process to use. Defaults to traffic_server.
  * select_ports - have the testing system auto select the ports to use for this instance of ATS
 
-This function will return a AuTest process object that will have a number of files and variables define for making it easier to define a test.
+This function will define a sandbox for an instance of trafficserver to run under. The function will return a AuTest process object that will have a number of files and variables define for making it easier to define a test.
+
+#### Environment
+The environment of the process will have a number of added environment variables to control trafficserver running the in the sandbox location correctly. This can be used to easily setup other commands that should run under same environment.
+
+##### Example
+
+```python
+# Define default ATS
+ts=Test.MakeATSProcess("ts")
+# Call traffic_ctrl to set new generation
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='traffic_ctl'
+tr.Processes.Default.ReturnCode=0
+# set the environment for traffic_control to run in to be the same as the "ts" ATS instance
+tr.Processes.Default.Env=ts.Env 
+```
 
 #### Variables
 These are the current variable that are define dynamically 
@@ -127,20 +143,44 @@ ts.Disk.remap_config.AddLines([
         ])
 ```
 
+### CopyConfig(file, targetname=None, process=None)
+* file - name of the file to copy. Relative paths are relative from the test file location
+* targetname - the name name of the file when copied to the correct configuration location
+* process - optional process object to use for getting path location to copy to. Only needed if the Setup object call is not in the scope of the process object created with the MakeATSProcess(...) API.
+
+This function copies a given configuration file the location of a given trafficserver sandbox used in a test. Given a test might have more than on trafficserver instance, it can be difficult to understand the correct location to copy to. This function will deal with the details correctly.
+
+#### Examples
+
+Copy a file over 
+
+```python
+ts1 = Test.MakeATSProcess("ts1",select_ports=False)
+# uses the setup object in the scope of the process object
+ts1.Setup.ts.CopyConfig('config/records_8090.config','records.config')
+```
+```python
+ts1 = Test.MakeATSProcess("ts1",select_ports=False)
+# uses the Setup in the global process via a variable passing
+Test.Setup.ts.CopyConfig('config/records_8090.config','records.config',ts1)
+# same as above, but uses the dynamic object model form
+Test.Setup.ts.CopyConfig('config/records_8090.config','records.config',Test.Processes.ts1)
+```
+
 ## Setup Origin Server
-###Test.MakeOriginServer(Name)
+### Test.MakeOriginServer(Name)
  * name - A name for this instance of Origin Server.
  
  This function returns a AuTest process object that launches the python-based microserver. Micro-Server is a mock server which responds to client http requests. Microserver needs to be setup for the tests that require an origin server behind ATS. The server reads a JSON-formatted data file that contains request headers and the corresponding response headers. Microserver responds with payload if the response header contains Content-Length or Transfer-Enconding specified.
  
-###addResponse(filename, request_header, response_header)
+### addResponse(filename, request_header, response_header)
 * filename - name of the file where the request header and response header will be written to in JSON format
 * request_header - dictionary of request header
 * response_header - dictionary of response header corresponding to the request header.
 
 This function adds the request header and response header to a file which is then read by the microserver to populate request-response map. The key-fields required for the header dictionary are 'headers', 'timestamp' and 'body'.
 
-###Example
+### Example
 ```python
 #create the origin server process
 server=Test.MakeOriginServer("server")
@@ -155,27 +195,3 @@ ts.Disk.remap_config.AddLine(
     'map http://www.example.com http://127.0.0.1:{0}'.format(server.Variables.Port)
 )
 ```
-### CopyConfig(file, targetname=None, process=None)
-* file - name of the file to copy. Relative paths are relative from the test file location
-* targetname - the name name of the file when copied to the correct configuration location
-* process - optional process object to use for getting path location to copy to. Only needed if the Setup object call is not in the scope of the process object created with the MakeATSProcess(...) API.
-
-This function copies a given configuration file the location of a given trafficserver sandbox used in a test. Given a test might have more than on trafficserver instance, it can be difficult to understand the correct location to copy to. This function will deal with the details correctly.
-
-#### Examples
-
-Copy a file over 
-
-```python
-ts1 = Test.MakeATSProcess("ts1",select_ports=False)
-# uses the setup object in the scope of the process object
-ts1.Setup.ts.CopyConfig('config/records_8090.config','records.config')
-```
-```python
-ts1 = Test.MakeATSProcess("ts1",select_ports=False)
-# uses the Setup in the global process via a variable passing
-Test.Setup.ts.CopyConfig('config/records_8090.config','records.config',ts1)
-# same as above, but uses the dynamic object model form
-Test.Setup.ts.CopyConfig('config/records_8090.config','records.config',Test.Processes.ts1)
-```
-

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 06/14: better fix for RHEL and CentOS systems

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a4fb5bc8e95f4d674fc0e22c4a1084ddb07a9aed
Author: Jason <dr...@live.com>
AuthorDate: Fri Feb 24 14:35:05 2017 -0600

    better fix for RHEL and CentOS systems
---
 tests/autest.sh    |  2 +-
 tests/bootstrap.py | 24 ++++++++++++++++++------
 2 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/tests/autest.sh b/tests/autest.sh
index 2efc491..087ea00 100755
--- a/tests/autest.sh
+++ b/tests/autest.sh
@@ -27,7 +27,7 @@ if [ ! -f ./env-test/bin/autest ]; then\
         echo -e "${GREEN}Done!${NC}";\
 	fi
 # this is for rhel or centos systems
-. /opt/rh/rh-python35/enable || true ;
+test -x /opt/rh/rh-python35/enable && . /opt/rh/rh-python35/enable
 . env-test/bin/activate
 ./env-test/bin/autest -D gold_tests $*
 ret=$?
diff --git a/tests/bootstrap.py b/tests/bootstrap.py
index babe621..480a90b 100755
--- a/tests/bootstrap.py
+++ b/tests/bootstrap.py
@@ -165,11 +165,20 @@ def venv_cmds(path):
     Create virtual environment and add it
     to the path being used for the script
     '''
-    return [
-        # first command only needed for rhel and centos systems at this time
-        ". /opt/rh/rh-python35/enable || true ; virtualenv --python=python3 {0}".format(path),
-        ". /opt/rh/rh-python35/enable || true ; {0}/bin/pip install pip --upgrade".format(path)
-    ]
+
+    if isRedHatBased():
+        ret = [
+            # first command only needed for rhel and centos systems at this time
+            ". /opt/rh/rh-python35/enable || true ; virtualenv --python=python3 {0}".format(path),
+            ". /opt/rh/rh-python35/enable || true ; {0}/bin/pip install pip --upgrade".format(path)
+        ]
+    else:
+        ret = [
+            # first command only needed for rhel and centos systems at this time
+            "virtualenv --python=python3 {0}".format(path),
+            "{0}/bin/pip install pip --upgrade".format(path)
+        ]
+    return ret
 
 
 def main():
@@ -217,7 +226,10 @@ def main():
             path_to_pip = os.path.join(args.venv_path, "bin", args.use_pip)
 
     # install pip packages
-    cmds += [". /opt/rh/rh-python35/enable || true ;{0} install {1}".format(path_to_pip, " ".join(pip_packages))]
+    if isRedHatBased():
+        cmds += [". /opt/rh/rh-python35/enable || true ;{0} install {1}".format(path_to_pip, " ".join(pip_packages))]
+    else:
+        cmds += ["{0} install {1}".format(path_to_pip, " ".join(pip_packages))]
 
     run_cmds(cmds)
 

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 12/14: Fix custom log test Added 10 second delay to get AST to write out the log file Added a verbose message to help clarify a conf file was written as expected

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 2779fae61c44c9f296c51eee82da13147d791f36
Author: Jason <dr...@live.com>
AuthorDate: Thu Mar 2 17:12:17 2017 -0600

    Fix custom log test
    Added 10 second delay to get AST to write out the log file
    Added a verbose message to help clarify a conf file was written as expected
---
 .../gold_tests/autest-site/trafficserver.test.ext  |   2 +
 tests/gold_tests/logging/custom-log.test.py        | 103 +++++++++++++++++++++
 tests/gold_tests/logging/custom.test.py            |  85 -----------------
 3 files changed, 105 insertions(+), 85 deletions(-)

diff --git a/tests/gold_tests/autest-site/trafficserver.test.ext b/tests/gold_tests/autest-site/trafficserver.test.ext
index 9fef2d1..f8e2dc8 100644
--- a/tests/gold_tests/autest-site/trafficserver.test.ext
+++ b/tests/gold_tests/autest-site/trafficserver.test.ext
@@ -266,6 +266,7 @@ class Config(File):
         '''
         Write contents to disk
         '''
+        host.WriteVerbosef('ats-config-file',"Writting out file {0}",self.Name)
         if self.content is not None:
             with open(name, 'w') as f:
                 f.write(self.content)
@@ -307,6 +308,7 @@ class RecordsConfig(Config, dict):
         self.WriteCustomOn(self._do_write)
 
     def _do_write(self,name):
+        host.WriteVerbosef('ats-config-file',"Writting out file {0}",name)
         if len(self) > 0:
             with open(name, 'w') as f:
                 for name, val in self.items():
diff --git a/tests/gold_tests/logging/custom-log.test.py b/tests/gold_tests/logging/custom-log.test.py
new file mode 100644
index 0000000..f58f146
--- /dev/null
+++ b/tests/gold_tests/logging/custom-log.test.py
@@ -0,0 +1,103 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+
+Test.Summary = '''
+Test custom log file format
+'''
+# need Curl
+Test.SkipUnless(
+    Condition.HasProgram(
+        "curl", "Curl need to be installed on system for this test to work"),
+    Condition.IsPlatform("linux")
+)
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts")
+
+# setup some config file for this server
+ts.Disk.remap_config.AddLine(
+    'map / http://www.linkedin.com/ @action=deny'
+)
+
+ts.Disk.logging_config.AddLines(
+    '''custom = format {
+  Format = "%<hii> %<hiih>"
+}
+
+log.ascii {
+  Format = custom,
+  Filename = 'test_log_field'
+}'''.split("\n")
+)
+
+# #########################################################################
+# at the end of the different test run a custom log file should exist
+# Because of this we expect the testruns to pass the real test is if the
+# customlog file exists and passes the format check
+Test.Disk.File(os.path.join(ts.Variables.LOGDIR, 'test_log_field.log'),
+               exists=True, content='gold/custom.gold')
+
+# first test is a miss for default
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl "http://127.0.0.1:{0}" --verbose'.format(
+    ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+# time delay as proxy.config.http.wait_for_cache could be broken
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl "http://127.1.1.1:{0}" --verbose'.format(
+    ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl "http://127.2.2.2:{0}" --verbose'.format(
+    ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl "http://127.3.3.3:{0}" --verbose'.format(
+    ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl "http://127.3.0.1:{0}" --verbose'.format(
+    ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl "http://127.43.2.1:{0}" --verbose'.format(
+    ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl "http://127.213.213.132:{0}" --verbose'.format(
+    ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl "http://127.123.32.243:{0}" --verbose'.format(
+    ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+
+tr = Test.AddTestRun()
+tr.DelayStart=10
+tr.Processes.Default.Command = 'echo "Delay"'
+tr.Processes.Default.ReturnCode = 0
\ No newline at end of file
diff --git a/tests/gold_tests/logging/custom.test.py b/tests/gold_tests/logging/custom.test.py
deleted file mode 100644
index c558eb3..0000000
--- a/tests/gold_tests/logging/custom.test.py
+++ /dev/null
@@ -1,85 +0,0 @@
-'''
-'''
-#  Licensed to the Apache Software Foundation (ASF) under one
-#  or more contributor license agreements.  See the NOTICE file
-#  distributed with this work for additional information
-#  regarding copyright ownership.  The ASF licenses this file
-#  to you under the Apache License, Version 2.0 (the
-#  "License"); you may not use this file except in compliance
-#  with the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-import os
-
-Test.Summary = '''
-Test custom log file format
-'''
-# need Curl
-Test.SkipUnless(
-    Condition.HasProgram("curl", "Curl need to be installed on system for this test to work"),
-    Condition.IsPlatform("linux")
-    )
-
-# Define default ATS
-ts=Test.MakeATSProcess("ts")
-
-# setup some config file for this server
-ts.Disk.remap_config.AddLine(
-            'map / http://www.linkedin.com/ @action=deny'
-        )
-
-ts.Disk.logging_config.AddLines(
-            '''custom = format {
-  Format = "%<hii> %<hiih>"
-}
-
-log.ascii {
-  Format = custom,
-  Filename = 'test_log_field'
-}'''.split("\n")
-        )
-# at the end of the different test run a custom log file should exist
-Test.Disk.File(os.path.join(ts.Variables.LOGDIR,'test_log_field.log'),exists=True,content='gold/custom.gold')
-
-#first test is a miss for default
-tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl "http://127.0.0.1:{0}" --verbose'.format(ts.Variables.port)
-tr.Processes.Default.ReturnCode=0
-# time delay as proxy.config.http.wait_for_cache could be broken
-tr.Processes.Default.StartBefore(Test.Processes.ts)
-        
-tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl "http://127.1.1.1:{0}" --verbose'.format(ts.Variables.port)
-tr.Processes.Default.ReturnCode=0
-
-tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl "http://127.2.2.2:{0}" --verbose'.format(ts.Variables.port)
-tr.Processes.Default.ReturnCode=0
-
-tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl "http://127.3.3.3:{0}" --verbose'.format(ts.Variables.port)
-tr.Processes.Default.ReturnCode=0
-
-tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl "http://127.3.0.1:{0}" --verbose'.format(ts.Variables.port)
-tr.Processes.Default.ReturnCode=0
-
-tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl "http://127.43.2.1:{0}" --verbose'.format(ts.Variables.port)
-tr.Processes.Default.ReturnCode=0
-
-tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl "http://127.213.213.132:{0}" --verbose'.format(ts.Variables.port)
-tr.Processes.Default.ReturnCode=0
-
-tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl "http://127.123.32.243:{0}" --verbose'.format(ts.Variables.port)
-tr.Processes.Default.ReturnCode=0    
-

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 04/14: add execute permission to boostrap.py script

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a2847b3a2753ba010573c7d27a68a316738786fa
Author: Jason Kenny <dr...@live.com>
AuthorDate: Thu Feb 23 21:30:41 2017 -0600

    add execute permission to boostrap.py script
---
 tests/autest.sh    | 0
 tests/bootstrap.py | 2 +-
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/autest.sh b/tests/autest.sh
old mode 100644
new mode 100755
diff --git a/tests/bootstrap.py b/tests/bootstrap.py
old mode 100644
new mode 100755
index 3a3c126..cde71b8
--- a/tests/bootstrap.py
+++ b/tests/bootstrap.py
@@ -26,7 +26,7 @@ import platform
 import sys
 
 pip_packages = [
-    "git+https://bitbucket.org/dragon512/reusable-gold-testing-system.git",
+    "autest",
     "hyper"
 ]
 

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 01/14: Add new testing infra to ATS

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8c96e13cfff9721e1526f558dbbfd5428f93f2fc
Author: Jason Kenny <dr...@live.com>
AuthorDate: Wed Feb 22 10:30:10 2017 -0600

    Add new testing infra to ATS
    
    Added new testing code
    Added Apache license header
    Add some getting started documentation
---
 NOTICE                                             |   6 +
 tests/autest.sh                                    |  33 ++
 tests/bootstrap.py                                 | 226 ++++++++
 tests/getting_started.md                           | 153 +++++
 tests/gold_tests/autest-site/copy_config.test.ext  |  49 ++
 tests/gold_tests/autest-site/init.cli.ext          |  27 +
 tests/gold_tests/autest-site/microserver.test.ext  | 118 ++++
 tests/gold_tests/autest-site/min_cfg/cache.config  |   1 +
 .../gold_tests/autest-site/min_cfg/hosting.config  |   1 +
 .../gold_tests/autest-site/min_cfg/ip_allow.config |   4 +
 tests/gold_tests/autest-site/min_cfg/parent.config |   1 +
 tests/gold_tests/autest-site/min_cfg/readme.txt    |   3 +
 .../gold_tests/autest-site/min_cfg/records.config  |   1 +
 tests/gold_tests/autest-site/min_cfg/remap.config  |   1 +
 .../autest-site/min_cfg/ssl_multicert.config       |   1 +
 .../gold_tests/autest-site/min_cfg/storage.config  |   4 +
 tests/gold_tests/autest-site/min_cfg/volume.config |   1 +
 tests/gold_tests/autest-site/ports.py              | 107 ++++
 tests/gold_tests/autest-site/setup.cli.ext         |  49 ++
 .../gold_tests/autest-site/trafficserver.test.ext  | 323 +++++++++++
 tests/gold_tests/basic/basic-cop.test.py           |  33 ++
 tests/gold_tests/basic/basic-manager.test.py       |  33 ++
 tests/gold_tests/basic/basic.test.py               |  33 ++
 tests/gold_tests/basic/config.test.py              |  33 ++
 tests/gold_tests/basic/config/records_8090.config  |   2 +
 tests/gold_tests/basic/config/records_8091.config  |   2 +
 tests/gold_tests/basic/config/remap.config         |   1 +
 tests/gold_tests/basic/copy_config.test.py         |  49 ++
 tests/gold_tests/basic/copy_config2.test.py        |  46 ++
 tests/gold_tests/body_factory/config/remap.config  |   1 +
 .../body_factory/custom_response.test.py           |  64 +++
 .../cache/cache-generation-clear.test.py           |  90 +++
 .../cache/cache-generation-disjoint.test.py        |  88 +++
 .../cache/disjoint-wait-for-cache.test.py          |  88 +++
 tests/gold_tests/cache/gold/hit_default-1.gold     |   8 +
 tests/gold_tests/cache/gold/hit_default77.gold     |   8 +
 tests/gold_tests/cache/gold/hit_gen1.gold          |   8 +
 tests/gold_tests/cache/gold/hit_gen2.gold          |   8 +
 tests/gold_tests/cache/gold/miss_default-1.gold    |   8 +
 tests/gold_tests/cache/gold/miss_default77.gold    |   8 +
 tests/gold_tests/cache/gold/miss_gen1.gold         |   8 +
 tests/gold_tests/cache/gold/miss_gen2.gold         |   8 +
 tests/gold_tests/h2/gold/remap-200.gold            |   4 +
 tests/gold_tests/h2/h2client.py                    |  63 +++
 tests/gold_tests/h2/http2.test.py                  |  70 +++
 tests/gold_tests/h2/ssl/server.key                 |  15 +
 tests/gold_tests/h2/ssl/server.pem                 |  32 ++
 tests/gold_tests/logging/custom.test.py            |  85 +++
 tests/gold_tests/logging/gold/custom.gold          |   8 +
 .../header_rewrite/gold/header_rewrite-303.gold    |  15 +
 .../header_rewrite/gold/header_rewrite-tag.gold    |   1 +
 .../header_rewrite/header_rewrite.test.py          |  68 +++
 .../pluginTest/header_rewrite/rules/rule.conf      |   2 +
 tests/gold_tests/remap/gold/remap-200.gold         |  15 +
 tests/gold_tests/remap/gold/remap-404.gold         |  12 +
 tests/gold_tests/remap/gold/remap-hitATS-404.gold  |  11 +
 tests/gold_tests/remap/gold/remap-https-200.gold   |  13 +
 tests/gold_tests/remap/remap_http.test.py          |  91 +++
 tests/gold_tests/remap/remap_https.test.py         | 112 ++++
 tests/gold_tests/remap/ssl/server.key              |  15 +
 tests/gold_tests/remap/ssl/server.pem              |  32 ++
 tests/tools/microServer/ssl/server.crt             |  17 +
 tests/tools/microServer/ssl/server.pem             |  32 ++
 tests/tools/microServer/uWServer.py                | 627 +++++++++++++++++++++
 tests/tools/readme.txt                             |  20 +
 tests/tools/sessionvalidation/__init__.py          |  17 +
 tests/tools/sessionvalidation/badsession.py        |  33 ++
 tests/tools/sessionvalidation/request.py           |  47 ++
 tests/tools/sessionvalidation/response.py          |  38 ++
 tests/tools/sessionvalidation/session.py           |  41 ++
 tests/tools/sessionvalidation/sessionvalidation.py | 269 +++++++++
 tests/tools/sessionvalidation/transaction.py       |  39 ++
 72 files changed, 3580 insertions(+)

diff --git a/NOTICE b/NOTICE
index 5feedac..7209189 100644
--- a/NOTICE
+++ b/NOTICE
@@ -103,3 +103,9 @@ Copyright 2014 Google Inc. All Rights Reserved.
 
 plugins/experimental/memcache/protocol_binary.h developed by Sun Microsystems, Inc.
 Copyright (c) <2008>, Sun Microsystems, Inc.  All rights reserved.
+
+~~
+
+Reusable Gold Testing System  
+https://bitbucket.org/dragon512/reusable-gold-testing-system
+Copyright (c) 2015-2016 Jason Kenny All Rights Reserved.
diff --git a/tests/autest.sh b/tests/autest.sh
new file mode 100644
index 0000000..05656b6
--- /dev/null
+++ b/tests/autest.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+#  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.
+
+pushd $(dirname $0) > /dev/null
+export PYTHONPATH=$(pwd):$PYTHONPATH
+RED='\033[0;31m'
+GREEN='\033[1;32m'
+NC='\033[0m' # No Color
+if [ ! -f ./env-test/bin/autest ]; then\
+        echo -e "${RED}AuTest is not installed! Bootstrapping system...${NC}";\
+		./bootstrap.py;\
+        echo -e "${GREEN}Done!${NC}";\
+	fi
+. env-test/bin/activate
+./env-test/bin/autest -D gold_tests $*
+ret=$?
+popd > /dev/null
+exit $ret
diff --git a/tests/bootstrap.py b/tests/bootstrap.py
new file mode 100644
index 0000000..3a3c126
--- /dev/null
+++ b/tests/bootstrap.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# this script sets up the testing packages to allow the tests
+
+#  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.
+
+from __future__ import absolute_import, division, print_function
+
+import argparse
+import os
+import subprocess
+import platform
+import sys
+
+pip_packages = [
+    "git+https://bitbucket.org/dragon512/reusable-gold-testing-system.git",
+    "hyper"
+]
+
+
+distro_packages = {
+    "RHEL": [
+        "install epel-release",
+        "install python35",
+        "install rh-python35-python-virtualenv"
+    ],
+    "Fedora": [
+        "install python3",
+        "install python3-virtualenv",
+    ],
+    "Ubuntu": [
+        "install python3",
+        "install python3-virtualenv",
+        "install virtualenv"
+    ],
+    "CentOS": [
+        "install epel-release",
+        "install python34",
+        "install rh-python34-python-virtualenv"
+    ]
+}
+
+
+def command_output(cmd_str):
+    print(cmd_str)
+    proc = subprocess.Popen(
+        cmd_str,
+        shell=True,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.STDOUT,
+        universal_newlines=True)
+
+    # while command runs get output
+    while proc.poll() == None:
+        tmp = proc.stdout.readline()
+        sys.stdout.write(tmp)
+
+    for last_output in proc.stdout.readlines():
+        sys.stdout.write(last_output)
+
+    return proc.returncode
+
+
+def get_distro():
+    return platform.linux_distribution()
+
+
+def distro_version():
+    return int(get_distro()[1].split(".")[0])
+
+
+def isFedora():
+    return get_distro()[0].startswith("Fedora")
+
+
+def isCentOS():
+    return get_distro()[0].startswith("CentOS")
+
+
+def distro():
+    if isFedora():
+        return "Fedora"
+    if isCentOS():
+        return "CentOS"
+    if get_distro()[0].startswith("Red Hat"):
+        return "RHEL"
+    if get_distro()[0].startswith("Ubuntu"):
+        return "Ubuntu"
+
+
+def isRedHatBased():
+    return get_distro()[0].startswith("Red Hat") or get_distro()[0].startswith(
+        "Fedora") or get_distro()[0].startswith("CentOS")
+
+
+def isInstalled(prog):
+    out = subprocess.Popen(
+        ["which", prog], stdout=subprocess.PIPE).communicate()
+    if out[0] != '':
+        return True
+    return False
+
+
+def installManagerName():
+    if isRedHatBased() and distro_version() >= 22:
+        ret = "sudo dnf -y"  # Fedora 22 or newer
+    elif isRedHatBased():
+        ret = "sudo yum -y"  # Red Hat distro
+    else:
+        ret = "sudo apt-get -y"  # Ubuntu/Debian
+
+    return ret
+
+
+def installToolName():
+    if isRedHatBased():
+        ret = "rpm -ihv"  # Red Hat Based
+    else:
+        ret = "dpkg -iv"  # Ubuntu/Debian
+
+    return ret
+
+
+def run_cmds(cmds):
+    for cmd in cmds:
+        # print (cmd.split[" "])
+        # subprocess.call(cmd.split[" "])
+        if command_output(cmd):
+            print("'{0}'' - Failed".format(cmd))
+
+
+def gen_package_cmds(packages):
+
+    # main install tool/manager (yum, dnf, apt-get, etc)
+    mtool = installManagerName()
+    # core install tool (rpm, dpkg, etc)
+    itool = installToolName()
+    ret = []
+
+    for p in packages:
+        if p.startswith("wget"):
+            pth = p[5:]
+            pack = os.path.split(pth)[1]
+            cmd = ["wget {0}".format(pth), "{0} ./{1}".format(itool, pack)]
+        else:
+            cmd = ["{0} {1}".format(mtool, p)]
+        ret.extend(cmd)
+    return ret
+
+
+def venv_cmds(path):
+    '''
+    Create virtual environment and add it
+    to the path being used for the script
+    '''
+    return [
+        "virtualenv --python=python3 {0}".format(path),
+        "{0}/bin/pip install pip --upgrade".format(path)
+    ]
+
+
+def main():
+    " main script logic"
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument(
+        "--use-pip", nargs='?', default="pip", help="Which pip to use")
+
+    parser.add_argument(
+        "venv_path",
+        nargs='?',
+        default="env-test",
+        help="The directory to us to for the virtualenv")
+
+    parser.add_argument(
+        "--disable-virtualenv",
+        default=False,
+        action='store_true',
+        help="Do not create virtual environment to install packages under")
+
+    parser.add_argument(
+        '-V', '--version', action='version', version='%(prog)s 1.0.0')
+
+    args = parser.parse_args()
+    # print(args)
+    # print(get_distro())
+
+    # do we know of packages to install for the given platform
+    dist = distro()
+    cmds = []
+    if dist:
+        cmds = gen_package_cmds(distro_packages[dist])
+
+    # test to see if we should use a certain version of pip
+    path_to_pip = None
+    if args.use_pip != "pip":
+        path_to_pip = args.use_pip
+
+    # install on the system, or use virtualenv for pip based stuff
+    if not args.disable_virtualenv:
+        # Create virtual env
+        cmds += venv_cmds(args.venv_path)
+        if path_to_pip is None:
+            path_to_pip = os.path.join(args.venv_path, "bin", args.use_pip)
+
+    # install pip packages
+    cmds += ["{0} install {1}".format(path_to_pip, " ".join(pip_packages))]
+
+    run_cmds(cmds)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tests/getting_started.md b/tests/getting_started.md
new file mode 100644
index 0000000..ba6c911
--- /dev/null
+++ b/tests/getting_started.md
@@ -0,0 +1,153 @@
+
+# Getting Started
+
+This directory contains different tests for Apache Trafficserver. It is recommended that all test move to this common area under the correct location based on the type of test being added.
+
+## Layout
+The current layout is:
+
+**gold_tests/** - contains all the TSQA v4 based tests that run on the Reusable Gold Testing System (AuTest)
+**tools/** - contain programs used to help with testing. 
+
+In the future a directory called **"unit/"** will be added for adding unit tests based on some standardized testing system.
+
+
+## Scripts
+
+To help with easy running of the tests, there is a autest.sh and bootstrap.py file.
+
+### autest.sh
+This file is a simple wrapper that will call the AuTest program in a python virtualenv. If the virtualenv is not setup it will try to install system. That will set up the Reusable Gold Testing System on most systems in a Python virtual environment. The wrapper add some basic options to the command to point to the location of the tests. Add --help for more details on options for running autest test system.
+
+### bootstrap.py
+This script should try to install python35 or better on the system, and needed python packages for running the tests.
+
+# Advance setup
+
+AuTest can be install manually instead of using the wrapper script. The advange of this is that it is often easier to debug issues with the testing system, or the tests. There are two ways this can be done.
+1. run the bootstrap script then source the path with a "source /env-test/bin/activate" command. At this point autest command should run without the wrapper script
+2. The other way is to make sure you install python 3.5 or better on your system. From there install these python packages ( ie pip install ):
+  - hyper
+  - git+https://bitbucket.org/dragon512/reusable-gold-testing-system.git 
+
+# Writting tests for AuTest
+When writting for the AuTest system please refer to the current documenation on the [online wiki](https://bitbucket.org/dragon512/reusable-gold-testing-system/wiki/Home) for general use of the system.
+
+## Documenation of AuTest extension for ATS.
+Autest allows for the creation of extension to help specilaize and simplify test writting for a given application domian. Minus API addition the extension code will check that python 3.5 or better is used. There is also a new command line argumented added:
+
+--ats-bin < path to bin directory >
+
+This command line argument will point to your build of ATS you want to test. At this time v6.0 or newer of Trafficserver should work.
+
+###MakeATSProcess(name,command=[traffic_server],select_ports=[True])
+ * name - A name for this instance of ATS
+ * command - optional argument defining what process to use. Defaults to traffic_server.
+ * select_ports - have the testing system auto select the ports to use for this instance of ATS
+
+This function will return a AuTest process object that will have a number of files and variables define for making it easier to define a test.
+
+#### Variables
+These are the current variable that are define dynamically 
+
+port - the ipv4 port to listen on
+portv6 - the ipv4 port to listen on
+manager_port - the manager port used. This is set even is select_port is False
+admin_port - the admin port used. This is set even is select_port is False
+
+#### File objects
+A number of file object are define to help with adding values to a given configuration value to for a test, or testing a value exists in a log file. File that are defined currently are:
+
+##### log files
+ * squid.log
+ * error.log
+ * diags.log
+    
+##### config files
+ * records.config
+ * cache.config
+ * congestion.config
+ * hosting.config
+ * icp.config    
+ * ip_allow.config
+ * log_hosts.config
+ * logging.config
+ * metrics.config
+ * parent.config
+ * plugin.config
+ * remap.config
+ * socks.config
+ * splitdns.config
+ * ssl_multicert.config
+ * storage.config
+ * vaddrs.config
+ * volume.config
+
+#### Examples
+
+Create a server
+
+```python
+# don't ports because a config file will set them
+ts1 = Test.MakeATSProcess("ts1",select_ports=False)
+ts1.Setup.ts.CopyConfig('config/records_8090.config','records.config')
+```
+
+Create a server and get the dynamic port value
+
+```python
+# Define default ATS
+ts=Test.MakeATSProcess("ts")
+#first test is a miss for default
+tr=Test.AddTestRun()
+# get port for command from Variables
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}" --verbose'.format(ts.Variables.port)
+
+```
+
+Add value to a configuration file
+```python
+# setup some config file for this server
+ts.Disk.records_config.update({
+            'proxy.config.body_factory.enable_customizations': 3,  # enable domain specific body factory
+            'proxy.config.http.cache.generation':-1, # Start with cache turned off 
+            'proxy.config.config_update_interval_ms':1,
+        })
+ts.Disk.plugin_config.AddLine('xdebug.so')
+ts.Disk.remap_config.AddLines([
+            'map /default/ http://127.0.0.1/ @plugin=generator.so',
+            #line 2
+            'map /generation1/ http://127.0.0.1/' +
+            ' @plugin=conf_remap.so @pparam=proxy.config.http.cache.generation=1' +
+            ' @plugin=generator.so',
+            #line 3
+            'map /generation2/ http://127.0.0.1/' +
+            ' @plugin=conf_remap.so @pparam=proxy.config.http.cache.generation=2' +
+            ' @plugin=generator.so'
+        ])
+```
+
+### CopyConfig(file, targetname=None, process=None)
+* file - name of the file to copy. Relative paths are relative from the test file location
+* targetname - the name name of the file when copied to the correct configuration location
+* process - optional process object to use for getting path location to copy to. Only needed if the Setup object call is not in the scope of the process object created with the MakeATSProcess(...) API.
+
+This function copies a given configuration file the location of a given trafficserver sandbox used in a test. Given a test might have more than on trafficserver instance, it can be difficult to understand the correct location to copy to. This function will deal with the details correctly.
+
+#### Examples
+
+Copy a file over 
+
+```python
+ts1 = Test.MakeATSProcess("ts1",select_ports=False)
+# uses the setup object in the scope of the process object
+ts1.Setup.ts.CopyConfig('config/records_8090.config','records.config')
+```
+```python
+ts1 = Test.MakeATSProcess("ts1",select_ports=False)
+# uses the Setup in the global process via a variable passing
+Test.Setup.ts.CopyConfig('config/records_8090.config','records.config',ts1)
+# same as above, but uses the dynamic object model form
+Test.Setup.ts.CopyConfig('config/records_8090.config','records.config',Test.Processes.ts1)
+```
+
diff --git a/tests/gold_tests/autest-site/copy_config.test.ext b/tests/gold_tests/autest-site/copy_config.test.ext
new file mode 100644
index 0000000..2053989
--- /dev/null
+++ b/tests/gold_tests/autest-site/copy_config.test.ext
@@ -0,0 +1,49 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+from future.utils import native_str 
+
+class CopyATSConfig(SetupItem):
+	def __init__(self, file, targetname=None, process=None):
+		super(CopyATSConfig, self).__init__(
+			itemname="CopyATSConfig"
+		)
+		self.file=file
+		# some protection
+		if process is None and not isinstance(targetname,native_str):
+			self.process=targetname
+		else:
+			self.process=process
+		self.targetname=targetname
+				
+	def setup(self):
+		
+		process = self.process if self.process else self
+		try:
+			ts_dir=process.Env['TS_ROOT']
+		except:
+			if self.process:
+				raise SetupError('TS_ROOT is not defined. Cannot copy ats config file without location to copy to.')
+			else:
+				raise SetupError('TS_ROOT is not defined. Cannot copy ats config file without location to copy to. Please pass in an ATS process object')
+		config_dir = os.path.join(ts_dir,process.ComposeVariables().SYSCONFDIR.replace(process.ComposeVariables().PREFIX+"/",""))
+		host.WriteVerbose("CopyATSConfig", "Copying {0} to {1}".format(self.file, config_dir))
+		self.CopyAs(self.file,config_dir,self.targetname)
+
+AddSetupItem(CopyATSConfig, "CopyConfig", ns="ts")
diff --git a/tests/gold_tests/autest-site/init.cli.ext b/tests/gold_tests/autest-site/init.cli.ext
new file mode 100644
index 0000000..cc3a1a6
--- /dev/null
+++ b/tests/gold_tests/autest-site/init.cli.ext
@@ -0,0 +1,27 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+
+import sys
+if sys.version_info<(3,5,0):
+  host.WriteError("You need python 3.5 or later to run these tests\n",show_stack=False)
+
+Settings.path_argument(["--ats-bin"],
+                        required=True,
+                        help="A user provided directory to ATS bin")
\ No newline at end of file
diff --git a/tests/gold_tests/autest-site/microserver.test.ext b/tests/gold_tests/autest-site/microserver.test.ext
new file mode 100644
index 0000000..ff009a4
--- /dev/null
+++ b/tests/gold_tests/autest-site/microserver.test.ext
@@ -0,0 +1,118 @@
+'''
+'''
+#  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.
+
+from ports import get_port
+import json
+
+def addMethod(self,testName, request_header, functionName):
+    return 
+
+# creates the full request or response block using headers and message data
+def httpObject(self,header,data):
+    r=dict()
+    r["timestamp"]=""
+    r["headers"]=header
+    r["body"]=data
+    return r
+
+# addResponse adds customized response with respect to request_header. request_header and response_header are both dictionaries
+def addResponse(self,filename, testName, request_header, response_header):
+
+    txn = dict()
+    txn["timestamp"] = ""
+    txn["uuid"] = testName
+    txn["request"] = request_header
+    txn["response"] = response_header
+    print("data dir",self.Variables.DataDir)
+    addTransactionToSession(txn,filename)
+    absFilepath=os.path.abspath(filename)
+    self.Setup.CopyAs(absFilepath,self.Variables.DataDir)
+    return
+
+
+
+# addResponse adds customized response with respect to request_header. request_header and response_header are both dictionaries
+def addResponse(self,filename, request_header, response_header):
+    requestline = request_header["headers"].split("\r\n")[0]
+    requestline = requestline.split(" ")[1]
+    resourceLocation = requestline.split("/",1)
+    if len(resourceLocation)>1:
+        rl = resourceLocation[1]
+    else:
+        rl = ""
+    txn = dict()
+    txn["timestamp"] = ""
+    txn["uuid"] = rl
+    txn["request"] = request_header
+    txn["response"] = response_header
+    absFilepath = os.path.join(self.Variables.DataDir, filename)
+    addTransactionToSession(txn,absFilepath)
+    #absFilepath=os.path.abspath(filename)
+    #self.Setup.CopyAs(absFilepath,self.Variables.DataDir)
+    return
+
+#adds transaction in json format to the specified file
+def addTransactionToSession(txn,JFile):
+    jsondata=None
+    if not os.path.exists(os.path.dirname(JFile)):
+        os.makedirs(os.path.dirname(JFile))
+    if os.path.exists(JFile):
+        jf = open(JFile,'r')
+        jsondata = json.load(jf)
+    
+    if jsondata == None:
+        jsondata = dict()
+        jsondata["version"]='0.1'
+        jsondata["timestamp"]="1234567890.098"
+        jsondata["encoding"]="url_encoded"
+        jsondata["txns"]=list()
+        jsondata["txns"].append(txn)
+    else:
+        jsondata["txns"].append(txn)
+    with open(JFile,'w+') as jf:
+        jf.write(json.dumps(jsondata))
+        
+
+#make headers with the key and values provided        
+def makeHeader(self,requestString, **kwargs):
+    headerStr = requestString+'\r\n'
+    for k,v in kwargs.iteritems():
+        headerStr += k+': '+v+'\r\n'
+    headerStr = headerStr+'\r\n'
+    return headerStr
+
+
+def MakeOriginServer(obj, name,public_ip=False):
+    server_path= os.path.join(obj.Variables.AtsTestToolsDir,'microServer/uWServer.py')
+    data_dir = os.path.join(obj.RunDirectory, name)
+    # create Process
+    p = obj.Processes.Process(name)
+    port=get_port(p,"Port")
+    command = "python {0} --data-dir {1} --port {2} --public {3} -m test".format(server_path, data_dir, port, public_ip)
+    
+    # create process
+    p.Command = command
+    p.Setup.MakeDir(data_dir)
+    p.Variables.DataDir = data_dir
+    AddMethodToInstance(p,addResponse)
+    AddMethodToInstance(p,addTransactionToSession)
+    
+    return p
+
+AddTestRunSet(MakeOriginServer,name="MakeOriginServer")
+AddTestRunSet(MakeOriginServer,name="MakeOrigin")
diff --git a/tests/gold_tests/autest-site/min_cfg/cache.config b/tests/gold_tests/autest-site/min_cfg/cache.config
new file mode 100644
index 0000000..6ac656b
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/cache.config
@@ -0,0 +1 @@
+# this files just needs to exist
\ No newline at end of file
diff --git a/tests/gold_tests/autest-site/min_cfg/hosting.config b/tests/gold_tests/autest-site/min_cfg/hosting.config
new file mode 100644
index 0000000..6ac656b
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/hosting.config
@@ -0,0 +1 @@
+# this files just needs to exist
\ No newline at end of file
diff --git a/tests/gold_tests/autest-site/min_cfg/ip_allow.config b/tests/gold_tests/autest-site/min_cfg/ip_allow.config
new file mode 100644
index 0000000..061bbe5
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/ip_allow.config
@@ -0,0 +1,4 @@
+src_ip=127.0.0.1 action=ip_allow method=ALL
+src_ip=::1 action=ip_allow method=ALL
+src_ip=0.0.0.0-255.255.255.255 action=ip_deny method=PUSH|PURGE|DELETE
+src_ip=::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff action=ip_deny method=PUSH|PURGE|DELETE
\ No newline at end of file
diff --git a/tests/gold_tests/autest-site/min_cfg/parent.config b/tests/gold_tests/autest-site/min_cfg/parent.config
new file mode 100644
index 0000000..6ac656b
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/parent.config
@@ -0,0 +1 @@
+# this files just needs to exist
\ No newline at end of file
diff --git a/tests/gold_tests/autest-site/min_cfg/readme.txt b/tests/gold_tests/autest-site/min_cfg/readme.txt
new file mode 100644
index 0000000..15a2cf8
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/readme.txt
@@ -0,0 +1,3 @@
+Contains the miniumn set of config file and setting to allow trafficserver to start in a usable way. 
+The goal is to remove the need for any of these files to exist. 
+Each of these files should provide an understanding of what we need to fix in the code
diff --git a/tests/gold_tests/autest-site/min_cfg/records.config b/tests/gold_tests/autest-site/min_cfg/records.config
new file mode 100644
index 0000000..8d97ae7
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/records.config
@@ -0,0 +1 @@
+# some stuff
diff --git a/tests/gold_tests/autest-site/min_cfg/remap.config b/tests/gold_tests/autest-site/min_cfg/remap.config
new file mode 100644
index 0000000..6ac656b
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/remap.config
@@ -0,0 +1 @@
+# this files just needs to exist
\ No newline at end of file
diff --git a/tests/gold_tests/autest-site/min_cfg/ssl_multicert.config b/tests/gold_tests/autest-site/min_cfg/ssl_multicert.config
new file mode 100644
index 0000000..6ac656b
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/ssl_multicert.config
@@ -0,0 +1 @@
+# this files just needs to exist
\ No newline at end of file
diff --git a/tests/gold_tests/autest-site/min_cfg/storage.config b/tests/gold_tests/autest-site/min_cfg/storage.config
new file mode 100644
index 0000000..a0849ee
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/storage.config
@@ -0,0 +1,4 @@
+# seems good enought for doing something for playing with. 
+# not good for production
+# File must exist and must have this value in it
+var 256M
diff --git a/tests/gold_tests/autest-site/min_cfg/volume.config b/tests/gold_tests/autest-site/min_cfg/volume.config
new file mode 100644
index 0000000..6ac656b
--- /dev/null
+++ b/tests/gold_tests/autest-site/min_cfg/volume.config
@@ -0,0 +1 @@
+# this files just needs to exist
\ No newline at end of file
diff --git a/tests/gold_tests/autest-site/ports.py b/tests/gold_tests/autest-site/ports.py
new file mode 100644
index 0000000..ce0b20e
--- /dev/null
+++ b/tests/gold_tests/autest-site/ports.py
@@ -0,0 +1,107 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import socket
+import subprocess
+
+try:
+    import queue as Queue
+except ImportError:
+    import Queue
+
+g_ports = None  # ports we can use
+
+
+def PortOpen(port, address=None):
+
+    ret = False
+    if address is None:
+        address = "localhost"
+
+    address = (address, port)
+
+    try:
+        s = socket.create_connection(address, timeout=.5)
+        s.close()
+        ret = True
+    except socket.error:
+        s = None
+        ret = False
+    except socket.timeout:
+        s = None
+
+    return ret
+
+
+def setup_port_queue(amount=1000):
+    global g_ports
+    if g_ports is None:
+        g_ports = Queue.LifoQueue()
+    else:
+        return
+    try:
+        dmin, dmax = subprocess.check_output(
+            ["sysctl", "net.ipv4.ip_local_port_range"]).decode().split("=")[1].split()
+        dmin = int(dmin)
+        dmax = int(dmax)
+    except:
+        return
+
+    rmin = dmin - 2000
+    rmax = 65536 - dmax
+
+    if rmax > amount:
+        # fill in ports
+        port = dmax + 1
+        while port < 65536 and g_ports.qsize() < amount:
+            # if port good:
+            if not PortOpen(port):
+                g_ports.put(port)
+            port += 1
+    if rmin > amount and g_ports.qsize() < amount:
+        port = 2001
+        while port < dmin and g_ports.qsize() < amount:
+            # if port good:
+            if not PortOpen(port):
+                g_ports.put(port)
+            port += 1
+
+def get_port(obj, name):
+    '''
+    Get a port and set it to a variable on the object
+
+    '''
+
+    setup_port_queue()
+    if g_ports.qsize():
+        # get port
+        port = g_ports.get()
+        # assign to variable
+        obj.Variables[name] = port
+        # setup clean up step to recycle the port
+        obj.Setup.Lambda(func_cleanup=lambda: g_ports.put(
+            port), description="recycling port")
+        return port
+
+    # use old code
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(('', 0))  # bind to all interfaces on an ephemeral port
+    port = sock.getsockname()[1]
+    obj.Variables[name] = port
+    return port
diff --git a/tests/gold_tests/autest-site/setup.cli.ext b/tests/gold_tests/autest-site/setup.cli.ext
new file mode 100644
index 0000000..6f38a87
--- /dev/null
+++ b/tests/gold_tests/autest-site/setup.cli.ext
@@ -0,0 +1,49 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import json, subprocess
+import pprint
+
+if Arguments.ats_bin is not None:
+    # Add environment variables
+    ENV['ATS_BIN'] = Arguments.ats_bin
+    #if Arguments.ats_bin not in ENV['PATH']:
+        #ENV['PATH'] = Arguments.ats_bin + ':' + ENV['PATH']
+    
+if ENV['ATS_BIN'] is not None:
+    # Add variables for Tests
+    traffic_layout = os.path.join(ENV['ATS_BIN'], "traffic_layout")
+    if not os.path.isdir(ENV['ATS_BIN']):
+        host.WriteError("--ats-bin requires a directory", show_stack=False)
+    if not os.path.isfile(traffic_layout):
+        host.WriteError("traffic_layout is not found. Aborting tests - Bad build or install.", show_stack=False)
+    try:
+        out = subprocess.check_output([traffic_layout, "--json"])
+    except subprocess.CalledProcessError:
+        host.WriteError("traffic_layout is broken. Aborting tests - The build of traffic server is bad.", show_stack=False) 
+    out = json.loads(out.decode("utf-8"))
+    for k,v in out.items():
+        out[k]=v[:-1] if v.endswith('/') else v
+    Variables.update(out)
+    host.WriteVerbose(['ats'],"Traffic server layout Data:\n",pprint.pformat(out))
+
+Variables.AtsTestToolsDir = os.path.join(AutestSitePath,'../../tools')
+
+# modify delay times as we always have to kill Trafficserver
+# no need to wait
+Variables.Autest.StopProcessLongDelaySeconds=0
diff --git a/tests/gold_tests/autest-site/trafficserver.test.ext b/tests/gold_tests/autest-site/trafficserver.test.ext
new file mode 100644
index 0000000..9fef2d1
--- /dev/null
+++ b/tests/gold_tests/autest-site/trafficserver.test.ext
@@ -0,0 +1,323 @@
+'''
+'''
+#  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.
+
+from __future__ import print_function
+import os
+import socket
+from ports import get_port
+
+
+def make_id(s):
+    return s.replace(".","_").replace('-','_')
+    
+# this forms is for the global process define
+def MakeATSProcess(obj, name, command='traffic_server', select_ports=True):
+    #####################################
+    # common locations
+    
+    # directory we will setup for the ATS to run under
+    ts_dir = os.path.join(obj.RunDirectory, name)
+    #common bin directory
+    bin_dir = 'bin'
+    # ideally we would use a value like config.. but there is a bug
+    # in which records.conf values are not loaded correctly from this location,
+    # so we use the expecetd "build" layout to correct the issue for the time being
+    config_dir = os.path.join(ts_dir,
+                              obj.Variables.SYSCONFDIR.replace(
+                                  obj.Variables.PREFIX+"/",
+                                  ""
+                                  )
+                             )
+    # directory contains the html response templates
+    template_dir = os.path.join(config_dir, "body_factory")
+    # contains plugins
+    plugin_dir = os.path.join(ts_dir, obj.Variables.PLUGINDIR.replace(obj.Variables.PREFIX+"/",""))
+    # the log directory
+    log_dir=os.path.join(ts_dir, 'log')
+    runtime_dir=os.path.join(ts_dir, obj.Variables.RUNTIMEDIR.replace(obj.Variables.PREFIX+"/",""))
+    ssl_dir=os.path.join(ts_dir, 'ssl')
+    
+    # create process
+    p = obj.Processes.Process(name, command)
+    
+    # we want to have a few directories more fixed
+    # this helps with debugging as location are common
+    # we do this by overiding locations from the "layout"
+    # used as part of build. This means loctaion such as 
+    # PROXY_CONFIG_BIN_PATH with alway be $root/bin
+    # not something else such as bin64    
+    #####
+    
+    # set root for this test
+    p.Env['TS_ROOT'] = ts_dir
+
+    # set bin location
+    
+    p.Env['PROXY_CONFIG_BIN_PATH'] = bin_dir
+    bin_path=os.path.join(ts_dir, bin_dir)
+    p.Env['PATH']=bin_path+os.pathsep+p.ComposeEnv()['PATH']
+    p.Setup.Copy(p.Variables.BINDIR, bin_path, True)
+    
+    #########################################################
+    ## setup config directory
+    
+    # copy all basic config files we need to get this to work
+    cfg_dir=os.path.join(AUTEST_SITE_PATH,"min_cfg")
+    for f in os.listdir(cfg_dir):
+        p.Setup.CopyAs(os.path.join(cfg_dir,f),config_dir)
+    
+    #########################################################
+    # setup read-only data directory in config. Needed for response body reponses
+    
+    p.Env['PROXY_CONFIG_BODY_FACTORY_TEMPLATE_SETS_DIR']=template_dir
+    p.Variables.body_factory_template_dir=template_dir
+    p.Setup.Copy(os.path.join(p.Variables.SYSCONFDIR,'body_factory'), template_dir)
+	
+	#########################################################
+    # setup read-only data directory for plugins
+    
+    p.Env['PROXY_CONFIG_PLUGIN_PLUGIN_DIR']=plugin_dir
+    p.Setup.Copy(p.Variables.PLUGINDIR, plugin_dir)
+
+    ######################################################### 
+    # create subdirectories that need to exist (but are empty)
+    # log directory has to be created with correct permissions
+    p.Setup.MakeDir(log_dir) # log directory has to be created
+    p.Setup.Chown(log_dir, "nobody", "nobody",ignore=True) 
+
+    # set env so traffic server uses correct locations
+    p.Env['PROXY_CONFIG_LOG_LOGFILE_DIR'] = log_dir
+    p.Variables.LOGDIR=log_dir
+
+    # this is needed for cache and communication sockets
+    # Below was to make shorter paths but the code in
+    # traffic_ctl is broken and ignores this.
+    #p.Env['PROXY_CONFIG_LOCAL_STATE_DIR']=runtime_dir
+    p.Env['PROXY_CONFIG_HOSTDB_STORAGE_PATH']=runtime_dir
+    #p.Variables.RUNTIMEDIR=runtime_dir
+    p.Setup.MakeDir(runtime_dir)
+    # will need this for traffic_manager is it runs
+    p.Setup.MakeDir(os.path.join(config_dir, 'snapshots'))
+
+    ##########################################################
+    # create subdirectories that need to exist (but are empty)
+    p.Setup.MakeDir(ssl_dir) # ssl directory has to be created for keeping certs and keys
+    p.Setup.Chown(ssl_dir, "nobody", "nobody",ignore=True) 
+
+    # set env so traffic server uses correct locations
+    p.Env['PROXY_CONFIG_SSL_DIR'] = ssl_dir
+    p.Variables.SSLDir=ssl_dir
+    AddMethodToInstance(p,addSSLfile)
+    ######################################################### 
+    # define the basic file for a given test run 
+    #traffic.out ?? # cannot find it at the moment...
+    #squid.log
+    fname = "squid.log"
+    tmpname = os.path.join(log_dir, fname)
+    p.Disk.File(tmpname, id=make_id(fname))
+    #error.log
+    fname = "error.log"
+    tmpname = os.path.join(log_dir, fname)
+    p.Disk.File(tmpname, id=make_id(fname), exists=False)
+    #diags.log
+    fname = "diags.log"
+    tmpname = os.path.join(log_dir, fname)
+    p.Disk.File(tmpname, id=make_id(fname))
+    # add this test back once we have network namespaces working again
+    p.Disk.diags_log.Content = Testers.ExcludesExpression("ERROR:", "diags.log should not contain errors")
+    p.Disk.diags_log.Content += Testers.ExcludesExpression("FATAL:", "diags.log should not contain errors")
+
+    # config files
+    fname="records.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config:records")
+    
+    fname="cache.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="congestion.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="hosting.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="icp.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="ip_allow.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="log_hosts.config"
+    tmpname=os.path.join(config_dir,fname)
+    #p.Disk.File(tmpname,id=make_id(fname),typename="ats:config").AddLine("# need something in here")
+
+    # magic file that should probally not exist
+    fname="logging.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="metrics.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="parent.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+
+    fname="plugin.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="remap.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="socks.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="splitdns.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="ssl_multicert.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="storage.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="vaddrs.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+    
+    fname="volume.config"
+    tmpname=os.path.join(config_dir,fname)
+    p.Disk.File(tmpname,id=make_id(fname),typename="ats:config")
+
+    ##########################################################
+    # set up default ports
+    # get some ports  TODO make it so we can hold on to the socket
+    if select_ports:
+        # some system have a bug in which ipv4 and ipv6 share port space
+        # Make two different ports to avoid this
+        get_port(p,"port")
+        get_port(p,"portv6")
+        p.Ready=When.PortOpen(p.Variables.port)
+    else:
+        p.Variables.port          = 8080
+        p.Variables.portv6        = 8080
+    get_port(p,"manager_port")
+    get_port(p,"admin_port")
+    
+    # set the ports
+    if select_ports:
+        p.Env['PROXY_CONFIG_HTTP_SERVER_PORTS']= "{0} {1}:ipv6".format(p.Variables.port,p.Variables.portv6)  # your own listen port
+    p.Env['PROXY_CONFIG_PROCESS_MANAGER_MGMT_PORT']= str(p.Variables.manager_port)  
+    p.Env['PROXY_CONFIG_ADMIN_SYNTHETIC_PORT']= str(p.Variables.admin_port)
+    p.Env['PROXY_CONFIG_ADMIN_AUTOCONF_PORT']= str(p.Variables.admin_port)  # support pre ATS 6.x
+
+    # since we always kill this
+    p.ReturnCode=None
+	
+    return p
+    
+##################################
+# added to ats process object to help deal with config files 
+class Config(File):
+    '''
+    Class to represent a config file
+    '''
+
+    def __init__(self, runable, name, exists = None, size = None, content_tester = None, execute=False, runtime=True, content=None):
+        super(Config, self).__init__(
+            runable, name, exists = None, size = None, content_tester = None, execute=False, runtime=True
+        )
+        
+        self.content=content
+        self._added=False
+        
+    def AddLines(self, lines):
+        for line in lines:
+            self.AddLine(line)
+
+    def _do_write(self,name):
+        '''
+        Write contents to disk
+        '''
+        if self.content is not None:
+            with open(name, 'w') as f:
+                f.write(self.content)
+        return (False,"Appended file {0}".format(self.Name),"Success")
+
+    def AddLine(self, line):
+        if not self._added:
+            self.WriteCustomOn(self._do_write)
+            self._added=True
+        if self.content is None:
+            self.content=""
+        if not line.endswith('\n'):
+            line += '\n'
+        self.content += line
+
+
+class RecordsConfig(Config, dict):
+    '''
+    Create a "dict" representation of records.config
+
+    This can be accessed as a multi-level dictionary
+
+    such as:
+    rc['CONFIG']['proxy.config.log.hostname']
+    '''
+    
+
+    reverse_kind_map = {str: 'STRING',
+                        int: 'INT',
+                        float: 'FLOAT',
+                        }
+
+    line_template = 'CONFIG {name} {kind} {val}\n'
+	
+    def __init__(self, runable, name, exists = None, size = None, content_tester = None, execute=False, runtime=True):
+        super(RecordsConfig, self).__init__(
+            runable, name, exists = None, size = None, content_tester = None, execute=False, runtime=True
+        )
+        self.WriteCustomOn(self._do_write)
+
+    def _do_write(self,name):
+        if len(self) > 0:
+            with open(name, 'w') as f:
+                for name, val in self.items():
+                    f.write(self.line_template.format(name=name,
+                                                       kind=self.reverse_kind_map[type(val)],
+                                                       val=val)) 
+        return (False,"Writing config file {0}".format(os.path.split(self.Name)[-1]),"Success")
+############################################################################################################
+def addSSLfile(self,filename):
+    self.Setup.CopyAs(filename, self.Variables.SSLDir)
+
+RegisterFileType(Config,"ats:config")
+RegisterFileType(RecordsConfig,"ats:config:records")
+AddTestRunSet(MakeATSProcess,name="MakeATSProcess")
diff --git a/tests/gold_tests/basic/basic-cop.test.py b/tests/gold_tests/basic/basic-cop.test.py
new file mode 100644
index 0000000..fa87654
--- /dev/null
+++ b/tests/gold_tests/basic/basic-cop.test.py
@@ -0,0 +1,33 @@
+'''
+'''
+#  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.
+
+Test.Summary = '''
+Test that Trafficserver starts with default configurations.
+'''
+
+Test.SkipUnless(Condition.HasProgram("curl","Curl need to be installed on system for this test to work"))
+
+p=Test.MakeATSProcess("ts", command="traffic_cop --debug --stdout", select_ports=False)
+t = Test.AddTestRun("Test traffic server started properly")
+t.StillRunningAfter = Test.Processes.ts
+
+p = t.Processes.Default
+p.Command = "curl http://127.0.0.1:8080"
+p.ReturnCode = 0
+p.StartBefore(Test.Processes.ts, ready = When.PortOpen(8080))
+
diff --git a/tests/gold_tests/basic/basic-manager.test.py b/tests/gold_tests/basic/basic-manager.test.py
new file mode 100644
index 0000000..a9eba74
--- /dev/null
+++ b/tests/gold_tests/basic/basic-manager.test.py
@@ -0,0 +1,33 @@
+'''
+'''
+#  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.
+
+Test.Summary = '''
+Test that Trafficserver starts with default configurations.
+'''
+
+Test.SkipUnless(Condition.HasProgram("curl","Curl need to be installed on system for this test to work"))
+
+p=Test.MakeATSProcess("ts", command="traffic_manager",select_ports=False)
+t = Test.AddTestRun("Test traffic server started properly")
+t.StillRunningAfter = Test.Processes.ts
+
+p = t.Processes.Default
+p.Command = "curl http://127.0.0.1:8080"
+p.ReturnCode = 0
+p.StartBefore(Test.Processes.ts, ready = When.PortOpen(8080))
+
diff --git a/tests/gold_tests/basic/basic.test.py b/tests/gold_tests/basic/basic.test.py
new file mode 100644
index 0000000..7dcbcd2
--- /dev/null
+++ b/tests/gold_tests/basic/basic.test.py
@@ -0,0 +1,33 @@
+'''
+'''
+#  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.
+
+Test.Summary = '''
+Test that Trafficserver starts with default configurations.
+'''
+
+Test.SkipUnless(Condition.HasProgram("curl","Curl need to be installed on system for this test to work"))
+
+p=Test.MakeATSProcess("ts",select_ports=False)
+t = Test.AddTestRun("Test traffic server started properly")
+t.StillRunningAfter = Test.Processes.ts
+
+p = t.Processes.Default
+p.Command = "curl http://127.0.0.1:8080"
+p.ReturnCode = 0
+p.StartBefore(Test.Processes.ts, ready = When.PortOpen(8080))
+
diff --git a/tests/gold_tests/basic/config.test.py b/tests/gold_tests/basic/config.test.py
new file mode 100644
index 0000000..48f3c73
--- /dev/null
+++ b/tests/gold_tests/basic/config.test.py
@@ -0,0 +1,33 @@
+'''
+'''
+#  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.
+
+Test.Summary = "Test start up of Traffic server with configuration modification of starting port"
+
+Test.SkipUnless(Condition.HasProgram("curl",
+                    "Curl needs to be installed on your system for this test to work"))
+
+ts1 = Test.MakeATSProcess("ts1",select_ports=False)
+ts1.Setup.ts.CopyConfig('config/records_8090.config',"records.config")
+t = Test.AddTestRun("Test traffic server started properly")
+t.StillRunningAfter = ts1
+
+p = t.Processes.Default
+p.Command = "curl 127.0.0.1:8090"
+p.ReturnCode = 0
+
+p.StartBefore(Test.Processes.ts1, ready = When.PortOpen(8090))                    
\ No newline at end of file
diff --git a/tests/gold_tests/basic/config/records_8090.config b/tests/gold_tests/basic/config/records_8090.config
new file mode 100644
index 0000000..ca56e77
--- /dev/null
+++ b/tests/gold_tests/basic/config/records_8090.config
@@ -0,0 +1,2 @@
+CONFIG proxy.config.http.server_ports STRING 8090
+
diff --git a/tests/gold_tests/basic/config/records_8091.config b/tests/gold_tests/basic/config/records_8091.config
new file mode 100644
index 0000000..1b560d8
--- /dev/null
+++ b/tests/gold_tests/basic/config/records_8091.config
@@ -0,0 +1,2 @@
+CONFIG proxy.config.http.server_ports STRING 8091
+
diff --git a/tests/gold_tests/basic/config/remap.config b/tests/gold_tests/basic/config/remap.config
new file mode 100644
index 0000000..553b66b
--- /dev/null
+++ b/tests/gold_tests/basic/config/remap.config
@@ -0,0 +1 @@
+regex_map http://(.*)/ http://localhost:9999/
\ No newline at end of file
diff --git a/tests/gold_tests/basic/copy_config.test.py b/tests/gold_tests/basic/copy_config.test.py
new file mode 100644
index 0000000..7543be3
--- /dev/null
+++ b/tests/gold_tests/basic/copy_config.test.py
@@ -0,0 +1,49 @@
+'''
+'''
+#  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.
+
+Test.Summary = "Test start up of Traffic server with configuration modification of starting port of different servers at the same time"
+
+Test.SkipUnless(Condition.HasProgram("curl",
+                    "Curl needs to be installed on your system for this test to work"))
+
+# set up some ATS processes
+ts1 = Test.MakeATSProcess("ts1",select_ports=False)
+ts1.Setup.ts.CopyConfig('config/records_8090.config','records.config')
+
+ts2 = Test.MakeATSProcess("ts2",select_ports=False)
+ts2.Setup.ts.CopyConfig('config/records_8091.config','records.config')
+
+# setup a testrun
+t = Test.AddTestRun("Talk to ts1")
+t.StillRunningAfter = ts1
+t.StillRunningAfter += ts2
+p = t.Processes.Default
+p.Command = "curl 127.0.0.1:8090"
+p.ReturnCode = 0
+p.StartBefore(Test.Processes.ts1, ready = When.PortOpen(8090))
+p.StartBefore(Test.Processes.ts2, ready = When.PortOpen(8091))
+
+# setup a testrun
+t = Test.AddTestRun("Talk to ts2")
+t.StillRunningBefore = ts1
+t.StillRunningBefore += ts2
+t.StillRunningAfter = ts1
+t.StillRunningAfter += ts2
+p = t.Processes.Default
+p.Command = "curl 127.0.0.1:8091"
+p.ReturnCode = 0
\ No newline at end of file
diff --git a/tests/gold_tests/basic/copy_config2.test.py b/tests/gold_tests/basic/copy_config2.test.py
new file mode 100644
index 0000000..65c3ad0
--- /dev/null
+++ b/tests/gold_tests/basic/copy_config2.test.py
@@ -0,0 +1,46 @@
+'''
+'''
+#  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.
+
+Test.Summary = "Test start up of Traffic server with generated ports of more than one servers at the same time"
+
+Test.SkipUnless(Condition.HasProgram("curl",
+                    "Curl needs to be installed on your system for this test to work"))
+
+# set up some ATS processes
+ts1 = Test.MakeATSProcess("ts1")
+ts2 = Test.MakeATSProcess("ts2")
+
+# setup a testrun
+t = Test.AddTestRun("Talk to ts1")
+t.StillRunningAfter = ts1
+t.StillRunningAfter += ts2
+p = t.Processes.Default
+p.Command = "curl 127.0.0.1:{0}".format(ts1.Variables.port)
+p.ReturnCode = 0
+p.StartBefore(Test.Processes.ts1, ready = When.PortOpen(ts1.Variables.port))
+p.StartBefore(Test.Processes.ts2, ready = When.PortOpen(ts2.Variables.port))
+
+# setup a testrun
+t = Test.AddTestRun("Talk to ts2")
+t.StillRunningBefore = ts1
+t.StillRunningBefore += ts2
+t.StillRunningAfter = ts1
+t.StillRunningAfter += ts2
+p = t.Processes.Default
+p.Command = "curl 127.0.0.1:{0}".format(ts2.Variables.port)
+p.ReturnCode = 0
\ No newline at end of file
diff --git a/tests/gold_tests/body_factory/config/remap.config b/tests/gold_tests/body_factory/config/remap.config
new file mode 100644
index 0000000..553b66b
--- /dev/null
+++ b/tests/gold_tests/body_factory/config/remap.config
@@ -0,0 +1 @@
+regex_map http://(.*)/ http://localhost:9999/
\ No newline at end of file
diff --git a/tests/gold_tests/body_factory/custom_response.test.py b/tests/gold_tests/body_factory/custom_response.test.py
new file mode 100644
index 0000000..18350ac
--- /dev/null
+++ b/tests/gold_tests/body_factory/custom_response.test.py
@@ -0,0 +1,64 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os 
+
+Test.Summary = '''
+Tests for custom reponse body
+'''
+
+# this test currently fails and it should not
+Test.SkipIf(Condition.true("This test fails at the moment as is turned off"))
+Test.SkipUnless(Condition.HasProgram("curl","Curl need to be installed on system for this test to work"))
+
+ts=Test.MakeATSProcess("ts")
+ts.Disk.records_config.update({
+            'proxy.config.body_factory.enable_customizations': 3,  # enable domain specific body factory
+        })
+ts.Disk.remap_config.AddLine(
+            'map / http://www.linkedin.com/ @action=deny'
+        )
+
+
+domain_directory = ['www.linkedin.com', '127.0.0.1', 'www.foobar.net']
+body_factory_dir=ts.Variables.body_factory_template_dir
+# for each domain
+set=False
+for directory_item in domain_directory:
+    # write out a files with some content for Traffic server for given domain
+    ts.Disk.File(os.path.join(body_factory_dir, directory_item, "access#denied")).\
+        WriteOn("{0} 44 Not 89 found".format(directory_item))
+    
+    ts.Disk.File(os.path.join(body_factory_dir, directory_item, ".body_factory_info")).\
+        WriteOn("")
+    # make a test run for a given domain
+    tr=Test.AddTestRun("Test domain {0}".format(directory_item))
+    if not set:
+        #Start the ATS process for first test run
+        tr.Processes.Default.StartBefore(Test.Processes.ts)
+        set = True
+        tr.StillRunningAfter = ts
+    else:
+        # test that ats is still running before and after
+        tr.StillRunningBefore = ts
+        tr.StillRunningAfter = ts
+        
+    tr.Processes.Default.Command="curl --proxy 127.0.0.1:{1} {0}".format(directory_item,ts.Variables.port)
+    tr.Processes.Default.ReturnCode=0
+    tr.Streams.All=Testers.ContainsExpression("{0} Not found".format(directory_item),"should contain custom data")
+    
diff --git a/tests/gold_tests/cache/cache-generation-clear.test.py b/tests/gold_tests/cache/cache-generation-clear.test.py
new file mode 100644
index 0000000..f36a339
--- /dev/null
+++ b/tests/gold_tests/cache/cache-generation-clear.test.py
@@ -0,0 +1,90 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import uuid
+
+Test.Summary = '''
+Test that incrementing the cache generation acts like a cache clear
+'''
+# need Curl
+Test.SkipUnless(Condition.HasProgram("curl","Curl need to be installed on system for this test to work"))
+Test.ContinueOnFail=True
+# Define default ATS
+ts=Test.MakeATSProcess("ts",command="traffic_manager")
+
+# setup some config file for this server
+ts.Disk.records_config.update({
+            'proxy.config.body_factory.enable_customizations': 3,  # enable domain specific body factory
+            'proxy.config.http.cache.generation':-1, # Start with cache turned off 
+            'proxy.config.config_update_interval_ms':1,
+        })
+ts.Disk.plugin_config.AddLine('xdebug.so')
+ts.Disk.remap_config.AddLines([
+            'map /default/ http://127.0.0.1/ @plugin=generator.so',
+            #line 2
+            'map /generation1/ http://127.0.0.1/' +
+            ' @plugin=conf_remap.so @pparam=proxy.config.http.cache.generation=1' +
+            ' @plugin=generator.so',
+            #line 3
+            'map /generation2/ http://127.0.0.1/' +
+            ' @plugin=conf_remap.so @pparam=proxy.config.http.cache.generation=2' +
+            ' @plugin=generator.so'
+        ])
+
+objectid = uuid.uuid4()
+#first test is a miss for default
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+# time delay as proxy.config.http.wait_for_cache could be broken
+tr.Processes.Default.StartBefore(Test.Processes.ts,ready=5)
+tr.Processes.Default.Streams.All="gold/miss_default-1.gold"
+
+# Second touch is a HIT for default.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/hit_default-1.gold"
+
+# Call traffic_ctrl to set new generation
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='traffic_ctl --debug config set proxy.config.http.cache.generation 77'
+tr.Processes.Default.ForceUseShell=False
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Env=ts.Env # set the environment for traffic_control to run in
+
+# new generation should first be a miss.
+tr=Test.AddTestRun()
+tr.DelayStart=15 # delay start of test run to allow previous command to take effect
+# create a new traffic_ctrl call and the environment 
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/miss_default77.gold"
+
+# new generation should should now hit.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/hit_default77.gold"
+
+# should still hit.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/hit_default77.gold"
+
diff --git a/tests/gold_tests/cache/cache-generation-disjoint.test.py b/tests/gold_tests/cache/cache-generation-disjoint.test.py
new file mode 100644
index 0000000..39c0b67
--- /dev/null
+++ b/tests/gold_tests/cache/cache-generation-disjoint.test.py
@@ -0,0 +1,88 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import uuid
+
+Test.Summary = '''
+Test that the same URL path in different cache generations creates disjoint objects
+'''
+# need Curl
+Test.SkipUnless(Condition.HasProgram("curl","Curl need to be installed on system for this test to work"))
+Test.ContinueOnFail=True
+# Define default ATS
+ts=Test.MakeATSProcess("ts")
+
+# setup some config file for this server
+ts.Disk.records_config.update({
+            'proxy.config.body_factory.enable_customizations': 3,  # enable domain specific body factory
+            'proxy.config.http.cache.generation':-1, # Start with cache turned off
+            'proxy.config.http.wait_for_cache': 1, 
+            'proxy.config.config_update_interval_ms':1,
+            
+        })
+ts.Disk.plugin_config.AddLine('xdebug.so')
+ts.Disk.remap_config.AddLines([
+            'map /default/ http://127.0.0.1/ @plugin=generator.so',
+            #line 2
+            'map /generation1/ http://127.0.0.1/' +
+            ' @plugin=conf_remap.so @pparam=proxy.config.http.cache.generation=1' +
+            ' @plugin=generator.so',
+            #line 3
+            'map /generation2/ http://127.0.0.1/' +
+            ' @plugin=conf_remap.so @pparam=proxy.config.http.cache.generation=2' +
+            ' @plugin=generator.so'
+        ])
+
+objectid = uuid.uuid4()
+#first test is a miss for default
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+# time delay as proxy.config.http.wait_for_cache could be broken
+tr.Processes.Default.StartBefore(Test.Processes.ts,ready=2)
+tr.Processes.Default.Streams.All="gold/miss_default-1.gold"
+
+# Same URL in generation 1 is a MISS.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/generation1/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/miss_gen1.gold"
+
+# Same URL in generation 2 is still a MISS.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/generation2/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/miss_gen2.gold"
+
+# Second touch is a HIT for default.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/hit_default-1.gold"
+
+# Second touch is a HIT for generation1.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/generation1/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/hit_gen1.gold"
+
+# Second touch is a HIT for generation2.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/generation2/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/hit_gen2.gold"
diff --git a/tests/gold_tests/cache/disjoint-wait-for-cache.test.py b/tests/gold_tests/cache/disjoint-wait-for-cache.test.py
new file mode 100644
index 0000000..cc45877
--- /dev/null
+++ b/tests/gold_tests/cache/disjoint-wait-for-cache.test.py
@@ -0,0 +1,88 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import uuid
+
+Test.Summary = '''
+Same as cache-generaertaion-disjoint, but uses proxy.config.http.wait_for_cache which should delay
+the server from accepting connection till the cache is loaded
+'''
+# need Curl
+Test.SkipUnless(Condition.HasProgram("curl","Curl need to be installed on system for this test to work"))
+Test.SkipIf(Condition.true("This test fails at the moment as is turned off"))
+Test.ContinueOnFail=True
+# Define default ATS
+ts=Test.MakeATSProcess("ts")
+
+# setup some config file for this server
+ts.Disk.records_config.update({
+            'proxy.config.body_factory.enable_customizations': 3,  # enable domain specific body factory
+            'proxy.config.http.cache.generation':-1, # Start with cache turned off
+            'proxy.config.config_update_interval_ms':1,
+            'proxy.config.http.wait_for_cache':3,
+        })
+ts.Disk.plugin_config.AddLine('xdebug.so')
+ts.Disk.remap_config.AddLines([
+            'map /default/ http://127.0.0.1/ @plugin=generator.so',
+            #line 2
+            'map /generation1/ http://127.0.0.1/' +
+            ' @plugin=conf_remap.so @pparam=proxy.config.http.cache.generation=1' +
+            ' @plugin=generator.so',
+            #line 3
+            'map /generation2/ http://127.0.0.1/' +
+            ' @plugin=conf_remap.so @pparam=proxy.config.http.cache.generation=2' +
+            ' @plugin=generator.so'
+        ])
+
+objectid = uuid.uuid4()
+#first test is a miss for default
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.Processes.Default.Streams.All="gold/miss_default-1.gold"
+
+# Same URL in generation 1 is a MISS.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/generation1/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/miss_gen1.gold"
+
+# Same URL in generation 2 is still a MISS.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/generation2/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/miss_gen2.gold"
+
+# Second touch is a HIT for default.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/default/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/hit_default-1.gold"
+
+# Second touch is a HIT for generation1.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/generation1/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/hit_gen1.gold"
+
+# Second touch is a HIT for generation2.
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/generation2/cache/10/{1}" -H "x-debug: x-cache,x-cache-key,via,x-cache-generation" --verbose'.format(ts.Variables.port,objectid)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.All="gold/hit_gen2.gold"
diff --git a/tests/gold_tests/cache/gold/hit_default-1.gold b/tests/gold_tests/cache/gold/hit_default-1.gold
new file mode 100644
index 0000000..55590ff
--- /dev/null
+++ b/tests/gold_tests/cache/gold/hit_default-1.gold
@@ -0,0 +1,8 @@
+{}
+> GET /default/cache/10/{} HTTP/1.1
+{}
+< Server: ATS/{}
+< X-Cache-Key: http://127.0.0.1/cache/10/{}
+< X-Cache: hit-fresh
+< X-Cache-Generation: -1
+{}
diff --git a/tests/gold_tests/cache/gold/hit_default77.gold b/tests/gold_tests/cache/gold/hit_default77.gold
new file mode 100644
index 0000000..79f4dc8
--- /dev/null
+++ b/tests/gold_tests/cache/gold/hit_default77.gold
@@ -0,0 +1,8 @@
+{}
+> GET /default/cache/10/{} HTTP/1.1
+{}
+< Server: ATS/{}
+< X-Cache-Key: http://127.0.0.1/cache/10/{}
+< X-Cache: hit-fresh
+< X-Cache-Generation: 77
+{}
diff --git a/tests/gold_tests/cache/gold/hit_gen1.gold b/tests/gold_tests/cache/gold/hit_gen1.gold
new file mode 100644
index 0000000..0830f99
--- /dev/null
+++ b/tests/gold_tests/cache/gold/hit_gen1.gold
@@ -0,0 +1,8 @@
+{}
+> GET /generation1/cache/10/{} HTTP/1.1
+{}
+< Server: ATS/{}
+< X-Cache-Key: http://127.0.0.1/cache/10/{}
+< X-Cache: hit-fresh
+< X-Cache-Generation: 1
+{}
diff --git a/tests/gold_tests/cache/gold/hit_gen2.gold b/tests/gold_tests/cache/gold/hit_gen2.gold
new file mode 100644
index 0000000..ee30558
--- /dev/null
+++ b/tests/gold_tests/cache/gold/hit_gen2.gold
@@ -0,0 +1,8 @@
+{}
+> GET /generation2/cache/10/{} HTTP/1.1
+{}
+< Server: ATS/{}
+< X-Cache-Key: http://127.0.0.1/cache/10/{}
+< X-Cache: hit-fresh
+< X-Cache-Generation: 2
+{}
diff --git a/tests/gold_tests/cache/gold/miss_default-1.gold b/tests/gold_tests/cache/gold/miss_default-1.gold
new file mode 100644
index 0000000..ada9842
--- /dev/null
+++ b/tests/gold_tests/cache/gold/miss_default-1.gold
@@ -0,0 +1,8 @@
+{}
+> GET /default/cache/10/{} HTTP/1.1
+{}
+< Server: ATS/{}
+< X-Cache-Key: http://127.0.0.1/cache/10/{}
+< X-Cache: miss
+< X-Cache-Generation: -1
+{}
diff --git a/tests/gold_tests/cache/gold/miss_default77.gold b/tests/gold_tests/cache/gold/miss_default77.gold
new file mode 100644
index 0000000..083ff08
--- /dev/null
+++ b/tests/gold_tests/cache/gold/miss_default77.gold
@@ -0,0 +1,8 @@
+{}
+> GET /default/cache/10/{} HTTP/1.1
+{}
+< Server: ATS/{}
+< X-Cache-Key: http://127.0.0.1/cache/10/{}
+< X-Cache: miss
+< X-Cache-Generation: 77
+{}
diff --git a/tests/gold_tests/cache/gold/miss_gen1.gold b/tests/gold_tests/cache/gold/miss_gen1.gold
new file mode 100644
index 0000000..cacf5c3
--- /dev/null
+++ b/tests/gold_tests/cache/gold/miss_gen1.gold
@@ -0,0 +1,8 @@
+{}
+> GET /generation1/cache/10/{} HTTP/1.1
+{}
+< Server: ATS/{}
+< X-Cache-Key: http://127.0.0.1/cache/10/{}
+< X-Cache: miss
+< X-Cache-Generation: 1
+{}
diff --git a/tests/gold_tests/cache/gold/miss_gen2.gold b/tests/gold_tests/cache/gold/miss_gen2.gold
new file mode 100644
index 0000000..cf1eb42
--- /dev/null
+++ b/tests/gold_tests/cache/gold/miss_gen2.gold
@@ -0,0 +1,8 @@
+{}
+> GET /generation2/cache/10/{} HTTP/1.1
+{}
+< Server: ATS/{}
+< X-Cache-Key: http://127.0.0.1/cache/10/{}
+< X-Cache: miss
+< X-Cache-Generation: 2
+{}
diff --git a/tests/gold_tests/h2/gold/remap-200.gold b/tests/gold_tests/h2/gold/remap-200.gold
new file mode 100644
index 0000000..5f7e6ec
--- /dev/null
+++ b/tests/gold_tests/h2/gold/remap-200.gold
@@ -0,0 +1,4 @@
+HTTP/2 200
+date: {}
+server: ATS/{}
+
diff --git a/tests/gold_tests/h2/h2client.py b/tests/gold_tests/h2/h2client.py
new file mode 100644
index 0000000..1654f59
--- /dev/null
+++ b/tests/gold_tests/h2/h2client.py
@@ -0,0 +1,63 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import json
+from hyper import HTTPConnection
+import hyper
+import argparse
+
+def getResponseString(response):
+    typestr = str(type(response))
+    if typestr.find('HTTP20') != -1:
+        string = "HTTP/2 {0}\r\n".format(response.status)
+    else:
+        string = "HTTP {0}\r\n".format(response.status)
+    string+='date: '+response.headers.get('date')[0].decode('utf-8')+"\r\n"
+    string+='server: '+response.headers.get('Server')[0].decode('utf-8')+"\r\n"
+    return string
+
+def makerequest(port):
+    hyper.tls._context = hyper.tls.init_context()
+    hyper.tls._context.check_hostname = False
+    hyper.tls._context.verify_mode = hyper.compat.ssl.CERT_NONE
+
+    conn = HTTPConnection('localhost:{0}'.format(port), secure=True)
+
+    sites={'/'}
+    responses = []
+    request_ids = []
+    for site in sites:
+            request_id = conn.request('GET',url=site)
+            request_ids.append(request_id)
+
+    # get responses
+    for req_id in request_ids:
+        response = conn.get_response(req_id)    
+        print(getResponseString(response))
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--port","-p",
+                        type=int,                        
+                        help="Port to use")
+    args=parser.parse_args()
+    makerequest(args.port)
+
+if __name__ == '__main__':
+    main()
+    
diff --git a/tests/gold_tests/h2/http2.test.py b/tests/gold_tests/h2/http2.test.py
new file mode 100644
index 0000000..5630daf
--- /dev/null
+++ b/tests/gold_tests/h2/http2.test.py
@@ -0,0 +1,70 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+Test.Summary = '''
+Test a basic remap of a http connection
+'''
+# need Curl
+Test.SkipUnless(
+    Condition.HasProgram("curl","Curl need to be installed on system for this test to work")
+    )
+Test.ContinueOnFail=True
+# Define default ATS
+ts=Test.MakeATSProcess("ts",select_ports=False)
+server=Test.MakeOriginServer("server")
+
+testName = ""
+request_header={"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+#desired response form the origin server
+response_header={"headers": "HTTP/1.1 200 OK\r\nServer: microserver\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+#add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Variables.ssl_port = 4443
+ts.Disk.remap_config.AddLine(
+    'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+ts.Disk.ssl_multicert_config.AddLine(
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+ts.Disk.records_config.update({
+        'proxy.config.diags.debug.enabled': 1,
+        'proxy.config.diags.debug.tags': 'ssl',
+        'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), 
+        'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
+        'proxy.config.ssl.number.threads': 0,
+        'proxy.config.http.server_ports': '{0} {1}:proto=http2;http:ssl'.format(ts.Variables.port,ts.Variables.ssl_port),  # enable ssl port
+        'proxy.config.ssl.client.verify.server':  0,
+        'proxy.config.ssl.server.cipher_suite' : 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    })
+ts.Setup.CopyAs('h2client.py',Test.RunDirectory)
+# call localhost straight
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='python h2client.py -p {0}'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode=0
+# time delay as proxy.config.http.wait_for_cache could be broken
+tr.Processes.Default.StartBefore(server,ready=When.PortOpen(server.Variables.Port))
+tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port))
+tr.Processes.Default.Streams.stdout="gold/remap-200.gold"
+tr.StillRunningAfter=server
+
+     
diff --git a/tests/gold_tests/h2/ssl/server.key b/tests/gold_tests/h2/ssl/server.key
new file mode 100644
index 0000000..4c7a661
--- /dev/null
+++ b/tests/gold_tests/h2/ssl/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
+kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
+SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
+AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
+B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
+sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
+GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
+YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
+pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
+tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
+yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
+lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
+vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
+-----END RSA PRIVATE KEY-----
diff --git a/tests/gold_tests/h2/ssl/server.pem b/tests/gold_tests/h2/ssl/server.pem
new file mode 100644
index 0000000..a1de94f
--- /dev/null
+++ b/tests/gold_tests/h2/ssl/server.pem
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
+kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
+SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
+AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
+B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
+sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
+GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
+YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
+pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
+tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
+yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
+lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
+vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICszCCAhwCCQCRJsJJ+mTsdDANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wHhcNMTYwODI1MjI1NzIxWhcNMTcwODI1MjI1NzIxWjCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYwc6JQX45GZmMDEjwxYT11
+uVvuBBInfpYJeU8WIXHrKcX5LUSRcBikiKnlfSnMNRohsu6TElQACc60wQ7Q8KDE
+lBSsS1FaHzCIl1t1AkXRmz/1H65JSBvrV/6Z1NC+Gp58EbH7Gul8ByC1xaJm5ID1
+Dd++kOPlY5ZI9ZcFS7HLAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAXSVfZ5p1TkhW
+QiYq9nfQlBnX2NVaf8ymA8edQR0qH/QBv4/52bNNXC7V/V+ev9LCho2iRMeYYyXB
+yo1wBAGR83lS9cF/tOABcYrxjdP54Sfkyh5fomcg8SV7zap6C8mhbV8r3EujbKCx
+igH3fMX5F/eRwNCzaMMyQsXaxTJ3trk=
+-----END CERTIFICATE-----
diff --git a/tests/gold_tests/logging/custom.test.py b/tests/gold_tests/logging/custom.test.py
new file mode 100644
index 0000000..c558eb3
--- /dev/null
+++ b/tests/gold_tests/logging/custom.test.py
@@ -0,0 +1,85 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+
+Test.Summary = '''
+Test custom log file format
+'''
+# need Curl
+Test.SkipUnless(
+    Condition.HasProgram("curl", "Curl need to be installed on system for this test to work"),
+    Condition.IsPlatform("linux")
+    )
+
+# Define default ATS
+ts=Test.MakeATSProcess("ts")
+
+# setup some config file for this server
+ts.Disk.remap_config.AddLine(
+            'map / http://www.linkedin.com/ @action=deny'
+        )
+
+ts.Disk.logging_config.AddLines(
+            '''custom = format {
+  Format = "%<hii> %<hiih>"
+}
+
+log.ascii {
+  Format = custom,
+  Filename = 'test_log_field'
+}'''.split("\n")
+        )
+# at the end of the different test run a custom log file should exist
+Test.Disk.File(os.path.join(ts.Variables.LOGDIR,'test_log_field.log'),exists=True,content='gold/custom.gold')
+
+#first test is a miss for default
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+# time delay as proxy.config.http.wait_for_cache could be broken
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+        
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.1.1.1:{0}" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.2.2.2:{0}" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.3.3.3:{0}" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.3.0.1:{0}" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.43.2.1:{0}" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.213.213.132:{0}" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.123.32.243:{0}" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0    
+
diff --git a/tests/gold_tests/logging/gold/custom.gold b/tests/gold_tests/logging/gold/custom.gold
new file mode 100644
index 0000000..4432d33
--- /dev/null
+++ b/tests/gold_tests/logging/gold/custom.gold
@@ -0,0 +1,8 @@
+127.0.0.1 7F000001
+127.1.1.1 7F010101
+127.2.2.2 7F020202
+127.3.3.3 7F030303
+127.3.0.1 7F030001
+127.43.2.1 7F2B0201
+127.213.213.132 7FD5D584
+127.123.32.243 7F7B20F3
diff --git a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold
new file mode 100644
index 0000000..5a0be7f
--- /dev/null
+++ b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-303.gold
@@ -0,0 +1,15 @@
+``
+> GET http://www.example.com``
+> Host: www.example.com``
+> User-Agent: curl/``
+> Accept: */*
+> Proxy-Connection: Keep-Alive
+``
+< HTTP/1.1 303 See Other
+< Date: ``
+< Age: ``
+< Transfer-Encoding: chunked
+< Proxy-Connection: keep-alive
+< Server: ATS/``
+< 
+``
diff --git a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-tag.gold b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-tag.gold
new file mode 100644
index 0000000..eac700f
--- /dev/null
+++ b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite-tag.gold
@@ -0,0 +1 @@
+``DIAG: (header_rewrite)``
diff --git a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py
new file mode 100644
index 0000000..5c4590f
--- /dev/null
+++ b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite.test.py
@@ -0,0 +1,68 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+Test.Summary = '''
+Test a basic remap of a http connection
+'''
+# need Curl
+Test.SkipUnless(
+    Condition.HasProgram("curl","Curl need to be installed on system for this test to work")
+    )
+Test.ContinueOnFail=True
+# Define default ATS
+ts=Test.MakeATSProcess("ts")
+server=Test.MakeOriginServer("server")
+
+Test.testName = ""
+request_header={"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+#expected response from the origin server
+response_header={"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+
+#add response to the server dictionary
+server.addResponse("sessionfile.log", request_header, response_header)
+ts.Disk.records_config.update({
+        'proxy.config.diags.debug.enabled': 1,
+        'proxy.config.diags.debug.tags': 'header.*',
+    })
+# The following rule changes the status code returned from origin server to 303
+ts.Setup.CopyAs('rules/rule.conf',Test.RunDirectory)
+ts.Disk.plugin_config.AddLine(
+    'header_rewrite.so {0}/rule.conf'.format(Test.RunDirectory)
+)
+ts.Disk.remap_config.AddLine(
+    'map http://www.example.com http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+ts.Disk.remap_config.AddLine(
+    'map http://www.example.com:8080 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+# call localhost straight
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --proxy 127.0.0.1:{0} "http://www.example.com" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+# time delay as proxy.config.http.wait_for_cache could be broken
+tr.Processes.Default.StartBefore(server,ready=When.PortOpen(server.Variables.Port))
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.Processes.Default.Streams.stderr="gold/header_rewrite-303.gold"
+tr.StillRunningAfter=server
+
+ts.Streams.All="gold/header_rewrite-tag.gold"
+
+
+     
diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf
new file mode 100644
index 0000000..358dc34
--- /dev/null
+++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule.conf
@@ -0,0 +1,2 @@
+cond %{STATUS} =200
+set-status 303
diff --git a/tests/gold_tests/remap/gold/remap-200.gold b/tests/gold_tests/remap/gold/remap-200.gold
new file mode 100644
index 0000000..e4f5eb9
--- /dev/null
+++ b/tests/gold_tests/remap/gold/remap-200.gold
@@ -0,0 +1,15 @@
+``
+> GET http://www.example.com``
+> Host: www.example.com``
+> User-Agent: curl/``
+> Accept: */*
+> Proxy-Connection: Keep-Alive
+``
+< HTTP/1.1 200 OK
+< Date: ``
+< Age: ``
+< Transfer-Encoding: chunked
+< Proxy-Connection: keep-alive
+< Server: ATS/``
+< 
+``
diff --git a/tests/gold_tests/remap/gold/remap-404.gold b/tests/gold_tests/remap/gold/remap-404.gold
new file mode 100644
index 0000000..779053a
--- /dev/null
+++ b/tests/gold_tests/remap/gold/remap-404.gold
@@ -0,0 +1,12 @@
+``
+> GET `` HTTP/1.1
+> Host: ``
+> User-Agent: curl/``
+``
+< HTTP/1.1 404 Not Found
+< Date: ``
+< Proxy-Connection: keep-alive
+< Server: ATS/``
+``
+< Content-Type: text/html
+``
diff --git a/tests/gold_tests/remap/gold/remap-hitATS-404.gold b/tests/gold_tests/remap/gold/remap-hitATS-404.gold
new file mode 100644
index 0000000..67a81bc
--- /dev/null
+++ b/tests/gold_tests/remap/gold/remap-hitATS-404.gold
@@ -0,0 +1,11 @@
+``
+> GET / HTTP/1.1
+> Host: ``
+> User-Agent: curl/``
+`` 
+< HTTP/1.1 404 Not Found on Accelerator
+< Date: ``
+< Connection: ``
+< Server: ATS/``
+< Content-Type: text/html
+``
diff --git a/tests/gold_tests/remap/gold/remap-https-200.gold b/tests/gold_tests/remap/gold/remap-https-200.gold
new file mode 100644
index 0000000..9cd42fc
--- /dev/null
+++ b/tests/gold_tests/remap/gold/remap-https-200.gold
@@ -0,0 +1,13 @@
+``
+> GET / HTTP/1.1
+> Host: www.example.com``
+> User-Agent: curl/``
+``
+< HTTP/1.1 200 OK
+< Date: ``
+< Age: ``
+< Transfer-Encoding: chunked
+< Connection: keep-alive
+< Server: ATS/``
+< 
+``
diff --git a/tests/gold_tests/remap/remap_http.test.py b/tests/gold_tests/remap/remap_http.test.py
new file mode 100644
index 0000000..2f6e007
--- /dev/null
+++ b/tests/gold_tests/remap/remap_http.test.py
@@ -0,0 +1,91 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+Test.Summary = '''
+Test a basic remap of a http connection
+'''
+# need Curl
+Test.SkipUnless(
+    Condition.HasProgram("curl","Curl need to be installed on system for this test to work")
+    )
+Test.ContinueOnFail=True
+# Define default ATS
+ts=Test.MakeATSProcess("ts")
+server=Test.MakeOriginServer("server")
+
+Test.testName = ""
+request_header={"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+#expected response from the origin server
+response_header={"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+
+#add response to the server dictionary
+server.addResponse("sessionfile.log", request_header, response_header)
+ts.Disk.records_config.update({
+        'proxy.config.diags.debug.enabled': 1,
+        'proxy.config.diags.debug.tags': 'url.*',
+    })
+
+ts.Disk.remap_config.AddLine(
+    'map http://www.example.com http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+ts.Disk.remap_config.AddLine(
+    'map http://www.example.com:8080 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+# call localhost straight
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl "http://127.0.0.1:{0}/" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+# time delay as proxy.config.http.wait_for_cache could be broken
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.Processes.Default.Streams.stderr="gold/remap-hitATS-404.gold"
+tr.StillRunningAfter=server
+
+# www.example.com host
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --proxy 127.0.0.1:{0} "http://www.example.com" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-200.gold"
+
+# www.example.com:80 host
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.example.com:80/" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-200.gold"
+
+# www.example.com:8080 host
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.example.com:8080" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-200.gold"
+
+# no rule for this
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.test.com/" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-404.gold"
+
+# bad port
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl  --proxy 127.0.0.1:{0} "http://www.example.com:1234/" --verbose'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-404.gold"
+
+     
diff --git a/tests/gold_tests/remap/remap_https.test.py b/tests/gold_tests/remap/remap_https.test.py
new file mode 100644
index 0000000..7c74478
--- /dev/null
+++ b/tests/gold_tests/remap/remap_https.test.py
@@ -0,0 +1,112 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+Test.Summary = '''
+Test a basic remap of a http connection
+'''
+# need Curl
+Test.SkipUnless(
+    Condition.HasProgram("curl","Curl need to be installed on system for this test to work")
+    )
+Test.ContinueOnFail=True
+# Define default ATS
+ts=Test.MakeATSProcess("ts",select_ports=False)
+server=Test.MakeOriginServer("server")
+
+#**testname is required**
+testName = ""
+request_header={"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+#desired response form the origin server
+response_header={"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+#add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Variables.ssl_port = 4443
+ts.Disk.records_config.update({
+        'proxy.config.diags.debug.enabled': 1,
+        'proxy.config.diags.debug.tags': 'lm|ssl',
+        'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), 
+        'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
+        'proxy.config.ssl.number.threads': 0,
+        'proxy.config.http.server_ports': '{0} {1}:proto=http2;http:ssl'.format(ts.Variables.port,ts.Variables.ssl_port),  # enable ssl port
+        'proxy.config.ssl.client.verify.server':  0,
+        'proxy.config.ssl.server.cipher_suite' : 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    })
+
+ts.Disk.remap_config.AddLine(
+    'map https://www.example.com http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+ts.Disk.remap_config.AddLine(
+    'map https://www.example.com:{1} http://127.0.0.1:{0}'.format(server.Variables.Port,ts.Variables.ssl_port)
+)
+
+
+ts.Disk.ssl_multicert_config.AddLine(
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+# call localhost straight
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --http1.1 -k https://127.0.0.1:{0} --verbose'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode=0
+
+# time delay as proxy.config.http.wait_for_cache could be broken
+tr.Processes.Default.StartBefore(server)
+# Delay on readyness of our ssl ports
+tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port))
+tr.Processes.Default.Streams.stderr="gold/remap-hitATS-404.gold"
+tr.StillRunningAfter=server
+tr.StillRunningAfter=ts
+
+
+# www.example.com host
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --http1.1 -k https://127.0.0.1:{0} -H "Host: www.example.com" --verbose'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-https-200.gold"
+
+
+# www.example.com:80 host
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --http1.1 -k https://127.0.0.1:{0} -H "Host: www.example.com:443" --verbose'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-https-200.gold"
+
+# www.example.com:8080 host
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --http1.1 -k https://127.0.0.1:{0} -H "Host: www.example.com:4443" --verbose'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-https-200.gold"
+
+# no rule for this
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --http1.1 -k https://127.0.0.1:{0} -H "Host: www.test.com" --verbose'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-hitATS-404.gold"
+
+# bad port
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --http1.1 -k https://127.0.0.1:{0} -H "Host: www.example.com:1234" --verbose'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stderr="gold/remap-hitATS-404.gold"
+
+    
diff --git a/tests/gold_tests/remap/ssl/server.key b/tests/gold_tests/remap/ssl/server.key
new file mode 100644
index 0000000..4c7a661
--- /dev/null
+++ b/tests/gold_tests/remap/ssl/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
+kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
+SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
+AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
+B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
+sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
+GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
+YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
+pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
+tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
+yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
+lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
+vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
+-----END RSA PRIVATE KEY-----
diff --git a/tests/gold_tests/remap/ssl/server.pem b/tests/gold_tests/remap/ssl/server.pem
new file mode 100644
index 0000000..a1de94f
--- /dev/null
+++ b/tests/gold_tests/remap/ssl/server.pem
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
+kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
+SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
+AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
+B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
+sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
+GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
+YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
+pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
+tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
+yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
+lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
+vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICszCCAhwCCQCRJsJJ+mTsdDANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wHhcNMTYwODI1MjI1NzIxWhcNMTcwODI1MjI1NzIxWjCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYwc6JQX45GZmMDEjwxYT11
+uVvuBBInfpYJeU8WIXHrKcX5LUSRcBikiKnlfSnMNRohsu6TElQACc60wQ7Q8KDE
+lBSsS1FaHzCIl1t1AkXRmz/1H65JSBvrV/6Z1NC+Gp58EbH7Gul8ByC1xaJm5ID1
+Dd++kOPlY5ZI9ZcFS7HLAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAXSVfZ5p1TkhW
+QiYq9nfQlBnX2NVaf8ymA8edQR0qH/QBv4/52bNNXC7V/V+ev9LCho2iRMeYYyXB
+yo1wBAGR83lS9cF/tOABcYrxjdP54Sfkyh5fomcg8SV7zap6C8mhbV8r3EujbKCx
+igH3fMX5F/eRwNCzaMMyQsXaxTJ3trk=
+-----END CERTIFICATE-----
diff --git a/tests/tools/microServer/ssl/server.crt b/tests/tools/microServer/ssl/server.crt
new file mode 100644
index 0000000..0ce6ac5
--- /dev/null
+++ b/tests/tools/microServer/ssl/server.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAhwCCQCRJsJJ+mTsdDANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wHhcNMTYwODI1MjI1NzIxWhcNMTcwODI1MjI1NzIxWjCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYwc6JQX45GZmMDEjwxYT11
+uVvuBBInfpYJeU8WIXHrKcX5LUSRcBikiKnlfSnMNRohsu6TElQACc60wQ7Q8KDE
+lBSsS1FaHzCIl1t1AkXRmz/1H65JSBvrV/6Z1NC+Gp58EbH7Gul8ByC1xaJm5ID1
+Dd++kOPlY5ZI9ZcFS7HLAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAXSVfZ5p1TkhW
+QiYq9nfQlBnX2NVaf8ymA8edQR0qH/QBv4/52bNNXC7V/V+ev9LCho2iRMeYYyXB
+yo1wBAGR83lS9cF/tOABcYrxjdP54Sfkyh5fomcg8SV7zap6C8mhbV8r3EujbKCx
+igH3fMX5F/eRwNCzaMMyQsXaxTJ3trk=
+-----END CERTIFICATE-----
diff --git a/tests/tools/microServer/ssl/server.pem b/tests/tools/microServer/ssl/server.pem
new file mode 100644
index 0000000..a1de94f
--- /dev/null
+++ b/tests/tools/microServer/ssl/server.pem
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
+kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
+SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
+AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
+B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
+sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
+GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
+YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
+pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
+tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
+yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
+lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
+vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICszCCAhwCCQCRJsJJ+mTsdDANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wHhcNMTYwODI1MjI1NzIxWhcNMTcwODI1MjI1NzIxWjCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYwc6JQX45GZmMDEjwxYT11
+uVvuBBInfpYJeU8WIXHrKcX5LUSRcBikiKnlfSnMNRohsu6TElQACc60wQ7Q8KDE
+lBSsS1FaHzCIl1t1AkXRmz/1H65JSBvrV/6Z1NC+Gp58EbH7Gul8ByC1xaJm5ID1
+Dd++kOPlY5ZI9ZcFS7HLAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAXSVfZ5p1TkhW
+QiYq9nfQlBnX2NVaf8ymA8edQR0qH/QBv4/52bNNXC7V/V+ev9LCho2iRMeYYyXB
+yo1wBAGR83lS9cF/tOABcYrxjdP54Sfkyh5fomcg8SV7zap6C8mhbV8r3EujbKCx
+igH3fMX5F/eRwNCzaMMyQsXaxTJ3trk=
+-----END CERTIFICATE-----
diff --git a/tests/tools/microServer/uWServer.py b/tests/tools/microServer/uWServer.py
new file mode 100644
index 0000000..91643a1
--- /dev/null
+++ b/tests/tools/microServer/uWServer.py
@@ -0,0 +1,627 @@
+#!/bin/env python3
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+
+import string
+import http.client
+import cgi
+import time
+import sys
+import json
+import os
+import threading
+from ipaddress import ip_address
+from http.server import BaseHTTPRequestHandler, HTTPServer
+from socketserver import ThreadingMixIn, ForkingMixIn, BaseServer
+from http import HTTPStatus
+import argparse
+import ssl
+import socket
+
+test_mode_enabled = True
+__version__="1.0.Beta"
+
+# hack to deal with sessionvalidation until we fix up the 
+# packaing logic
+sys.path.append(
+    os.path.normpath(
+        os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            '..'
+            )
+        )
+    )
+
+import sessionvalidation.sessionvalidation as sv
+
+
+SERVER_PORT = 5005 # default port
+HTTP_VERSION = 'HTTP/1.1'
+G_replay_dict = {}
+
+count = 0
+class ThreadingServer(ThreadingMixIn, HTTPServer):
+    '''This class forces the creation of a new thread on each connection'''
+    pass
+
+class ForkingServer(ForkingMixIn, HTTPServer):
+    '''This class forces the creation of a new process on each connection'''
+    pass
+
+class SSLServer(ThreadingMixIn, HTTPServer):
+    	def __init__(self, server_address, HandlerClass, options):
+            BaseServer.__init__(self, server_address, HandlerClass)
+            pwd = os.path.dirname(os.path.realpath(__file__))
+            keys = os.path.join(pwd,options.key)
+            certs = os.path.join(pwd,options.cert)
+            self.options = options
+
+            self.daemon_threads = True
+            self.protocol_version = 'HTTP/1.1'
+            if options.clientverify:
+            	self.socket = ssl.wrap_socket(socket.socket(self.address_family, self.socket_type),
+                    keyfile=keys, certfile=certs, server_side=True, cert_reqs=ssl.CERT_REQUIRED, ca_certs='/etc/ssl/certs/ca-certificates.crt')
+            else:
+                self.socket = ssl.wrap_socket(socket.socket(self.address_family, self.socket_type),
+                    keyfile=keys, certfile=certs, server_side=True)
+
+            self.server_bind()
+            self.server_activate()
+
+# Warning: if you can't tell already, it's pretty hacky
+#
+# The standard library HTTP server doesn't exactly provide all the functionality we need from the API it exposes,
+# so we have to go in and override various methods that probably weren't intended to be overridden
+#
+# See the source code (https://hg.python.org/cpython/file/3.5/Lib/http/server.py) if you want to see where all these
+# variables are coming from
+class MyHandler(BaseHTTPRequestHandler):
+    def getTestName(self,requestline):
+        key=None
+        keys=requestline.split(" ")
+        #print(keys)
+        if keys:
+            rkey=keys[1]
+        key=rkey.split("/",1)[1]
+        if key+"/" in G_replay_dict:
+            key = key+"/"
+        elif len(key) > 1 and key[:-1] in G_replay_dict:
+            key = key[:-1]
+        return key
+
+    def parseRequestline(self,requestline):
+        testName=None        
+        return testName
+
+    def testMode(self,requestline):
+        print(requestline)
+        key=self.parseRequestline(requestline)
+        
+        self.send_response(200)
+        self.send_header('Connection', 'close')
+        self.end_headers()
+
+        
+    def get_response_code(self, header):
+        # this could totally go wrong
+        return int(header.split(' ')[1])
+
+    def generator(self):
+        yield 'microserver'
+        yield 'yahoo'
+    def send_response(self, code, message=None):
+        ''' Override `send_response()`'s tacking on of server and date header lines. '''
+        #self.log_request(code)
+        self.send_response_only(code, message)
+
+    def createDummyBodywithLength(self,numberOfbytes):
+        if numberOfbytes==0:
+            return None
+        body= 'a'
+        while numberOfbytes!=1:
+            body += 'b'
+            numberOfbytes -= 1
+        return body
+
+    def writeChunkedData(self):
+        for chunk in self.generator():
+            response_string=bytes('%X\r\n%s\r\n'%(len(chunk),chunk),'UTF-8')
+            self.wfile.write(response_string)
+        response_string=bytes('0\r\n\r\n','UTF-8')
+        self.wfile.write(response_string)
+
+    def readChunks(self):
+        raw_data=b''
+        raw_size = self.rfile.readline(65537)        
+        size = str(raw_size, 'UTF-8').rstrip('\r\n')
+        #print("==========================================>",size)
+        size = int(size,16)
+        while size>0:
+            #print("reading bytes",raw_size)
+            chunk = self.rfile.read(size+2) # 2 for reading /r/n
+            #print("cuhnk: ",chunk)
+            raw_data += chunk
+            raw_size = self.rfile.readline(65537)            
+            size = str(raw_size, 'UTF-8').rstrip('\r\n')
+            size = int(size,16)
+        #print("full chunk",raw_data)
+        chunk = self.rfile.readline(65537) # read the extra blank newline \r\n after the last chunk
+
+    def send_header(self, keyword, value):
+        """Send a MIME header to the headers buffer."""
+        if self.request_version != 'HTTP/0.9':
+            if not hasattr(self, '_headers_buffer'):
+                self._headers_buffer = []
+            self._headers_buffer.append(
+                ("%s: %s\r\n" % (keyword, value)).encode('UTF-8', 'strict')) #original code used latin-1.. seriously?
+
+        if keyword.lower() == 'connection':
+            if value.lower() == 'close':
+                self.close_connection = True
+            elif value.lower() == 'keep-alive':
+                self.close_connection = False
+    def parse_request(self):
+        """Parse a request (internal).
+
+        The request should be stored in self.raw_requestline; the results
+        are in self.command, self.path, self.request_version and
+        self.headers.
+
+        Return True for success, False for failure; on failure, an
+        error is sent back.
+
+        """
+        
+        global count, test_mode_enabled
+        
+        self.command = None  # set in case of error on the first line
+        self.request_version = version = self.default_request_version
+        self.close_connection = True
+        requestline = str(self.raw_requestline, 'UTF-8')
+        #print("request",requestline)
+        requestline = requestline.rstrip('\r\n')
+        self.requestline = requestline        
+        
+        # Examine the headers and look for a Connection directive.        
+        try:
+            self.headers = http.client.parse_headers(self.rfile,
+                                                     _class=self.MessageClass)
+            # read message body
+            if self.headers.get('Content-Length') != None:
+                bodysize = int(self.headers.get('Content-Length'))
+                #print("length of the body is",bodysize)
+                message = self.rfile.read(bodysize)
+                #print("message body",message)
+            if self.headers.get('Transfer-Encoding',"") == 'chunked':
+                #print(self.headers)
+                self.readChunks()
+        except http.client.LineTooLong:
+            self.send_error(
+                HTTPStatus.BAD_REQUEST,
+                "Line too long")
+            return False
+        except http.client.HTTPException as err:
+            self.send_error(
+                HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
+                "Too many headers",
+                str(err)
+            )
+            return False
+        
+        
+        words = requestline.split()
+        if len(words) == 3:
+            command, path, version = words
+            if version[:5] != 'HTTP/':
+                self.send_error(
+                    HTTPStatus.BAD_REQUEST,
+                    "Bad request version (%r)" % version)
+                return False
+            try:
+                base_version_number = version.split('/', 1)[1]
+                version_number = base_version_number.split(".")
+                # RFC 2145 section 3.1 says there can be only one "." and
+                #   - major and minor numbers MUST be treated as
+                #      separate integers;
+                #   - HTTP/2.4 is a lower version than HTTP/2.13, which in
+                #      turn is lower than HTTP/12.3;
+                #   - Leading zeros MUST be ignored by recipients.
+                if len(version_number) != 2:
+                    raise ValueError
+                version_number = int(version_number[0]), int(version_number[1])
+            except (ValueError, IndexError):
+                self.send_error(
+                    HTTPStatus.BAD_REQUEST,
+                    "Bad request version (%r)" % version)
+                return False
+            if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
+                self.close_connection = False
+            if version_number >= (2, 0):
+                self.send_error(
+                    HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
+                    "Invalid HTTP Version (%s)" % base_version_number)
+                return False
+        elif len(words) == 2:
+            command, path = words
+            self.close_connection = True
+            if command != 'GET':
+                self.send_error(
+                    HTTPStatus.BAD_REQUEST,
+                    "Bad HTTP/0.9 request type (%r)" % command)
+                return False
+        elif not words:
+            count += 1
+            print("bla bla on 157 {0} => {1}".format(count,self.close_connection))
+            return False
+        else:
+            self.send_error(
+                HTTPStatus.BAD_REQUEST,
+                "Bad request syntax (%r)" % requestline)
+            return False
+        self.command, self.path, self.request_version = command, path, version
+
+        conntype = self.headers.get('Connection', "")
+        if conntype.lower() == 'close':
+            self.close_connection = True
+        elif (conntype.lower() == 'keep-alive' and
+              self.protocol_version >= "HTTP/1.1"):
+            self.close_connection = False
+         
+        # Examine the headers and look for an Expect directive
+        expect = self.headers.get('Expect', "")
+        if (expect.lower() == "100-continue" and
+                self.protocol_version >= "HTTP/1.1" and
+                self.request_version >= "HTTP/1.1"):
+            print("blabla on 185",self.close_connection)
+            if not self.handle_expect_100():
+                return False
+        return True
+
+    def do_GET(self):
+        global G_replay_dict, test_mode_enabled
+        if test_mode_enabled:
+            request_hash = self.getTestName(self.requestline)
+        else:
+            request_hash, __ = cgi.parse_header(self.headers.get('Content-MD5'))
+        #print("key:",request_hash)
+        try:
+            response_string=None
+            chunkedResponse= False
+            if request_hash not in G_replay_dict:
+                self.send_response(404)
+                self.send_header('Server','blablabla')
+                self.send_header('Connection', 'close')
+                self.end_headers()
+
+            else:
+                resp = G_replay_dict[request_hash]
+                headers = resp.getHeaders().split('\r\n')
+
+                # set status codes
+                status_code = self.get_response_code(headers[0])
+                self.send_response(status_code)
+
+                # set headers
+                for header in headers[1:]: # skip first one b/c it's response code
+                    if header == '':
+                        continue
+                    elif 'Content-Length' in header:
+                        if 'Access-Control' in header: # skipping Access-Control-Allow-Credentials, Access-Control-Allow-Origin, Content-Length
+                            header_parts = header.split(':', 1)
+                            header_field = str(header_parts[0].strip())
+                            header_field_val = str(header_parts[1].strip())
+                            self.send_header(header_field, header_field_val)
+                            continue
+                        lengthSTR = header.split(':')[1]
+                        length = lengthSTR.strip(' ')
+                        if test_mode_enabled: # the length of the body is given priority in test mode rather than the value in Content-Length. But in replay mode Content-Length gets the priority
+                            if not (resp and resp.getBody()): # Don't attach content-length yet if body is present in the response specified by tester
+                                self.send_header('Content-Length', str(length))
+                        else:
+                            self.send_header('Content-Length', str(length))
+                        response_string = self.createDummyBodywithLength(int(length))
+                        continue
+                    if 'Transfer-Encoding' in header:
+                        self.send_header('Transfer-Encoding','Chunked')
+                        response_string='%X\r\n%s\r\n'%(len('ats'),'ats')
+                        chunkedResponse= True                    
+                        continue
+            
+                    header_parts = header.split(':', 1)
+                    header_field = str(header_parts[0].strip())
+                    header_field_val = str(header_parts[1].strip())
+                    #print("{0} === >{1}".format(header_field, header_field_val))
+                    self.send_header(header_field, header_field_val)
+                #End for
+                if test_mode_enabled:
+                    if resp and resp.getBody():
+                        length = len(bytes(resp.getBody(),'UTF-8'))
+                        response_string=resp.getBody()
+                        self.send_header('Content-Length', str(length))
+                self.end_headers()
+                
+                
+                if (chunkedResponse):
+                    self.writeChunkedData()
+                elif response_string!=None and response_string!='':
+                    self.wfile.write(bytes(response_string, 'UTF-8'))
+            return
+        except:
+            e=sys.exc_info()
+            print("Error",e,self.headers)
+            self.send_response(400)
+            self.send_header('Connection', 'close')
+            self.end_headers()
+       
+
+        
+    def do_HEAD(self):
+        global G_replay_dict, test_mode_enabled
+        if test_mode_enabled:
+            request_hash = self.getTestName(self.requestline)
+        else:
+            request_hash, __ = cgi.parse_header(self.headers.get('Content-MD5'))
+        
+        if request_hash not in G_replay_dict:
+            self.send_response(404)
+            self.send_header('Connection', 'close')
+            self.end_headers()
+
+        else:
+            resp = G_replay_dict[request_hash]
+            headers = resp.getHeaders().split('\r\n')
+
+            # set status codes
+            status_code = self.get_response_code(headers[0])
+            self.send_response(status_code)
+
+            # set headers
+            for header in headers[1:]: # skip first one b/c it's response code
+                if header == '':
+                    continue
+                elif 'Content-Length' in header:
+                    self.send_header('Content-Length', '0')
+                    continue
+        
+                header_parts = header.split(':', 1)
+                header_field = str(header_parts[0].strip())
+                header_field_val = str(header_parts[1].strip())
+                #print("{0} === >{1}".format(header_field, header_field_val))
+                self.send_header(header_field, header_field_val)
+
+            self.end_headers()
+
+    def do_POST(self):
+        response_string=None
+        chunkedResponse= False
+        global G_replay_dict, test_mode_enabled
+        if test_mode_enabled:
+            request_hash = self.getTestName(self.requestline)
+        else:
+            request_hash, __ = cgi.parse_header(self.headers.get('Content-MD5'))
+        try:
+            if self.headers.get('Content-MD5') == None:
+                print("Content-MD5 not found")
+                self.send_response(404)
+                self.send_header('Connection', 'close')
+                self.end_headers()
+                return
+
+            if request_hash not in G_replay_dict:
+                self.send_response(404)
+                self.send_header('Connection', 'close')
+                self.end_headers()
+                resp = None
+            else:
+                resp = G_replay_dict[request_hash]
+                resp_headers = resp.getHeaders().split('\r\n')
+                # set status codes
+                status_code = self.get_response_code(resp_headers[0])
+                #print("response code",status_code)
+                self.send_response(status_code)
+                #print("reposen is ",resp_headers)
+                # set headers
+                for header in resp_headers[1:]: # skip first one b/c it's response code
+                    
+                    if header == '':
+                        continue
+                    elif 'Content-Length' in header:
+                        if 'Access-Control' in header: # skipping Access-Control-Allow-Credentials, Access-Control-Allow-Origin, Content-Length
+                            header_parts = header.split(':', 1)
+                            header_field = str(header_parts[0].strip())
+                            header_field_val = str(header_parts[1].strip())
+                            self.send_header(header_field, header_field_val)
+                            continue
+                        
+                        lengthSTR = header.split(':')[1]
+                        length = lengthSTR.strip(' ')
+                        if test_mode_enabled: # the length of the body is given priority in test mode rather than the value in Content-Length. But in replay mode Content-Length gets the priority
+                            if not (resp and resp.getBody()): # Don't attach content-length yet if body is present in the response specified by tester
+                                self.send_header('Content-Length', str(length))
+                        else:
+                            self.send_header('Content-Length', str(length))
+                        response_string = self.createDummyBodywithLength(int(length))
+                        continue
+                    if 'Transfer-Encoding' in header:
+                        self.send_header('Transfer-Encoding','Chunked')
+                        response_string='%X\r\n%s\r\n'%(len('microserver'),'microserver')
+                        chunkedResponse= True                    
+                        continue
+                    
+                    header_parts = header.split(':', 1)
+                    header_field = str(header_parts[0].strip())
+                    header_field_val = str(header_parts[1].strip())
+                    #print("{0} === >{1}".format(header_field, header_field_val))
+                    self.send_header(header_field, header_field_val)
+                # End for loop
+                if test_mode_enabled:
+                    if resp and resp.getBody():
+                        length = len(bytes(resp.getBody(),'UTF-8'))
+                        response_string=resp.getBody()
+                        self.send_header('Content-Length', str(length))    
+                self.end_headers()
+            
+            if (chunkedResponse):
+                self.writeChunkedData()
+            elif response_string!=None and response_string!='':
+                self.wfile.write(bytes(response_string, 'UTF-8'))
+            return
+        except:
+            e=sys.exc_info()
+            print("Error",e,self.headers)
+            self.send_response(400)
+            self.send_header('Connection', 'close')
+            self.end_headers()
+
+def populate_global_replay_dictionary(sessions):
+    ''' Populates the global dictionary of {uuid (string): reponse (Response object)} '''
+    global G_replay_dict
+    for session in sessions:
+        for txn in session.getTransactionIter():
+            G_replay_dict[txn._uuid] = txn.getResponse()
+    
+    print("size",len(G_replay_dict))
+    
+#tests will add responses to the dictionary where key is the testname
+def addResponseHeader(key,response_header):
+    G_replay_dict[key] = response_header
+    
+def _path(exists, arg ):
+    path = os.path.abspath(arg)
+    if not os.path.exists(path) and exists:
+        msg = '"{0}" is not a valid path'.format(path)
+        raise argparse.ArgumentTypeError(msg)
+    return path
+
+def _bool(arg):
+        
+        opt_true_values = set(['y', 'yes', 'true', 't', '1', 'on' , 'all'])
+        opt_false_values = set(['n', 'no', 'false', 'f', '0', 'off', 'none'])
+
+        tmp = arg.lower()
+        if tmp in opt_true_values:
+            return True
+        elif tmp in opt_false_values:
+            return False
+        else:
+            msg = 'Invalid value Boolean value : "{0}"\n Valid options are {0}'.format(arg,
+                    opt_true_values | opt_false_values)
+            raise argparse.ArgumentTypeError(msg)
+
+
+def main():
+    global test_mode_enabled
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument("--data-dir","-d",
+                        type=lambda x: _path(True,x),
+                        required=True,
+                        help="Directory with data file"
+                        )
+
+    parser.add_argument("--public","-P", 
+                        type=_bool, 
+                        default=False,                        
+                        help="Bind server to public IP 0.0.0.0 vs private IP of 127.0.0.1"
+                        )
+
+    parser.add_argument("--port","-p",
+                        type=int,
+                        default=SERVER_PORT,                        
+                        help="Port to use")
+
+    parser.add_argument("--timeout","-t", 
+                        type=float,
+                        default=None,                        
+                        help="socket time out in seconds")                        
+
+    parser.add_argument('-V','--version', action='version', version='%(prog)s {0}'.format(__version__))
+
+    parser.add_argument("--mode","-m",
+                        type=str,
+                        default="test",                        
+                        help="Mode of operation")
+    parser.add_argument("--connection","-c",
+                        type=str,
+                        default="nonSSL",                        
+                        help="use SSL")
+    parser.add_argument("--key","-k",
+                        type=str,
+                        default="ssl/server.pem",                        
+                        help="key for ssl connnection")
+    parser.add_argument("--cert","-cert",
+                        type=str,
+                        default="ssl/server.crt",                        
+                        help="certificate")
+    parser.add_argument("--clientverify","-cverify",
+                        type=bool,
+                        default=False,
+                        help="verify client cert")
+
+    args=parser.parse_args()
+    options = args
+    # set up global dictionary of {uuid (string): response (Response object)}
+    s = sv.SessionValidator(args.data_dir)
+    populate_global_replay_dictionary(s.getSessionIter())
+    print("Dropped {0} sessions for being malformed".format(len(s.getBadSessionList())))
+    
+    # start server
+    try:
+        server_port = args.port
+        socket_timeout = args.timeout
+        test_mode_enabled = args.mode=="test"
+        
+        MyHandler.protocol_version = HTTP_VERSION        
+        if options.connection == 'ssl':
+            server = SSLServer(('',options.port), MyHandler, options)
+        else:
+            server = ThreadingServer(('', server_port), MyHandler)
+        server.timeout = 5
+        print("started server")
+        server_thread = threading.Thread(target=server.serve_forever())
+        server_thread.daemon=True
+        server_thread.start()
+
+        #s_serverThread.daemon = True
+        #s_serverThread.start()
+        #threads.append(s_serverThread)
+
+        
+        #server.timeout = socket_timeout or 5
+        #print("=== started httpserver ===")
+        #server_thread = threading.Thread(target=server.serve_forever())
+        #server_thread.daemon=True
+        #server_thread.start()
+        #threads.append(server_thread)
+        #server.serve_forever()
+        
+        #for t in threads:
+        #    t.start()
+
+        #for t in threads:
+        #    t.join()
+    except KeyboardInterrupt:
+        print("\n=== ^C received, shutting down httpserver ===")
+        server.socket.close()
+        #s_server.socket.close()
+        sys.exit(0)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tests/tools/readme.txt b/tests/tools/readme.txt
new file mode 100644
index 0000000..9bf71d6
--- /dev/null
+++ b/tests/tools/readme.txt
@@ -0,0 +1,20 @@
+
+#  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.
+
+These tools are meant to become it own repository in the furture. They are here at the moment to help accelerate progress at getting everything working.
+
+Note these Tools require python 3.4 or better.
diff --git a/tests/tools/sessionvalidation/__init__.py b/tests/tools/sessionvalidation/__init__.py
new file mode 100644
index 0000000..bcbf685
--- /dev/null
+++ b/tests/tools/sessionvalidation/__init__.py
@@ -0,0 +1,17 @@
+'''
+'''
+#  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.
diff --git a/tests/tools/sessionvalidation/badsession.py b/tests/tools/sessionvalidation/badsession.py
new file mode 100644
index 0000000..50e0047
--- /dev/null
+++ b/tests/tools/sessionvalidation/badsession.py
@@ -0,0 +1,33 @@
+'''
+'''
+#  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.
+class BadSession(object):
+    ''' 
+    Session encapsulates a single BAD user session. Bad meaning that for some reason the session is invalid.
+
+    _filename is the filename of the bad JSON session 
+    _reason is a string with some kind of explanation on why the session was bad
+    '''
+
+    def __repr__(self):
+        return "<Session {{'filename': {0}, 'reason': {1}>".format(
+            self._filename, self._reason
+        )
+
+    def __init__(self, filename, reason):
+        self._filename = filename
+        self._reason = reason
diff --git a/tests/tools/sessionvalidation/request.py b/tests/tools/sessionvalidation/request.py
new file mode 100644
index 0000000..e664642
--- /dev/null
+++ b/tests/tools/sessionvalidation/request.py
@@ -0,0 +1,47 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+import hashlib
+
+class Request(object):
+    ''' Request encapsulates a single request from the UA '''
+
+    def getTimestamp(self):
+        return self._timestamp
+
+    def getHeaders(self):
+        return self._headers
+
+    def getBody(self):
+        return self._body
+
+    def getHeaderMD5(self):
+        ''' Returns the MD5 hash of the headers
+
+        This is used to do a unique mapping to a request/response transaction '''
+        return hashlib.md5(self._headers.encode()).hexdigest()
+
+    def __repr__(self):
+        #return str(self._timestamp)
+        return "<Request: {{'timestamp': {0}, 'headers': {1}, 'body': {2}}}>".format(
+            str(self._timestamp), str(self._headers), str(self._body)
+        )
+
+    def __init__(self, timestamp, headers, body):
+        self._timestamp = timestamp
+        self._headers = headers
+        self._body = body
diff --git a/tests/tools/sessionvalidation/response.py b/tests/tools/sessionvalidation/response.py
new file mode 100644
index 0000000..49b0d02
--- /dev/null
+++ b/tests/tools/sessionvalidation/response.py
@@ -0,0 +1,38 @@
+'''
+'''
+#  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.
+class Response(object):
+    ''' Response encapsulates a single request from the UA '''
+
+    def getTimestamp(self):
+        return self._timestamp
+
+    def getHeaders(self):
+        return self._headers
+
+    def getBody(self):
+        return self._body
+
+    def __repr__(self):
+        return "<Response: {{'timestamp': {0}, 'headers': {1}, 'body': {2}}}>".format(
+            self._timestamp, self._headers, self._body
+        )
+
+    def __init__(self, timestamp, headers, body):
+        self._timestamp = timestamp
+        self._headers = headers
+        self._body = body
diff --git a/tests/tools/sessionvalidation/session.py b/tests/tools/sessionvalidation/session.py
new file mode 100644
index 0000000..4bcb2c6
--- /dev/null
+++ b/tests/tools/sessionvalidation/session.py
@@ -0,0 +1,41 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+import sessionvalidation.transaction as transaction
+
+class Session(object):
+    ''' Session encapsulates a single user session '''
+
+    def getTransactionList(self):
+        ''' Returns a list of transaction objects '''
+        return self._transaction_list
+
+    def getTransactionIter(self):
+        ''' Returns an iterator of transaction objects '''
+        return iter(self._transaction_list)
+
+    def __repr__(self):
+        return "<Session {{'filename': {0}, 'version': {1}, 'timestamp: {2}, 'encoding': {3}, 'transaction_list': {4}}}>".format(
+                  self._filename, self._version, self._timestamp, self._encoding, repr(self._transaction_list)
+            )
+
+    def __init__(self, filename, version, timestamp, transaction_list, encoding=None):
+        self._filename = filename
+        self._version = version
+        self._timestamp = timestamp
+        self._encoding = encoding
+        self._transaction_list = transaction_list
diff --git a/tests/tools/sessionvalidation/sessionvalidation.py b/tests/tools/sessionvalidation/sessionvalidation.py
new file mode 100644
index 0000000..c33b7c7
--- /dev/null
+++ b/tests/tools/sessionvalidation/sessionvalidation.py
@@ -0,0 +1,269 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+import json
+import os
+
+import sessionvalidation.session as session
+import sessionvalidation.transaction as transaction
+import sessionvalidation.request as request
+import sessionvalidation.response as response
+
+valid_HTTP_request_methods = ['GET', 'POST', 'HEAD']
+G_VERBOSE_LOG = True
+
+
+def _verbose_print(msg, verbose_on=False):
+    ''' Print msg if verbose_on is set to True or G_VERBOSE_LOG is set to True'''
+    if verbose_on or G_VERBOSE_LOG:
+        print(msg)
+
+
+class SessionValidator(object):
+    '''
+    SessionValidator parses, validates, and exports an API for a given set of JSON sessions generated from Apache Traffic Server
+
+    SessionValidator is initialized with a path to a directory of JSON sessions. It then automatically parses and validates all the
+    session in the directory. After initialization, the user may use the provided API
+
+    TODO :
+    Provide a list of guaranteed fields for each type of object (ie a Transaction has a request and a response, a request has ...)
+    '''
+
+    def parse(self):
+        ''' 
+        Constructs Session objects from JSON files on disk and stores objects into _sessions 
+
+        All sessions missing required fields (ie. a session timestamp, a response for every request, etc) are
+        dropped and the filename is stored inside _bad_sessions
+        '''
+
+        log_filenames = [os.path.join(self._json_log_dir, f) for f in os.listdir(self._json_log_dir) if os.path.isfile(os.path.join(self._json_log_dir, f))]
+
+        for fname in log_filenames:
+            with open(fname) as f:
+                # first attempt to load the JSON
+                try:
+                    sesh = json.load(f)
+                except:
+                    self._bad_sessions.append(fname)
+                    _verbose_print("Warning: JSON parse error on file={0}".format(fname))
+                    print("Warning: JSON parse error on file={0}".format(fname))
+                    continue
+
+                # then attempt to extract all the required fields from the JSON
+                try:
+                    session_timestamp = sesh['timestamp']
+                    session_version = sesh['version']
+                    session_txns = list()
+                    for txn in sesh['txns']:
+                        #print("PERSIA____________________________________________________________",txn)
+                        # create transaction Request object
+                        txn_request = txn['request']
+                       
+                        txn_request_body = ''
+                        if 'body' in txn_request:
+                            txn_request_body = txn_request['body']
+                        txn_request_obj = request.Request(txn_request['timestamp'], txn_request['headers'], txn_request_body)
+                        # Create transaction Response object
+                        txn_response = txn['response']
+                        txn_response_body = ''
+                        if 'body' in txn_response:
+                            txn_response_body = txn_response['body']
+                        txn_response_obj = response.Response(txn_response['timestamp'], txn_response['headers'], txn_response_body)
+
+                        # create Transaction object
+                        txn_obj = transaction.Transaction(txn_request_obj, txn_response_obj, txn['uuid'])
+                        session_txns.append(txn_obj)
+                        #print(txn_request['timestamp'])
+                    session_obj = session.Session(fname, session_version, session_timestamp, session_txns)
+
+                except KeyError as e:
+                    self._bad_sessions.append(fname)
+                    print("Warning: parse error on key={0} for file={1}".format(e, fname))
+                    _verbose_print("Warning: parse error on key={0} for file={1}".format(e, fname))
+                    continue
+
+                self._sessions.append(session_obj)
+
+
+    def validate(self):
+        ''' Prunes out all the invalid Sessions in _sessions '''
+
+        good_sessions = list()
+
+        for sesh in self._sessions:
+            if SessionValidator.validateSingleSession(sesh):
+                good_sessions.append(sesh)
+            else:
+                self._bad_sessions.append(sesh._filename)
+
+        self._sessions = good_sessions
+
+
+    @staticmethod
+    def validateSingleSession(sesh):
+        ''' Takes in a single Session object as input, returns whether or not the Session is valid '''
+
+        retval = True
+
+        try:
+            # first validate fields
+            if not sesh._filename:
+                _verbose_print("bad session filename")
+                retval = False
+            elif not sesh._version:
+                _verbose_print("bad session version")
+                retval = False
+            elif float(sesh._timestamp) <= 0:
+                _verbose_print("bad session timestamp")
+                retval = False
+            elif not bool(sesh.getTransactionList()):
+                _verbose_print("session has no transaction list")
+                retval = False
+
+            # validate Transactions now
+            for txn in sesh.getTransactionIter():
+                if not SessionValidator.validateSingleTransaction(txn): 
+                    retval = False
+
+        except ValueError as e:
+            _verbose_print("most likely an invalid session timestamp")
+            retval = False
+
+        return retval
+
+
+    @staticmethod
+    def validateSingleTransaction(txn):
+        ''' Takes in a single Transaction object as input, and returns whether or not the Transaction is valid '''
+
+        txn_req = txn.getRequest()
+        txn_resp = txn.getResponse()
+        retval = True
+
+        #valid_HTTP_request_methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH']
+        # we can later uncomment the previous line to support more HTTP methods
+        valid_HTTP_versions = ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2.0']
+
+        try:
+            # validate request first
+            if not txn_req:
+                _verbose_print("no transaction request")
+                retval = False
+            elif txn_req.getBody() == None:
+                _verbose_print("transaction body is set to None")
+                retval = False
+            elif float(txn_req.getTimestamp()) <= 0:
+                _verbose_print("invalid transaction request timestamp")
+                retval = False
+            elif txn_req.getHeaders().split()[0] not in valid_HTTP_request_methods:
+                _verbose_print("invalid HTTP method for transaction {0}".format( txn_req.getHeaders().split()[0]))
+                retval = False
+            elif not txn_req.getHeaders().endswith("\r\n\r\n"):
+                _verbose_print("transaction request headers didn't end with \\r\\n\\r\\n")
+                retval = False
+            elif txn_req.getHeaders().split()[2] not in valid_HTTP_versions:
+                _verbose_print("invalid HTTP version in request")
+                retval = False
+
+            # if the Host header is not present and vaild we reject this transaction
+            found_host = False
+            for header in txn_req.getHeaders().split('\r\n'):
+                split_header = header.split(' ')
+                if split_header[0] == 'Host:':
+                    found_host = True
+                    host_header_no_space = len(split_header) == 1
+                    host_header_with_space = len(split_header) == 2 and split_header[1] == ''
+                    if host_header_no_space or host_header_with_space:
+                        found_host = False
+            if not found_host:
+                print("missing host",txn_req)
+                _verbose_print("transaction request Host header doesn't have specified host")
+                retval = False
+
+            # reject if the host is localhost (since ATS seems to ignore remap rules for localhost requests)
+            if "127.0.0.1" in txn_req.getHeaders() or "localhost" in txn_req.getHeaders():
+                _verbose_print("transaction request Host is localhost, we must reject because ATS ignores remap rules for localhost requests")
+                retval = False
+
+
+            # now validate response
+            if not txn_resp:
+                _verbose_print("no transaction response")
+                retval = False
+            elif txn_resp.getBody() == None:
+                _verbose_print("transaction response body set to None")
+                retval = False
+            elif float(txn_resp.getTimestamp()) <= 0:
+                _verbose_print("invalid transaction response timestamp")
+                retval = False
+            elif txn_resp.getHeaders().split()[0] not in valid_HTTP_versions:
+                _verbose_print("invalid HTTP response header")
+                retval = False
+            elif not txn_resp.getHeaders().endswith("\r\n\r\n"):
+                _verbose_print("transaction response headers didn't end with \\r\\n\\r\\n")
+                retval = False
+
+            # if any of the 3xx responses have bodies, then the must reject this transaction, since 3xx
+            # errors by definition can't have bodies
+            response_line = txn_resp.getHeaders().split('\r\n')[0]
+            response_code = response_line.split(' ')[1]
+            if response_code.startswith('3') and txn_resp.getBody():
+                _verbose_print("transaction response was 3xx and had a body")
+                retval = False
+
+
+        except ValueError as e:
+            _verbose_print("most likely an invalid transaction timestamp")
+            retval = False
+
+        except IndexError as e:
+            _verbose_print("most likely a bad transaction header")
+            retval = False
+
+        return retval
+
+
+    def getSessionList(self):
+        ''' Returns the list of Session objects '''
+        return self._sessions
+
+
+    def getSessionIter(self):
+        ''' Returns an iterator of the Session objects '''
+        return iter(self._sessions)
+
+
+    def getBadSessionList(self):
+        ''' Returns a list of bad session filenames (list of strings) '''
+        return self._bad_sessions
+
+
+    def getBadSessionListIter(self):
+        ''' Returns an iterator of bad session filenames (iterator of strings) '''
+        return iter(self._bad_sessions)
+
+
+    def __init__(self, json_log_dir):
+        global valid_HTTP_request_methods
+        self._json_log_dir = json_log_dir
+        self._bad_sessions = list()   # list of filenames
+        self._sessions = list()       # list of _good_ session objects
+
+        self.parse()
+        self.validate()
diff --git a/tests/tools/sessionvalidation/transaction.py b/tests/tools/sessionvalidation/transaction.py
new file mode 100644
index 0000000..ca70de6
--- /dev/null
+++ b/tests/tools/sessionvalidation/transaction.py
@@ -0,0 +1,39 @@
+'''
+'''
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import sessionvalidation.request as request
+import sessionvalidation.response as response
+
+class Transaction(object):
+    ''' Tranaction encapsulates a single UA transaction '''
+
+    def getRequest(self):
+        return self._request
+
+    def getResponse(self):
+        return self._response
+
+    def __repr__(self):
+        return "<Transaction {{'uuid': {0}, 'request': {1}, 'response': {2}}}>".format(
+            self._uuid, self._request, self._response
+        )
+
+    def __init__(self, request, response, uuid):
+        self._request = request
+        self._response = response
+        self._uuid = uuid

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 02/14: Doc for microserver in AuTest

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 80f36529d1bf24119573d5476a9b03664c91ec61
Author: Persia Aziz <pe...@yahoo-inc.com>
AuthorDate: Wed Feb 22 13:17:06 2017 -0600

    Doc for microserver in AuTest
---
 tests/getting_started.md | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/tests/getting_started.md b/tests/getting_started.md
index ba6c911..f3df186 100644
--- a/tests/getting_started.md
+++ b/tests/getting_started.md
@@ -88,7 +88,7 @@ A number of file object are define to help with adding values to a given configu
 Create a server
 
 ```python
-# don't ports because a config file will set them
+# don't set ports because a config file will set them
 ts1 = Test.MakeATSProcess("ts1",select_ports=False)
 ts1.Setup.ts.CopyConfig('config/records_8090.config','records.config')
 ```
@@ -127,6 +127,34 @@ ts.Disk.remap_config.AddLines([
         ])
 ```
 
+## Setup Origin Server
+###Test.MakeOriginServer(Name)
+ * name - A name for this instance of Origin Server.
+ 
+ This function returns a AuTest process object that launches the python-based microserver. Micro-Server is a mock server which responds to client http requests. Microserver needs to be setup for the tests that require an origin server behind ATS. The server reads a JSON-formatted data file that contains request headers and the corresponding response headers. Microserver responds with payload if the response header contains Content-Length or Transfer-Enconding specified.
+ 
+###addResponse(filename, request_header, response_header)
+* filename - name of the file where the request header and response header will be written to in JSON format
+* request_header - dictionary of request header
+* response_header - dictionary of response header corresponding to the request header.
+
+This function adds the request header and response header to a file which is then read by the microserver to populate request-response map. The key-fields required for the header dictionary are 'headers', 'timestamp' and 'body'.
+
+###Example
+```python
+#create the origin server process
+server=Test.MakeOriginServer("server")
+#define the request header and the desired response header
+request_header={"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+#desired response form the origin server
+response_header={"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+#addResponse adds the transaction to a file which is used by the server
+server.addResponse("sessionlog.json", request_header, response_header)
+#add remap rule to traffic server
+ts.Disk.remap_config.AddLine(
+    'map http://www.example.com http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+```
 ### CopyConfig(file, targetname=None, process=None)
 * file - name of the file to copy. Relative paths are relative from the test file location
 * targetname - the name name of the file when copied to the correct configuration location

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.

[trafficserver] 07/14: change -x to -r as 'enable' is not executable

Posted by zw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f6b284c837cfe9b264bbd0c343092e19ea02300c
Author: Jason Kenny <dr...@live.com>
AuthorDate: Fri Feb 24 15:29:19 2017 -0600

    change -x to -r as 'enable' is not executable
---
 tests/autest.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/autest.sh b/tests/autest.sh
index 087ea00..45f4d51 100755
--- a/tests/autest.sh
+++ b/tests/autest.sh
@@ -27,7 +27,7 @@ if [ ! -f ./env-test/bin/autest ]; then\
         echo -e "${GREEN}Done!${NC}";\
 	fi
 # this is for rhel or centos systems
-test -x /opt/rh/rh-python35/enable && . /opt/rh/rh-python35/enable
+test -r /opt/rh/rh-python35/enable && . /opt/rh/rh-python35/enable
 . env-test/bin/activate
 ./env-test/bin/autest -D gold_tests $*
 ret=$?

-- 
To stop receiving notification emails like this one, please contact
"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>.