add support for using sni to enabled/disable 0-rtt (#9741)

diff --git a/doc/admin-guide/files/sni.yaml.en.rst b/doc/admin-guide/files/sni.yaml.en.rst
index 3bae962..b5b7a5b 100644
--- a/doc/admin-guide/files/sni.yaml.en.rst
+++ b/doc/admin-guide/files/sni.yaml.en.rst
@@ -218,6 +218,11 @@
 
                                     ATS negotiates application protocol with the client on behalf of the origin server.
                                     This only works with ``partial_blind_route``.
+
+server_max_early_data     Inbound   Specifies the maximum amount of early data in bytes that is permitted to be sent on a single connection.
+
+                                    If not specified, the value of :ts:cv:`proxy.config.ssl.server.max_early_data` is used.
+
 ========================= ========= ========================================================================================
 
 Pre-warming TLS Tunnel
diff --git a/iocore/net/P_SNIActionPerformer.h b/iocore/net/P_SNIActionPerformer.h
index bc8e9cb..8375a5b 100644
--- a/iocore/net/P_SNIActionPerformer.h
+++ b/iocore/net/P_SNIActionPerformer.h
@@ -411,3 +411,29 @@
 private:
   std::string_view policy{};
 };
+
+class ServerMaxEarlyData : public ActionItem
+{
+public:
+  ServerMaxEarlyData(uint32_t value) : server_max_early_data(value) {}
+  ~ServerMaxEarlyData() override {}
+
+  int
+  SNIAction(TLSSNISupport *snis, const Context &ctx) const override
+  {
+#if TS_HAS_TLS_EARLY_DATA
+    auto ssl_vc = dynamic_cast<SSLNetVConnection *>(snis);
+    if (ssl_vc) {
+      ssl_vc->hints_from_sni.server_max_early_data = server_max_early_data;
+      const uint32_t EARLY_DATA_DEFAULT_SIZE       = 16384;
+      const uint32_t server_recv_max_early_data =
+        server_max_early_data > 0 ? std::max(server_max_early_data, EARLY_DATA_DEFAULT_SIZE) : 0;
+      ssl_vc->update_early_data_config(server_max_early_data, server_recv_max_early_data);
+    }
+#endif
+    return SSL_TLSEXT_ERR_OK;
+  }
+
+private:
+  uint32_t server_max_early_data = 0;
+};
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index e2f1dcc..cb41b9a 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -141,6 +141,7 @@
   void net_read_io(NetHandler *nh, EThread *lthread) override;
   int64_t load_buffer_and_write(int64_t towrite, MIOBufferAccessor &buf, int64_t &total_written, int &needs) override;
   void do_io_close(int lerrno = -1) override;
+  void update_early_data_config(uint32_t max_early_data, uint32_t recv_max_early_data);
 
   ////////////////////////////////////////////////////////////
   // Instances of NetVConnection should be allocated        //
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index bcb6b49..6153243 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -173,42 +173,7 @@
       SSL_set_bio(ssl, rbio, wbio);
 
 #if TS_HAS_TLS_EARLY_DATA
-      // Must disable OpenSSL's internal anti-replay if external cache is used with
-      // 0-rtt, otherwise session reuse will be broken. The freshness check described
-      // in https://tools.ietf.org/html/rfc8446#section-8.3 is still performed. But we
-      // still need to implement something to try to prevent replay atacks.
-      //
-      // We are now also disabling this when using OpenSSL's internal cache, since we
-      // are calling "ssl_accept" non-blocking, it seems to be confusing the anti-replay
-      // mechanism and causing session resumption to fail.
-      SSLConfig::scoped_config params;
-      if (SSL_version(ssl) >= TLS1_3_VERSION && params->server_max_early_data > 0) {
-#ifdef HAVE_SSL_SET_MAX_EARLY_DATA
-        bool ret1 = false;
-        bool ret2 = false;
-        if ((ret1 = SSL_set_max_early_data(ssl, params->server_max_early_data)) == 1) {
-          Debug("ssl_early_data", "SSL_set_max_early_data: success");
-        } else {
-          Debug("ssl_early_data", "SSL_set_max_early_data: failed");
-        }
-
-        if ((ret2 = SSL_set_recv_max_early_data(ssl, params->server_recv_max_early_data)) == 1) {
-          Debug("ssl_early_data", "SSL_set_recv_max_early_data: success");
-        } else {
-          Debug("ssl_early_data", "SSL_set_recv_max_early_data: failed");
-        }
-
-        if (ret1 && ret2) {
-          Debug("ssl_early_data", "Must disable anti-replay if 0-rtt is enabled.");
-          SSL_set_options(ssl, SSL_OP_NO_ANTI_REPLAY);
-        }
-#else
-        // If SSL_set_max_early_data is unavailable, it's probably BoringSSL,
-        // and SSL_set_early_data_enabled should be available.
-        SSL_set_early_data_enabled(ssl, 1);
-        Warning("max_early_data is not used due to library limitations");
-#endif
-      }
+      update_early_data_config(SSLConfigParams::server_max_early_data, SSLConfigParams::server_recv_max_early_data);
 #endif
     }
     this->_bindSSLObject();
