package httpx import ( "context" "net/http" "strings" ) // secureKey carries the per-request "served over https" decision on the request // context. The door resolves it ONCE (RequestIsHTTPS) and every cookie writer // reads it back, so the session cookie's `Secure` flag, the `__Host-` prefix, // and HSTS all agree on one value (D-043). type secureKey struct{} // WithSecure records whether the request is served over https (D-043). The door // sets it before dispatching; a request that never passed through the door has // no value and is treated as plain http. func WithSecure(ctx context.Context, secure bool) context.Context { return context.WithValue(ctx, secureKey{}, secure) } // SecureFromRequest reports whether the request is served over https, as // resolved by the door (D-043). The zero value is false — plain http — which is // what keeps the loopback bootstrap (the SSH tunnel) working. func SecureFromRequest(r *http.Request) bool { v, _ := r.Context().Value(secureKey{}).(bool) return v } // RequestIsHTTPS reports whether r reached the app over https (D-043): either // the connection itself is TLS, or a TRUSTED proxy terminated TLS and said so // via `X-Forwarded-Proto`. A header from an untrusted peer is ignored, so a // client cannot spoof `Secure` cookies onto a plain-http connection. The // leftmost token is the original client-facing scheme. func RequestIsHTTPS(r *http.Request, trusted TrustedProxies) bool { if r.TLS != nil { return true } if !trusted.Contains(remoteIP(r.RemoteAddr)) { return false } proto := r.Header.Get("X-Forwarded-Proto") if i := strings.IndexByte(proto, ','); i >= 0 { proto = proto[:i] } return strings.EqualFold(strings.TrimSpace(proto), "https") }