fix(masque): default enableDatagrams to true on HTTP/3 listener and fix CONNECT-UDP error handling
- listener/http3: default enableDatagrams to true when not explicitly set, so CONNECT-UDP (RFC 9298) works out of the box without requiring ?enableDatagrams=true in the config URL - handler/masque: dial target before sending 200 OK in handleConnectUDP, matching the handleConnectTCP order; if target dial fails, the handler can still return an error status instead of having already committed 200 - handler/masque: add missing WriteHeader(BadRequest) when ResolveUDPAddr fails, so the client gets a proper HTTP error instead of an opaque H3_REQUEST_CANCELLED stream reset Fixes #103
This commit is contained in:
+38
-37
@@ -355,6 +355,7 @@ func (h *masqueHandler) handleConnectUDP(ctx context.Context, w http.ResponseWri
|
||||
// Resolve target address
|
||||
raddr, err := net.ResolveUDPAddr("udp", targetAddr)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
log.Error("masque: failed to resolve target address: ", err)
|
||||
return err
|
||||
}
|
||||
@@ -370,43 +371,6 @@ func (h *masqueHandler) handleConnectUDP(ctx context.Context, w http.ResponseWri
|
||||
// Get the underlying HTTP/3 stream
|
||||
stream := streamer.HTTPStream()
|
||||
|
||||
// Send success response with capsule-protocol header
|
||||
w.Header().Set("Capsule-Protocol", "?1")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// Flush the response headers
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
// Create datagram connection wrapping the HTTP/3 stream (client side)
|
||||
datagramConn := masque_util.NewDatagramConn(stream, laddr, raddr)
|
||||
defer datagramConn.Close()
|
||||
|
||||
// Wrap with recorder stats and traffic limiter
|
||||
var clientPC net.PacketConn = datagramConn
|
||||
clientPC = stats_wrapper.WrapPacketConn(clientPC, pStats)
|
||||
clientPC = traffic_wrapper.WrapPacketConn(
|
||||
clientPC,
|
||||
h.limiter,
|
||||
clientID,
|
||||
limiter.ServiceOption(h.options.Service),
|
||||
limiter.ScopeOption(limiter.ScopeClient),
|
||||
limiter.NetworkOption("udp"),
|
||||
limiter.AddrOption(targetAddr),
|
||||
limiter.ClientOption(clientID),
|
||||
limiter.SrcOption(ro.RemoteAddr),
|
||||
)
|
||||
|
||||
// Track per-client connection stats
|
||||
if h.options.Observer != nil {
|
||||
pstats := h.stats.Stats(clientID)
|
||||
pstats.Add(stats.KindTotalConns, 1)
|
||||
pstats.Add(stats.KindCurrentConns, 1)
|
||||
defer pstats.Add(stats.KindCurrentConns, -1)
|
||||
clientPC = stats_wrapper.WrapPacketConn(clientPC, pstats)
|
||||
}
|
||||
|
||||
// Get target connection - either through router/chain or direct
|
||||
var targetPC net.PacketConn
|
||||
|
||||
@@ -460,6 +424,43 @@ func (h *masqueHandler) handleConnectUDP(ctx context.Context, w http.ResponseWri
|
||||
// Wrap target with metrics
|
||||
targetPC = metrics.WrapPacketConn(h.options.Service, targetPC)
|
||||
|
||||
// Send success response with capsule-protocol header
|
||||
w.Header().Set("Capsule-Protocol", "?1")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// Flush the response headers
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
// Create datagram connection wrapping the HTTP/3 stream (client side)
|
||||
datagramConn := masque_util.NewDatagramConn(stream, laddr, raddr)
|
||||
defer datagramConn.Close()
|
||||
|
||||
// Wrap with recorder stats and traffic limiter
|
||||
var clientPC net.PacketConn = datagramConn
|
||||
clientPC = stats_wrapper.WrapPacketConn(clientPC, pStats)
|
||||
clientPC = traffic_wrapper.WrapPacketConn(
|
||||
clientPC,
|
||||
h.limiter,
|
||||
clientID,
|
||||
limiter.ServiceOption(h.options.Service),
|
||||
limiter.ScopeOption(limiter.ScopeClient),
|
||||
limiter.NetworkOption("udp"),
|
||||
limiter.AddrOption(targetAddr),
|
||||
limiter.ClientOption(clientID),
|
||||
limiter.SrcOption(ro.RemoteAddr),
|
||||
)
|
||||
|
||||
// Track per-client connection stats
|
||||
if h.options.Observer != nil {
|
||||
pstats := h.stats.Stats(clientID)
|
||||
pstats.Add(stats.KindTotalConns, 1)
|
||||
pstats.Add(stats.KindCurrentConns, 1)
|
||||
defer pstats.Add(stats.KindCurrentConns, -1)
|
||||
clientPC = stats_wrapper.WrapPacketConn(clientPC, pstats)
|
||||
}
|
||||
|
||||
// Relay UDP packets between client and target
|
||||
relay := udp.NewRelay(clientPC, targetPC).
|
||||
WithService(h.options.Service).
|
||||
|
||||
@@ -47,7 +47,11 @@ func (l *http3Listener) parseMetadata(md mdata.Metadata) (err error) {
|
||||
l.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout)
|
||||
l.md.maxIdleTimeout = mdutil.GetDuration(md, maxIdleTimeout)
|
||||
l.md.maxStreams = mdutil.GetInt(md, maxStreams)
|
||||
l.md.enableDatagrams = mdutil.GetBool(md, "enableDatagrams")
|
||||
if mdutil.IsExists(md, "enableDatagrams") {
|
||||
l.md.enableDatagrams = mdutil.GetBool(md, "enableDatagrams")
|
||||
} else {
|
||||
l.md.enableDatagrams = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user