go.uber.org/yarpc@v1.72.1/encoding/json/register.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package json
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	"reflect"
    27  
    28  	"go.uber.org/yarpc/api/transport"
    29  )
    30  
    31  var (
    32  	_ctxType            = reflect.TypeOf((*context.Context)(nil)).Elem()
    33  	_errorType          = reflect.TypeOf((*error)(nil)).Elem()
    34  	_interfaceEmptyType = reflect.TypeOf((*interface{})(nil)).Elem()
    35  )
    36  
    37  // Register calls the RouteTable's Register method.
    38  //
    39  // This function exists for backwards compatibility only. It will be removed
    40  // in a future version.
    41  //
    42  // Deprecated: Use the RouteTable's Register method directly.
    43  func Register(r transport.RouteTable, rs []transport.Procedure) {
    44  	r.Register(rs)
    45  }
    46  
    47  // Procedure builds a Procedure from the given JSON handler. handler must be
    48  // a function with a signature similar to,
    49  //
    50  // 	f(ctx context.Context, body $reqBody) ($resBody, error)
    51  //
    52  // Where $reqBody and $resBody are a map[string]interface{} or pointers to
    53  // structs.
    54  func Procedure(name string, handler interface{}) []transport.Procedure {
    55  	return []transport.Procedure{
    56  		{
    57  			Name: name,
    58  			HandlerSpec: transport.NewUnaryHandlerSpec(
    59  				wrapUnaryHandler(name, handler),
    60  			),
    61  			Encoding: Encoding,
    62  		},
    63  	}
    64  }
    65  
    66  // OnewayProcedure builds a Procedure from the given JSON handler. handler must be
    67  // a function with a signature similar to,
    68  //
    69  // 	f(ctx context.Context, body $reqBody) error
    70  //
    71  // Where $reqBody is a map[string]interface{} or pointer to a struct.
    72  func OnewayProcedure(name string, handler interface{}) []transport.Procedure {
    73  	return []transport.Procedure{
    74  		{
    75  			Name: name,
    76  			HandlerSpec: transport.NewOnewayHandlerSpec(
    77  				wrapOnewayHandler(name, handler)),
    78  			Encoding: Encoding,
    79  		},
    80  	}
    81  }
    82  
    83  // wrapUnaryHandler takes a valid JSON handler function and converts it into a
    84  // transport.UnaryHandler.
    85  func wrapUnaryHandler(name string, handler interface{}) transport.UnaryHandler {
    86  	reqBodyType := verifyUnarySignature(name, reflect.TypeOf(handler))
    87  	return newJSONHandler(reqBodyType, handler)
    88  }
    89  
    90  // wrapOnewayHandler takes a valid JSON handler function and converts it into a
    91  // transport.OnewayHandler.
    92  func wrapOnewayHandler(name string, handler interface{}) transport.OnewayHandler {
    93  	reqBodyType := verifyOnewaySignature(name, reflect.TypeOf(handler))
    94  	return newJSONHandler(reqBodyType, handler)
    95  }
    96  
    97  func newJSONHandler(reqBodyType reflect.Type, handler interface{}) jsonHandler {
    98  	var r requestReader
    99  	if reqBodyType == _interfaceEmptyType {
   100  		r = ifaceEmptyReader{}
   101  	} else if reqBodyType.Kind() == reflect.Map {
   102  		r = mapReader{reqBodyType}
   103  	} else {
   104  		// struct ptr
   105  		r = structReader{reqBodyType.Elem()}
   106  	}
   107  
   108  	return jsonHandler{
   109  		reader:  r,
   110  		handler: reflect.ValueOf(handler),
   111  	}
   112  }
   113  
   114  // verifyUnarySignature verifies that the given type matches what we expect from
   115  // JSON unary handlers and returns the request type.
   116  func verifyUnarySignature(n string, t reflect.Type) reflect.Type {
   117  	reqBodyType := verifyInputSignature(n, t)
   118  
   119  	if t.NumOut() != 2 {
   120  		panic(fmt.Sprintf(
   121  			"expected handler for %q to have 2 results but it had %v",
   122  			n, t.NumOut(),
   123  		))
   124  	}
   125  
   126  	if t.Out(1) != _errorType {
   127  		panic(fmt.Sprintf(
   128  			"handler for %q must return error as its second reuslt, not %v",
   129  			n, t.Out(1),
   130  		))
   131  	}
   132  
   133  	resBodyType := t.Out(0)
   134  
   135  	if !isValidReqResType(resBodyType) {
   136  		panic(fmt.Sprintf(
   137  			"the first result of the handler for %q must be "+
   138  				"a struct pointer, a map[string]interface{}, or interface{], and not: %v",
   139  			n, resBodyType,
   140  		))
   141  	}
   142  
   143  	return reqBodyType
   144  }
   145  
   146  // verifyOnewaySignature verifies that the given type matches what we expect
   147  // from oneway JSON handlers.
   148  //
   149  // Returns the request type.
   150  func verifyOnewaySignature(n string, t reflect.Type) reflect.Type {
   151  	reqBodyType := verifyInputSignature(n, t)
   152  
   153  	if t.NumOut() != 1 {
   154  		panic(fmt.Sprintf(
   155  			"expected handler for %q to have 1 result but it had %v",
   156  			n, t.NumOut(),
   157  		))
   158  	}
   159  
   160  	if t.Out(0) != _errorType {
   161  		panic(fmt.Sprintf(
   162  			"the result of the handler for %q must be of type error, and not: %v",
   163  			n, t.Out(0),
   164  		))
   165  	}
   166  
   167  	return reqBodyType
   168  }
   169  
   170  // verifyInputSignature verifies that the given input argument types match
   171  // what we expect from JSON handlers and returns the request body type.
   172  func verifyInputSignature(n string, t reflect.Type) reflect.Type {
   173  	if t.Kind() != reflect.Func {
   174  		panic(fmt.Sprintf(
   175  			"handler for %q is not a function but a %v", n, t.Kind(),
   176  		))
   177  	}
   178  
   179  	if t.NumIn() != 2 {
   180  		panic(fmt.Sprintf(
   181  			"expected handler for %q to have 2 arguments but it had %v",
   182  			n, t.NumIn(),
   183  		))
   184  	}
   185  
   186  	if t.In(0) != _ctxType {
   187  		panic(fmt.Sprintf(
   188  			"the first argument of the handler for %q must be of type "+
   189  				"context.Context, and not: %v", n, t.In(0),
   190  		))
   191  
   192  	}
   193  
   194  	reqBodyType := t.In(1)
   195  
   196  	if !isValidReqResType(reqBodyType) {
   197  		panic(fmt.Sprintf(
   198  			"the second argument of the handler for %q must be "+
   199  				"a struct pointer, a map[string]interface{}, or interface{}, and not: %v",
   200  			n, reqBodyType,
   201  		))
   202  	}
   203  
   204  	return reqBodyType
   205  }
   206  
   207  // isValidReqResType checks if the given type is a pointer to a struct, a
   208  // map[string]interface{}, or a interface{}.
   209  func isValidReqResType(t reflect.Type) bool {
   210  	return (t == _interfaceEmptyType) ||
   211  		(t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct) ||
   212  		(t.Kind() == reflect.Map && t.Key().Kind() == reflect.String)
   213  }