github.com/v2fly/tools@v0.100.0/internal/jsonrpc2_v2/serve.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package jsonrpc2 6 7 import ( 8 "context" 9 "io" 10 "sync" 11 "time" 12 13 errors "golang.org/x/xerrors" 14 ) 15 16 // Listener is implemented by protocols to accept new inbound connections. 17 type Listener interface { 18 // Accept an inbound connection to a server. 19 // It must block until an inbound connection is made, or the listener is 20 // shut down. 21 Accept(context.Context) (io.ReadWriteCloser, error) 22 23 // Close is used to ask a listener to stop accepting new connections. 24 Close() error 25 26 // Dialer returns a dialer that can be used to connect to this listener 27 // locally. 28 // If a listener does not implement this it will return a nil. 29 Dialer() Dialer 30 } 31 32 // Dialer is used by clients to dial a server. 33 type Dialer interface { 34 // Dial returns a new communication byte stream to a listening server. 35 Dial(ctx context.Context) (io.ReadWriteCloser, error) 36 } 37 38 // Server is a running server that is accepting incoming connections. 39 type Server struct { 40 listener Listener 41 binder Binder 42 async async 43 } 44 45 // Dial uses the dialer to make a new connection, wraps the returned 46 // reader and writer using the framer to make a stream, and then builds 47 // a connection on top of that stream using the binder. 48 func Dial(ctx context.Context, dialer Dialer, binder Binder) (*Connection, error) { 49 // dial a server 50 rwc, err := dialer.Dial(ctx) 51 if err != nil { 52 return nil, err 53 } 54 return newConnection(ctx, rwc, binder) 55 } 56 57 // Serve starts a new server listening for incoming connections and returns 58 // it. 59 // This returns a fully running and connected server, it does not block on 60 // the listener. 61 // You can call Wait to block on the server, or Shutdown to get the sever to 62 // terminate gracefully. 63 // To notice incoming connections, use an intercepting Binder. 64 func Serve(ctx context.Context, listener Listener, binder Binder) (*Server, error) { 65 server := &Server{ 66 listener: listener, 67 binder: binder, 68 } 69 server.async.init() 70 go server.run(ctx) 71 return server, nil 72 } 73 74 // Wait returns only when the server has shut down. 75 func (s *Server) Wait() error { 76 return s.async.wait() 77 } 78 79 // run accepts incoming connections from the listener, 80 // If IdleTimeout is non-zero, run exits after there are no clients for this 81 // duration, otherwise it exits only on error. 82 func (s *Server) run(ctx context.Context) { 83 defer s.async.done() 84 var activeConns []*Connection 85 for { 86 // we never close the accepted connection, we rely on the other end 87 // closing or the socket closing itself naturally 88 rwc, err := s.listener.Accept(ctx) 89 if err != nil { 90 if !isClosingError(err) { 91 s.async.setError(err) 92 } 93 // we are done generating new connections for good 94 break 95 } 96 97 // see if any connections were closed while we were waiting 98 activeConns = onlyActive(activeConns) 99 100 // a new inbound connection, 101 conn, err := newConnection(ctx, rwc, s.binder) 102 if err != nil { 103 if !isClosingError(err) { 104 s.async.setError(err) 105 } 106 continue 107 } 108 activeConns = append(activeConns, conn) 109 } 110 111 // wait for all active conns to finish 112 for _, c := range activeConns { 113 c.Wait() 114 } 115 } 116 117 func onlyActive(conns []*Connection) []*Connection { 118 i := 0 119 for _, c := range conns { 120 if !c.async.isDone() { 121 conns[i] = c 122 i++ 123 } 124 } 125 // trim the slice down 126 return conns[:i] 127 } 128 129 // isClosingError reports if the error occurs normally during the process of 130 // closing a network connection. It uses imperfect heuristics that err on the 131 // side of false negatives, and should not be used for anything critical. 132 func isClosingError(err error) bool { 133 if err == nil { 134 return false 135 } 136 // fully unwrap the error, so the following tests work 137 for wrapped := err; wrapped != nil; wrapped = errors.Unwrap(err) { 138 err = wrapped 139 } 140 141 // was it based on an EOF error? 142 if err == io.EOF { 143 return true 144 } 145 146 // Per https://github.com/golang/go/issues/4373, this error string should not 147 // change. This is not ideal, but since the worst that could happen here is 148 // some superfluous logging, it is acceptable. 149 if err.Error() == "use of closed network connection" { 150 return true 151 } 152 153 return false 154 } 155 156 // NewIdleListener wraps a listener with an idle timeout. 157 // When there are no active connections for at least the timeout duration a 158 // call to accept will fail with ErrIdleTimeout. 159 func NewIdleListener(timeout time.Duration, wrap Listener) Listener { 160 l := &idleListener{ 161 timeout: timeout, 162 wrapped: wrap, 163 newConns: make(chan *idleCloser), 164 closed: make(chan struct{}), 165 wasTimeout: make(chan struct{}), 166 } 167 go l.run() 168 return l 169 } 170 171 type idleListener struct { 172 wrapped Listener 173 timeout time.Duration 174 newConns chan *idleCloser 175 closed chan struct{} 176 wasTimeout chan struct{} 177 closeOnce sync.Once 178 } 179 180 type idleCloser struct { 181 wrapped io.ReadWriteCloser 182 closed chan struct{} 183 closeOnce sync.Once 184 } 185 186 func (c *idleCloser) Read(p []byte) (int, error) { 187 n, err := c.wrapped.Read(p) 188 if err != nil && isClosingError(err) { 189 c.closeOnce.Do(func() { close(c.closed) }) 190 } 191 return n, err 192 } 193 194 func (c *idleCloser) Write(p []byte) (int, error) { 195 // we do not close on write failure, we rely on the wrapped writer to do that 196 // if it is appropriate, which we will detect in the next read. 197 return c.wrapped.Write(p) 198 } 199 200 func (c *idleCloser) Close() error { 201 // we rely on closing the wrapped stream to signal to the next read that we 202 // are closed, rather than triggering the closed signal directly 203 return c.wrapped.Close() 204 } 205 206 func (l *idleListener) Accept(ctx context.Context) (io.ReadWriteCloser, error) { 207 rwc, err := l.wrapped.Accept(ctx) 208 if err != nil { 209 if isClosingError(err) { 210 // underlying listener was closed 211 l.closeOnce.Do(func() { close(l.closed) }) 212 // was it closed because of the idle timeout? 213 select { 214 case <-l.wasTimeout: 215 err = ErrIdleTimeout 216 default: 217 } 218 } 219 return nil, err 220 } 221 conn := &idleCloser{ 222 wrapped: rwc, 223 closed: make(chan struct{}), 224 } 225 l.newConns <- conn 226 return conn, err 227 } 228 229 func (l *idleListener) Close() error { 230 defer l.closeOnce.Do(func() { close(l.closed) }) 231 return l.wrapped.Close() 232 } 233 234 func (l *idleListener) Dialer() Dialer { 235 return l.wrapped.Dialer() 236 } 237 238 func (l *idleListener) run() { 239 var conns []*idleCloser 240 for { 241 var firstClosed chan struct{} // left at nil if there are no active conns 242 var timeout <-chan time.Time // left at nil if there are active conns 243 if len(conns) > 0 { 244 firstClosed = conns[0].closed 245 } else { 246 timeout = time.After(l.timeout) 247 } 248 select { 249 case <-l.closed: 250 // the main listener closed, no need to keep going 251 return 252 case conn := <-l.newConns: 253 // a new conn arrived, add it to the list 254 conns = append(conns, conn) 255 case <-timeout: 256 // we timed out, only happens when there are no active conns 257 // close the underlying listener, and allow the normal closing process to happen 258 close(l.wasTimeout) 259 l.wrapped.Close() 260 case <-firstClosed: 261 // a conn closed, remove it from the active list 262 conns = conns[:copy(conns, conns[1:])] 263 } 264 } 265 }