61 lines
1.9 KiB
Go
61 lines
1.9 KiB
Go
package jsonrpc2
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
// ReadWriterServer wraps an io.ReadWriter so that it can handle RPC calls.
|
|
//
|
|
// The HandleNext method will process a single request and response. It should
|
|
// be called in a loop until it returns an error.
|
|
type ReadWriterServer struct {
|
|
handler Handler
|
|
enc *json.Encoder
|
|
dec *json.Decoder
|
|
}
|
|
|
|
// NewReadWriterServer returns a ReadWriterServer which will read/write on the
|
|
// given io.ReadWriter.
|
|
//
|
|
// All methods exported from rpcInterface which accept a context and one other
|
|
// value, and return a value and an error, will be considered to be available
|
|
// RPC methods, and the ReadWriterServer will execute calls to those methods
|
|
// against the given rpcInterface instance.
|
|
func NewReadWriterServer(handler Handler, rw io.ReadWriter) *ReadWriterServer {
|
|
return &ReadWriterServer{
|
|
handler: handler,
|
|
enc: json.NewEncoder(rw),
|
|
dec: json.NewDecoder(rw),
|
|
}
|
|
}
|
|
|
|
// HandleNext blocks until a single RPC call has been handled. If an error is
|
|
// returned, including context errors, then the ReadWriterServer should be
|
|
// considered in an unusable state and discarded.
|
|
func (h *ReadWriterServer) HandleNext(ctx context.Context) error {
|
|
req, decErr := decodeRequest(h.dec)
|
|
if decErr.Code != 0 {
|
|
encErr := encodeErrorResponse(h.enc, req.ID, decErr)
|
|
return errors.Join(decErr, encErr)
|
|
}
|
|
|
|
var rpcErr Error
|
|
res, err := h.handler.ServeRPC(ctx, req)
|
|
if err != nil && !errors.As(err, &rpcErr) {
|
|
// We deliberately don't include the actual error in the response. If
|
|
// doing so is desired this can be achieved using a middleware which
|
|
// translates the non-Error into an Error prior to it reaching this
|
|
// point.
|
|
rpcErr = NewError(errCodeServerError, "unexpected server-side error")
|
|
}
|
|
|
|
if rpcErr.Code != 0 {
|
|
return encodeErrorResponse(h.enc, req.ID, rpcErr)
|
|
}
|
|
|
|
return encodeResponse(h.enc, req.ID, res)
|
|
}
|