forked from quic-go/quic-go
http3: add a RoundTripOpt to check the server's SETTINGS frame (#4355)
For some requests, the client is required to check the server's HTTP/3 SETTINGS. For example, a client is only allowed to send HTTP/3 datagrams if the server explicitly enabled support. SETTINGS are sent asynchronously on a control stream (usually the first unidirectional stream). This means that the SETTINGS might not be available at the beginning of the connection. This is not expected to be the common case, since the server can send the SETTINGS in 0.5-RTT data, but we have to be able to deal with arbitrary delays. For WebTransport, there are even more SETTINGS values that the client needs to check. By making CheckSettings a callback on the RoundTripOpt, this entire validation logic can live at the WebTransport layer.
This commit is contained in:
@@ -58,6 +58,9 @@ type client struct {
|
||||
dialer dialFunc
|
||||
handshakeErr error
|
||||
|
||||
receivedSettings chan struct{} // closed once the server's SETTINGS frame was processed
|
||||
settings *Settings // set once receivedSettings is closed
|
||||
|
||||
requestWriter *requestWriter
|
||||
|
||||
decoder *qpack.Decoder
|
||||
@@ -107,14 +110,15 @@ func newClient(hostname string, tlsConf *tls.Config, opts *roundTripperOpts, con
|
||||
tlsConf.NextProtos = []string{versionToALPN(conf.Versions[0])}
|
||||
|
||||
return &client{
|
||||
hostname: authorityAddr("https", hostname),
|
||||
tlsConf: tlsConf,
|
||||
requestWriter: newRequestWriter(logger),
|
||||
decoder: qpack.NewDecoder(func(hf qpack.HeaderField) {}),
|
||||
config: conf,
|
||||
opts: opts,
|
||||
dialer: dialer,
|
||||
logger: logger,
|
||||
hostname: authorityAddr("https", hostname),
|
||||
tlsConf: tlsConf,
|
||||
requestWriter: newRequestWriter(logger),
|
||||
receivedSettings: make(chan struct{}),
|
||||
decoder: qpack.NewDecoder(func(hf qpack.HeaderField) {}),
|
||||
config: conf,
|
||||
opts: opts,
|
||||
dialer: dialer,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -234,6 +238,12 @@ func (c *client) handleUnidirectionalStreams(conn quic.EarlyConnection) {
|
||||
conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeMissingSettings), "")
|
||||
return
|
||||
}
|
||||
c.settings = &Settings{
|
||||
EnableDatagram: sf.Datagram,
|
||||
EnableExtendedConnect: sf.ExtendedConnect,
|
||||
Other: sf.Other,
|
||||
}
|
||||
close(c.receivedSettings)
|
||||
if !sf.Datagram {
|
||||
return
|
||||
}
|
||||
@@ -299,6 +309,18 @@ func (c *client) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Respon
|
||||
}
|
||||
}
|
||||
|
||||
if opt.CheckSettings != nil {
|
||||
// wait for the server's SETTINGS frame to arrive
|
||||
select {
|
||||
case <-c.receivedSettings:
|
||||
case <-conn.Context().Done():
|
||||
return nil, context.Cause(conn.Context())
|
||||
}
|
||||
if err := opt.CheckSettings(*c.settings); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
str, err := conn.OpenStreamSync(req.Context())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user