THRIFT-3118: add http (for non-ssl and for ssl) to the python cross tests
diff --git a/build/docker/scripts/sca.sh b/build/docker/scripts/sca.sh
index f17f703..f26ce95 100755
--- a/build/docker/scripts/sca.sh
+++ b/build/docker/scripts/sca.sh
@@ -39,10 +39,10 @@
 cppcheck --force --quiet --inline-suppr --error-exitcode=1 -j2 lib/c_glib/src lib/c_glib/test test/c_glib/src tutorial/c_glib
 
 # Python code style
-flake8 --ignore=E501 lib/py
-flake8 tutorial/py
+flake8 --ignore=E501 --exclude=lib/py/build lib/py
+flake8 --exclude=tutorial/py/build tutorial/py
 # THRIFT-4371 : generated files are excluded because they haven't been scrubbed yet
-flake8 --ignore=E501 --exclude="*/gen-py*/*" test/py
+flake8 --ignore=E501 --exclude="*/gen-py*/*",test/py/build test/py
 flake8 test/py.twisted
 flake8 test/py.tornado
 flake8 --ignore=E501 test/test.py
diff --git a/lib/py/src/server/THttpServer.py b/lib/py/src/server/THttpServer.py
index 1b501a7..85cf400 100644
--- a/lib/py/src/server/THttpServer.py
+++ b/lib/py/src/server/THttpServer.py
@@ -17,6 +17,8 @@
 # under the License.
 #
 
+import ssl
+
 from six.moves import BaseHTTPServer
 
 from thrift.server import TServer
@@ -47,11 +49,17 @@
                  server_address,
                  inputProtocolFactory,
                  outputProtocolFactory=None,
-                 server_class=BaseHTTPServer.HTTPServer):
-        """Set up protocol factories and HTTP server.
+                 server_class=BaseHTTPServer.HTTPServer,
+                 **kwargs):
+        """Set up protocol factories and HTTP (or HTTPS) server.
 
         See BaseHTTPServer for server_address.
         See TServer for protocol factories.
+
+        To make a secure server, provide the named arguments:
+        * cafile    - to validate clients [optional]
+        * cert_file - the server cert
+        * key_file  - the server's key
         """
         if outputProtocolFactory is None:
             outputProtocolFactory = inputProtocolFactory
@@ -83,5 +91,16 @@
 
         self.httpd = server_class(server_address, RequestHander)
 
+        if (kwargs.get('cafile') or kwargs.get('cert_file') or kwargs.get('key_file')):
+            context = ssl.create_default_context(cafile=kwargs.get('cafile'))
+            context.check_hostname = False
+            context.load_cert_chain(kwargs.get('cert_file'), kwargs.get('key_file'))
+            context.verify_mode = ssl.CERT_REQUIRED if kwargs.get('cafile') else ssl.CERT_NONE
+            self.httpd.socket = context.wrap_socket(self.httpd.socket, server_side=True)
+
     def serve(self):
         self.httpd.serve_forever()
+
+    def shutdown(self):
+        self.httpd.socket.close()
+        # self.httpd.shutdown() # hangs forever, python doesn't handle POLLNVAL properly!
diff --git a/lib/py/src/transport/THttpClient.py b/lib/py/src/transport/THttpClient.py
index fb33421..60ff226 100644
--- a/lib/py/src/transport/THttpClient.py
+++ b/lib/py/src/transport/THttpClient.py
@@ -20,6 +20,7 @@
 from io import BytesIO
 import os
 import socket
+import ssl
 import sys
 import warnings
 import base64
@@ -34,17 +35,20 @@
 class THttpClient(TTransportBase):
     """Http implementation of TTransport base."""
 
-    def __init__(self, uri_or_host, port=None, path=None):
-        """THttpClient supports two different types constructor parameters.
+    def __init__(self, uri_or_host, port=None, path=None, cafile=None, cert_file=None, key_file=None, ssl_context=None):
+        """THttpClient supports two different types of construction:
 
         THttpClient(host, port, path) - deprecated
-        THttpClient(uri)
+        THttpClient(uri, [port=<n>, path=<s>, cafile=<filename>, cert_file=<filename>, key_file=<filename>, ssl_context=<context>])
 
-        Only the second supports https.
+        Only the second supports https.  To properly authenticate against the server,
+        provide the client's identity by specifying cert_file and key_file.  To properly
+        authenticate the server, specify either cafile or ssl_context with a CA defined.
+        NOTE: if both cafile and ssl_context are defined, ssl_context will override cafile.
         """
         if port is not None:
             warnings.warn(
-                "Please use the THttpClient('http://host:port/path') syntax",
+                "Please use the THttpClient('http{s}://host:port/path') constructor",
                 DeprecationWarning,
                 stacklevel=2)
             self.host = uri_or_host
