move connection logic into conn.go
This commit is contained in:
parent
94cceb64e7
commit
1090fd92a8
85
client.go
85
client.go
@ -12,13 +12,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/proxy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client contains all of the information necessary to run a single IRC
|
// Client contains all of the information necessary to run a single IRC
|
||||||
@ -190,19 +186,6 @@ func New(config Config) *Client {
|
|||||||
|
|
||||||
// Connect attempts to connect to the given IRC server
|
// Connect attempts to connect to the given IRC server
|
||||||
func (c *Client) Connect() error {
|
func (c *Client) Connect() error {
|
||||||
// Sanity check a few options.
|
|
||||||
if c.Config.Server == "" {
|
|
||||||
return errors.New("invalid server specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Config.Port < 21 || c.Config.Port > 65535 {
|
|
||||||
return errors.New("invalid port (21-65535)")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !IsValidNick(c.Config.Nick) || !IsValidUser(c.Config.User) {
|
|
||||||
return errors.New("invalid nickname or user")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up any old running stuff.
|
// Clean up any old running stuff.
|
||||||
c.cleanup(false)
|
c.cleanup(false)
|
||||||
|
|
||||||
@ -213,71 +196,17 @@ func (c *Client) Connect() error {
|
|||||||
// Reset the state.
|
// Reset the state.
|
||||||
c.state = newState()
|
c.state = newState()
|
||||||
|
|
||||||
|
// Validate info, and actually make the connection.
|
||||||
c.debug.Printf("connecting to %s...", c.Server())
|
c.debug.Printf("connecting to %s...", c.Server())
|
||||||
|
conn, err := newConn(c.Config, c.Server())
|
||||||
var conn net.Conn
|
if err != nil {
|
||||||
var err error
|
return err
|
||||||
|
|
||||||
dialer := &net.Dialer{Timeout: 5 * time.Second}
|
|
||||||
|
|
||||||
if c.Config.Bind != "" {
|
|
||||||
var local *net.TCPAddr
|
|
||||||
local, err = net.ResolveTCPAddr("tcp", c.Config.Bind+":0")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to resolve bind address %s: %s", c.Config.Bind, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dialer.LocalAddr = local
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Config.Proxy != "" {
|
|
||||||
var proxyUri *url.URL
|
|
||||||
var proxyDialer proxy.Dialer
|
|
||||||
|
|
||||||
proxyUri, err = url.Parse(c.Config.Proxy)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to use proxy %q: %s", c.Config.Proxy, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyDialer, err = proxy.FromURL(proxyUri, dialer)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to use proxy %q: %s", c.Config.Proxy, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err = proxyDialer.Dial("tcp", c.Server())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to use proxy %q: %s", c.Config.Proxy, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
conn, err = dialer.Dial("tcp", c.Server())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to connect to %q: %s", c.Server(), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Config.SSL {
|
|
||||||
var sslConf *tls.Config
|
|
||||||
|
|
||||||
if c.Config.TLSConfig == nil {
|
|
||||||
sslConf = &tls.Config{ServerName: c.Config.Server}
|
|
||||||
} else {
|
|
||||||
sslConf = c.Config.TLSConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsConn := tls.Client(conn, sslConf)
|
|
||||||
if err = tlsConn.Handshake(); err != nil {
|
|
||||||
return fmt.Errorf("failed handshake during tls conn to %q: %s", c.Server(), err)
|
|
||||||
}
|
|
||||||
conn = tlsConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.state.mu.Lock()
|
c.state.mu.Lock()
|
||||||
c.state.conn = conn
|
c.state.conn = conn
|
||||||
c.state.mu.Unlock()
|
c.state.mu.Unlock()
|
||||||
|
|
||||||
c.state.reader = newDecoder(c.state.conn)
|
|
||||||
c.state.writer = newEncoder(c.state.conn)
|
|
||||||
|
|
||||||
// Send a virtual event allowing hooks for successful socket connection.
|
// Send a virtual event allowing hooks for successful socket connection.
|
||||||
c.Events <- &Event{Command: INITIALIZED, Trailing: c.Server()}
|
c.Events <- &Event{Command: INITIALIZED, Trailing: c.Server()}
|
||||||
|
|
||||||
@ -451,8 +380,8 @@ func (c *Client) readLoop(ctx context.Context) {
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
c.state.conn.SetDeadline(time.Now().Add(300 * time.Second))
|
c.state.conn.lconn.SetDeadline(time.Now().Add(300 * time.Second))
|
||||||
event, err = c.state.reader.Decode()
|
event, err = c.state.conn.Decode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Attempt a reconnect (if applicable). If it fails, send
|
// Attempt a reconnect (if applicable). If it fails, send
|
||||||
// the error to c.Config.HandleError to be dealt with, if
|
// the error to c.Config.HandleError to be dealt with, if
|
||||||
@ -569,7 +498,7 @@ func (c *Client) write(event *Event) error {
|
|||||||
c.debug.Print("> ", StripRaw(event.String()))
|
c.debug.Print("> ", StripRaw(event.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.state.writer.Encode(event)
|
return c.state.conn.Encode(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uptime is the time at which the client successfully connected to the
|
// Uptime is the time at which the client successfully connected to the
|
||||||
|
99
conn.go
99
conn.go
@ -6,8 +6,16 @@ package girc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Messages are delimited with CR and LF line endings, we're using the last
|
// Messages are delimited with CR and LF line endings, we're using the last
|
||||||
@ -22,12 +30,89 @@ type ircConn struct {
|
|||||||
ircEncoder
|
ircEncoder
|
||||||
ircDecoder
|
ircDecoder
|
||||||
|
|
||||||
c io.ReadWriteCloser
|
lconn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConn(conf Config, addr string) (*ircConn, error) {
|
||||||
|
// Sanity check a few options.
|
||||||
|
if conf.Server == "" {
|
||||||
|
return nil, errors.New("invalid server specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Port < 21 || conf.Port > 65535 {
|
||||||
|
return nil, errors.New("invalid port (21-65535)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsValidNick(conf.Nick) || !IsValidUser(conf.User) {
|
||||||
|
return nil, errors.New("invalid nickname or user")
|
||||||
|
}
|
||||||
|
|
||||||
|
var conn net.Conn
|
||||||
|
var err error
|
||||||
|
|
||||||
|
dialer := &net.Dialer{Timeout: 5 * time.Second}
|
||||||
|
|
||||||
|
if conf.Bind != "" {
|
||||||
|
var local *net.TCPAddr
|
||||||
|
local, err = net.ResolveTCPAddr("tcp", conf.Bind+":0")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to resolve bind address %s: %s", conf.Bind, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer.LocalAddr = local
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Proxy != "" {
|
||||||
|
var proxyUri *url.URL
|
||||||
|
var proxyDialer proxy.Dialer
|
||||||
|
|
||||||
|
proxyUri, err = url.Parse(conf.Proxy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to use proxy %q: %s", conf.Proxy, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyDialer, err = proxy.FromURL(proxyUri, dialer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to use proxy %q: %s", conf.Proxy, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err = proxyDialer.Dial("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to connect to proxy %q: %s", conf.Proxy, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
conn, err = dialer.Dial("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to connect to %q: %s", addr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.SSL {
|
||||||
|
var sslConf *tls.Config
|
||||||
|
|
||||||
|
if conf.TLSConfig == nil {
|
||||||
|
sslConf = &tls.Config{ServerName: conf.Server}
|
||||||
|
} else {
|
||||||
|
sslConf = conf.TLSConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConn := tls.Client(conn, sslConf)
|
||||||
|
if err = tlsConn.Handshake(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed handshake during tls conn to %q: %s", addr, err)
|
||||||
|
}
|
||||||
|
conn = tlsConn
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ircConn{
|
||||||
|
ircEncoder: ircEncoder{writer: conn},
|
||||||
|
ircDecoder: ircDecoder{reader: bufio.NewReader(conn)},
|
||||||
|
lconn: conn,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the underlying ReadWriteCloser.
|
// Close closes the underlying ReadWriteCloser.
|
||||||
func (c *ircConn) Close() error {
|
func (c *ircConn) Close() error {
|
||||||
return c.c.Close()
|
return c.lconn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ircDecoder reads Event objects from an input stream.
|
// ircDecoder reads Event objects from an input stream.
|
||||||
@ -37,11 +122,6 @@ type ircDecoder struct {
|
|||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// newDecoder returns a new Decoder that reads from r.
|
|
||||||
func newDecoder(r io.Reader) *ircDecoder {
|
|
||||||
return &ircDecoder{reader: bufio.NewReader(r)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode attempts to read a single Event from the stream, returns non-nil
|
// Decode attempts to read a single Event from the stream, returns non-nil
|
||||||
// error if read failed. event may be nil if unparseable.
|
// error if read failed. event may be nil if unparseable.
|
||||||
func (dec *ircDecoder) Decode() (event *Event, err error) {
|
func (dec *ircDecoder) Decode() (event *Event, err error) {
|
||||||
@ -62,11 +142,6 @@ type ircEncoder struct {
|
|||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// newEncoder returns a new Encoder that writes to w.
|
|
||||||
func newEncoder(w io.Writer) *ircEncoder {
|
|
||||||
return &ircEncoder{writer: w}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode writes the IRC encoding of m to the stream. Goroutine safe.
|
// Encode writes the IRC encoding of m to the stream. Goroutine safe.
|
||||||
// returns non-nil error if the write to the underlying stream stopped early.
|
// returns non-nil error if the write to the underlying stream stopped early.
|
||||||
func (enc *ircEncoder) Encode(e *Event) (err error) {
|
func (enc *ircEncoder) Encode(e *Event) (err error) {
|
||||||
|
7
state.go
7
state.go
@ -6,7 +6,6 @@ package girc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -19,12 +18,8 @@ type state struct {
|
|||||||
// corruption.
|
// corruption.
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
||||||
// reader is the socket buffer reader from the IRC server.
|
|
||||||
reader *ircDecoder
|
|
||||||
// reader is the socket buffer write to the IRC server.
|
|
||||||
writer *ircEncoder
|
|
||||||
// conn is a net.Conn reference to the IRC server.
|
// conn is a net.Conn reference to the IRC server.
|
||||||
conn net.Conn
|
conn *ircConn
|
||||||
|
|
||||||
// lastWrite is used ot keep track of when we last wrote to the server.
|
// lastWrite is used ot keep track of when we last wrote to the server.
|
||||||
lastWrite time.Time
|
lastWrite time.Time
|
||||||
|
Loading…
Reference in New Issue
Block a user