Changed: DB Params

This commit is contained in:
2025-03-20 12:35:13 +01:00
parent 8640a12439
commit b71b3d12ca
822 changed files with 134218 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2
// Code is an error code as defined in the JSON-RPC spec.
type Code int32
// list of JSON-RPC error codes.
const (
// ParseError is the invalid JSON was received by the server.
// An error occurred on the server while parsing the JSON text.
ParseError Code = -32700
// InvalidRequest is the JSON sent is not a valid Request object.
InvalidRequest Code = -32600
// MethodNotFound is the method does not exist / is not available.
MethodNotFound Code = -32601
// InvalidParams is the invalid method parameter(s).
InvalidParams Code = -32602
// InternalError is the internal JSON-RPC error.
InternalError Code = -32603
// JSONRPCReservedErrorRangeStart is the start range of JSON RPC reserved error codes.
//
// It doesn't denote a real error code. No LSP error codes should
// be defined between the start and end range. For backwards
// compatibility the "ServerNotInitialized" and the "UnknownErrorCode"
// are left in the range.
//
// @since 3.16.0.
JSONRPCReservedErrorRangeStart Code = -32099
// CodeServerErrorStart reserved for implementation-defined server-errors.
//
// Deprecated: Use JSONRPCReservedErrorRangeStart instead.
CodeServerErrorStart = JSONRPCReservedErrorRangeStart
// ServerNotInitialized is the error of server not initialized.
ServerNotInitialized Code = -32002
// UnknownError should be used for all non coded errors.
UnknownError Code = -32001
// JSONRPCReservedErrorRangeEnd is the start range of JSON RPC reserved error codes.
//
// It doesn't denote a real error code.
//
// @since 3.16.0.
JSONRPCReservedErrorRangeEnd Code = -32000
// CodeServerErrorEnd reserved for implementation-defined server-errors.
//
// Deprecated: Use JSONRPCReservedErrorRangeEnd instead.
CodeServerErrorEnd = JSONRPCReservedErrorRangeEnd
)
// This file contains the Go forms of the wire specification.
//
// See http://www.jsonrpc.org/specification for details.
//
// list of JSON-RPC errors.
var (
// ErrUnknown should be used for all non coded errors.
ErrUnknown = NewError(UnknownError, "JSON-RPC unknown error")
// ErrParse is used when invalid JSON was received by the server.
ErrParse = NewError(ParseError, "JSON-RPC parse error")
// ErrInvalidRequest is used when the JSON sent is not a valid Request object.
ErrInvalidRequest = NewError(InvalidRequest, "JSON-RPC invalid request")
// ErrMethodNotFound should be returned by the handler when the method does
// not exist / is not available.
ErrMethodNotFound = NewError(MethodNotFound, "JSON-RPC method not found")
// ErrInvalidParams should be returned by the handler when method
// parameter(s) were invalid.
ErrInvalidParams = NewError(InvalidParams, "JSON-RPC invalid params")
// ErrInternal is not currently returned but defined for completeness.
ErrInternal = NewError(InternalError, "JSON-RPC internal error")
)

244
templ/lsp/jsonrpc2/conn.go Normal file
View File

