tools/http3_test: support testing for custom HTTP headers

introduce two environment variables for customizing "headers" test case
`EXTRA_HEADERS` for adding additional request headers in JSON format.
`EXPECT_REQ_HEADERS` for request header validation in JSON format.
Headers with null value represents non-existence in the forwarded request headers.
diff --git a/tools/http3_test/README.md b/tools/http3_test/README.md
index 45ba9d2..0ea5726 100644
--- a/tools/http3_test/README.md
+++ b/tools/http3_test/README.md
@@ -37,6 +37,11 @@
 * IDLE_TIMEOUT     - client's idle timeout in integer milliseconds.
                      Default value is 60000 (60 seconds).
 
+* EXTRA_HEADERS    - additional request headers in JSON format.
+                     Currently used by `headers` test only.
+
+* EXPECT_REQ_HEADERS - expected request header/value pairs in JSON format.
+                       Currently used by `headers` test only.
 
 For example, to test a non-default server, use the HTTPBIN_ENDPOINT environment
 variable
diff --git a/tools/http3_test/tests/httpbin_tests.rs b/tools/http3_test/tests/httpbin_tests.rs
index 74cda26..cea89e1 100644
--- a/tools/http3_test/tests/httpbin_tests.rs
+++ b/tools/http3_test/tests/httpbin_tests.rs
@@ -94,6 +94,29 @@
         }
     }
 
+    fn extra_headers() -> Option<serde_json::Map<String, serde_json::Value>> {
+        if let Some(val) = std::env::var_os("EXTRA_HEADERS") {
+            let json_string = val.into_string().unwrap();
+            let parsed: serde_json::Value =
+                serde_json::from_str(&json_string).unwrap();
+            return Some(parsed.as_object().unwrap().clone());
+        }
+
+        return None;
+    }
+
+    fn expect_req_headers() -> Option<serde_json::Map<String, serde_json::Value>>
+    {
+        if let Some(val) = std::env::var_os("EXPECT_REQ_HEADERS") {
+            let json_string = val.into_string().unwrap();
+            let parsed: serde_json::Value =
+                serde_json::from_str(&json_string).unwrap();
+            return Some(parsed.as_object().unwrap().clone());
+        }
+
+        return None;
+    }
+
     // A rudimentary structure to hold httpbin response data
     #[derive(Debug, serde::Deserialize)]
     struct HttpBinResponseBody {
@@ -230,14 +253,44 @@
 
     #[test]
     fn headers() {
-        let reqs = request_check_status("headers", 200);
+        let mut reqs = Vec::new();
+        let expect_hdrs = Some(vec![Header::new(":status", "200")]);
+
+        let mut url = endpoint(Some("headers"));
+        url.set_query(Some("show_env=1")); // reveal X-Forwarded-* headers
+        reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone()));
+
+        if let Some(headers) = &extra_headers() {
+            for (name, val) in headers {
+                reqs[0].hdrs.push(Header::new(&name, val.as_str().unwrap()));
+            }
+        };
 
         let assert = |reqs: &[Http3Req]| {
             assert_headers!(reqs[0]);
 
             let json = jsonify(&reqs[0].resp_body);
-            if let Some(args) = json.args {
-                assert_eq!(args["Host"], reqs[0].url.host_str().unwrap());
+            assert_ne!(json.headers, None);
+            if let Some(headers) = json.headers {
+                if let Some(expected_headers) = &expect_req_headers() {
+                    for (name, val) in expected_headers {
+                        if let Some(expected_value) = val.as_str() {
+                            assert_eq!(
+                                headers.get(name),
+                                Some(&String::from(expected_value)),
+                                "Header '{}' doesn't match",
+                                name
+                            );
+                        } else {
+                            assert_eq!(
+                                headers.get(name),
+                                None,
+                                "Header '{}' exists",
+                                name
+                            );
+                        }
+                    }
+                }
             }
         };