go-sdk/internal/jsonrpc2/jsonrpc2.go
Rob Findley 0c89f898b7 internal/jsonrpc2_v2: initialize async before binding
The lsprpc package needs to wait on the newly bound connection to
perform some tear-down, so it must be safe to call conn.Wait from Bind.

Achieve this by switching async from an init pattern to a constructor.
This means moving async fields to pointers, but since they weren't safe
to use prior to initialization anyway this feels correct.

Change-Id: I4f93980915e0b568fbf2db3cd7d062adf06e4b99
Reviewed-on: https://go-review.googlesource.com/c/tools/+/330929
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
2021-06-25 19:21:17 +00:00

101 lines
2.7 KiB
Go

// Copyright 2018 The Go MCP SDK Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec.
// https://www.jsonrpc.org/specification
// It is intended to be compatible with other implementations at the wire level.
package jsonrpc2
import (
"context"
"errors"
)
var (
// ErrIdleTimeout is returned when serving timed out waiting for new connections.
ErrIdleTimeout = errors.New("timed out waiting for new connections")
// ErrNotHandled is returned from a handler to indicate it did not handle the
// message.
ErrNotHandled = errors.New("JSON RPC not handled")
// ErrAsyncResponse is returned from a handler to indicate it will generate a
// response asynchronously.
ErrAsyncResponse = errors.New("JSON RPC asynchronous response")
)
// Preempter handles messages on a connection before they are queued to the main
// handler.
// Primarily this is used for cancel handlers or notifications for which out of
// order processing is not an issue.
type Preempter interface {
// Preempt is invoked for each incoming request before it is queued.
// If the request is a call, it must return a value or an error for the reply.
// Preempt should not block or start any new messages on the connection.
Preempt(ctx context.Context, req *Request) (interface{}, error)
}
// Handler handles messages on a connection.
type Handler interface {
// Handle is invoked for each incoming request.
// If the request is a call, it must return a value or an error for the reply.
Handle(ctx context.Context, req *Request) (interface{}, error)
}
type defaultHandler struct{}
func (defaultHandler) Preempt(context.Context, *Request) (interface{}, error) {
return nil, ErrNotHandled
}
func (defaultHandler) Handle(context.Context, *Request) (interface{}, error) {
return nil, ErrNotHandled
}
type HandlerFunc func(ctx context.Context, req *Request) (interface{}, error)
func (f HandlerFunc) Handle(ctx context.Context, req *Request) (interface{}, error) {
return f(ctx, req)
}
// async is a small helper for things with an asynchronous result that you can
// wait for.
type async struct {
ready chan struct{}
errBox chan error
}
func newAsync() *async {
var a async
a.ready = make(chan struct{})
a.errBox = make(chan error, 1)
a.errBox <- nil
return &a
}
func (a *async) done() {
close(a.ready)
}
func (a *async) isDone() bool {
select {
case <-a.ready:
return true
default:
return false
}
}
func (a *async) wait() error {
<-a.ready
err := <-a.errBox
a.errBox <- err
return err
}
func (a *async) setError(err error) {
storedErr := <-a.errBox
if storedErr == nil {
storedErr = err
}
a.errBox <- storedErr
}