source: code/trunk/conn.go@ 320

Last change on this file since 320 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
RevLine 
[210]1package soju
2
3import (
4 "fmt"
5 "net"
[280]6 "sync"
[210]7 "time"
8
9 "gopkg.in/irc.v3"
10)
11
[315]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
[210]30type conn struct {
[315]31 conn ircConn
[280]32 srv *Server
33 logger Logger
34
35 lock sync.Mutex
[210]36 outgoing chan<- *irc.Message
[280]37 closed bool
[210]38}
39
[315]40func newConn(srv *Server, ic ircConn, logger Logger) *conn {
[210]41 outgoing := make(chan *irc.Message, 64)
42 c := &conn{
[315]43 conn: ic,
[210]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 }
[315]54 c.conn.SetWriteDeadline(time.Now().Add(writeTimeout))
55 if err := c.conn.WriteMessage(msg); err != nil {
[210]56 c.logger.Printf("failed to write message: %v", err)
57 break
58 }
59 }
[315]60 if err := c.conn.Close(); err != nil {
[210]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 {
[280]76 c.lock.Lock()
77 defer c.lock.Unlock()
78 return c.closed
[210]79}
80
81// Close closes the connection. It is safe to call from any goroutine.
82func (c *conn) Close() error {
[280]83 c.lock.Lock()
84 defer c.lock.Unlock()
85
86 if c.closed {
[210]87 return fmt.Errorf("connection already closed")
88 }
[280]89
[315]90 err := c.conn.Close()
[280]91 c.closed = true
[210]92 close(c.outgoing)
[312]93 return err
[210]94}
95
96func (c *conn) ReadMessage() (*irc.Message, error) {
[315]97 msg, err := c.conn.ReadMessage()
[210]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.
[280]111//
112// If the connection is closed before the message is sent, SendMessage silently
113// drops the message.
[210]114func (c *conn) SendMessage(msg *irc.Message) {
[280]115 c.lock.Lock()
116 defer c.lock.Unlock()
117
118 if c.closed {
[210]119 return
120 }
121 c.outgoing <- msg
122}
Note: See TracBrowser for help on using the repository browser.