-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
216 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
Package jsonrpc2 implements a JSON-RPC 2.0 ClientCodec and ServerCodec | ||
for the net/rpc package and HTTP transport for JSON-RPC 2.0. | ||
RPC method's signature | ||
JSON-RPC 2.0 support positional and named parameters. Which one should be | ||
used when calling server's method depends on type of that method's first | ||
parameter: if it is an Array or Slice then positional parameters should be | ||
used, if it is a Map or Struct then named parameters should be used. (Also | ||
any method can be called without parameters at all.) If first parameter | ||
will be of custom type with json.Unmarshaler interface then it depends on | ||
what is supported by that type - this way you can even implement method | ||
which can be called both with positional and named parameters. | ||
JSON-RPC 2.0 support result of any type, so method's result (second param) | ||
can be a reference of any type supported by json.Marshal. | ||
JSON-RPC 2.0 support error codes and optional extra error data in addition | ||
to error message. If method returns error of standard error type (i.e. | ||
just error message without error code) then error code -32000 will be | ||
used. To define custom error code (and optionally extra error data) method | ||
should return jsonrpc2.Error. | ||
Using positional parameters of different types | ||
If you'll have to provide method which should be called using positional | ||
parameters of different types then it's recommended to implement this | ||
using first parameter of custom type with json.Unmarshaler interface. | ||
To call such a method you'll have to use client.Call() with []interface{} | ||
in args. | ||
Decoding errors on client | ||
Because of net/rpc limitations client.Call() can't return JSON-RPC 2.0 | ||
error with code, message and extra data - it'll return either one of | ||
rpc.ErrShutdown or io.ErrUnexpectedEOF errors, or encoded JSON-RPC 2.0 | ||
error, which have to be decoded using jsonrpc2.ServerError to get error's | ||
code, message and extra data. | ||
Limitations | ||
HTTP does not support Pipelined Requests/Responses. | ||
HTTP does not support GET Request. | ||
Because of net/rpc limitations RPC method MUST NOT return standard | ||
error which begins with '{' and ends with '}'. | ||
Because of net/rpc limitations there is no way to provide | ||
transport-level details (like client's IP) to RPC method. | ||
Current implementation does a lot of sanity checks to conform to | ||
protocol spec. Making most of them optional may improve performance. | ||
*/ | ||
package jsonrpc2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package jsonrpc2_test | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net" | ||
"net/http" | ||
"net/rpc" | ||
|
||
"github.com/powerman/rpc-codec/jsonrpc2" | ||
) | ||
|
||
// A server wishes to export an object of type ExampleSvc: | ||
type ExampleSvc struct{} | ||
|
||
// Method with positional params. | ||
func (*ExampleSvc) Sum(vals [2]int, res *int) error { | ||
*res = vals[0] + vals[1] | ||
return nil | ||
} | ||
|
||
// Method with positional params. | ||
func (*ExampleSvc) SumAll(vals []int, res *int) error { | ||
for _, v := range vals { | ||
*res += v | ||
} | ||
return nil | ||
} | ||
|
||
// Method with named params. | ||
func (*ExampleSvc) MapLen(m map[string]int, res *int) error { | ||
*res = len(m) | ||
return nil | ||
} | ||
|
||
type NameArg struct{ Fname, Lname string } | ||
type NameRes struct{ Name string } | ||
|
||
// Method with named params. | ||
func (*ExampleSvc) FullName(t NameArg, res *NameRes) error { | ||
*res = NameRes{t.Fname + " " + t.Lname} | ||
return nil | ||
} | ||
|
||
// Method returns error with code -32000. | ||
func (*ExampleSvc) Err1(struct{}, *struct{}) error { | ||
return errors.New("some issue") | ||
} | ||
|
||
// Method returns error with code 42. | ||
func (*ExampleSvc) Err2(struct{}, *struct{}) error { | ||
return jsonrpc2.NewError(42, "some issue") | ||
} | ||
|
||
// Method returns error with code 42 and extra error data. | ||
func (*ExampleSvc) Err3(struct{}, *struct{}) error { | ||
return &jsonrpc2.Error{42, "some issue", map[string]int{"one": 1, "two": 2}} | ||
} | ||
|
||
func Example() { | ||
// Server export an object of type ExampleSvc. | ||
rpc.Register(&ExampleSvc{}) | ||
|
||
// Server provide a TCP transport. | ||
lnTCP, err := net.Listen("tcp", "127.0.0.1:0") | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer lnTCP.Close() | ||
go func() { | ||
for { | ||
conn, err := lnTCP.Accept() | ||
if err != nil { | ||
return | ||
} | ||
go jsonrpc2.ServeConn(conn) | ||
} | ||
}() | ||
|
||
// Server provide a HTTP transport on /rpc endpoint. | ||
http.Handle("/rpc", jsonrpc2.HTTPHandler(nil)) | ||
lnHTTP, err := net.Listen("tcp", "127.0.0.1:0") | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer lnHTTP.Close() | ||
go http.Serve(lnHTTP, nil) | ||
|
||
// Client use TCP transport. | ||
clientTCP, err := jsonrpc2.Dial("tcp", lnTCP.Addr().String()) | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer clientTCP.Close() | ||
|
||
// Client use HTTP transport. | ||
clientHTTP := jsonrpc2.NewHTTPClient("http://" + lnHTTP.Addr().String() + "/rpc") | ||
defer clientHTTP.Close() | ||
|
||
var reply int | ||
|
||
// Synchronous call using positional params and TCP. | ||
err = clientTCP.Call("ExampleSvc.Sum", [2]int{3, 5}, &reply) | ||
fmt.Printf("Sum(3,5)=%d\n", reply) | ||
|
||
// Synchronous call using positional params and HTTP. | ||
err = clientHTTP.Call("ExampleSvc.SumAll", []int{3, 5, -2}, &reply) | ||
fmt.Printf("SumAll(3,5,-2)=%d\n", reply) | ||
|
||
// Asynchronous call using named params and TCP. | ||
startCall := clientTCP.Go("ExampleSvc.MapLen", | ||
map[string]int{"a": 10, "b": 20, "c": 30}, &reply, nil) | ||
replyCall := <-startCall.Done | ||
fmt.Printf("MapLen({a:10,b:20,c:30})=%d\n", *replyCall.Reply.(*int)) | ||
|
||
// Notification using named params and HTTP. | ||
clientHTTP.Notify("ExampleSvc.FullName", NameArg{"First", "Last"}) | ||
|
||
// Correct error handling. | ||
err = clientTCP.Call("ExampleSvc.Err1", nil, nil) | ||
if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF { | ||
fmt.Printf("Err1(): %q\n", err) | ||
} else if err != nil { | ||
rpcerr := jsonrpc2.ServerError(err) | ||
fmt.Printf("Err1(): code=%d msg=%q data=%v\n", rpcerr.Code, rpcerr.Message, rpcerr.Data) | ||
} | ||
|
||
err = clientHTTP.Call("ExampleSvc.Err3", nil, nil) | ||
if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF { | ||
fmt.Printf("Err3(): %q\n", err) | ||
} else if err != nil { | ||
rpcerr := jsonrpc2.ServerError(err) | ||
fmt.Printf("Err3(): code=%d msg=%q data=%v\n", rpcerr.Code, rpcerr.Message, rpcerr.Data) | ||
} | ||
|
||
// Output: | ||
// Sum(3,5)=8 | ||
// SumAll(3,5,-2)=6 | ||
// MapLen({a:10,b:20,c:30})=3 | ||
// Err1(): code=-32000 msg="some issue" data=<nil> | ||
// Err3(): code=42 msg="some issue" data=map[one:1 two:2] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters