source: code/trunk/conn.go@ 321

Last change on this file since 321 was 315, checked in by contact, 5 years ago

Introduce ircConn

This interface will allow a conn to be backed by a websocket.

File size: 2.3 KB
Line 
1package soju
2
3import (
4 "fmt"
5 "net"
6 "sync"
7 "time"
8
9 "gopkg.in/irc.v3"
10)
11
12// ircConn is a generic IRC connection. It's similar to net.Conn but focuses on
13// reading and writing IRC messages.
14type ircConn interface {
15 ReadMessage() (*irc.Message, error)
16 WriteMessage(*irc.Message) error
17 Close() error
18 SetWriteDeadline(time.Time) error
19 SetReadDeadline(time.Time) error
20}
21
22func netIRCConn(c net.Conn) ircConn {
23 type netConn net.Conn
24 return struct {
25 *irc.Conn
26 netConn
27 }{irc.NewConn(c), c}
28}
29
30type conn struct {
31 conn ircConn
32 srv *Server
33 logger Logger
34
35 lock sync.Mutex
36 outgoing chan<- *irc.Message
37 closed bool
38}
39
40func newConn(srv *Server, ic ircConn, logger Logger) *conn {
41 outgoing := make(chan *irc.Message, 64)
42 c := &conn{
43 conn: ic,
44 srv: srv,
45 outgoing: outgoing,
46 logger: logger,
47 }
48
49 go func() {
50 for msg := range outgoing {
51 if c.srv.Debug {
52 c.logger.Printf("sent: %v", msg)
53 }
54 c.conn.SetWriteDeadline(time.Now().Add(writeTimeout))
55 if err := c.conn.WriteMessage(msg); err != nil {
56 c.logger.Printf("failed to write message: %v", err)
57 break
58 }
59 }
60 if err := c.conn.Close(); err != nil {
61 c.logger.Printf("failed to close connection: %v", err)
62 } else {
63 c.logger.Printf("connection closed")
64 }
65 // Drain the outgoing channel to prevent SendMessage from blocking
66 for range outgoing {
67 // This space is intentionally left blank
68 }
69 }()
70
71 c.logger.Printf("new connection")
72 return c
73}
74
75func (c *conn) isClosed() bool {
76 c.lock.Lock()
77 defer c.lock.Unlock()
78 return c.closed
79}
80
81// Close closes the connection. It is safe to call from any goroutine.
82func (c *conn) Close() error {
83 c.lock.Lock()
84 defer c.lock.Unlock()
85
86 if c.closed {
87 return fmt.Errorf("connection already closed")
88 }
89
90 err := c.conn.Close()
91 c.closed = true
92 close(c.outgoing)
93 return err
94}
95
96func (c *conn) ReadMessage() (*irc.Message, error) {
97 msg, err := c.conn.ReadMessage()
98 if err != nil {
99 return nil, err
100 }
101
102 if c.srv.Debug {
103 c.logger.Printf("received: %v", msg)
104 }
105
106 return msg, nil
107}
108
109// SendMessage queues a new outgoing message. It is safe to call from any
110// goroutine.
111//
112// If the connection is closed before the message is sent, SendMessage silently
113// drops the message.
114func (c *conn) SendMessage(msg *irc.Message) {
115 c.lock.Lock()
116 defer c.lock.Unlock()
117
118 if c.closed {
119 return
120 }
121 c.outgoing <- msg
122}
Note: See TracBrowser for help on using the repository browser.