source: code/trunk/conn.go@ 312

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

Close net.Conn in conn.Close

Close the connection in conn.Close. This ensures the connection isn't
still alive after conn.Close, which would cause issues when
disconnecting and reconnecting quickly to an upstream server.

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
12func setKeepAlive(c net.Conn) error {
13 tcpConn, ok := c.(*net.TCPConn)
14 if !ok {
15 return fmt.Errorf("cannot enable keep-alive on a non-TCP connection")
16 }
17 if err := tcpConn.SetKeepAlive(true); err != nil {
18 return err
19 }
20 return tcpConn.SetKeepAlivePeriod(keepAlivePeriod)
21}
22
23type conn struct {
24 net net.Conn
25 irc *irc.Conn
26 srv *Server
27 logger Logger
28
29 lock sync.Mutex
30 outgoing chan<- *irc.Message
31 closed bool
32}
33
34func newConn(srv *Server, netConn net.Conn, logger Logger) *conn {
35 setKeepAlive(netConn)
36
37 outgoing := make(chan *irc.Message, 64)
38 c := &conn{
39 net: netConn,
40 irc: irc.NewConn(netConn),
41 srv: srv,
42 outgoing: outgoing,
43 logger: logger,
44 }
45
46 go func() {
47 for msg := range outgoing {
48 if c.srv.Debug {
49 c.logger.Printf("sent: %v", msg)
50 }
51 c.net.SetWriteDeadline(time.Now().Add(writeTimeout))
52 if err := c.irc.WriteMessage(msg); err != nil {
53 c.logger.Printf("failed to write message: %v", err)
54 break
55 }
56 }
57 if err := c.net.Close(); err != nil {
58 c.logger.Printf("failed to close connection: %v", err)
59 } else {
60 c.logger.Printf("connection closed")
61 }
62 // Drain the outgoing channel to prevent SendMessage from blocking
63 for range outgoing {
64 // This space is intentionally left blank
65 }
66 }()
67
68 c.logger.Printf("new connection")
69 return c
70}
71
72func (c *conn) isClosed() bool {
73 c.lock.Lock()
74 defer c.lock.Unlock()
75 return c.closed
76}
77
78// Close closes the connection. It is safe to call from any goroutine.
79func (c *conn) Close() error {
80 c.lock.Lock()
81 defer c.lock.Unlock()
82
83 if c.closed {
84 return fmt.Errorf("connection already closed")
85 }
86
87 err := c.net.Close()
88 c.closed = true
89 close(c.outgoing)
90 return err
91}
92
93func (c *conn) ReadMessage() (*irc.Message, error) {
94 msg, err := c.irc.ReadMessage()
95 if err != nil {
96 return nil, err
97 }
98
99 if c.srv.Debug {
100 c.logger.Printf("received: %v", msg)
101 }
102
103 return msg, nil
104}
105
106// SendMessage queues a new outgoing message. It is safe to call from any
107// goroutine.
108//
109// If the connection is closed before the message is sent, SendMessage silently
110// drops the message.
111func (c *conn) SendMessage(msg *irc.Message) {
112 c.lock.Lock()
113 defer c.lock.Unlock()
114
115 if c.closed {
116 return
117 }
118 c.outgoing <- msg
119}
Note: See TracBrowser for help on using the repository browser.