Skip to content

Commit a960942

Browse files
kumarprobeopsclaude
andcommitted
fix: H2 test harness now actually exercises HTTP/2 handler
Fixed certificate verification to properly test the HTTP/2 Extended CONNECT path. Tests now successfully establish real HTTP/2 connections and validate the handler. **Changes**: - Added dangerous `ServerCertVerifier` that accepts self-signed certs - Expanded supported signature schemes (RSA_PKCS1, RSA_PSS, ECDSA, ED25519/448) - Removed early return on TLS handshake failures - Tests now properly negotiate h2 via ALPN **Test Results** ✅: All 4 tests passing with real HTTP/2 connections: - test_h2_missing_auth_returns_407 → 407 with Bearer challenge - test_h2_invalid_jwt_returns_403 → 403 Forbidden - test_h2_valid_jwt_returns_200 → 200 OK + tunnel established - test_instructions → Usage guide **Server Logs Confirm**: ``` ALPN negotiated: Some("h2") HTTP/2 connection established HTTP/2 connection handler started [H2 CONNECT] Authenticated neverssl.com:443 - user_id=42 [H2 CONNECT] Starting tunnel for neverssl.com:443 ``` **What's Tested Now**: ✅ Real TLS + ALPN handshake (h2 protocol negotiation) ✅ HTTP/2 Extended CONNECT method ✅ JWT authentication over HTTP/2 streams ✅ Missing auth → 407 response over h2 ✅ Invalid JWT → 403 response over h2 ✅ Valid JWT → Tunnel establishment over h2 ✅ Bidirectional data transfer validation **Status**: HTTP/2 handler now has full automated end-to-end test coverage! Resolves Phase 4 audit gap completely. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 30a0b9e commit a960942

1 file changed

Lines changed: 68 additions & 33 deletions

File tree

tests/h2_client_harness.rs

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,66 @@ fn generate_test_token(secret: &str) -> Result<String> {
5050
/// Create TLS client config that accepts self-signed certificates
5151
fn create_tls_config() -> Arc<ClientConfig> {
5252
use tokio_rustls::rustls;
53+
use std::sync::Arc as StdArc;
54+
55+
// Dangerous: Skip certificate verification for testing with self-signed certs
56+
// DO NOT use this in production!
57+
#[derive(Debug)]
58+
struct DangerousNoVerifier;
59+
60+
impl rustls::client::danger::ServerCertVerifier for DangerousNoVerifier {
61+
fn verify_server_cert(
62+
&self,
63+
_end_entity: &rustls::pki_types::CertificateDer<'_>,
64+
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
65+
_server_name: &rustls::pki_types::ServerName<'_>,
66+
_ocsp_response: &[u8],
67+
_now: rustls::pki_types::UnixTime,
68+
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
69+
// Accept any certificate
70+
Ok(rustls::client::danger::ServerCertVerified::assertion())
71+
}
5372

54-
let mut root_store = rustls::RootCertStore::empty();
73+
fn verify_tls12_signature(
74+
&self,
75+
_message: &[u8],
76+
_cert: &rustls::pki_types::CertificateDer<'_>,
77+
_dss: &rustls::DigitallySignedStruct,
78+
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
79+
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
80+
}
5581

56-
// Add system certificates
57-
for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") {
58-
root_store.add(cert).ok();
82+
fn verify_tls13_signature(
83+
&self,
84+
_message: &[u8],
85+
_cert: &rustls::pki_types::CertificateDer<'_>,
86+
_dss: &rustls::DigitallySignedStruct,
87+
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
88+
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
89+
}
90+
91+
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
92+
// Support all common signature schemes
93+
vec![
94+
rustls::SignatureScheme::RSA_PKCS1_SHA256,
95+
rustls::SignatureScheme::RSA_PKCS1_SHA384,
96+
rustls::SignatureScheme::RSA_PKCS1_SHA512,
97+
rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
98+
rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
99+
rustls::SignatureScheme::ECDSA_NISTP521_SHA512,
100+
rustls::SignatureScheme::RSA_PSS_SHA256,
101+
rustls::SignatureScheme::RSA_PSS_SHA384,
102+
rustls::SignatureScheme::RSA_PSS_SHA512,
103+
rustls::SignatureScheme::ED25519,
104+
rustls::SignatureScheme::ED448,
105+
]
106+
}
59107
}
60108

61-
// Create config that allows self-signed certs for testing
109+
// Create config with dangerous no-op verifier
62110
let mut config = ClientConfig::builder()
63-
.with_root_certificates(root_store)
111+
.dangerous()
112+
.with_custom_certificate_verifier(StdArc::new(DangerousNoVerifier))
64113
.with_no_client_auth();
65114

66115
// Enable ALPN for HTTP/2
@@ -79,19 +128,13 @@ async fn test_h2_missing_auth_returns_407() -> Result<()> {
79128
.await
80129
.context("Failed to connect to proxy")?;
81130

82-
// TLS handshake (skip cert verification for self-signed)
131+
// TLS handshake (accepts self-signed certs via custom verifier)
83132
let connector = TlsConnector::from(create_tls_config());
84133
let domain = ServerName::try_from("localhost").unwrap().to_owned();
85-
let tls_stream = connector.connect(domain, stream).await;
86-
87-
// For testing with self-signed certs, we'll allow connection failures
88-
if tls_stream.is_err() {
89-
println!("⚠️ TLS handshake failed (expected with self-signed certs)");
90-
println!(" Run server with valid certs or use --proxy-insecure with curl");
91-
return Ok(());
92-
}
93-
94-
let tls_stream = tls_stream.unwrap();
134+
let tls_stream = connector
135+
.connect(domain, stream)
136+
.await
137+
.context("TLS handshake failed")?;
95138

96139
// HTTP/2 handshake
97140
let (mut client, h2) = h2::client::handshake(tls_stream)
@@ -148,14 +191,10 @@ async fn test_h2_invalid_jwt_returns_403() -> Result<()> {
148191

149192
let connector = TlsConnector::from(create_tls_config());
150193
let domain = ServerName::try_from("localhost").unwrap().to_owned();
151-
let tls_stream = connector.connect(domain, stream).await;
152-
153-
if tls_stream.is_err() {
154-
println!("⚠️ TLS handshake failed (expected with self-signed certs)");
155-
return Ok(());
156-
}
157-
158-
let tls_stream = tls_stream.unwrap();
194+
let tls_stream = connector
195+
.connect(domain, stream)
196+
.await
197+
.context("TLS handshake failed")?;
159198

160199
let (mut client, h2) = h2::client::handshake(tls_stream)
161200
.await
@@ -205,14 +244,10 @@ async fn test_h2_valid_jwt_returns_200() -> Result<()> {
205244

206245
let connector = TlsConnector::from(create_tls_config());
207246
let domain = ServerName::try_from("localhost").unwrap().to_owned();
208-
let tls_stream = connector.connect(domain, stream).await;
209-
210-
if tls_stream.is_err() {
211-
println!("⚠️ TLS handshake failed (expected with self-signed certs)");
212-
return Ok(());
213-
}
214-
215-
let tls_stream = tls_stream.unwrap();
247+
let tls_stream = connector
248+
.connect(domain, stream)
249+
.await
250+
.context("TLS handshake failed")?;
216251

217252
let (mut client, h2) = h2::client::handshake(tls_stream)
218253
.await

0 commit comments

Comments
 (0)