parent
f98675660b
commit
6ad39cb386
@ -1,58 +0,0 @@ |
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/* |
|
||||||
Package gorilla/rpc/json provides a codec for JSON-RPC over HTTP services. |
|
||||||
|
|
||||||
To register the codec in a RPC server: |
|
||||||
|
|
||||||
import ( |
|
||||||
"http" |
|
||||||
"github.com/gorilla/rpc/v2" |
|
||||||
"github.com/gorilla/rpc/v2/json" |
|
||||||
) |
|
||||||
|
|
||||||
func init() { |
|
||||||
s := rpc.NewServer() |
|
||||||
s.RegisterCodec(json.NewCodec(), "application/json") |
|
||||||
// [...]
|
|
||||||
http.Handle("/rpc", s) |
|
||||||
} |
|
||||||
|
|
||||||
A codec is tied to a content type. In the example above, the server will use |
|
||||||
the JSON codec for requests with "application/json" as the value for the |
|
||||||
"Content-Type" header. |
|
||||||
|
|
||||||
This package follows the JSON-RPC 1.0 specification: |
|
||||||
|
|
||||||
http://json-rpc.org/wiki/specification
|
|
||||||
|
|
||||||
Request format is: |
|
||||||
|
|
||||||
method: |
|
||||||
The name of the method to be invoked, as a string in dotted notation |
|
||||||
as in "Service.Method". |
|
||||||
params: |
|
||||||
An array with a single object to pass as argument to the method. |
|
||||||
id: |
|
||||||
The request id, a uint. It is used to match the response with the |
|
||||||
request that it is replying to. |
|
||||||
|
|
||||||
Response format is: |
|
||||||
|
|
||||||
result: |
|
||||||
The Object that was returned by the invoked method, |
|
||||||
or null in case there was an error invoking the method. |
|
||||||
error: |
|
||||||
An Error object if there was an error invoking the method, |
|
||||||
or null if there was no error. |
|
||||||
id: |
|
||||||
The same id as the request it is responding to. |
|
||||||
|
|
||||||
Check the gorilla/rpc documentation for more details: |
|
||||||
|
|
||||||
http://gorilla-web.appspot.com/pkg/rpc
|
|
||||||
*/ |
|
||||||
package json |
|
@ -1,132 +0,0 @@ |
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package json |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/json" |
|
||||||
"errors" |
|
||||||
"net/http" |
|
||||||
"net/http/httptest" |
|
||||||
"reflect" |
|
||||||
"testing" |
|
||||||
|
|
||||||
"github.com/gorilla/rpc/v2" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
ErrResponseError = errors.New("response error") |
|
||||||
ErrResponseJsonError = &Error{Data: map[string]interface{}{ |
|
||||||
"stackstrace": map[string]interface{}{"0": "foo()"}, |
|
||||||
"error": "a message", |
|
||||||
}} |
|
||||||
) |
|
||||||
|
|
||||||
type Service1Request struct { |
|
||||||
A int |
|
||||||
B int |
|
||||||
} |
|
||||||
|
|
||||||
type Service1Response struct { |
|
||||||
Result int |
|
||||||
} |
|
||||||
|
|
||||||
type Service1 struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error { |
|
||||||
res.Result = req.A * req.B |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (t *Service1) ResponseError(r *http.Request, req *Service1Request, res *Service1Response) error { |
|
||||||
return ErrResponseError |
|
||||||
} |
|
||||||
|
|
||||||
func (t *Service1) ResponseJsonError(r *http.Request, req *Service1Request, res *Service1Response) error { |
|
||||||
return ErrResponseJsonError |
|
||||||
} |
|
||||||
|
|
||||||
func execute(t *testing.T, s *rpc.Server, method string, req, res interface{}) error { |
|
||||||
if !s.HasMethod(method) { |
|
||||||
t.Fatal("Expected to be registered:", method) |
|
||||||
} |
|
||||||
|
|
||||||
buf, _ := EncodeClientRequest(method, req) |
|
||||||
body := bytes.NewBuffer(buf) |
|
||||||
r, _ := http.NewRequest("POST", "http://localhost:8080/", body) |
|
||||||
r.Header.Set("Content-Type", "application/json") |
|
||||||
|
|
||||||
w := httptest.NewRecorder() |
|
||||||
s.ServeHTTP(w, r) |
|
||||||
|
|
||||||
return DecodeClientResponse(w.Body, res) |
|
||||||
} |
|
||||||
|
|
||||||
func executeRaw(t *testing.T, s *rpc.Server, req json.RawMessage) (int, *bytes.Buffer) { |
|
||||||
r, _ := http.NewRequest("POST", "http://localhost:8080/", bytes.NewBuffer(req)) |
|
||||||
r.Header.Set("Content-Type", "application/json") |
|
||||||
|
|
||||||
w := httptest.NewRecorder() |
|
||||||
s.ServeHTTP(w, r) |
|
||||||
|
|
||||||
return w.Code, w.Body |
|
||||||
} |
|
||||||
|
|
||||||
func field(name string, blob json.RawMessage) (v interface{}, ok bool) { |
|
||||||
var obj map[string]interface{} |
|
||||||
if err := json.Unmarshal(blob, &obj); err != nil { |
|
||||||
return nil, false |
|
||||||
} |
|
||||||
v, ok = obj[name] |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func TestService(t *testing.T) { |
|
||||||
s := rpc.NewServer() |
|
||||||
s.RegisterCodec(NewCodec(), "application/json") |
|
||||||
s.RegisterService(new(Service1), "") |
|
||||||
|
|
||||||
var res Service1Response |
|
||||||
if err := execute(t, s, "Service1.Multiply", &Service1Request{4, 2}, &res); err != nil { |
|
||||||
t.Error("Expected err to be nil, but got", err) |
|
||||||
} |
|
||||||
if res.Result != 8 { |
|
||||||
t.Error("Expected res.Result to be 8, but got", res.Result) |
|
||||||
} |
|
||||||
if err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err == nil { |
|
||||||
t.Errorf("Expected to get %q, but got nil", ErrResponseError) |
|
||||||
} else if err.Error() != ErrResponseError.Error() { |
|
||||||
t.Errorf("Expected to get %q, but got %q", ErrResponseError, err) |
|
||||||
} |
|
||||||
if code, res := executeRaw(t, s, json.RawMessage(`{"method":"Service1.Multiply","params":null,"id":5}`)); code != 400 { |
|
||||||
t.Error("Expected response code to be 400, but got", code) |
|
||||||
} else if v, ok := field("result", res.Bytes()); !ok || v != nil { |
|
||||||
t.Errorf("Expected ok to be true and v to be nil, but got %v and %v", ok, v) |
|
||||||
} |
|
||||||
if err := execute(t, s, "Service1.ResponseJsonError", &Service1Request{4, 2}, &res); err == nil { |
|
||||||
t.Errorf("Expected to get %q, but got nil", ErrResponseError) |
|
||||||
} else if jsonErr, ok := err.(*Error); !ok { |
|
||||||
t.Error("Expected err to be of a *json.Error type") |
|
||||||
} else if !reflect.DeepEqual(jsonErr.Data, ErrResponseJsonError.Data) { |
|
||||||
t.Errorf("Expected jsonErr to be %q, but got %q", ErrResponseJsonError, jsonErr) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestClientNullResult(t *testing.T) { |
|
||||||
data := `{"jsonrpc": "2.0", "id": 8674665223082153551, "result": null}` |
|
||||||
reader := bytes.NewReader([]byte(data)) |
|
||||||
|
|
||||||
var reply interface{} |
|
||||||
|
|
||||||
err := DecodeClientResponse(reader, &reply) |
|
||||||
if err == nil { |
|
||||||
t.Fatal(err) |
|
||||||
} |
|
||||||
if err.Error() != "Unexpected null result" { |
|
||||||
t.Fatalf("Unexpected error: %s", err) |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,39 @@ |
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
) |
||||||
|
|
||||||
|
type ErrorCode int |
||||||
|
|
||||||
|
const ( |
||||||
|
E_PARSE ErrorCode = -32700 |
||||||
|
E_INVALID_REQ ErrorCode = -32600 |
||||||
|
E_NO_METHOD ErrorCode = -32601 |
||||||
|
E_BAD_PARAMS ErrorCode = -32602 |
||||||
|
E_INTERNAL ErrorCode = -32603 |
||||||
|
E_SERVER ErrorCode = -32000 |
||||||
|
) |
||||||
|
|
||||||
|
var ErrNullResult = errors.New("result is null") |
||||||
|
|
||||||
|
type Error struct { |
||||||
|
// A Number that indicates the error type that occurred.
|
||||||
|
Code ErrorCode `json:"code"` /* required */ |
||||||
|
|
||||||
|
// A String providing a short description of the error.
|
||||||
|
// The message SHOULD be limited to a concise single sentence.
|
||||||
|
Message string `json:"message"` /* required */ |
||||||
|
|
||||||
|
// A Primitive or Structured value that contains additional information about the error.
|
||||||
|
Data interface{} `json:"data"` /* optional */ |
||||||
|
} |
||||||
|
|
||||||
|
func (e *Error) Error() string { |
||||||
|
return e.Message |
||||||
|
} |
@ -0,0 +1,161 @@ |
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package json2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
"net/http" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/gorilla/rpc/v2" |
||||||
|
) |
||||||
|
|
||||||
|
// ResponseRecorder is an implementation of http.ResponseWriter that
|
||||||
|
// records its mutations for later inspection in tests.
|
||||||
|
type ResponseRecorder struct { |
||||||
|
Code int // the HTTP response code from WriteHeader
|
||||||
|
HeaderMap http.Header // the HTTP response headers
|
||||||
|
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
|
||||||
|
Flushed bool |
||||||
|
} |
||||||
|
|
||||||
|
// NewRecorder returns an initialized ResponseRecorder.
|
||||||
|
func NewRecorder() *ResponseRecorder { |
||||||
|
return &ResponseRecorder{ |
||||||
|
HeaderMap: make(http.Header), |
||||||
|
Body: new(bytes.Buffer), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
|
||||||
|
// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
|
||||||
|
const DefaultRemoteAddr = "1.2.3.4" |
||||||
|
|
||||||
|
// Header returns the response headers.
|
||||||
|
func (rw *ResponseRecorder) Header() http.Header { |
||||||
|
return rw.HeaderMap |
||||||
|
} |
||||||
|
|
||||||
|
// Write always succeeds and writes to rw.Body, if not nil.
|
||||||
|
func (rw *ResponseRecorder) Write(buf []byte) (int, error) { |
||||||
|
if rw.Body != nil { |
||||||
|
rw.Body.Write(buf) |
||||||
|
} |
||||||
|
if rw.Code == 0 { |
||||||
|
rw.Code = http.StatusOK |
||||||
|
} |
||||||
|
return len(buf), nil |
||||||
|
} |
||||||
|
|
||||||
|
// WriteHeader sets rw.Code.
|
||||||
|
func (rw *ResponseRecorder) WriteHeader(code int) { |
||||||
|
rw.Code = code |
||||||
|
} |
||||||
|
|
||||||
|
// Flush sets rw.Flushed to true.
|
||||||
|
func (rw *ResponseRecorder) Flush() { |
||||||
|
rw.Flushed = true |
||||||
|
} |
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var ErrResponseError = errors.New("response error") |
||||||
|
|
||||||
|
type Service1Request struct { |
||||||
|
A int |
||||||
|
B int |
||||||
|
} |
||||||
|
|
||||||
|
type Service1BadRequest struct { |
||||||
|
V string `json:"jsonrpc"` |
||||||
|
M string `json:"method"` |
||||||
|
ID uint64 `json:"id"` |
||||||
|
} |
||||||
|
|
||||||
|
type Service1Response struct { |
||||||
|
Result int |
||||||
|
} |
||||||
|
|
||||||
|
type Service1 struct { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error { |
||||||
|
res.Result = req.A * req.B |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (t *Service1) ResponseError(r *http.Request, req *Service1Request, res *Service1Response) error { |
||||||
|
return ErrResponseError |
||||||
|
} |
||||||
|
|
||||||
|
func execute(t *testing.T, s *rpc.Server, method string, req, res interface{}) error { |
||||||
|
if !s.HasMethod(method) { |
||||||
|
t.Fatal("Expected to be registered:", method) |
||||||
|
} |
||||||
|
|
||||||
|
buf, _ := EncodeClientRequest(method, req) |
||||||
|
body := bytes.NewBuffer(buf) |
||||||
|
r, _ := http.NewRequest("POST", "http://localhost:8080/", body) |
||||||
|
r.Header.Set("Content-Type", "application/json") |
||||||
|
|
||||||
|
w := NewRecorder() |
||||||
|
s.ServeHTTP(w, r) |
||||||
|
|
||||||
|
return DecodeClientResponse(w.Body, res) |
||||||
|
} |
||||||
|
|
||||||
|
func executeRaw(t *testing.T, s *rpc.Server, req interface{}, res interface{}) error { |
||||||
|
j, _ := json.Marshal(req) |
||||||
|
r, _ := http.NewRequest("POST", "http://localhost:8080/", bytes.NewBuffer(j)) |
||||||
|
r.Header.Set("Content-Type", "application/json") |
||||||
|
|
||||||
|
w := NewRecorder() |
||||||
|
s.ServeHTTP(w, r) |
||||||
|
|
||||||
|
return DecodeClientResponse(w.Body, res) |
||||||
|
} |
||||||
|
|
||||||
|
func TestService(t *testing.T) { |
||||||
|
s := rpc.NewServer() |
||||||
|
s.RegisterCodec(NewCodec(), "application/json") |
||||||
|
s.RegisterService(new(Service1), "") |
||||||
|
|
||||||
|
var res Service1Response |
||||||
|
if err := execute(t, s, "Service1.Multiply", &Service1Request{4, 2}, &res); err != nil { |
||||||
|
t.Error("Expected err to be nil, but got:", err) |
||||||
|
} |
||||||
|
if res.Result != 8 { |
||||||
|
t.Errorf("Wrong response: %v.", res.Result) |
||||||
|
} |
||||||
|
|
||||||
|
if err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err == nil { |
||||||
|
t.Errorf("Expected to get %q, but got nil", ErrResponseError) |
||||||
|
} else if err.Error() != ErrResponseError.Error() { |
||||||
|
t.Errorf("Expected to get %q, but got %q", ErrResponseError, err) |
||||||
|
} |
||||||
|
|
||||||
|
if err := executeRaw(t, s, &Service1BadRequest{"2.0", "Service1.Multiply", 1}, &res); err == nil { |
||||||
|
t.Errorf("Expected error but error in nil") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestDecodeNullResult(t *testing.T) { |
||||||
|
data := `{"jsonrpc": "2.0", "id": 12345, "result": null}` |
||||||
|
reader := bytes.NewReader([]byte(data)) |
||||||
|
var result interface{} |
||||||
|
|
||||||
|
err := DecodeClientResponse(reader, &result) |
||||||
|
|
||||||
|
if err != ErrNullResult { |
||||||
|
t.Error("Expected err no be ErrNullResult, but got:", err) |
||||||
|
} |
||||||
|
|
||||||
|
if result != nil { |
||||||
|
t.Error("Expected result to be nil, but got:", result) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue