rpc
API
rpc
packageAPI reference for the rpc
package.
Imports
(9)
S
struct
Request
Request is a JSON RPC request.
rpc/rpc.go:15-19
type Request struct
Fields
| Name | Type | Description |
|---|---|---|
| ID | string | json:"id" |
| Method | string | json:"method" |
| Params | json.RawMessage | json:"params,omitempty" |
S
struct
Response
Response is a JSON RPC response.
rpc/rpc.go:22-26
type Response struct
Fields
| Name | Type | Description |
|---|---|---|
| ID | string | json:"id" |
| Result | json.RawMessage | json:"result,omitempty" |
| Error | string | json:"error,omitempty" |
T
type
HandlerFunc
HandlerFunc handles an RPC method.
rpc/rpc.go:29-29
type HandlerFunc func(context.Context, json.RawMessage) (any, error)
S
struct
Server
Server dispatches requests to registered handlers.
rpc/rpc.go:32-35
type Server struct
Methods
Register
Method
Register adds or replaces a method handler.
Parameters
method
string
func (*Server) Register(method string, h HandlerFunc)
{
s.mu.Lock()
defer s.mu.Unlock()
s.handlers[method] = h
}
Handle
Method
Handle decodes and dispatches one RPC request.
Parameters
ctx
context.Context
reqBytes
[]byte
Returns
[]byte
error
func (*Server) Handle(ctx context.Context, reqBytes []byte) ([]byte, error)
{
var req Request
if err := json.Unmarshal(reqBytes, &req); err != nil {
return nil, err
}
if req.Method == "" {
return nil, errors.New("rpc: missing method")
}
s.mu.RLock()
h := s.handlers[req.Method]
s.mu.RUnlock()
if h == nil {
res := Response{ID: req.ID, Error: ErrMethodNotFound.Error()}
return json.Marshal(res)
}
out, err := h(ctx, req.Params)
if err != nil {
res := Response{ID: req.ID, Error: err.Error()}
return json.Marshal(res)
}
b, err := json.Marshal(out)
if err != nil {
return nil, err
}
res := Response{ID: req.ID, Result: b}
return json.Marshal(res)
}
Fields
| Name | Type | Description |
|---|---|---|
| mu | sync.RWMutex | |
| handlers | map[string]HandlerFunc |
F
function
NewServer
NewServer creates an empty RPC server.
Returns
rpc/rpc.go:38-38
func NewServer() *Server
{ return &Server{handlers: map[string]HandlerFunc{}} }
F
function
Call
Call invokes an RPC method over HTTP.
Parameters
Returns
error
rpc/rpc.go:83-125
func Call(ctx context.Context, endpoint, method string, params any, out any) error
{
req := Request{ID: "1", Method: method}
if params != nil {
b, err := json.Marshal(params)
if err != nil {
return err
}
req.Params = b
}
body, err := json.Marshal(req)
if err != nil {
return err
}
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
if err != nil {
return err
}
httpReq.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(httpReq)
if err != nil {
return err
}
var res Response
decodeErr := json.NewDecoder(resp.Body).Decode(&res)
closeErr := resp.Body.Close()
if decodeErr != nil {
return decodeErr
}
if closeErr != nil {
return closeErr
}
if res.Error != "" {
return fmt.Errorf("rpc: %s", res.Error)
}
if out == nil {
return nil
}
return json.Unmarshal(res.Result, out)
}
F
function
TestServerHandle_DispatchSuccess
Parameters
t
rpc/rpc_test.go:13-43
func TestServerHandle_DispatchSuccess(t *testing.T)
{
s := NewServer()
s.Register("echo", func(_ context.Context, p json.RawMessage) (any, error) {
var in struct {
S string `json:"s"`
}
_ = json.Unmarshal(p, &in)
return map[string]string{"out": in.S}, nil
})
req := []byte(`{"id":"99","method":"echo","params":{"s":"hi"}}`)
b, err := s.Handle(context.Background(), req)
if err != nil {
t.Fatalf("Handle error: %v", err)
}
var res Response
if err := json.Unmarshal(b, &res); err != nil {
t.Fatalf("unmarshal response: %v", err)
}
if res.ID != "99" || res.Error != "" {
t.Fatalf("unexpected response: %+v", res)
}
var out map[string]string
if err := json.Unmarshal(res.Result, &out); err != nil {
t.Fatalf("unmarshal result: %v", err)
}
if out["out"] != "hi" {
t.Fatalf("expected hi, got %v", out)
}
}
F
function
TestServerHandle_MethodNotFound
Parameters
t
rpc/rpc_test.go:45-56
func TestServerHandle_MethodNotFound(t *testing.T)
{
s := NewServer()
b, err := s.Handle(context.Background(), []byte(`{"id":"1","method":"missing"}`))
if err != nil {
t.Fatalf("Handle error: %v", err)
}
var res Response
_ = json.Unmarshal(b, &res)
if res.Error == "" {
t.Fatalf("expected error")
}
}
F
function
TestCall_HTTPRoundTrip
Parameters
t
rpc/rpc_test.go:58-92
func TestCall_HTTPRoundTrip(t *testing.T)
{
s := NewServer()
s.Register("add", func(_ context.Context, p json.RawMessage) (any, error) {
var in struct {
A int `json:"a"`
B int `json:"b"`
}
_ = json.Unmarshal(p, &in)
return map[string]int{"sum": in.A + in.B}, nil
})
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var req Request
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
t.Fatalf("decode request: %v", err)
}
b, err := s.Handle(r.Context(), mustJSON(req))
if err != nil {
t.Fatalf("handle: %v", err)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(b)
}))
defer srv.Close()
var out struct {
Sum int `json:"sum"`
}
if err := Call(context.Background(), srv.URL, "add", map[string]int{"a": 2, "b": 5}, &out); err != nil {
t.Fatalf("Call error: %v", err)
}
if out.Sum != 7 {
t.Fatalf("expected 7, got %d", out.Sum)
}
}
F
function
mustJSON
Parameters
v
any
Returns
[]byte
rpc/rpc_test.go:94-97
func mustJSON(v any) []byte
{
b, _ := json.Marshal(v)
return b
}