trpc.group/trpc-go/trpc-go@v1.0.3/http/codec_test.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_test 15 16 import ( 17 "bufio" 18 "bytes" 19 "context" 20 "encoding/base64" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "io" 25 "net" 26 "net/http" 27 "net/http/httptest" 28 "net/url" 29 "strings" 30 "testing" 31 32 trpcpb "trpc.group/trpc/trpc-protocol/pb/go/trpc" 33 34 "trpc.group/trpc-go/trpc-go/client" 35 "trpc.group/trpc-go/trpc-go/codec" 36 "trpc.group/trpc-go/trpc-go/errs" 37 thttp "trpc.group/trpc-go/trpc-go/http" 38 "trpc.group/trpc-go/trpc-go/server" 39 "trpc.group/trpc-go/trpc-go/testdata/restful/helloworld" 40 41 "github.com/stretchr/testify/assert" 42 "github.com/stretchr/testify/require" 43 ) 44 45 func TestRegister(t *testing.T) { 46 thttp.SetContentType("application/proto", codec.SerializationTypePB) 47 thttp.RegisterContentType("application/proto", codec.SerializationTypePB) 48 thttp.RegisterSerializer("application/proto", codec.SerializationTypePB, &codec.PBSerialization{}) 49 thttp.RegisterContentEncoding("gzip", codec.CompressTypeGzip) 50 thttp.RegisterStatus(100, 500) 51 52 req := thttp.Request(context.Background()) 53 require.Nil(t, req, "request empty") 54 rsp := thttp.Response(context.Background()) 55 require.Nil(t, rsp, "response empty") 56 } 57 58 func TestServerEncode(t *testing.T) { 59 r := &http.Request{} 60 w := &httptest.ResponseRecorder{} 61 m := &thttp.Header{Request: r, Response: w} 62 ctx := thttp.WithHeader(context.Background(), m) 63 msg := codec.Message(ctx) 64 msg.WithCompressType(codec.CompressTypeGzip) 65 sc := thttp.ServerCodec{} 66 _, err := sc.Encode(msg, nil) 67 require.Nil(t, err, "failed to encode http") 68 require.NotNil(t, thttp.Request(ctx)) 69 require.NotNil(t, thttp.Response(ctx)) 70 } 71 72 func TestServerEncodeWithContentType(t *testing.T) { 73 r := &http.Request{} 74 w := httptest.NewRecorder() 75 w.Header().Set("Content-Type", "application/json") 76 m := &thttp.Header{Request: r, Response: w} 77 ctx := thttp.WithHeader(context.Background(), m) 78 msg := codec.Message(ctx) 79 sc := thttp.ServerCodec{} 80 _, err := sc.Encode(msg, nil) 81 require.Nil(t, err, "failed to encode http") 82 } 83 84 func TestServerErrEncode(t *testing.T) { 85 req := &http.Request{} 86 w := &httptest.ResponseRecorder{} 87 h := &thttp.Header{Request: req, Response: w} 88 ctx := thttp.WithHeader(context.Background(), h) 89 msg := codec.Message(ctx) 90 msg.WithServerRspErr(errs.ErrServerNoFunc) 91 sc := thttp.DefaultServerCodec 92 _, err := sc.Encode(msg, nil) 93 require.Nil(t, err, "failed to encode err http") 94 95 // After the server returns an error, even there is a response data, 96 // it will be ignored and will not be processed or returned. 97 rsp := &responseRecorder{} 98 h = &thttp.Header{Request: req, Response: rsp} 99 ctx = thttp.WithHeader(context.Background(), h) 100 msg = codec.Message(ctx) 101 msg.WithServerRspErr(errs.ErrServerNoFunc) 102 _, err = sc.Encode(msg, []byte("write failed")) 103 require.Nil(t, err) 104 } 105 106 func TestNotHead(t *testing.T) { 107 msg := codec.Message(context.Background()) 108 _, err := thttp.DefaultServerCodec.Decode(msg, nil) 109 require.NotNil(t, err, "failed to decode get head") 110 _, err = thttp.DefaultServerCodec.Encode(msg, nil) 111 require.NotNil(t, err, "failed to encode get head") 112 } 113 114 func TestMultipartFormData(t *testing.T) { 115 require := require.New(t) 116 r, _ := http.NewRequest("POST", "http://www.qq.com/trpc.http.test.helloworld/SayHello", bytes.NewReader([]byte(""))) 117 r.Header.Add("Content-Type", "multipart/form-data; boundary=--------------------------487682300036072392114180") 118 body := `----------------------------487682300036072392114180 119 Content-Disposition: form-data; name="competition" 120 121 NBA 122 ----------------------------487682300036072392114180 123 Content-Disposition: form-data; name="teams" 124 125 湖人 126 ----------------------------487682300036072392114180 127 Content-Disposition: form-data; name="teams" 128 129 勇士 130 ----------------------------487682300036072392114180 131 Content-Disposition: form-data; name="season" 132 133 2021 134 ----------------------------487682300036072392114180 135 Content-Disposition: form-data; name="file1"; filename="1.txt" 136 Content-Type: text/plain 137 138 1 139 ----------------------------487682300036072392114180 140 Content-Disposition: form-data; name="file2"; filename="1px.png" 141 Content-Type: image/png 142 143 �PNG 144 145 IHDR%�V�PLTE����� 146 IDA�c�!�3IEND�B� 147 ----------------------------487682300036072392114180 148 Content-Disposition: form-data; name="file3"; filename="json.json" 149 Content-Type: application/json 150 151 { 152 "name":"1" 153 } 154 ----------------------------487682300036072392114180-- 155 ` 156 // Decode multipart form data. 157 r.Body = io.NopCloser(strings.NewReader(body)) 158 w := &httptest.ResponseRecorder{} 159 header := &thttp.Header{Request: r, Response: w} 160 msg := codec.Message(thttp.WithHeader(context.Background(), header)) 161 in, err := thttp.DefaultServerCodec.Decode(msg, nil) 162 require.Nil(err) 163 require.Equal("competition=NBA&season=2021&teams=%E6%B9%96%E4%BA%BA&teams=%E5%8B%87%E5%A3%AB", string(in)) 164 head := thttp.Head(msg.Context()) 165 166 // Content-Type: text/plain. 167 file1, fileHeader1, err := head.Request.FormFile("file1") 168 require.Nil(err) 169 defer func() { require.Nil(file1.Close()) }() 170 require.Equal("text/plain", fileHeader1.Header.Get("Content-Type")) 171 require.Equal("1.txt", fileHeader1.Filename) 172 file1Content := make([]byte, 256) 173 n, err := io.ReadFull(file1, file1Content) 174 require.NotNil(err) 175 file1Content = file1Content[:n] 176 require.Equal("1", string(file1Content)) 177 178 // Content-Type: image/png. 179 file2, fileHeader2, err := head.Request.FormFile("file2") 180 require.Nil(err) 181 defer func() { require.Nil(file2.Close()) }() 182 require.Equal("image/png", fileHeader2.Header.Get("Content-Type")) 183 require.Equal("1px.png", fileHeader2.Filename) 184 185 // Content-Type: application/json. 186 file3, fileHeader3, err := head.Request.FormFile("file3") 187 require.Nil(err) 188 defer func() { require.Nil(file3.Close()) }() 189 require.Equal("application/json", fileHeader3.Header.Get("Content-Type")) 190 require.Equal("json.json", fileHeader3.Filename) 191 defer func() { require.Nil(file1.Close()) }() 192 file3Content := make([]byte, 256) 193 n, err = io.ReadFull(file3, file3Content) 194 require.NotNil(err) 195 file3Content = file3Content[:n] 196 expected := `{ 197 "name":"1" 198 }` 199 require.Equal(expected, string(file3Content)) 200 201 // Encode json response data. 202 rsp := []byte(`{"competitionID":100000,"player":"opta"}`) 203 b, err := thttp.DefaultServerCodec.Encode(msg, rsp) 204 require.Nil(err) 205 ct := header.Response.Header().Get("Content-Type") 206 require.Equal("application/json", ct) 207 require.Nil(b) 208 } 209 210 func TestServerDecodeHTTPHeader(t *testing.T) { 211 r, err := http.NewRequest("POST", "http://www.qq.com/trpc.http.test.helloworld/SayHello", bytes.NewReader([]byte(""))) 212 require.Nil(t, err) 213 r.Header.Add("Content-Encoding", "gzip") 214 r.Header.Add("Content-Type", "application/json") 215 r.Header.Add(thttp.TrpcVersion, "1") 216 r.Header.Add(thttp.TrpcCallType, "1") 217 r.Header.Add(thttp.TrpcMessageType, "1") 218 r.Header.Add(thttp.TrpcRequestID, "1") 219 r.Header.Add(thttp.TrpcTimeout, "1000") 220 r.Header.Add(thttp.TrpcCaller, "trpc.app.server.helloworld") 221 r.Header.Add(thttp.TrpcCallee, "trpc.http.test.helloworld") 222 // Request data must encode by base64 first. 223 // val1 -> dmFsMQ== val2 -> dmFsMg== 224 r.Header.Add(thttp.TrpcTransInfo, `{"key1":"dmFsMQ==", "key2":"dmFsMg=="}`) 225 w := &httptest.ResponseRecorder{} 226 h := &thttp.Header{Request: r, Response: w} 227 ctx := thttp.WithHeader(context.Background(), h) 228 msg := codec.Message(ctx) 229 _, err = thttp.DefaultServerCodec.Decode(msg, nil) 230 require.Nil(t, err, "failed to decode get body") 231 232 require.Equal(t, codec.CompressTypeGzip, msg.CompressType()) 233 require.Equal(t, codec.SerializationTypeJSON, msg.SerializationType()) 234 235 req, ok := msg.ServerReqHead().(*trpcpb.RequestProtocol) 236 require.True(t, ok) 237 require.NotNil(t, req, "failed to decode get trpc req head") 238 require.Equal(t, 1, int(req.GetVersion())) 239 require.Equal(t, 1, int(req.GetCallType())) 240 require.Equal(t, 1, int(req.GetMessageType())) 241 require.Equal(t, 1, int(req.GetRequestId())) 242 require.Equal(t, 1000, int(req.GetTimeout())) 243 require.Equal(t, "trpc.app.server.helloworld", string(req.GetCaller())) 244 require.Equal(t, "trpc.http.test.helloworld", string(req.GetCallee())) 245 require.Equal(t, "val1", string(req.GetTransInfo()["key1"])) 246 require.Equal(t, "val2", string(req.GetTransInfo()["key2"])) 247 248 // JSON unmarshal failed. 249 r.Header.Set(thttp.TrpcTransInfo, `{"key1":"dmFsMQ==", "key2":"dmFsMg=="`) 250 w = &httptest.ResponseRecorder{} 251 h = &thttp.Header{Request: r, Response: w} 252 ctx = thttp.WithHeader(context.Background(), h) 253 msg = codec.Message(ctx) 254 _, err = thttp.DefaultServerCodec.Decode(msg, nil) 255 require.NotNil(t, err) 256 257 // base64 decode failed. 258 // If parsing fails then use raw data. 259 r.Header.Set(thttp.TrpcTransInfo, fmt.Sprintf(`{"%s":"%s"}`, thttp.TrpcEnv, "Production")) 260 w = &httptest.ResponseRecorder{} 261 h = &thttp.Header{Request: r, Response: w} 262 ctx = thttp.WithHeader(context.Background(), h) 263 msg = codec.Message(ctx) 264 _, err = thttp.DefaultServerCodec.Decode(msg, nil) 265 req, _ = msg.ServerReqHead().(*trpcpb.RequestProtocol) 266 require.Nil(t, err) 267 require.Equal(t, "Production", string(req.GetTransInfo()[thttp.TrpcEnv])) 268 269 // ReadAll failed. 270 r.Header.Set(thttp.TrpcTransInfo, `{"key1":"dmFsMQ==", "key2":"dmFsM-1"}`) 271 w = &httptest.ResponseRecorder{} 272 rp, _ := io.Pipe() 273 _ = rp.CloseWithError(err) 274 r.Body = rp 275 h = &thttp.Header{Request: r, Response: w} 276 ctx = thttp.WithHeader(context.Background(), h) 277 msg = codec.Message(ctx) 278 _, err = thttp.DefaultServerCodec.Decode(msg, nil) 279 require.NotNil(t, err) 280 } 281 282 func TestServerDecode(t *testing.T) { 283 r, _ := http.NewRequest("GET", "www.qq.com/xyz=abc", bytes.NewReader([]byte(""))) 284 w := &httptest.ResponseRecorder{} 285 m := &thttp.Header{Request: r, Response: w} 286 ctx := thttp.WithHeader(context.Background(), m) 287 msg := codec.Message(ctx) 288 msg.WithServerRspErr(errs.ErrServerNoFunc) 289 _, err := thttp.DefaultServerCodec.Decode(msg, nil) 290 require.Nil(t, err, "failed to decode get body") 291 } 292 293 func TestServerPostDecode(t *testing.T) { 294 r, _ := http.NewRequest("POST", "www.qq.com", bytes.NewReader([]byte("{xyz:\"abc\""))) 295 w := &httptest.ResponseRecorder{} 296 m := &thttp.Header{Request: r, Response: w} 297 ctx := thttp.WithHeader(context.Background(), m) 298 msg := codec.Message(ctx) 299 msg.WithServerRspErr(errs.ErrServerNoFunc) 300 sc := thttp.ServerCodec{} 301 _, err := sc.Decode(msg, nil) 302 require.Nil(t, err, "failed to decode post body") 303 } 304 305 func TestClientEncode(t *testing.T) { 306 _, msg := codec.WithNewMessage(context.Background()) 307 cc := thttp.ClientCodec{} 308 _, err := cc.Encode(msg, []byte("{\"username\":\"xyz\",\"password\":\"xyz\",\"from\":\"xyz\"}")) 309 require.Nil(t, err, "Failed to encode") 310 require.NotNil(t, msg.ClientReqHead(), "req head is nil") 311 } 312 313 func TestClientEncodeWithHeader(t *testing.T) { 314 _, msg := codec.WithNewMessage(context.Background()) 315 httpReqHeader := &thttp.ClientReqHeader{} 316 msg.WithClientReqHead(httpReqHeader) 317 cc := thttp.ClientCodec{} 318 _, err := cc.Encode(msg, []byte("{\"username\":\"xyz\",\"password\":\"xyz\",\"from\":\"xyz\"}")) 319 require.Nil(t, err, "failed to encode") 320 require.NotNil(t, msg.ClientReqHead(), "req head is nil") 321 322 // Failed to parse req header. 323 _, msg = codec.WithNewMessage(context.Background()) 324 reqHeader := &thttp.ClientRspHeader{} 325 msg.WithClientReqHead(reqHeader) 326 cc = thttp.ClientCodec{} 327 _, err = cc.Encode(msg, nil) 328 require.NotNil(t, err) 329 330 // Failed to parse rsp header. 331 _, msg = codec.WithNewMessage(context.Background()) 332 rspHeader := &thttp.ClientReqHeader{} 333 msg.WithClientRspHead(rspHeader) 334 cc = thttp.ClientCodec{} 335 _, err = cc.Encode(msg, nil) 336 require.NotNil(t, err) 337 } 338 339 func TestClientErrDecode(t *testing.T) { 340 _, msg := codec.WithNewMessage(context.Background()) 341 httprsp, err := http.ReadResponse(bufio.NewReader(strings.NewReader(respTests[0].Raw)), &http.Request{Method: "POST"}) 342 require.Nil(t, err) 343 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 344 cc := thttp.ClientCodec{} 345 _, err = cc.Decode(msg, []byte("{\"username\":\"xyz\",\"password\":\"xyz\",\"from\":\"xyz\"}")) 346 require.Nil(t, err) 347 require.NotNil(t, msg.ClientRspHead(), "req head is nil") 348 349 // Failed to parse rsp header. 350 _, m := codec.WithNewMessage(context.Background()) 351 m.WithClientRspHead(&thttp.ClientReqHeader{}) 352 cc = thttp.ClientCodec{} 353 _, err = cc.Decode(m, nil) 354 require.NotNil(t, err) 355 356 // Failed to read body. 357 rp, _ := io.Pipe() 358 _ = rp.CloseWithError(errors.New("read failed")) 359 httprsp, err = http.ReadResponse(bufio.NewReader(strings.NewReader(respTests[0].Raw)), 360 &http.Request{Method: "POST"}) 361 require.Nil(t, err) 362 httprsp.Body = rp 363 httprsp.StatusCode = http.StatusOK 364 365 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 366 cc = thttp.ClientCodec{} 367 _, err = cc.Decode(msg, []byte("{\"username\":\"xyz\",\"password\":\"xyz\",\"from\":\"xyz\"}")) 368 require.NotNil(t, err) 369 370 // HTTP status code is 300 (when status code >= 300, ClientCodec.Decode should return response error). 371 httprsp, err = http.ReadResponse(bufio.NewReader(strings.NewReader(respTests[0].Raw)), 372 &http.Request{Method: "POST"}) 373 require.Nil(t, err) 374 httprsp.StatusCode = http.StatusMultipleChoices 375 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 376 377 cc = thttp.ClientCodec{} 378 _, err = cc.Decode(msg, []byte("{\"username\":\"xyz\",\"password\":\"xyz\",\"from\":\"xyz\"}")) 379 require.Nil(t, err, "Failed to decode") 380 require.NotNil(t, msg.ClientRspErr(), "response error should not be nil") 381 } 382 383 func TestClientSuccessDecode(t *testing.T) { 384 _, msg := codec.WithNewMessage(context.Background()) 385 httprsp, _ := http.ReadResponse(bufio.NewReader(strings.NewReader(respTests[1].Raw)), 386 &http.Request{Method: "POST"}) 387 httprsp.Header.Add("Content-Encoding", "gzip") 388 httprsp.Header.Add("trpc-trans-info", `{"key1":"val1", "key2":"val2"}`) 389 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 390 body, err := thttp.DefaultClientCodec.Decode(msg, []byte("{\"username\":\"xyz\","+ 391 "\"password\":\"xyz\",\"from\":\"xyz\"}")) 392 require.Nil(t, err, "Failed to decode") 393 require.NotNil(t, msg.ClientRspHead(), "req head is nil") 394 require.Equal(t, string(body), respTests[1].Body, "body is error", string(body)) 395 require.Equal(t, codec.CompressTypeGzip, msg.CompressType()) 396 397 // HTTP status code 101. 398 httprsp, err = http.ReadResponse(bufio.NewReader(strings.NewReader(respTests[2].Raw)), 399 &http.Request{Method: "POST"}) 400 require.Nil(t, err) 401 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 402 403 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 404 cc := thttp.ClientCodec{} 405 body, err = cc.Decode(msg, []byte("{\"username\":\"xyz\",\"password\":\"xyz\",\"from\":\"xyz\"}")) 406 require.Nil(t, err, "Failed to decode") 407 require.Empty(t, body) 408 409 // HTTP status code 201. 410 httprsp, err = http.ReadResponse(bufio.NewReader(strings.NewReader(respTests[0].Raw)), 411 &http.Request{Method: "POST"}) 412 require.Nil(t, err) 413 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 414 httprsp.StatusCode = http.StatusCreated 415 416 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 417 cc = thttp.ClientCodec{} 418 body, err = cc.Decode(msg, []byte("{\"username\":\"xyz\",\"password\":\"xyz\",\"from\":\"xyz\"}")) 419 require.Nil(t, err, "Failed to decode") 420 require.Equal(t, respTests[0].Body, string(body), "body is error", string(body)) 421 } 422 423 func TestClientRetDecode(t *testing.T) { 424 _, msg := codec.WithNewMessage(context.Background()) 425 httprsp, err := http.ReadResponse(bufio.NewReader(strings.NewReader(respTests[1].Raw)), &http.Request{Method: "POST"}) 426 require.Nil(t, err) 427 httprsp.Header.Add("trpc-ret", "1") 428 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 429 _, err = thttp.DefaultClientCodec.Decode(msg, 430 []byte("{\"username\":\"xyz\",\"password\":\"xyz\",\"from\":\"xyz\"}")) 431 require.Nil(t, err, "Failed to decode") 432 require.NotNil(t, msg.ClientRspErr()) 433 require.EqualValues(t, 1, errs.Code(msg.ClientRspErr())) 434 } 435 436 func TestClientFuncRetDecode(t *testing.T) { 437 _, msg := codec.WithNewMessage(context.Background()) 438 httprsp, err := http.ReadResponse(bufio.NewReader(strings.NewReader(respTests[1].Raw)), &http.Request{Method: "POST"}) 439 require.Nil(t, err) 440 httprsp.Header.Add("trpc-func-ret", "1000") 441 httprsp.Header.Add("Content-Type", "application/json") 442 msg.WithClientRspHead(&thttp.ClientRspHeader{Response: httprsp}) 443 _, err = thttp.DefaultClientCodec.Decode(msg, 444 []byte("{\"username\":\"xyz\",\"password\":\"xyz\",\"from\":\"xyz\"}")) 445 require.Nil(t, err, "Failed to decode") 446 require.NotNil(t, msg.ClientRspErr()) 447 require.EqualValues(t, 1000, errs.Code(msg.ClientRspErr())) 448 } 449 450 func TestServiceDecodeWithHeader(t *testing.T) { 451 req := &http.Request{ 452 URL: &url.URL{ 453 Path: "my_path", 454 }, 455 } 456 header := &thttp.Header{ 457 Request: req, 458 } 459 460 sc := &thttp.ServerCodec{} 461 ctx := thttp.WithHeader(context.Background(), header) 462 _, msg := codec.WithNewMessage(ctx) 463 464 _, err := sc.Decode(msg, nil) 465 assert.Nil(t, err) 466 467 method := msg.CalleeMethod() 468 rpcName := msg.ServerRPCName() 469 470 assert.Equal(t, method, req.URL.Path) 471 assert.Equal(t, rpcName, req.URL.Path) 472 } 473 474 func TestServerCodecDecodeTransInfo(t *testing.T) { 475 transInfo := map[string]string{ 476 thttp.TrpcEnv: base64.StdEncoding.EncodeToString([]byte("env-test")), 477 thttp.TrpcDyeingKey: base64.StdEncoding.EncodeToString([]byte("dyeing-test")), 478 } 479 data, err := json.Marshal(transInfo) 480 require.Nil(t, err) 481 head := http.Header{} 482 head.Add(thttp.TrpcTransInfo, string(data)) 483 484 req := &http.Request{ 485 Header: head, 486 URL: &url.URL{}, 487 } 488 489 header := &thttp.Header{ 490 Request: req, 491 } 492 493 sc := &thttp.ServerCodec{ 494 AutoGenTrpcHead: true, 495 } 496 ctx := thttp.WithHeader(context.Background(), header) 497 _, msg := codec.WithNewMessage(ctx) 498 499 _, err = sc.Decode(msg, nil) 500 require.Nil(t, err) 501 502 assert.Equal(t, msg.EnvTransfer(), "env-test") 503 assert.Equal(t, msg.DyeingKey(), "dyeing-test") 504 } 505 506 func TestDisableEncodeBase64(t *testing.T) { 507 r, err := http.NewRequest("POST", "/SayHello", bytes.NewReader([]byte(""))) 508 require.Nil(t, err) 509 w := &httptest.ResponseRecorder{} 510 h := &thttp.Header{Request: r, Response: w} 511 ctx := thttp.WithHeader(context.Background(), h) 512 ctx, msg := codec.EnsureMessage(ctx) 513 msg.WithServerMetaData(codec.MetaData{"meta-key": []byte("meta-value")}) 514 515 serverCodec := thttp.ServerCodec{ 516 DisableEncodeTransInfoBase64: true, 517 } 518 _, err = serverCodec.Encode(msg, nil) 519 require.Nil(t, err) 520 rsp := thttp.Head(ctx).Response 521 require.Contains(t, string(rsp.Header().Get(thttp.TrpcTransInfo)), "meta-value") 522 } 523 524 func TestCoexistenceOfHTTPRPCAndNoProtocol(t *testing.T) { 525 defer func() { thttp.ServiceDesc.Methods = thttp.ServiceDesc.Methods[:0] }() 526 ln, err := net.Listen("tcp", "127.0.0.1:0") 527 require.Nil(t, err) 528 defer ln.Close() 529 serviceName := "trpc.test.hello.service" + t.Name() 530 s := server.New( 531 server.WithServiceName(serviceName), 532 server.WithListener(ln), 533 // Although the "http" protocol is represented as an HTTP RPC service and 534 // the standard HTTP service has its own protocol "http_no_protocol", some 535 // users require that both protocols can coexist in the same service 536 // (with the same ip and port). 537 // This requires that the standard HTTP handler function can still read the 538 // request body, even if the `AutoReadBody` field in the default server 539 // codec `DefaultServerCodec` for the `http` protocol is `true`. 540 server.WithProtocol("http"), 541 ) 542 // Register standard HTTP handle. 543 thttp.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) error { 544 s := &codec.JSONPBSerialization{} 545 body, err := io.ReadAll(r.Body) 546 if err != nil { 547 return err 548 } 549 req := &helloworld.HelloRequest{} 550 if err := s.Unmarshal(body, req); err != nil { 551 return err 552 } 553 rsp := &helloworld.HelloReply{Message: req.Name} 554 body, err = s.Marshal(rsp) 555 if err != nil { 556 return err 557 } 558 w.WriteHeader(http.StatusOK) 559 w.Write(body) 560 return nil 561 }) 562 thttp.RegisterNoProtocolService(s) 563 // Register protocol file service (HTTP RPC) implementation. 564 helloworld.RegisterGreeterService(s, &greeterImpl{}) 565 566 // Start server. 567 go s.Serve() 568 569 ctx := context.Background() 570 target := "ip://" + ln.Addr().String() 571 572 // Send standard HTTP request. 573 c := thttp.NewClientProxy(serviceName, client.WithTarget(target)) 574 msg := "hello" 575 req := &helloworld.HelloRequest{Name: msg} 576 rsp := &helloworld.HelloReply{} 577 require.Nil(t, c.Post(ctx, "/", req, rsp, 578 client.WithSerializationType(codec.SerializationTypeJSON))) 579 require.Equal(t, msg, rsp.Message) 580 581 // Send HTTP RPC request. 582 proxy := helloworld.NewGreeterClientProxy(client.WithTarget(target), client.WithProtocol("http")) 583 resp, err := proxy.SayHello(ctx, &helloworld.HelloRequest{Name: msg}) 584 require.Nil(t, err) 585 require.Equal(t, msg, resp.Message) 586 } 587 588 type greeterImpl struct{} 589 590 func (i *greeterImpl) SayHello(ctx context.Context, req *helloworld.HelloRequest) (*helloworld.HelloReply, error) { 591 return &helloworld.HelloReply{Message: req.Name}, nil 592 } 593 594 func (i *greeterImpl) SayHi(ctx context.Context, req *helloworld.HelloRequest) (*helloworld.HelloReply, error) { 595 return nil, nil 596 } 597 598 type responseRecorder struct { 599 httptest.ResponseRecorder 600 } 601 602 func (r *responseRecorder) Write(buf []byte) (int, error) { 603 return 0, errors.New("write failed") 604 } 605 606 type respTest struct { 607 Raw string 608 Body string 609 } 610 611 var respTests = []respTest{ 612 // Unchunked response without Content-Length. 613 { 614 "HTTP/1.0 404 NOT FOUND\r\n" + 615 "Connection: close\r\n" + 616 "\r\n" + 617 "Body here\n", 618 619 "Body here\n", 620 }, 621 622 // Unchunked HTTP/1.1 response without Content-Length or 623 // Connection headers. 624 { 625 "HTTP/1.1 200 OK\r\n" + 626 "\r\n" + 627 "{\"msg\":\"from hi\"}\n", 628 629 "{\"msg\":\"from hi\"}\n", 630 }, 631 632 // Unchunked HTTP/1.1 response without body. 633 { 634 "HTTP/1.1 101 Switching Protocols\r\n" + 635 "\r\n", 636 637 "", 638 }}