@@ -60,6 +64,9 @@
                 self.port = parsed.port or http_client.HTTP_PORT
             elif self.scheme == 'https':
                 self.port = parsed.port or http_client.HTTPS_PORT
+                self.certfile = cert_file
+                self.keyfile = key_file
+                self.context = ssl.create_default_context(cafile=cafile) if (cafile and not ssl_context) else ssl_context
             self.host = parsed.hostname
             self.path = parsed.path
             if parsed.query:
@@ -100,12 +107,17 @@
 
     def open(self):
         if self.scheme == 'http':
-            self.__http = http_client.HTTPConnection(self.host, self.port)
+            self.__http = http_client.HTTPConnection(self.host, self.port,
+                                                     timeout=self.__timeout)
         elif self.scheme == 'https':
-            self.__http = http_client.HTTPSConnection(self.host, self.port)
-            if self.using_proxy():
-                self.__http.set_tunnel(self.realhost, self.realport,
-                                       {"Proxy-Authorization": self.proxy_auth})
+            self.__http = http_client.HTTPSConnection(self.host, self.port,
+                                                      key_file=self.keyfile,
+                                                      cert_file=self.certfile,
+                                                      timeout=self.__timeout,
+                                                      context=self.context)
+        if self.using_proxy():
+            self.__http.set_tunnel(self.realhost, self.realport,
+                                   {"Proxy-Authorization": self.proxy_auth})
 
     def close(self):
         self.__http.close()
diff --git a/test/crossrunner/run.py b/test/crossrunner/run.py
index 25c58ce..a7bc939 100644
--- a/test/crossrunner/run.py
+++ b/test/crossrunner/run.py
@@ -235,7 +235,7 @@
                 logger.warn('[%s]: Detected socket bind failure, retrying...', test.server.name)
                 bind_retry_count += 1
             else:
-                result = RESULT_TIMEOUT if cl.expired else cl.returncode if cl.proc.poll() is not None else RESULT_ERROR
+                result = RESULT_TIMEOUT if cl.expired else cl.returncode if (cl.proc and cl.proc.poll()) is not None else RESULT_ERROR
 
                 # For servers that handle a controlled shutdown by signal
                 # if they are killed, or return an error code, that is a
diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json
index f767649..16ede27 100644
--- a/test/known_failures_Linux.json
+++ b/test/known_failures_Linux.json
@@ -83,6 +83,46 @@
   "cpp-nodejs_multij-json_http-domain",
   "cpp-nodejs_multij-json_http-ip",
   "cpp-nodejs_multij-json_http-ip-ssl",
