source: code/trunk/server.go@ 73

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

Allow clients to specify an upstream name in their username

File size: 4.7 KB
RevLine 
[1]1package jounce
2
3import (
4 "fmt"
[37]5 "log"
[1]6 "net"
[24]7 "sync"
[67]8 "time"
[1]9
10 "gopkg.in/irc.v3"
11)
12
[67]13// TODO: make configurable
14var keepAlivePeriod = time.Minute
[71]15var retryConnectMinDelay = time.Minute
[67]16
17func setKeepAlive(c net.Conn) error {
18 tcpConn, ok := c.(*net.TCPConn)
19 if !ok {
20 return fmt.Errorf("cannot enable keep-alive on a non-TCP connection")
21 }
22 if err := tcpConn.SetKeepAlive(true); err != nil {
23 return err
24 }
25 return tcpConn.SetKeepAlivePeriod(keepAlivePeriod)
26}
27
[9]28type Logger interface {
29 Print(v ...interface{})
30 Printf(format string, v ...interface{})
31}
32
[21]33type prefixLogger struct {
34 logger Logger
35 prefix string
36}
37
38var _ Logger = (*prefixLogger)(nil)
39
40func (l *prefixLogger) Print(v ...interface{}) {
41 v = append([]interface{}{l.prefix}, v...)
42 l.logger.Print(v...)
43}
44
45func (l *prefixLogger) Printf(format string, v ...interface{}) {
46 v = append([]interface{}{l.prefix}, v...)
47 l.logger.Printf("%v"+format, v...)
48}
49
[37]50type user struct {
51 username string
52 srv *Server
53
[40]54 lock sync.Mutex
55 upstreamConns []*upstreamConn
56 downstreamConns []*downstreamConn
[37]57}
58
[57]59func newUser(srv *Server, username string) *user {
60 return &user{
61 username: username,
62 srv: srv,
63 }
64}
65
[39]66func (u *user) forEachUpstream(f func(uc *upstreamConn)) {
67 u.lock.Lock()
68 for _, uc := range u.upstreamConns {
[41]69 if !uc.registered || uc.closed {
[39]70 continue
71 }
72 f(uc)
73 }
74 u.lock.Unlock()
75}
76
[40]77func (u *user) forEachDownstream(f func(dc *downstreamConn)) {
78 u.lock.Lock()
79 for _, dc := range u.downstreamConns {
80 f(dc)
81 }
82 u.lock.Unlock()
83}
84
[73]85func (u *user) getChannel(name string, upstream *Upstream) (*upstreamChannel, error) {
[46]86 var channel *upstreamChannel
87 var err error
88 u.forEachUpstream(func(uc *upstreamConn) {
89 if err != nil {
90 return
91 }
[73]92 if upstream != nil && uc.upstream != upstream {
93 return
94 }
[46]95 if ch, ok := uc.channels[name]; ok {
96 if channel != nil {
97 err = fmt.Errorf("ambiguous channel name %q", name)
98 } else {
99 channel = ch
100 }
101 }
102 })
103 if channel == nil {
[49]104 return nil, ircError{&irc.Message{
105 Command: irc.ERR_NOSUCHCHANNEL,
106 Params: []string{name, "No such channel"},
107 }}
[46]108 }
109 return channel, nil
110}
111
[10]112type Upstream struct {
113 Addr string
114 Nick string
115 Username string
116 Realname string
[19]117 Channels []string
[10]118}
119
120type Server struct {
121 Hostname string
122 Logger Logger
[50]123 RingCap int
[64]124 Debug bool
[10]125 Upstreams []Upstream // TODO: per-user
[22]126
[24]127 lock sync.Mutex
[37]128 users map[string]*user
[22]129 downstreamConns []*downstreamConn
[10]130}
131
[37]132func NewServer() *Server {
133 return &Server{
[50]134 Logger: log.New(log.Writer(), "", log.LstdFlags),
135 RingCap: 4096,
136 users: make(map[string]*user),
[37]137 }
138}
139
[5]140func (s *Server) prefix() *irc.Prefix {
141 return &irc.Prefix{Name: s.Hostname}
142}
143
[71]144func (s *Server) runUpstream(u *user, upstream *Upstream) {
145 var lastTry time.Time
146 for {
147 if dur := time.Now().Sub(lastTry); dur < retryConnectMinDelay {
148 delay := retryConnectMinDelay - dur
149 s.Logger.Printf("waiting %v before trying to reconnect to %q", delay.Truncate(time.Second), upstream.Addr)
150 time.Sleep(delay)
151 }
152 lastTry = time.Now()
153
154 uc, err := connectToUpstream(u, upstream)
155 if err != nil {
156 s.Logger.Printf("failed to connect to upstream server %q: %v", upstream.Addr, err)
157 continue
158 }
159
160 uc.register()
161
162 u.lock.Lock()
163 u.upstreamConns = append(u.upstreamConns, uc)
164 u.lock.Unlock()
165
166 if err := uc.readMessages(); err != nil {
167 uc.logger.Printf("failed to handle messages: %v", err)
168 }
169 uc.Close()
170
171 u.lock.Lock()
172 for i := range u.upstreamConns {
173 if u.upstreamConns[i] == uc {
174 u.upstreamConns = append(u.upstreamConns[:i], u.upstreamConns[i+1:]...)
175 break
176 }
177 }
178 u.lock.Unlock()
179 }
180}
181
[10]182func (s *Server) Run() {
[37]183 // TODO: multi-user
[57]184 u := newUser(s, "jounce")
[37]185
186 s.lock.Lock()
187 s.users[u.username] = u
188 s.lock.Unlock()
189
[10]190 for i := range s.Upstreams {
[71]191 go s.runUpstream(u, &s.Upstreams[i])
[10]192 }
193}
194
[38]195func (s *Server) getUser(name string) *user {
196 s.lock.Lock()
197 u := s.users[name]
198 s.lock.Unlock()
199 return u
200}
201
[73]202func (s *Server) getUpstream(name string) *Upstream {
203 for i, upstream := range s.Upstreams {
204 if upstream.Addr == name {
205 return &s.Upstreams[i]
206 }
207 }
208 return nil
209}
210
[3]211func (s *Server) Serve(ln net.Listener) error {
[1]212 for {
[22]213 netConn, err := ln.Accept()
[1]214 if err != nil {
215 return fmt.Errorf("failed to accept connection: %v", err)
216 }
217
[67]218 setKeepAlive(netConn)
219
[65]220 dc := newDownstreamConn(s, netConn)
[1]221 go func() {
[24]222 s.lock.Lock()
[65]223 s.downstreamConns = append(s.downstreamConns, dc)
[24]224 s.lock.Unlock()
[29]225
[65]226 if err := dc.readMessages(); err != nil {
227 dc.logger.Printf("failed to handle messages: %v", err)
[1]228 }
[65]229 dc.Close()
[29]230
[24]231 s.lock.Lock()
[65]232 for i := range s.downstreamConns {
233 if s.downstreamConns[i] == dc {
[24]234 s.downstreamConns = append(s.downstreamConns[:i], s.downstreamConns[i+1:]...)
235 break
236 }
237 }
238 s.lock.Unlock()
[1]239 }()
240 }
241}
Note: See TracBrowser for help on using the repository browser.