http3: send SETTINGS_ENABLE_CONNECT_PROTOCOL (for Extended CONNECT) (#4341)

This commit is contained in:
Marten Seemann 2024-03-03 17:45:59 +10:30 committed by GitHub
parent 0405634108
commit 9813766373
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 83 additions and 12 deletions

View file

@ -88,11 +88,18 @@ func (f *headersFrame) Append(b []byte) []byte {
return quicvarint.Append(b, f.Length) return quicvarint.Append(b, f.Length)
} }
const settingDatagram = 0x33 const (
// Extended CONNECT, RFC 9220
settingExtendedConnect = 0x8
// HTTP Datagrams, RFC 9297
settingDatagram = 0x33
)
type settingsFrame struct { type settingsFrame struct {
Datagram bool Datagram bool // HTTP Datagrams, RFC 9297
Other map[uint64]uint64 // all settings that we don't explicitly recognize ExtendedConnect bool // Extended CONNECT, RFC 9220
Other map[uint64]uint64 // all settings that we don't explicitly recognize
} }
func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) { func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) {
@ -108,7 +115,7 @@ func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) {
} }
frame := &settingsFrame{} frame := &settingsFrame{}
b := bytes.NewReader(buf) b := bytes.NewReader(buf)
var readDatagram bool var readDatagram, readExtendedConnect bool
for b.Len() > 0 { for b.Len() > 0 {
id, err := quicvarint.Read(b) id, err := quicvarint.Read(b)
if err != nil { // should not happen. We allocated the whole frame already. if err != nil { // should not happen. We allocated the whole frame already.
@ -120,13 +127,22 @@ func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) {
} }
switch id { switch id {
case settingExtendedConnect:
if readExtendedConnect {
return nil, fmt.Errorf("duplicate setting: %d", id)
}
readExtendedConnect = true
if val != 0 && val != 1 {
return nil, fmt.Errorf("invalid value for SETTINGS_ENABLE_CONNECT_PROTOCOL: %d", val)
}
frame.ExtendedConnect = val == 1
case settingDatagram: case settingDatagram:
if readDatagram { if readDatagram {
return nil, fmt.Errorf("duplicate setting: %d", id) return nil, fmt.Errorf("duplicate setting: %d", id)
} }
readDatagram = true readDatagram = true
if val != 0 && val != 1 { if val != 0 && val != 1 {
return nil, fmt.Errorf("invalid value for H3_DATAGRAM: %d", val) return nil, fmt.Errorf("invalid value for SETTINGS_H3_DATAGRAM: %d", val)
} }
frame.Datagram = val == 1 frame.Datagram = val == 1
default: default:
@ -151,11 +167,18 @@ func (f *settingsFrame) Append(b []byte) []byte {
if f.Datagram { if f.Datagram {
l += quicvarint.Len(settingDatagram) + quicvarint.Len(1) l += quicvarint.Len(settingDatagram) + quicvarint.Len(1)
} }
if f.ExtendedConnect {
l += quicvarint.Len(settingExtendedConnect) + quicvarint.Len(1)
}
b = quicvarint.Append(b, uint64(l)) b = quicvarint.Append(b, uint64(l))
if f.Datagram { if f.Datagram {
b = quicvarint.Append(b, settingDatagram) b = quicvarint.Append(b, settingDatagram)
b = quicvarint.Append(b, 1) b = quicvarint.Append(b, 1)
} }
if f.ExtendedConnect {
b = quicvarint.Append(b, settingExtendedConnect)
b = quicvarint.Append(b, 1)
}
for id, val := range f.Other { for id, val := range f.Other {
b = quicvarint.Append(b, id) b = quicvarint.Append(b, id)
b = quicvarint.Append(b, val) b = quicvarint.Append(b, val)

View file

@ -127,8 +127,8 @@ var _ = Describe("Frames", func() {
} }
}) })
Context("H3_DATAGRAM", func() { Context("HTTP Datagrams", func() {
It("reads the H3_DATAGRAM value", func() { It("reads the SETTINGS_H3_DATAGRAM value", func() {
settings := quicvarint.Append(nil, settingDatagram) settings := quicvarint.Append(nil, settingDatagram)
settings = quicvarint.Append(settings, 1) settings = quicvarint.Append(settings, 1)
data := quicvarint.Append(nil, 4) // type byte data := quicvarint.Append(nil, 4) // type byte
@ -141,7 +141,7 @@ var _ = Describe("Frames", func() {
Expect(sf.Datagram).To(BeTrue()) Expect(sf.Datagram).To(BeTrue())
}) })
It("rejects duplicate H3_DATAGRAM entries", func() { It("rejects duplicate SETTINGS_H3_DATAGRAM entries", func() {
settings := quicvarint.Append(nil, settingDatagram) settings := quicvarint.Append(nil, settingDatagram)
settings = quicvarint.Append(settings, 1) settings = quicvarint.Append(settings, 1)
settings = quicvarint.Append(settings, settingDatagram) settings = quicvarint.Append(settings, settingDatagram)
@ -153,23 +153,67 @@ var _ = Describe("Frames", func() {
Expect(err).To(MatchError(fmt.Sprintf("duplicate setting: %d", settingDatagram))) Expect(err).To(MatchError(fmt.Sprintf("duplicate setting: %d", settingDatagram)))
}) })
It("rejects invalid values for the H3_DATAGRAM entry", func() { It("rejects invalid values for the SETTINGS_H3_DATAGRAM entry", func() {
settings := quicvarint.Append(nil, settingDatagram) settings := quicvarint.Append(nil, settingDatagram)
settings = quicvarint.Append(settings, 1337) settings = quicvarint.Append(settings, 1337)
data := quicvarint.Append(nil, 4) // type byte data := quicvarint.Append(nil, 4) // type byte
data = quicvarint.Append(data, uint64(len(settings))) data = quicvarint.Append(data, uint64(len(settings)))
data = append(data, settings...) data = append(data, settings...)
_, err := parseNextFrame(bytes.NewReader(data), nil) _, err := parseNextFrame(bytes.NewReader(data), nil)
Expect(err).To(MatchError("invalid value for H3_DATAGRAM: 1337")) Expect(err).To(MatchError("invalid value for SETTINGS_H3_DATAGRAM: 1337"))
}) })
It("writes the H3_DATAGRAM setting", func() { It("writes the SETTINGS_H3_DATAGRAM setting", func() {
sf := &settingsFrame{Datagram: true} sf := &settingsFrame{Datagram: true}
frame, err := parseNextFrame(bytes.NewReader(sf.Append(nil)), nil) frame, err := parseNextFrame(bytes.NewReader(sf.Append(nil)), nil)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(frame).To(Equal(sf)) Expect(frame).To(Equal(sf))
}) })
}) })
Context("Extended Connect", func() {
It("reads the SETTINGS_ENABLE_CONNECT_PROTOCOL value", func() {
settings := quicvarint.Append(nil, settingExtendedConnect)
settings = quicvarint.Append(settings, 1)
data := quicvarint.Append(nil, 4) // type byte
data = quicvarint.Append(data, uint64(len(settings)))
data = append(data, settings...)
f, err := parseNextFrame(bytes.NewReader(data), nil)
Expect(err).ToNot(HaveOccurred())
Expect(f).To(BeAssignableToTypeOf(&settingsFrame{}))
sf := f.(*settingsFrame)
Expect(sf.ExtendedConnect).To(BeTrue())
})
It("rejects duplicate SETTINGS_ENABLE_CONNECT_PROTOCOL entries", func() {
settings := quicvarint.Append(nil, settingExtendedConnect)
settings = quicvarint.Append(settings, 1)
settings = quicvarint.Append(settings, settingExtendedConnect)
settings = quicvarint.Append(settings, 1)
data := quicvarint.Append(nil, 4) // type byte
data = quicvarint.Append(data, uint64(len(settings)))
data = append(data, settings...)
_, err := parseNextFrame(bytes.NewReader(data), nil)
Expect(err).To(MatchError(fmt.Sprintf("duplicate setting: %d", settingExtendedConnect)))
})
It("rejects invalid values for the SETTINGS_ENABLE_CONNECT_PROTOCOL entry", func() {
settings := quicvarint.Append(nil, settingExtendedConnect)
settings = quicvarint.Append(settings, 1337)
data := quicvarint.Append(nil, 4) // type byte
data = quicvarint.Append(data, uint64(len(settings)))
data = append(data, settings...)
_, err := parseNextFrame(bytes.NewReader(data), nil)
Expect(err).To(MatchError("invalid value for SETTINGS_ENABLE_CONNECT_PROTOCOL: 1337"))
})
It("writes the SETTINGS_ENABLE_CONNECT_PROTOCOL setting", func() {
sf := &settingsFrame{ExtendedConnect: true}
frame, err := parseNextFrame(bytes.NewReader(sf.Append(nil)), nil)
Expect(err).ToNot(HaveOccurred())
Expect(frame).To(Equal(sf))
})
})
}) })
Context("hijacking", func() { Context("hijacking", func() {

View file

@ -451,7 +451,11 @@ func (s *Server) handleConn(conn quic.Connection) error {
} }
b := make([]byte, 0, 64) b := make([]byte, 0, 64)
b = quicvarint.Append(b, streamTypeControlStream) // stream type b = quicvarint.Append(b, streamTypeControlStream) // stream type
b = (&settingsFrame{Datagram: s.EnableDatagrams, Other: s.AdditionalSettings}).Append(b) b = (&settingsFrame{
Datagram: s.EnableDatagrams,
ExtendedConnect: true,
Other: s.AdditionalSettings,
}).Append(b)
str.Write(b) str.Write(b)
go s.handleUnidirectionalStreams(conn) go s.handleUnidirectionalStreams(conn)