This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.0.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit f54789ba7625b9ef991dc85645c0de0ec559a7a0 Author: Fei Deng <[email protected]> AuthorDate: Wed Jun 26 11:45:56 2024 -0400 add test to check H2 grace shutdown works as intended (#11046) (cherry picked from commit 36bbb0ee900d152637daac35044e24b6c4308c21) --- tests/gold_tests/h2/http2_close_connection.test.py | 68 ++++++++++++++ tests/gold_tests/h2/http2_close_connection.yaml | 100 +++++++++++++++++++++ tests/tools/plugins/CMakeLists.txt | 1 + tests/tools/plugins/http2_close_connection.cc | 92 +++++++++++++++++++ 4 files changed, 261 insertions(+) diff --git a/tests/gold_tests/h2/http2_close_connection.test.py b/tests/gold_tests/h2/http2_close_connection.test.py new file mode 100644 index 0000000000..fd69360a32 --- /dev/null +++ b/tests/gold_tests/h2/http2_close_connection.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 = ''' +Check whether the HTTP/2 grace shutdown works as intended. +''' + +Test.SkipUnless(Condition.HasProxyVerifierVersion('2.8.0')) + +pv_server = Test.MakeVerifierServerProcess("pv_server", "http2_close_connection.yaml") + +ts = Test.MakeATSProcess('ts', select_ports=True, enable_tls=True) + +ts.addDefaultSSLFiles() +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.http.server_ports": f"{ts.Variables.port} {ts.Variables.ssl_port}:ssl", + 'proxy.config.ssl.server.cert.path': f'{ts.Variables.SSLDir}', + 'proxy.config.ssl.server.private_key.path': f'{ts.Variables.SSLDir}', + 'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE', + 'proxy.config.diags.debug.enabled': 3, + 'proxy.config.diags.debug.tags': 'http', + 'proxy.config.exec_thread.autoconfig.enabled': 0, + 'proxy.config.exec_thread.limit': 4, + 'proxy.config.ssl.client.alpn_protocols': 'h2,http/1.1', + 'proxy.config.http.server_session_sharing.pool': 'thread', + 'proxy.config.http.server_session_sharing.match': 'ip,sni,cert', + }) + +ts.Disk.remap_config.AddLines([f'map / https://127.0.0.1:{pv_server.Variables.https_port}']) + +Test.PrepareTestPlugin(os.path.join(Test.Variables.AtsTestPluginsDir, 'http2_close_connection.so'), ts) + +tr = Test.AddTestRun() +tr.Processes.Default.StartBefore(pv_server) +tr.Processes.Default.StartBefore(ts) +tr.AddVerifierClientProcess( + "pv_client", + "http2_close_connection.yaml", + http_ports=[ts.Variables.port], + https_ports=[ts.Variables.ssl_port], + other_args='--thread-limit 1') +tr.Processes.Default.ReturnCode = 0 + +tr.Processes.Default.Streams.All += Testers.ContainsExpression( + 'Equals Success: Key: "1", Content Data: "body", Value: "server_test_1"', 'Response check') +tr.Processes.Default.Streams.All += Testers.ContainsExpression( + 'Received GOAWAY frame with last stream id 2147483647, error code 0', 'initial GOAWAY frame with last stream id set to max') +tr.Processes.Default.Streams.All += Testers.ContainsExpression( + 'Received GOAWAY frame with last stream id 1, error code 0', 'updated GOAWAY frame with last stream id set to 1') + +pv_server.Streams.All += Testers.ExcludesExpression('server_test_2', 'Only one response should be sent') diff --git a/tests/gold_tests/h2/http2_close_connection.yaml b/tests/gold_tests/h2/http2_close_connection.yaml new file mode 100644 index 0000000000..5e2d7e6b5a --- /dev/null +++ b/tests/gold_tests/h2/http2_close_connection.yaml @@ -0,0 +1,100 @@ +# 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. + +# +# This replay file assumes that caching is enabled and +# proxy.config.http.cache.ignore_client_cc_max_age is set to 0 so that we can +# test max-age in the client requests. +# + + +meta: + version: '1.0' +sessions: +- protocol: + - name: http + version: 2 + - name: tls + sni: test_sni + - name: tcp + - name: ip + version: 4 + transactions: + - client-request: + frames: + - HEADERS: + headers: + fields: + - [:method, GET] + - [:scheme, https] + - [:authority, example.data.com] + - [:path, /a/path] + - [Content-Type, text/html] + - [uuid, 1] + + server-response: + frames: + - HEADERS: + headers: + fields: + - [:status, 200] + - [Content-Type, text/html] + - [Content-Length, '13'] + - DATA: + delay: 4s + content: + encoding: plain + data: server_test_1 + size: 13 + + proxy-response: + content: + encoding: plain + data: server_test_1 + verify: {as: equal} + + - client-request: + delay: 4s + frames: + - HEADERS: + headers: + fields: + - [:method, GET] + - [:scheme, https] + - [:authority, example.data.com] + - [:path, /b/path] + - [Content-Type, text/html] + - [uuid, 2] + + server-response: + frames: + - HEADERS: + headers: + fields: + - [:status, 200] + - [Content-Type, text/html] + - [Content-Length, '13'] + - DATA: + content: + encoding: plain + data: server_test_2 + size: 13 + + proxy-response: + content: + encoding: plain + data: server_test_2 + verify: {as: equal} diff --git a/tests/tools/plugins/CMakeLists.txt b/tests/tools/plugins/CMakeLists.txt index 437965bc54..05a4d3d6e1 100644 --- a/tests/tools/plugins/CMakeLists.txt +++ b/tests/tools/plugins/CMakeLists.txt @@ -37,6 +37,7 @@ add_autest_plugin(user_args user_args.cc) add_autest_plugin(async_engine async_engine.c) add_autest_plugin(hook_tunnel_plugin hook_tunnel_plugin.cc) add_autest_plugin(tunnel_transform tunnel_transform.cc) +add_autest_plugin(http2_close_connection http2_close_connection.cc) target_link_libraries(continuations_verify PRIVATE OpenSSL::SSL) target_link_libraries(ssl_client_verify_test PRIVATE OpenSSL::SSL) diff --git a/tests/tools/plugins/http2_close_connection.cc b/tests/tools/plugins/http2_close_connection.cc new file mode 100644 index 0000000000..2faef81f9f --- /dev/null +++ b/tests/tools/plugins/http2_close_connection.cc @@ -0,0 +1,92 @@ +/** @file + + Test adding continuation from same hook point + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +#include <ts/ts.h> +#include <string> +#include <cstring> + +#define PLUGIN_NAME "http2_close_connection" + +static DbgCtl dbg_ctl_tag{PLUGIN_NAME}; + +const char *FIELD_CONNECTION = "Connection"; +const char *VALUE_CLOSE = "close"; + +const int LEN_CONNECTION = 10; +const int LEN_CLOSE = 5; + +static int +txn_handler(TSCont /* contp */, TSEvent event, void *edata) +{ + Dbg(dbg_ctl_tag, "txn_handler event: %d", event); + + TSHttpTxn txnp = static_cast<TSHttpTxn>(edata); + + TSMBuffer resp_bufp = nullptr; + TSMLoc resp_hdr_loc = nullptr; + if (TSHttpTxnClientRespGet(txnp, &resp_bufp, &resp_hdr_loc) != TS_SUCCESS) { + Dbg(dbg_ctl_tag, "TSHttpTxnClientRespGet failed"); + } else { + Dbg(dbg_ctl_tag, "TSHttpTxnClientRespGet success"); + TSMLoc field_loc = TSMimeHdrFieldFind(resp_bufp, resp_hdr_loc, FIELD_CONNECTION, LEN_CONNECTION); + if (field_loc) { + Dbg(dbg_ctl_tag, "Found header %s", FIELD_CONNECTION); + TSMimeHdrFieldValueStringSet(resp_bufp, resp_hdr_loc, field_loc, 0, VALUE_CLOSE, LEN_CLOSE); + Dbg(dbg_ctl_tag, "Setting header %s:%s", FIELD_CONNECTION, VALUE_CLOSE); + } else { + Dbg(dbg_ctl_tag, "Header %s not found", FIELD_CONNECTION); + if (TSMimeHdrFieldCreate(resp_bufp, resp_hdr_loc, &field_loc) == TS_SUCCESS) { + TSMimeHdrFieldNameSet(resp_bufp, resp_hdr_loc, field_loc, FIELD_CONNECTION, LEN_CONNECTION); + TSMimeHdrFieldAppend(resp_bufp, resp_hdr_loc, field_loc); + TSMimeHdrFieldValueStringInsert(resp_bufp, resp_hdr_loc, field_loc, 0, VALUE_CLOSE, LEN_CLOSE); + Dbg(dbg_ctl_tag, "Adding header %s:%s", FIELD_CONNECTION, VALUE_CLOSE); + } else { + Dbg(dbg_ctl_tag, "TSMimeHdrFieldCreate failed"); + } + } + TSHandleMLocRelease(resp_bufp, resp_hdr_loc, field_loc); + } + TSHandleMLocRelease(resp_bufp, TS_NULL_MLOC, resp_hdr_loc); + resp_bufp = nullptr; + + TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); + return TS_EVENT_NONE; +} + +void +TSPluginInit(int argc, const char **argv) +{ + TSPluginRegistrationInfo info; + + info.plugin_name = const_cast<char *>(PLUGIN_NAME); + info.support_email = const_cast<char *>("[email protected]"); + info.vendor_name = const_cast<char *>("Yahoo"); + + if (TSPluginRegister(&info) != TS_SUCCESS) { + TSError("[" PLUGIN_NAME "] plugin registration failed\n"); + return; + } + + Dbg(dbg_ctl_tag, "plugin registered"); + TSCont txn_cont = TSContCreate(txn_handler, nullptr); + TSHttpHookAdd(TS_HTTP_SEND_RESPONSE_HDR_HOOK, txn_cont); +}
