fluffle/goirc

goirc disconnects (actively) after 5 minutes of inactivity

Closed this issue · 5 comments

In issue #5, a default timeout of 5 minutes was introduced. The comment stated that there should be a PING within 5 minutes anyway. While it is probably true that most IRC servers are configured with a pingfreq of some value less than 5 minutes, this does not mean that there actually is a PING within 5 minutes:

At least with Unreal3.2.8.1, a ping will be sent after $pingfreq seconds of inactivity. I run a goirc bot on a server where pingfreq is 260. When the goirc bot posts a line, the server will register this is activity. Therefore, for 260 seconds, no PING will be sent. However, since this was a write and not a read, and the IRC connection is otherwise absolutely quiet, goirc will eventually run into a timeout, close the connection (the IRC server says "Client exited"), signal the error "irc.recv(): read tcp 127.0.0.1:6667: resource temporarily unavailable" and reconnect (due to my code). I can reliably reproduce this every time.

Therefore, I propose goirc sending a PING request to the server every 250 seconds (or whatever value you think is sufficient) to make the timeout not lead to a disconnect in situations like the one I described above. Unfortunately, I’m not very familiar with neither Go nor goirc’s code structure, otherwise I would have sent a pull request.

Thanks!

Hmm, interesting. I suspect a client-side ping (with a configurable frequency) should be pretty easy to set up. I'll try hacking something together in the next couple of days :-)

Thanks. For the record, the following code works for me as a workaround (and confirms the issue):

    // Workaround channel to send a PING every 250 seconds to avoid the 300
    // second default timeout from disconnecting us.
    ping := make(chan bool)

    go func() {
        for {
            // sleep 250 seconds
            time.Sleep(250 * 1e9)
            ping <- true
        }
    }()

And in the main loop:

    for {
        select {
        case err, ok := <-c.Err:
            // If the error channel was closed, we lost the IRC connection.
            if !ok {
                break
            }
            if err != nil {
                log.Printf("goirc error: %s", err.String())
            }
        case <-ping:
            log.Println("Sending keepalive ping")
            c.Raw("PING :keepalive")
        }
    }

Ok, s/days/hours/ ;-)

Fancy trying the latest master? Your problems should be fixed, the default ping loop is 3 minutes. The algorithm is naive but works, it's slightly less trivial to make it "3 minutes since last line sent" but if you really want that it shouldn't be too hard.

Thanks for the commit. I tested it and it works great (so far) :).

Good good. Closing this :-)