@@ -4,11 +4,8 @@ import (
44 "bytes"
55 "compress/gzip"
66 "context"
7- "crypto/rand"
8- "crypto/tls"
97 "errors"
108 "io"
11- "net"
129 "net/http"
1310 "runtime"
1411 "strconv"
2017 DefaultClient = & http.Client {
2118 Transport : & http.Transport {
2219 Proxy : http .ProxyFromEnvironment ,
23- TLSClientConfig : & tls.Config {
24- MinVersion : tls .VersionTLS12 ,
25- MaxVersion : tls .VersionTLS13 ,
26- Rand : rand .Reader ,
27- Time : time .Now ,
28- },
29- DialContext : (& net.Dialer {
30- Timeout : time .Minute ,
31- KeepAlive : time .Minute ,
32- }).DialContext ,
33- TLSHandshakeTimeout : 30 * time .Second ,
34- MaxIdleConns : 0 ,
35- IdleConnTimeout : time .Minute ,
36- ForceAttemptHTTP2 : true ,
37- ReadBufferSize : 256 << 10 ,
38- WriteBufferSize : 256 << 10 ,
3920 },
4021 Timeout : time .Minute ,
4122 }
4526
4627// NewClient - обертка над http.Client для удобной работы с API v3
4728func NewClient (domain , token string , opts ... ClientOption ) (c * ClientV3 ) {
48- // обмен трафиком идёт очень активный, поэтому целесообразно использовать http2 + KeepAlive
49- // бэкэнд мегаплана корректно умеет работать с http и KeepAlive, что экономит время и ресурсы на соединение
5029 c = & ClientV3 {
5130 client : DefaultClient ,
5231 domain : domain ,
@@ -64,7 +43,7 @@ type ClientV3 struct {
6443 defaultHeaders http.Header
6544}
6645
67- // Do - http.Do + установка обязательных заголовков
46+ // Do - http.Do + установка обязательных заголовков + декомпрессия ответа, если ответ сжат
6847func (c * ClientV3 ) Do (req * http.Request ) (* http.Response , error ) {
6948 const ct = "Content-Type"
7049 for h := range c .defaultHeaders {
@@ -73,11 +52,18 @@ func (c *ClientV3) Do(req *http.Request) (*http.Response, error) {
7352 if req .Header .Get (ct ) == "" {
7453 req .Header .Set (ct , "application/json" )
7554 }
76- return c .client .Do (req )
55+ res , err := c .client .Do (req )
56+ if err != nil {
57+ return nil , err
58+ }
59+ if err := unzipResponse (res ); err != nil {
60+ return nil , err
61+ }
62+ return res , nil
7763}
7864
7965// DoRequestAPI - т.к. в v3 параметры запроса для GET (json маршализируется и будет иметь вид: "*?{params}=")
80- func (c ClientV3 ) DoRequestAPI (method string , endpoint string , search QueryParams , body io.Reader ) (rc io. ReadCloser , err error ) {
66+ func (c ClientV3 ) DoRequestAPI (method string , endpoint string , search QueryParams , body io.Reader ) (* http. Response , error ) {
8167 var args string // параметры строки запроса
8268 if search != nil {
8369 args = search .QueryEscape ()
@@ -88,15 +74,11 @@ func (c ClientV3) DoRequestAPI(method string, endpoint string, search QueryParam
8874 }
8975 request .URL .Path = endpoint
9076 request .URL .RawQuery = args
91- response , err := c .Do (request )
92- if err != nil {
93- return nil , err
94- }
95- return unzipResponse (response )
77+ return c .Do (request )
9678}
9779
9880// DoRequestAPI - т.к. в v3 параметры запроса для GET (json маршализируется и будет иметь вид: "*?{params}=")
99- func (c ClientV3 ) DoCtxRequestAPI (ctx context.Context , method string , endpoint string , search QueryParams , body io.Reader ) (rc io. ReadCloser , err error ) {
81+ func (c ClientV3 ) DoCtxRequestAPI (ctx context.Context , method string , endpoint string , search QueryParams , body io.Reader ) (* http. Response , error ) {
10082 var args string // параметры строки запроса
10183 if search != nil {
10284 args = search .QueryEscape ()
@@ -107,11 +89,7 @@ func (c ClientV3) DoCtxRequestAPI(ctx context.Context, method string, endpoint s
10789 }
10890 request .URL .Path = endpoint
10991 request .URL .RawQuery = args
110- response , err := c .Do (request )
111- if err != nil {
112- return nil , err
113- }
114- return unzipResponse (response )
92+ return c .Do (request )
11593}
11694
11795// ErrUnknownCompressionMethod - неизвестное значение в заголовке "Content-Encoding"
@@ -120,30 +98,35 @@ func (c ClientV3) DoCtxRequestAPI(ctx context.Context, method string, endpoint s
12098var ErrUnknownCompressionMethod = errors .New ("unknown compression method" )
12199
122100// unzipResponse - распаковка сжатого ответа
123- func unzipResponse (response * http.Response ) (rc io. ReadCloser , err error ) {
101+ func unzipResponse (response * http.Response ) (err error ) {
124102 if response .Uncompressed {
125- return response . Body , nil
103+ return nil
126104 }
127- body , err := io .ReadAll (response .Body )
128- if err != nil {
129- return nil , err
130- }
131- if err := response .Body .Close (); err != nil {
132- return nil , err
133- }
134- var r = bytes .NewReader (body )
135105 switch response .Header .Get ("Content-Encoding" ) {
136106 case "" :
137- // кейс, когда запрашивалось сжатие, но сервер не поддерживает запрашиваемый вид сжатия
138- // response.Uncompressed будет в значении false, но в тело ответа будет не сжато и заголовок "Content-Encoding" отсутствует
139- rc = io .NopCloser (r )
107+ return nil
140108 case "gzip" :
141- rc , err = gzip .NewReader (r )
109+ gz , err := gzip .NewReader (response .Body )
110+ if err != nil {
111+ return err
112+ }
113+ b , err := io .ReadAll (gz )
114+ if err != nil {
115+ return err
116+ }
117+ if err := response .Body .Close (); err != nil {
118+ return err
119+ }
120+ if err := gz .Close (); err != nil {
121+ return err
122+ }
123+ response .Body = io .NopCloser (bytes .NewReader (b ))
124+ response .Header .Del ("Content-Encoding" )
125+ response .Uncompressed = true
126+ return nil
142127 default :
143- rc = io .NopCloser (r )
144- err = ErrUnknownCompressionMethod
128+ return ErrUnknownCompressionMethod
145129 }
146- return
147130}
148131
149132// SetOptions - применить опции
0 commit comments