@@ -0,0 +1,244 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2
import (
"bytes"
"context"
"fmt"
"sync"
"sync/atomic"
"encoding/json"
)
// Conn is the common interface to jsonrpc clients and servers.
//
// Conn is bidirectional; it does not have a designated server or client end.
// It manages the jsonrpc2 protocol, connecting responses back to their calls.
type Conn interface {
// Call invokes the target method and waits for a response.
//
// The params will be marshaled to JSON before sending over the wire, and will
// be handed to the method invoked.
//
// The response will be unmarshaled from JSON into the result.
//
// The id returned will be unique from this connection, and can be used for
// logging or tracking.
Call(ctx context.Context, method string, params, result any) (ID, error)
// Notify invokes the target method but does not wait for a response.
//
// The params will be marshaled to JSON before sending over the wire, and will
// be handed to the method invoked.
Notify(ctx context.Context, method string, params any) error
// Go starts a goroutine to handle the connection.
//
// It must be called exactly once for each Conn. It returns immediately.
// Must block on Done() to wait for the connection to shut down.
//
// This is a temporary measure, this should be started automatically in the
// future.
Go(ctx context.Context, handler Handler)
// Close closes the connection and it's underlying stream.
//
// It does not wait for the close to complete, use the Done() channel for
// that.
Close() error
// Done returns a channel that will be closed when the processing goroutine
// has terminated, which will happen if Close() is called or an underlying
// stream is closed.
Done() <-chan struct{}
// Err returns an error if there was one from within the processing goroutine.
//
// If err returns non nil, the connection will be already closed or closing.
Err() error
}
type conn struct {
seq int32 // access atomically
writeMu sync.Mutex // protects writes to the stream
stream Stream // supplied stream
pendingMu sync.Mutex // protects the pending map
pending map[ID]chan *Response // holds the pending response channel with the ID as the key.
done chan struct{} // closed when done
err atomic.Value // holds run error
}
// NewConn creates a new connection object around the supplied stream.
func NewConn(s Stream) Conn {
conn := &conn{
stream: s,
pending: make(map[ID]chan *Response),
done: make(chan struct{}),
}
return conn
}
// Call implements Conn.
func (c *conn) Call(ctx context.Context, method string, params, result any) (id ID, err error) {
// generate a new request identifier
id = NewNumberID(atomic.AddInt32(&c.seq, 1))
call, err := NewCall(id, method, params)
if err != nil {
return id, fmt.Errorf("marshaling call parameters: %w", err)
}
// We have to add ourselves to the pending map before we send, otherwise we
// are racing the response. Also add a buffer to rchan, so that if we get a
// wire response between the time this call is cancelled and id is deleted
// from c.pending, the send to rchan will not block.
rchan := make(chan *Response, 1)
c.pendingMu.Lock()
c.pending[id] = rchan
c.pendingMu.Unlock()
defer func() {
c.pendingMu.Lock()
delete(c.pending, id)
c.pendingMu.Unlock()
}()
// now we are ready to send
_, err = c.write(ctx, call)
if err != nil {
// sending failed, we will never get a response, so don't leave it pending
return id, err
}
// now wait for the response
select {
case resp := <-rchan:
// is it an error response?
if resp.err != nil {
return id, resp.err
}
if result == nil || len(resp.result) == 0 {
return id, nil
}
dec := json.NewDecoder(bytes.NewReader(resp.result))
if err := dec.Decode(result); err != nil {
return id, fmt.Errorf("unmarshaling result: %w", err)
}
return id, nil
case <-ctx.Done():
return id, ctx.Err()
}
}
// Notify implements Conn.
func (c *conn) Notify(ctx context.Context, method string, params any) (err error) {
notify, err := NewNotification(method, params)
if err != nil {
return fmt.Errorf("marshaling notify parameters: %w", err)
}
_, err = c.write(ctx, notify)
return err
}
func (c *conn) replier(req Message) Replier {
return func(ctx context.Context, result any, err error) error {
call, ok := req.(*Call)
if !ok {
// request was a notify, no need to respond
return nil
}
response, err := NewResponse(call.id, result, err)
if err != nil {
return err
}
_, err = c.write(ctx, response)
if err != nil {
// TODO(iancottrell): if a stream write fails, we really need to shut down the whole stream
return err
}
return nil
}
}
func (c *conn) write(ctx context.Context, msg Message) (int64, error) {
c.writeMu.Lock()
n, err := c.stream.Write(ctx, msg)
c.writeMu.Unlock()
if err != nil {
return 0, fmt.Errorf("write to stream: %w", err)
}
return n, nil
}
// Go implements Conn.
func (c *conn) Go(ctx context.Context, handler Handler) {
go c.run(ctx, handler)
}
func (c *conn) run(ctx context.Context, handler Handler) {
defer close(c.done)
for {
// get the next message
msg, _, err := c.stream.Read(ctx)
if err != nil {
// The stream failed, we cannot continue.
c.fail(err)
return
}
switch msg := msg.(type) {
case Request:
if err := handler(ctx, c.replier(msg), msg); err != nil {
c.fail(err)
}
case *Response:
// If method is not set, this should be a response, in which case we must
// have an id to send the response back to the caller.
c.pendingMu.Lock()
rchan, ok := c.pending[msg.id]
c.pendingMu.Unlock()
if ok {
rchan <- msg
}
}
}
}
// Close implements Conn.
func (c *conn) Close() error {
return c.stream.Close()
}
// Done implements Conn.
func (c *conn) Done() <-chan struct{} {
return c.done
}
// Err implements Conn.
func (c *conn) Err() error {
if err := c.err.Load(); err != nil {
return err.(error)
}
return nil
}
// fail sets a failure condition on the stream and closes it.
func (c *conn) fail(err error) {
c.err.Store(err)
c.stream.Close()
}

View File

@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2
import (
"errors"
"fmt"
"encoding/json"
)
// Error represents a JSON-RPC error.
type Error struct {
// Code a number indicating the error type that occurred.
Code Code `json:"code"`
// Message a string providing a short description of the error.
Message string `json:"message"`
// Data a Primitive or Structured value that contains additional
// information about the error. Can be omitted.
Data *json.RawMessage `json:"data,omitempty"`
}
// compile time check whether the Error implements error interface.
var _ error = (*Error)(nil)
// Error implements error.Error.
func (e *Error) Error() string {
if e == nil {
return ""
}
return e.Message
}
// Unwrap implements errors.Unwrap.
//
// Returns the error underlying the receiver, which may be nil.
func (e *Error) Unwrap() error { return errors.New(e.Message) }
// NewError builds a Error struct for the suppied code and message.
func NewError(c Code, message string) *Error {
return &Error{
Code: c,
Message: message,
}
}
// Errorf builds a Error struct for the suppied code, format and args.
func Errorf(c Code, format string, args ...any) *Error {
return &Error{
Code: c,
Message: fmt.Sprintf(format, args...),
}
}
// constErr represents a error constant.
type constErr string
// compile time check whether the constErr implements error interface.
var _ error = (*constErr)(nil)
// Error implements error.Error.
func (e constErr) Error() string { return string(e) }
const (
// ErrIdleTimeout is returned when serving timed out waiting for new connections.
ErrIdleTimeout = constErr("timed out waiting for new connections")
)

