135 lines
2.7 KiB
Go
135 lines
2.7 KiB
Go
// Copyright 2014 Vic Demuzere
|
|
//
|
|
// Use of this source code is governed by the MIT license.
|
|
|
|
package irc
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"net"
|
|
"sync"
|
|
)
|
|
|
|
// Messages are delimited with CR and LF line endings,
|
|
// we're using the last one to split the stream. Both are removed
|
|
// during message parsing.
|
|
const delim byte = '\n'
|
|
|
|
var endline = []byte("\r\n")
|
|
|
|
// A Conn represents an IRC network protocol connection.
|
|
// It consists of an Encoder and Decoder to manage I/O.
|
|
type Conn struct {
|
|
Encoder
|
|
Decoder
|
|
|
|
conn io.ReadWriteCloser
|
|
}
|
|
|
|
// NewConn returns a new Conn using rwc for I/O.
|
|
func NewConn(rwc io.ReadWriteCloser) *Conn {
|
|
return &Conn{
|
|
Encoder: Encoder{
|
|
writer: rwc,
|
|
},
|
|
Decoder: Decoder{
|
|
reader: bufio.NewReader(rwc),
|
|
},
|
|
conn: rwc,
|
|
}
|
|
}
|
|
|
|
// Dial connects to the given address using net.Dial and
|
|
// then returns a new Conn for the connection.
|
|
func Dial(addr string) (*Conn, error) {
|
|
c, err := net.Dial("tcp", addr)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return NewConn(c), nil
|
|
}
|
|
|
|
// Close closes the underlying ReadWriteCloser.
|
|
func (c *Conn) Close() error {
|
|
return c.conn.Close()
|
|
}
|
|
|
|
// A Decoder reads Message objects from an input stream.
|
|
type Decoder struct {
|
|
reader *bufio.Reader
|
|
line string
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// NewDecoder returns a new Decoder that reads from r.
|
|
func NewDecoder(r io.Reader) *Decoder {
|
|
return &Decoder{
|
|
reader: bufio.NewReader(r),
|
|
}
|
|
}
|
|
|
|
// Decode attempts to read a single Message from the stream.
|
|
//
|
|
// Returns a non-nil error if the read failed.
|
|
func (dec *Decoder) Decode() (m *Message, err error) {
|
|
|
|
dec.mu.Lock()
|
|
dec.line, err = dec.reader.ReadString(delim)
|
|
dec.mu.Unlock()
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ParseMessage(dec.line), nil
|
|
}
|
|
|
|
// An Encoder writes Message objects to an output stream.
|
|
type Encoder struct {
|
|
writer io.Writer
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// NewEncoder returns a new Encoder that writes to w.
|
|
func NewEncoder(w io.Writer) *Encoder {
|
|
return &Encoder{
|
|
writer: w,
|
|
}
|
|
}
|
|
|
|
// Encode writes the IRC encoding of m to the stream.
|
|
//
|
|
// This method may be used from multiple goroutines.
|
|
//
|
|
// Returns an non-nil error if the write to the underlying stream stopped early.
|
|
func (enc *Encoder) Encode(m *Message) (err error) {
|
|
|
|
_, err = enc.Write(m.Bytes())
|
|
|
|
return
|
|
}
|
|
|
|
// Write writes len(p) bytes from p followed by CR+LF.
|
|
//
|
|
// This method can be used simultaneously from multiple goroutines,
|
|
// it guarantees to serialize access. However, writing a single IRC message
|
|
// using multiple Write calls will cause corruption.
|
|
func (enc *Encoder) Write(p []byte) (n int, err error) {
|
|
|
|
enc.mu.Lock()
|
|
n, err = enc.writer.Write(p)
|
|
|
|
if err != nil {
|
|
enc.mu.Unlock()
|
|
return
|
|
}
|
|
|
|
_, err = enc.writer.Write(endline)
|
|
enc.mu.Unlock()
|
|
|
|
return
|
|
}
|