forked from lug/matterbridge
		
	
		
			
				
	
	
		
			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
 | |
| }
 | 