+  "cpp-py3_binary-accel_http-ip",
+  "cpp-py3_binary-accel_http-ip-ssl",
+  "cpp-py3_binary_http-ip",
+  "cpp-py3_binary_http-ip-ssl",
+  "cpp-py3_compact-accelc_http-ip",
+  "cpp-py3_compact-accelc_http-ip-ssl",
+  "cpp-py3_compact_http-ip",
+  "cpp-py3_compact_http-ip-ssl",
+  "cpp-py3_json_http-ip",
+  "cpp-py3_json_http-ip-ssl",
+  "cpp-py3_multi-accel_http-ip",
+  "cpp-py3_multi-accel_http-ip-ssl",
+  "cpp-py3_multi-binary_http-ip",
+  "cpp-py3_multi-binary_http-ip-ssl",
+  "cpp-py3_multic-accelc_http-ip",
+  "cpp-py3_multic-accelc_http-ip-ssl",
+  "cpp-py3_multic-compact_http-ip",
+  "cpp-py3_multic-compact_http-ip-ssl",
+  "cpp-py3_multij-json_http-ip",
+  "cpp-py3_multij-json_http-ip-ssl",
+  "cpp-py_binary-accel_http-ip",
+  "cpp-py_binary-accel_http-ip-ssl",
+  "cpp-py_binary_http-ip",
+  "cpp-py_binary_http-ip-ssl",
+  "cpp-py_compact-accelc_http-ip",
+  "cpp-py_compact-accelc_http-ip-ssl",
+  "cpp-py_compact_http-ip",
+  "cpp-py_compact_http-ip-ssl",
+  "cpp-py_json_http-ip",
+  "cpp-py_json_http-ip-ssl",
+  "cpp-py_multi-accel_http-ip",
+  "cpp-py_multi-accel_http-ip-ssl",
+  "cpp-py_multi-binary_http-ip",
+  "cpp-py_multi-binary_http-ip-ssl",
+  "cpp-py_multic-accelc_http-ip",
+  "cpp-py_multic-accelc_http-ip-ssl",
+  "cpp-py_multic-compact_http-ip",
+  "cpp-py_multic-compact_http-ip-ssl",
+  "cpp-py_multij-json_http-ip",
+  "cpp-py_multij-json_http-ip-ssl",
   "cpp-rs_multi_buffered-ip",
   "cpp-rs_multi_framed-ip",
   "cpp-rs_multic_buffered-ip",
@@ -166,42 +206,62 @@
   "d-py3_binary-accel_buffered-ip-ssl",
   "d-py3_binary-accel_framed-ip",
   "d-py3_binary-accel_framed-ip-ssl",
+  "d-py3_binary-accel_http-ip",
+  "d-py3_binary-accel_http-ip-ssl",
   "d-py3_binary_buffered-ip",
   "d-py3_binary_buffered-ip-ssl",
   "d-py3_binary_framed-ip",
   "d-py3_binary_framed-ip-ssl",
+  "d-py3_binary_http-ip",
+  "d-py3_binary_http-ip-ssl",
   "d-py3_compact-accelc_buffered-ip",
   "d-py3_compact-accelc_buffered-ip-ssl",
   "d-py3_compact-accelc_framed-ip",
   "d-py3_compact-accelc_framed-ip-ssl",
+  "d-py3_compact-accelc_http-ip",
+  "d-py3_compact-accelc_http-ip-ssl",
   "d-py3_compact_buffered-ip",
   "d-py3_compact_buffered-ip-ssl",
   "d-py3_compact_framed-ip",
   "d-py3_compact_framed-ip-ssl",
+  "d-py3_compact_http-ip",
+  "d-py3_compact_http-ip-ssl",
   "d-py3_json_buffered-ip",
   "d-py3_json_buffered-ip-ssl",
   "d-py3_json_framed-ip",
   "d-py3_json_framed-ip-ssl",
+  "d-py3_json_http-ip",
+  "d-py3_json_http-ip-ssl",
   "d-py_binary-accel_buffered-ip",
   "d-py_binary-accel_buffered-ip-ssl",
   "d-py_binary-accel_framed-ip",
   "d-py_binary-accel_framed-ip-ssl",
+  "d-py_binary-accel_http-ip",
+  "d-py_binary-accel_http-ip-ssl",
   "d-py_binary_buffered-ip",
   "d-py_binary_buffered-ip-ssl",
   "d-py_binary_framed-ip",
   "d-py_binary_framed-ip-ssl",
+  "d-py_binary_http-ip",
+  "d-py_binary_http-ip-ssl",
   "d-py_compact-accelc_buffered-ip",
   "d-py_compact-accelc_buffered-ip-ssl",
   "d-py_compact-accelc_framed-ip",
   "d-py_compact-accelc_framed-ip-ssl",
+  "d-py_compact-accelc_http-ip",
+  "d-py_compact-accelc_http-ip-ssl",
   "d-py_compact_buffered-ip",
   "d-py_compact_buffered-ip-ssl",
   "d-py_compact_framed-ip",
   "d-py_compact_framed-ip-ssl",
