mirror of
https://github.com/fosrl/newt.git
synced 2026-05-12 19:29:54 +00:00
fix(http): populate Request.TLS for private HTTPS via httpConnCtx
net/http only sets Request.TLS for *tls.Conn or conns implementing ConnectionState(). Our listener wrapped tls.Server in httpConnCtx with an embedded net.Conn, so TLS was never surfaced and r.TLS stayed nil. That triggered the HTTP→HTTPS permanent redirect on every request for HTTPS rules. Add ConnectionState() on httpConnCtx delegating to the underlying TLS conn. Add tests for TLS forwarding and plain TCP.
This commit is contained in:
@@ -139,6 +139,21 @@ type httpConnCtx struct {
|
||||
rule *SubnetRule
|
||||
}
|
||||
|
||||
// ConnectionState allows net/http.Server to populate Request.TLS when the
|
||||
// underlying connection is TLS (e.g. *tls.Conn from tls.Server). Without this,
|
||||
// the connection is not *tls.Conn and does not expose ConnectionState through
|
||||
// the net.Conn interface field, so tlsState stays nil and the HTTPS redirect
|
||||
// in handleRequest runs on every request.
|
||||
func (c *httpConnCtx) ConnectionState() tls.ConnectionState {
|
||||
type tlsConn interface {
|
||||
ConnectionState() tls.ConnectionState
|
||||
}
|
||||
if tc, ok := c.Conn.(tlsConn); ok {
|
||||
return tc.ConnectionState()
|
||||
}
|
||||
return tls.ConnectionState{}
|
||||
}
|
||||
|
||||
// connCtxKey is the unexported context key used to store a *SubnetRule on the
|
||||
// per-connection context created by http.Server.ConnContext.
|
||||
type connCtxKey struct{}
|
||||
|
||||
48
netstack2/http_handler_tls_test.go
Normal file
48
netstack2/http_handler_tls_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package netstack2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// tlsConnStub is a minimal net.Conn that also exposes TLS state, matching
|
||||
// *tls.Conn's ConnectionState used by net/http.Server.
|
||||
type tlsConnStub struct {
|
||||
net.Conn
|
||||
state tls.ConnectionState
|
||||
}
|
||||
|
||||
func (t *tlsConnStub) ConnectionState() tls.ConnectionState {
|
||||
return t.state
|
||||
}
|
||||
|
||||
func TestHTTPConnCtxForwardsConnectionState(t *testing.T) {
|
||||
c1, c2 := net.Pipe()
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
inner := &tlsConnStub{
|
||||
Conn: c1,
|
||||
state: tls.ConnectionState{Version: tls.VersionTLS12, HandshakeComplete: true},
|
||||
}
|
||||
wrapped := &httpConnCtx{Conn: inner, rule: nil}
|
||||
|
||||
got := wrapped.ConnectionState()
|
||||
if got.Version != tls.VersionTLS12 || !got.HandshakeComplete {
|
||||
t.Fatalf("ConnectionState = %+v, want TLS 1.2 and HandshakeComplete", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPConnCtxConnectionStatePlainTCP(t *testing.T) {
|
||||
c1, c2 := net.Pipe()
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
wrapped := &httpConnCtx{Conn: c1, rule: nil}
|
||||
got := wrapped.ConnectionState()
|
||||
if got.Version != 0 {
|
||||
t.Fatalf("expected zero ConnectionState for plain conn, got %+v", got)
|
||||
}
|
||||
_ = c2
|
||||
}
|
||||
Reference in New Issue
Block a user