View File

@@ -0,0 +1,120 @@
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2
import (
"context"
"fmt"
"sync"
)
// Handler is invoked to handle incoming requests.
//
// The Replier sends a reply to the request and must be called exactly once.
type Handler func(ctx context.Context, reply Replier, req Request) error
// Replier is passed to handlers to allow them to reply to the request.
//
// If err is set then result will be ignored.
type Replier func(ctx context.Context, result any, err error) error
// MethodNotFoundHandler is a Handler that replies to all call requests with the
// standard method not found response.
//
// This should normally be the final handler in a chain.
func MethodNotFoundHandler(ctx context.Context, reply Replier, req Request) error {
return reply(ctx, nil, fmt.Errorf("%q: %w", req.Method(), ErrMethodNotFound))
}
// ReplyHandler creates a Handler that panics if the wrapped handler does
// not call Reply for every request that it is passed.
func ReplyHandler(handler Handler) (h Handler) {
h = Handler(func(ctx context.Context, reply Replier, req Request) error {
called := false
err := handler(ctx, func(ctx context.Context, result any, err error) error {
if called {
panic(fmt.Errorf("request %q replied to more than once", req.Method()))
}
called = true
return reply(ctx, result, err)
}, req)
if !called {
panic(fmt.Errorf("request %q was never replied to", req.Method()))
}
return err
})
return h
}
// CancelHandler returns a handler that supports cancellation, and a function
// that can be used to trigger canceling in progress requests.
func CancelHandler(handler Handler) (h Handler, canceller func(id ID)) {
var mu sync.Mutex
handling := make(map[ID]context.CancelFunc)
h = Handler(func(ctx context.Context, reply Replier, req Request) error {
if call, ok := req.(*Call); ok {
cancelCtx, cancel := context.WithCancel(ctx)
ctx = cancelCtx
mu.Lock()
handling[call.ID()] = cancel
mu.Unlock()
innerReply := reply
reply = func(ctx context.Context, result any, err error) error {
mu.Lock()
delete(handling, call.ID())
mu.Unlock()
return innerReply(ctx, result, err)
}
}
return handler(ctx, reply, req)
})
canceller = func(id ID) {
mu.Lock()
cancel, found := handling[id]
mu.Unlock()
if found {
cancel()
}
}
return h, canceller
}
// AsyncHandler returns a handler that processes each request goes in its own
// goroutine.
//
// The handler returns immediately, without the request being processed.
// Each request then waits for the previous request to finish before it starts.
//
// This allows the stream to unblock at the cost of unbounded goroutines
// all stalled on the previous one.
func AsyncHandler(handler Handler) (h Handler) {
nextRequest := make(chan struct{})
close(nextRequest)
h = Handler(func(ctx context.Context, reply Replier, req Request) error {
waitForPrevious := nextRequest
nextRequest = make(chan struct{})
unlockNext := nextRequest
innerReply := reply
reply = func(ctx context.Context, result any, err error) error {
close(unlockNext)
return innerReply(ctx, result, err)
}
go func() {
<-waitForPrevious
_ = handler(ctx, reply, req)
}()
return nil
})
return h
}

View File

@@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: 2019 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
// Package jsonrpc2 is an implementation of the JSON-RPC 2 specification for Go.
//
// https://www.jsonrpc.org/specification
package jsonrpc2 // import "github.com/a-h/templ/lsp/jsonrpc2"

View File

