github.com/cloudwego/kitex@v0.9.0/pkg/remote/codec/header_codec_test.go (about)

     1  /*
     2   * Copyright 2021 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 codec
    18  
    19  import (
    20  	"context"
    21  	"encoding/binary"
    22  	"net"
    23  	"testing"
    24  
    25  	"github.com/bytedance/gopkg/cloud/metainfo"
    26  
    27  	"github.com/cloudwego/kitex/internal/mocks"
    28  	"github.com/cloudwego/kitex/internal/test"
    29  	"github.com/cloudwego/kitex/pkg/discovery"
    30  	"github.com/cloudwego/kitex/pkg/remote"
    31  	"github.com/cloudwego/kitex/pkg/remote/codec/perrors"
    32  	"github.com/cloudwego/kitex/pkg/remote/transmeta"
    33  	"github.com/cloudwego/kitex/pkg/rpcinfo"
    34  	"github.com/cloudwego/kitex/pkg/rpcinfo/remoteinfo"
    35  	"github.com/cloudwego/kitex/pkg/serviceinfo"
    36  	tm "github.com/cloudwego/kitex/pkg/transmeta"
    37  	"github.com/cloudwego/kitex/transport"
    38  )
    39  
    40  var mockPayloadLen = 100
    41  
    42  func TestTTHeaderCodec(t *testing.T) {
    43  	ctx := context.Background()
    44  	sendMsg := initClientSendMsg(transport.TTHeader)
    45  
    46  	// encode
    47  	out := remote.NewWriterBuffer(256)
    48  	totalLenField, err := ttHeaderCodec.encode(ctx, sendMsg, out)
    49  	binary.BigEndian.PutUint32(totalLenField, uint32(out.MallocLen()-Size32+mockPayloadLen))
    50  	test.Assert(t, err == nil, err)
    51  
    52  	// decode
    53  	recvMsg := initServerRecvMsg()
    54  	buf, err := out.Bytes()
    55  	test.Assert(t, err == nil, err)
    56  	in := remote.NewReaderBuffer(buf)
    57  	err = ttHeaderCodec.decode(ctx, recvMsg, in)
    58  	test.Assert(t, err == nil, err)
    59  	test.Assert(t, recvMsg.PayloadLen() == mockPayloadLen, recvMsg.PayloadLen())
    60  }
    61  
    62  func TestTTHeaderCodecWithTransInfo(t *testing.T) {
    63  	ctx := context.Background()
    64  	intKVInfo := prepareIntKVInfo()
    65  	strKVInfo := prepareStrKVInfo()
    66  	sendMsg := initClientSendMsg(transport.TTHeader)
    67  	sendMsg.TransInfo().PutTransIntInfo(intKVInfo)
    68  	sendMsg.TransInfo().PutTransStrInfo(strKVInfo)
    69  	sendMsg.Tags()[HeaderFlagsKey] = HeaderFlagSupportOutOfOrder
    70  
    71  	// encode
    72  	out := remote.NewWriterBuffer(256)
    73  	totalLenField, err := ttHeaderCodec.encode(ctx, sendMsg, out)
    74  	binary.BigEndian.PutUint32(totalLenField, uint32(out.MallocLen()-Size32+mockPayloadLen))
    75  	test.Assert(t, err == nil, err)
    76  
    77  	// decode
    78  	recvMsg := initServerRecvMsg()
    79  	buf, err := out.Bytes()
    80  	test.Assert(t, err == nil, err)
    81  	in := remote.NewReaderBuffer(buf)
    82  	err = ttHeaderCodec.decode(ctx, recvMsg, in)
    83  	test.Assert(t, err == nil, err)
    84  	test.Assert(t, recvMsg.PayloadLen() == mockPayloadLen, recvMsg.PayloadLen())
    85  
    86  	intKVInfoRecv := recvMsg.TransInfo().TransIntInfo()
    87  	strKVInfoRecv := recvMsg.TransInfo().TransStrInfo()
    88  	test.DeepEqual(t, intKVInfoRecv, intKVInfo)
    89  	test.DeepEqual(t, strKVInfoRecv, strKVInfo)
    90  	flag := recvMsg.Tags()[HeaderFlagsKey]
    91  	test.Assert(t, flag != nil)
    92  	test.Assert(t, flag == HeaderFlagSupportOutOfOrder)
    93  }
    94  
    95  func TestTTHeaderCodecWithTransInfoWithGDPRToken(t *testing.T) {
    96  	ctx := context.Background()
    97  	intKVInfo := prepareIntKVInfo()
    98  	strKVInfo := prepareStrKVInfoWithGDPRToken()
    99  	sendMsg := initClientSendMsg(transport.TTHeader)
   100  	sendMsg.TransInfo().PutTransIntInfo(intKVInfo)
   101  	sendMsg.TransInfo().PutTransStrInfo(strKVInfo)
   102  	sendMsg.Tags()[HeaderFlagsKey] = HeaderFlagSupportOutOfOrder
   103  
   104  	// encode
   105  	out := remote.NewWriterBuffer(256)
   106  	totalLenField, err := ttHeaderCodec.encode(ctx, sendMsg, out)
   107  	binary.BigEndian.PutUint32(totalLenField, uint32(out.MallocLen()-Size32+mockPayloadLen))
   108  	test.Assert(t, err == nil, err)
   109  
   110  	// decode
   111  	recvMsg := initServerRecvMsg()
   112  	buf, err := out.Bytes()
   113  	test.Assert(t, err == nil, err)
   114  	in := remote.NewReaderBuffer(buf)
   115  	err = ttHeaderCodec.decode(ctx, recvMsg, in)
   116  	test.Assert(t, err == nil, err)
   117  	test.Assert(t, recvMsg.PayloadLen() == mockPayloadLen, recvMsg.PayloadLen())
   118  
   119  	intKVInfoRecv := recvMsg.TransInfo().TransIntInfo()
   120  	strKVInfoRecv := recvMsg.TransInfo().TransStrInfo()
   121  	test.DeepEqual(t, intKVInfoRecv, intKVInfo)
   122  	test.DeepEqual(t, strKVInfoRecv, strKVInfo)
   123  	flag := recvMsg.Tags()[HeaderFlagsKey]
   124  	test.Assert(t, flag != nil)
   125  	test.Assert(t, flag == HeaderFlagSupportOutOfOrder)
   126  }
   127  
   128  func TestTTHeaderCodecWithTransInfoFromMetaInfoGDPRToken(t *testing.T) {
   129  	ctx := context.Background()
   130  	intKVInfo := prepareIntKVInfo()
   131  	ctx = metainfo.WithValue(ctx, "gdpr-token", "test token")
   132  	sendMsg := initClientSendMsg(transport.TTHeader)
   133  	sendMsg.TransInfo().PutTransIntInfo(intKVInfo)
   134  	ctx, err := tm.MetainfoClientHandler.WriteMeta(ctx, sendMsg)
   135  	test.Assert(t, err == nil)
   136  	sendMsg.Tags()[HeaderFlagsKey] = HeaderFlagSupportOutOfOrder
   137  
   138  	// encode
   139  	out := remote.NewWriterBuffer(256)
   140  	totalLenField, err := ttHeaderCodec.encode(ctx, sendMsg, out)
   141  	binary.BigEndian.PutUint32(totalLenField, uint32(out.MallocLen()-Size32+mockPayloadLen))
   142  	test.Assert(t, err == nil, err)
   143  
   144  	// decode
   145  	recvMsg := initServerRecvMsg()
   146  	buf, err := out.Bytes()
   147  	test.Assert(t, err == nil, err)
   148  	in := remote.NewReaderBuffer(buf)
   149  	err = ttHeaderCodec.decode(ctx, recvMsg, in)
   150  	test.Assert(t, err == nil, err)
   151  	test.Assert(t, recvMsg.PayloadLen() == mockPayloadLen, recvMsg.PayloadLen())
   152  
   153  	intKVInfoRecv := recvMsg.TransInfo().TransIntInfo()
   154  	strKVInfoRecv := recvMsg.TransInfo().TransStrInfo()
   155  	test.DeepEqual(t, intKVInfoRecv, intKVInfo)
   156  	test.DeepEqual(t, strKVInfoRecv, map[string]string{transmeta.GDPRToken: "test token"})
   157  	flag := recvMsg.Tags()[HeaderFlagsKey]
   158  	test.Assert(t, flag != nil)
   159  	test.Assert(t, flag == HeaderFlagSupportOutOfOrder)
   160  }
   161  
   162  func TestFillBasicInfoOfTTHeader(t *testing.T) {
   163  	ctx := context.Background()
   164  	mockAddr := "mock address"
   165  	// 1. server side fill from address
   166  	// encode
   167  	sendMsg := initClientSendMsg(transport.TTHeader)
   168  	sendMsg.TransInfo().TransStrInfo()[transmeta.HeaderTransRemoteAddr] = mockAddr
   169  	sendMsg.TransInfo().TransIntInfo()[transmeta.FromService] = mockServiceName
   170  	out := remote.NewWriterBuffer(256)
   171  	totalLenField, err := ttHeaderCodec.encode(ctx, sendMsg, out)
   172  	binary.BigEndian.PutUint32(totalLenField, uint32(out.MallocLen()-Size32+mockPayloadLen))
   173  	test.Assert(t, err == nil, err)
   174  	// decode
   175  	recvMsg := initServerRecvMsg()
   176  	buf, err := out.Bytes()
   177  	test.Assert(t, err == nil, err)
   178  	in := remote.NewReaderBuffer(buf)
   179  	err = ttHeaderCodec.decode(ctx, recvMsg, in)
   180  	test.Assert(t, err == nil, err)
   181  	test.Assert(t, recvMsg.TransInfo().TransStrInfo()[transmeta.HeaderTransRemoteAddr] == mockAddr)
   182  	test.Assert(t, recvMsg.RPCInfo().From().Address().String() == mockAddr)
   183  	test.Assert(t, recvMsg.RPCInfo().From().ServiceName() == mockServiceName)
   184  
   185  	// 2. client side fill to address
   186  	// encode
   187  	sendMsg = initServerSendMsg(transport.TTHeader)
   188  	sendMsg.TransInfo().TransStrInfo()[transmeta.HeaderTransRemoteAddr] = mockAddr
   189  	out = remote.NewWriterBuffer(256)
   190  	totalLenField, err = ttHeaderCodec.encode(ctx, sendMsg, out)
   191  	binary.BigEndian.PutUint32(totalLenField, uint32(out.MallocLen()-Size32+mockPayloadLen))
   192  	test.Assert(t, err == nil, err)
   193  	// decode
   194  	recvMsg = initClientRecvMsg()
   195  	toInfo := remoteinfo.AsRemoteInfo(recvMsg.RPCInfo().To())
   196  	toInfo.SetInstance(&mockInst{})
   197  	buf, err = out.Bytes()
   198  	test.Assert(t, err == nil, err)
   199  	in = remote.NewReaderBuffer(buf)
   200  	err = ttHeaderCodec.decode(ctx, recvMsg, in)
   201  	test.Assert(t, err == nil, err)
   202  	test.Assert(t, recvMsg.TransInfo().TransStrInfo()[transmeta.HeaderTransRemoteAddr] == mockAddr)
   203  	test.Assert(t, toInfo.Address().String() == mockAddr, toInfo.Address())
   204  }
   205  
   206  func BenchmarkTTHeaderCodec(b *testing.B) {
   207  	ctx := context.Background()
   208  
   209  	b.ResetTimer()
   210  	for i := 0; i < b.N; i++ {
   211  		sendMsg := initClientSendMsg(transport.TTHeader)
   212  		// encode
   213  		out := remote.NewWriterBuffer(256)
   214  		totalLenField, err := ttHeaderCodec.encode(ctx, sendMsg, out)
   215  		binary.BigEndian.PutUint32(totalLenField, uint32(out.MallocLen()-Size32+mockPayloadLen))
   216  		test.Assert(b, err == nil, err)
   217  
   218  		// decode
   219  		recvMsg := initServerRecvMsg()
   220  		buf, err := out.Bytes()
   221  		test.Assert(b, err == nil, err)
   222  		in := remote.NewReaderBuffer(buf)
   223  		err = ttHeaderCodec.decode(ctx, recvMsg, in)
   224  		test.Assert(b, recvMsg.PayloadLen() == mockPayloadLen, recvMsg.PayloadLen())
   225  		test.Assert(b, err == nil, err)
   226  	}
   227  }
   228  
   229  func BenchmarkTTHeaderWithTransInfoParallel(b *testing.B) {
   230  	ctx := context.Background()
   231  	intKVInfo := prepareIntKVInfo()
   232  	strKVInfo := prepareStrKVInfo()
   233  
   234  	b.ResetTimer()
   235  	b.RunParallel(func(pb *testing.PB) {
   236  		for pb.Next() {
   237  			sendMsg := initClientSendMsg(transport.TTHeader)
   238  			sendMsg.TransInfo().PutTransIntInfo(intKVInfo)
   239  			sendMsg.TransInfo().PutTransStrInfo(strKVInfo)
   240  			sendMsg.Tags()[HeaderFlagsKey] = HeaderFlagSupportOutOfOrder
   241  
   242  			// encode
   243  			out := remote.NewWriterBuffer(256)
   244  			totalLenField, err := ttHeaderCodec.encode(ctx, sendMsg, out)
   245  			binary.BigEndian.PutUint32(totalLenField, uint32(out.MallocLen()-Size32+mockPayloadLen))
   246  			test.Assert(b, err == nil, err)
   247  
   248  			// decode
   249  			recvMsg := initServerRecvMsg()
   250  			buf, err := out.Bytes()
   251  			test.Assert(b, err == nil, err)
   252  			in := remote.NewReaderBuffer(buf)
   253  			err = ttHeaderCodec.decode(ctx, recvMsg, in)
   254  			test.Assert(b, err == nil, err)
   255  			test.Assert(b, recvMsg.PayloadLen() == mockPayloadLen, recvMsg.PayloadLen())
   256  
   257  			intKVInfoRecv := recvMsg.TransInfo().TransIntInfo()
   258  			strKVInfoRecv := recvMsg.TransInfo().TransStrInfo()
   259  			test.DeepEqual(b, intKVInfoRecv, intKVInfo)
   260  			test.DeepEqual(b, strKVInfoRecv, strKVInfo)
   261  			flag := recvMsg.Tags()[HeaderFlagsKey]
   262  			test.Assert(b, flag != nil)
   263  			test.Assert(b, flag == HeaderFlagSupportOutOfOrder)
   264  		}
   265  	})
   266  }
   267  
   268  func BenchmarkTTHeaderCodecParallel(b *testing.B) {
   269  	ctx := context.Background()
   270  
   271  	b.ResetTimer()
   272  	b.RunParallel(func(pb *testing.PB) {
   273  		for pb.Next() {
   274  			sendMsg := initClientSendMsg(transport.TTHeader)
   275  			// encode
   276  			out := remote.NewWriterBuffer(256)
   277  			totalLenField, err := ttHeaderCodec.encode(ctx, sendMsg, out)
   278  			binary.BigEndian.PutUint32(totalLenField, uint32(out.MallocLen()-Size32+mockPayloadLen))
   279  			test.Assert(b, err == nil, err)
   280  
   281  			// decode
   282  			recvMsg := initServerRecvMsg()
   283  			buf, err := out.Bytes()
   284  			test.Assert(b, err == nil, err)
   285  			in := remote.NewReaderBuffer(buf)
   286  			err = ttHeaderCodec.decode(ctx, recvMsg, in)
   287  			test.Assert(b, err == nil, err)
   288  			test.Assert(b, recvMsg.PayloadLen() == mockPayloadLen, recvMsg.PayloadLen())
   289  		}
   290  	})
   291  }
   292  
   293  var (
   294  	mockServiceName = "mock service"
   295  	mockMethod      = "mock"
   296  
   297  	mockCliRPCInfo = rpcinfo.NewRPCInfo(
   298  		rpcinfo.EmptyEndpointInfo(),
   299  		remoteinfo.NewRemoteInfo(&rpcinfo.EndpointBasicInfo{ServiceName: mockServiceName}, mockMethod).ImmutableView(),
   300  		rpcinfo.NewInvocation("", mockMethod),
   301  		rpcinfo.NewRPCConfig(), rpcinfo.NewRPCStats())
   302  
   303  	mockSvrRPCInfo = rpcinfo.NewRPCInfo(rpcinfo.EmptyEndpointInfo(),
   304  		rpcinfo.FromBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: mockServiceName}),
   305  		rpcinfo.NewServerInvocation(),
   306  		rpcinfo.NewRPCConfig(), rpcinfo.NewRPCStats())
   307  )
   308  
   309  func initServerRecvMsg() remote.Message {
   310  	svcInfo := mocks.ServiceInfo()
   311  	svcSearchMap := map[string]*serviceinfo.ServiceInfo{
   312  		remote.BuildMultiServiceKey(mocks.MockServiceName, mocks.MockMethod):          svcInfo,
   313  		remote.BuildMultiServiceKey(mocks.MockServiceName, mocks.MockExceptionMethod): svcInfo,
   314  		remote.BuildMultiServiceKey(mocks.MockServiceName, mocks.MockErrorMethod):     svcInfo,
   315  		remote.BuildMultiServiceKey(mocks.MockServiceName, mocks.MockOnewayMethod):    svcInfo,
   316  		mocks.MockMethod:          svcInfo,
   317  		mocks.MockExceptionMethod: svcInfo,
   318  		mocks.MockErrorMethod:     svcInfo,
   319  		mocks.MockOnewayMethod:    svcInfo,
   320  	}
   321  	msg := remote.NewMessageWithNewer(svcInfo, svcSearchMap, mockSvrRPCInfo, remote.Call, remote.Server, false)
   322  	return msg
   323  }
   324  
   325  func initClientSendMsg(tp transport.Protocol) remote.Message {
   326  	var req interface{}
   327  	svcInfo := mocks.ServiceInfo()
   328  	msg := remote.NewMessage(req, svcInfo, mockCliRPCInfo, remote.Call, remote.Client)
   329  	msg.SetProtocolInfo(remote.NewProtocolInfo(tp, svcInfo.PayloadCodec))
   330  	return msg
   331  }
   332  
   333  func initServerSendMsg(tp transport.Protocol) remote.Message {
   334  	var resp interface{}
   335  	msg := remote.NewMessage(resp, mocks.ServiceInfo(), mockSvrRPCInfo, remote.Reply, remote.Server)
   336  	msg.SetProtocolInfo(remote.NewProtocolInfo(tp, mocks.ServiceInfo().PayloadCodec))
   337  	return msg
   338  }
   339  
   340  func initClientRecvMsg() remote.Message {
   341  	var resp interface{}
   342  	svcInfo := mocks.ServiceInfo()
   343  	msg := remote.NewMessage(resp, svcInfo, mockCliRPCInfo, remote.Reply, remote.Client)
   344  	return msg
   345  }
   346  
   347  var _ discovery.Instance = &mockInst{}
   348  
   349  type mockInst struct {
   350  	addr net.Addr
   351  }
   352  
   353  func (m *mockInst) Address() net.Addr {
   354  	return m.addr
   355  }
   356  
   357  func (m *mockInst) RefreshInstanceWithAddr(addr net.Addr) discovery.Instance {
   358  	m.addr = addr
   359  	return m
   360  }
   361  
   362  func (m *mockInst) Weight() int {
   363  	return 10
   364  }
   365  
   366  func (m *mockInst) Tag(key string) (value string, exist bool) {
   367  	return
   368  }
   369  
   370  func prepareIntKVInfo() map[uint16]string {
   371  	kvInfo := map[uint16]string{
   372  		transmeta.FromService: "mockFromService",
   373  		transmeta.FromMethod:  "mockFromMethod",
   374  		transmeta.ToService:   "mockToService",
   375  		transmeta.ToMethod:    "mockToMethod",
   376  	}
   377  	return kvInfo
   378  }
   379  
   380  func prepareStrKVInfo() map[string]string {
   381  	kvInfo := map[string]string{transmeta.HeaderIDLServiceName: mocks.MockServiceName}
   382  	return kvInfo
   383  }
   384  
   385  func prepareStrKVInfoWithGDPRToken() map[string]string {
   386  	kvInfo := map[string]string{
   387  		transmeta.GDPRToken:             "mockToken",
   388  		transmeta.HeaderTransRemoteAddr: "mockRemoteAddr",
   389  		transmeta.HeaderIDLServiceName:  mocks.MockServiceName,
   390  	}
   391  	return kvInfo
   392  }
   393  
   394  // // TODO 是否提供buf.writeInt8/16/32方法,否则得先计算,然后malloc,最后write,待确认频繁malloc是否有影响
   395  // 暂时不删除,测试一次malloc, 和多次malloc差异
   396  //
   397  //lint:ignore U1000 until encode2 is used
   398  func (t ttHeader) encode2(ctx context.Context, message remote.Message, payloadBuf, out remote.ByteBuffer) error {
   399  	tm := message.TransInfo()
   400  
   401  	// 1. header meta
   402  	headerMeta, err := out.Malloc(TTHeaderMetaSize)
   403  	if err != nil {
   404  		return perrors.NewProtocolError(err)
   405  	}
   406  
   407  	totalLenField := headerMeta[0:4]
   408  	headerSizeField := headerMeta[12:14]
   409  
   410  	binary.BigEndian.PutUint32(headerMeta[4:8], TTHeaderMagic+uint32(getFlags(message)))
   411  	binary.BigEndian.PutUint32(headerMeta[8:12], uint32(message.RPCInfo().Invocation().SeqID()))
   412  
   413  	var transformIDs []uint8
   414  	// 2. header info, calculate size
   415  	var headerInfo []byte
   416  	var headerInfoSize int
   417  	// PROTOCOL ID(u8) + NUM TRANSFORMS(always 0)(u8) + TRANSFORM IDs([]u8)
   418  	headerInfoSize = 1 + 1 + len(transformIDs)
   419  	// str kv info
   420  	if len(tm.TransStrInfo()) > 0 {
   421  		headerInfoSize += 3 // INFO ID TYPE(u8) + NUM HEADERS(u16)
   422  		for k, v := range tm.TransStrInfo() {
   423  			headerInfoSize += 4 + len(k) + len(v)
   424  		}
   425  	}
   426  	// int kv info
   427  	if len(tm.TransIntInfo()) > 0 {
   428  		headerInfoSize += 3 // INFO ID TYPE(u8) + NUM HEADERS(u16)
   429  		for _, v := range tm.TransIntInfo() {
   430  			headerInfoSize += 4 + len(v) // key is uint16
   431  		}
   432  	}
   433  	// padding blank
   434  	padding := (4 - headerInfoSize%4) % 4
   435  	headerInfoSize += padding
   436  	binary.BigEndian.PutUint16(headerSizeField, uint16(headerInfoSize/4))
   437  	totalLen := TTHeaderMetaSize - Size32 + headerInfoSize + payloadBuf.MallocLen()
   438  	binary.BigEndian.PutUint32(totalLenField, uint32(totalLen))
   439  
   440  	// 3.  header info, malloc and write
   441  	if headerInfo, err = out.Malloc(headerInfoSize); err != nil {
   442  		return perrors.NewProtocolError(err)
   443  	}
   444  	headerInfo[0] = byte(getProtocolID(message.ProtocolInfo()))
   445  	headerInfo[1] = byte(len(transformIDs))
   446  	hdIdx := 2
   447  	for tid := range transformIDs {
   448  		// TODO 需确认下transformId的编码格式
   449  		headerInfo[hdIdx] = byte(tid)
   450  		hdIdx++
   451  	}
   452  	// str kv info
   453  	if len(tm.TransStrInfo()) > 0 {
   454  		// INFO ID TYPE(u8) + NUM HEADERS(u16)
   455  		headerInfo[hdIdx] = byte(InfoIDKeyValue)
   456  		binary.BigEndian.PutUint16(headerInfo[hdIdx+1:hdIdx+3], uint16(len(tm.TransStrInfo())))
   457  		hdIdx += 3
   458  		for key, value := range tm.TransStrInfo() {
   459  			binary.BigEndian.PutUint16(headerInfo[hdIdx:hdIdx+2], uint16(len(key)))
   460  			hdIdx += 2
   461  			hdIdx += copy(headerInfo[hdIdx:], key)
   462  
   463  			binary.BigEndian.PutUint16(headerInfo[hdIdx:hdIdx+2], uint16(len(value)))
   464  			hdIdx += 2
   465  			hdIdx += copy(headerInfo[hdIdx:], value)
   466  		}
   467  	}
   468  	// int kv info
   469  	if len(tm.TransIntInfo()) > 0 {
   470  		// INFO ID TYPE(u8) + NUM HEADERS(u16)
   471  		headerInfo[hdIdx] = byte(InfoIDIntKeyValue)
   472  		binary.BigEndian.PutUint16(headerInfo[hdIdx+1:hdIdx+3], uint16(len(tm.TransIntInfo())))
   473  		hdIdx += 3
   474  		for key, value := range tm.TransIntInfo() {
   475  			binary.BigEndian.PutUint16(headerInfo[hdIdx:hdIdx+2], key)
   476  			hdIdx += 2
   477  
   478  			binary.BigEndian.PutUint16(headerInfo[hdIdx:hdIdx+2], uint16(len(value)))
   479  			hdIdx += 2
   480  			hdIdx += copy(headerInfo[hdIdx:], value)
   481  		}
   482  	}
   483  	// padding = (4 - headerInfoSize%4) % 4
   484  	for padding := (4 - hdIdx%4) % 4; padding > 0; padding-- {
   485  		headerInfo[hdIdx] = byte(0)
   486  		hdIdx++
   487  	}
   488  	if hdIdx != headerInfoSize {
   489  		return perrors.NewProtocolErrorWithMsg("the size that header info write is not equal with malloc size")
   490  	}
   491  
   492  	if err := out.AppendBuffer(payloadBuf); err != nil {
   493  		return perrors.NewProtocolError(err)
   494  	}
   495  	return err
   496  }
   497  
   498  func TestHeaderFlags(t *testing.T) {
   499  	// case 1: correct HeaderFlags
   500  	msg := initClientSendMsg(transport.TTHeader)
   501  	msg.Tags()[HeaderFlagsKey] = HeaderFlagSASL | HeaderFlagSupportOutOfOrder
   502  	hfs := getFlags(msg)
   503  	test.Assert(t, hfs == HeaderFlagSASL|HeaderFlagSupportOutOfOrder)
   504  
   505  	// case 2: invalid type for HeaderFlagsKey
   506  	msg = initClientSendMsg(transport.TTHeader)
   507  	msg.Tags()[HeaderFlagsKey] = 1
   508  	hfs = getFlags(msg)
   509  	test.Assert(t, hfs == 0)
   510  
   511  	// case 3: setFlags then get Flags
   512  	msg = initClientSendMsg(transport.TTHeader)
   513  	setFlags(uint16(HeaderFlagSupportOutOfOrder), msg)
   514  	hfs = getFlags(msg)
   515  	test.Assert(t, hfs == HeaderFlagSupportOutOfOrder, hfs)
   516  }