2018-05-01 23:45:06 +00:00
|
|
|
package origin
|
|
|
|
|
|
|
|
import (
|
2019-01-10 20:55:44 +00:00
|
|
|
"context"
|
2018-05-01 23:45:06 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2020-10-08 10:12:26 +00:00
|
|
|
"runtime/debug"
|
2018-05-01 23:45:06 +00:00
|
|
|
"strings"
|
2019-02-19 17:40:49 +00:00
|
|
|
"sync"
|
2018-05-01 23:45:06 +00:00
|
|
|
"time"
|
|
|
|
|
2019-11-21 17:03:13 +00:00
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
|
2019-06-17 21:18:47 +00:00
|
|
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo"
|
2020-07-29 22:48:27 +00:00
|
|
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/ui"
|
2019-11-21 18:10:44 +00:00
|
|
|
"github.com/cloudflare/cloudflared/connection"
|
2020-10-08 10:12:26 +00:00
|
|
|
"github.com/cloudflare/cloudflared/edgediscovery"
|
2018-05-01 23:45:06 +00:00
|
|
|
"github.com/cloudflare/cloudflared/h2mux"
|
2020-10-09 00:12:29 +00:00
|
|
|
"github.com/cloudflare/cloudflared/ingress"
|
2020-04-29 20:51:32 +00:00
|
|
|
"github.com/cloudflare/cloudflared/logger"
|
2019-03-04 19:48:56 +00:00
|
|
|
"github.com/cloudflare/cloudflared/signal"
|
2018-05-01 23:45:06 +00:00
|
|
|
"github.com/cloudflare/cloudflared/tunnelrpc"
|
|
|
|
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
dialTimeout = 15 * time.Second
|
2019-12-04 17:22:08 +00:00
|
|
|
muxerTimeout = 5 * time.Second
|
2018-05-01 23:45:06 +00:00
|
|
|
lbProbeUserAgentPrefix = "Mozilla/5.0 (compatible; Cloudflare-Traffic-Manager/1.0; +https://www.cloudflare.com/traffic-manager/;"
|
|
|
|
DuplicateConnectionError = "EDUPCONN"
|
2020-03-31 13:59:00 +00:00
|
|
|
FeatureSerializedHeaders = "serialized_headers"
|
|
|
|
FeatureQuickReconnects = "quick_reconnects"
|
2018-05-01 23:45:06 +00:00
|
|
|
)
|
|
|
|
|
2020-09-28 09:10:30 +00:00
|
|
|
type rpcName string
|
2020-01-28 16:43:37 +00:00
|
|
|
|
|
|
|
const (
|
2020-09-28 09:10:30 +00:00
|
|
|
register rpcName = "register"
|
|
|
|
reconnect rpcName = "reconnect"
|
|
|
|
unregister rpcName = "unregister"
|
|
|
|
authenticate rpcName = " authenticate"
|
2020-01-28 16:43:37 +00:00
|
|
|
)
|
|
|
|
|
2018-05-01 23:45:06 +00:00
|
|
|
type TunnelConfig struct {
|
2020-10-08 10:12:26 +00:00
|
|
|
ConnectionConfig *connection.Config
|
|
|
|
ProxyConfig *ProxyConfig
|
|
|
|
BuildInfo *buildinfo.BuildInfo
|
|
|
|
ClientID string
|
|
|
|
CloseConnOnce *sync.Once // Used to close connectedSignal no more than once
|
|
|
|
EdgeAddrs []string
|
|
|
|
HAConnections int
|
|
|
|
IncidentLookup IncidentLookup
|
|
|
|
IsAutoupdated bool
|
|
|
|
IsFreeTunnel bool
|
|
|
|
LBPool string
|
|
|
|
Logger logger.Service
|
|
|
|
Observer *connection.Observer
|
|
|
|
ReportedVersion string
|
|
|
|
Retries uint
|
|
|
|
RunFromTerminal bool
|
|
|
|
TLSConfig *tls.Config
|
|
|
|
|
|
|
|
NamedTunnel *connection.NamedTunnelConfig
|
|
|
|
ClassicTunnel *connection.ClassicTunnelConfig
|
|
|
|
MuxerConfig *connection.MuxerConfig
|
|
|
|
TunnelEventChan chan ui.TunnelEvent
|
2020-10-15 17:41:50 +00:00
|
|
|
IngressRules ingress.Ingress
|
2018-05-01 23:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type muxerShutdownError struct{}
|
|
|
|
|
|
|
|
func (e muxerShutdownError) Error() string {
|
|
|
|
return "muxer shutdown"
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterTunnel error from server
|
|
|
|
type serverRegisterTunnelError struct {
|
|
|
|
cause error
|
|
|
|
permanent bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e serverRegisterTunnelError) Error() string {
|
|
|
|
return e.cause.Error()
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterTunnel error from client
|
|
|
|
type clientRegisterTunnelError struct {
|
|
|
|
cause error
|
|
|
|
}
|
|
|
|
|
2020-09-28 09:10:30 +00:00
|
|
|
func newRPCError(cause error, counter *prometheus.CounterVec, name rpcName) clientRegisterTunnelError {
|
2020-01-28 17:29:33 +00:00
|
|
|
counter.WithLabelValues(cause.Error(), string(name)).Inc()
|
2019-03-15 23:46:53 +00:00
|
|
|
return clientRegisterTunnelError{cause: cause}
|
|
|
|
}
|
|
|
|
|
2018-05-01 23:45:06 +00:00
|
|
|
func (e clientRegisterTunnelError) Error() string {
|
|
|
|
return e.cause.Error()
|
|
|
|
}
|
|
|
|
|
2018-10-08 19:20:28 +00:00
|
|
|
func (c *TunnelConfig) RegistrationOptions(connectionID uint8, OriginLocalIP string, uuid uuid.UUID) *tunnelpogs.RegistrationOptions {
|
2018-05-01 23:45:06 +00:00
|
|
|
policy := tunnelrpc.ExistingTunnelPolicy_balance
|
|
|
|
if c.HAConnections <= 1 && c.LBPool == "" {
|
|
|
|
policy = tunnelrpc.ExistingTunnelPolicy_disconnect
|
|
|
|
}
|
|
|
|
return &tunnelpogs.RegistrationOptions{
|
|
|
|
ClientID: c.ClientID,
|
|
|
|
Version: c.ReportedVersion,
|
|
|
|
OS: fmt.Sprintf("%s_%s", c.BuildInfo.GoOS, c.BuildInfo.GoArch),
|
|
|
|
ExistingTunnelPolicy: policy,
|
|
|
|
PoolName: c.LBPool,
|
2020-10-08 10:12:26 +00:00
|
|
|
Tags: c.ProxyConfig.Tags,
|
2018-05-01 23:45:06 +00:00
|
|
|
ConnectionID: connectionID,
|
|
|
|
OriginLocalIP: OriginLocalIP,
|
|
|
|
IsAutoupdated: c.IsAutoupdated,
|
|
|
|
RunFromTerminal: c.RunFromTerminal,
|
2020-10-08 10:12:26 +00:00
|
|
|
CompressionQuality: uint64(c.MuxerConfig.CompressionSetting),
|
2018-10-08 19:20:28 +00:00
|
|
|
UUID: uuid.String(),
|
2020-03-31 13:59:00 +00:00
|
|
|
Features: c.SupportedFeatures(),
|
2018-05-01 23:45:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 15:22:23 +00:00
|
|
|
func (c *TunnelConfig) ConnectionOptions(originLocalAddr string, numPreviousAttempts uint8) *tunnelpogs.ConnectionOptions {
|
2020-06-25 18:25:39 +00:00
|
|
|
// attempt to parse out origin IP, but don't fail since it's informational field
|
|
|
|
host, _, _ := net.SplitHostPort(originLocalAddr)
|
|
|
|
originIP := net.ParseIP(host)
|
|
|
|
|
|
|
|
return &tunnelpogs.ConnectionOptions{
|
2020-07-31 15:22:23 +00:00
|
|
|
Client: c.NamedTunnel.Client,
|
|
|
|
OriginLocalIP: originIP,
|
2020-10-08 10:12:26 +00:00
|
|
|
ReplaceExisting: c.ConnectionConfig.ReplaceExisting,
|
|
|
|
CompressionQuality: uint8(c.MuxerConfig.CompressionSetting),
|
2020-07-31 15:22:23 +00:00
|
|
|
NumPreviousAttempts: numPreviousAttempts,
|
2020-06-25 18:25:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 13:59:00 +00:00
|
|
|
func (c *TunnelConfig) SupportedFeatures() []string {
|
2020-06-25 18:25:39 +00:00
|
|
|
features := []string{FeatureSerializedHeaders}
|
|
|
|
if c.NamedTunnel == nil {
|
|
|
|
features = append(features, FeatureQuickReconnects)
|
2020-03-31 13:59:00 +00:00
|
|
|
}
|
2020-06-25 18:25:39 +00:00
|
|
|
return features
|
2020-03-31 13:59:00 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 18:25:39 +00:00
|
|
|
func StartTunnelDaemon(ctx context.Context, config *TunnelConfig, connectedSignal *signal.Signal, cloudflaredID uuid.UUID, reconnectCh chan ReconnectSignal) error {
|
|
|
|
s, err := NewSupervisor(config, cloudflaredID)
|
2019-12-13 23:05:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-03-19 15:38:28 +00:00
|
|
|
return s.Run(ctx, connectedSignal, reconnectCh)
|
2018-05-01 23:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func ServeTunnelLoop(ctx context.Context,
|
2020-08-18 10:14:14 +00:00
|
|
|
credentialManager *reconnectCredentialManager,
|
2018-05-01 23:45:06 +00:00
|
|
|
config *TunnelConfig,
|
|
|
|
addr *net.TCPAddr,
|
2020-06-25 18:25:39 +00:00
|
|
|
connectionIndex uint8,
|
2019-03-04 19:48:56 +00:00
|
|
|
connectedSignal *signal.Signal,
|
2020-06-17 18:33:55 +00:00
|
|
|
cloudflaredUUID uuid.UUID,
|
2020-04-30 05:02:08 +00:00
|
|
|
reconnectCh chan ReconnectSignal,
|
2018-05-01 23:45:06 +00:00
|
|
|
) error {
|
2020-10-08 10:12:26 +00:00
|
|
|
haConnections.Inc()
|
|
|
|
defer haConnections.Dec()
|
|
|
|
|
2018-05-01 23:45:06 +00:00
|
|
|
backoff := BackoffHandler{MaxRetries: config.Retries}
|
|
|
|
connectedFuse := h2mux.NewBooleanFuse()
|
|
|
|
go func() {
|
|
|
|
if connectedFuse.Await() {
|
2019-03-04 19:48:56 +00:00
|
|
|
connectedSignal.Notify()
|
2018-05-01 23:45:06 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
// Ensure the above goroutine will terminate if we return without connecting
|
|
|
|
defer connectedFuse.Fuse(false)
|
|
|
|
for {
|
2019-11-04 11:11:54 +00:00
|
|
|
err, recoverable := ServeTunnel(
|
|
|
|
ctx,
|
2019-12-06 21:32:15 +00:00
|
|
|
credentialManager,
|
2019-11-04 11:11:54 +00:00
|
|
|
config,
|
2020-06-25 18:25:39 +00:00
|
|
|
addr, connectionIndex,
|
2019-11-04 11:11:54 +00:00
|
|
|
connectedFuse,
|
|
|
|
&backoff,
|
2020-06-17 18:33:55 +00:00
|
|
|
cloudflaredUUID,
|
2020-03-19 15:38:28 +00:00
|
|
|
reconnectCh,
|
2019-11-04 11:11:54 +00:00
|
|
|
)
|
2018-05-01 23:45:06 +00:00
|
|
|
if recoverable {
|
|
|
|
if duration, ok := backoff.GetBackoffDuration(ctx); ok {
|
2020-07-24 22:17:17 +00:00
|
|
|
if config.TunnelEventChan != nil {
|
|
|
|
config.TunnelEventChan <- ui.TunnelEvent{Index: connectionIndex, EventType: ui.Reconnecting}
|
2020-08-10 23:09:02 +00:00
|
|
|
}
|
2020-10-08 10:12:26 +00:00
|
|
|
config.Logger.Infof("Retrying connection %d in %s seconds, error %v", connectionIndex, duration, err)
|
2018-05-01 23:45:06 +00:00
|
|
|
backoff.Backoff(ctx)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ServeTunnel(
|
|
|
|
ctx context.Context,
|
2020-08-18 10:14:14 +00:00
|
|
|
credentialManager *reconnectCredentialManager,
|
2018-05-01 23:45:06 +00:00
|
|
|
config *TunnelConfig,
|
|
|
|
addr *net.TCPAddr,
|
2020-06-25 18:25:39 +00:00
|
|
|
connectionIndex uint8,
|
2020-10-08 10:12:26 +00:00
|
|
|
fuse *h2mux.BooleanFuse,
|
2018-05-01 23:45:06 +00:00
|
|
|
backoff *BackoffHandler,
|
2020-06-17 18:33:55 +00:00
|
|
|
cloudflaredUUID uuid.UUID,
|
2020-04-30 05:02:08 +00:00
|
|
|
reconnectCh chan ReconnectSignal,
|
2018-05-01 23:45:06 +00:00
|
|
|
) (err error, recoverable bool) {
|
|
|
|
// Treat panics as recoverable errors
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
var ok bool
|
|
|
|
err, ok = r.(error)
|
|
|
|
if !ok {
|
|
|
|
err = fmt.Errorf("ServeTunnel: %v", r)
|
|
|
|
}
|
2020-10-08 10:12:26 +00:00
|
|
|
err = errors.Wrapf(err, "stack trace: %s", string(debug.Stack()))
|
2018-05-01 23:45:06 +00:00
|
|
|
recoverable = true
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2020-07-29 22:48:27 +00:00
|
|
|
// If launch-ui flag is set, send disconnect msg
|
2020-07-24 22:17:17 +00:00
|
|
|
if config.TunnelEventChan != nil {
|
2020-07-29 22:48:27 +00:00
|
|
|
defer func() {
|
2020-07-24 22:17:17 +00:00
|
|
|
config.TunnelEventChan <- ui.TunnelEvent{Index: connectionIndex, EventType: ui.Disconnected}
|
2020-07-29 22:48:27 +00:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2020-10-08 10:12:26 +00:00
|
|
|
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, config.TLSConfig, addr)
|
|
|
|
if err != nil {
|
|
|
|
return err, true
|
|
|
|
}
|
|
|
|
connectedFuse := &connectedFuse{
|
|
|
|
fuse: fuse,
|
|
|
|
backoff: backoff,
|
2020-10-08 09:48:10 +00:00
|
|
|
}
|
2020-10-08 10:12:26 +00:00
|
|
|
if config.NamedTunnel != nil && config.NamedTunnel.Protocol == connection.HTTP2 {
|
|
|
|
connOptions := config.ConnectionOptions(edgeConn.LocalAddr().String(), uint8(backoff.retries))
|
|
|
|
return ServeHTTP2(ctx, config, edgeConn, connOptions, connectionIndex, connectedFuse, reconnectCh)
|
|
|
|
}
|
|
|
|
return ServeH2mux(ctx, credentialManager, config, edgeConn, connectionIndex, connectedFuse, cloudflaredUUID, reconnectCh)
|
|
|
|
}
|
2020-10-08 09:48:10 +00:00
|
|
|
|
2020-10-08 10:12:26 +00:00
|
|
|
func ServeH2mux(
|
|
|
|
ctx context.Context,
|
|
|
|
credentialManager *reconnectCredentialManager,
|
|
|
|
config *TunnelConfig,
|
|
|
|
edgeConn net.Conn,
|
|
|
|
connectionIndex uint8,
|
|
|
|
connectedFuse *connectedFuse,
|
|
|
|
cloudflaredUUID uuid.UUID,
|
|
|
|
reconnectCh chan ReconnectSignal,
|
|
|
|
) (err error, recoverable bool) {
|
2018-05-01 23:45:06 +00:00
|
|
|
// Returns error from parsing the origin URL or handshake errors
|
2020-10-08 10:12:26 +00:00
|
|
|
handler, err, recoverable := connection.NewH2muxConnection(ctx, config.ConnectionConfig, config.MuxerConfig, config.ProxyConfig.URL.String(), edgeConn, connectionIndex, config.Observer)
|
2018-05-01 23:45:06 +00:00
|
|
|
if err != nil {
|
2020-10-08 10:12:26 +00:00
|
|
|
return err, recoverable
|
2018-05-01 23:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
errGroup, serveCtx := errgroup.WithContext(ctx)
|
|
|
|
|
2019-12-06 21:32:15 +00:00
|
|
|
errGroup.Go(func() (err error) {
|
2020-10-08 10:12:26 +00:00
|
|
|
if config.NamedTunnel != nil {
|
|
|
|
connOptions := config.ConnectionOptions(edgeConn.LocalAddr().String(), uint8(connectedFuse.backoff.retries))
|
|
|
|
return handler.ServeNamedTunnel(ctx, config.NamedTunnel, credentialManager, connOptions, connectedFuse)
|
2018-05-01 23:45:06 +00:00
|
|
|
}
|
2020-10-08 10:12:26 +00:00
|
|
|
registrationOptions := config.RegistrationOptions(connectionIndex, edgeConn.LocalAddr().String(), cloudflaredUUID)
|
|
|
|
return handler.ServeClassicTunnel(ctx, config.ClassicTunnel, credentialManager, registrationOptions, connectedFuse)
|
2018-05-01 23:45:06 +00:00
|
|
|
})
|
|
|
|
|
2020-10-08 10:12:26 +00:00
|
|
|
errGroup.Go(listenReconnect(serveCtx, reconnectCh))
|
2018-05-01 23:45:06 +00:00
|
|
|
|
|
|
|
err = errGroup.Wait()
|
|
|
|
if err != nil {
|
2020-07-07 21:35:44 +00:00
|
|
|
switch err := err.(type) {
|
2020-10-08 10:12:26 +00:00
|
|
|
case *connection.DupConnRegisterTunnelError:
|
2020-07-07 21:35:44 +00:00
|
|
|
// don't retry this connection anymore, let supervisor pick new a address
|
|
|
|
return err, false
|
|
|
|
case *serverRegisterTunnelError:
|
2020-10-08 10:12:26 +00:00
|
|
|
config.Logger.Errorf("Register tunnel error from server side: %s", err.cause)
|
2018-05-01 23:45:06 +00:00
|
|
|
// Don't send registration error return from server to Sentry. They are
|
|
|
|
// logged on server side
|
2019-01-10 20:55:44 +00:00
|
|
|
if incidents := config.IncidentLookup.ActiveIncidents(); len(incidents) > 0 {
|
2020-10-08 10:12:26 +00:00
|
|
|
config.Logger.Error(activeIncidentsMsg(incidents))
|
2019-01-10 20:55:44 +00:00
|
|
|
}
|
2020-07-07 21:35:44 +00:00
|
|
|
return err.cause, !err.permanent
|
|
|
|
case *clientRegisterTunnelError:
|
2020-10-08 10:12:26 +00:00
|
|
|
config.Logger.Errorf("Register tunnel error on client side: %s", err.cause)
|
2018-05-01 23:45:06 +00:00
|
|
|
return err, true
|
2020-07-07 21:35:44 +00:00
|
|
|
case *muxerShutdownError:
|
2020-10-08 10:12:26 +00:00
|
|
|
config.Logger.Info("Muxer shutdown")
|
2020-04-30 05:02:08 +00:00
|
|
|
return err, true
|
|
|
|
case *ReconnectSignal:
|
2020-10-08 10:12:26 +00:00
|
|
|
config.Logger.Infof("Restarting connection %d due to reconnect signal in %d seconds", connectionIndex, err.Delay)
|
2020-07-07 21:35:44 +00:00
|
|
|
err.DelayBeforeReconnect()
|
2018-05-01 23:45:06 +00:00
|
|
|
return err, true
|
|
|
|
default:
|
2020-07-07 21:35:44 +00:00
|
|
|
if err == context.Canceled {
|
2020-10-08 10:12:26 +00:00
|
|
|
config.Logger.Debugf("Serve tunnel error: %s", err)
|
2020-07-07 21:35:44 +00:00
|
|
|
return err, false
|
|
|
|
}
|
2020-10-08 10:12:26 +00:00
|
|
|
config.Logger.Errorf("Serve tunnel error: %s", err)
|
2018-05-01 23:45:06 +00:00
|
|
|
return err, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, true
|
|
|
|
}
|
|
|
|
|
2020-10-08 10:12:26 +00:00
|
|
|
func ServeHTTP2(
|
2020-09-11 22:02:34 +00:00
|
|
|
ctx context.Context,
|
|
|
|
config *TunnelConfig,
|
2020-10-08 10:12:26 +00:00
|
|
|
tlsServerConn net.Conn,
|
|
|
|
connOptions *tunnelpogs.ConnectionOptions,
|
2020-09-25 13:12:53 +00:00
|
|
|
connIndex uint8,
|
2020-10-08 10:12:26 +00:00
|
|
|
connectedFuse connection.ConnectedFuse,
|
2020-09-25 13:12:53 +00:00
|
|
|
reconnectCh chan ReconnectSignal,
|
|
|
|
) (err error, recoverable bool) {
|
2020-10-08 10:12:26 +00:00
|
|
|
server, err := connection.NewHTTP2Connection(tlsServerConn, config.ConnectionConfig, config.ProxyConfig.URL, config.NamedTunnel, connOptions, config.Observer, connIndex, connectedFuse)
|
2020-09-11 22:02:34 +00:00
|
|
|
if err != nil {
|
2020-09-25 13:12:53 +00:00
|
|
|
return err, false
|
2020-09-11 22:02:34 +00:00
|
|
|
}
|
|
|
|
|
2020-09-25 13:12:53 +00:00
|
|
|
errGroup, serveCtx := errgroup.WithContext(ctx)
|
|
|
|
errGroup.Go(func() error {
|
2020-10-08 10:12:26 +00:00
|
|
|
server.Serve(serveCtx)
|
2020-09-25 13:12:53 +00:00
|
|
|
return fmt.Errorf("Connection with edge closed")
|
|
|
|
})
|
|
|
|
|
2020-10-08 10:12:26 +00:00
|
|
|
errGroup.Go(listenReconnect(serveCtx, reconnectCh))
|
2020-09-11 22:02:34 +00:00
|
|
|
|
2020-09-25 13:12:53 +00:00
|
|
|
err = errGroup.Wait()
|
|
|
|
if err != nil {
|
|
|
|
return err, true
|
|
|
|
}
|
|
|
|
return nil, false
|
2020-09-11 22:02:34 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 10:12:26 +00:00
|
|
|
func listenReconnect(ctx context.Context, reconnectCh <-chan ReconnectSignal) func() error {
|
|
|
|
return func() error {
|
|
|
|
select {
|
|
|
|
case reconnect := <-reconnectCh:
|
|
|
|
return &reconnect
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil
|
2018-10-08 19:20:28 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-25 23:13:06 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 10:12:26 +00:00
|
|
|
type connectedFuse struct {
|
|
|
|
fuse *h2mux.BooleanFuse
|
|
|
|
backoff *BackoffHandler
|
2018-05-01 23:45:06 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 10:12:26 +00:00
|
|
|
func (cf *connectedFuse) Connected() {
|
|
|
|
cf.fuse.Fuse(true)
|
|
|
|
cf.backoff.SetGracePeriod()
|
2018-10-08 19:20:28 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 10:12:26 +00:00
|
|
|
func (cf *connectedFuse) IsConnected() bool {
|
|
|
|
return cf.fuse.Value()
|
2018-10-08 19:20:28 +00:00
|
|
|
}
|
2019-01-10 20:55:44 +00:00
|
|
|
|
|
|
|
func activeIncidentsMsg(incidents []Incident) string {
|
|
|
|
preamble := "There is an active Cloudflare incident that may be related:"
|
|
|
|
if len(incidents) > 1 {
|
|
|
|
preamble = "There are active Cloudflare incidents that may be related:"
|
|
|
|
}
|
|
|
|
incidentStrings := []string{}
|
|
|
|
for _, incident := range incidents {
|
|
|
|
incidentString := fmt.Sprintf("%s (%s)", incident.Name, incident.URL())
|
|
|
|
incidentStrings = append(incidentStrings, incidentString)
|
|
|
|
}
|
|
|
|
return preamble + " " + strings.Join(incidentStrings, "; ")
|
|
|
|
|
|
|
|
}
|