func main() { var normal tec.TempErrCatcher var skipper tec.TempErrCatcher skipper.IsTemp = func(e error) bool { return e == ErrSkip } fmt.Println("trying normal (uses Temporary interface)") tryTec(normal) fmt.Println("") fmt.Println("trying skipper (uses our IsTemp function)") tryTec(skipper) }
// accept continously accepts incoming connections and // adds them to the listener's Swarm. is is meant to be // run in a goroutine. // TODO: add rate limiting func (l *Listener) accept() { var wg sync.WaitGroup defer func() { wg.Wait() // must happen before teardown l.teardown() }() // catching the error here is odd. doing what net/http does: // http://golang.org/src/net/http/server.go?s=51504:51550#L1728 // Using the lib: https://godoc.org/github.com/jbenet/go-temp-err-catcher var catcher tec.TempErrCatcher // rate limit concurrency limit := make(chan struct{}, AcceptConcurrency) // loop forever accepting connections for { conn, err := l.netList.Accept() if err != nil { if catcher.IsTemporary(err) { continue } l.acceptErr <- fmt.Errorf("peerstream listener failed: %s", err) return // ok, problems. bail. } // add conn to swarm and listen for incoming streams // do this in a goroutine to avoid blocking the Accept loop. // note that this does not rate limit accepts. limit <- struct{}{} // sema down wg.Add(1) go func(conn net.Conn) { defer func() { <-limit }() // sema up defer wg.Done() conn2, err := l.swarm.addConn(conn, true) if err != nil { l.acceptErr <- err return } conn2.groups.AddSet(&l.groups) // add out groups }(conn) } }
func tryTec(c tec.TempErrCatcher) { errs := []error{ ErrTemp, ErrSkip, ErrOther, ErrTemp, ErrSkip, ErrOther, } for _, e := range errs { if c.IsTemporary(e) { fmt.Printf("\tIsTemporary: true - skipped %s\n", e) continue } fmt.Printf("\tIsTemporary: false - not skipped %s\n", e) } }
// Accept waits for and returns the next connection to the listener. // Note that unfortunately this func (l *listener) Accept() (net.Conn, error) { // listeners dont have contexts. given changes dont make sense here anymore // note that the parent of listener will Close, which will interrupt all io. // Contexts and io don't mix. ctx := context.Background() var catcher tec.TempErrCatcher catcher.IsTemp = func(e error) bool { // ignore connection breakages up to this point. but log them if e == io.EOF { log.Debugf("listener ignoring conn with EOF: %s", e) return true } te, ok := e.(tec.Temporary) if ok { log.Debugf("listener ignoring conn with temporary err: %s", e) return te.Temporary() } return false } for { maconn, err := l.Listener.Accept() if err != nil { if catcher.IsTemporary(err) { continue } return nil, err } log.Debugf("listener %s got connection: %s <---> %s", l, maconn.LocalMultiaddr(), maconn.RemoteMultiaddr()) if l.filters != nil && l.filters.AddrBlocked(maconn.RemoteMultiaddr()) { log.Debugf("blocked connection from %s", maconn.RemoteMultiaddr()) maconn.Close() continue } // If we have a wrapper func, wrap this conn if l.wrapper != nil { maconn = l.wrapper(maconn) } c, err := newSingleConn(ctx, l.local, "", maconn) if err != nil { if catcher.IsTemporary(err) { continue } return nil, err } if l.privk == nil || EncryptConnections == false { log.Warning("listener %s listening INSECURELY!", l) return c, nil } sc, err := newSecureConn(ctx, l.privk, c) if err != nil { log.Infof("ignoring conn we failed to secure: %s %s", err, c) continue } return sc, nil } }