+  "d-py_compact_http-ip",
+  "d-py_compact_http-ip-ssl",
   "d-py_json_buffered-ip",
   "d-py_json_buffered-ip-ssl",
   "d-py_json_framed-ip",
   "d-py_json_framed-ip-ssl",
+  "d-py_json_http-ip",
+  "d-py_json_http-ip-ssl",
   "erl-cpp_binary_buffered-ip",
   "erl-cpp_compact_buffered-ip",
   "erl-csharp_binary_buffered-ip",
@@ -302,8 +362,118 @@
   "nodejs-netcore_json_buffered-ip-ssl",
   "nodejs-netcore_json_framed-ip",
   "nodejs-netcore_json_framed-ip-ssl",
+  "nodejs-py3_binary-accel_http-ip",
+  "nodejs-py3_binary-accel_http-ip-ssl",
+  "nodejs-py3_binary_http-ip",
+  "nodejs-py3_binary_http-ip-ssl",
+  "nodejs-py3_compact-accelc_http-ip",
+  "nodejs-py3_compact-accelc_http-ip-ssl",
+  "nodejs-py3_compact_http-ip",
+  "nodejs-py3_compact_http-ip-ssl",
+  "nodejs-py3_json_http-ip",
+  "nodejs-py3_json_http-ip-ssl",
+  "nodejs-py_binary-accel_http-ip",
+  "nodejs-py_binary-accel_http-ip-ssl",
+  "nodejs-py_binary_http-ip",
+  "nodejs-py_binary_http-ip-ssl",
+  "nodejs-py_compact-accelc_http-ip",
+  "nodejs-py_compact-accelc_http-ip-ssl",
+  "nodejs-py_compact_http-ip",
+  "nodejs-py_compact_http-ip-ssl",
+  "nodejs-py_json_http-ip",
+  "nodejs-py_json_http-ip-ssl",
   "perl-rs_multi_buffered-ip",
   "perl-rs_multi_framed-ip",
