github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/thrift/annotation/http_mapping.go (about) 1 /** 2 * Copyright 2023 CloudWeGo Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // /* 18 // * Copyright 2021 CloudWeGo Authors 19 // * 20 // * Licensed under the Apache License, Version 2.0 (the "License"); 21 // * you may not use this file except in compliance with the License. 22 // * You may obtain a copy of the License at 23 // * 24 // * http://www.apache.org/licenses/LICENSE-2.0 25 // * 26 // * Unless required by applicable law or agreed to in writing, software 27 // * distributed under the License is distributed on an "AS IS" BASIS, 28 // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 // * See the License for the specific language governing permissions and 30 // * limitations under the License. 31 // */ 32 33 package annotation 34 35 import ( 36 "context" 37 "errors" 38 "fmt" 39 "strconv" 40 41 "github.com/cloudwego/dynamicgo/conv" 42 "github.com/cloudwego/dynamicgo/http" 43 "github.com/cloudwego/dynamicgo/internal/rt" 44 "github.com/cloudwego/dynamicgo/meta" 45 "github.com/cloudwego/dynamicgo/thrift" 46 "github.com/cloudwego/thriftgo/parser" 47 ) 48 49 const ( 50 APIQuery thrift.AnnoType = 1 + iota 51 APIPath 52 APIHeader 53 APICookie 54 APIBody 55 APIHTTPCode 56 APIRawBody 57 APIPostForm 58 APIRawUri 59 APINoBodyStruct 60 ) 61 62 type httpMappingAnnotation struct { 63 typ thrift.AnnoID 64 } 65 66 func newHttpMappingAnnotation(typ thrift.AnnoID) httpMappingAnnotation { 67 return httpMappingAnnotation{ 68 typ: typ, 69 } 70 } 71 72 func (self httpMappingAnnotation) ID() thrift.AnnoID { 73 return self.typ 74 } 75 76 func (self httpMappingAnnotation) Make(ctx context.Context, values []parser.Annotation, ast interface{}) (interface{}, error) { 77 if len(values) != 1 || len(values[0].Values) < 1 { 78 return nil, errors.New("httpMappingAnnotation only accept single key and value") 79 } 80 value := values[0].Values[0] 81 switch t := self.typ.Type(); t { 82 case APIQuery: 83 return apiQuery{value: value}, nil 84 case APIPath: 85 return apiPath{value: value}, nil 86 case APIHeader: 87 return apiHeader{value: value}, nil 88 case APICookie: 89 return apiCookie{value: value}, nil 90 case APIBody: 91 return apiBody{value: value}, nil 92 case APIHTTPCode: 93 return apiHTTPCode{}, nil 94 case APIRawBody: 95 return apiRawBody{}, nil 96 case APIPostForm: 97 return apiPostForm{value: value}, nil 98 case APIRawUri: 99 return apiRawUri{}, nil 100 case APINoBodyStruct: 101 return apiNoBodyStruct{}, nil 102 default: 103 return nil, errNotImplemented(fmt.Sprintf("unsupported type %d of http-mapping annotation", t)) 104 } 105 } 106 107 type apiPostForm struct { 108 value string // == xx 109 } 110 111 func (m apiPostForm) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 112 v := req.GetPostForm(m.value) 113 if v == "" { 114 return "", errNotFound(m.value, "postform") 115 } 116 return v, nil 117 } 118 119 func (apiPostForm) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 120 return errNotImplemented("apiPostForm not support http response!") 121 } 122 123 func (apiPostForm) Encoding() meta.Encoding { 124 return meta.EncodingJSON 125 } 126 127 type apiQuery struct { 128 value string // == xx 129 } 130 131 func (m apiQuery) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 132 v := req.GetQuery(m.value) 133 if v == "" { 134 return "", errNotFound(m.value, "query") 135 } 136 return v, nil 137 } 138 139 func (apiQuery) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 140 return errNotImplemented("apiQuery not support http response!") 141 } 142 143 func (apiQuery) Encoding() meta.Encoding { 144 return meta.EncodingJSON 145 } 146 147 // api.path = xx 148 type apiPath struct { 149 value string 150 } 151 152 func (m apiPath) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 153 v := req.GetParam(m.value) 154 if v == "" { 155 return "", errNotFound(m.value, "url path") 156 } 157 return v, nil 158 } 159 160 func (apiPath) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 161 return errNotImplemented("apiPath not support http response!") 162 } 163 164 func (apiPath) Encoding() meta.Encoding { 165 return meta.EncodingJSON 166 } 167 168 // api.header = xx 169 type apiHeader struct { 170 value string 171 } 172 173 func (m apiHeader) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 174 v := req.GetHeader(m.value) 175 if v == "" { 176 return "", errNotFound(m.value, "request header") 177 } 178 return v, nil 179 } 180 181 func (m apiHeader) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 182 return resp.SetHeader(m.value, val) 183 } 184 185 func (apiHeader) Encoding() meta.Encoding { 186 return meta.EncodingJSON 187 } 188 189 // api.cookie = xx 190 type apiCookie struct { 191 value string 192 } 193 194 func (m apiCookie) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 195 v := req.GetCookie(m.value) 196 if v == "" { 197 return "", errNotFound(m.value, "request cookie") 198 } 199 return v, nil 200 } 201 202 func (m apiCookie) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 203 return resp.SetCookie(m.value, val) 204 } 205 206 func (apiCookie) Encoding() meta.Encoding { 207 return meta.EncodingJSON 208 } 209 210 // api.body = xx 211 type apiBody struct { 212 value string 213 } 214 215 func (m apiBody) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 216 v := req.GetMapBody(m.value) 217 if v == "" { 218 return "", errNotFound(m.value, "body") 219 } 220 return v, nil 221 } 222 223 func (m apiBody) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 224 // v, err := convertAny(val, field.Type().Type()) 225 // if err != nil { 226 // return err 227 // } 228 // resp.Body[m.value] = val 229 return errNotImplemented("apiBody not support http response!") 230 } 231 232 func (apiBody) Encoding() meta.Encoding { 233 return meta.EncodingJSON 234 } 235 236 type apiHTTPCode struct{} 237 238 func (m apiHTTPCode) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 239 return "", errNotImplemented("apiBody not support http request!") 240 } 241 242 func (m apiHTTPCode) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 243 i, err := strconv.Atoi(val) 244 if err != nil { 245 return err 246 } 247 return resp.SetStatusCode(i) 248 } 249 250 func (apiHTTPCode) Encoding() meta.Encoding { 251 return meta.EncodingJSON 252 } 253 254 type apiNone struct{} 255 256 func (m apiNone) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 257 return "", nil 258 } 259 260 func (m apiNone) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 261 return nil 262 } 263 264 func (apiNone) Encoding() meta.Encoding { 265 return meta.EncodingJSON 266 } 267 268 type apiRawBody struct{} 269 270 func (apiRawBody) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 271 // OPT: pass unsafe buffer to conv.DoNative may cause panic 272 return string(req.GetBody()), nil 273 } 274 275 func (apiRawBody) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 276 return resp.SetRawBody(rt.Str2Mem(val)) 277 } 278 279 func (apiRawBody) Encoding() meta.Encoding { 280 return meta.EncodingJSON 281 } 282 283 type apiRawUri struct{} 284 285 func (m apiRawUri) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 286 return req.GetUri(), nil 287 } 288 289 func (m apiRawUri) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 290 return nil 291 } 292 293 func (apiRawUri) Encoding() meta.Encoding { 294 return meta.EncodingJSON 295 } 296 297 type apiNoBodyStruct struct{} 298 299 func (m apiNoBodyStruct) Request(ctx context.Context, req http.RequestGetter, field *thrift.FieldDescriptor) (string, error) { 300 if field.Type().Type() != thrift.STRUCT { 301 return "", fmt.Errorf("apiNoBodyStruct only support STRUCT type, but got %v", field.Type().Type()) 302 } 303 304 var opts conv.Options 305 if v := ctx.Value(conv.CtxKeyConvOptions); v != nil { 306 op1, ok := v.(conv.Options) 307 if !ok { 308 op2, ok := v.(*conv.Options) 309 if !ok { 310 return "", fmt.Errorf("invalid conv options type: %T", v) 311 } 312 opts = *op2 313 } 314 opts = op1 315 } 316 317 p := thrift.NewBinaryProtocolBuffer() 318 // p.WriteStructBegin(field.Name()) 319 for _, f := range field.Type().Struct().HttpMappingFields() { 320 var ok bool 321 var val string 322 for _, hm := range f.HTTPMappings() { 323 v, err := hm.Request(ctx, req, f) 324 if err == nil { 325 val = v 326 ok = true 327 break 328 } 329 } 330 p.WriteFieldBegin(f.Name(), f.Type().Type(), f.ID()) 331 if !ok || val == "" { 332 p.WriteDefaultOrEmpty(f) 333 } else { 334 // TODO: pass conv options to decide 335 p.WriteStringWithDesc(val, f.Type(), opts.DisallowUnknownField, !opts.NoBase64Binary) 336 } 337 // p.WriteFieldEnd() 338 } 339 p.WriteStructEnd() 340 out := make([]byte, len(p.Buf)) 341 copy(out, p.Buf) 342 p.Recycle() 343 return rt.Mem2Str(out), nil 344 } 345 346 func (m apiNoBodyStruct) Response(ctx context.Context, resp http.ResponseSetter, field *thrift.FieldDescriptor, val string) error { 347 return errNotImplemented("apiNoBodyStruct not support http Response!") 348 } 349 350 func (apiNoBodyStruct) Encoding() meta.Encoding { 351 return meta.EncodingThriftBinary 352 }