@@ -0,0 +1,171 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2_test
import (
"bytes"
"context"
"fmt"
"io"
"net"
"path"
"reflect"
"testing"
"encoding/json"
"github.com/a-h/templ/lsp/jsonrpc2"
)
const (
methodNoArgs = "no_args"
methodOneString = "one_string"
methodOneNumber = "one_number"
methodJoin = "join"
)
type callTest struct {
method string
params any
expect any
}
var callTests = []callTest{
{
method: methodNoArgs,
params: nil,
expect: true,
},
{
method: methodOneString,
params: "fish",
expect: "got:fish",
},
{
method: methodOneNumber,
params: 10,
expect: "got:10",
},
{
method: methodJoin,
params: []string{"a", "b", "c"},
expect: "a/b/c",
},
// TODO: expand the test cases
}
func (test *callTest) newResults() any {
switch e := test.expect.(type) {
case []any:
var r []any
for _, v := range e {
r = append(r, reflect.New(reflect.TypeOf(v)).Interface())
}
return r
case nil:
return nil
default:
return reflect.New(reflect.TypeOf(test.expect)).Interface()
}
}
func (test *callTest) verifyResults(t *testing.T, results any) {
t.Helper()
if results == nil {
return
}
val := reflect.Indirect(reflect.ValueOf(results)).Interface()
if !reflect.DeepEqual(val, test.expect) {
t.Errorf("%v:Results are incorrect, got %+v expect %+v", test.method, val, test.expect)
}
}
func TestRequest(t *testing.T) {
ctx := context.Background()
a, b, done := prepare(ctx, t)
defer done()
for _, test := range callTests {
t.Run(test.method, func(t *testing.T) {
results := test.newResults()
if _, err := a.Call(ctx, test.method, test.params, results); err != nil {
t.Fatalf("%v:Call failed: %v", test.method, err)
}
test.verifyResults(t, results)
if _, err := b.Call(ctx, test.method, test.params, results); err != nil {
t.Fatalf("%v:Call failed: %v", test.method, err)
}
test.verifyResults(t, results)
})
}
}
func prepare(ctx context.Context, t *testing.T) (a, b jsonrpc2.Conn, done func()) {
t.Helper()
// make a wait group that can be used to wait for the system to shut down
aPipe, bPipe := net.Pipe()
a = run(ctx, aPipe)
b = run(ctx, bPipe)
done = func() {
a.Close()
b.Close()
<-a.Done()
<-b.Done()
}
return a, b, done
}
func run(ctx context.Context, nc io.ReadWriteCloser) jsonrpc2.Conn {
stream := jsonrpc2.NewStream(nc)
conn := jsonrpc2.NewConn(stream)
conn.Go(ctx, testHandler())
return conn
}
func testHandler() jsonrpc2.Handler {
return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
switch req.Method() {
case methodNoArgs:
if len(req.Params()) > 0 {
return reply(ctx, nil, fmt.Errorf("expected no params: %w", jsonrpc2.ErrInvalidParams))
}
return reply(ctx, true, nil)
case methodOneString:
var v string
dec := json.NewDecoder(bytes.NewReader(req.Params()))
if err := dec.Decode(&v); err != nil {
return reply(ctx, nil, fmt.Errorf("%s: %w", jsonrpc2.ErrParse, err))
}
return reply(ctx, "got:"+v, nil)
case methodOneNumber:
var v int
dec := json.NewDecoder(bytes.NewReader(req.Params()))
if err := dec.Decode(&v); err != nil {
return reply(ctx, nil, fmt.Errorf("%s: %w", jsonrpc2.ErrParse, err))
}
return reply(ctx, fmt.Sprintf("got:%d", v), nil)
case methodJoin:
var v []string
dec := json.NewDecoder(bytes.NewReader(req.Params()))
if err := dec.Decode(&v); err != nil {
return reply(ctx, nil, fmt.Errorf("%s: %w", jsonrpc2.ErrParse, err))
}
return reply(ctx, path.Join(v...), nil)
default:
return jsonrpc2.MethodNotFoundHandler(ctx, reply, req)
}
}
}

View File

