keepalive running in a separate goroutine makes sure the connection is alive by sending pings.
()
| 1762 | |
| 1763 | // keepalive running in a separate goroutine makes sure the connection is alive by sending pings. |
| 1764 | func (t *http2Client) keepalive() { |
| 1765 | var err error |
| 1766 | defer func() { |
| 1767 | close(t.keepaliveDone) |
| 1768 | if err != nil { |
| 1769 | t.Close(err) |
| 1770 | } |
| 1771 | }() |
| 1772 | p := &ping{data: [8]byte{}} |
| 1773 | // True iff a ping has been sent, and no data has been received since then. |
| 1774 | outstandingPing := false |
| 1775 | // Amount of time remaining before which we should receive an ACK for the |
| 1776 | // last sent ping. |
| 1777 | timeoutLeft := time.Duration(0) |
| 1778 | // Records the last value of t.lastRead before we go block on the timer. |
| 1779 | // This is required to check for read activity since then. |
| 1780 | prevNano := time.Now().UnixNano() |
| 1781 | timer := time.NewTimer(t.kp.Time) |
| 1782 | for { |
| 1783 | select { |
| 1784 | case <-timer.C: |
| 1785 | lastRead := atomic.LoadInt64(&t.lastRead) |
| 1786 | if lastRead > prevNano { |
| 1787 | // There has been read activity since the last time we were here. |
| 1788 | outstandingPing = false |
| 1789 | // Next timer should fire at kp.Time seconds from lastRead time. |
| 1790 | timer.Reset(time.Duration(lastRead) + t.kp.Time - time.Duration(time.Now().UnixNano())) |
| 1791 | prevNano = lastRead |
| 1792 | continue |
| 1793 | } |
| 1794 | if outstandingPing && timeoutLeft <= 0 { |
| 1795 | err = connectionErrorf(true, nil, "keepalive ping failed to receive ACK within timeout") |
| 1796 | return |
| 1797 | } |
| 1798 | t.mu.Lock() |
| 1799 | if t.state == closing { |
| 1800 | // If the transport is closing, we should exit from the |
| 1801 | // keepalive goroutine here. If not, we could have a race |
| 1802 | // between the call to Signal() from Close() and the call to |
| 1803 | // Wait() here, whereby the keepalive goroutine ends up |
| 1804 | // blocking on the condition variable which will never be |
| 1805 | // signalled again. |
| 1806 | t.mu.Unlock() |
| 1807 | return |
| 1808 | } |
| 1809 | if len(t.activeStreams) < 1 && !t.kp.PermitWithoutStream { |
| 1810 | // If a ping was sent out previously (because there were active |
| 1811 | // streams at that point) which wasn't acked and its timeout |
| 1812 | // hadn't fired, but we got here and are about to go dormant, |
| 1813 | // we should make sure that we unconditionally send a ping once |
| 1814 | // we awaken. |
| 1815 | outstandingPing = false |
| 1816 | t.kpDormant = true |
| 1817 | t.kpDormancyCond.Wait() |
| 1818 | } |
| 1819 | t.kpDormant = false |
| 1820 | t.mu.Unlock() |
| 1821 |
no test coverage detected