github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/conv/t2j/http_conv.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  package t2j
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/cloudwego/dynamicgo/conv"
    24  	"github.com/cloudwego/dynamicgo/http"
    25  	"github.com/cloudwego/dynamicgo/meta"
    26  	"github.com/cloudwego/dynamicgo/thrift"
    27  )
    28  
    29  // HTTPConv is a converter from thrift message to http response
    30  type HTTPConv struct {
    31  	proto meta.Encoding
    32  	st    *thrift.StructDescriptor
    33  }
    34  
    35  // NewHTTPConv returns a new HTTPConv
    36  func NewHTTPConv(proto meta.Encoding, desc *thrift.FunctionDescriptor) *HTTPConv {
    37  	switch proto {
    38  	case meta.EncodingThriftBinary:
    39  		// do nothing
    40  	default:
    41  		panic(fmt.Sprintf("protocol %#v is not supported", proto))
    42  	}
    43  
    44  	if desc.Response().Struct().FieldById(0) == nil {
    45  		panic("response field is not found in function")
    46  	}
    47  
    48  	return &HTTPConv{
    49  		proto: proto,
    50  		st:    desc.Response().Struct(),
    51  	}
    52  }
    53  
    54  // Do converts thrift message (tbytes) into http response.
    55  // resp body is set as json protocol if has
    56  func (h HTTPConv) Do(ctx context.Context, resp http.ResponseSetter, tbytes []byte, opt conv.Options) (err error) {
    57  	if h.proto != meta.EncodingThriftBinary {
    58  		panic("now only support binary protocol")
    59  	}
    60  	var stDef *thrift.TypeDescriptor
    61  	// not a reply struct, cannot be decoded
    62  	_, rTyp, _, respID, tbytes, err := thrift.UnwrapBinaryMessage(tbytes)
    63  	if err != nil {
    64  		return wrapError(meta.ErrRead, "", err)
    65  	}
    66  	if rTyp != thrift.REPLY {
    67  		field := h.st.FieldById(respID)
    68  		if field == nil {
    69  			return wrapError(meta.ErrUnknownField, "exception field is not foud in function", nil)
    70  		}
    71  		stDef = field.Type()
    72  	} else {
    73  		if respID != 0 {
    74  			return wrapError(meta.ErrInvalidParam, fmt.Sprintf("unexpected response field id %d", respID), nil)
    75  		}
    76  		stDef = h.st.FieldById(0).Type()
    77  	}
    78  
    79  	opt.EnableHttpMapping = true
    80  	cv := NewBinaryConv(opt)
    81  	// manage buffer
    82  	buf := conv.NewBytes()
    83  	// do translation
    84  	err = cv.do(ctx, tbytes, stDef, buf, resp)
    85  	if err != nil {
    86  		return
    87  	}
    88  
    89  	body := make([]byte, len(*buf))
    90  	copy(body, *buf)
    91  	resp.SetRawBody(body)
    92  
    93  	conv.FreeBytes(buf) // explicit leak by on panic
    94  	return
    95  }
    96  
    97  // DoInto converts the thrift message (tbytes) to into buf in JSON protocol, as well as other http response arguments.
    98  // WARN: This will set buf to resp, thus DONOT reuse the buf afterward.
    99  func (h HTTPConv) DoInto(ctx context.Context, resp http.ResponseSetter, tbytes []byte, buf *[]byte, opt conv.Options) (err error) {
   100  	if h.proto != meta.EncodingThriftBinary {
   101  		panic("now only support binary protocol")
   102  	}
   103  	var stDef *thrift.TypeDescriptor
   104  	// not a reply struct, cannot be decoded
   105  	_, rTyp, _, respID, tbytes, err := thrift.UnwrapBinaryMessage(tbytes)
   106  	if err != nil {
   107  		return wrapError(meta.ErrRead, "", err)
   108  	}
   109  	if rTyp != thrift.REPLY {
   110  		field := h.st.FieldById(respID)
   111  		if field == nil {
   112  			return wrapError(meta.ErrUnknownField, "exception field is not foud in function", nil)
   113  		}
   114  		stDef = field.Type()
   115  	} else {
   116  		if respID != 0 {
   117  			return wrapError(meta.ErrInvalidParam, fmt.Sprintf("unexpected response field id %d", respID), nil)
   118  		}
   119  		stDef = h.st.FieldById(0).Type()
   120  	}
   121  
   122  	opt.EnableHttpMapping = true
   123  	cv := NewBinaryConv(opt)
   124  	err = cv.do(ctx, tbytes, stDef, buf, resp)
   125  	if err != nil {
   126  		return
   127  	}
   128  	resp.SetRawBody(*buf)
   129  	return
   130  }