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.
15 "golang.org/x/tools/internal/event"
16 errors "golang.org/x/xerrors"
19 // NOTE: This file provides an experimental API for serving multiple remote
20 // jsonrpc2 clients over the network. For now, it is intentionally similar to
21 // net/http, but that may change in the future as we figure out the correct
24 // A StreamServer is used to serve incoming jsonrpc2 clients communicating over
25 // a newly created connection.
26 type StreamServer interface {
27 ServeStream(context.Context, Conn) error
30 // The ServerFunc type is an adapter that implements the StreamServer interface
31 // using an ordinary function.
32 type ServerFunc func(context.Context, Conn) error
34 // ServeStream calls f(ctx, s).
35 func (f ServerFunc) ServeStream(ctx context.Context, c Conn) error {
39 // HandlerServer returns a StreamServer that handles incoming streams using the
41 func HandlerServer(h Handler) StreamServer {
42 return ServerFunc(func(ctx context.Context, conn Conn) error {
49 // ListenAndServe starts an jsonrpc2 server on the given address. If
50 // idleTimeout is non-zero, ListenAndServe exits after there are no clients for
51 // this duration, otherwise it exits only on error.
52 func ListenAndServe(ctx context.Context, network, addr string, server StreamServer, idleTimeout time.Duration) error {
53 ln, err := net.Listen(network, addr)
58 if network == "unix" {
61 return Serve(ctx, ln, server, idleTimeout)
64 // Serve accepts incoming connections from the network, and handles them using
65 // the provided server. If idleTimeout is non-zero, ListenAndServe exits after
66 // there are no clients for this duration, otherwise it exits only on error.
67 func Serve(ctx context.Context, ln net.Listener, server StreamServer, idleTimeout time.Duration) error {
68 ctx, cancel := context.WithCancel(ctx)
70 // Max duration: ~290 years; surely that's long enough.
71 const forever = 1<<63 - 1
75 connTimer := time.NewTimer(idleTimeout)
77 newConns := make(chan net.Conn)
78 doneListening := make(chan error)
79 closedConns := make(chan error)
83 nc, err := ln.Accept()
86 case doneListening <- fmt.Errorf("Accept(): %w", err):
98 case netConn := <-newConns:
101 stream := NewHeaderStream(netConn)
103 conn := NewConn(stream)
104 closedConns <- server.ServeStream(ctx, conn)
107 case err := <-doneListening:
109 case err := <-closedConns:
110 if !isClosingError(err) {
111 event.Error(ctx, "closed a connection", err)
114 if activeConns == 0 {
115 connTimer.Reset(idleTimeout)
118 return ErrIdleTimeout
125 // isClosingError reports if the error occurs normally during the process of
126 // closing a network connection. It uses imperfect heuristics that err on the
127 // side of false negatives, and should not be used for anything critical.
128 func isClosingError(err error) bool {
129 if errors.Is(err, io.EOF) {
132 // Per https://github.com/golang/go/issues/4373, this error string should not
133 // change. This is not ideal, but since the worst that could happen here is
134 // some superfluous logging, it is acceptable.
135 if err.Error() == "use of closed network connection" {