storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/rpc/json2/json_test.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Copyright 2012 The Gorilla Authors. All rights reserved.
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  // Copyright 2020 MinIO, Inc. All rights reserved.
     7  // forked from https://github.com/gorilla/rpc/v2
     8  // modified to be used with MinIO under Apache
     9  // 2.0 license that can be found in the LICENSE file.
    10  
    11  package json2
    12  
    13  import (
    14  	"bytes"
    15  	"encoding/json"
    16  	"errors"
    17  	"net/http"
    18  	"strings"
    19  	"testing"
    20  
    21  	"storj.io/minio/pkg/rpc"
    22  )
    23  
    24  // ResponseRecorder is an implementation of http.ResponseWriter that
    25  // records its mutations for later inspection in tests.
    26  type ResponseRecorder struct {
    27  	Code      int           // the HTTP response code from WriteHeader
    28  	HeaderMap http.Header   // the HTTP response headers
    29  	Body      *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
    30  	Flushed   bool
    31  }
    32  
    33  // NewRecorder returns an initialized ResponseRecorder.
    34  func NewRecorder() *ResponseRecorder {
    35  	return &ResponseRecorder{
    36  		HeaderMap: make(http.Header),
    37  		Body:      new(bytes.Buffer),
    38  	}
    39  }
    40  
    41  // DefaultRemoteAddr is the default remote address to return in RemoteAddr if
    42  // an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
    43  const DefaultRemoteAddr = "1.2.3.4"
    44  
    45  // Header returns the response headers.
    46  func (rw *ResponseRecorder) Header() http.Header {
    47  	return rw.HeaderMap
    48  }
    49  
    50  // Write always succeeds and writes to rw.Body, if not nil.
    51  func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
    52  	if rw.Body != nil {
    53  		rw.Body.Write(buf)
    54  	}
    55  	if rw.Code == 0 {
    56  		rw.Code = http.StatusOK
    57  	}
    58  	return len(buf), nil
    59  }
    60  
    61  // WriteHeader sets rw.Code.
    62  func (rw *ResponseRecorder) WriteHeader(code int) {
    63  	rw.Code = code
    64  }
    65  
    66  // Flush sets rw.Flushed to true.
    67  func (rw *ResponseRecorder) Flush() {
    68  	rw.Flushed = true
    69  }
    70  
    71  // ----------------------------------------------------------------------------
    72  
    73  var ErrResponseError = errors.New("response error")
    74  var ErrMappedResponseError = errors.New("mapped response error")
    75  
    76  type Service1Request struct {
    77  	A int
    78  	B int
    79  }
    80  
    81  type Service1NoParamsRequest struct {
    82  	V  string `json:"jsonrpc"`
    83  	M  string `json:"method"`
    84  	ID uint64 `json:"id"`
    85  }
    86  
    87  type Service1ParamsArrayRequest struct {
    88  	V string `json:"jsonrpc"`
    89  	P []struct {
    90  		T string
    91  	} `json:"params"`
    92  	M  string `json:"method"`
    93  	ID uint64 `json:"id"`
    94  }
    95  
    96  type Service1Response struct {
    97  	Result int
    98  }
    99  
   100  type Service1 struct {
   101  }
   102  
   103  const Service1DefaultResponse = 9999
   104  
   105  func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error {
   106  	if req.A == 0 && req.B == 0 {
   107  		// Sentinel value for test with no params.
   108  		res.Result = Service1DefaultResponse
   109  	} else {
   110  		res.Result = req.A * req.B
   111  	}
   112  	return nil
   113  }
   114  
   115  func (t *Service1) ResponseError(r *http.Request, req *Service1Request, res *Service1Response) error {
   116  	return ErrResponseError
   117  }
   118  
   119  func (t *Service1) MappedResponseError(r *http.Request, req *Service1Request, res *Service1Response) error {
   120  	return ErrMappedResponseError
   121  }
   122  
   123  func execute(t *testing.T, s *rpc.Server, method string, req, res interface{}) error {
   124  	if !s.HasMethod(method) {
   125  		t.Fatal("Expected to be registered:", method)
   126  	}
   127  
   128  	buf, _ := EncodeClientRequest(method, req)
   129  	body := bytes.NewBuffer(buf)
   130  	r, _ := http.NewRequest("POST", "http://localhost:8080/", body)
   131  	r.Header.Set("Content-Type", "application/json")
   132  
   133  	w := NewRecorder()
   134  	s.ServeHTTP(w, r)
   135  
   136  	return DecodeClientResponse(w.Body, res)
   137  }
   138  
   139  func executeRaw(t *testing.T, s *rpc.Server, req interface{}, res interface{}) error {
   140  	j, _ := json.Marshal(req)
   141  	r, _ := http.NewRequest("POST", "http://localhost:8080/", bytes.NewBuffer(j))
   142  	r.Header.Set("Content-Type", "application/json")
   143  
   144  	w := NewRecorder()
   145  	s.ServeHTTP(w, r)
   146  
   147  	return DecodeClientResponse(w.Body, res)
   148  }
   149  
   150  func executeInvalidJSON(t *testing.T, s *rpc.Server, res interface{}) error {
   151  	r, _ := http.NewRequest("POST", "http://localhost:8080/", strings.NewReader(`not even a json`))
   152  	r.Header.Set("Content-Type", "application/json")
   153  
   154  	w := NewRecorder()
   155  	s.ServeHTTP(w, r)
   156  
   157  	return DecodeClientResponse(w.Body, res)
   158  }
   159  
   160  func TestService(t *testing.T) {
   161  	s := rpc.NewServer()
   162  	s.RegisterCodec(NewCodec(), "application/json")
   163  	s.RegisterService(new(Service1), "")
   164  
   165  	var res Service1Response
   166  	if err := execute(t, s, "Service1.Multiply", &Service1Request{4, 2}, &res); err != nil {
   167  		t.Error("Expected err to be nil, but got:", err)
   168  	}
   169  	if res.Result != 8 {
   170  		t.Errorf("Wrong response: %v.", res.Result)
   171  	}
   172  
   173  	if err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err == nil {
   174  		t.Errorf("Expected to get %q, but got nil", ErrResponseError)
   175  	} else if err.Error() != ErrResponseError.Error() {
   176  		t.Errorf("Expected to get %q, but got %q", ErrResponseError, err)
   177  	}
   178  
   179  	// No parameters.
   180  	res = Service1Response{}
   181  	if err := executeRaw(t, s, &Service1NoParamsRequest{"2.0", "Service1.Multiply", 1}, &res); err != nil {
   182  		t.Error(err)
   183  	}
   184  	if res.Result != Service1DefaultResponse {
   185  		t.Errorf("Wrong response: got %v, want %v", res.Result, Service1DefaultResponse)
   186  	}
   187  
   188  	// Parameters as by-position.
   189  	res = Service1Response{}
   190  	req := Service1ParamsArrayRequest{
   191  		V: "2.0",
   192  		P: []struct {
   193  			T string
   194  		}{{
   195  			T: "test",
   196  		}},
   197  		M:  "Service1.Multiply",
   198  		ID: 1,
   199  	}
   200  	if err := executeRaw(t, s, &req, &res); err != nil {
   201  		t.Error(err)
   202  	}
   203  	if res.Result != Service1DefaultResponse {
   204  		t.Errorf("Wrong response: got %v, want %v", res.Result, Service1DefaultResponse)
   205  	}
   206  
   207  	res = Service1Response{}
   208  	if err := executeInvalidJSON(t, s, &res); err == nil {
   209  		t.Error("Expected to receive an E_PARSE error, but got nil")
   210  	} else if jsonRpcErr, ok := err.(*Error); !ok {
   211  		t.Errorf("Expected to receive an Error, but got %T: %s", err, err)
   212  	} else if jsonRpcErr.Code != E_PARSE {
   213  		t.Errorf("Expected to receive an E_PARSE JSON-RPC error (%d) but got %d", E_PARSE, jsonRpcErr.Code)
   214  	}
   215  }
   216  
   217  func TestServiceWithErrorMapper(t *testing.T) {
   218  	const mappedErrorCode = 100
   219  
   220  	// errorMapper maps ErrMappedResponseError to an Error with mappedErrorCode Code, everything else is returned as-is
   221  	errorMapper := func(err error) error {
   222  		if err == ErrMappedResponseError {
   223  			return &Error{
   224  				Code:    mappedErrorCode,
   225  				Message: err.Error(),
   226  			}
   227  		}
   228  		// Map everything else to E_SERVER
   229  		return &Error{
   230  			Code:    E_SERVER,
   231  			Message: err.Error(),
   232  		}
   233  	}
   234  
   235  	s := rpc.NewServer()
   236  	s.RegisterCodec(NewCustomCodecWithErrorMapper(rpc.DefaultEncoderSelector, errorMapper), "application/json")
   237  	s.RegisterService(new(Service1), "")
   238  
   239  	var res Service1Response
   240  	if err := execute(t, s, "Service1.MappedResponseError", &Service1Request{4, 2}, &res); err == nil {
   241  		t.Errorf("Expected to get a JSON-RPC error, but got nil")
   242  	} else if jsonRpcErr, ok := err.(*Error); !ok {
   243  		t.Errorf("Expected to get an *Error, but got %T: %s", err, err)
   244  	} else if jsonRpcErr.Code != mappedErrorCode {
   245  		t.Errorf("Expected to get Code %d, but got %d", mappedErrorCode, jsonRpcErr.Code)
   246  	} else if jsonRpcErr.Message != ErrMappedResponseError.Error() {
   247  		t.Errorf("Expected to get Message %q, but got %q", ErrMappedResponseError.Error(), jsonRpcErr.Message)
   248  	}
   249  
   250  	// Unmapped error behaves as usual
   251  	if err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err == nil {
   252  		t.Errorf("Expected to get a JSON-RPC error, but got nil")
   253  	} else if jsonRpcErr, ok := err.(*Error); !ok {
   254  		t.Errorf("Expected to get an *Error, but got %T: %s", err, err)
   255  	} else if jsonRpcErr.Code != E_SERVER {
   256  		t.Errorf("Expected to get Code %d, but got %d", E_SERVER, jsonRpcErr.Code)
   257  	} else if jsonRpcErr.Message != ErrResponseError.Error() {
   258  		t.Errorf("Expected to get Message %q, but got %q", ErrResponseError.Error(), jsonRpcErr.Message)
   259  	}
   260  
   261  	// Malformed request without method: our framework tries to return an error: we shouldn't map that one
   262  	malformedRequest := struct {
   263  		V  string `json:"jsonrpc"`
   264  		ID string `json:"id"`
   265  	}{
   266  		V:  "3.0",
   267  		ID: "any",
   268  	}
   269  	if err := executeRaw(t, s, &malformedRequest, &res); err == nil {
   270  		t.Errorf("Expected to get a JSON-RPC error, but got nil")
   271  	} else if jsonRpcErr, ok := err.(*Error); !ok {
   272  		t.Errorf("Expected to get an *Error, but got %T: %s", err, err)
   273  	} else if jsonRpcErr.Code != E_INVALID_REQ {
   274  		t.Errorf("Expected to get an E_INVALID_REQ error (%d), but got %d", E_INVALID_REQ, jsonRpcErr.Code)
   275  	}
   276  }
   277  
   278  func TestDecodeNullResult(t *testing.T) {
   279  	data := `{"jsonrpc": "2.0", "id": 12345, "result": null}`
   280  	reader := bytes.NewReader([]byte(data))
   281  	var result interface{}
   282  
   283  	err := DecodeClientResponse(reader, &result)
   284  
   285  	if err != ErrNullResult {
   286  		t.Error("Expected err no be ErrNullResult, but got:", err)
   287  	}
   288  
   289  	if result != nil {
   290  		t.Error("Expected result to be nil, but got:", result)
   291  	}
   292  }