github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/conv/t2j/impl.go (about) 1 /** 2 * Copyright 2023 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 t2j 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 24 "github.com/cloudwego/dynamicgo/conv" 25 "github.com/cloudwego/dynamicgo/http" 26 "github.com/cloudwego/dynamicgo/internal/json" 27 "github.com/cloudwego/dynamicgo/internal/primitive" 28 "github.com/cloudwego/dynamicgo/internal/rt" 29 "github.com/cloudwego/dynamicgo/meta" 30 "github.com/cloudwego/dynamicgo/thrift" 31 "github.com/cloudwego/dynamicgo/thrift/base" 32 ) 33 34 const ( 35 _GUARD_SLICE_FACTOR = 2 36 ) 37 38 //go:noinline 39 func wrapError(code meta.ErrCode, msg string, err error) error { 40 // panic(meta.NewError(meta.NewErrorCode(code, meta.THRIFT2JSON), msg, err)) 41 return meta.NewError(meta.NewErrorCode(code, meta.THRIFT2JSON), msg, err) 42 } 43 44 //go:noinline 45 func unwrapError(msg string, err error) error { 46 if v, ok := err.(meta.Error); ok { 47 return wrapError(v.Code, msg, err) 48 } else { 49 return wrapError(meta.ErrConvert, msg, err) 50 } 51 } 52 53 func (self *BinaryConv) readResponseBase(ctx context.Context, p *thrift.BinaryProtocol) (bool, error) { 54 obj := ctx.Value(conv.CtxKeyThriftRespBase) 55 if obj == nil { 56 return false, nil 57 } 58 base, ok := obj.(*base.BaseResp) 59 if !ok || base == nil { 60 return false, wrapError(meta.ErrInvalidParam, "invalid response base", nil) 61 } 62 s := p.Read 63 if err := p.Skip(thrift.STRUCT, self.opts.UseNativeSkip); err != nil { 64 return false, wrapError(meta.ErrRead, "", err) 65 } 66 e := p.Read 67 if _, err := base.FastRead(p.Buf[s:e]); err != nil { 68 return false, wrapError(meta.ErrRead, "", err) 69 } 70 return true, nil 71 } 72 73 func (self *BinaryConv) do(ctx context.Context, src []byte, desc *thrift.TypeDescriptor, out *[]byte, resp http.ResponseSetter) (err error) { 74 //NOTICE: output buffer must be larger than src buffer 75 rt.GuardSlice(out, len(src)*_GUARD_SLICE_FACTOR) 76 77 var p = thrift.BinaryProtocol{ 78 Buf: src, 79 } 80 81 if desc.Type() != thrift.STRUCT { 82 return self.doRecurse(ctx, desc, out, resp, &p) 83 } 84 85 _, e := p.ReadStructBegin() 86 if e != nil { 87 return wrapError(meta.ErrRead, "", e) 88 } 89 *out = json.EncodeObjectBegin(*out) 90 91 r := thrift.NewRequiresBitmap() 92 desc.Struct().Requires().CopyTo(r) 93 comma := false 94 95 existExceptionField := false 96 97 for { 98 _, typeId, id, e := p.ReadFieldBegin() 99 if e != nil { 100 return wrapError(meta.ErrRead, "", e) 101 } 102 if typeId == thrift.STOP { 103 break 104 } 105 106 field := desc.Struct().FieldById(thrift.FieldID(id)) 107 if field == nil { 108 if self.opts.DisallowUnknownField { 109 return wrapError(meta.ErrUnknownField, fmt.Sprintf("unknown field %d", id), nil) 110 } 111 if e := p.Skip(typeId, self.opts.UseNativeSkip); e != nil { 112 return wrapError(meta.ErrRead, "", e) 113 } 114 continue 115 } 116 117 r.Set(field.ID(), thrift.OptionalRequireness) 118 119 if self.opts.EnableThriftBase && field.IsResponseBase() { 120 skip, err := self.readResponseBase(ctx, &p) 121 if err != nil { 122 return err 123 } 124 if skip { 125 continue 126 } 127 } 128 129 restart := p.Read 130 131 if resp != nil && self.opts.EnableHttpMapping && field.HTTPMappings() != nil { 132 ok, err := self.writeHttpValue(ctx, resp, &p, field) 133 if err != nil { 134 return unwrapError(fmt.Sprintf("mapping field %s of STRUCT %s failed", field.Name(), desc.Name()), err) 135 } 136 // NOTICE: if option HttpMappingAsExtra is false and http-mapping failed, 137 // continue to write to json body 138 if !self.opts.WriteHttpValueFallback || ok { 139 continue 140 } 141 } 142 143 p.Read = restart 144 if comma { 145 *out = json.EncodeObjectComma(*out) 146 } else { 147 comma = true 148 } 149 // NOTICE: always use field.Alias() here, because alias equals to name by default 150 *out = json.EncodeString(*out, field.Alias()) 151 *out = json.EncodeObjectColon(*out) 152 153 // handle a thrift exception field. the id of a thrift exception field is non-zero 154 if self.opts.ConvertException && id != 0 { 155 existExceptionField = true 156 // reset out to get only exception field data 157 *out = (*out)[:0] 158 } 159 160 if self.opts.EnableValueMapping && field.ValueMapping() != nil { 161 err = field.ValueMapping().Read(ctx, &p, field, out) 162 if err != nil { 163 return unwrapError(fmt.Sprintf("mapping field %s of STRUCT %s failed", field.Name(), desc.Type()), err) 164 } 165 } else { 166 err = self.doRecurse(ctx, field.Type(), out, resp, &p) 167 if err != nil { 168 return unwrapError(fmt.Sprintf("converting field %s of STRUCT %s failed", field.Name(), desc.Type()), err) 169 } 170 } 171 172 if existExceptionField { 173 break 174 } 175 } 176 177 if err = self.handleUnsets(r, desc.Struct(), out, comma, ctx, resp); err != nil { 178 return err 179 } 180 181 thrift.FreeRequiresBitmap(r) 182 if existExceptionField && err == nil { 183 err = errors.New(string(*out)) 184 } else { 185 *out = json.EncodeObjectEnd(*out) 186 } 187 return err 188 } 189 190 func (self *BinaryConv) doRecurse(ctx context.Context, desc *thrift.TypeDescriptor, out *[]byte, resp http.ResponseSetter, p *thrift.BinaryProtocol) (err error) { 191 tt := desc.Type() 192 switch tt { 193 case thrift.BOOL: 194 v, e := p.ReadBool() 195 if e != nil { 196 return wrapError(meta.ErrRead, "", e) 197 } 198 *out = json.EncodeBool(*out, v) 199 case thrift.BYTE: 200 v, e := p.ReadByte() 201 if e != nil { 202 return wrapError(meta.ErrWrite, "", e) 203 } 204 if self.opts.ByteAsUint8 { 205 *out = json.EncodeInt64(*out, int64(uint8(v))) 206 } else { 207 *out = json.EncodeInt64(*out, int64(int8(v))) 208 } 209 case thrift.I16: 210 v, e := p.ReadI16() 211 if e != nil { 212 return wrapError(meta.ErrWrite, "", e) 213 } 214 *out = json.EncodeInt64(*out, int64(v)) 215 case thrift.I32: 216 v, e := p.ReadI32() 217 if e != nil { 218 return wrapError(meta.ErrWrite, "", e) 219 } 220 *out = json.EncodeInt64(*out, int64(v)) 221 case thrift.I64: 222 v, e := p.ReadI64() 223 if e != nil { 224 return wrapError(meta.ErrWrite, "", e) 225 } 226 if self.opts.Int642String { 227 *out = append(*out, '"') 228 *out = json.EncodeInt64(*out, int64(v)) 229 *out = append(*out, '"') 230 } else { 231 *out = json.EncodeInt64(*out, int64(v)) 232 } 233 case thrift.DOUBLE: 234 v, e := p.ReadDouble() 235 if e != nil { 236 return wrapError(meta.ErrWrite, "", e) 237 } 238 *out = json.EncodeFloat64(*out, float64(v)) 239 case thrift.STRING: 240 if desc.IsBinary() && !self.opts.NoBase64Binary { 241 v, e := p.ReadBinary(false) 242 if e != nil { 243 return wrapError(meta.ErrRead, "", e) 244 } 245 *out = json.EncodeBaniry(*out, v) 246 } else { 247 v, e := p.ReadString(false) 248 if e != nil { 249 return wrapError(meta.ErrRead, "", e) 250 } 251 *out = json.EncodeString(*out, v) 252 } 253 case thrift.STRUCT: 254 _, e := p.ReadStructBegin() 255 if e != nil { 256 return wrapError(meta.ErrRead, "", e) 257 } 258 *out = json.EncodeObjectBegin(*out) 259 260 r := thrift.NewRequiresBitmap() 261 desc.Struct().Requires().CopyTo(r) 262 comma := false 263 264 for { 265 _, typeId, id, e := p.ReadFieldBegin() 266 if e != nil { 267 return wrapError(meta.ErrRead, "", e) 268 } 269 if typeId == 0 { 270 break 271 } 272 273 field := desc.Struct().FieldById(thrift.FieldID(id)) 274 if field == nil { 275 if self.opts.DisallowUnknownField { 276 return wrapError(meta.ErrUnknownField, fmt.Sprintf("unknown field %d", id), nil) 277 } 278 if e := p.Skip(typeId, self.opts.UseNativeSkip); e != nil { 279 return wrapError(meta.ErrRead, "", e) 280 } 281 continue 282 } 283 284 r.Set(field.ID(), thrift.OptionalRequireness) 285 restart := p.Read 286 287 if resp != nil && self.opts.EnableHttpMapping && field.HTTPMappings() != nil { 288 ok, err := self.writeHttpValue(ctx, resp, p, field) 289 if err != nil { 290 return unwrapError(fmt.Sprintf("mapping field %s of STRUCT %s failed", field.Name(), desc.Name()), err) 291 } 292 // NOTICE: if option HttpMappingAsExtra is false and http-mapping failed, 293 // continue to write to json body 294 if !self.opts.WriteHttpValueFallback || ok { 295 continue 296 } 297 } 298 299 // HttpMappingAsExtra is true, return to begining and write json body 300 p.Read = restart 301 if comma { 302 *out = json.EncodeObjectComma(*out) 303 if err != nil { 304 return wrapError(meta.ErrWrite, "", err) 305 } 306 } else { 307 comma = true 308 } 309 // NOTICE: always use field.Alias() here, because alias equals to name by default 310 *out = json.EncodeString(*out, field.Alias()) 311 *out = json.EncodeObjectColon(*out) 312 313 if self.opts.EnableValueMapping && field.ValueMapping() != nil { 314 if err = field.ValueMapping().Read(ctx, p, field, out); err != nil { 315 return unwrapError(fmt.Sprintf("mapping field %s of STRUCT %s failed", field.Name(), desc.Type()), err) 316 } 317 } else { 318 err = self.doRecurse(ctx, field.Type(), out, nil, p) 319 if err != nil { 320 return unwrapError(fmt.Sprintf("converting field %s of STRUCT %s failed", field.Name(), desc.Type()), err) 321 } 322 } 323 } 324 325 if err = self.handleUnsets(r, desc.Struct(), out, comma, ctx, resp); err != nil { 326 return err 327 } 328 329 thrift.FreeRequiresBitmap(r) 330 *out = json.EncodeObjectEnd(*out) 331 case thrift.MAP: 332 keyType, valueType, size, e := p.ReadMapBegin() 333 if e != nil { 334 return wrapError(meta.ErrRead, "", e) 335 } 336 if keyType != desc.Key().Type() { 337 return wrapError(meta.ErrDismatchType, fmt.Sprintf("expect type %s but got type %s", desc.Key().Type(), keyType), nil) 338 } else if valueType != desc.Elem().Type() { 339 return wrapError(meta.ErrDismatchType, fmt.Sprintf("expect type %s but got type %s", desc.Elem().Type(), valueType), nil) 340 } 341 *out = json.EncodeObjectBegin(*out) 342 for i := 0; i < size; i++ { 343 if i != 0 { 344 *out = json.EncodeObjectComma(*out) 345 } 346 err = self.buildinTypeToKey(p, desc.Key(), out) 347 if err != nil { 348 return wrapError(meta.ErrConvert, "", err) 349 } 350 *out = json.EncodeObjectColon(*out) 351 err = self.doRecurse(ctx, desc.Elem(), out, nil, p) 352 if err != nil { 353 return unwrapError(fmt.Sprintf("converting %dth element of MAP failed", i), err) 354 } 355 } 356 e = p.ReadMapEnd() 357 if e != nil { 358 return wrapError(meta.ErrRead, "", e) 359 } 360 *out = json.EncodeObjectEnd(*out) 361 case thrift.SET, thrift.LIST: 362 elemType, size, e := p.ReadSetBegin() 363 if e != nil { 364 return wrapError(meta.ErrRead, "", e) 365 } 366 if elemType != desc.Elem().Type() { 367 return wrapError(meta.ErrDismatchType, fmt.Sprintf("expect type %s but got type %s", desc.Elem().Type(), elemType), nil) 368 } 369 *out = json.EncodeArrayBegin(*out) 370 for i := 0; i < size; i++ { 371 if i != 0 { 372 *out = json.EncodeArrayComma(*out) 373 } 374 err = self.doRecurse(ctx, desc.Elem(), out, nil, p) 375 if err != nil { 376 return unwrapError(fmt.Sprintf("converting %dth element of SET failed", i), err) 377 } 378 } 379 e = p.ReadSetEnd() 380 if e != nil { 381 return wrapError(meta.ErrRead, "", e) 382 } 383 *out = json.EncodeArrayEnd(*out) 384 385 default: 386 return wrapError(meta.ErrUnsupportedType, fmt.Sprintf("unknown descriptor type %s", tt), nil) 387 } 388 389 return 390 } 391 392 func (self *BinaryConv) handleUnsets(b *thrift.RequiresBitmap, desc *thrift.StructDescriptor, out *[]byte, comma bool, ctx context.Context, resp http.ResponseSetter) error { 393 return b.HandleRequires(desc, self.opts.WriteRequireField, self.opts.WriteDefaultField, self.opts.WriteOptionalField, func(field *thrift.FieldDescriptor) error { 394 // check if field has http mapping 395 var ok = false 396 if hms := field.HTTPMappings(); self.opts.EnableHttpMapping && hms != nil { 397 // make a default thrift value 398 p := thrift.BinaryProtocol{Buf: make([]byte, 0, conv.DefaulHttpValueBufferSizeForJSON)}; 399 if err := p.WriteDefaultOrEmpty(field); err != nil { 400 return wrapError(meta.ErrWrite, fmt.Sprintf("encoding field '%s' default value failed", field.Name()), err) 401 } 402 // convert it into http 403 var err error 404 ok, err = self.writeHttpValue(ctx, resp, &p, field) 405 if err != nil { 406 return err 407 } 408 } 409 if ok { 410 return nil 411 } 412 if !comma { 413 comma = true 414 } else { 415 *out = json.EncodeArrayComma(*out) 416 } 417 *out = json.EncodeString(*out, field.Name()) 418 *out = json.EncodeObjectColon(*out) 419 return writeDefaultOrEmpty(field, out) 420 }) 421 } 422 423 func writeDefaultOrEmpty(field *thrift.FieldDescriptor, out *[]byte) (err error) { 424 if dv := field.DefaultValue(); dv != nil { 425 *out = append(*out, dv.JSONValue()...) 426 return nil 427 } 428 switch t := field.Type().Type(); t { 429 case thrift.BOOL: 430 *out = json.EncodeBool(*out, false) 431 case thrift.BYTE, thrift.I16, thrift.I32, thrift.I64: 432 *out = json.EncodeInt64(*out, 0) 433 case thrift.DOUBLE: 434 *out = json.EncodeFloat64(*out, 0) 435 case thrift.STRING: 436 *out = json.EncodeString(*out, "") 437 case thrift.LIST, thrift.SET: 438 *out = json.EncodeEmptyArray(*out) 439 case thrift.MAP: 440 *out = json.EncodeEmptyObject(*out) 441 case thrift.STRUCT: 442 *out = json.EncodeObjectBegin(*out) 443 for i, field := range field.Type().Struct().Fields() { 444 if i != 0 { 445 *out = json.EncodeObjectComma(*out) 446 } 447 *out = json.EncodeString(*out, field.Alias()) 448 *out = json.EncodeObjectColon(*out) 449 if err := writeDefaultOrEmpty(field, out); err != nil { 450 return err 451 } 452 } 453 *out = json.EncodeObjectEnd(*out) 454 default: 455 return wrapError(meta.ErrUnsupportedType, fmt.Sprintf("unknown descriptor type %s", t), nil) 456 } 457 return err 458 } 459 460 func (self *BinaryConv) buildinTypeToKey(p *thrift.BinaryProtocol, dest *thrift.TypeDescriptor, out *[]byte) error { 461 *out = append(*out, '"') 462 switch t := dest.Type(); t { 463 // case thrift.BOOL: 464 // v, err := p.ReadBool() 465 // if err != nil { 466 // return err 467 // } 468 // *out = json.EncodeBool(*out, v) 469 case thrift.I08: 470 v, err := p.ReadByte() 471 if err != nil { 472 return err 473 } 474 if self.opts.ByteAsUint8 { 475 *out = json.EncodeInt64(*out, int64(uint8(v))) 476 } else { 477 *out = json.EncodeInt64(*out, int64(int8(v))) 478 } 479 case thrift.I16: 480 v, err := p.ReadI16() 481 if err != nil { 482 return err 483 } 484 *out = json.EncodeInt64(*out, int64(v)) 485 case thrift.I32: 486 v, err := p.ReadI32() 487 if err != nil { 488 return err 489 } 490 *out = json.EncodeInt64(*out, int64(v)) 491 case thrift.I64: 492 v, err := p.ReadI64() 493 if err != nil { 494 return err 495 } 496 *out = json.EncodeInt64(*out, int64(v)) 497 // case thrift.DOUBLE: 498 // v, err := p.ReadDouble() 499 // if err != nil { 500 // return err 501 // } 502 // *out, err = json.EncodeFloat64(*out, v) 503 case thrift.STRING: 504 v, err := p.ReadString(false) 505 if err != nil { 506 return err 507 } 508 json.NoQuote(out, v) 509 default: 510 return wrapError(meta.ErrUnsupportedType, fmt.Sprintf("unsupported descriptor type %s as MAP key", t), nil) 511 } 512 *out = append(*out, '"') 513 return nil 514 } 515 516 func (self *BinaryConv) writeHttpValue(ctx context.Context, resp http.ResponseSetter, p *thrift.BinaryProtocol, field *thrift.FieldDescriptor) (ok bool, err error) { 517 var thriftVal []byte 518 var jsonVal []byte 519 var textVal []byte 520 521 for _, hm := range field.HTTPMappings() { 522 var val []byte 523 enc := hm.Encoding(); 524 525 if enc == meta.EncodingThriftBinary { 526 // raw encoding, check if raw value is set 527 if thriftVal == nil { 528 var start = p.Read 529 if err := p.Skip(field.Type().Type(), self.opts.UseNativeSkip); err != nil { 530 return false, wrapError(meta.ErrRead, "", err) 531 } 532 val = p.Buf[start:p.Read] 533 thriftVal = val 534 } else { 535 val = thriftVal 536 } 537 538 } else if enc == meta.EncodingText || !field.Type().Type().IsComplex() { // not complex value, must use text encoding 539 if textVal == nil { 540 tmp := make([]byte, 0, conv.DefaulHttpValueBufferSizeForJSON) 541 err = p.ReadStringWithDesc(field.Type(), &tmp, self.opts.ByteAsUint8, self.opts.DisallowUnknownField, !self.opts.NoBase64Binary) 542 if err != nil { 543 return false, unwrapError(fmt.Sprintf("reading thrift value of '%s' failed, thrift pos:%d", field.Name(), p.Read), err) 544 } 545 val = tmp 546 textVal = val 547 } else { 548 val = textVal 549 } 550 } else if self.opts.UseKitexHttpEncoding { 551 // kitex http encoding fallback 552 if textVal == nil { 553 obj, err := p.ReadAnyWithDesc(field.Type(), self.opts.ByteAsUint8, !self.opts.NoCopyString, self.opts.DisallowUnknownField, true) 554 if err != nil { 555 return false, unwrapError(fmt.Sprintf("reading thrift value of '%s' failed, thrift pos:%d", field.Name(), p.Read), err) 556 } 557 textVal = rt.Str2Mem(primitive.KitexToString(obj)) 558 val = textVal 559 } else { 560 val = textVal 561 } 562 } else if enc == meta.EncodingJSON { 563 // for nested type, convert it to a new JSON string 564 if jsonVal == nil { 565 tmp := make([]byte, 0, conv.DefaulHttpValueBufferSizeForJSON) 566 err := self.doRecurse(ctx, field.Type(), &tmp, resp, p) 567 if err != nil { 568 return false, unwrapError(fmt.Sprintf("mapping field %s failed, thrift pos:%d", field.Name(), p.Read), err) 569 } 570 val = tmp 571 jsonVal = val 572 } else { 573 val = jsonVal 574 } 575 576 } else { 577 return false, wrapError(meta.ErrConvert, fmt.Sprintf("unsuported http-value encoding %v of field '%s'", enc, field.Name()), nil) 578 } 579 580 // NOTICE: ignore error if the value is not set 581 if e := hm.Response(ctx, resp, field, rt.Mem2Str(val)); e == nil { 582 ok = true 583 break 584 } else if !self.opts.OmitHttpMappingErrors { 585 return false, e 586 } 587 } 588 return 589 } 590 591 // func setValueToHttp(resp http.ResponseSetter, typ thrift.AnnoType, key string, val string) error { 592 // switch typ { 593 // case annotation.APIHeader: 594 // return resp.SetHeader(key, val) 595 // case annotation.APIRawBody: 596 // return resp.SetRawBody(rt.Str2Mem(val)) 597 // case annotation.APICookie: 598 // return resp.SetCookie(key, val) 599 // case annotation.APIHTTPCode: 600 // code, err := strconv.Atoi(val) 601 // if err != nil { 602 // return err 603 // } 604 // return resp.SetStatusCode(code) 605 // default: 606 // return wrapError(meta.ErrUnsupportedType, fmt.Sprintf("unsupported annotation type %d", typ), nil) 607 // } 608 // }