Reconnect and retry IRC connections
This commit is contained in:
parent
5cf2822c34
commit
c325168a20
213
server/irc.go
213
server/irc.go
@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -59,13 +58,19 @@ type Message struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type IRC struct {
|
type IRC struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
reader *bufio.Reader
|
connected bool
|
||||||
out chan string
|
dialer *net.Dialer
|
||||||
ready sync.WaitGroup
|
reader *bufio.Reader
|
||||||
once sync.Once
|
out chan string
|
||||||
nick string
|
|
||||||
nickLock sync.Mutex
|
quit chan struct{}
|
||||||
|
reconnect chan struct{}
|
||||||
|
ready sync.WaitGroup
|
||||||
|
once sync.Once
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
nick string
|
||||||
|
|
||||||
Messages chan *Message
|
Messages chan *Message
|
||||||
Server string
|
Server string
|
||||||
@ -79,15 +84,17 @@ type IRC struct {
|
|||||||
|
|
||||||
func NewIRC(nick, username string) *IRC {
|
func NewIRC(nick, username string) *IRC {
|
||||||
return &IRC{
|
return &IRC{
|
||||||
nick: nick,
|
nick: nick,
|
||||||
Username: username,
|
Username: username,
|
||||||
Realname: nick,
|
Realname: nick,
|
||||||
Messages: make(chan *Message, 32),
|
Messages: make(chan *Message, 32),
|
||||||
out: make(chan string, 32),
|
out: make(chan string, 32),
|
||||||
|
quit: make(chan struct{}),
|
||||||
|
reconnect: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) Connect(address string) error {
|
func (i *IRC) Connect(address string) {
|
||||||
if idx := strings.Index(address, ":"); idx < 0 {
|
if idx := strings.Index(address, ":"); idx < 0 {
|
||||||
i.Host = address
|
i.Host = address
|
||||||
|
|
||||||
@ -100,40 +107,16 @@ func (i *IRC) Connect(address string) error {
|
|||||||
i.Host = address[:idx]
|
i.Host = address[:idx]
|
||||||
}
|
}
|
||||||
i.Server = address
|
i.Server = address
|
||||||
|
i.dialer = &net.Dialer{Timeout: 10 * time.Second}
|
||||||
|
|
||||||
dialer := &net.Dialer{Timeout: 30 * time.Second}
|
go i.run()
|
||||||
|
}
|
||||||
|
|
||||||
if i.TLS {
|
func (i *IRC) Connected() bool {
|
||||||
if i.TLSConfig == nil {
|
i.lock.Lock()
|
||||||
i.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
defer i.lock.Unlock()
|
||||||
}
|
|
||||||
|
|
||||||
if conn, err := tls.DialWithDialer(dialer, "tcp", address, i.TLSConfig); err != nil {
|
return i.connected
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
i.conn = conn
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if conn, err := dialer.Dial("tcp", address); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
i.conn = conn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i.reader = bufio.NewReader(i.conn)
|
|
||||||
|
|
||||||
if i.Password != "" {
|
|
||||||
i.Pass(i.Password)
|
|
||||||
}
|
|
||||||
i.Nick(i.nick)
|
|
||||||
i.User(i.Username, i.Realname)
|
|
||||||
|
|
||||||
i.ready.Add(1)
|
|
||||||
go i.send()
|
|
||||||
go i.recv()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) Pass(password string) {
|
func (i *IRC) Pass(password string) {
|
||||||
@ -141,11 +124,11 @@ func (i *IRC) Pass(password string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) Nick(nick string) {
|
func (i *IRC) Nick(nick string) {
|
||||||
i.write("NICK " + nick)
|
i.Write("NICK " + nick)
|
||||||
|
|
||||||
i.nickLock.Lock()
|
i.lock.Lock()
|
||||||
i.nick = nick
|
i.nick = nick
|
||||||
i.nickLock.Unlock()
|
i.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) User(username, realname string) {
|
func (i *IRC) User(username, realname string) {
|
||||||
@ -162,9 +145,10 @@ func (i *IRC) Mode(target, modes, params string) {
|
|||||||
|
|
||||||
func (i *IRC) Quit() {
|
func (i *IRC) Quit() {
|
||||||
go func() {
|
go func() {
|
||||||
i.ready.Wait()
|
if i.Connected() {
|
||||||
i.write("QUIT")
|
i.write("QUIT")
|
||||||
i.conn.Close()
|
}
|
||||||
|
close(i.quit)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +189,8 @@ func (i *IRC) Away(message string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) GetNick() string {
|
func (i *IRC) GetNick() string {
|
||||||
i.nickLock.Lock()
|
i.lock.Lock()
|
||||||
defer i.nickLock.Unlock()
|
defer i.lock.Unlock()
|
||||||
|
|
||||||
return i.nick
|
return i.nick
|
||||||
}
|
}
|
||||||
@ -227,25 +211,117 @@ func (i *IRC) writef(format string, a ...interface{}) {
|
|||||||
fmt.Fprintf(i.conn, format+"\r\n", a...)
|
fmt.Fprintf(i.conn, format+"\r\n", a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) send() {
|
func (i *IRC) run() {
|
||||||
i.ready.Wait()
|
i.tryConnect()
|
||||||
for {
|
for {
|
||||||
_, err := i.conn.Write([]byte(<-i.out))
|
select {
|
||||||
if err != nil {
|
case <-i.quit:
|
||||||
|
i.close()
|
||||||
|
i.lock.Lock()
|
||||||
|
i.connected = false
|
||||||
|
i.lock.Unlock()
|
||||||
|
return
|
||||||
|
|
||||||
|
case <-i.reconnect:
|
||||||
|
i.reconnect = make(chan struct{})
|
||||||
|
i.once = sync.Once{}
|
||||||
|
i.tryConnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IRC) tryConnect() {
|
||||||
|
// TODO: backoff
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-i.quit:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
err := i.connect()
|
||||||
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *IRC) connect() error {
|
||||||
|
if i.TLS {
|
||||||
|
if i.TLSConfig == nil {
|
||||||
|
i.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn, err := tls.DialWithDialer(i.dialer, "tcp", i.Server, i.TLSConfig); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
i.conn = conn
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if conn, err := i.dialer.Dial("tcp", i.Server); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
i.conn = conn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i.lock.Lock()
|
||||||
|
i.connected = true
|
||||||
|
i.lock.Unlock()
|
||||||
|
|
||||||
|
i.reader = bufio.NewReader(i.conn)
|
||||||
|
|
||||||
|
if i.Password != "" {
|
||||||
|
i.Pass(i.Password)
|
||||||
|
}
|
||||||
|
i.write("NICK " + i.nick)
|
||||||
|
i.User(i.Username, i.Realname)
|
||||||
|
|
||||||
|
i.ready.Add(1)
|
||||||
|
go i.send()
|
||||||
|
go i.recv()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IRC) send() {
|
||||||
|
i.ready.Wait()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-i.quit:
|
||||||
|
return
|
||||||
|
|
||||||
|
case <-i.reconnect:
|
||||||
|
return
|
||||||
|
|
||||||
|
case msg := <-i.out:
|
||||||
|
_, err := i.conn.Write([]byte(msg))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (i *IRC) recv() {
|
func (i *IRC) recv() {
|
||||||
defer i.close()
|
defer i.conn.Close()
|
||||||
for {
|
for {
|
||||||
line, err := i.reader.ReadString('\n')
|
line, err := i.reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("IRC connection to", i.Server, "died")
|
i.lock.Lock()
|
||||||
|
i.connected = false
|
||||||
|
i.lock.Unlock()
|
||||||
|
i.once.Do(i.ready.Done)
|
||||||
|
close(i.reconnect)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-i.quit:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
msg := parseMessage(line)
|
msg := parseMessage(line)
|
||||||
i.Messages <- msg
|
i.Messages <- msg
|
||||||
|
|
||||||
@ -260,8 +336,9 @@ func (i *IRC) recv() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) close() {
|
func (i *IRC) close() {
|
||||||
i.conn.Close()
|
if i.Connected() {
|
||||||
i.once.Do(i.ready.Done)
|
i.once.Do(i.ready.Done)
|
||||||
|
}
|
||||||
close(i.out)
|
close(i.out)
|
||||||
close(i.Messages)
|
close(i.Messages)
|
||||||
}
|
}
|
||||||
@ -275,7 +352,12 @@ func parseMessage(line string) *Message {
|
|||||||
if strings.HasPrefix(line, ":") {
|
if strings.HasPrefix(line, ":") {
|
||||||
cmdStart = strings.Index(line, " ") + 1
|
cmdStart = strings.Index(line, " ") + 1
|
||||||
msg.Prefix = line[1 : cmdStart-1]
|
msg.Prefix = line[1 : cmdStart-1]
|
||||||
msg.Nick = parseNick(msg.Prefix)
|
|
||||||
|
if i := strings.Index(msg.Prefix, "!"); i > 0 {
|
||||||
|
msg.Nick = msg.Prefix[:i]
|
||||||
|
} else {
|
||||||
|
msg.Nick = msg.Prefix
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if i := strings.Index(line, " :"); i > 0 {
|
if i := strings.Index(line, " :"); i > 0 {
|
||||||
@ -295,10 +377,3 @@ func parseMessage(line string) *Message {
|
|||||||
|
|
||||||
return &msg
|
return &msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseNick(prefix string) string {
|
|
||||||
if i := strings.Index(prefix, "!"); i > 0 {
|
|
||||||
return prefix[:i]
|
|
||||||
}
|
|
||||||
return prefix
|
|
||||||
}
|
|
||||||
|
@ -69,22 +69,18 @@ func reconnect() {
|
|||||||
irc.Realname = server.Realname
|
irc.Realname = server.Realname
|
||||||
|
|
||||||
go func(server storage.Server) {
|
go func(server storage.Server) {
|
||||||
err := irc.Connect(server.Address)
|
irc.Connect(server.Address)
|
||||||
if err != nil {
|
session.setIRC(irc.Host, irc)
|
||||||
log.Println(err)
|
|
||||||
} else {
|
|
||||||
session.setIRC(irc.Host, irc)
|
|
||||||
|
|
||||||
go handleMessages(irc, session)
|
go handleMessages(irc, session)
|
||||||
|
|
||||||
var joining []string
|
var joining []string
|
||||||
for _, channel := range channels {
|
for _, channel := range channels {
|
||||||
if channel.Server == server.Address {
|
if channel.Server == server.Address {
|
||||||
joining = append(joining, channel.Name)
|
joining = append(joining, channel.Name)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
irc.Join(joining...)
|
|
||||||
}
|
}
|
||||||
|
irc.Join(joining...)
|
||||||
}(server)
|
}(server)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,24 +95,18 @@ func handleWS(ws *websocket.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := irc.Connect(data.Server)
|
irc.Connect(data.Server)
|
||||||
if err != nil {
|
go handleMessages(irc, session)
|
||||||
session.deleteIRC(irc.Host)
|
|
||||||
session.sendError(err, irc.Host)
|
|
||||||
log.Println(err)
|
|
||||||
} else {
|
|
||||||
go handleMessages(irc, session)
|
|
||||||
|
|
||||||
session.user.AddServer(storage.Server{
|
session.user.AddServer(storage.Server{
|
||||||
Name: data.Name,
|
Name: data.Name,
|
||||||
Address: irc.Host,
|
Address: irc.Host,
|
||||||
TLS: data.TLS,
|
TLS: data.TLS,
|
||||||
Password: data.Password,
|
Password: data.Password,
|
||||||
Nick: data.Nick,
|
Nick: data.Nick,
|
||||||
Username: data.Username,
|
Username: data.Username,
|
||||||
Realname: data.Realname,
|
Realname: data.Realname,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
log.Println(addr, "already connected to", data.Server)
|
log.Println(addr, "already connected to", data.Server)
|
||||||
|
Loading…
Reference in New Issue
Block a user