@@ -0,0 +1,354 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2
import (
"bytes"
"errors"
"fmt"
"encoding/json"
)
// Message is the interface to all JSON-RPC message types.
//
// They share no common functionality, but are a closed set of concrete types
// that are allowed to implement this interface.
//
// The message types are *Call, *Response and *Notification.
type Message interface {
// jsonrpc2Message is used to make the set of message implementations a
// closed set.
jsonrpc2Message()
}
// Request is the shared interface to jsonrpc2 messages that request
// a method be invoked.
//
// The request types are a closed set of *Call and *Notification.
type Request interface {
Message
// Method is a string containing the method name to invoke.
Method() string
// Params is either a struct or an array with the parameters of the method.
Params() json.RawMessage
// jsonrpc2Request is used to make the set of request implementations closed.
jsonrpc2Request()
}
// Call is a request that expects a response.
//
// The response will have a matching ID.
type Call struct {
// Method is a string containing the method name to invoke.
method string
// Params is either a struct or an array with the parameters of the method.
params json.RawMessage
// id of this request, used to tie the Response back to the request.
id ID
}
// make sure a Call implements the Request, json.Marshaler and json.Unmarshaler and interfaces.
var (
_ Request = (*Call)(nil)
_ json.Marshaler = (*Call)(nil)
_ json.Unmarshaler = (*Call)(nil)
)
// NewCall constructs a new Call message for the supplied ID, method and
// parameters.
func NewCall(id ID, method string, params any) (*Call, error) {
p, merr := marshalInterface(params)
req := &Call{
id: id,
method: method,
params: p,
}
return req, merr
}
// ID returns the current call id.
func (c *Call) ID() ID { return c.id }
// Method implements Request.
func (c *Call) Method() string { return c.method }
// Params implements Request.
func (c *Call) Params() json.RawMessage { return c.params }
// jsonrpc2Message implements Request.
func (Call) jsonrpc2Message() {}
// jsonrpc2Request implements Request.
func (Call) jsonrpc2Request() {}
// MarshalJSON implements json.Marshaler.
func (c Call) MarshalJSON() ([]byte, error) {
req := wireRequest{
Method: c.method,
Params: &c.params,
ID: &c.id,
}
data, err := json.Marshal(req)
if err != nil {
return data, fmt.Errorf("marshaling call: %w", err)
}
return data, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (c *Call) UnmarshalJSON(data []byte) error {
var req wireRequest
dec := json.NewDecoder(bytes.NewReader(data))
if err := dec.Decode(&req); err != nil {
return fmt.Errorf("unmarshaling call: %w", err)
}
c.method = req.Method
if req.Params != nil {
c.params = *req.Params
}
if req.ID != nil {
c.id = *req.ID
}
return nil
}
// Response is a reply to a Request.
//
// It will have the same ID as the call it is a response to.
type Response struct {
// result is the content of the response.
result json.RawMessage
// err is set only if the call failed.
err error
// ID of the request this is a response to.
id ID
}
// make sure a Response implements the Message, json.Marshaler and json.Unmarshaler and interfaces.
var (
_ Message = (*Response)(nil)
_ json.Marshaler = (*Response)(nil)
_ json.Unmarshaler = (*Response)(nil)
)
// NewResponse constructs a new Response message that is a reply to the
// supplied. If err is set result may be ignored.
func NewResponse(id ID, result any, err error) (*Response, error) {
r, merr := marshalInterface(result)
resp := &Response{
id: id,
result: r,
err: err,
}
return resp, merr
}
// ID returns the current response id.
func (r *Response) ID() ID { return r.id }
// Result returns the Response result.
func (r *Response) Result() json.RawMessage { return r.result }
// Err returns the Response error.
func (r *Response) Err() error { return r.err }
// jsonrpc2Message implements Message.
func (r *Response) jsonrpc2Message() {}
// MarshalJSON implements json.Marshaler.
func (r Response) MarshalJSON() ([]byte, error) {
resp := &wireResponse{
Error: toError(r.err),
ID: &r.id,
}
if resp.Error == nil {
resp.Result = &r.result
}
data, err := json.Marshal(resp)
if err != nil {
return data, fmt.Errorf("marshaling notification: %w", err)
}
return data, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (r *Response) UnmarshalJSON(data []byte) error {
var resp wireResponse
dec := json.NewDecoder(bytes.NewReader(data))
if err := dec.Decode(&resp); err != nil {
return fmt.Errorf("unmarshaling jsonrpc response: %w", err)
}
if resp.Result != nil {
r.result = *resp.Result
}
if resp.Error != nil {
r.err = resp.Error
}
if resp.ID != nil {
r.id = *resp.ID
}
return nil
}
func toError(err error) *Error {
if err == nil {
// no error, the response is complete
return nil
}
var wrapped *Error
if errors.As(err, &wrapped) {
// already a wire error, just use it
return wrapped
}
result := &Error{Message: err.Error()}
if errors.As(err, &wrapped) {
// if we wrapped a wire error, keep the code from the wrapped error
// but the message from the outer error
result.Code = wrapped.Code
}
return result
}
// Notification is a request for which a response cannot occur, and as such
// it has not ID.
type Notification struct {
// Method is a string containing the method name to invoke.
method string
params json.RawMessage
}
// make sure a Notification implements the Request, json.Marshaler and json.Unmarshaler and interfaces.
var (
_ Request = (*Notification)(nil)
_ json.Marshaler = (*Notification)(nil)
_ json.Unmarshaler = (*Notification)(nil)
)
// NewNotification constructs a new Notification message for the supplied
// method and parameters.
func NewNotification(method string, params any) (*Notification, error) {
p, merr := marshalInterface(params)
notify := &Notification{
method: method,
params: p,
}
return notify, merr
}
// Method implements Request.
func (n *Notification) Method() string { return n.method }
// Params implements Request.
func (n *Notification) Params() json.RawMessage { return n.params }
// jsonrpc2Message implements Request.
func (Notification) jsonrpc2Message() {}
// jsonrpc2Request implements Request.
func (Notification) jsonrpc2Request() {}
// MarshalJSON implements json.Marshaler.
func (n Notification) MarshalJSON() ([]byte, error) {
req := wireRequest{
Method: n.method,
Params: &n.params,
}
data, err := json.Marshal(req)
if err != nil {
return data, fmt.Errorf("marshaling notification: %w", err)
}
return data, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (n *Notification) UnmarshalJSON(data []byte) error {
var req wireRequest
dec := json.NewDecoder(bytes.NewReader(data))
if err := dec.Decode(&req); err != nil {
return fmt.Errorf("unmarshaling notification: %w", err)
}
n.method = req.Method
if req.Params != nil {
n.params = *req.Params
}
return nil
}
// DecodeMessage decodes data to Message.
func DecodeMessage(data []byte) (Message, error) {
var msg combined
dec := json.NewDecoder(bytes.NewReader(data))
if err := dec.Decode(&msg); err != nil {
return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err)
}
if msg.Method == "" {
// no method, should be a response
if msg.ID == nil {
return nil, ErrInvalidRequest
}
resp := &Response{
id: *msg.ID,
}
if msg.Error != nil {
resp.err = msg.Error
}
if msg.Result != nil {
resp.result = *msg.Result
}
return resp, nil
}
// has a method, must be a request
if msg.ID == nil {
// request with no ID is a notify
notify := &Notification{
method: msg.Method,
}
if msg.Params != nil {
notify.params = *msg.Params
}
return notify, nil
}
// request with an ID, must be a call
call := &Call{
method: msg.Method,
id: *msg.ID,
}
if msg.Params != nil {
call.params = *msg.Params
}
return call, nil
}
// marshalInterface marshal obj to json.RawMessage.
func marshalInterface(obj any) (json.RawMessage, error) {
data, err := json.Marshal(obj)
if err != nil {
return json.RawMessage{}, fmt.Errorf("failed to marshal json: %w", err)
}
return json.RawMessage(data), nil
}

129
templ/lsp/jsonrpc2/serve.go Normal file
View File

@@ -0,0 +1,129 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2
import (
"context"
"fmt"
"net"
"os"
"time"
)
// 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.
// StreamServer is used to serve incoming jsonrpc2 clients communicating over
// a newly created connection.
type StreamServer interface {
ServeStream(context.Context, Conn) error
}
// ServerFunc is an adapter that implements the StreamServer interface
// using an ordinary function.
type ServerFunc func(context.Context, Conn) error
// ServeStream implements StreamServer.
//
// 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 fmt.Errorf("failed to listen %s:%s: %w", network, addr, 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 := NewStream(netConn)
go func() {
conn := NewConn(stream)
closedConns <- server.ServeStream(ctx, conn)
stream.Close()
}()
case err := <-doneListening:
return err
case <-closedConns:
// if !isClosingError(err) {
// }
activeConns--
if activeConns == 0 {
connTimer.Reset(idleTimeout)
}
case <-connTimer.C:
return ErrIdleTimeout
case <-ctx.Done():
return ctx.Err()
}
}
}

