// Package jsonrpc2 implements server/client interfaces for a JSONRPC2 // transport, as defined by https://www.jsonrpc.org/specification. package jsonrpc2 import ( "crypto/rand" "encoding/json" "fmt" "time" ) const version = "2.0" // Request encodes an RPC request according to the spec. type Request struct { Version string `json:"jsonrpc"` // must be "2.0" Method string `json:"method"` Params []json.RawMessage `json:"params,omitempty"` ID string `json:"id"` } type response[Result any] struct { Version string `json:"jsonrpc"` // must be "2.0" ID string `json:"id"` Result Result `json:"result,omitempty"` Error *Error `json:"error,omitempty"` } func newID() string { b := make([]byte, 8) if _, err := rand.Read(b); err != nil { panic(err) } return fmt.Sprintf("%d-%x", time.Now().UnixNano(), b) } // encodeRequest writes a request to an io.Writer, returning the ID of the // request. func encodeRequest( enc *json.Encoder, method string, params []any, ) ( string, error, ) { var ( paramsBs = make([]json.RawMessage, len(params)) err error ) for i := range params { paramsBs[i], err = json.Marshal(params[i]) if err != nil { return "", fmt.Errorf("encoding param %d as JSON: %w", i, err) } } var ( id = newID() reqEnvelope = Request{ Version: version, Method: method, Params: paramsBs, ID: id, } ) if err := enc.Encode(reqEnvelope); err != nil { return "", fmt.Errorf("encoding as JSON: %w", err) } return id, nil } // decodeRequest decodes a request from an io.Reader, or returns an error code // and an error describing what went wrong. func decodeRequest(dec *json.Decoder) (Request, Error) { var reqEnvelope Request err := dec.Decode(&reqEnvelope) switch { case err != nil: return reqEnvelope, NewError( errCodeParse, "decoding request as JSON: %v", err, ) case reqEnvelope.Version != version: return reqEnvelope, NewError( errCodeInvalidRequest, "unknown JSONRPC version: %q", reqEnvelope.Version, ) case reqEnvelope.ID == "": return reqEnvelope, NewError( errCodeInvalidRequest, "request is missing an ID", ) } return reqEnvelope, Error{} } // encodeRequest writes a successful response to an io.Writer. func encodeResponse(enc *json.Encoder, id string, res any) error { resEnvelope := response[any]{ Version: version, ID: id, Result: res, } if err := enc.Encode(resEnvelope); err != nil { return fmt.Errorf("encoding as JSON: %w", err) } return nil } // encodeErrorRequest writes an error response to an io.Writer. func encodeErrorResponse(enc *json.Encoder, id string, err Error) error { resEnvelope := response[any]{ Version: version, ID: id, Error: &err, } if err := enc.Encode(resEnvelope); err != nil { return fmt.Errorf("encoding as JSON: %w", err) } return nil } // decodeResponse decodes a response from an io.Reader, unmarshaling the result // field into the given pointer receiver. If the pointer receiver is nil then // the result is discarded. If an error response is decoded then an Error struct // is returned. The ID of the response is returned in both cases. func decodeResponse(dec *json.Decoder, rcv any) (string, error) { var rcvEnvelope response[json.RawMessage] err := dec.Decode(&rcvEnvelope) switch { case err != nil: return "", fmt.Errorf("decoding response as JSON: %w", err) case rcvEnvelope.Version != version: return "", fmt.Errorf( "unknown JSONRPC version: %q", rcvEnvelope.Version, ) case rcvEnvelope.Error != nil: return rcvEnvelope.ID, *rcvEnvelope.Error } if rcv != nil { if err := json.Unmarshal(rcvEnvelope.Result, rcv); err != nil { return rcvEnvelope.ID, fmt.Errorf( "decoding result into receiver of type %T: %w", rcv, err, ) } } return rcvEnvelope.ID, nil }