1
- // SPDX-License-Identifier: BSD-3-Clause
2
1
// SPDX-FileCopyrightText: Copyright 2021 The Go Language Server Authors
2
+ // SPDX-License-Identifier: BSD-3-Clause
3
3
4
4
package jsonrpc2
5
5
@@ -11,23 +11,27 @@ import (
11
11
"net"
12
12
"os"
13
13
"time"
14
+
15
+ "go.lsp.dev/pkg/event"
14
16
)
15
17
16
18
// NOTE: This file provides an experimental API for serving multiple remote
17
19
// jsonrpc2 clients over the network. For now, it is intentionally similar to
18
20
// net/http, but that may change in the future as we figure out the correct
19
21
// semantics.
20
22
21
- // A StreamServer is used to serve incoming jsonrpc2 clients communicating over
23
+ // StreamServer is used to serve incoming jsonrpc2 clients communicating over
22
24
// a newly created connection.
23
25
type StreamServer interface {
24
26
ServeStream (context.Context , Conn ) error
25
27
}
26
28
27
- // The ServerFunc type is an adapter that implements the StreamServer interface
29
+ // ServerFunc is an adapter that implements the StreamServer interface
28
30
// using an ordinary function.
29
31
type ServerFunc func (context.Context , Conn ) error
30
32
33
+ // ServeStream implements StreamServer.
34
+ //
31
35
// ServeStream calls f(ctx, s).
32
36
func (f ServerFunc ) ServeStream (ctx context.Context , c Conn ) error {
33
37
return f (ctx , c )
@@ -43,18 +47,21 @@ func HandlerServer(h Handler) StreamServer {
43
47
})
44
48
}
45
49
46
- // ListenAndServe starts an jsonrpc2 server on the given address. If
47
- // idleTimeout is non-zero, ListenAndServe exits after there are no clients for
50
+ // ListenAndServe starts an jsonrpc2 server on the given address.
51
+ //
52
+ // If idleTimeout is non-zero, ListenAndServe exits after there are no clients for
48
53
// this duration, otherwise it exits only on error.
49
54
func ListenAndServe (ctx context.Context , network , addr string , server StreamServer , idleTimeout time.Duration ) error {
50
55
ln , err := net .Listen (network , addr )
51
56
if err != nil {
52
57
return fmt .Errorf ("failed to listen %s:%s: %w" , network , addr , err )
53
58
}
54
59
defer ln .Close ()
60
+
55
61
if network == "unix" {
56
62
defer os .Remove (addr )
57
63
}
64
+
58
65
return Serve (ctx , ln , server , idleTimeout )
59
66
}
60
67
@@ -64,6 +71,7 @@ func ListenAndServe(ctx context.Context, network, addr string, server StreamServ
64
71
func Serve (ctx context.Context , ln net.Listener , server StreamServer , idleTimeout time.Duration ) error {
65
72
ctx , cancel := context .WithCancel (ctx )
66
73
defer cancel ()
74
+
67
75
// Max duration: ~290 years; surely that's long enough.
68
76
const forever = 1 << 63 - 1
69
77
if idleTimeout <= 0 {
@@ -85,6 +93,7 @@ func Serve(ctx context.Context, ln net.Listener, server StreamServer, idleTimeou
85
93
}
86
94
return
87
95
}
96
+
88
97
newConns <- nc
89
98
}
90
99
}()
@@ -101,36 +110,45 @@ func Serve(ctx context.Context, ln net.Listener, server StreamServer, idleTimeou
101
110
closedConns <- server .ServeStream (ctx , conn )
102
111
stream .Close ()
103
112
}()
113
+
104
114
case err := <- doneListening :
105
115
return err
116
+
106
117
case err := <- closedConns :
107
118
if ! isClosingError (err ) {
108
- return err
119
+ event . Error ( ctx , "closed a connection" , err )
109
120
}
121
+
110
122
activeConns --
111
123
if activeConns == 0 {
112
124
connTimer .Reset (idleTimeout )
113
125
}
126
+
114
127
case <- connTimer .C :
115
128
return ErrIdleTimeout
129
+
116
130
case <- ctx .Done ():
117
131
return ctx .Err ()
118
132
}
119
133
}
120
134
}
121
135
122
- // isClosingError reports if the error occurs normally during the process of
123
- // closing a network connection. It uses imperfect heuristics that err on the
124
- // side of false negatives, and should not be used for anything critical.
136
+ // isClosingError reports whether the error occurs normally during the process of
137
+ // closing a network connection.
138
+ //
139
+ // It uses imperfect heuristics that err on the side of false negatives,
140
+ // and should not be used for anything critical.
125
141
func isClosingError (err error ) bool {
126
142
if errors .Is (err , io .EOF ) {
127
143
return true
128
144
}
145
+
129
146
// Per https://github.com/golang/go/issues/4373, this error string should not
130
147
// change. This is not ideal, but since the worst that could happen here is
131
148
// some superfluous logging, it is acceptable.
132
149
if err .Error () == "use of closed network connection" {
133
150
return true
134
151
}
152
+
135
153
return false
136
154
}
0 commit comments