trpc.group/trpc-go/trpc-go@v1.0.3/http/codec.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package http
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"encoding/base64"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"net/http"
    24  	"os"
    25  	"path"
    26  	"strconv"
    27  	"strings"
    28  	"time"
    29  
    30  	trpcpb "trpc.group/trpc/trpc-protocol/pb/go/trpc"
    31  
    32  	"trpc.group/trpc-go/trpc-go/codec"
    33  	"trpc.group/trpc-go/trpc-go/errs"
    34  	icodec "trpc.group/trpc-go/trpc-go/internal/codec"
    35  )
    36  
    37  // Constants of header keys related to trpc.
    38  const (
    39  	TrpcVersion     = "trpc-version"
    40  	TrpcCallType    = "trpc-call-type"
    41  	TrpcMessageType = "trpc-message-type"
    42  	TrpcRequestID   = "trpc-request-id"
    43  	TrpcTimeout     = "trpc-timeout"
    44  	TrpcCaller      = "trpc-caller"
    45  	TrpcCallee      = "trpc-callee"
    46  	TrpcTransInfo   = "trpc-trans-info"
    47  	TrpcEnv         = "trpc-env"
    48  	TrpcDyeingKey   = "trpc-dyeing-key"
    49  	// TrpcErrorMessage used to pass error messages,
    50  	// contains user code's error or frame errors (such as validation framework).
    51  	TrpcErrorMessage = "trpc-error-msg"
    52  	// TrpcFrameworkErrorCode used to pass the error code reported by framework.
    53  	TrpcFrameworkErrorCode = "trpc-ret"
    54  	// TrpcUserFuncErrorCode used to pass the error code reported by user.
    55  	TrpcUserFuncErrorCode = "trpc-func-ret"
    56  	// Connection is used to set whether connect mode is "Connection".
    57  	Connection = "Connection"
    58  )
    59  
    60  var contentTypeSerializationType = map[string]int{
    61  	"application/json":                  codec.SerializationTypeJSON,
    62  	"application/protobuf":              codec.SerializationTypePB,
    63  	"application/x-protobuf":            codec.SerializationTypePB,
    64  	"application/pb":                    codec.SerializationTypePB,
    65  	"application/proto":                 codec.SerializationTypePB,
    66  	"application/flatbuffer":            codec.SerializationTypeFlatBuffer,
    67  	"application/octet-stream":          codec.SerializationTypeNoop,
    68  	"application/x-www-form-urlencoded": codec.SerializationTypeForm,
    69  	"application/xml":                   codec.SerializationTypeXML,
    70  	"text/xml":                          codec.SerializationTypeTextXML,
    71  	"multipart/form-data":               codec.SerializationTypeFormData,
    72  }
    73  
    74  var serializationTypeContentType = map[int]string{
    75  	codec.SerializationTypeJSON:       "application/json",
    76  	codec.SerializationTypePB:         "application/proto",
    77  	codec.SerializationTypeFlatBuffer: "application/flatbuffer",
    78  	codec.SerializationTypeNoop:       "application/octet-stream",
    79  	codec.SerializationTypeForm:       "application/x-www-form-urlencoded",
    80  	codec.SerializationTypeXML:        "application/xml",
    81  	codec.SerializationTypeTextXML:    "text/xml",
    82  	codec.SerializationTypeFormData:   "multipart/form-data",
    83  }
    84  
    85  var contentEncodingCompressType = map[string]int{
    86  	"gzip": codec.CompressTypeGzip,
    87  }
    88  
    89  var compressTypeContentEncoding = map[int]string{
    90  	codec.CompressTypeGzip: "gzip",
    91  }
    92  
    93  // RegisterSerializer registers a new custom serialization method,
    94  // such as RegisterSerializer("text/plain", 130, xxxSerializer).
    95  func RegisterSerializer(httpContentType string, serializationType int, serializer codec.Serializer) {
    96  	codec.RegisterSerializer(serializationType, serializer)
    97  	RegisterContentType(httpContentType, serializationType)
    98  }
    99  
   100  // RegisterContentType registers existing serialization method to
   101  // contentTypeSerializationType and serializationTypeContentType.
   102  func RegisterContentType(httpContentType string, serializationType int) {
   103  	contentTypeSerializationType[httpContentType] = serializationType
   104  	serializationTypeContentType[serializationType] = httpContentType
   105  }
   106  
   107  // SetContentType sets one-way mapping relationship for compatibility
   108  // with old framework services, allowing multiple http content type to
   109  // map to the save trpc serialization type.
   110  // Tell the framework which serialization method to use to parse this content-type.
   111  // Such as, a non-standard http server returns content type seems to be "text/html",
   112  // but it is actually "json" data. At this time, you can set it like this:
   113  // SetContentType("text/html", codec.SerializationTypeJSON).
   114  func SetContentType(httpContentType string, serializationType int) {
   115  	contentTypeSerializationType[httpContentType] = serializationType
   116  }
   117  
   118  // RegisterContentEncoding registers an existing decompression method,
   119  // such as RegisterContentEncoding("gzip", codec.CompressTypeGzip).
   120  func RegisterContentEncoding(httpContentEncoding string, compressType int) {
   121  	contentEncodingCompressType[httpContentEncoding] = compressType
   122  	compressTypeContentEncoding[compressType] = httpContentEncoding
   123  }
   124  
   125  // RegisterStatus registers trpc return code to http status.
   126  func RegisterStatus[T errs.ErrCode](code T, httpStatus int) {
   127  	ErrsToHTTPStatus[trpcpb.TrpcRetCode(code)] = httpStatus
   128  }
   129  
   130  func init() {
   131  	codec.Register("http", DefaultServerCodec, DefaultClientCodec)
   132  	codec.Register("http2", DefaultServerCodec, DefaultClientCodec)
   133  	// Support no protocol file custom routing and feature isolation.
   134  	codec.Register("http_no_protocol", DefaultNoProtocolServerCodec, DefaultClientCodec)
   135  	codec.Register("http2_no_protocol", DefaultNoProtocolServerCodec, DefaultClientCodec)
   136  }
   137  
   138  var (
   139  	// DefaultClientCodec is the default http client codec.
   140  	DefaultClientCodec = &ClientCodec{}
   141  
   142  	// DefaultServerCodec is the default http server codec.
   143  	DefaultServerCodec = &ServerCodec{
   144  		AutoGenTrpcHead:              true,
   145  		ErrHandler:                   defaultErrHandler,
   146  		RspHandler:                   defaultRspHandler,
   147  		AutoReadBody:                 true,
   148  		DisableEncodeTransInfoBase64: false,
   149  	}
   150  
   151  	// DefaultNoProtocolServerCodec is the default http no protocol server codec.
   152  	DefaultNoProtocolServerCodec = &ServerCodec{
   153  		AutoGenTrpcHead:              true,
   154  		ErrHandler:                   defaultErrHandler,
   155  		RspHandler:                   defaultRspHandler,
   156  		AutoReadBody:                 false,
   157  		DisableEncodeTransInfoBase64: false,
   158  	}
   159  )
   160  
   161  // ErrEncodeMissingHeader defines error used for special handling
   162  // in transport when ctx lost header information.
   163  var ErrEncodeMissingHeader = errors.New("trpc/http: server encode missing http header in context")
   164  
   165  // ServerCodec is the encoder/decoder for HTTP server.
   166  type ServerCodec struct {
   167  	// AutoGenTrpcHead converts trpc header automatically.
   168  	// Auto conversion could be enabled by setting http.DefaultServerCodec.AutoGenTrpcHead with true.
   169  	AutoGenTrpcHead bool
   170  
   171  	// ErrHandler is error code handle function, which is filled into header by default.
   172  	// Business can set this with http.DefaultServerCodec.ErrHandler = func(rsp, req, err) {}.
   173  	ErrHandler ErrorHandler
   174  
   175  	// RspHandler returns the data handle function. By default, data is returned directly.
   176  	// Business can customize this method to shape returned data.
   177  	// Business can set this with http.DefaultServerCodec.RspHandler = func(rsp, req, rspBody) {}.
   178  	RspHandler ResponseHandler
   179  
   180  	// AutoReadBody reads http request body automatically.
   181  	AutoReadBody bool
   182  
   183  	// DisableEncodeTransInfoBase64 indicates whether to disable encoding the transinfo value by base64.
   184  	DisableEncodeTransInfoBase64 bool
   185  }
   186  
   187  // ContextKey defines context key of http.
   188  type ContextKey string
   189  
   190  const (
   191  	// ContextKeyHeader key of http header
   192  	ContextKeyHeader = ContextKey("TRPC_SERVER_HTTP_HEADER")
   193  	// ParseMultipartFormMaxMemory maximum memory for parsing request body, default is 32M.
   194  	ParseMultipartFormMaxMemory int64 = 32 << 20
   195  )
   196  
   197  // Header encapsulates http context.
   198  type Header struct {
   199  	ReqBody  []byte
   200  	Request  *http.Request
   201  	Response http.ResponseWriter
   202  }
   203  
   204  // ClientReqHeader encapsulates http client context.
   205  // Setting ClientReqHeader is not allowed when NewClientProxy is waiting for the init of Client.
   206  // Setting ClientReqHeader is needed for each RPC.
   207  type ClientReqHeader struct {
   208  	// Schema should be named as scheme according to https://www.rfc-editor.org/rfc/rfc3986#section-3.
   209  	// Now that it has been exported, we can do nothing more than add a comment here.
   210  	Schema  string // Examples: HTTP, HTTPS.
   211  	Method  string
   212  	Host    string
   213  	Request *http.Request
   214  	Header  http.Header
   215  	ReqBody io.Reader
   216  }
   217  
   218  // AddHeader adds http header.
   219  func (h *ClientReqHeader) AddHeader(key string, value string) {
   220  	if h.Header == nil {
   221  		h.Header = make(http.Header)
   222  	}
   223  	h.Header.Add(key, value)
   224  }
   225  
   226  // ClientRspHeader encapsulates the context returned by http client request.
   227  type ClientRspHeader struct {
   228  	// ManualReadBody is used to control whether to read http response manually
   229  	// (not read automatically by the framework).
   230  	// Set it to true so that you can read data directly from Response.Body manually.
   231  	// The default value is false.
   232  	ManualReadBody bool
   233  	Response       *http.Response
   234  }
   235  
   236  // ErrsToHTTPStatus maps from framework errs retcode to http status code.
   237  var ErrsToHTTPStatus = map[trpcpb.TrpcRetCode]int{
   238  	errs.RetServerDecodeFail:   http.StatusBadRequest,
   239  	errs.RetServerEncodeFail:   http.StatusInternalServerError,
   240  	errs.RetServerNoService:    http.StatusNotFound,
   241  	errs.RetServerNoFunc:       http.StatusNotFound,
   242  	errs.RetServerTimeout:      http.StatusGatewayTimeout,
   243  	errs.RetServerOverload:     http.StatusTooManyRequests,
   244  	errs.RetServerSystemErr:    http.StatusInternalServerError,
   245  	errs.RetServerAuthFail:     http.StatusUnauthorized,
   246  	errs.RetServerValidateFail: http.StatusBadRequest,
   247  	errs.RetUnknown:            http.StatusInternalServerError,
   248  }
   249  
   250  // Head gets the corresponding http header from context.
   251  func Head(ctx context.Context) *Header {
   252  	if ret, ok := ctx.Value(ContextKeyHeader).(*Header); ok {
   253  		return ret
   254  	}
   255  	return nil
   256  }
   257  
   258  // Request gets the corresponding http request from context.
   259  func Request(ctx context.Context) *http.Request {
   260  	head := Head(ctx)
   261  	if head == nil {
   262  		return nil
   263  	}
   264  	return head.Request
   265  }
   266  
   267  // Response gets the corresponding http response from context.
   268  func Response(ctx context.Context) http.ResponseWriter {
   269  	head := Head(ctx)
   270  	if head == nil {
   271  		return nil
   272  	}
   273  	return head.Response
   274  }
   275  
   276  // WithHeader sets http header in context.
   277  func WithHeader(ctx context.Context, value *Header) context.Context {
   278  	return context.WithValue(ctx, ContextKeyHeader, value)
   279  }
   280  
   281  // setReqHeader sets request header.
   282  func (sc *ServerCodec) setReqHeader(head *Header, msg codec.Msg) error {
   283  	if !sc.AutoGenTrpcHead { // Auto generates trpc head.
   284  		return nil
   285  	}
   286  
   287  	trpcReq := &trpcpb.RequestProtocol{}
   288  	msg.WithServerReqHead(trpcReq)
   289  	msg.WithServerRspHead(trpcReq)
   290  
   291  	trpcReq.Func = []byte(msg.ServerRPCName())
   292  	trpcReq.ContentType = uint32(msg.SerializationType())
   293  	trpcReq.ContentEncoding = uint32(msg.CompressType())
   294  
   295  	if v := head.Request.Header.Get(TrpcVersion); v != "" {
   296  		i, _ := strconv.Atoi(v)
   297  		trpcReq.Version = uint32(i)
   298  	}
   299  	if v := head.Request.Header.Get(TrpcCallType); v != "" {
   300  		i, _ := strconv.Atoi(v)
   301  		trpcReq.CallType = uint32(i)
   302  	}
   303  	if v := head.Request.Header.Get(TrpcMessageType); v != "" {
   304  		i, _ := strconv.Atoi(v)
   305  		trpcReq.MessageType = uint32(i)
   306  	}
   307  	if v := head.Request.Header.Get(TrpcRequestID); v != "" {
   308  		i, _ := strconv.Atoi(v)
   309  		trpcReq.RequestId = uint32(i)
   310  	}
   311  	if v := head.Request.Header.Get(TrpcTimeout); v != "" {
   312  		i, _ := strconv.Atoi(v)
   313  		trpcReq.Timeout = uint32(i)
   314  		msg.WithRequestTimeout(time.Millisecond * time.Duration(i))
   315  	}
   316  	if v := head.Request.Header.Get(TrpcCaller); v != "" {
   317  		trpcReq.Caller = []byte(v)
   318  		msg.WithCallerServiceName(v)
   319  	}
   320  	if v := head.Request.Header.Get(TrpcCallee); v != "" {
   321  		trpcReq.Callee = []byte(v)
   322  		msg.WithCalleeServiceName(v)
   323  	}
   324  
   325  	msg.WithDyeing((trpcReq.GetMessageType() & uint32(trpcpb.TrpcMessageType_TRPC_DYEING_MESSAGE)) != 0)
   326  
   327  	if v := head.Request.Header.Get(TrpcTransInfo); v != "" {
   328  		transInfo, err := unmarshalTransInfo(msg, v)
   329  		if err != nil {
   330  			return err
   331  		}
   332  		trpcReq.TransInfo = transInfo
   333  	}
   334  	return nil
   335  }
   336  
   337  func unmarshalTransInfo(msg codec.Msg, v string) (map[string][]byte, error) {
   338  	m := make(map[string]string)
   339  	if err := codec.Unmarshal(codec.SerializationTypeJSON, []byte(v), &m); err != nil {
   340  		return nil, err
   341  	}
   342  	transInfo := make(map[string][]byte)
   343  	// Since the http header can only transmit plaintext, but trpc transinfo is binary stream,
   344  	// so it needs to be protected with base64.
   345  	for k, v := range m {
   346  		decoded, err := base64.StdEncoding.DecodeString(v)
   347  		if err != nil {
   348  			decoded = []byte(v)
   349  		}
   350  		transInfo[k] = decoded
   351  		if k == TrpcEnv {
   352  			msg.WithEnvTransfer(string(decoded))
   353  		}
   354  		if k == TrpcDyeingKey {
   355  			msg.WithDyeingKey(string(decoded))
   356  		}
   357  	}
   358  	msg.WithServerMetaData(transInfo)
   359  	return transInfo, nil
   360  }
   361  
   362  // getReqbody gets the body of request.
   363  func (sc *ServerCodec) getReqbody(head *Header, msg codec.Msg) ([]byte, error) {
   364  	msg.WithCalleeMethod(head.Request.URL.Path)
   365  	msg.WithServerRPCName(head.Request.URL.Path)
   366  
   367  	if !sc.AutoReadBody {
   368  		return nil, nil
   369  	}
   370  
   371  	var reqBody []byte
   372  	if head.Request.Method == http.MethodGet {
   373  		msg.WithSerializationType(codec.SerializationTypeGet)
   374  		reqBody = []byte(head.Request.URL.RawQuery)
   375  	} else {
   376  		var exist bool
   377  		msg.WithSerializationType(codec.SerializationTypeJSON)
   378  		ct := head.Request.Header.Get("Content-Type")
   379  		for contentType, serializationType := range contentTypeSerializationType {
   380  			if strings.Contains(ct, contentType) {
   381  				msg.WithSerializationType(serializationType)
   382  				exist = true
   383  				break
   384  			}
   385  		}
   386  		if exist {
   387  			var err error
   388  			reqBody, err = getBody(ct, head.Request)
   389  			if err != nil {
   390  				return nil, err
   391  			}
   392  		}
   393  	}
   394  	head.ReqBody = reqBody
   395  	return reqBody, nil
   396  }
   397  
   398  // getBody gets the body of request.
   399  func getBody(contentType string, r *http.Request) ([]byte, error) {
   400  	if strings.Contains(contentType, serializationTypeContentType[codec.SerializationTypeFormData]) {
   401  		if r.Form == nil {
   402  			if err := r.ParseMultipartForm(ParseMultipartFormMaxMemory); err != nil {
   403  				return nil, fmt.Errorf("parse multipart form: %w", err)
   404  			}
   405  		}
   406  		return []byte(r.Form.Encode()), nil
   407  	}
   408  	body, err := io.ReadAll(r.Body)
   409  	if err != nil {
   410  		return nil, fmt.Errorf("body readAll: %w", err)
   411  	}
   412  	// Reset body and allow multiple reads.
   413  	// Refer to testcase: TestCoexistenceOfHTTPRPCAndNoProtocol.
   414  	r.Body.Close()
   415  	r.Body = io.NopCloser(bytes.NewReader(body))
   416  	return body, nil
   417  }
   418  
   419  // updateMsg updates msg.
   420  func (sc *ServerCodec) updateMsg(head *Header, msg codec.Msg) {
   421  	ce := head.Request.Header.Get("Content-Encoding")
   422  	if ce != "" {
   423  		msg.WithCompressType(contentEncodingCompressType[ce])
   424  	}
   425  
   426  	// Update upstream service name.
   427  	if msg.CallerServiceName() == "" {
   428  		msg.WithCallerServiceName("trpc.http.upserver.upservice")
   429  	}
   430  
   431  	// Update current service name.
   432  	if msg.CalleeServiceName() == "" {
   433  		msg.WithCalleeServiceName(fmt.Sprintf("trpc.http.%s.service", path.Base(os.Args[0])))
   434  	}
   435  }
   436  
   437  // Decode decodes http header.
   438  // http server transport has filled all the data of request into ctx,
   439  // and reqBuf here is empty.
   440  func (sc *ServerCodec) Decode(msg codec.Msg, _ []byte) ([]byte, error) {
   441  	head := Head(msg.Context())
   442  	if head == nil {
   443  		return nil, errors.New("server decode missing http header in context")
   444  	}
   445  
   446  	reqBody, err := sc.getReqbody(head, msg)
   447  	if err != nil {
   448  		return nil, err
   449  	}
   450  	if err := sc.setReqHeader(head, msg); err != nil {
   451  		return nil, err
   452  	}
   453  
   454  	sc.updateMsg(head, msg)
   455  	return reqBody, nil
   456  }
   457  
   458  // ErrorHandler handles error of http server's response.
   459  // By default, the error code is placed in header,
   460  // which can be replaced by a specific implementation of user.
   461  type ErrorHandler func(w http.ResponseWriter, r *http.Request, e *errs.Error)
   462  
   463  var defaultErrHandler = func(w http.ResponseWriter, _ *http.Request, e *errs.Error) {
   464  	errMsg := strings.Replace(e.Msg, "\r", "\\r", -1)
   465  	errMsg = strings.Replace(errMsg, "\n", "\\n", -1)
   466  
   467  	w.Header().Add(TrpcErrorMessage, errMsg)
   468  	if e.Type == errs.ErrorTypeFramework {
   469  		w.Header().Add(TrpcFrameworkErrorCode, strconv.Itoa(int(e.Code)))
   470  	} else {
   471  		w.Header().Add(TrpcUserFuncErrorCode, strconv.Itoa(int(e.Code)))
   472  	}
   473  
   474  	if code, ok := ErrsToHTTPStatus[e.Code]; ok {
   475  		w.WriteHeader(code)
   476  	}
   477  }
   478  
   479  // ResponseHandler handles data of http server's response.
   480  // By default, the content is returned directly,
   481  // which can replaced by a specific implementation of user.
   482  type ResponseHandler func(w http.ResponseWriter, r *http.Request, rspBody []byte) error
   483  
   484  var defaultRspHandler = func(w http.ResponseWriter, _ *http.Request, rspBody []byte) error {
   485  	if len(rspBody) == 0 {
   486  		return nil
   487  	}
   488  	if _, err := w.Write(rspBody); err != nil {
   489  		return fmt.Errorf("http write response error: %s", err.Error())
   490  	}
   491  	return nil
   492  }
   493  
   494  // Encode sets http header.
   495  // The buffer of the returned packet has been written to the response writer in header,
   496  // no need to return rspBuf.
   497  func (sc *ServerCodec) Encode(msg codec.Msg, rspBody []byte) (b []byte, err error) {
   498  	head := Head(msg.Context())
   499  	if head == nil {
   500  		return nil, ErrEncodeMissingHeader
   501  	}
   502  	req := head.Request
   503  	rsp := head.Response
   504  	ctKey := "Content-Type"
   505  
   506  	rsp.Header().Add("X-Content-Type-Options", "nosniff")
   507  	ct := rsp.Header().Get(ctKey)
   508  	if ct == "" {
   509  		ct = req.Header.Get(ctKey)
   510  		if req.Method == http.MethodGet || ct == "" {
   511  			ct = "application/json"
   512  		}
   513  		rsp.Header().Add(ctKey, ct)
   514  	}
   515  	if strings.Contains(ct, serializationTypeContentType[codec.SerializationTypeFormData]) {
   516  		formDataCt := getFormDataContentType()
   517  		rsp.Header().Set(ctKey, formDataCt)
   518  	}
   519  
   520  	if len(msg.ServerMetaData()) > 0 {
   521  		m := make(map[string]string)
   522  		for k, v := range msg.ServerMetaData() {
   523  			if sc.DisableEncodeTransInfoBase64 {
   524  				m[k] = string(v)
   525  				continue
   526  			}
   527  			m[k] = base64.StdEncoding.EncodeToString(v)
   528  		}
   529  		val, _ := codec.Marshal(codec.SerializationTypeJSON, m)
   530  		rsp.Header().Set("trpc-trans-info", string(val))
   531  	}
   532  
   533  	// Return packet tells client to use which decompress method.
   534  	if t := msg.CompressType(); icodec.IsValidCompressType(t) && t != codec.CompressTypeNoop {
   535  		rsp.Header().Add("Content-Encoding", compressTypeContentEncoding[t])
   536  	}
   537  
   538  	// 1. Handle exceptions first, as long as server returns an error,
   539  	// the returned data will no longer be processed.
   540  	if e := msg.ServerRspErr(); e != nil {
   541  		if sc.ErrHandler != nil {
   542  			sc.ErrHandler(rsp, req, e)
   543  		}
   544  		return
   545  	}
   546  	// 2. process returned data under normal case.
   547  	if sc.RspHandler != nil {
   548  		if err := sc.RspHandler(rsp, req, rspBody); err != nil {
   549  			return nil, err
   550  		}
   551  	}
   552  	return nil, nil
   553  }
   554  
   555  // ClientCodec decodes http client request.
   556  type ClientCodec struct{}
   557  
   558  // Encode sets metadata requested by http client.
   559  // Client has been serialized and passed to reqBody with compress.
   560  func (c *ClientCodec) Encode(msg codec.Msg, reqBody []byte) ([]byte, error) {
   561  	var reqHeader *ClientReqHeader
   562  	if msg.ClientReqHead() != nil { // User himself has set http client req header.
   563  		httpReqHeader, ok := msg.ClientReqHead().(*ClientReqHeader)
   564  		if !ok {
   565  			return nil, errors.New("http header must be type of *http.ClientReqHeader")
   566  		}
   567  		reqHeader = httpReqHeader
   568  	} else {
   569  		reqHeader = &ClientReqHeader{}
   570  		msg.WithClientReqHead(reqHeader)
   571  	}
   572  
   573  	if reqHeader.Method == "" {
   574  		if len(reqBody) == 0 {
   575  			reqHeader.Method = http.MethodGet
   576  		} else {
   577  			reqHeader.Method = http.MethodPost
   578  		}
   579  	}
   580  
   581  	if msg.ClientRspHead() != nil { // User himself has set http client rsp header.
   582  		_, ok := msg.ClientRspHead().(*ClientRspHeader)
   583  		if !ok {
   584  			return nil, errors.New("http header must be type of *http.ClientRspHeader")
   585  		}
   586  	} else {
   587  		msg.WithClientRspHead(&ClientRspHeader{})
   588  	}
   589  
   590  	c.updateMsg(msg)
   591  	return reqBody, nil
   592  }
   593  
   594  // Decode parses metadata in http client's response.
   595  func (c *ClientCodec) Decode(msg codec.Msg, _ []byte) ([]byte, error) {
   596  	rspHeader, ok := msg.ClientRspHead().(*ClientRspHeader)
   597  	if !ok {
   598  		return nil, errors.New("rsp header must be type of *http.ClientRspHeader")
   599  	}
   600  
   601  	var (
   602  		body []byte
   603  		err  error
   604  	)
   605  	rsp := rspHeader.Response
   606  	if rsp.Body != nil && !rspHeader.ManualReadBody {
   607  		defer rsp.Body.Close()
   608  		if body, err = io.ReadAll(rsp.Body); err != nil {
   609  			return nil, fmt.Errorf("readall http body fail: %w", err)
   610  		}
   611  		// Reset body and allow multiple read.
   612  		rsp.Body.Close()
   613  		rsp.Body = io.NopCloser(bytes.NewReader(body))
   614  	}
   615  
   616  	if val := rsp.Header.Get("Content-Encoding"); val != "" {
   617  		msg.WithCompressType(contentEncodingCompressType[val])
   618  	}
   619  	ct := rsp.Header.Get("Content-Type")
   620  	for contentType, serializationType := range contentTypeSerializationType {
   621  		if strings.Contains(ct, contentType) {
   622  			msg.WithSerializationType(serializationType)
   623  			break
   624  		}
   625  	}
   626  	if val := rsp.Header.Get(TrpcFrameworkErrorCode); val != "" {
   627  		i, _ := strconv.Atoi(val)
   628  		if i != 0 {
   629  			e := &errs.Error{
   630  				Type: errs.ErrorTypeCalleeFramework,
   631  				Code: trpcpb.TrpcRetCode(i),
   632  				Desc: "trpc",
   633  				Msg:  rsp.Header.Get(TrpcErrorMessage),
   634  			}
   635  			msg.WithClientRspErr(e)
   636  			return nil, nil
   637  		}
   638  	}
   639  	if val := rsp.Header.Get(TrpcUserFuncErrorCode); val != "" {
   640  		i, _ := strconv.Atoi(val)
   641  		if i != 0 {
   642  			msg.WithClientRspErr(errs.New(i, rsp.Header.Get(TrpcErrorMessage)))
   643  			return nil, nil
   644  		}
   645  	}
   646  	if rsp.StatusCode >= http.StatusMultipleChoices {
   647  		e := &errs.Error{
   648  			Type: errs.ErrorTypeBusiness,
   649  			Code: trpcpb.TrpcRetCode(rsp.StatusCode),
   650  			Desc: "http",
   651  			Msg:  fmt.Sprintf("http client codec StatusCode: %s, body: %q", http.StatusText(rsp.StatusCode), body),
   652  		}
   653  		msg.WithClientRspErr(e)
   654  		return nil, nil
   655  	}
   656  	return body, nil
   657  }
   658  
   659  // updateMsg updates msg.
   660  func (c *ClientCodec) updateMsg(msg codec.Msg) {
   661  	if msg.CallerServiceName() == "" {
   662  		msg.WithCallerServiceName(fmt.Sprintf("trpc.http.%s.service", path.Base(os.Args[0])))
   663  	}
   664  }