View File

@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2_test
import (
"context"
"errors"
"net"
"sync"
"testing"
"time"
"github.com/a-h/templ/lsp/jsonrpc2"
)
func TestIdleTimeout(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
ln, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
connect := func() net.Conn {
conn, err := net.DialTimeout("tcp", ln.Addr().String(), 5*time.Second)
if err != nil {
panic(err)
}
return conn
}
server := jsonrpc2.HandlerServer(jsonrpc2.MethodNotFoundHandler)
var (
runErr error
wg sync.WaitGroup
)
wg.Add(1)
go func() {
defer wg.Done()
runErr = jsonrpc2.Serve(ctx, ln, server, 100*time.Millisecond)
}()
// Exercise some connection/disconnection patterns, and then assert that when
// our timer fires, the server exits.
conn1 := connect()
conn2 := connect()
conn1.Close()
conn2.Close()
conn3 := connect()
conn3.Close()
wg.Wait()
if !errors.Is(runErr, jsonrpc2.ErrIdleTimeout) {
t.Errorf("run() returned error %v, want %v", runErr, jsonrpc2.ErrIdleTimeout)
}
}

View File

@@ -0,0 +1,226 @@
// SPDX-FileCopyrightText: 2018 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2
import (
"bufio"
"context"
stdjson "encoding/json"
"fmt"
"io"
"strconv"
"strings"
"encoding/json"
)
const (
// HdrContentLength is the HTTP header name of the length of the content part in bytes. This header is required.
// This entity header indicates the size of the entity-body, in bytes, sent to the recipient.
//
// RFC 7230, section 3.3.2: Content-Length:
// https://tools.ietf.org/html/rfc7230#section-3.3.2
HdrContentLength = "Content-Length"
// HeaderContentType is the mime type of the content part. Defaults to "application/vscode-jsonrpc; charset=utf-8".
// This entity header is used to indicate the media type of the resource.
//
// RFC 7231, section 3.1.1.5: Content-Type:
// https://tools.ietf.org/html/rfc7231#section-3.1.1.5
HdrContentType = "Content-Type"
// HeaderContentSeparator is the header and content part separator.
HdrContentSeparator = "\r\n\r\n"
)
// Framer wraps a network connection up into a Stream.
//
// It is responsible for the framing and encoding of messages into wire form.
// NewRawStream and NewStream are implementations of a Framer.
type Framer func(conn io.ReadWriteCloser) Stream
// Stream abstracts the transport mechanics from the JSON RPC protocol.
//
// A Conn reads and writes messages using the stream it was provided on
// construction, and assumes that each call to Read or Write fully transfers
// a single message, or returns an error.
//
// A stream is not safe for concurrent use, it is expected it will be used by
// a single Conn in a safe manner.
type Stream interface {
// Read gets the next message from the stream.
Read(context.Context) (Message, int64, error)
// Write sends a message to the stream.
Write(context.Context, Message) (int64, error)
// Close closes the connection.
// Any blocked Read or Write operations will be unblocked and return errors.
Close() error
}
type rawStream struct {
conn io.ReadWriteCloser
in *stdjson.Decoder
}
// NewRawStream returns a Stream built on top of a io.ReadWriteCloser.
//
// The messages are sent with no wrapping, and rely on json decode consistency
// to determine message boundaries.
func NewRawStream(conn io.ReadWriteCloser) Stream {
return &rawStream{
conn: conn,
in: stdjson.NewDecoder(conn), // TODO(zchee): why test fail using segmentio json.Decoder?
}
}
// Read implements Stream.Read.
func (s *rawStream) Read(ctx context.Context) (Message, int64, error) {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
default:
}
var raw stdjson.RawMessage
if err := s.in.Decode(&raw); err != nil {
return nil, 0, fmt.Errorf("decoding raw message: %w", err)
}
msg, err := DecodeMessage(raw)
return msg, int64(len(raw)), err
}
// Write implements Stream.Write.
func (s *rawStream) Write(ctx context.Context, msg Message) (int64, error) {
select {
case <-ctx.Done():
return 0, ctx.Err()
default:
}
data, err := json.Marshal(msg)
if err != nil {
return 0, fmt.Errorf("marshaling message: %w", err)
}
n, err := s.conn.Write(data)
if err != nil {
return 0, fmt.Errorf("write to stream: %w", err)
}
return int64(n), nil
}
// Close implements Stream.Close.
func (s *rawStream) Close() error {
return s.conn.Close()
}
type stream struct {
conn io.ReadWriteCloser
in *bufio.Reader
}
// NewStream returns a Stream built on top of a io.ReadWriteCloser.
//
// The messages are sent with HTTP content length and MIME type headers.
// This is the format used by LSP and others.
func NewStream(conn io.ReadWriteCloser) Stream {
return &stream{
conn: conn,
in: bufio.NewReader(conn),
}
}
// Read implements Stream.Read.
func (s *stream) Read(ctx context.Context) (Message, int64, error) {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
default:
}
var total int64
var length int64
// read the header, stop on the first empty line
for {
line, err := s.in.ReadString('\n')
total += int64(len(line))
if err != nil {
return nil, total, fmt.Errorf("failed reading header line: %w", err)
}
line = strings.TrimSpace(line)
// check we have a header line
if line == "" {
break
}
colon := strings.IndexRune(line, ':')
if colon < 0 {
return nil, total, fmt.Errorf("invalid header line %q", line)
}
name, value := line[:colon], strings.TrimSpace(line[colon+1:])
switch name {
case HdrContentLength:
if length, err = strconv.ParseInt(value, 10, 32); err != nil {
return nil, total, fmt.Errorf("failed parsing %s: %v: %w", HdrContentLength, value, err)
}
if length <= 0 {
return nil, total, fmt.Errorf("invalid %s: %v", HdrContentLength, length)
}
default:
// ignoring unknown headers
}
}
if length == 0 {
return nil, total, fmt.Errorf("missing %s header", HdrContentLength)
}
data := make([]byte, length)
if _, err := io.ReadFull(s.in, data); err != nil {
return nil, total, fmt.Errorf("read full of data: %w", err)
}
total += length
msg, err := DecodeMessage(data)
return msg, total, err
}
// Write implements Stream.Write.
func (s *stream) Write(ctx context.Context, msg Message) (int64, error) {
select {
case <-ctx.Done():
return 0, ctx.Err()
default:
}
data, err := json.Marshal(msg)
if err != nil {
return 0, fmt.Errorf("marshaling message: %w", err)
}
n, err := fmt.Fprintf(s.conn, "%s: %v%s", HdrContentLength, len(data), HdrContentSeparator)
total := int64(n)
if err != nil {
return 0, fmt.Errorf("write data to conn: %w", err)
}
n, err = s.conn.Write(data)
total += int64(n)
if err != nil {
return 0, fmt.Errorf("write data to conn: %w", err)
}
return total, nil
}
// Close implements Stream.Close.
func (s *stream) Close() error {
return s.conn.Close()
}

