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 }