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 }