Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / jsonrpc2 / serve.go
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         "fmt"
10         "io"
11         "net"
12         "os"
13         "time"
14
15         "golang.org/x/tools/internal/event"
16         errors "golang.org/x/xerrors"
17 )
18
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
22 // semantics.
23
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
28 }
29
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
33
34 // ServeStream calls f(ctx, s).
35 func (f ServerFunc) ServeStream(ctx context.Context, c Conn) error {
36         return f(ctx, c)
37 }
38
39 // HandlerServer returns a StreamServer that handles incoming streams using the
40 // provided handler.
41 func HandlerServer(h Handler) StreamServer {
42         return ServerFunc(func(ctx context.Context, conn Conn) error {
43                 conn.Go(ctx, h)
44                 <-conn.Done()
45                 return conn.Err()
46         })
47 }
48
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)
54         if err != nil {
55                 return err
56         }
57         defer ln.Close()
58         if network == "unix" {
59                 defer os.Remove(addr)
60         }
61         return Serve(ctx, ln, server, idleTimeout)
62 }
63
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)
69         defer cancel()
70         // Max duration: ~290 years; surely that's long enough.
71         const forever = 1<<63 - 1
72         if idleTimeout <= 0 {
73                 idleTimeout = forever
74         }
75         connTimer := time.NewTimer(idleTimeout)
76
77         newConns := make(chan net.Conn)
78         doneListening := make(chan error)
79         closedConns := make(chan error)
80
81         go func() {
82                 for {
83                         nc, err := ln.Accept()
84                         if err != nil {
85                                 select {
86                                 case doneListening <- fmt.Errorf("Accept(): %w", err):
87                                 case <-ctx.Done():
88                                 }
89                                 return
90                         }
91                         newConns <- nc
92                 }
93         }()
94
95         activeConns := 0
96         for {
97                 select {
98                 case netConn := <-newConns:
99                         activeConns++
100                         connTimer.Stop()
101                         stream := NewHeaderStream(netConn)
102                         go func() {
103                                 conn := NewConn(stream)
104                                 closedConns <- server.ServeStream(ctx, conn)
105                                 stream.Close()
106                         }()
107                 case err := <-doneListening:
108                         return err
109                 case err := <-closedConns:
110                         if !isClosingError(err) {
111                                 event.Error(ctx, "closed a connection", err)
112                         }
113                         activeConns--
114                         if activeConns == 0 {
115                                 connTimer.Reset(idleTimeout)
116                         }
117                 case <-connTimer.C:
118                         return ErrIdleTimeout
119                 case <-ctx.Done():
120                         return ctx.Err()
121                 }
122         }
123 }
124
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) {
130                 return true
131         }
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" {
136                 return true
137         }
138         return false
139 }