+  "py-cpp_accel-binary_http-ip",
+  "py-cpp_accel-binary_http-ip-ssl",
+  "py-cpp_accelc-compact_http-ip",
+  "py-cpp_accelc-compact_http-ip-ssl",
+  "py-cpp_binary_http-ip",
+  "py-cpp_binary_http-ip-ssl",
+  "py-cpp_compact_http-ip",
+  "py-cpp_compact_http-ip-ssl",
+  "py-cpp_json_http-ip",
+  "py-cpp_json_http-ip-ssl",
+  "py-d_accel-binary_http-ip",
+  "py-d_accel-binary_http-ip-ssl",
+  "py-d_accelc-compact_http-ip",
+  "py-d_accelc-compact_http-ip-ssl",
+  "py-d_binary_http-ip",
+  "py-d_binary_http-ip-ssl",
+  "py-d_compact_http-ip",
+  "py-d_compact_http-ip-ssl",
+  "py-d_json_http-ip",
+  "py-d_json_http-ip-ssl",
+  "py-dart_accel-binary_http-ip",
+  "py-dart_accelc-compact_http-ip",
+  "py-dart_binary_http-ip",
+  "py-dart_compact_http-ip",
+  "py-dart_json_http-ip",
+  "py-hs_accel-binary_http-ip",
+  "py-hs_accelc-compact_http-ip",
+  "py-hs_binary_http-ip",
+  "py-hs_compact_http-ip",
+  "py-hs_json_http-ip",
+  "py-java_accel-binary_http-ip",
+  "py-java_accel-binary_http-ip-ssl",
+  "py-java_accelc-compact_http-ip",
+  "py-java_accelc-compact_http-ip-ssl",
+  "py-java_binary_http-ip",
+  "py-java_binary_http-ip-ssl",
+  "py-java_compact_http-ip",
+  "py-java_compact_http-ip-ssl",
+  "py-java_json_http-ip",
+  "py-java_json_http-ip-ssl",
+  "py-lua_accel-binary_http-ip",
+  "py-lua_accelc-compact_http-ip",
+  "py-lua_binary_http-ip",
+  "py-lua_compact_http-ip",
+  "py-lua_json_http-ip",
+  "py3-cpp_accel-binary_http-ip",
+  "py3-cpp_accel-binary_http-ip-ssl",
+  "py3-cpp_accelc-compact_http-ip",
+  "py3-cpp_accelc-compact_http-ip-ssl",
+  "py3-cpp_binary_http-ip",
+  "py3-cpp_binary_http-ip-ssl",
+  "py3-cpp_compact_http-ip",
+  "py3-cpp_compact_http-ip-ssl",
+  "py3-cpp_json_http-ip",
+  "py3-cpp_json_http-ip-ssl",
+  "py3-d_accel-binary_http-ip",
+  "py3-d_accel-binary_http-ip-ssl",
+  "py3-d_accelc-compact_http-ip",
+  "py3-d_accelc-compact_http-ip-ssl",
+  "py3-d_binary_http-ip",
+  "py3-d_binary_http-ip-ssl",
+  "py3-d_compact_http-ip",
+  "py3-d_compact_http-ip-ssl",
+  "py3-d_json_http-ip",
+  "py3-d_json_http-ip-ssl",
+  "py3-dart_accel-binary_http-ip",
+  "py3-dart_accelc-compact_http-ip",
+  "py3-dart_binary_http-ip",
+  "py3-dart_compact_http-ip",
+  "py3-dart_json_http-ip",
+  "py3-hs_accel-binary_http-ip",
+  "py3-hs_accelc-compact_http-ip",
+  "py3-hs_binary_http-ip",
+  "py3-hs_compact_http-ip",
+  "py3-hs_json_http-ip",
+  "py3-java_accel-binary_http-ip",
+  "py3-java_accel-binary_http-ip-ssl",
+  "py3-java_accelc-compact_http-ip",
+  "py3-java_accelc-compact_http-ip-ssl",
+  "py3-java_binary_http-ip",
+  "py3-java_binary_http-ip-ssl",
+  "py3-java_compact_http-ip",
+  "py3-java_compact_http-ip-ssl",
+  "py3-java_json_http-ip",
+  "py3-java_json_http-ip-ssl",
+  "py3-lua_accel-binary_http-ip",
+  "py3-lua_accelc-compact_http-ip",
+  "py3-lua_binary_http-ip",
+  "py3-lua_compact_http-ip",
+  "py3-lua_json_http-ip",
   "rb-cpp_json_buffered-domain",
   "rb-cpp_json_buffered-ip",
   "rb-cpp_json_buffered-ip-ssl",
@@ -322,4 +492,4 @@
   "rs-cpp_multic-compact_framed-ip",
   "rs-cpp_multic_buffered-ip",
   "rs-cpp_multic_framed-ip"
-]
+]
\ No newline at end of file
diff --git a/test/py/TestClient.py b/test/py/TestClient.py
index 1ab8e78..edab610 100755
--- a/test/py/TestClient.py
+++ b/test/py/TestClient.py
@@ -32,8 +32,18 @@
 
 class AbstractTest(unittest.TestCase):
     def setUp(self):
-        if options.http_path:
-            self.transport = THttpClient.THttpClient(options.host, port=options.port, path=options.http_path)
+        if options.trans == 'http':
+            uri = '{0}://{1}:{2}{3}'.format(('https' if options.ssl else 'http'),
+                                            options.host,
+                                            options.port,
+                                            (options.http_path if options.http_path else '/'))
+            if options.ssl:
+                __cafile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "CA.pem")
+                __certfile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "client.crt")
+                __keyfile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "client.key")
+                self.transport = THttpClient.THttpClient(uri, cafile=__cafile, cert_file=__certfile, key_file=__keyfile)
+            else:
+                self.transport = THttpClient.THttpClient(uri)
         else:
             if options.ssl:
                 from thrift.transport import TSSLSocket
@@ -325,9 +335,9 @@
                       dest="verbose", const=0,
                       help="minimal output")
     parser.add_option('--protocol', dest="proto", type="string",
-                      help="protocol to use, one of: accel, binary, compact, json")
+                      help="protocol to use, one of: accel, accelc, binary, compact, json")
     parser.add_option('--transport', dest="trans", type="string",
-                      help="transport to use, one of: buffered, framed")
+                      help="transport to use, one of: buffered, framed, http")
     parser.set_defaults(framed=False, http_path=None, verbose=1, host='localhost', port=9090, proto='binary')
     options, args = parser.parse_args()
 