@@ -2125,7 +2090,7 @@
   int ssl_error = SSL_ERROR_NONE;
 
 #if TS_HAS_TLS_EARLY_DATA
-  if (SSLConfigParams::server_max_early_data > 0 && !this->_early_data_finish) {
+  if (!this->_early_data_finish) {
 #if HAVE_SSL_READ_EARLY_DATA
     size_t nread = 0;
 #else
@@ -2375,7 +2340,10 @@
       return SSL_ERROR_NONE;
     }
 
-    if (SSLConfigParams::server_max_early_data > 0 && !this->_early_data_finish) {
+    bool early_data_enabled = this->hints_from_sni.server_max_early_data.has_value() ?
+                                this->hints_from_sni.server_max_early_data.value() > 0 :
+                                SSLConfigParams::server_max_early_data > 0;
+    if (early_data_enabled && !this->_early_data_finish) {
       bool had_error_on_reading_early_data = false;
       bool finished_reading_early_data     = false;
       Debug("ssl_early_data", "More early data to read.");
@@ -2435,7 +2403,6 @@
           Debug("ssl_early_data", "SSL_READ_EARLY_DATA_SUCCESS: size = %" PRId64, nread);
         }
       }
-
       return ssl_error;
     }
   }
@@ -2489,3 +2456,45 @@
   }
   SSL_set_max_proto_version(this->ssl, ver);
 }
+
+void
+SSLNetVConnection::update_early_data_config(uint32_t max_early_data, uint32_t recv_max_early_data)
+{
+#if TS_HAS_TLS_EARLY_DATA
+  // Must disable OpenSSL's internal anti-replay if external cache is used with
+  // 0-rtt, otherwise session reuse will be broken. The freshness check described
+  // in https://tools.ietf.org/html/rfc8446#section-8.3 is still performed. But we
+  // still need to implement something to try to prevent replay atacks.
+  //
+  // We are now also disabling this when using OpenSSL's internal cache, since we
+  // are calling "ssl_accept" non-blocking, it seems to be confusing the anti-replay
+  // mechanism and causing session resumption to fail.
+  if (SSL_version(ssl) >= TLS1_3_VERSION) {
+#ifdef HAVE_SSL_SET_MAX_EARLY_DATA
+    bool ret1 = false;
+    bool ret2 = false;
+    if ((ret1 = SSL_set_max_early_data(ssl, max_early_data)) == 1) {
+      Debug("ssl_early_data", "SSL_set_max_early_data %u: success", max_early_data);
+    } else {
+      Debug("ssl_early_data", "SSL_set_max_early_data %u: failed", max_early_data);
+    }
+
+    if ((ret2 = SSL_set_recv_max_early_data(ssl, recv_max_early_data)) == 1) {
+      Debug("ssl_early_data", "SSL_set_recv_max_early_data %u: success", recv_max_early_data);
+    } else {
+      Debug("ssl_early_data", "SSL_set_recv_max_early_data %u: failed", recv_max_early_data);
+    }
+
+    if (ret1 && ret2) {
+      Debug("ssl_early_data", "Must disable anti-replay if 0-rtt is enabled.");
+      SSL_set_options(ssl, SSL_OP_NO_ANTI_REPLAY);
+    }
+#else
+    // If SSL_set_max_early_data is unavailable, it's probably BoringSSL,
+    // and SSL_set_early_data_enabled should be available.
+    SSL_set_early_data_enabled(ssl, max_early_data > 0 ? 1 : 0);
+    Warning("max_early_data is not used due to library limitations");
+#endif
+  }
+#endif
+}
diff --git a/iocore/net/SSLSNIConfig.cc b/iocore/net/SSLSNIConfig.cc
index 5bc378d..4d35767 100644
--- a/iocore/net/SSLSNIConfig.cc
+++ b/iocore/net/SSLSNIConfig.cc
@@ -144,6 +144,8 @@
       ai->actions.push_back(std::make_unique<HTTP2BufferWaterMark>(item.http2_buffer_water_mark.value()));
     }
 
