From daee668b757e6170455c2b8d131c41ff21b9f41e Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Tue, 23 Apr 2019 11:06:12 -0400 Subject: [PATCH] client: Switch to case sensitive unmarshalling to be compatible with ugorji Using lessons learned from k8s changes: https://github.com/kubernetes/kubernetes/pull/65034 Change-Id: Ia17a8f94ae6ed00c5af2595c2b48d3c9a0344427 --- client/json.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ client/keys.go | 9 +++---- 2 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 client/json.go diff --git a/client/json.go b/client/json.go new file mode 100644 index 000000000..97cdbcd7c --- /dev/null +++ b/client/json.go @@ -0,0 +1,72 @@ +// Copyright 2019 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package client + +import ( + "github.com/json-iterator/go" + "github.com/modern-go/reflect2" + "strconv" + "unsafe" +) + +type customNumberExtension struct { + jsoniter.DummyExtension +} + +func (cne *customNumberExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder { + if typ.String() == "interface {}" { + return customNumberDecoder{} + } + return nil +} + +type customNumberDecoder struct { +} + +func (customNumberDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + switch iter.WhatIsNext() { + case jsoniter.NumberValue: + var number jsoniter.Number + iter.ReadVal(&number) + i64, err := strconv.ParseInt(string(number), 10, 64) + if err == nil { + *(*interface{})(ptr) = i64 + return + } + f64, err := strconv.ParseFloat(string(number), 64) + if err == nil { + *(*interface{})(ptr) = f64 + return + } + iter.ReportError("DecodeNumber", err.Error()) + default: + *(*interface{})(ptr) = iter.Read() + } +} + +// caseSensitiveJsonIterator returns a jsoniterator API that's configured to be +// case-sensitive when unmarshalling, and otherwise compatible with +// the encoding/json standard library. +func caseSensitiveJsonIterator() jsoniter.API { + config := jsoniter.Config{ + EscapeHTML: true, + SortMapKeys: true, + ValidateJsonRawMessage: true, + CaseSensitive: true, + }.Froze() + // Force jsoniter to decode number to interface{} via int64/float64, if possible. + config.RegisterExtension(&customNumberExtension{}) + return config +} diff --git a/client/keys.go b/client/keys.go index 6c700ac87..d0350b8d2 100644 --- a/client/keys.go +++ b/client/keys.go @@ -19,14 +19,12 @@ import ( "encoding/json" "errors" "fmt" + "go.etcd.io/etcd/pkg/pathutil" "net/http" "net/url" "strconv" "strings" "time" - - jsoniter "github.com/json-iterator/go" - "go.etcd.io/etcd/pkg/pathutil" ) const ( @@ -654,10 +652,11 @@ func unmarshalHTTPResponse(code int, header http.Header, body []byte) (res *Resp return res, err } +var jsonIterator = caseSensitiveJsonIterator() + func unmarshalSuccessfulKeysResponse(header http.Header, body []byte) (*Response, error) { var res Response - var json = jsoniter.ConfigCompatibleWithStandardLibrary - err := json.Unmarshal(body, &res) + err := jsonIterator.Unmarshal(body, &res) if err != nil { return nil, ErrInvalidJSON }