Skip to content

Commit

Permalink
fix:(thrift) only enable api.body fast-path on demands (#84)
Browse files Browse the repository at this point in the history
* fix:(thrift) `api.body` shoudn't change field alias by default (for json-generic)

* fix test

* test

* test

* add test

* ci: remove go_latest test
  • Loading branch information
AsterDY authored Jan 8, 2025
1 parent 55704ea commit d8a885f
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 40 deletions.
66 changes: 44 additions & 22 deletions conv/j2t/conv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,29 +552,51 @@ func TestNullJSON2Thrift(t *testing.T) {
}

func TestApiBody(t *testing.T) {
desc := getExampleDescByName("ApiBodyMethod", true, thrift.Options{})
data := []byte(`{"code":1024,"InnerCode":{}}`)
cv := NewBinaryConv(conv.Options{
EnableHttpMapping: true,
WriteDefaultField: true,
ReadHttpValueFallback: true,
TracebackRequredOrRootFields: true,
t.Run("http", func(t *testing.T) {
desc := getExampleDescByName("ApiBodyMethod", true, thrift.Options{
ApiBodyFastPath: true,
})
data := []byte(`{"code":1024,"Code":2048,"InnerCode":{}}`)
cv := NewBinaryConv(conv.Options{
EnableHttpMapping: true,
WriteDefaultField: true,
ReadHttpValueFallback: true,
TracebackRequredOrRootFields: true,
})
ctx := context.Background()
req, err := stdh.NewRequest("POST", "http://localhost:8888/root", bytes.NewBuffer(data))
require.Nil(t, err)
req.Header.Set("Content-Type", "application/json")
r, _ := http.NewHTTPRequestFromStdReq(req)
ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, r)
out, err := cv.Do(ctx, desc, data)
require.Nil(t, err)
act := example3.NewExampleApiBody()
_, err = act.FastRead(out)
require.Nil(t, err)
require.Equal(t, int64(1024), act.Code)
require.Equal(t, int16(1024), act.Code2)
require.Equal(t, int64(1024), act.InnerCode.C1)
require.Equal(t, int16(0), act.InnerCode.C2)
})
t.Run("not http", func(t *testing.T) {
desc := getExampleDescByName("ApiBodyMethod", true, thrift.Options{
ApiBodyFastPath: false,
})
data := []byte(`{"code":1024,"Code":2048,"InnerCode":{"C1":1,"code":2}}`)
cv := NewBinaryConv(conv.Options{
WriteDefaultField: true,
})
out, err := cv.Do(context.Background(), desc, data)
require.Nil(t, err)
act := example3.NewExampleApiBody()
_, err = act.FastRead(out)
require.Nil(t, err)
require.Equal(t, int64(2048), act.Code)
require.Equal(t, int16(1024), act.Code2)
require.Equal(t, int64(1), act.InnerCode.C1)
require.Equal(t, int16(2), act.InnerCode.C2)
})
ctx := context.Background()
req, err := stdh.NewRequest("POST", "http://localhost:8888/root", bytes.NewBuffer(data))
require.Nil(t, err)
req.Header.Set("Content-Type", "application/json")
r, err := http.NewHTTPRequestFromStdReq(req)
ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, r)
out, err := cv.Do(ctx, desc, data)
require.Nil(t, err)
act := example3.NewExampleApiBody()
_, err = act.FastRead(out)
require.Nil(t, err)
require.Equal(t, int64(1024), act.Code)
require.Equal(t, int16(1024), act.Code2)
require.Equal(t, int64(1024), act.InnerCode.C1)
require.Equal(t, int16(0), act.InnerCode.C2)
}

func TestError(t *testing.T) {
Expand Down
22 changes: 22 additions & 0 deletions conv/t2j/conv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,28 @@ func TestAGWBodyDynamic(t *testing.T) {
require.Equal(t, (`{"Int64":1,"Xjson":"{\"b\":1}"}`), string(out))
}

func TestAPIBody(t *testing.T) {
cv := NewBinaryConv(conv.Options{
EnableValueMapping: true,
})
desc := GetDescByName("ApiBodyMethod", false)
exp := example3.NewExampleApiBody()
exp.Code = 1
exp.Code2 = 2
exp.InnerCode = new(example3.InnerCode)
exp.InnerCode.C1 = 31
exp.InnerCode.C2 = 32
exp.InnerCode.C3 = []*example3.InnerCode{
{C1: 41, C2: 42},
}
ctx := context.Background()
in := make([]byte, exp.BLength())
_ = exp.FastWriteNocopy(in, nil)
out, err := cv.Do(ctx, desc, in)
require.NoError(t, err)
require.Equal(t, `{"Code":1,"code":2,"InnerCode":{"C1":31,"code":32,"C3":[{"C1":41,"code":42,"C3":[]}]}}`, string(out))
}

func TestException(t *testing.T) {
cv := NewBinaryConv(conv.Options{
ConvertException: true,
Expand Down
16 changes: 8 additions & 8 deletions conv/t2j/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (self *BinaryConv) readResponseBase(ctx context.Context, p *thrift.BinaryPr
func (self *BinaryConv) do(ctx context.Context, src []byte, desc *thrift.TypeDescriptor, out *[]byte, resp http.ResponseSetter) (err error) {
//NOTICE: output buffer must be larger than src buffer
rt.GuardSlice(out, len(src)*_GUARD_SLICE_FACTOR)

var p = thrift.BinaryProtocol{
Buf: src,
}
Expand Down Expand Up @@ -395,7 +395,7 @@ func (self *BinaryConv) handleUnsets(b *thrift.RequiresBitmap, desc *thrift.Stru
var ok = false
if hms := field.HTTPMappings(); self.opts.EnableHttpMapping && hms != nil {
// make a default thrift value
p := thrift.BinaryProtocol{Buf: make([]byte, 0, conv.DefaulHttpValueBufferSizeForJSON)};
p := thrift.BinaryProtocol{Buf: make([]byte, 0, conv.DefaulHttpValueBufferSizeForJSON)}
if err := p.WriteDefaultOrEmpty(field); err != nil {
return wrapError(meta.ErrWrite, fmt.Sprintf("encoding field '%s' default value failed", field.Name()), err)
}
Expand Down Expand Up @@ -517,10 +517,10 @@ func (self *BinaryConv) writeHttpValue(ctx context.Context, resp http.ResponseSe
var thriftVal []byte
var jsonVal []byte
var textVal []byte

for _, hm := range field.HTTPMappings() {
var val []byte
enc := hm.Encoding();
enc := hm.Encoding()

if enc == meta.EncodingThriftBinary {
// raw encoding, check if raw value is set
Expand All @@ -543,10 +543,10 @@ func (self *BinaryConv) writeHttpValue(ctx context.Context, resp http.ResponseSe
return false, unwrapError(fmt.Sprintf("reading thrift value of '%s' failed, thrift pos:%d", field.Name(), p.Read), err)
}
val = tmp
textVal = val
textVal = val
} else {
val = textVal
}
}
} else if self.opts.UseKitexHttpEncoding {
// kitex http encoding fallback
if textVal == nil {
Expand All @@ -558,7 +558,7 @@ func (self *BinaryConv) writeHttpValue(ctx context.Context, resp http.ResponseSe
val = textVal
} else {
val = textVal
}
}
} else if enc == meta.EncodingJSON {
// for nested type, convert it to a new JSON string
if jsonVal == nil {
Expand All @@ -572,7 +572,7 @@ func (self *BinaryConv) writeHttpValue(ctx context.Context, resp http.ResponseSe
} else {
val = jsonVal
}

} else {
return false, wrapError(meta.ErrConvert, fmt.Sprintf("unsuported http-value encoding %v of field '%s'", enc, field.Name()), nil)
}
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion testdata/idl/example3.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct ExampleReq {
}

struct ExampleResp {
1: string Msg (api.body = "msg"),
1: string Msg (api.key = "msg"),
2: optional double Cookie (api.cookie = "cookie"),
3: required i32 Status (api.http_code = "status"),
4: optional bool Header (api.header = "heeader"),
Expand Down
2 changes: 1 addition & 1 deletion testdata/idl/example4.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct GetFavoriteReq {
255: required Favorite Base,
}
struct GetFavoriteResp {
1: required Favorite Favorite (api.body="favorite"),
1: required Favorite Favorite (api.key="favorite"),
255: required base.BaseResp BaseResp,
}

Expand Down
12 changes: 6 additions & 6 deletions thrift/annotation/anno_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ func (m apiBodyMapper) Map(ctx context.Context, anns []parser.Annotation, desc i
if len(ann.Values) != 1 {
return nil, nil, errors.New("api.body must have a value")
}
ret = append(ret, parser.Annotation{
Key: APIKeyName,
Values: []string{ann.Values[0]},
})
isRoot := ctx.Value(thrift.CtxKeyIsBodyRoot)
// special fast-path: if the field is at body root, we don't need to add api.body
if isRoot != nil && isRoot.(bool) {
// special fast-path: if the field is at body root, we don't need to add api.body to call GetMapBody()
if opts.ApiBodyFastPath && isRoot != nil && isRoot.(bool) {
ret = append(ret, parser.Annotation{
Key: APIKeyName,
Values: []string{ann.Values[0]},
})
continue
} else {
ret = append(ret, parser.Annotation{
Expand Down
18 changes: 16 additions & 2 deletions thrift/annotation/http_mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ func GetDescFromContent(content string, method string) (*thrift.FunctionDescript
return p.Functions()[method], nil
}

func GetDescFromContentWithOptions(content string, method string, opts thrift.Options) (*thrift.FunctionDescriptor, error) {
path := "a/b/main.thrift"
includes := map[string]string{
path: content,
}
p, err := opts.NewDescritorFromContent(context.Background(), path, content, includes, true)
if err != nil {
return nil, err
}
return p.Functions()[method], nil
}

func TestAPIQuery(t *testing.T) {
fn, err := GetDescFromContent(`
namespace go kitex.test.server
Expand Down Expand Up @@ -81,7 +93,7 @@ func TestAPIRawUri(t *testing.T) {
}

func TestAPIBody(t *testing.T) {
fn, err := GetDescFromContent(`
fn, err := GetDescFromContentWithOptions(`
namespace go kitex.test.server
struct Base {
1: required Base2 f1
Expand All @@ -95,7 +107,9 @@ func TestAPIBody(t *testing.T) {
service InboxService {
string ExampleMethod(1: Base req)
}
`, "ExampleMethod")
`, "ExampleMethod", thrift.Options{
ApiBodyFastPath: true,
})
if err != nil {
t.Fatal(err)
}
Expand Down
3 changes: 3 additions & 0 deletions thrift/idl.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ type Options struct {
// `// path := /a/b/c.thrift` will got ["/a/b/c.thrift"]
// NOTICE: at present, only StructDescriptor.Annotations() can get this
PutThriftFilenameToAnnotation bool

// ApiBodyFastPath indicates `api.body` will change alias-name of root field, which can avoid search http-body on them
ApiBodyFastPath bool
}

// NewDefaultOptions creates a default Options.
Expand Down

0 comments on commit d8a885f

Please sign in to comment.