2 // Copyright © 2011-2019 Guy M. Allard
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
27 Initialize heart beats if necessary and possible.
29 Return an error, possibly nil, to mainline if initialization can not
30 complete. Establish heartbeat send and receive goroutines as necessary.
32 func (c
*Connection
) initializeHeartBeats(ch Headers
) (e error
) {
33 // Client wants Heartbeats ?
34 vc
, ok
:= ch
.Contains(HK_HEART_BEAT
)
35 if !ok || vc
== "0,0" {
38 // Server wants Heartbeats ?
39 vs
, ok
:= c
.ConnectResponse
.Headers
.Contains(HK_HEART_BEAT
)
40 if !ok || vs
== "0,0" {
43 // Work area, may or may not become connection heartbeat data
44 w
:= &heartBeatData
{cx
: 0, cy
: 0, sx
: 0, sy
: 0,
45 hbs
: true, hbr
: true, // possible reset later
49 // Client specified values
50 cp
:= strings
.Split(vc
, ",")
51 if len(cp
) != 2 { // S/B caught by the server first
52 return Error("invalid client heart-beat header: " + vc
)
54 w
.cx
, e
= strconv
.ParseInt(cp
[0], 10, 64)
56 return Error("non-numeric cx heartbeat value: " + cp
[0])
58 w
.cy
, e
= strconv
.ParseInt(cp
[1], 10, 64)
60 return Error("non-numeric cy heartbeat value: " + cp
[1])
63 // Server specified values
64 sp
:= strings
.Split(vs
, ",")
66 return Error("invalid server heart-beat header: " + vs
)
68 w
.sx
, e
= strconv
.ParseInt(sp
[0], 10, 64)
70 return Error("non-numeric sx heartbeat value: " + sp
[0])
72 w
.sy
, e
= strconv
.ParseInt(sp
[1], 10, 64)
74 return Error("non-numeric sy heartbeat value: " + sp
[1])
77 // Check for sending needed
78 if w
.cx
== 0 || w
.sy
== 0 {
82 // Check for receiving needed
83 if w
.sx
== 0 || w
.cy
== 0 {
87 // ========================================================================
90 return nil // none required
93 // ========================================================================
95 c
.hbd
= w
// OK, we are doing some kind of heartbeating
96 ct
:= time
.Now().UnixNano() // Prime current time
98 if w
.hbs
{ // Finish sender parameters if required
99 sm
:= max(w
.cx
, w
.sy
) // ticker interval, ms
100 w
.sti
= 1000000 * sm
// ticker interval, ns
101 w
.ssd
= make(chan struct{}) // add shutdown channel
102 w
.ls
= ct
// Best guess at start
103 // fmt.Println("start send ticker")
107 if w
.hbr
{ // Finish receiver parameters if required
108 rm
:= max(w
.sx
, w
.cy
) // ticker interval, ms
109 w
.rti
= 1000000 * rm
// ticker interval, ns
110 w
.rsd
= make(chan struct{}) // add shutdown channel
111 w
.lr
= ct
// Best guess at start
112 // fmt.Println("start receive ticker")
119 The heart beat send ticker.
121 func (c
*Connection
) sendTicker() {
123 ticker
:= time
.NewTicker(time
.Duration(c
.hbd
.sti
))
129 c
.log("HeartBeat Send data")
131 f
:= Frame
{"\n", Headers
{}, NULLBUFF
} // Heartbeat frame
132 r
:= make(chan error
)
133 if e
:= c
.writeWireData(wiredata
{f
, r
}); e
!= nil {
141 fmt
.Printf("Heartbeat Send Failure: %v\n", e
)
149 case _
= <-c
.hbd
.ssd
:
155 c
.log("Heartbeat Send Ends", time
.Now())
160 The heart beat receive ticker.
162 func (c
*Connection
) receiveTicker() {
164 var first
, last
, nd
int64
167 nd
= c
.hbd
.rti
- (last
- first
)
168 // Check if receives are supposed to be "fast" *and* we spent a
169 // lot of time in the previous loop.
173 ticker
:= time
.NewTicker(time
.Duration(nd
))
175 case ct
:= <-ticker
.C
:
176 first
= time
.Now().UnixNano()
180 ld
:= ct
.UnixNano() - flr
181 c
.log("HeartBeat Receive TIC", "TickerVal", ct
.UnixNano(),
182 "LastReceive", flr
, "Diff", ld
)
183 if ld
> (c
.hbd
.rti
+ (c
.hbd
.rti
/ 5)) { // swag plus to be tolerant
184 c
.log("HeartBeat Receive Read is dirty")
185 c
.Hbrf
= true // Flag possible dirty connection
187 c
.Hbrf
= false // Reset
191 last
= time
.Now().UnixNano()
192 case _
= <-c
.hbd
.rsd
:
200 c
.log("Heartbeat Receive Ends", time
.Now())