11package megaplan
22
33import (
4- "bytes"
5- "compress/gzip"
6- "context"
74 "crypto/tls"
8- "errors"
9- "io"
105 "net/http"
6+ "net/http/cookiejar"
7+ "net/url"
118 "runtime"
129 "strconv"
10+ "strings"
1311 "time"
1412)
1513
1614// DefaultClient - клиент по умаолчанию для API.
1715var (
18- cpus = runtime .NumCPU ()
16+ cpus = runtime .NumCPU ()
17+ DefaultTransport = & http.Transport {
18+ Proxy : http .ProxyFromEnvironment ,
19+ MaxIdleConns : cpus ,
20+ MaxConnsPerHost : cpus ,
21+ MaxIdleConnsPerHost : cpus ,
22+ }
1923 DefaultClient = & http.Client {
20- Transport : & http.Transport {
21- Proxy : http .ProxyFromEnvironment ,
22- MaxIdleConns : cpus ,
23- MaxConnsPerHost : cpus ,
24- MaxIdleConnsPerHost : cpus ,
25- },
26- Timeout : time .Minute ,
24+ Transport : DefaultTransport ,
25+ Timeout : time .Minute ,
2726 }
2827 // DefaultHeaders - заголовок по умолчанию - версия go. Используется при инициализации клиента в NewClient.
2928 DefaultHeaders = http.Header {"User-Agent" : {runtime .Version ()}}
3029)
3130
3231// NewClient - обертка над http.Client для удобной работы с API v3
3332func NewClient (domain , token string , opts ... ClientOption ) (c * ClientV3 ) {
33+ if strings .HasPrefix ("http" , domain ) {
34+ _URL , _ := url .Parse (domain )
35+ domain = _URL .Host
36+ }
3437 c = & ClientV3 {
3538 client : DefaultClient ,
3639 domain : domain ,
@@ -48,91 +51,7 @@ type ClientV3 struct {
4851 client * http.Client
4952}
5053
51- // Do - http.Do + установка обязательных заголовков + декомпрессия ответа, если ответ сжат
52- func (c * ClientV3 ) Do (req * http.Request ) (* http.Response , error ) {
53- const ct = "Content-Type"
54- for h := range c .defaultHeaders {
55- req .Header .Set (h , c .defaultHeaders .Get (h ))
56- }
57- if _ , ok := req .Header [ct ]; ! ok {
58- req .Header .Set (ct , "application/json" )
59- }
60- res , err := c .client .Do (req )
61- if err != nil {
62- return nil , err
63- }
64- if err := unzipResponse (res ); err != nil {
65- return nil , err
66- }
67- return res , nil
68- }
69-
70- // DoRequestAPI - т.к. в v3 параметры запроса для GET (json маршализируется и будет иметь вид: "*?{params}=")
71- func (c ClientV3 ) DoRequestAPI (method string , endpoint string , search QueryParams , body io.Reader ) (* http.Response , error ) {
72- var args string // параметры строки запроса
73- if search != nil {
74- args = search .QueryEscape ()
75- }
76- request , err := http .NewRequest (method , c .domain , body )
77- if err != nil {
78- return nil , err
79- }
80- request .URL .Path = endpoint
81- request .URL .RawQuery = args
82- return c .Do (request )
83- }
84-
85- // DoRequestAPI - т.к. в v3 параметры запроса для GET (json маршализируется и будет иметь вид: "*?{params}=")
86- func (c ClientV3 ) DoCtxRequestAPI (ctx context.Context , method string , endpoint string , search QueryParams , body io.Reader ) (* http.Response , error ) {
87- var args string // параметры строки запроса
88- if search != nil {
89- args = search .QueryEscape ()
90- }
91- request , err := http .NewRequestWithContext (ctx , method , c .domain , body )
92- if err != nil {
93- return nil , err
94- }
95- request .URL .Path = endpoint
96- request .URL .RawQuery = args
97- return c .Do (request )
98- }
99-
100- // ErrUnknownCompressionMethod - неизвестное значение в заголовке "Content-Encoding"
101- // не является фатальной ошибкой, должна возвращаться вместе с http.Response.Body,
102- // чтобы пользователь мог реализовать свой метод обработки сжатого сообщения
103- var ErrUnknownCompressionMethod = errors .New ("unknown compression method" )
104-
105- // unzipResponse - распаковка сжатого ответа
106- func unzipResponse (response * http.Response ) (err error ) {
107- if response .Uncompressed {
108- return nil
109- }
110- switch response .Header .Get ("Content-Encoding" ) {
111- case "" :
112- return nil
113- case "gzip" :
114- gz , err := gzip .NewReader (response .Body )
115- if err != nil {
116- return err
117- }
118- b , err := io .ReadAll (gz )
119- if err != nil {
120- return err
121- }
122- if err := response .Body .Close (); err != nil {
123- return err
124- }
125- if err := gz .Close (); err != nil {
126- return err
127- }
128- response .Body = io .NopCloser (bytes .NewReader (b ))
129- response .Header .Del ("Content-Encoding" )
130- response .Uncompressed = true
131- return nil
132- default :
133- return ErrUnknownCompressionMethod
134- }
135- }
54+ func (c * ClientV3 ) Close () { c .client .CloseIdleConnections () }
13655
13756// SetOptions - применить опции
13857func (c * ClientV3 ) SetOptions (opts ... ClientOption ) {
@@ -149,13 +68,13 @@ type ClientOption func(*ClientV3)
14968
15069// OptionInsecureSkipVerify - переключение флага bool в http.Client.Transport.TLSClientConfig.InsecureSkipVerify - отключать или нет проверку сертификтов
15170// Если домен использует самоподписанные сертифика, то удобно включать на время отладки и разработки
152- func OptionInsecureSkipVerify (b bool ) ClientOption {
71+ func OptionInsecureSkipVerify (yes bool ) ClientOption {
15372 return func (c * ClientV3 ) {
15473 if c .client .Transport != nil {
15574 if (c .client .Transport .(* http.Transport )).TLSClientConfig == nil {
156- (c .client .Transport .(* http.Transport )).TLSClientConfig = & tls.Config {InsecureSkipVerify : b }
75+ (c .client .Transport .(* http.Transport )).TLSClientConfig = & tls.Config {InsecureSkipVerify : yes }
15776 } else {
158- (c .client .Transport .(* http.Transport )).TLSClientConfig .InsecureSkipVerify = b
77+ (c .client .Transport .(* http.Transport )).TLSClientConfig .InsecureSkipVerify = yes
15978 }
16079 }
16180 }
@@ -168,10 +87,10 @@ func OptionsSetHTTPTransport(tr http.RoundTripper) ClientOption {
16887
16988// OptionEnableAcceptEncodingGzip - доабвить заголов Accept-Encoding=gzip к запросу
17089// т.е. объекм трафика на хуках может быть большим, то удобно запрашивать сжатый ответ
171- func OptionEnableAcceptEncodingGzip (b bool ) ClientOption {
90+ func OptionEnableAcceptEncodingGzip (yes bool ) ClientOption {
17291 const header = "Accept-Encoding"
17392 return func (c * ClientV3 ) {
174- if b {
93+ if yes {
17594 c .defaultHeaders .Set (header , "gzip" )
17695 } else {
17796 c .defaultHeaders .Del (header )
@@ -200,3 +119,34 @@ func OptionSetXUserID(userID int) ClientOption {
200119 }
201120 }
202121}
122+
123+ func OptionDisableCookie (yes bool ) ClientOption {
124+ return func (c * ClientV3 ) {
125+ if yes {
126+ c .client .Jar = nil
127+ } else {
128+ jar , _ := cookiejar .New (nil )
129+ c .client .Jar = jar
130+ }
131+ }
132+ }
133+
134+ func OptionForceAttemptHTTP2 (yes bool ) ClientOption {
135+ return func (c * ClientV3 ) {
136+ if c .client .Transport != nil {
137+ (c .client .Transport .(* http.Transport )).ForceAttemptHTTP2 = yes
138+ } else {
139+ c .client .Transport = & http.Transport {ForceAttemptHTTP2 : yes }
140+ }
141+ }
142+ }
143+
144+ func OptionDisablekeepAlive (yes bool ) ClientOption {
145+ return func (c * ClientV3 ) {
146+ if c .client .Transport != nil {
147+ (c .client .Transport .(* http.Transport )).DisableKeepAlives = yes
148+ } else {
149+ c .client .Transport = & http.Transport {DisableKeepAlives : yes }
150+ }
151+ }
152+ }
0 commit comments