.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / internal / jsonrpc2 / serve.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/internal/jsonrpc2/serve.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/internal/jsonrpc2/serve.go
new file mode 100644 (file)
index 0000000..b9e31a8
--- /dev/null
@@ -0,0 +1,139 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jsonrpc2
+
+import (
+       "context"
+       "fmt"
+       "io"
+       "net"
+       "os"
+       "time"
+
+       "golang.org/x/tools/internal/event"
+       errors "golang.org/x/xerrors"
+)
+
+// NOTE: This file provides an experimental API for serving multiple remote
+// jsonrpc2 clients over the network. For now, it is intentionally similar to
+// net/http, but that may change in the future as we figure out the correct
+// semantics.
+
+// A StreamServer is used to serve incoming jsonrpc2 clients communicating over
+// a newly created connection.
+type StreamServer interface {
+       ServeStream(context.Context, Conn) error
+}
+
+// The ServerFunc type is an adapter that implements the StreamServer interface
+// using an ordinary function.
+type ServerFunc func(context.Context, Conn) error
+
+// ServeStream calls f(ctx, s).
+func (f ServerFunc) ServeStream(ctx context.Context, c Conn) error {
+       return f(ctx, c)
+}
+
+// HandlerServer returns a StreamServer that handles incoming streams using the
+// provided handler.
+func HandlerServer(h Handler) StreamServer {
+       return ServerFunc(func(ctx context.Context, conn Conn) error {
+               conn.Go(ctx, h)
+               <-conn.Done()
+               return conn.Err()
+       })
+}
+
+// ListenAndServe starts an jsonrpc2 server on the given address.  If
+// idleTimeout is non-zero, ListenAndServe exits after there are no clients for
+// this duration, otherwise it exits only on error.
+func ListenAndServe(ctx context.Context, network, addr string, server StreamServer, idleTimeout time.Duration) error {
+       ln, err := net.Listen(network, addr)
+       if err != nil {
+               return err
+       }
+       defer ln.Close()
+       if network == "unix" {
+               defer os.Remove(addr)
+       }
+       return Serve(ctx, ln, server, idleTimeout)
+}
+
+// Serve accepts incoming connections from the network, and handles them using
+// the provided server. If idleTimeout is non-zero, ListenAndServe exits after
+// there are no clients for this duration, otherwise it exits only on error.
+func Serve(ctx context.Context, ln net.Listener, server StreamServer, idleTimeout time.Duration) error {
+       ctx, cancel := context.WithCancel(ctx)
+       defer cancel()
+       // Max duration: ~290 years; surely that's long enough.
+       const forever = 1<<63 - 1
+       if idleTimeout <= 0 {
+               idleTimeout = forever
+       }
+       connTimer := time.NewTimer(idleTimeout)
+
+       newConns := make(chan net.Conn)
+       doneListening := make(chan error)
+       closedConns := make(chan error)
+
+       go func() {
+               for {
+                       nc, err := ln.Accept()
+                       if err != nil {
+                               select {
+                               case doneListening <- fmt.Errorf("Accept(): %w", err):
+                               case <-ctx.Done():
+                               }
+                               return
+                       }
+                       newConns <- nc
+               }
+       }()
+
+       activeConns := 0
+       for {
+               select {
+               case netConn := <-newConns:
+                       activeConns++
+                       connTimer.Stop()
+                       stream := NewHeaderStream(netConn)
+                       go func() {
+                               conn := NewConn(stream)
+                               closedConns <- server.ServeStream(ctx, conn)
+                               stream.Close()
+                       }()
+               case err := <-doneListening:
+                       return err
+               case err := <-closedConns:
+                       if !isClosingError(err) {
+                               event.Error(ctx, "closed a connection", err)
+                       }
+                       activeConns--
+                       if activeConns == 0 {
+                               connTimer.Reset(idleTimeout)
+                       }
+               case <-connTimer.C:
+                       return ErrIdleTimeout
+               case <-ctx.Done():
+                       return ctx.Err()
+               }
+       }
+}
+
+// isClosingError reports if the error occurs normally during the process of
+// closing a network connection. It uses imperfect heuristics that err on the
+// side of false negatives, and should not be used for anything critical.
+func isClosingError(err error) bool {
+       if errors.Is(err, io.EOF) {
+               return true
+       }
+       // Per https://github.com/golang/go/issues/4373, this error string should not
+       // change. This is not ideal, but since the worst that could happen here is
+       // some superfluous logging, it is acceptable.
+       if err.Error() == "use of closed network connection" {
+               return true
+       }
+       return false
+}