check that QUIC layer negotiated datagram support when handing SETTINGS

This commit is contained in:
Marten Seemann 2020-12-22 14:10:30 +07:00
parent 3cb07d8f00
commit b753005137
4 changed files with 77 additions and 2 deletions

View file

@ -167,8 +167,19 @@ func (c *client) handleUnidirectionalStreams() {
c.session.CloseWithError(quic.ErrorCode(errorFrameError), "")
return
}
if _, ok := f.(*settingsFrame); !ok {
sf, ok := f.(*settingsFrame)
if !ok {
c.session.CloseWithError(quic.ErrorCode(errorMissingSettings), "")
return
}
if !sf.Datagram {
return
}
// If datagram support was enabled on our side as well as on the server side,
// we can expect it to have been negotiated both on the transport and on the HTTP/3 layer.
// Note: ConnectionState() will block until the handshake is complete (relevant when using 0-RTT).
if c.opts.EnableDatagram && !c.session.ConnectionState().SupportsDatagrams {
c.session.CloseWithError(quic.ErrorCode(errorSettingsError), "missing QUIC Datagram support")
}
}()
}

View file

@ -328,6 +328,33 @@ var _ = Describe("Client", func() {
Expect(err).To(MatchError("done"))
Eventually(done).Should(BeClosed())
})
It("errors when the server advertises datagram support (and we enabled support for it)", func() {
client.opts.EnableDatagram = true
buf := &bytes.Buffer{}
utils.WriteVarInt(buf, streamTypeControlStream)
(&settingsFrame{Datagram: true}).Write(buf)
controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes()
sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil
})
sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
<-testDone
return nil, errors.New("test done")
})
sess.EXPECT().ConnectionState().Return(quic.ConnectionState{SupportsDatagrams: false})
done := make(chan struct{})
sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ErrorCode, reason string) {
defer GinkgoRecover()
Expect(code).To(BeEquivalentTo(errorSettingsError))
Expect(reason).To(Equal("missing QUIC Datagram support"))
close(done)
})
_, err := client.RoundTrip(request)
Expect(err).To(MatchError("done"))
Eventually(done).Should(BeClosed())
})
})
Context("Doing requests", func() {

View file

@ -301,8 +301,19 @@ func (s *Server) handleUnidirectionalStreams(sess quic.EarlySession) {
sess.CloseWithError(quic.ErrorCode(errorFrameError), "")
return
}
if _, ok := f.(*settingsFrame); !ok {
sf, ok := f.(*settingsFrame)
if !ok {
sess.CloseWithError(quic.ErrorCode(errorMissingSettings), "")
return
}
if !sf.Datagram {
return
}
// If datagram support was enabled on our side as well as on the client side,
// we can expect it to have been negotiated both on the transport and on the HTTP/3 layer.
// Note: ConnectionState() will block until the handshake is complete (relevant when using 0-RTT).
if s.EnableDatagrams && !sess.ConnectionState().SupportsDatagrams {
sess.CloseWithError(quic.ErrorCode(errorSettingsError), "missing QUIC Datagram support")
}
}(str)
}

View file

@ -304,6 +304,32 @@ var _ = Describe("Server", func() {
s.handleConn(sess)
Eventually(done).Should(BeClosed())
})
It("errors when the client advertises datagram support (and we enabled support for it)", func() {
s.EnableDatagrams = true
buf := &bytes.Buffer{}
utils.WriteVarInt(buf, streamTypeControlStream)
(&settingsFrame{Datagram: true}).Write(buf)
controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes()
sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil
})
sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
<-testDone
return nil, errors.New("test done")
})
sess.EXPECT().ConnectionState().Return(quic.ConnectionState{SupportsDatagrams: false})
done := make(chan struct{})
sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ErrorCode, reason string) {
defer GinkgoRecover()
Expect(code).To(BeEquivalentTo(errorSettingsError))
Expect(reason).To(Equal("missing QUIC Datagram support"))
close(done)
})
s.handleConn(sess)
Eventually(done).Should(BeClosed())
})
})
Context("stream- and connection-level errors", func() {