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  }