From 38cc3eeb8b9b81beb69f978e732c6065f80c9bfc Mon Sep 17 00:00:00 2001 From: Suvorov Daniil Date: Mon, 31 Jan 2022 16:02:12 +0300 Subject: [PATCH 1/2] docs: up version --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index ff749d80..3e3c12a1 100644 --- a/doc.go +++ b/doc.go @@ -7,6 +7,6 @@ package vksdk // Module constants. const ( - Version = "2.13.0" + Version = "2.14.0" API = "5.131" ) From 7836bc3eaa3c9d0321dbc7ec91bc06fdd591d64b Mon Sep 17 00:00:00 2001 From: Suvorov Daniil Date: Mon, 31 Jan 2022 16:19:59 +0300 Subject: [PATCH 2/2] feat: add zstd dict support --- api/README.md | 16 ++++++++++--- api/account.go | 15 ++++++++++++ api/account_test.go | 10 ++++++++ api/api.go | 58 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/api/README.md b/api/README.md index 5d2d0c76..ed28f8a5 100644 --- a/api/README.md +++ b/api/README.md @@ -98,9 +98,7 @@ VK API способно возвращать ответ в виде [MessagePack СЛОМАННУЮ КОДИРОВКУ. Для сжатия, вместо классического gzip, можно использовать -[zstd](https://github.com/facebook/zstd). Сейчас vksdk поддерживает zstd без -словаря. Если кто знает как получать словарь, -[отпишитесь сюда](https://github.com/SevereCloud/vksdk/issues/180). +[zstd](https://github.com/facebook/zstd). ```go vk := api.NewVK(os.Getenv("USER_TOKEN")) @@ -128,6 +126,18 @@ if err != nil { log.Println("msgpack:", len(r)) // msgpack: 650775 ``` +Чтобы загрузить и использовать zstd словарь, воспользуйтесь +методом `vk.UpdateZstdDict()` + +```go +vk.EnableZstd() + +err := vk.UpdateZstdDict() +if err != nil { + log.Fatal(err) +} +``` + ### Запрос любого метода Пример запроса [users.get](https://vk.com/dev/users.get) diff --git a/api/account.go b/api/account.go index 7e38ce2b..a3b90825 100644 --- a/api/account.go +++ b/api/account.go @@ -108,6 +108,21 @@ func (vk *VK) AccountGetPushSettings(params Params) (response AccountGetPushSett return } +// AccountGetZSTDDictResponse struct. +type AccountGetZSTDDictResponse struct { + Link string `json:"link"` + Version string `json:"version"` + Hash string `json:"hash"` +} + +// AccountGetZSTDDict method. +// +// https://vk.com/dev/account.getZSTDDict +func (vk *VK) AccountGetZSTDDict(params Params) (response AccountGetZSTDDictResponse, err error) { + err = vk.RequestUnmarshal("account.getZSTDDict", &response, params) + return +} + // AccountRegisterDevice subscribes an iOS/Android/Windows/Mac based device to receive push notifications. // // https://vk.com/dev/account.registerDevice diff --git a/api/account_test.go b/api/account_test.go index ff135339..8371bd9e 100644 --- a/api/account_test.go +++ b/api/account_test.go @@ -124,6 +124,16 @@ func TestVK_AccountGetPushSettings(t *testing.T) { noError(t, err) } +func TestVK_AccountGetZSTDDict(t *testing.T) { + t.Parallel() + + needUserToken(t) + + vkUser.EnableZstd() + err := vkUser.UpdateZstdDict() + noError(t, err) +} + // func TestVK_AccountRegisterDevice(t *testing.T) { // TODO: Add test cases // } diff --git a/api/api.go b/api/api.go index 760bc7e0..c4bf9cd3 100644 --- a/api/api.go +++ b/api/api.go @@ -98,6 +98,9 @@ type VK struct { msgpack bool zstd bool + zstdVersion string + zstdDict zstd.DOption + mux sync.Mutex lastTime time.Time rps int @@ -260,6 +263,10 @@ func (vk *VK) DefaultHandler(method string, sliceParams ...Params) (Response, er req.Header.Set("Accept-Encoding", acceptEncoding) + if vk.zstdVersion != "" { + req.Header.Set("x-zstd-dict-version", vk.zstdVersion) + } + var reader io.Reader resp, err := vk.Client.Do(req) @@ -269,7 +276,7 @@ func (vk *VK) DefaultHandler(method string, sliceParams ...Params) (Response, er switch resp.Header.Get("Content-Encoding") { case "zstd": - reader, _ = zstd.NewReader(resp.Body) + reader, _ = zstd.NewReader(resp.Body, vk.zstdDict) case "gzip": reader, _ = gzip.NewReader(resp.Body) default: @@ -370,6 +377,55 @@ func (vk *VK) EnableZstd() { vk.zstd = true } +// SetZstdDict set vk zstd dict. +func (vk *VK) SetZstdDict(data []byte) error { + // zstd dictionary start with Magic_Number: 4 bytes ID, value 0xEC30A437, + // little-endian format. + // + // https://datatracker.ietf.org/doc/html/rfc8878#section-5 + indexMagicNumber := bytes.Index(data, []byte{0x37, 0xa4, 0x30, 0xec}) + if indexMagicNumber == -1 { + return fmt.Errorf("api: zstd dictionary not found. %w", zstd.ErrMagicMismatch) + } + + vk.zstdVersion = string(data[:indexMagicNumber]) + vk.zstdDict = zstd.WithDecoderDicts(data[indexMagicNumber:]) + + return nil +} + +// LoadZstdDict return zstd dict. +func (vk *VK) LoadZstdDict() (data []byte, err error) { + r, err := vk.AccountGetZSTDDict(nil) + if err != nil { + return + } + + resp, err := vk.Client.Get(r.Link) + if err != nil { + return + } + + data, err = io.ReadAll(resp.Body) + if err != nil { + return + } + + err = resp.Body.Close() + + return +} + +// UpdateZstdDict update vk zstd dict. +func (vk *VK) UpdateZstdDict() error { + data, err := vk.LoadZstdDict() + if err != nil { + return err + } + + return vk.SetZstdDict(data) +} + func fmtReflectValue(value reflect.Value, depth int) string { switch f := value; value.Kind() { case reflect.Invalid: