GoPLS Viewer

Home|gopls/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
5package jsonrpc2
6
7import (
8    "context"
9    "errors"
10    "io"
11    "math"
12    "net"
13    "os"
14    "time"
15
16    "golang.org/x/tools/internal/event"
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.
26type StreamServer interface {
27    ServeStream(context.ContextConnerror
28}
29
30// The ServerFunc type is an adapter that implements the StreamServer interface
31// using an ordinary function.
32type ServerFunc func(context.ContextConnerror
33
34// ServeStream calls f(ctx, s).
35func (f ServerFuncServeStream(ctx context.Contextc Connerror {
36    return f(ctxc)
37}
38
39// HandlerServer returns a StreamServer that handles incoming streams using the
40// provided handler.
41func HandlerServer(h HandlerStreamServer {
42    return ServerFunc(func(ctx context.Contextconn Connerror {
43        conn.Go(ctxh)
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.
52func ListenAndServe(ctx context.Contextnetworkaddr stringserver StreamServeridleTimeout time.Durationerror {
53    lnerr := net.Listen(networkaddr)
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(ctxlnserveridleTimeout)
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.
67func Serve(ctx context.Contextln net.Listenerserver StreamServeridleTimeout time.Durationerror {
68    newConns := make(chan net.Conn)
69    closedConns := make(chan error)
70    activeConns := 0
71    var acceptErr error
72    go func() {
73        defer close(newConns)
74        for {
75            var nc net.Conn
76            ncacceptErr = ln.Accept()
77            if acceptErr != nil {
78                return
79            }
80            newConns <- nc
81        }
82    }()
83
84    ctxcancel := context.WithCancel(ctx)
85    defer func() {
86        // Signal the Accept goroutine to stop immediately
87        // and terminate all newly-accepted connections until it returns.
88        ln.Close()
89        for nc := range newConns {
90            nc.Close()
91        }
92        // Cancel pending ServeStream callbacks and wait for them to finish.
93        cancel()
94        for activeConns > 0 {
95            err := <-closedConns
96            if !isClosingError(err) {
97                event.Error(ctx"closed a connection"err)
98            }
99            activeConns--
100        }
101    }()
102
103    // Max duration: ~290 years; surely that's long enough.
104    const forever = math.MaxInt64
105    if idleTimeout <= 0 {
106        idleTimeout = forever
107    }
108    connTimer := time.NewTimer(idleTimeout)
109    defer connTimer.Stop()
110
111    for {
112        select {
113        case netConnok := <-newConns:
114            if !ok {
115                return acceptErr
116            }
117            if activeConns == 0 && !connTimer.Stop() {
118                // connTimer.C may receive a value even after Stop returns.
119                // (See https://golang.org/issue/37196.)
120                <-connTimer.C
121            }
122            activeConns++
123            stream := NewHeaderStream(netConn)
124            go func() {
125                conn := NewConn(stream)
126                err := server.ServeStream(ctxconn)
127                stream.Close()
128                closedConns <- err
129            }()
130
131        case err := <-closedConns:
132            if !isClosingError(err) {
133                event.Error(ctx"closed a connection"err)
134            }
135            activeConns--
136            if activeConns == 0 {
137                connTimer.Reset(idleTimeout)
138            }
139
140        case <-connTimer.C:
141            return ErrIdleTimeout
142
143        case <-ctx.Done():
144            return nil
145        }
146    }
147}
148
149// isClosingError reports if the error occurs normally during the process of
150// closing a network connection. It uses imperfect heuristics that err on the
151// side of false negatives, and should not be used for anything critical.
152func isClosingError(err errorbool {
153    if errors.Is(errio.EOF) {
154        return true
155    }
156    // Per https://github.com/golang/go/issues/4373, this error string should not
157    // change. This is not ideal, but since the worst that could happen here is
158    // some superfluous logging, it is acceptable.
159    if err.Error() == "use of closed network connection" {
160        return true
161    }
162    return false
163}
164
MembersX
ListenAndServe.addr
Serve.BlockStmt.RangeStmt_2580.nc
ServerFunc
Serve.ctx
Serve.server
Serve.acceptErr
Serve.BlockStmt.BlockStmt.nc
Serve.BlockStmt.BlockStmt.err
ServerFunc.ServeStream.f
ServerFunc.ServeStream
HandlerServer
ListenAndServe.server
Serve.newConns
Serve.BlockStmt.BlockStmt.BlockStmt.conn
Serve.BlockStmt.BlockStmt.BlockStmt.err
isClosingError.err
io
StreamServer
Serve.ln
Serve.activeConns
Serve.BlockStmt.BlockStmt.stream
isClosingError
os
ServerFunc.ServeStream.ctx
HandlerServer.h
ListenAndServe.ctx
ListenAndServe.idleTimeout
Serve.closedConns
Serve.connTimer
Serve.BlockStmt.BlockStmt.netConn
ListenAndServe
Serve.idleTimeout
Serve.cancel
Serve.BlockStmt.BlockStmt.ok
time
ListenAndServe.ln
ListenAndServe.err
math
ServerFunc.ServeStream.c
ListenAndServe.network
Serve
Serve.forever
Members
X