negotiate idle connection state lifetime

work towards #20
This commit is contained in:
Marten Seemann
2016-05-14 16:48:19 +07:00
parent 43621c9c25
commit 16bd559d9a
6 changed files with 82 additions and 24 deletions

View File

@@ -18,6 +18,7 @@ type ConnectionParametersManager struct {
params map[Tag][]byte
mutex sync.RWMutex
idleConnectionStateLifetime time.Duration
sendStreamFlowControlWindow protocol.ByteCount
sendConnectionFlowControlWindow protocol.ByteCount
receiveStreamFlowControlWindow protocol.ByteCount
@@ -31,9 +32,9 @@ var ErrTagNotInConnectionParameterMap = errors.New("Tag not found in Connections
func NewConnectionParamatersManager() *ConnectionParametersManager {
return &ConnectionParametersManager{
params: map[Tag][]byte{
TagICSL: {0x1e, 0x00, 0x00, 0x00}, // idle connection state lifetime = 30s
TagMSPC: {0x64, 0x00, 0x00, 0x00}, // Max streams per connection = 100
},
idleConnectionStateLifetime: protocol.InitialIdleConnectionStateLifetime,
sendStreamFlowControlWindow: protocol.InitialStreamFlowControlWindow, // can only be changed by the client
sendConnectionFlowControlWindow: protocol.InitialConnectionFlowControlWindow, // can only be changed by the client
receiveStreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow,
@@ -48,8 +49,14 @@ func (h *ConnectionParametersManager) SetFromMap(params map[Tag][]byte) error {
for key, value := range params {
switch key {
case TagICSL, TagMSPC, TagTCID:
case TagMSPC, TagTCID:
h.params[key] = value
case TagICSL:
clientValue, err := utils.ReadUint32(bytes.NewBuffer(value))
if err != nil {
return err
}
h.idleConnectionStateLifetime = h.negotiateIdleConnectionStateLifetime(time.Duration(clientValue) * time.Second)
case TagSFCW:
sendStreamFlowControlWindow, err := utils.ReadUint32(bytes.NewBuffer(value))
if err != nil {
@@ -68,6 +75,11 @@ func (h *ConnectionParametersManager) SetFromMap(params map[Tag][]byte) error {
return nil
}
func (h *ConnectionParametersManager) negotiateIdleConnectionStateLifetime(clientValue time.Duration) time.Duration {
// TODO: what happens if the clients sets 0 seconds?
return utils.MinDuration(clientValue, protocol.MaxIdleConnectionStateLifetime)
}
// getRawValue gets the byte-slice for a tag
func (h *ConnectionParametersManager) getRawValue(tag Tag) ([]byte, error) {
h.mutex.RLock()
@@ -83,12 +95,15 @@ func (h *ConnectionParametersManager) getRawValue(tag Tag) ([]byte, error) {
// GetSHLOMap gets all values (except crypto values) needed for the SHLO
func (h *ConnectionParametersManager) GetSHLOMap() map[Tag][]byte {
sfcw := bytes.NewBuffer([]byte{})
cfcw := bytes.NewBuffer([]byte{})
utils.WriteUint32(sfcw, uint32(h.GetReceiveStreamFlowControlWindow()))
cfcw := bytes.NewBuffer([]byte{})
utils.WriteUint32(cfcw, uint32(h.GetReceiveConnectionFlowControlWindow()))
icsl := bytes.NewBuffer([]byte{})
utils.Debugf("ICSL: %#v\n", h.GetIdleConnectionStateLifetime())
utils.WriteUint32(icsl, uint32(h.GetIdleConnectionStateLifetime()/time.Second))
return map[Tag][]byte{
TagICSL: []byte{0x1e, 0x00, 0x00, 0x00}, //30
TagICSL: icsl.Bytes(),
TagMSPC: []byte{0x64, 0x00, 0x00, 0x00}, //100
TagCFCW: cfcw.Bytes(),
TagSFCW: sfcw.Bytes(),
@@ -129,14 +144,10 @@ func (h *ConnectionParametersManager) GetReceiveConnectionFlowControlWindow() pr
// GetIdleConnectionStateLifetime gets the idle timeout
func (h *ConnectionParametersManager) GetIdleConnectionStateLifetime() time.Duration {
rawValue, err := h.getRawValue(TagICSL)
if err != nil {
panic("ConnectionParameters: Could not find ICSL")
}
if len(rawValue) != 4 {
panic("ConnectionParameters: ICSL has invalid value")
}
return time.Duration(binary.LittleEndian.Uint32(rawValue)) * time.Second
h.mutex.RLock()
defer h.mutex.RUnlock()
return h.idleConnectionStateLifetime
}
// TruncateConnectionID determines if the client requests truncated ConnectionIDs

View File

@@ -15,16 +15,16 @@ var _ = Describe("ConnectionsParameterManager", func() {
})
It("stores and retrieves a value", func() {
icsl := []byte{0x13, 0x37}
mspc := []byte{0x13, 0x37}
values := map[Tag][]byte{
TagICSL: icsl,
TagMSPC: mspc,
}
cpm.SetFromMap(values)
val, err := cpm.getRawValue(TagICSL)
val, err := cpm.getRawValue(TagMSPC)
Expect(err).ToNot(HaveOccurred())
Expect(val).To(Equal(icsl))
Expect(val).To(Equal(mspc))
})
It("returns an error for a tag that is not set", func() {
@@ -53,6 +53,13 @@ var _ = Describe("ConnectionsParameterManager", func() {
Expect(entryMap).To(HaveKey(TagCFCW))
Expect(entryMap[TagCFCW]).To(Equal([]byte{0xAD, 0xFB, 0xCA, 0xDE}))
})
It("returns connection-level flow control windows in SHLO", func() {
cpm.idleConnectionStateLifetime = 0xDECAFBAD * time.Second
entryMap := cpm.GetSHLOMap()
Expect(entryMap).To(HaveKey(TagICSL))
Expect(entryMap[TagICSL]).To(Equal([]byte{0xAD, 0xFB, 0xCA, 0xDE}))
})
})
Context("Truncated connection IDs", func() {
@@ -123,14 +130,33 @@ var _ = Describe("ConnectionsParameterManager", func() {
})
})
It("gets idle connection state lifetime", func() {
cpm.params[TagICSL] = []byte{0xad, 0xfb, 0xca, 0xde}
val := cpm.GetIdleConnectionStateLifetime()
Expect(val).To(Equal(0xdecafbad * time.Second))
})
Context("idle connection state lifetime", func() {
It("has initial idle conneciton state lifetime", func() {
Expect(cpm.GetIdleConnectionStateLifetime()).To(Equal(protocol.InitialIdleConnectionStateLifetime))
})
It("has initial idle conneciton state lifetime", func() {
val := cpm.GetIdleConnectionStateLifetime()
Expect(val).To(Equal(30 * time.Second))
It("negotiates correctly when the client wants a longer lifetime", func() {
Expect(cpm.negotiateIdleConnectionStateLifetime(protocol.MaxIdleConnectionStateLifetime + 10*time.Second)).To(Equal(protocol.MaxIdleConnectionStateLifetime))
})
It("negotiates correctly when the client wants a shorter lifetime", func() {
Expect(cpm.negotiateIdleConnectionStateLifetime(protocol.MaxIdleConnectionStateLifetime - 1*time.Second)).To(Equal(protocol.MaxIdleConnectionStateLifetime - 1*time.Second))
})
It("sets the negotiated lifetime", func() {
// this test only works if the value given here is smaller than protocol.MaxIdleConnectionStateLifetime
values := map[Tag][]byte{
TagICSL: []byte{10, 0, 0, 0},
}
err := cpm.SetFromMap(values)
Expect(err).ToNot(HaveOccurred())
Expect(cpm.GetIdleConnectionStateLifetime()).To(Equal(10 * time.Second))
})
It("gets idle connection state lifetime", func() {
value := 0xDECAFBAD * time.Second
cpm.idleConnectionStateLifetime = value
Expect(cpm.GetIdleConnectionStateLifetime()).To(Equal(value))
})
})
})