+    ai->actions.push_back(std::make_unique<ServerMaxEarlyData>(item.server_max_early_data));
+
     ai->actions.push_back(std::make_unique<SNI_IpAllow>(item.ip_allow, item.fqdn));
 
     // set the next hop properties
diff --git a/iocore/net/TLSSNISupport.h b/iocore/net/TLSSNISupport.h
index 15812b2..7276fd9 100644
--- a/iocore/net/TLSSNISupport.h
+++ b/iocore/net/TLSSNISupport.h
@@ -54,6 +54,7 @@
 
   struct HintsFromSNI {
     std::optional<uint32_t> http2_buffer_water_mark;
+    std::optional<uint32_t> server_max_early_data;
   } hints_from_sni;
 
 protected:
diff --git a/iocore/net/YamlSNIConfig.cc b/iocore/net/YamlSNIConfig.cc
index 209223b..3ae5c4f 100644
--- a/iocore/net/YamlSNIConfig.cc
+++ b/iocore/net/YamlSNIConfig.cc
@@ -157,7 +157,8 @@
                                                TS_valid_tls_version_min_in,
                                                TS_valid_tls_version_max_in,
 #endif
-                                               TS_host_sni_policy};
+                                               TS_host_sni_policy,
+                                               TS_server_max_early_data};
 
 namespace YAML
 {
@@ -357,6 +358,13 @@
     if (node[TS_valid_tls_version_max_in]) {
       item.valid_tls_version_max_in = TLS_PROTOCOLS_DESCRIPTOR.get(node[TS_valid_tls_version_max_in].as<std::string>());
     }
+
+    if (node[TS_server_max_early_data]) {
+      item.server_max_early_data = node[TS_server_max_early_data].as<uint32_t>();
+    } else {
+      item.server_max_early_data = SSLConfigParams::server_max_early_data;
+    }
+
     return true;
   }
 };
diff --git a/iocore/net/YamlSNIConfig.h b/iocore/net/YamlSNIConfig.h
index fba88a5..755b82e 100644
--- a/iocore/net/YamlSNIConfig.h
+++ b/iocore/net/YamlSNIConfig.h
@@ -58,6 +58,7 @@
 TSDECL(http2);
 TSDECL(http2_buffer_water_mark);
 TSDECL(host_sni_policy);