140
templ/lsp/jsonrpc2/wire.go Normal file
View File

@@ -0,0 +1,140 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2
import (
"fmt"
"encoding/json"
)
// Version represents a JSON-RPC version.
const Version = "2.0"
// version is a special 0 sized struct that encodes as the jsonrpc version tag.
//
// It will fail during decode if it is not the correct version tag in the stream.
type version struct{}
// compile time check whether the version implements a json.Marshaler and json.Unmarshaler interfaces.
var (
_ json.Marshaler = (*version)(nil)
_ json.Unmarshaler = (*version)(nil)
)
// MarshalJSON implements json.Marshaler.
func (version) MarshalJSON() ([]byte, error) {
return json.Marshal(Version)
}
// UnmarshalJSON implements json.Unmarshaler.
func (version) UnmarshalJSON(data []byte) error {
version := ""
if err := json.Unmarshal(data, &version); err != nil {
return fmt.Errorf("failed to Unmarshal: %w", err)
}
if version != Version {
return fmt.Errorf("invalid RPC version %v", version)
}
return nil
}
// ID is a Request identifier.
//
// Only one of either the Name or Number members will be set, using the
// number form if the Name is the empty string.
type ID struct {
name string
number int32
}
// compile time check whether the ID implements a fmt.Formatter, json.Marshaler and json.Unmarshaler interfaces.
var (
_ fmt.Formatter = (*ID)(nil)
_ json.Marshaler = (*ID)(nil)
_ json.Unmarshaler = (*ID)(nil)
)
// NewNumberID returns a new number request ID.
func NewNumberID(v int32) ID { return ID{number: v} }
// NewStringID returns a new string request ID.
func NewStringID(v string) ID { return ID{name: v} }
// Format writes the ID to the formatter.
//
// If the rune is q the representation is non ambiguous,
// string forms are quoted, number forms are preceded by a #.
func (id ID) Format(f fmt.State, r rune) {
numF, strF := `%d`, `%s`
if r == 'q' {
numF, strF = `#%d`, `%q`
}
switch {
case id.name != "":
fmt.Fprintf(f, strF, id.name)
default:
fmt.Fprintf(f, numF, id.number)
}
}
// MarshalJSON implements json.Marshaler.
func (id *ID) MarshalJSON() ([]byte, error) {
if id.name != "" {
return json.Marshal(id.name)
}
return json.Marshal(id.number)
}
// UnmarshalJSON implements json.Unmarshaler.
func (id *ID) UnmarshalJSON(data []byte) error {
*id = ID{}
if err := json.Unmarshal(data, &id.number); err == nil {
return nil
}
return json.Unmarshal(data, &id.name)
}
// wireRequest is sent to a server to represent a Call or Notify operaton.
type wireRequest struct {
// VersionTag is always encoded as the string "2.0"
VersionTag version `json:"jsonrpc"`
// Method is a string containing the method name to invoke.
Method string `json:"method"`
// Params is either a struct or an array with the parameters of the method.
Params *json.RawMessage `json:"params,omitempty"`
// The id of this request, used to tie the Response back to the request.
// Will be either a string or a number. If not set, the Request is a notify,
// and no response is possible.
ID *ID `json:"id,omitempty"`
}
// wireResponse is a reply to a Request.
//
// It will always have the ID field set to tie it back to a request, and will
// have either the Result or Error fields set depending on whether it is a
// success or failure wireResponse.
type wireResponse struct {
// VersionTag is always encoded as the string "2.0"
VersionTag version `json:"jsonrpc"`
// Result is the response value, and is required on success.
Result *json.RawMessage `json:"result,omitempty"`
// Error is a structured error response if the call fails.
Error *Error `json:"error,omitempty"`
// ID must be set and is the identifier of the Request this is a response to.
ID *ID `json:"id,omitempty"`
}
// combined has all the fields of both Request and Response.
//
// We can decode this and then work out which it is.
type combined struct {
VersionTag version `json:"jsonrpc"`
ID *ID `json:"id,omitempty"`
Method string `json:"method"`
Params *json.RawMessage `json:"params,omitempty"`
Result *json.RawMessage `json:"result,omitempty"`
Error *Error `json:"error,omitempty"`
}

