// Package jsonrpc is a JSON-RPC 2.0 connection over a framed byte stream. // // It holds everything the protocol says and nothing about any particular use // of it: requests and their answers, notifications, requests arriving the // other way, and the error object a failure travels in. How a message is // marked off from the next one is the caller's, through a Framer — the // Language Server Protocol puts an HTTP-style header in front of each message, // the Agent Client Protocol separates them with newlines, and everything above // that layer is the same. // // A connection is safe for concurrent use: several goroutines may call and // notify at once, and answers are matched back to their callers by request id. // The ids a peer chooses are its own and never collide with the ones sent from // here, because the two directions are tracked separately. // // conn := jsonrpc.NewConn(stream, framer, onNotify, onRequest) // go conn.Run() // // var result Position // err := conn.Call(ctx, "textDocument/definition", params, &result) package jsonrpc import ( "bufio" "encoding/json" "errors" "fmt" "io" ) // Version is the only value the protocol's jsonrpc field ever takes. const Version = "2.0" // ErrClosed is returned once the connection has been closed or its peer has // gone away. var ErrClosed = errors.New("jsonrpc: connection closed") // Framer marks off one message from the next on a stream. // // The two halves are deliberately separate functions rather than a codec // object: framing is stateless, and a reader and a writer of the same // connection are used from different goroutines. // // type NewlineFramer struct{} // // func (NewlineFramer) WriteMessage(w io.Writer, body []byte) error { // _, err := fmt.Fprintf(w, "%s\n", body) // return err // } type Framer interface { // ReadMessage reads one whole message and returns its body. It returns // io.EOF when the stream ends cleanly between messages, which is how a // peer shutting down is told apart from one that died mid-message. ReadMessage(r *bufio.Reader) ([]byte, error) // WriteMessage frames body and writes it out. WriteMessage(w io.Writer, body []byte) error } // ResponseError is the error object a response may carry. // // It is an error, so a failure at the far end reads like any other one at the // call site, and a handler may return one to choose the code the peer sees. // // return nil, &jsonrpc.ResponseError{Code: jsonrpc.CodeInvalidParams, Message: "no uri"} type ResponseError struct { Code int `json:"code"` Message string `json:"message"` Data json.RawMessage `json:"data,omitempty"` } // Error makes ResponseError an error. func (e *ResponseError) Error() string { return fmt.Sprintf("jsonrpc: peer error %d: %s", e.Code, e.Message) } // The standard error codes, of which a client normally sends only the first. const ( CodeParseError = -32700 CodeInvalidRequest = -32600 CodeMethodNotFound = -32601 CodeInvalidParams = -32602 CodeInternalError = -32603 ) // message is any frame: a request, a notification or a response. Which one it // is follows from which fields are present. type message struct { JSONRPC string `json:"jsonrpc"` ID json.RawMessage `json:"id,omitempty"` Method string `json:"method,omitempty"` Params json.RawMessage `json:"params,omitempty"` Result json.RawMessage `json:"result,omitempty"` Error *ResponseError `json:"error,omitempty"` } // NotificationFunc handles a notification the peer sent. It is called from the // connection's read loop, so it must not block for long. type NotificationFunc func(method string, params json.RawMessage) // RequestFunc handles a request the peer sent. // // It is called from the connection's read loop and must not block: a handler // whose answer has to come from somewhere else — the user, another goroutine, // the next turn of an event loop — keeps the Request and calls Reply when it // has one. Answering twice is harmless; never answering leaves the peer // waiting until the connection ends. type RequestFunc func(req *Request) // asResponseError turns any error into one the protocol can carry. func asResponseError(err error) *ResponseError { var responseErr *ResponseError if errors.As(err, &responseErr) { return responseErr } return &ResponseError{Code: CodeInternalError, Message: err.Error()} } // encodeParams encodes a parameter object, treating nil as absent. func encodeParams(params any) (json.RawMessage, error) { if params == nil { return nil, nil } encoded, err := json.Marshal(params) if err != nil { return nil, fmt.Errorf("jsonrpc: encoding the parameters: %w", err) } return encoded, nil } // decodeReply turns a response into an error or a decoded result. func decodeReply(reply *message, result any) error { if reply == nil { return ErrClosed } if reply.Error != nil { return reply.Error } if result == nil || len(reply.Result) == 0 { return nil } if err := json.Unmarshal(reply.Result, result); err != nil { return fmt.Errorf("jsonrpc: unreadable result: %w", err) } return nil }