diff --git a/interface.go b/interface.go index dba6b34c5..b58c4111d 100644 --- a/interface.go +++ b/interface.go @@ -187,6 +187,10 @@ type Session interface { // It blocks until the handshake completes. // Warning: This API should not be considered stable and might change soon. ConnectionState() ConnectionState + + // SendMessage sends a message as a datagram. + // See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/. + SendMessage([]byte) error } // An EarlySession is a session that is handshaking. diff --git a/internal/mocks/quic/early_session.go b/internal/mocks/quic/early_session.go index 78d44ea1f..1e7fe4ce0 100644 --- a/internal/mocks/quic/early_session.go +++ b/internal/mocks/quic/early_session.go @@ -210,3 +210,17 @@ func (mr *MockEarlySessionMockRecorder) RemoteAddr() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteAddr", reflect.TypeOf((*MockEarlySession)(nil).RemoteAddr)) } + +// SendMessage mocks base method +func (m *MockEarlySession) SendMessage(arg0 []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMessage", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMessage indicates an expected call of SendMessage +func (mr *MockEarlySessionMockRecorder) SendMessage(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMessage", reflect.TypeOf((*MockEarlySession)(nil).SendMessage), arg0) +} diff --git a/mock_quic_session_test.go b/mock_quic_session_test.go index 656f77a25..06a7f3400 100644 --- a/mock_quic_session_test.go +++ b/mock_quic_session_test.go @@ -224,6 +224,20 @@ func (mr *MockQuicSessionMockRecorder) RemoteAddr() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteAddr", reflect.TypeOf((*MockQuicSession)(nil).RemoteAddr)) } +// SendMessage mocks base method +func (m *MockQuicSession) SendMessage(arg0 []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMessage", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMessage indicates an expected call of SendMessage +func (mr *MockQuicSessionMockRecorder) SendMessage(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMessage", reflect.TypeOf((*MockQuicSession)(nil).SendMessage), arg0) +} + // destroy mocks base method func (m *MockQuicSession) destroy(arg0 error) { m.ctrl.T.Helper() diff --git a/session.go b/session.go index 4243f34e3..ad3b623ff 100644 --- a/session.go +++ b/session.go @@ -512,6 +512,9 @@ func (s *session) preSetup() { s.sessionCreationTime = now s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.connFlowController, s.framer.QueueControlFrame) + if s.config.EnableDatagrams { + s.datagramQueue = newDatagramQueue(s.scheduleSending) + } } // run the session main loop @@ -1115,6 +1118,8 @@ func (s *session) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, d err = s.handleRetireConnectionIDFrame(frame, destConnID) case *wire.HandshakeDoneFrame: err = s.handleHandshakeDoneFrame() + case *wire.DatagramFrame: + // TODO: handle DATRAGRAM frames default: err = fmt.Errorf("unexpected frame type: %s", reflect.ValueOf(&frame).Elem().Type().Name()) } @@ -1739,6 +1744,17 @@ func (s *session) onStreamCompleted(id protocol.StreamID) { } } +func (s *session) SendMessage(p []byte) error { + f := &wire.DatagramFrame{DataLenPresent: true} + if protocol.ByteCount(len(p)) > f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version) { + return errors.New("message too large") + } + f.Data = make([]byte, len(p)) + copy(f.Data, p) + s.datagramQueue.AddAndWait(f) + return nil +} + func (s *session) LocalAddr() net.Addr { return s.conn.LocalAddr() }