github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/conv/j2t/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 j2t
    18  
    19  import (
    20  	"context"
    21  
    22  	"github.com/cloudwego/dynamicgo/conv"
    23  	"github.com/cloudwego/dynamicgo/http"
    24  	"github.com/cloudwego/dynamicgo/meta"
    25  	"github.com/cloudwego/dynamicgo/thrift"
    26  )
    27  
    28  // HTTPConv is a converter from http request to thrift message
    29  type HTTPConv struct {
    30  	proto       meta.Encoding
    31  	st          *thrift.TypeDescriptor
    32  	top, bottom []byte
    33  }
    34  
    35  // NewHTTPConv returns a new HTTPConv, which contains the thrift message header and footer
    36  //
    37  // proto is specified thrift encoding protocol (meta.EncodingThriftBinary|meta.EncodingThriftCompact)
    38  // fnDesc is the thrift method descriptor corresponding to the http request url
    39  func NewHTTPConv(proto meta.Encoding, fnDesc *thrift.FunctionDescriptor) *HTTPConv {
    40  	firstField := fnDesc.Request().Struct().Fields()[0] // TODO: some idl may have more than one request
    41  	if firstField.Type().Type() != thrift.STRUCT {
    42  		panic("first request field doesn't have struct kind")
    43  	}
    44  
    45  	if proto != meta.EncodingThriftBinary {
    46  		panic("now only support binary protocol")
    47  	}
    48  
    49  	header, footer, err := thrift.GetBinaryMessageHeaderAndFooter(fnDesc.Name(), thrift.CALL, firstField.ID(), 0)
    50  	if err != nil {
    51  		panic(err)
    52  	}
    53  
    54  	return &HTTPConv{
    55  		proto: proto,
    56  		// if here is not exist, should panic, we don't have better solution
    57  		st:     firstField.Type(),
    58  		top:    header,
    59  		bottom: footer,
    60  	}
    61  }
    62  
    63  // Do converts http request into thrift message.
    64  // req body must be one of following:
    65  //   - json (application/json)
    66  //   - url encoded form (application/x-www-form-urlencoded)
    67  //   - empty
    68  func (h HTTPConv) Do(ctx context.Context, req http.RequestGetter, opt conv.Options) (tbytes []byte, err error) {
    69  	if h.proto != meta.EncodingThriftBinary {
    70  		panic("now only support binary protocol")
    71  	}
    72  	cv := NewBinaryConv(opt)
    73  	cv.opts.EnableHttpMapping = true
    74  	// dealing with http request
    75  	jbytes := req.GetBody()
    76  	// manage buffer
    77  	buf := conv.NewBytes()
    78  	// do translation
    79  	err = cv.do(ctx, jbytes, h.st, buf, req)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	// wrap the thrift message header and first fields' message
    84  	topLen := len(h.top)
    85  	bottomLen := len(h.bottom)
    86  	bufLen := len(*buf)
    87  	tbytes = make([]byte, topLen+bufLen+bottomLen)
    88  	copy(tbytes[:topLen], h.top)
    89  	copy(tbytes[topLen:topLen+bufLen], *buf)
    90  	copy(tbytes[topLen+bufLen:], h.bottom)
    91  	// reuse the buf buffer
    92  	conv.FreeBytes(buf)
    93  	return tbytes, nil
    94  }
    95  
    96  func (h HTTPConv) DoInto(ctx context.Context, req http.RequestGetter, buf *[]byte, opt conv.Options) (err error) {
    97  	if h.proto != meta.EncodingThriftBinary {
    98  		panic("now only support binary protocol")
    99  	}
   100  	cv := NewBinaryConv(opt)
   101  	cv.opts.EnableHttpMapping = true
   102  	// dealing with http request
   103  	jbytes := req.GetBody()
   104  	// write message header
   105  	*buf = append(*buf, h.top...)
   106  	// do translation
   107  	err = cv.do(ctx, jbytes, h.st, buf, req)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	// write thrift message end and first fields' message
   112  	*buf = append(*buf, h.bottom...)
   113  	return nil
   114  }