+TSDECL(server_max_early_data);
 #undef TSDECL
 
 struct YamlSNIConfig {
@@ -89,6 +90,7 @@
     int valid_tls_version_max_in = -1;
     std::vector<int> tunnel_alpn{};
     std::optional<int> http2_buffer_water_mark;
+    uint32_t server_max_early_data = 0;
 
     bool tunnel_prewarm_srv                  = false;
     uint32_t tunnel_prewarm_min              = 0;
diff --git a/tests/gold_tests/tls/test-0rtt-s_client.py b/tests/gold_tests/tls/test-0rtt-s_client.py
index b0033bd..797181b 100644
--- a/tests/gold_tests/tls/test-0rtt-s_client.py
+++ b/tests/gold_tests/tls/test-0rtt-s_client.py
@@ -23,19 +23,30 @@
 import os
 import shlex
 import h2_early_decode
+import argparse
 
 
 def main():
-    ats_port = sys.argv[1]
-    http_ver = sys.argv[2]
-    test = sys.argv[3]
-    sess_file_path = os.path.join(sys.argv[4], 'sess.dat')
-    early_data_file_path = os.path.join(sys.argv[4], 'early_{0}_{1}.txt'.format(http_ver, test))
+    parser = argparse.ArgumentParser(description='Process some args.')
+    parser.add_argument('-p', '--ats-port', type=int, dest='ats_port', required=True, help='ATS port number')
+    parser.add_argument('-v', '--http-version', type=str, dest='http_ver', choices=['h1', 'h2'], required=True, help='HTTP version')
+    parser.add_argument('-t', '--test-name', type=str, dest='test_name', required=True, help='Name of the test to run')
+    parser.add_argument('-r', '--run-dir', type=str, dest='run_dir', required=True, help='Path to the autest run directory')
+    parser.add_argument('-s', '--server-name', type=str, dest='sni', required=False, help='Server Name')
+    args = parser.parse_args()
+
+    sess_file_path = os.path.join(args.run_dir, 'sess.dat')
+    early_data_file_path = os.path.join(args.run_dir, 'early_{0}_{1}.txt'.format(args.http_ver, args.test_name))
+
+    if args.sni != '':
+        sni_str = '-servername {0}'.format(args.sni)
+    else:
+        sni_str = ''
 
     s_client_cmd_1 = shlex.split(
-        'openssl s_client -connect 127.0.0.1:{0} -tls1_3 -quiet -sess_out {1}'.format(ats_port, sess_file_path))
+        f'openssl s_client -connect 127.0.0.1:{args.ats_port} -tls1_3 -quiet -sess_out {sess_file_path} {sni_str}')
     s_client_cmd_2 = shlex.split(
-        'openssl s_client -connect 127.0.0.1:{0} -tls1_3 -quiet -sess_in {1} -early_data {2}'.format(ats_port, sess_file_path, early_data_file_path))
+        f'openssl s_client -connect 127.0.0.1:{args.ats_port} -tls1_3 -quiet -sess_in {sess_file_path} -early_data {early_data_file_path} {sni_str}')
 
     create_sess_proc = subprocess.Popen(s_client_cmd_1, env=os.environ.copy(
     ), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -53,7 +64,7 @@
         reuse_sess_proc.kill()
         output = reuse_sess_proc.communicate()[0]
 
-    if http_ver == 'h2':
+    if args.http_ver == 'h2':
         lines = output.split(bytes('\n', 'utf-8'))
         data = b''
         for line in lines:
diff --git a/tests/gold_tests/tls/tls_0rtt_server.test.py b/tests/gold_tests/tls/tls_0rtt_server.test.py
index 0dfc116..69a7f75 100644
--- a/tests/gold_tests/tls/tls_0rtt_server.test.py
+++ b/tests/gold_tests/tls/tls_0rtt_server.test.py
@@ -27,7 +27,8 @@
     Condition.HasOpenSSLVersion('1.1.1'),
 )
 
