diff --git a/plugins/experimental/traffic_dump/session_data.cc b/plugins/experimental/traffic_dump/session_data.cc index dac9eaeae85..4884bfe3d7c 100644 --- a/plugins/experimental/traffic_dump/session_data.cc +++ b/plugins/experimental/traffic_dump/session_data.cc @@ -213,7 +213,8 @@ SessionData::init(std::string_view log_directory, int64_t max_disk_usage, int64_ } std::string -SessionData::get_protocol_stack_helper(const get_protocol_stack_f &get_protocol_stack, const get_tls_description_f &get_tls_node) +SessionData::get_protocol_stack_helper(const get_protocol_stack_f &get_protocol_stack, const get_tls_description_f &get_tls_node, + const handle_http_version_f &handle_http_version) { std::ostringstream protocol_description; protocol_description << R"("protocol":[)"; @@ -244,7 +245,7 @@ SessionData::get_protocol_stack_helper(const get_protocol_stack_f &get_protocol_ // See whether an HTTP version is provided. If so, record it. auto const it = http_tag_to_version.find(std::string(protocol_string)); if (it != http_tag_to_version.end()) { - this->http_version_in_client_stack = it->second; + handle_http_version(it->second); } } } @@ -259,7 +260,8 @@ SessionData::get_client_protocol_description(TSHttpSsn client_ssnp) [&client_ssnp](int n, const char **result, int *actual) { return TSHttpSsnClientProtocolStackGet(client_ssnp, n, result, actual); }, - [&client_ssnp]() { return get_client_tls_description(client_ssnp); }); + [&client_ssnp]() { return get_client_tls_description(client_ssnp); }, + [this](std::string_view http_version) { this->http_version_in_client_stack = http_version; }); } std::string @@ -269,7 +271,7 @@ SessionData::get_server_protocol_description(TSHttpTxn server_txnp) [&server_txnp](int n, const char **result, int *actual) { return TSHttpTxnServerProtocolStackGet(server_txnp, n, result, actual); }, - [&server_txnp]() { return get_server_tls_description(server_txnp); }); + [&server_txnp]() { return get_server_tls_description(server_txnp); }, [](std::string_view http_version) {}); } SessionData::SessionData() diff --git a/plugins/experimental/traffic_dump/session_data.h b/plugins/experimental/traffic_dump/session_data.h index 68c865039c9..dbd075dab76 100644 --- a/plugins/experimental/traffic_dump/session_data.h +++ b/plugins/experimental/traffic_dump/session_data.h @@ -192,6 +192,7 @@ class SessionData using get_protocol_stack_f = std::function; using get_tls_description_f = std::function; + using handle_http_version_f = std::function; /** Create the protocol stack for a session. * @@ -201,10 +202,16 @@ class SessionData * @param[in] get_protocol_stack The function to use to populate a protocol * stack. * + * @param[in] get_tls_node The function to use to populate the tls node. + * + * @param[in] handle_http_version A function that performs arbitrary logic + * given the HTTP/2 protocol version. + * * @return The description of the protocol stack and True if the stack * contained an HTTP description, false otherwise. */ - std::string get_protocol_stack_helper(const get_protocol_stack_f &get_protocol_stack, const get_tls_description_f &get_tls_node); + std::string get_protocol_stack_helper(const get_protocol_stack_f &get_protocol_stack, const get_tls_description_f &get_tls_node, + const handle_http_version_f &handle_http_version); /** Get the JSON string that describes the client session stack. * diff --git a/tests/gold_tests/pluginTest/traffic_dump/traffic_dump.test.py b/tests/gold_tests/pluginTest/traffic_dump/traffic_dump.test.py index 9e1aa12c274..5051a993ea7 100644 --- a/tests/gold_tests/pluginTest/traffic_dump/traffic_dump.test.py +++ b/tests/gold_tests/pluginTest/traffic_dump/traffic_dump.test.py @@ -80,7 +80,11 @@ "Host: www.tls.com\r\nContent-Length: 0\r\n\r\n", "timestamp": "1469733493.993", "body": ""} server.addResponse("sessionfile.log", request_header, response_200) -request_header = {"headers": "GET /h2 HTTP/1.1\r\n" +request_header = {"headers": "GET /h2_first HTTP/2\r\n" + "Host: www.tls.com\r\nContent-Length: 0\r\n\r\n", + "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionfile.log", request_header, response_200) +request_header = {"headers": "GET /h2_second HTTP/2\r\n" "Host: www.tls.com\r\nContent-Length: 0\r\n\r\n", "timestamp": "1469733493.993", "body": ""} server.addResponse("sessionfile.log", request_header, response_200) @@ -409,10 +413,12 @@ # # Test 7: Verify correct protcol dumping of TLS and HTTP/2 connections. # -tr = Test.AddTestRun("Conduct an HTTP/2 transaction over a TLS connection.") +tr = Test.AddTestRun("Conduct two HTTP/2 transactions over a TLS connection.") tr.Processes.Default.Command = \ ('curl --http2 -k -H"Host: www.tls.com" --resolve "www.tls.com:{0}:127.0.0.1" ' - '--cert ./signed-foo.pem --key ./signed-foo.key --verbose https://www.tls.com:{0}/h2'.format( + '--cert ./signed-foo.pem --key ./signed-foo.key --verbose https://www.tls.com:{0}/h2_first ' + '--next --http2 -k -H"Host: www.tls.com" --resolve "www.tls.com:{0}:127.0.0.1" ' + '--cert ./signed-foo.pem --key ./signed-foo.key --verbose https://www.tls.com:{0}/h2_second'.format( ts.Variables.ssl_port)) tr.Processes.Default.ReturnCode = 0 diff --git a/tests/gold_tests/pluginTest/traffic_dump/verify_replay.py b/tests/gold_tests/pluginTest/traffic_dump/verify_replay.py index 7a4dbe18dcb..6f5113cc9fd 100644 --- a/tests/gold_tests/pluginTest/traffic_dump/verify_replay.py +++ b/tests/gold_tests/pluginTest/traffic_dump/verify_replay.py @@ -79,6 +79,26 @@ def verify_there_was_a_transaction(replay_json): return True +def verify_http_information(replay_json): + """ + Verify various expected HTTP information. + """ + for session in replay_json['sessions']: + top_layer = session['protocol'][0] + is_http2 = top_layer['name'] == 'http' and top_layer['version'] == '2' + if is_http2: + # Every HTTP/2 client request should have a stream-id node. + for transaction in session['transactions']: + client_request = transaction['client-request'] + try: + client_request['http2']['stream-id'] + except KeyError: + print("An HTTP/2 transaction did not have a stream-id node.") + return False + + return True + + def verify_request_target(replay_json, request_target): """ Verify that the 'url' element of the first transaction contains the request target. @@ -318,6 +338,9 @@ def main(): if not verify_there_was_a_transaction(replay_json): return 1 + if not verify_http_information(replay_json): + return 1 + if args.request_target and not verify_request_target(replay_json, args.request_target): return 1