github.com/cloudwego/kitex@v0.9.0/pkg/generic/http_test/generic_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 test 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/base64" 23 "encoding/json" 24 "fmt" 25 "math" 26 "net" 27 "net/http" 28 "reflect" 29 "strconv" 30 "strings" 31 "testing" 32 "time" 33 34 "github.com/bytedance/sonic" 35 "github.com/cloudwego/dynamicgo/conv" 36 "github.com/tidwall/gjson" 37 38 "github.com/cloudwego/kitex/client/callopt" 39 "github.com/cloudwego/kitex/client/genericclient" 40 kt "github.com/cloudwego/kitex/internal/mocks/thrift" 41 "github.com/cloudwego/kitex/internal/test" 42 "github.com/cloudwego/kitex/pkg/generic" 43 "github.com/cloudwego/kitex/pkg/generic/descriptor" 44 "github.com/cloudwego/kitex/server" 45 "github.com/cloudwego/kitex/transport" 46 ) 47 48 var customJson = sonic.Config{ 49 EscapeHTML: true, 50 UseNumber: true, 51 }.Froze() 52 53 func TestRun(t *testing.T) { 54 t.Run("TestThriftNormalBinaryEcho", testThriftNormalBinaryEcho) 55 t.Run("TestThriftException", testThriftException) 56 t.Run("TestRegression", testRegression) 57 t.Run("TestThriftBase64BinaryEcho", testThriftBase64BinaryEcho) 58 t.Run("TestUseRawBodyAndBodyCompatibility", testUseRawBodyAndBodyCompatibility) 59 } 60 61 func initThriftClientByIDL(t *testing.T, tp transport.Protocol, addr, idl string, opts []generic.Option, base64Binary, enableDynamicGo bool) genericclient.Client { 62 var p generic.DescriptorProvider 63 var err error 64 if enableDynamicGo { 65 p, err = generic.NewThriftFileProviderWithDynamicGo(idl) 66 } else { 67 p, err = generic.NewThriftFileProvider(idl) 68 } 69 test.Assert(t, err == nil) 70 g, err := generic.HTTPThriftGeneric(p, opts...) 71 test.Assert(t, err == nil) 72 err = generic.SetBinaryWithBase64(g, base64Binary) 73 test.Assert(t, err == nil) 74 cli := newGenericClient(tp, "destServiceName", g, addr) 75 test.Assert(t, err == nil) 76 return cli 77 } 78 79 func initThriftServer(t *testing.T, address string, handler generic.Service, idlPath string) server.Server { 80 addr, _ := net.ResolveTCPAddr("tcp", address) 81 p, err := generic.NewThriftFileProvider(idlPath) 82 test.Assert(t, err == nil) 83 g, err := generic.MapThriftGeneric(p) 84 test.Assert(t, err == nil) 85 svr := newGenericServer(g, addr, handler) 86 test.Assert(t, err == nil) 87 return svr 88 } 89 90 func initMockServer(t *testing.T, handler kt.Mock, address string) server.Server { 91 addr, _ := net.ResolveTCPAddr("tcp", address) 92 svr := newMockServer(handler, addr) 93 return svr 94 } 95 96 func testThriftNormalBinaryEcho(t *testing.T) { 97 addr := test.GetLocalAddress() 98 svr := initThriftServer(t, addr, new(GenericServiceBinaryEchoImpl), "./idl/binary_echo.thrift") 99 100 url := "http://example.com/BinaryEcho" 101 102 // []byte value for binary field 103 body := map[string]interface{}{ 104 "msg": []byte(mockMyMsg), 105 "got_base64": true, 106 "num": "", 107 } 108 data, err := json.Marshal(body) 109 if err != nil { 110 panic(err) 111 } 112 req, err := http.NewRequest(http.MethodGet, url, bytes.NewBuffer(data)) 113 if err != nil { 114 panic(err) 115 } 116 customReq, err := generic.FromHTTPRequest(req) 117 if err != nil { 118 t.Fatal(err) 119 } 120 121 // normal way 122 var opts []generic.Option 123 cli := initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", opts, false, false) 124 resp, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 125 test.Assert(t, err == nil, err) 126 gr, ok := resp.(*generic.HTTPResponse) 127 test.Assert(t, ok) 128 test.Assert(t, gr.Body["msg"] == base64.StdEncoding.EncodeToString([]byte(mockMyMsg))) 129 test.Assert(t, gr.Body["num"] == "0") 130 131 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 132 // read: dynamicgo 133 opts = append(opts, generic.UseRawBodyForHTTPResp(true)) 134 cli = initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", opts, false, true) 135 resp, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 136 test.Assert(t, err == nil, err) 137 gr, ok = resp.(*generic.HTTPResponse) 138 test.Assert(t, ok) 139 test.Assert(t, reflect.DeepEqual(gjson.Get(string(gr.RawBody), "msg").String(), base64.StdEncoding.EncodeToString([]byte(mockMyMsg))), gjson.Get(string(gr.RawBody), "msg").String()) 140 test.Assert(t, reflect.DeepEqual(gjson.Get(string(gr.RawBody), "num").String(), "0"), gjson.Get(string(gr.RawBody), "num").String()) 141 142 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 143 // read: dynamicgo 144 cli = initThriftClientByIDL(t, transport.PurePayload, addr, "./idl/binary_echo.thrift", opts, false, true) 145 resp, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 146 test.Assert(t, err == nil, err) 147 gr, ok = resp.(*generic.HTTPResponse) 148 test.Assert(t, ok) 149 test.Assert(t, reflect.DeepEqual(gjson.Get(string(gr.RawBody), "msg").String(), base64.StdEncoding.EncodeToString([]byte(mockMyMsg))), gjson.Get(string(gr.RawBody), "msg").String()) 150 test.Assert(t, reflect.DeepEqual(gjson.Get(string(gr.RawBody), "num").String(), "0"), gjson.Get(string(gr.RawBody), "num").String()) 151 152 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 153 // read: fallback 154 cli = initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", nil, false, true) 155 resp, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 156 test.Assert(t, err == nil, err) 157 gr, ok = resp.(*generic.HTTPResponse) 158 test.Assert(t, ok) 159 test.Assert(t, gr.Body["msg"] == base64.StdEncoding.EncodeToString([]byte(mockMyMsg))) 160 test.Assert(t, gr.Body["num"] == "0") 161 162 body = map[string]interface{}{ 163 "msg": string(mockMyMsg), 164 "got_base64": false, 165 "num": 0, 166 } 167 data, err = json.Marshal(body) 168 if err != nil { 169 panic(err) 170 } 171 req, err = http.NewRequest(http.MethodGet, url, bytes.NewBuffer(data)) 172 if err != nil { 173 panic(err) 174 } 175 customReq, err = generic.FromHTTPRequest(req) 176 if err != nil { 177 t.Fatal(err) 178 } 179 180 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 181 // read: dynamicgo 182 cli = initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", opts, false, true) 183 resp, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 184 test.Assert(t, err == nil, err) 185 gr, ok = resp.(*generic.HTTPResponse) 186 test.Assert(t, ok) 187 test.Assert(t, reflect.DeepEqual(gjson.Get(string(gr.RawBody), "msg").String(), mockMyMsg), gjson.Get(string(gr.RawBody), "msg").String()) 188 test.Assert(t, reflect.DeepEqual(gjson.Get(string(gr.RawBody), "num").String(), "0"), gjson.Get(string(gr.RawBody), "num").String()) 189 190 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 191 // read: fallback 192 cli = initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", nil, false, true) 193 resp, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 194 test.Assert(t, err == nil, err) 195 gr, ok = resp.(*generic.HTTPResponse) 196 test.Assert(t, ok) 197 test.Assert(t, gr.Body["msg"] == mockMyMsg) 198 test.Assert(t, gr.Body["num"] == "0") 199 200 body = map[string]interface{}{ 201 "msg": []byte(mockMyMsg), 202 "got_base64": true, 203 "num": "123", 204 } 205 data, err = json.Marshal(body) 206 if err != nil { 207 panic(err) 208 } 209 req, err = http.NewRequest(http.MethodGet, url, bytes.NewBuffer(data)) 210 if err != nil { 211 panic(err) 212 } 213 customReq, err = generic.FromHTTPRequest(req) 214 if err != nil { 215 t.Fatal(err) 216 } 217 218 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 219 // read: dynamicgo 220 cli = initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", opts, false, true) 221 _, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 222 test.Assert(t, err.Error() == "remote or network error[remote]: biz error: call failed, incorrect num", err.Error()) 223 224 svr.Stop() 225 } 226 227 func BenchmarkCompareDynamicgoAndOriginal_Small(b *testing.B) { 228 // small data 229 sobj := getSimpleValue() 230 data, err := json.Marshal(sobj) 231 if err != nil { 232 panic(err) 233 } 234 fmt.Println("small data size: ", len(string(data))) 235 url := "http://example.com/simple" 236 237 t := testing.T{} 238 var opts []generic.Option 239 opts = append(opts, generic.UseRawBodyForHTTPResp(true)) 240 241 b.Run("thrift_small_dynamicgo", func(b *testing.B) { 242 addr := test.GetLocalAddress() 243 svr := initThriftServer(&t, addr, new(GenericServiceBenchmarkImpl), "./idl/baseline.thrift") 244 cli := initThriftClientByIDL(&t, transport.TTHeader, addr, "./idl/baseline.thrift", opts, false, true) 245 246 req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data)) 247 if err != nil { 248 panic(err) 249 } 250 customReq, err := generic.FromHTTPRequest(req) 251 if err != nil { 252 t.Fatal(err) 253 } 254 resp, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 255 test.Assert(&t, err == nil, err) 256 gr, ok := resp.(*generic.HTTPResponse) 257 test.Assert(&t, ok) 258 test.Assert(&t, reflect.DeepEqual(gjson.Get(string(gr.RawBody), "I64Field").String(), strconv.Itoa(math.MaxInt64)), gjson.Get(string(gr.RawBody), "I64Field").String()) 259 260 b.ResetTimer() 261 for i := 0; i < b.N; i++ { 262 cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 263 } 264 svr.Stop() 265 }) 266 267 b.Run("thrift_small_original", func(b *testing.B) { 268 addr := test.GetLocalAddress() 269 svr := initThriftServer(&t, addr, new(GenericServiceBenchmarkImpl), "./idl/baseline.thrift") 270 cli := initThriftClientByIDL(&t, transport.TTHeader, addr, "./idl/baseline.thrift", nil, false, false) 271 272 req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data)) 273 if err != nil { 274 panic(err) 275 } 276 customReq, err := generic.FromHTTPRequest(req) 277 if err != nil { 278 t.Fatal(err) 279 } 280 resp, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 281 test.Assert(&t, err == nil, err) 282 gr, ok := resp.(*generic.HTTPResponse) 283 test.Assert(&t, ok) 284 test.Assert(&t, reflect.DeepEqual(gr.Body["I64Field"].(string), strconv.Itoa(math.MaxInt64)), gr.Body["I64Field"].(string)) 285 286 b.ResetTimer() 287 for i := 0; i < b.N; i++ { 288 cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 289 } 290 svr.Stop() 291 }) 292 } 293 294 func BenchmarkCompareDynamicgoAndOriginal_Medium(b *testing.B) { 295 // medium data 296 nobj := getNestingValue() 297 data, err := json.Marshal(nobj) 298 if err != nil { 299 panic(err) 300 } 301 fmt.Println("medium data size: ", len(string(data))) 302 url := "http://example.com/nesting/100" 303 304 t := testing.T{} 305 var opts []generic.Option 306 opts = append(opts, generic.UseRawBodyForHTTPResp(true)) 307 308 b.Run("thrift_medium_dynamicgo", func(b *testing.B) { 309 addr := test.GetLocalAddress() 310 svr := initThriftServer(&t, addr, new(GenericServiceBenchmarkImpl), "./idl/baseline.thrift") 311 cli := initThriftClientByIDL(&t, transport.TTHeader, addr, "./idl/baseline.thrift", opts, false, true) 312 313 req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data)) 314 if err != nil { 315 panic(err) 316 } 317 customReq, err := generic.FromHTTPRequest(req) 318 if err != nil { 319 t.Fatal(err) 320 } 321 resp, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 322 test.Assert(&t, err == nil, err) 323 gr, ok := resp.(*generic.HTTPResponse) 324 test.Assert(&t, ok) 325 test.Assert(&t, reflect.DeepEqual(gjson.Get(string(gr.RawBody), "I32").String(), strconv.Itoa(math.MaxInt32)), gjson.Get(string(gr.RawBody), "I32").String()) 326 327 b.ResetTimer() 328 for i := 0; i < b.N; i++ { 329 cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 330 } 331 svr.Stop() 332 }) 333 334 b.Run("thrift_medium_original", func(b *testing.B) { 335 addr := test.GetLocalAddress() 336 svr := initThriftServer(&t, addr, new(GenericServiceBenchmarkImpl), "./idl/baseline.thrift") 337 cli := initThriftClientByIDL(&t, transport.TTHeader, addr, "./idl/baseline.thrift", nil, false, false) 338 339 req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data)) 340 if err != nil { 341 panic(err) 342 } 343 customReq, err := generic.FromHTTPRequest(req) 344 if err != nil { 345 t.Fatal(err) 346 } 347 resp, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 348 test.Assert(&t, err == nil, err) 349 gr, ok := resp.(*generic.HTTPResponse) 350 test.Assert(&t, ok) 351 test.Assert(&t, gr.Body["I32"].(int32) == math.MaxInt32, gr.Body["I32"].(int32)) 352 353 b.ResetTimer() 354 for i := 0; i < b.N; i++ { 355 cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 356 } 357 svr.Stop() 358 }) 359 } 360 361 func testThriftException(t *testing.T) { 362 addr := test.GetLocalAddress() 363 svr := initMockServer(t, new(mockImpl), addr) 364 365 body := map[string]interface{}{ 366 "Msg": "hello", 367 "strMap": map[string]interface{}{"mk1": "mv1", "mk2": "mv2"}, 368 "strList": []string{"lv1", "lv2"}, 369 } 370 data, err := json.Marshal(body) 371 if err != nil { 372 panic(err) 373 } 374 url := "http://example.com/ExceptionTest" 375 req, err := http.NewRequest(http.MethodGet, url, bytes.NewBuffer(data)) 376 if err != nil { 377 panic(err) 378 } 379 customReq, err := generic.FromHTTPRequest(req) 380 if err != nil { 381 t.Fatal(err) 382 } 383 384 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 385 // read: dynamicgo 386 var opts []generic.Option 387 opts = append(opts, generic.UseRawBodyForHTTPResp(true)) 388 cli := initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/mock.thrift", opts, false, true) 389 resp, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 390 test.Assert(t, err == nil, err) 391 fmt.Println(string(resp.(*descriptor.HTTPResponse).RawBody)) 392 test.DeepEqual(t, gjson.Get(string(resp.(*descriptor.HTTPResponse).RawBody), "code").Int(), int64(400)) 393 test.DeepEqual(t, gjson.Get(string(resp.(*descriptor.HTTPResponse).RawBody), "msg").String(), "this is an exception") 394 395 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 396 // read: fallback 397 cli = initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/mock.thrift", nil, false, true) 398 resp, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 399 test.Assert(t, err == nil, err) 400 fmt.Println(string(resp.(*descriptor.HTTPResponse).RawBody)) 401 test.DeepEqual(t, resp.(*descriptor.HTTPResponse).Body["code"].(int32), int32(400)) 402 test.DeepEqual(t, resp.(*descriptor.HTTPResponse).Body["msg"], "this is an exception") 403 404 svr.Stop() 405 } 406 407 func testRegression(t *testing.T) { 408 addr := test.GetLocalAddress() 409 svr := initThriftServer(t, addr, new(GenericServiceAnnotationImpl), "./idl/http_annotation.thrift") 410 411 body := map[string]interface{}{ 412 "text": "text", 413 "req_items_map": map[string]interface{}{ 414 "1": map[string]interface{}{ 415 "MyID": "1", 416 "text": "text", 417 }, 418 }, 419 "some": map[string]interface{}{ 420 "MyID": "1", 421 "text": "text", 422 }, 423 } 424 data, err := json.Marshal(body) 425 if err != nil { 426 panic(err) 427 } 428 url := "http://example.com/life/client/1/1?v_int64=1&req_items=item1,item2,item3&cids=1,2,3&vids=1,2,3" 429 req, err := http.NewRequest(http.MethodGet, url, bytes.NewBuffer(data)) 430 if err != nil { 431 panic(err) 432 } 433 req.Header.Set("token", "1") 434 cookie := &http.Cookie{ 435 Name: "cookie", 436 Value: "cookie_val", 437 } 438 req.AddCookie(cookie) 439 customReq, err := generic.FromHTTPRequest(req) 440 if err != nil { 441 t.Fatal(err) 442 } 443 444 // client without dynamicgo 445 cli := initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/http_annotation.thrift", nil, false, false) 446 respI, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 447 test.Assert(t, err == nil, err) 448 resp, ok := respI.(*generic.HTTPResponse) 449 test.Assert(t, ok) 450 451 // client with dynamicgo 452 var opts []generic.Option 453 opts = append(opts, generic.UseRawBodyForHTTPResp(true)) 454 cli = initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/http_annotation.thrift", opts, false, true) 455 respI, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 456 test.Assert(t, err == nil, err) 457 dresp, ok := respI.(*generic.HTTPResponse) 458 test.Assert(t, ok) 459 460 // check body 461 var dMapBody map[string]interface{} 462 err = customJson.Unmarshal(dresp.RawBody, &dMapBody) 463 test.Assert(t, err == nil) 464 bytes, err := customJson.Marshal(resp.Body) 465 test.Assert(t, err == nil) 466 err = customJson.Unmarshal(bytes, &resp.Body) 467 test.Assert(t, err == nil, err) 468 test.Assert(t, isEqual(dMapBody, resp.Body)) 469 470 test.DeepEqual(t, resp.StatusCode, dresp.StatusCode) 471 test.DeepEqual(t, resp.ContentType, dresp.ContentType) 472 473 checkHeader(t, resp) 474 checkHeader(t, dresp) 475 476 svr.Stop() 477 } 478 479 func testThriftBase64BinaryEcho(t *testing.T) { 480 addr := test.GetLocalAddress() 481 svr := initThriftServer(t, addr, new(GenericServiceBinaryEchoImpl), "./idl/binary_echo.thrift") 482 483 var opts []generic.Option 484 convOpts := conv.Options{EnableValueMapping: true, NoBase64Binary: false} 485 opts = append(opts, generic.WithCustomDynamicGoConvOpts(&convOpts), generic.UseRawBodyForHTTPResp(true)) 486 487 url := "http://example.com/BinaryEcho" 488 489 // []byte value for binary field 490 body := map[string]interface{}{ 491 "msg": []byte(mockMyMsg), 492 "got_base64": false, 493 "num": "0", 494 } 495 data, err := json.Marshal(body) 496 if err != nil { 497 panic(err) 498 } 499 req, err := http.NewRequest(http.MethodGet, url, bytes.NewBuffer(data)) 500 if err != nil { 501 panic(err) 502 } 503 customReq, err := generic.FromHTTPRequest(req) 504 if err != nil { 505 t.Fatal(err) 506 } 507 508 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 509 // read: dynamicgo 510 cli := initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", opts, true, true) 511 resp, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 512 test.Assert(t, err == nil, err) 513 gr, ok := resp.(*generic.HTTPResponse) 514 test.Assert(t, ok) 515 test.Assert(t, reflect.DeepEqual(gjson.Get(string(gr.RawBody), "msg").String(), base64.StdEncoding.EncodeToString([]byte(mockMyMsg))), gjson.Get(string(gr.RawBody), "msg").String()) 516 517 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 518 // read: fallback 519 cli = initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", nil, true, true) 520 resp, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 521 test.Assert(t, err == nil, err) 522 gr, ok = resp.(*generic.HTTPResponse) 523 test.Assert(t, ok) 524 test.Assert(t, gr.Body["msg"] == base64.StdEncoding.EncodeToString(body["msg"].([]byte))) 525 526 // string value for binary field which should fail 527 body = map[string]interface{}{ 528 "msg": string(mockMyMsg), 529 } 530 data, err = json.Marshal(body) 531 if err != nil { 532 panic(err) 533 } 534 req, err = http.NewRequest(http.MethodGet, url, bytes.NewBuffer(data)) 535 if err != nil { 536 panic(err) 537 } 538 customReq, err = generic.FromHTTPRequest(req) 539 if err != nil { 540 t.Fatal(err) 541 } 542 543 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 544 // read: dynamicgo 545 cli = initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", opts, true, true) 546 _, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 547 test.Assert(t, strings.Contains(err.Error(), "illegal base64 data")) 548 549 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 550 // read: fallback 551 cli = initThriftClientByIDL(t, transport.PurePayload, addr, "./idl/binary_echo.thrift", nil, true, true) 552 _, err = cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 553 test.Assert(t, strings.Contains(err.Error(), "illegal base64 data")) 554 555 svr.Stop() 556 } 557 558 func testUseRawBodyAndBodyCompatibility(t *testing.T) { 559 addr := test.GetLocalAddress() 560 svr := initThriftServer(t, addr, new(GenericServiceBinaryEchoImpl), "./idl/binary_echo.thrift") 561 562 url := "http://example.com/BinaryEcho" 563 564 // []byte value for binary field 565 body := map[string]interface{}{ 566 "msg": []byte(mockMyMsg), 567 "got_base64": true, 568 "num": "", 569 } 570 data, err := json.Marshal(body) 571 if err != nil { 572 panic(err) 573 } 574 req, err := http.NewRequest(http.MethodGet, url, bytes.NewBuffer(data)) 575 if err != nil { 576 panic(err) 577 } 578 customReq, err := generic.FromHTTPRequest(req) 579 if err != nil { 580 t.Fatal(err) 581 } 582 583 var opts []generic.Option 584 opts = append(opts, generic.UseRawBodyForHTTPResp(true)) 585 cli := initThriftClientByIDL(t, transport.TTHeader, addr, "./idl/binary_echo.thrift", opts, false, false) 586 resp, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second)) 587 test.Assert(t, err == nil) 588 gr, ok := resp.(*generic.HTTPResponse) 589 test.Assert(t, ok) 590 591 var mapBody map[string]interface{} 592 err = customJson.Unmarshal(gr.RawBody, &mapBody) 593 test.Assert(t, err == nil) 594 test.DeepEqual(t, gr.Body, mapBody) 595 596 svr.Stop() 597 } 598 599 func isEqual(a, b interface{}) bool { 600 if reflect.TypeOf(a) != reflect.TypeOf(b) { 601 return false 602 } 603 switch a := a.(type) { 604 case []interface{}: 605 b, ok := b.([]interface{}) 606 if !ok { 607 return false 608 } 609 if len(a) != len(b) { 610 return false 611 } 612 for i := range a { 613 if !isEqual(a[i], b[i]) { 614 return false 615 } 616 } 617 return true 618 case map[string]interface{}: 619 b, ok := b.(map[string]interface{}) 620 if !ok { 621 return false 622 } 623 if len(a) != len(b) { 624 return false 625 } 626 for k, v1 := range a { 627 v2, ok := b[k] 628 if !ok || !isEqual(v1, v2) { 629 return false 630 } 631 } 632 return true 633 default: 634 return reflect.DeepEqual(a, b) 635 } 636 } 637 638 func checkHeader(t *testing.T, resp *generic.HTTPResponse) { 639 test.Assert(t, resp.Header.Get("b") == "true") 640 test.Assert(t, resp.Header.Get("eight") == "8") 641 test.Assert(t, resp.Header.Get("sixteen") == "16") 642 test.Assert(t, resp.Header.Get("thirtytwo") == "32") 643 test.Assert(t, resp.Header.Get("sixtyfour") == "64") 644 test.Assert(t, resp.Header.Get("d") == "123.45") 645 test.Assert(t, resp.Header.Get("T") == "1") 646 test.Assert(t, resp.Header.Get("item_count") == "1,2,3") 647 test.Assert(t, resp.Header.Get("header_map") == "map[map1:1 map2:2]") 648 test.Assert(t, resp.Header.Get("header_struct") == "map[item_id:1 text:1]") 649 test.Assert(t, resp.Header.Get("string_set") == "a,b,c") 650 }