-ts = Test.MakeATSProcess('ts', enable_tls=True)
+ts1 = Test.MakeATSProcess('ts1', enable_tls=True)
+ts2 = Test.MakeATSProcess('ts2', enable_tls=True)
 server = Test.MakeOriginServer('server')
 
 request_header1 = {
@@ -97,25 +98,25 @@
 server.addResponse('sessionlog.json', request_header5, response_header5)
 server.addResponse('sessionlog.json', request_header6, response_header6)
 
-ts.addSSLfile('ssl/server.pem')
-ts.addSSLfile('ssl/server.key')
+ts1.addSSLfile('ssl/server.pem')
+ts1.addSSLfile('ssl/server.key')
 
-ts.Setup.Copy('test-0rtt-s_client.py')
-ts.Setup.Copy('h2_early_decode.py')
-ts.Setup.Copy('early_h1_get.txt')
-ts.Setup.Copy('early_h1_post.txt')
-ts.Setup.Copy('early_h2_get.txt')
-ts.Setup.Copy('early_h2_post.txt')
-ts.Setup.Copy('early_h2_multi1.txt')
-ts.Setup.Copy('early_h2_multi2.txt')
+ts1.Setup.Copy('test-0rtt-s_client.py')
+ts1.Setup.Copy('h2_early_decode.py')
+ts1.Setup.Copy('early_h1_get.txt')
+ts1.Setup.Copy('early_h1_post.txt')
+ts1.Setup.Copy('early_h2_get.txt')
+ts1.Setup.Copy('early_h2_post.txt')
+ts1.Setup.Copy('early_h2_multi1.txt')
+ts1.Setup.Copy('early_h2_multi2.txt')
 
-ts.Disk.records_config.update({
+ts1.Disk.records_config.update({
     'proxy.config.diags.debug.enabled': 1,
-    'proxy.config.diags.debug.tags': 'http',
+    'proxy.config.diags.debug.tags': 'http|ssl_early_data|ssl',
     'proxy.config.exec_thread.autoconfig.enabled': 0,
     'proxy.config.exec_thread.limit': 8,
-    '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.server.cert.path': '{0}'.format(ts1.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': '{0}'.format(ts1.Variables.SSLDir),
     'proxy.config.ssl.session_cache.value': 2,
     'proxy.config.ssl.session_cache.size': 512000,
     'proxy.config.ssl.session_cache.timeout': 7200,
@@ -126,72 +127,149 @@
     'proxy.config.ssl.server.cipher_suite': 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'
 })
 
-ts.Disk.ssl_multicert_config.AddLine(
+ts1.Disk.ssl_multicert_config.AddLine(
     'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
 )
 
-ts.Disk.remap_config.AddLine(
+ts1.Disk.remap_config.AddLine(
     'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
 )
 
+ts1.Disk.sni_yaml.AddLines([
+    'sni:',
+    '- fqdn: example-no.com',
+    '  server_max_early_data: 0'
+])
+
+ts2.Disk.records_config.update({
+    'proxy.config.diags.debug.enabled': 1,
+    'proxy.config.diags.debug.tags': 'http|ssl_early_data|ssl',
+    'proxy.config.exec_thread.autoconfig.enabled': 0,
+    'proxy.config.exec_thread.limit': 8,
+    'proxy.config.ssl.server.cert.path': '{0}'.format(ts1.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': '{0}'.format(ts1.Variables.SSLDir),
+    'proxy.config.ssl.session_cache.value': 2,
+    'proxy.config.ssl.session_cache.size': 512000,
+    'proxy.config.ssl.session_cache.timeout': 7200,
+    'proxy.config.ssl.session_cache.num_buckets': 32768,
+    'proxy.config.ssl.server.session_ticket.enable': 1,
+    'proxy.config.ssl.server.max_early_data': 0,
+    'proxy.config.ssl.server.allow_early_data_params': 0,
+    'proxy.config.ssl.server.cipher_suite': 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'
+})
+
+ts2.Disk.ssl_multicert_config.AddLine(
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+ts2.Disk.remap_config.AddLine(
+    'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+ts2.Disk.sni_yaml.AddLines([
+    'sni:',
+    '- fqdn: example-yes.com',
+    '  server_max_early_data: 16384'
+])
+
 tr = Test.AddTestRun('Basic Curl Test')
-tr.Processes.Default.Command = 'curl https://127.0.0.1:{0} -k'.format(ts.Variables.ssl_port)
+tr.Processes.Default.Command = 'curl -k --resolve example.com:{0}:127.0.0.1 https://example.com:{0}'.format(ts1.Variables.ssl_port)
 tr.Processes.Default.ReturnCode = 0
 tr.Processes.Default.StartBefore(server)
-tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.Processes.Default.StartBefore(ts1)
 tr.Processes.Default.Streams.All = Testers.ContainsExpression('curl test', 'Making sure the basics still work')
 tr.Processes.Default.Streams.All += Testers.ExcludesExpression('early data accepted', '')
 tr.StillRunningAfter = server
-tr.StillRunningAfter += ts
+tr.StillRunningAfter += ts1
 
 tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/1.1 GET)')
-tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py {ts.Variables.ssl_port} h1 get {Test.RunDirectory}'
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts1.Variables.ssl_port} -v h1 -t get -r {Test.RunDirectory}'
 tr.Processes.Default.ReturnCode = 0
 tr.Processes.Default.Streams.All = Testers.ContainsExpression('early data accepted', '')
 tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
 tr.StillRunningAfter = server
-tr.StillRunningAfter += ts
+tr.StillRunningAfter += ts1
 
 tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/1.1 POST)')
-tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py {ts.Variables.ssl_port} h1 post {Test.RunDirectory}'
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts1.Variables.ssl_port} -v h1 -t post -r {Test.RunDirectory}'
 tr.Processes.Default.ReturnCode = 0
 tr.Processes.Default.Streams.All = Testers.ContainsExpression('HTTP/1.1 425 Too Early', '')
 tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
 tr.Processes.Default.Streams.All += Testers.ExcludesExpression('early data accepted', '')
 tr.StillRunningAfter = server
-tr.StillRunningAfter += ts
+tr.StillRunningAfter += ts1
 
 tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/2 GET)')
-tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py {ts.Variables.ssl_port} h2 get {Test.RunDirectory}'
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts1.Variables.ssl_port} -v h2 -t get -r {Test.RunDirectory}'
 tr.Processes.Default.ReturnCode = 0
 tr.Processes.Default.Streams.All = Testers.ContainsExpression('early data accepted', '')
 tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
 tr.StillRunningAfter = server
-tr.StillRunningAfter += ts
+tr.StillRunningAfter += ts1
 
 tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/2 POST)')
-tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py {ts.Variables.ssl_port} h2 post {Test.RunDirectory}'
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts1.Variables.ssl_port} -v h2 -t post -r {Test.RunDirectory}'
 tr.Processes.Default.ReturnCode = 0
 tr.Processes.Default.Streams.All = Testers.ContainsExpression(':status 425', 'Only safe methods are allowed')
 tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
 tr.Processes.Default.Streams.All += Testers.ExcludesExpression('early data accepted', '')
 tr.StillRunningAfter = server
-tr.StillRunningAfter += ts
+tr.StillRunningAfter += ts1
 
 tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/2 Multiplex)')
-tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py {ts.Variables.ssl_port} h2 multi1 {Test.RunDirectory}'
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts1.Variables.ssl_port} -v h2 -t multi1 -r {Test.RunDirectory}'
 tr.Processes.Default.ReturnCode = 0
 tr.Processes.Default.Streams.All = Testers.ContainsExpression('early data accepted multi_1', '')
 tr.Processes.Default.Streams.All += Testers.ContainsExpression('early data accepted multi_2', '')
 tr.Processes.Default.Streams.All += Testers.ContainsExpression('early data accepted multi_3', '')
 tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
 tr.StillRunningAfter = server
-tr.StillRunningAfter += ts
+tr.StillRunningAfter += ts1
 
 tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/2 Multiplex with POST)')
-tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py {ts.Variables.ssl_port} h2 multi2 {Test.RunDirectory}'
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts1.Variables.ssl_port} -v h2 -t multi2 -r {Test.RunDirectory}'
 tr.Processes.Default.ReturnCode = 0
 tr.Processes.Default.Streams.All = Testers.ContainsExpression('early data accepted multi_1', '')
 tr.Processes.Default.Streams.All += Testers.ContainsExpression(':status 425', 'Only safe methods are allowed')
 tr.Processes.Default.Streams.All += Testers.ContainsExpression('early data accepted multi_3', '')
 tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
+tr.StillRunningAfter = server
+tr.StillRunningAfter += ts1
+
+tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/1.1 GET) SNI Provided')
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts1.Variables.ssl_port} -v h1 -t get -r {Test.RunDirectory} -s example.com'
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.All = Testers.ContainsExpression('early data accepted', '')
+tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
+tr.StillRunningAfter = server
+tr.StillRunningAfter += ts1
+
+tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/1.1 GET) Disabled By SNI Config')
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts1.Variables.ssl_port} -v h1 -t get -r {Test.RunDirectory} -s example-no.com'
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.All = Testers.ExcludesExpression('early data accepted', '')
+tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
+tr.StillRunningAfter = server
+
+tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/1.1 GET) Disabled In General')
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts2.Variables.ssl_port} -v h1 -t get -r {Test.RunDirectory}'
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.StartBefore(ts2)
+tr.Processes.Default.Streams.All = Testers.ExcludesExpression('early data accepted', '')
+tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
+tr.StillRunningAfter = server
+tr.StillRunningAfter += ts2
+
+tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/1.1 GET) Disabled In General SNI Provided')
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts2.Variables.ssl_port} -v h1 -t get -r {Test.RunDirectory} -s example.com'
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.All = Testers.ExcludesExpression('early data accepted', '')
+tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')
+tr.StillRunningAfter = server
+tr.StillRunningAfter += ts2
+
+tr = Test.AddTestRun('TLSv1.3 0-RTT Support (HTTP/1.1 GET) Enabled By SNI Config')
+tr.Processes.Default.Command = f'{sys.executable} test-0rtt-s_client.py -p {ts2.Variables.ssl_port} -v h1 -t get -r {Test.RunDirectory} -s example-yes.com'
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.All = Testers.ContainsExpression('early data accepted', '')
+tr.Processes.Default.Streams.All += Testers.ExcludesExpression('curl test', '')