@@ -335,6 +345,9 @@
         sys.path.insert(0, os.path.join(SCRIPT_DIR, options.genpydir))
     sys.path.insert(0, local_libpath())
 
+    if options.http_path:
+        options.trans = 'http'
+
     from ThriftTest import ThriftTest
     from ThriftTest.ttypes import Xtruct, Xtruct2, Numberz, Xception, Xception2
     from thrift.Thrift import TException
diff --git a/test/py/TestServer.py b/test/py/TestServer.py
index 04ad62a..4dc4c07 100755
--- a/test/py/TestServer.py
+++ b/test/py/TestServer.py
@@ -21,6 +21,7 @@
 from __future__ import division
 import logging
 import os
+import signal
 import sys
 import time
 from optparse import OptionParser
@@ -180,11 +181,11 @@
 def main(options):
     # set up the protocol factory form the --protocol option
     prot_factories = {
-        'binary': TBinaryProtocol.TBinaryProtocolFactory,
         'accel': TBinaryProtocol.TBinaryProtocolAcceleratedFactory,
-        'compact': TCompactProtocol.TCompactProtocolFactory,
         'accelc': TCompactProtocol.TCompactProtocolAcceleratedFactory,
-        'json': TJSONProtocol.TJSONProtocolFactory,
+        'binary': TBinaryProtocol.TBinaryProtocolFactory,
+        'compact': TCompactProtocol.TCompactProtocolFactory,
+        'json': TJSONProtocol.TJSONProtocolFactory
     }
     pfactory_cls = prot_factories.get(options.proto, None)
     if pfactory_cls is None:
@@ -201,14 +202,23 @@
     if len(args) > 1:
         raise AssertionError('Only one server type may be specified, not multiple types.')
     server_type = args[0]
+    if options.trans == 'http':
+        server_type = 'THttpServer'
 
     # Set up the handler and processor objects
     handler = TestHandler()
     processor = ThriftTest.Processor(handler)
 
+    global server
+
     # Handle THttpServer as a special case
     if server_type == 'THttpServer':
-        server = THttpServer.THttpServer(processor, ('', options.port), pfactory)
+        if options.ssl:
+            __certfile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "server.crt")
+            __keyfile = os.path.join(os.path.dirname(SCRIPT_DIR), "keys", "server.key")
+            server = THttpServer.THttpServer(processor, ('', options.port), pfactory, cert_file=__certfile, key_file=__keyfile)
+        else:
+            server = THttpServer.THttpServer(processor, ('', options.port), pfactory)
         server.serve()
         sys.exit(0)
 
@@ -268,7 +278,15 @@
     server.serve()
 
 
+def exit_gracefully(signum, frame):
+    print("SIGINT received\n")
+    server.shutdown()   # doesn't work properly, yet
+    sys.exit(0)
+
+
 if __name__ == '__main__':
+    signal.signal(signal.SIGINT, exit_gracefully)
+
     parser = OptionParser()
     parser.add_option('--libpydir', type='string', dest='libpydir',
                       help='include this directory to sys.path for locating library code')
@@ -288,12 +306,12 @@
                       dest="verbose", const=0,
                       help="minimal output")
     parser.add_option('--protocol', dest="proto", type="string",
-                      help="protocol to use, one of: accel, binary, compact, json")
+                      help="protocol to use, one of: accel, accelc, binary, compact, json")
     parser.add_option('--transport', dest="trans", type="string",
-                      help="transport to use, one of: buffered, framed")
+                      help="transport to use, one of: buffered, framed, http")
     parser.add_option('--container-limit', dest='container_limit', type='int', default=None)
     parser.add_option('--string-limit', dest='string_limit', type='int', default=None)
-    parser.set_defaults(port=9090, verbose=1, proto='binary')
+    parser.set_defaults(port=9090, verbose=1, proto='binary', transport='buffered')
     options, args = parser.parse_args()
 
     # Print TServer log to stdout so that the test-runner can redirect it to log files