View File

@@ -0,0 +1,156 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause
package jsonrpc2_test
import (
"bytes"
"fmt"
"reflect"
"testing"
"encoding/json"
"github.com/a-h/templ/lsp/jsonrpc2"
)
var wireIDTestData = []struct {
name string
id jsonrpc2.ID
encoded []byte
plain string
quoted string
}{
{
name: `empty`,
encoded: []byte(`0`),
plain: `0`,
quoted: `#0`,
}, {
name: `number`,
id: jsonrpc2.NewNumberID(43),
encoded: []byte(`43`),
plain: `43`,
quoted: `#43`,
}, {
name: `string`,
id: jsonrpc2.NewStringID("life"),
encoded: []byte(`"life"`),
plain: `life`,
quoted: `"life"`,
},
}
func TestIDFormat(t *testing.T) {
t.Parallel()
for _, tt := range wireIDTestData {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := fmt.Sprint(tt.id); got != tt.plain {
t.Errorf("got %s expected %s", got, tt.plain)
}
if got := fmt.Sprintf("%q", tt.id); got != tt.quoted {
t.Errorf("got %s want %s", got, tt.quoted)
}
})
}
}
func TestIDEncode(t *testing.T) {
t.Parallel()
for _, tt := range wireIDTestData {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
data, err := json.Marshal(&tt.id)
if err != nil {
t.Fatal(err)
}
checkJSON(t, data, tt.encoded)
})
}
}
func TestIDDecode(t *testing.T) {
t.Parallel()
for _, tt := range wireIDTestData {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var got *jsonrpc2.ID
dec := json.NewDecoder(bytes.NewReader(tt.encoded))
if err := dec.Decode(&got); err != nil {
t.Fatal(err)
}
if reflect.ValueOf(&got).IsZero() {
t.Fatalf("got nil want %s", tt.id)
}
if *got != tt.id {
t.Fatalf("got %s want %s", got, tt.id)
}
})
}
}
func TestErrorEncode(t *testing.T) {
t.Parallel()
b, err := json.Marshal(jsonrpc2.NewError(0, ""))
if err != nil {
t.Fatal(err)
}
checkJSON(t, b, []byte(`{
"code": 0,
"message": ""
}`))
}
func TestErrorResponse(t *testing.T) {
t.Parallel()
// originally reported in #39719, this checks that result is not present if
// it is an error response
r, _ := jsonrpc2.NewResponse(jsonrpc2.NewNumberID(3), nil, fmt.Errorf("computing fix edits"))
data, err := json.Marshal(r)
if err != nil {
t.Fatal(err)
}
checkJSON(t, data, []byte(`{
"jsonrpc":"2.0",
"error":{
"code":0,
"message":"computing fix edits"
},
"id":3
}`))
}
func checkJSON(t *testing.T, got, want []byte) {
t.Helper()
// compare the compact form, to allow for formatting differences
g := &bytes.Buffer{}
if err := json.Compact(g, got); err != nil {
t.Fatal(err)
}
w := &bytes.Buffer{}
if err := json.Compact(w, want); err != nil {
t.Fatal(err)
}
if g.String() != w.String() {
t.Fatalf("Got:\n%s\nWant:\n%s", g, w)
}
}