func (s *reconnectingRaw) Accept() (conn.Conn, error) { for { s.RLock() c, err := s.RawSession.Accept() s.RUnlock() // if we get an error, reconnect instead of returning it if err != nil { // s.Error("Error from Accept(): %v, reconnecting . . .", err) log.Printf("[ERROR] Error from Accept(): %v, reconnecting . . .\n", err) // reconnect can still return errors for permanent failures if err = s.reconnect(); err != nil { return nil, err } } else { return c, err } } }
func (s *ReconnectingSession) reconnect() error { var wait time.Duration = time.Second failTemp := func(err error) { // s.raw.Info("Session failed: %v", err) log.Printf("[INFO] Session failed: %v\n", err) // session failed, wait before reconnecting // s.raw.Info("Waiting %d seconds before reconnecting", int(wait.Seconds())) log.Printf("[INFO] Waiting %d seconds before reconnecting", int(wait.Seconds())) time.Sleep(wait) // exponentially increase wait time up to a limit wait = 2 * wait wait = time.Duration(math.Min(float64(wait), float64(maxWait))) } fail := func(err error) error { s.done <- err return err } retry: // dial the tunnel server mux, err := s.dialer() if err != nil { failTemp(err) goto retry } // swap the muxado session in s.raw.Lock() s.raw.mux = mux s.raw.Unlock() resp, err := s.raw.Auth(s.raw.id, s.authExtra) if err != nil { failTemp(err) goto retry } // auth errors are considered permanent if resp.Error != "" { return fail(errors.New(resp.Error)) } // re-establish binds s.RLock() for _, t := range s.tunnels { resp, err := s.raw.Listen(t.proto, t.bindOpts, t.bindExtra) if err != nil { s.RUnlock() failTemp(err) goto retry } if resp.Error != "" { s.RUnlock() return fail(errors.New(resp.Error)) } } s.RUnlock() return nil }