mirror of
https://github.com/minio/minio.git
synced 2025-11-09 21:49:46 -05:00
Implement HTTP POST based RPC (#5840)
Added support for new RPC support using HTTP POST. RPC's arguments and reply are Gob encoded and sent as HTTP request/response body. This patch also removes Go RPC based implementation.
This commit is contained in:
404
cmd/rpc_test.go
Normal file
404
cmd/rpc_test.go
Normal file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
xrpc "github.com/minio/minio/cmd/rpc"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
)
|
||||
|
||||
func TestAuthArgsAuthenticate(t *testing.T) {
|
||||
tmpGlobalServerConfig := globalServerConfig
|
||||
defer func() {
|
||||
globalServerConfig = tmpGlobalServerConfig
|
||||
}()
|
||||
globalServerConfig = newServerConfig()
|
||||
|
||||
case1Args := AuthArgs{
|
||||
Token: newAuthToken(),
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
RequestTime: UTCNow(),
|
||||
}
|
||||
|
||||
case2Args := AuthArgs{
|
||||
Token: newAuthToken(),
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
RequestTime: UTCNow().Add(15 * time.Minute),
|
||||
}
|
||||
|
||||
case3Args := AuthArgs{
|
||||
Token: newAuthToken(),
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
RequestTime: UTCNow().Add(-16 * time.Minute),
|
||||
}
|
||||
|
||||
case4Args := AuthArgs{
|
||||
Token: newAuthToken(),
|
||||
RPCVersion: RPCVersion{99, 99, 99},
|
||||
RequestTime: UTCNow(),
|
||||
}
|
||||
|
||||
case5Args := AuthArgs{
|
||||
Token: "invalid-token",
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
RequestTime: UTCNow(),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
args AuthArgs
|
||||
expectErr bool
|
||||
}{
|
||||
{case1Args, false},
|
||||
{case2Args, false},
|
||||
{case3Args, true},
|
||||
{case4Args, true},
|
||||
{case5Args, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
err := testCase.args.Authenticate()
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthArgsSetAuthArgs(t *testing.T) {
|
||||
tmpGlobalServerConfig := globalServerConfig
|
||||
defer func() {
|
||||
globalServerConfig = tmpGlobalServerConfig
|
||||
}()
|
||||
globalServerConfig = newServerConfig()
|
||||
|
||||
case1Args := AuthArgs{
|
||||
Token: newAuthToken(),
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
RequestTime: UTCNow(),
|
||||
}
|
||||
|
||||
case2Args := AuthArgs{
|
||||
Token: newAuthToken(),
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
RequestTime: UTCNow().Add(15 * time.Minute),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
args *AuthArgs
|
||||
authArgs AuthArgs
|
||||
expectedResult *AuthArgs
|
||||
}{
|
||||
{&AuthArgs{}, case1Args, &case1Args},
|
||||
{&case2Args, case1Args, &case1Args},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
testCase.args.SetAuthArgs(testCase.authArgs)
|
||||
result := testCase.args
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRPCClientArgsValidate(t *testing.T) {
|
||||
case1URL, err := xnet.ParseURL("http://localhost:12345/rpc")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
case1Args := RPCClientArgs{
|
||||
NewAuthTokenFunc: newAuthToken,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceName: "Arith",
|
||||
ServiceURL: case1URL,
|
||||
TLSConfig: nil,
|
||||
}
|
||||
|
||||
case2URL, err := xnet.ParseURL("https://localhost:12345/rpc")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
case2Args := RPCClientArgs{
|
||||
NewAuthTokenFunc: newAuthToken,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceName: "Arith",
|
||||
ServiceURL: case1URL,
|
||||
TLSConfig: &tls.Config{},
|
||||
}
|
||||
|
||||
case3Args := RPCClientArgs{
|
||||
NewAuthTokenFunc: nil,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceName: "Arith",
|
||||
ServiceURL: case1URL,
|
||||
TLSConfig: &tls.Config{},
|
||||
}
|
||||
|
||||
case4Args := RPCClientArgs{
|
||||
NewAuthTokenFunc: newAuthToken,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceURL: case1URL,
|
||||
TLSConfig: &tls.Config{},
|
||||
}
|
||||
|
||||
case5URL, err := xnet.ParseURL("ftp://localhost:12345/rpc")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
case5Args := RPCClientArgs{
|
||||
NewAuthTokenFunc: newAuthToken,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceName: "Arith",
|
||||
ServiceURL: case5URL,
|
||||
TLSConfig: &tls.Config{},
|
||||
}
|
||||
|
||||
case6URL, err := xnet.ParseURL("http://localhost:12345/rpc?location")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
case6Args := RPCClientArgs{
|
||||
NewAuthTokenFunc: newAuthToken,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceName: "Arith",
|
||||
ServiceURL: case6URL,
|
||||
TLSConfig: &tls.Config{},
|
||||
}
|
||||
|
||||
case7Args := RPCClientArgs{
|
||||
NewAuthTokenFunc: newAuthToken,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceName: "Arith",
|
||||
ServiceURL: case2URL,
|
||||
TLSConfig: nil,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
args RPCClientArgs
|
||||
expectErr bool
|
||||
}{
|
||||
{case1Args, false},
|
||||
{case2Args, false},
|
||||
// NewAuthTokenFunc must not be empty error.
|
||||
{case3Args, true},
|
||||
// ServiceName must not be empty.
|
||||
{case4Args, true},
|
||||
// unknown RPC URL error.
|
||||
{case5Args, true},
|
||||
// unknown RPC URL error.
|
||||
{case6Args, true},
|
||||
// tls configuration must not be empty for https url error.
|
||||
{case7Args, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
err := testCase.args.validate()
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Args struct {
|
||||
AuthArgs
|
||||
A, B int
|
||||
}
|
||||
|
||||
type Quotient struct {
|
||||
Quo, Rem int
|
||||
}
|
||||
|
||||
type Arith struct{}
|
||||
|
||||
func (t *Arith) Multiply(args *Args, reply *int) error {
|
||||
*reply = args.A * args.B
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Arith) Divide(args *Args, quo *Quotient) error {
|
||||
if args.B == 0 {
|
||||
return errors.New("divide by zero")
|
||||
}
|
||||
quo.Quo = args.A / args.B
|
||||
quo.Rem = args.A % args.B
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRPCClientCall(t *testing.T) {
|
||||
tmpGlobalServerConfig := globalServerConfig
|
||||
defer func() {
|
||||
globalServerConfig = tmpGlobalServerConfig
|
||||
}()
|
||||
globalServerConfig = newServerConfig()
|
||||
|
||||
rpcServer := xrpc.NewServer()
|
||||
if err := rpcServer.RegisterName("Arith", &Arith{}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rpcServer.ServeHTTP(w, r)
|
||||
}))
|
||||
defer httpServer.Close()
|
||||
|
||||
url, err := xnet.ParseURL(httpServer.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
rpcClient, err := NewRPCClient(RPCClientArgs{
|
||||
NewAuthTokenFunc: newAuthToken,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceName: "Arith",
|
||||
ServiceURL: url,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
var case1Result int
|
||||
case1ExpectedResult := 19 * 8
|
||||
|
||||
testCases := []struct {
|
||||
serviceMethod string
|
||||
args interface {
|
||||
SetAuthArgs(args AuthArgs)
|
||||
}
|
||||
result interface{}
|
||||
changeConfig bool
|
||||
expectedResult interface{}
|
||||
expectErr bool
|
||||
}{
|
||||
{"Arith.Multiply", &Args{A: 19, B: 8}, &case1Result, false, &case1ExpectedResult, false},
|
||||
{"Arith.Divide", &Args{A: 19, B: 8}, &Quotient{}, false, &Quotient{2, 3}, false},
|
||||
{"Arith.Multiply", &Args{A: 19, B: 8}, &case1Result, true, &case1ExpectedResult, false},
|
||||
{"Arith.Divide", &Args{A: 19, B: 8}, &Quotient{}, true, &Quotient{2, 3}, false},
|
||||
{"Arith.Divide", &Args{A: 19, B: 0}, &Quotient{}, false, nil, true},
|
||||
{"Arith.Divide", &Args{A: 19, B: 8}, &case1Result, false, nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
if testCase.changeConfig {
|
||||
globalServerConfig = newServerConfig()
|
||||
}
|
||||
|
||||
err := rpcClient.Call(testCase.serviceMethod, testCase.args, testCase.result)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(testCase.result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, testCase.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRPCClientClose(t *testing.T) {
|
||||
tmpGlobalServerConfig := globalServerConfig
|
||||
defer func() {
|
||||
globalServerConfig = tmpGlobalServerConfig
|
||||
}()
|
||||
globalServerConfig = newServerConfig()
|
||||
|
||||
url, err := xnet.ParseURL("http://localhost:12345")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
rpcClient, err := NewRPCClient(RPCClientArgs{
|
||||
NewAuthTokenFunc: newAuthToken,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceName: "Arith",
|
||||
ServiceURL: url,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
rpcClient *RPCClient
|
||||
expectErr bool
|
||||
}{
|
||||
{rpcClient, false},
|
||||
// Double close.
|
||||
{rpcClient, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
err := testCase.rpcClient.Close()
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRPCClientServiceURL(t *testing.T) {
|
||||
tmpGlobalServerConfig := globalServerConfig
|
||||
defer func() {
|
||||
globalServerConfig = tmpGlobalServerConfig
|
||||
}()
|
||||
globalServerConfig = newServerConfig()
|
||||
|
||||
url, err := xnet.ParseURL("http://localhost:12345")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
rpcClient, err := NewRPCClient(RPCClientArgs{
|
||||
NewAuthTokenFunc: newAuthToken,
|
||||
RPCVersion: globalRPCAPIVersion,
|
||||
ServiceName: "Arith",
|
||||
ServiceURL: url,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
case1Result, err := xnet.ParseURL("http://localhost:12345")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
testCases := []struct {
|
||||
rpcClient *RPCClient
|
||||
expectedResult *xnet.URL
|
||||
}{
|
||||
{rpcClient, case1Result},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.rpcClient.ServiceURL()
|
||||
|
||||
if !reflect.DeepEqual(result, testCase.expectedResult) {
|
||||
t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user