diff --git a/errorcodes/error_codes.go b/errorcodes/error_codes.go index 30a8b2e68..5f2244dd0 100644 --- a/errorcodes/error_codes.go +++ b/errorcodes/error_codes.go @@ -19,10 +19,10 @@ const ( QUIC_ENCRYPTION_FAILURE = protocol.ErrorCode(13) QUIC_PACKET_TOO_LARGE = protocol.ErrorCode(14) // QUIC_PACKET_FOR_NONEXISTENT_STREAM= Data was sent for a stream which did not exist. - QUIC_PEER_GOING_AWAY = protocol.ErrorCode(16) - QUIC_INVALID_STREAM_ID = protocol.ErrorCode(17) - QUIC_TOO_MANY_OPEN_STREAMS = protocol.ErrorCode(18) - // QUIC_CONNECTION_TIMED_OUT= We hit our pre-negotiated (or default) timeout + QUIC_PEER_GOING_AWAY = protocol.ErrorCode(16) + QUIC_INVALID_STREAM_ID = protocol.ErrorCode(17) + QUIC_TOO_MANY_OPEN_STREAMS = protocol.ErrorCode(18) + QUIC_NETWORK_IDLE_TIMEOUT = protocol.ErrorCode(25) QUIC_CRYPTO_TAGS_OUT_OF_ORDER = protocol.ErrorCode(29) QUIC_CRYPTO_TOO_MANY_ENTRIES = protocol.ErrorCode(30) QUIC_CRYPTO_INVALID_VALUE_LENGTH = protocol.ErrorCode(31) diff --git a/handshake/connection_parameters_manager.go b/handshake/connection_parameters_manager.go index 697670f62..d25479ef7 100644 --- a/handshake/connection_parameters_manager.go +++ b/handshake/connection_parameters_manager.go @@ -79,14 +79,13 @@ func (h *ConnectionParametersManager) GetStreamFlowControlWindow() (protocol.Byt } // GetIdleConnectionStateLifetime gets the idle timeout -func (h *ConnectionParametersManager) GetIdleConnectionStateLifetime() (time.Duration, error) { +func (h *ConnectionParametersManager) GetIdleConnectionStateLifetime() time.Duration { rawValue, err := h.GetRawValue(TagICSL) if err != nil { - return 0, err + panic("ConnectionParameters: Could not find ICSL") } - if len(rawValue) != 4 { - return 0, errors.New("expected uint32 for ICSL") + panic("ConnectionParameters: ICSL has invalid value") } - return time.Duration(binary.LittleEndian.Uint32(rawValue)) * time.Second, nil + return time.Duration(binary.LittleEndian.Uint32(rawValue)) * time.Second } diff --git a/handshake/connection_parameters_manager_test.go b/handshake/connection_parameters_manager_test.go index a1c7c6750..acb062f48 100644 --- a/handshake/connection_parameters_manager_test.go +++ b/handshake/connection_parameters_manager_test.go @@ -62,8 +62,7 @@ var _ = Describe("ConnectionsParameterManager", func() { It("gets idle connection state lifetime", func() { cpm.params[TagICSL] = []byte{0xad, 0xfb, 0xca, 0xde} - val, err := cpm.GetIdleConnectionStateLifetime() - Expect(err).ToNot(HaveOccurred()) + val := cpm.GetIdleConnectionStateLifetime() Expect(val).To(Equal(0xdecafbad * time.Second)) }) }) diff --git a/session.go b/session.go index 2d34c5f84..93def6a5d 100644 --- a/session.go +++ b/session.go @@ -119,6 +119,8 @@ func (s *Session) Run() { s.scheduleSending() case <-s.sendingScheduled: err = s.sendPacket() + case <-time.After(s.connectionParametersManager.GetIdleConnectionStateLifetime()): + s.Close(protocol.NewQuicError(errorcodes.QUIC_NETWORK_IDLE_TIMEOUT, "No recent network activity."), true) } if err != nil { diff --git a/session_test.go b/session_test.go index 7a4f90eb7..2a831ebe7 100644 --- a/session_test.go +++ b/session_test.go @@ -45,6 +45,9 @@ var _ = Describe("Session", func() { streams: make(map[protocol.StreamID]*stream), streamCallback: func(*Session, utils.Stream) { callbackCalled = true }, connectionParametersManager: handshake.NewConnectionParamatersManager(), + closeChan: make(chan struct{}, 1), + closeCallback: func(*Session) {}, + packer: &packetPacker{aead: &crypto.NullAEAD{}}, } }) @@ -344,4 +347,13 @@ var _ = Describe("Session", func() { Expect(conn.written).To(HaveLen(1)) Expect(conn.written[0]).To(ContainSubstring(string([]byte("PRST")))) }) + + It("times out", func(done Done) { + session.connectionParametersManager.SetFromMap(map[handshake.Tag][]byte{ + handshake.TagICSL: {0, 0, 0, 0}, + }) + session.Run() // Would normally not return + Expect(conn.written[0]).To(ContainSubstring("No recent network activity.")) + close(done) + }, 0.5) })