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  }