diff --git a/test/test.py b/test/test.py
index 24e7c4e..f59256a 100755
--- a/test/test.py
+++ b/test/test.py
@@ -52,7 +52,7 @@
 CONFIG_FILE = 'tests.json'
 
 
-def run_cross_tests(server_match, client_match, jobs, skip_known_failures, retry_count, regex):
+def run_cross_tests(server_match, client_match, jobs, skip_known_failures, only_known_failures, retry_count, regex):
     logger = multiprocessing.get_logger()
     logger.debug('Collecting tests')
     with open(path_join(TEST_DIR, CONFIG_FILE), 'r') as fp:
@@ -63,6 +63,10 @@
         print('  servers: %s' % server_match, file=sys.stderr)
         print('  clients: %s' % client_match, file=sys.stderr)
         return False
+    if only_known_failures:
+        logger.debug('Only running known failures')
+        known = crossrunner.load_known_failures(TEST_DIR)
+        tests = list(filter(lambda t: crossrunner.test_name(**t) in known, tests))
     if skip_known_failures:
         logger.debug('Skipping known failures')
         known = crossrunner.load_known_failures(TEST_DIR)
@@ -81,7 +85,7 @@
         return False
 
 
-def run_feature_tests(server_match, feature_match, jobs, skip_known_failures, retry_count, regex):
+def run_feature_tests(server_match, feature_match, jobs, skip_known_failures, only_known_failures, retry_count, regex):
     basedir = path_join(ROOT_DIR, FEATURE_DIR_RELATIVE)
     logger = multiprocessing.get_logger()
     logger.debug('Collecting tests')
@@ -95,6 +99,10 @@
         print('  servers: %s' % server_match, file=sys.stderr)
         print('  features: %s' % feature_match, file=sys.stderr)
         return False
+    if only_known_failures:
+        logger.debug('Only running known failures')
+        known = crossrunner.load_known_failures(basedir)
+        tests = list(filter(lambda t: crossrunner.test_name(**t) in known, tests))
     if skip_known_failures:
         logger.debug('Skipping known failures')
         known = crossrunner.load_known_failures(basedir)
@@ -130,6 +138,8 @@
     parser.add_argument('-F', '--features', nargs='*', default=None,
                         help='run server feature tests instead of cross language tests')
     parser.add_argument('-R', '--regex', help='test name pattern to run')
+    parser.add_argument('-o', '--only-known_failures', action='store_true', dest='only_known_failures',
+                        help='only execute tests that are known to fail')
     parser.add_argument('-s', '--skip-known-failures', action='store_true', dest='skip_known_failures',
                         help='do not execute tests that are known to fail')
     parser.add_argument('-r', '--retry-count', type=int,
@@ -169,10 +179,12 @@
     elif options.features is not None:
         features = options.features or ['.*']
         res = run_feature_tests(server_match, features, options.jobs,
-                                options.skip_known_failures, options.retry_count, options.regex)
+                                options.skip_known_failures, options.only_known_failures,
+                                options.retry_count, options.regex)
     else:
         res = run_cross_tests(server_match, client_match, options.jobs,
-                              options.skip_known_failures, options.retry_count, options.regex)
+                              options.skip_known_failures, options.only_known_failures,
+                              options.retry_count, options.regex)
     return 0 if res else 1
 
 
diff --git a/test/tests.json b/test/tests.json
index 4641f22..ed38fea 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -64,7 +64,7 @@
     "server": {
       "command": [
         "thrift_test_server",
-	"--trace"
+        "--trace"
       ]
     },
     "client": {
@@ -254,7 +254,8 @@
     },
     "transports": [
       "buffered",
-      "framed"
+      "framed",
+      "http"
     ],
     "sockets": [
       "ip",
@@ -292,11 +293,12 @@
     },
     "transports": [
       "buffered",
-      "framed"
+      "framed",
+      "http"
     ],
     "sockets": [
-      "ip-ssl",
-      "ip"
+      "ip",
+      "ip-ssl"
     ],
     "protocols": [
       "compact",