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 }