Skip to content

Commit

Permalink
implement HTTP/3 unistream hijacking
Browse files Browse the repository at this point in the history
  • Loading branch information
hareku committed Apr 20, 2022
1 parent 6d4a694 commit 1e53956
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 2 deletions.
13 changes: 12 additions & 1 deletion http3/client.go
Expand Up @@ -44,6 +44,7 @@ type roundTripperOpts struct {
MaxHeaderBytes int64
AdditionalSettings map[uint64]uint64
StreamHijacker func(FrameType, quic.Connection, quic.Stream) (hijacked bool, err error)
UniStreamHijacker func(FrameType, quic.Connection, quic.ReceiveStream) (hijacked bool, err error)
}

// client is a HTTP3 client doing requests
Expand Down Expand Up @@ -195,8 +196,18 @@ func (c *client) handleUnidirectionalStreams() {
str.CancelRead(quic.StreamErrorCode(errorStreamCreationError))
return
}
f, err := parseNextFrame(str, nil)

var ufh unknownFrameHandlerFunc
if c.opts.UniStreamHijacker != nil {
ufh = func(ft FrameType) (processed bool, err error) {
return c.opts.UniStreamHijacker(ft, c.conn, str)
}
}
f, err := parseNextFrame(str, ufh)
if err != nil {
if err == errHijacked {
return
}
c.conn.CloseWithError(quic.ApplicationErrorCode(errorFrameError), "")
return
}
Expand Down
4 changes: 4 additions & 0 deletions http3/roundtrip.go
Expand Up @@ -58,6 +58,9 @@ type RoundTripper struct {
// Alternatively, callers can take over the QUIC stream (by returning hijacked true).
StreamHijacker func(FrameType, quic.Connection, quic.Stream) (hijacked bool, err error)

// When set, this callback is called for the first unknown frame parsed on a unidirectional stream.
UniStreamHijacker func(FrameType, quic.Connection, quic.ReceiveStream) (hijacked bool, err error)

// Dial specifies an optional dial function for creating QUIC
// connections for requests.
// If Dial is nil, quic.DialAddrEarlyContext will be used.
Expand Down Expand Up @@ -154,6 +157,7 @@ func (r *RoundTripper) getClient(hostname string, onlyCached bool) (http.RoundTr
DisableCompression: r.DisableCompression,
MaxHeaderBytes: r.MaxResponseHeaderBytes,
StreamHijacker: r.StreamHijacker,
UniStreamHijacker: r.UniStreamHijacker,
},
r.QuicConfig,
r.Dial,
Expand Down
15 changes: 14 additions & 1 deletion http3/server.go
Expand Up @@ -151,6 +151,9 @@ type Server struct {
// Alternatively, callers can take over the QUIC stream (by returning hijacked true).
StreamHijacker func(FrameType, quic.Connection, quic.Stream) (hijacked bool, err error)

// When set, this callback is called for the first unknown frame parsed on a unidirectional receive stream.
UniStreamHijacker func(FrameType, quic.Connection, quic.ReceiveStream) (hijacked bool, err error)

mutex sync.RWMutex
listeners map[*quic.EarlyListener]listenerInfo

Expand Down Expand Up @@ -424,8 +427,18 @@ func (s *Server) handleUnidirectionalStreams(conn quic.EarlyConnection) {
str.CancelRead(quic.StreamErrorCode(errorStreamCreationError))
return
}
f, err := parseNextFrame(str, nil)

var ufh unknownFrameHandlerFunc
if s.UniStreamHijacker != nil {
ufh = func(ft FrameType) (processed bool, err error) {
return s.UniStreamHijacker(ft, conn, str)
}
}
f, err := parseNextFrame(str, ufh)
if err != nil {
if err == errHijacked {
return
}
conn.CloseWithError(quic.ApplicationErrorCode(errorFrameError), "")
return
}
Expand Down

0 comments on commit 1e53956

Please sign in to comment.