source: code/trunk/downstream.go@ 26

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

Write to downstream conn in dedicated goroutine

File size: 4.2 KB
RevLine 
[13]1package jounce
2
3import (
4 "fmt"
5 "io"
6 "net"
7
8 "gopkg.in/irc.v3"
9)
10
11type ircError struct {
12 Message *irc.Message
13}
14
15func newUnknownCommandError(cmd string) ircError {
16 return ircError{&irc.Message{
17 Command: irc.ERR_UNKNOWNCOMMAND,
18 Params: []string{
19 "*",
20 cmd,
21 "Unknown command",
22 },
23 }}
24}
25
26func newNeedMoreParamsError(cmd string) ircError {
27 return ircError{&irc.Message{
28 Command: irc.ERR_NEEDMOREPARAMS,
29 Params: []string{
30 "*",
31 cmd,
32 "Not enough parameters",
33 },
34 }}
35}
36
37func (err ircError) Error() string {
38 return err.Message.String()
39}
40
41type downstreamConn struct {
[26]42 net net.Conn
43 irc *irc.Conn
44 srv *Server
45 logger Logger
46 messages chan<- *irc.Message
[22]47
[13]48 registered bool
49 closed bool
50 nick string
51 username string
52 realname string
53}
54
[22]55func newDownstreamConn(srv *Server, netConn net.Conn) *downstreamConn {
[26]56 msgs := make(chan *irc.Message, 64)
57 conn := &downstreamConn{
58 net: netConn,
59 irc: irc.NewConn(netConn),
60 srv: srv,
61 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
62 messages: msgs,
[22]63 }
[26]64
65 go func() {
66 for msg := range msgs {
67 if err := conn.irc.WriteMessage(msg); err != nil {
68 conn.logger.Printf("failed to write message: %v", err)
69 }
70 }
71 }()
72
73 return conn
[22]74}
75
76func (c *downstreamConn) readMessages() error {
77 c.logger.Printf("new connection")
78 defer c.Close()
79
80 for {
81 msg, err := c.irc.ReadMessage()
82 if err == io.EOF {
83 break
84 } else if err != nil {
85 return fmt.Errorf("failed to read IRC command: %v", err)
86 }
87
88 err = c.handleMessage(msg)
89 if ircErr, ok := err.(ircError); ok {
90 ircErr.Message.Prefix = c.srv.prefix()
[26]91 c.messages <- ircErr.Message
[22]92 } else if err != nil {
93 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
94 }
95
96 if c.closed {
97 return nil
98 }
99 }
100
101 return c.Close()
102}
103
[13]104func (c *downstreamConn) Close() error {
[26]105 if c.closed {
106 return fmt.Errorf("downstream connection already closed")
107 }
[13]108 if err := c.net.Close(); err != nil {
109 return err
110 }
[26]111 close(c.messages)
[13]112 c.closed = true
113 return nil
114}
115
[26]116func (c *downstreamConn) WriteMessage(msg *irc.Message) {
[13]117 msg.Prefix = c.srv.prefix()
[26]118 c.messages <- msg
[13]119}
120
121func (c *downstreamConn) handleMessage(msg *irc.Message) error {
122 switch msg.Command {
123 case "PING":
124 // TODO: handle params
[26]125 c.WriteMessage(&irc.Message{
[13]126 Command: "PONG",
127 Params: []string{c.srv.Hostname},
128 })
[26]129 return nil
[13]130 default:
131 if c.registered {
132 return c.handleMessageRegistered(msg)
133 } else {
134 return c.handleMessageUnregistered(msg)
135 }
136 }
137}
138
139func (c *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
140 switch msg.Command {
141 case "NICK":
142 if len(msg.Params) != 1 {
143 return newNeedMoreParamsError(msg.Command)
144 }
145 c.nick = msg.Params[0]
146 case "USER":
147 if len(msg.Params) != 4 {
148 return newNeedMoreParamsError(msg.Command)
149 }
150 c.username = "~" + msg.Params[0]
151 c.realname = msg.Params[3]
152 case "QUIT":
153 return c.Close()
154 default:
[22]155 c.logger.Printf("unhandled message: %v", msg)
[13]156 return newUnknownCommandError(msg.Command)
157 }
158 if c.username != "" && c.nick != "" {
159 return c.register()
160 }
161 return nil
162}
163
164func (c *downstreamConn) register() error {
165 c.registered = true
166
[26]167 c.WriteMessage(&irc.Message{
[13]168 Command: irc.RPL_WELCOME,
169 Params: []string{c.nick, "Welcome to jounce, " + c.nick},
170 })
171
[26]172 c.WriteMessage(&irc.Message{
[13]173 Command: irc.RPL_YOURHOST,
174 Params: []string{c.nick, "Your host is " + c.srv.Hostname},
175 })
176
[26]177 c.WriteMessage(&irc.Message{
[13]178 Command: irc.RPL_CREATED,
179 Params: []string{c.nick, "This server was created <datetime>"}, // TODO
180 })
181
[26]182 c.WriteMessage(&irc.Message{
[13]183 Command: irc.RPL_MYINFO,
[15]184 Params: []string{c.nick, c.srv.Hostname, "unknown", "aiwroO", "OovaimnqpsrtklbeI"},
[13]185 })
186
[26]187 c.WriteMessage(&irc.Message{
[13]188 Command: irc.ERR_NOMOTD,
189 Params: []string{c.nick, "No MOTD"},
190 })
191
192 return nil
193}
194
195func (c *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
196 switch msg.Command {
197 case "NICK", "USER":
198 return ircError{&irc.Message{
199 Command: irc.ERR_ALREADYREGISTERED,
200 Params: []string{
201 c.nick,
202 "You may not reregister",
203 },
204 }}
205 case "QUIT":
206 return c.Close()
207 default:
[22]208 c.logger.Printf("unhandled message: %v", msg)
[13]209 return newUnknownCommandError(msg.Command)
210 }
211}
Note: See TracBrowser for help on using the repository browser.