diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9e07c21f..fdbca225 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -11,10 +11,12 @@ env:
jobs:
test:
- name: Test ${{ matrix.rust }}
+ name: Test ${{ matrix.os }}-${{ matrix.rust }}
#needs: [style]
strategy:
matrix:
+
+ os: [ubuntu-latest]
rust:
- stable
- beta
@@ -26,8 +28,11 @@ jobs:
include:
- rust: nightly
benches: true
+ # Add testing for the nightly toolchain on windows to test debugger_visualizer support.
+ - rust: nightly
+ os: windows-latest
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}
steps:
- name: Checkout
@@ -52,6 +57,17 @@ jobs:
command: test
args: --benches ${{ matrix.features }}
+ # The #[debugger_visualizer] attribute is currently gated behind an unstable feature flag.
+ # In order to test the visualizers for the regex crate, they have to be tested on a nightly build.
+ - name: Run tests with debugger_visualizer feature
+ if: |
+ matrix.os == 'windows-latest' &&
+ matrix.rust == 'nightly'
+ uses: actions-rs/cargo@v1
+ with:
+ command: test
+ args: --test debugger_visualizer --features 'debugger_visualizer' -- --test-threads=1
+
wasm:
name: WASM
#needs: [style]
diff --git a/Cargo.toml b/Cargo.toml
index 6fe4b13c..94af6681 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,6 +37,13 @@ serde = "1.0"
serde_json = "1.0"
doc-comment = "0.3"
criterion = "0.3.2"
+debugger_test = "0.1.0"
+debugger_test_parser = "0.1.0"
+
+[features]
+# UNSTABLE FEATURES (requires Rust nightly)
+# Enable to use the #[debugger_visualizer] attribute.
+debugger_visualizer = []
[[bench]]
name = "header_map"
@@ -62,3 +69,13 @@ path = "benches/method.rs"
[[bench]]
name = "uri"
path = "benches/uri.rs"
+
+[[test]]
+name = "debugger_visualizer"
+path = "tests/debugger_visualizer.rs"
+# Do not run these tests by default. These tests need to
+# be run with the additional rustc flag `--test-threads=1`
+# since each test causes a debugger to attach to the current
+# test process. If multiple debuggers try to attach at the same
+# time, the test will fail.
+test = false
diff --git a/debug_metadata/http.natvis b/debug_metadata/http.natvis
new file mode 100644
index 00000000..341bd8b3
--- /dev/null
+++ b/debug_metadata/http.natvis
@@ -0,0 +1,106 @@
+
+
+
+ {bytes.ptr,[bytes.len]sb}
+
+
+
+ {{ key={key}, value={value} }}
+
+ - hash
+ - key
+ - value
+ - links
+
+
+
+
+
+ entries
+ - extra_values
+
+
+
+
+
+ {inner.variant0.__0,en}
+ {inner.variant1.__0.__0.bytes.ptr,[inner.variant1.__0.__0.bytes.len]s8}
+
+
+
+ {(char*)inner.ptr,[inner.len]}
+
+ - is_sensitive
+
+
+
+
+ {__0}
+
+
+
+
+ inner
+
+
+
+
+ {{ method={method}, uri={uri} }}
+
+ - method
+ - uri
+ - version
+ - headers
+ - extensions
+
+
+
+
+ {{ status={status} }}
+
+ - status
+ - version
+ - headers
+ - extensions
+
+
+
+
+ {__0,d}
+
+
+
+ {data.bytes.ptr,[data.bytes.len]sb}
+
+
+
+
+ /
+ {data.bytes.ptr,[data.bytes.len]sb}
+
+
+
+
+ {inner.variant0,en}
+ {inner.variant1.__0,en}
+ {inner.variant2.__0,en}
+
+
+
+
+
+ {authority}{path_and_query}
+ http://{authority}{path_and_query}
+ https://{authority}{path_and_query}
+ {scheme}://{authority}{path_and_query}
+
+ - scheme
+ - authority
+ - path_and_query
+
+
+
+
+ {__0,en}
+
+
diff --git a/src/lib.rs b/src/lib.rs
index 42118296..61d6f416 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -158,6 +158,12 @@
//! assert_eq!(uri.query(), None);
//! ```
+#![cfg_attr(feature = "debugger_visualizer", feature(debugger_visualizer))]
+#![cfg_attr(
+ feature = "debugger_visualizer",
+ debugger_visualizer(natvis_file = "../debug_metadata/http.natvis")
+)]
+
#![deny(warnings, missing_docs, missing_debug_implementations)]
#[cfg(test)]
diff --git a/tests/debugger_visualizer.rs b/tests/debugger_visualizer.rs
new file mode 100644
index 00000000..9d44879a
--- /dev/null
+++ b/tests/debugger_visualizer.rs
@@ -0,0 +1,119 @@
+use debugger_test::debugger_test;
+use http::uri::Scheme;
+use http::{Request, Response, StatusCode, Uri};
+
+#[inline(never)]
+fn __break() {}
+
+#[debugger_test(
+ debugger = "cdb",
+ commands = r#"
+.nvlist
+
+dx request
+dx request.head
+dx request.head.uri
+dx request.head.headers
+
+dx response
+dx response.head
+dx response.head.headers
+
+dx uri
+"#,
+ expected_statements = r#"
+pattern:.*\.exe \(embedded NatVis .*debugger_visualizer-0\.natvis
+
+request [Type: http::request::Request]
+ [+0x000] head : { method=Get, uri=https://www.rust-lang.org/ } [Type: http::request::Parts]
+ [+0x0e0] body : "HELLLLOOOOO WOOOOOORLLLLDDD!" [Type: str]
+
+request.head : { method=Get, uri=https://www.rust-lang.org/ } [Type: http::request::Parts]
+ [] [Type: http::request::Parts]
+ [method] : Get [Type: http::method::Method]
+ [uri] : https://www.rust-lang.org/ [Type: http::uri::Uri]
+ [version] : Http11 [Type: http::version::Version]
+ [headers] : { len=0x3 } [Type: http::header::map::HeaderMap]
+ [extensions] [Type: http::extensions::Extensions]
+
+request.head.uri : https://www.rust-lang.org/ [Type: http::uri::Uri]
+ [] [Type: http::uri::Uri]
+ [scheme] : Https [Type: http::uri::scheme::Scheme]
+ [authority] : www.rust-lang.org [Type: http::uri::authority::Authority]
+ [path_and_query] : / [Type: http::uri::path::PathAndQuery]
+
+request.head.headers : { len=0x3 } [Type: http::header::map::HeaderMap]
+ [] [Type: http::header::map::HeaderMap]
+ [extra_values] : { len=0x0 } [Type: alloc::vec::Vec,alloc::alloc::Global>]
+ [len] : 0x3 [Type: unsigned __int64]
+ [capacity] : 0x6 [Type: unsigned __int64]
+ [1] : { key=UserAgent, value="my-awesome-agent/1.0" } [Type: http::header::map::Bucket]
+ [2] : { key=ContentLanguage, value="en_US" } [Type: http::header::map::Bucket]
+
+response [Type: http::response::Response]
+ [+0x000] head : { status=404 } [Type: http::response::Parts]
+ [+0x070] body : "HELLLLOOOOO WOOOOOORLLLLDDD!" [Type: str]
+
+response.head : { status=404 } [Type: http::response::Parts]
+ [] [Type: http::response::Parts]
+ [status] : 404 [Type: http::status::StatusCode]
+ [version] : Http11 [Type: http::version::Version]
+ [headers] : { len=0x0 } [Type: http::header::map::HeaderMap]
+ [extensions] [Type: http::extensions::Extensions]
+
+response.head.headers : { len=0x0 } [Type: http::header::map::HeaderMap]
+ [] [Type: http::header::map::HeaderMap]
+ [extra_values] : { len=0x0 } [Type: alloc::vec::Vec,alloc::alloc::Global>]
+ [len] : 0x0 [Type: unsigned __int64]
+ [capacity] : 0x0 [Type: unsigned __int64]
+
+uri : https://www.rust-lang.org/index.html [Type: http::uri::Uri]
+ [] [Type: http::uri::Uri]
+ [scheme] : Https [Type: http::uri::scheme::Scheme]
+ [authority] : www.rust-lang.org [Type: http::uri::authority::Authority]
+ [path_and_query] : /index.html [Type: http::uri::path::PathAndQuery]
+"#
+)]
+fn test_debugger_visualizer() {
+ let request = Request::builder()
+ .uri("https://www.rust-lang.org/")
+ .header(http::header::AGE, 0)
+ .header(http::header::USER_AGENT, "my-awesome-agent/1.0")
+ .header(http::header::CONTENT_LANGUAGE, "en_US")
+ .body("HELLLLOOOOO WOOOOOORLLLLDDD!")
+ .unwrap();
+
+ assert!(request.headers().contains_key(http::header::USER_AGENT));
+ assert_eq!(
+ "www.rust-lang.org",
+ request.uri().authority().unwrap().host()
+ );
+
+ let response = send(request).expect("http response is success");
+ assert!(!response.status().is_success());
+
+ let uri = "https://www.rust-lang.org/index.html"
+ .parse::()
+ .unwrap();
+ assert_eq!(uri.scheme(), Some(&Scheme::HTTPS));
+ assert_eq!(uri.host(), Some("www.rust-lang.org"));
+ assert_eq!(uri.path(), "/index.html");
+ assert_eq!(uri.query(), None);
+ __break();
+}
+
+fn send(req: Request<&str>) -> http::Result> {
+ if req.uri() != "/awesome-url" {
+ let result = Response::builder()
+ .status(StatusCode::NOT_FOUND)
+ .body(req.body().clone());
+
+ return result;
+ }
+
+ let body = req.body().clone();
+
+ let response = Response::builder().status(StatusCode::OK).body(body);
+
+ response
+}