From 30da2df47564a8982a0093360b9841e0bcec057d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=9A=E5=90=83=E7=82=B9=E8=8B=B9=E6=9E=9C?= <73388495+eatmoreapple@users.noreply.github.com> Date: Fri, 13 Jan 2023 20:40:55 +0800 Subject: [PATCH] =?UTF-8?q?[fix]:=20=E5=BD=93=E7=94=A8=E6=88=B7=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89http=20client=E7=9A=84=E6=97=B6=E5=80=99=20co?= =?UTF-8?q?okie=20=E6=97=A0=E6=B3=95=E8=A2=AB=E5=8F=8D=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8C=96=E7=9A=84=E9=97=AE=E9=A2=98=20(#205)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.go | 6 ++--- client.go | 67 ++++++++++++++++++++++++++++++++++++------------------- jar.go | 12 +++++----- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/bot.go b/bot.go index 2e64bfd..428a167 100644 --- a/bot.go +++ b/bot.go @@ -285,10 +285,10 @@ func (b *Bot) DumpHotReloadStorage() error { // DumpTo 将热登录需要的数据写入到指定的 io.Writer 中 // 注: 写之前最好先清空之前的数据 func (b *Bot) DumpTo(writer io.Writer) error { - cookies := b.Caller.Client.GetCookieJar() + jar := b.Caller.Client.Jar() item := HotReloadStorageItem{ BaseRequest: b.Storage.Request, - Jar: cookies, + Jar: fromCookieJar(jar), LoginInfo: b.Storage.LoginInfo, WechatDomain: b.Caller.Client.Domain, UUID: b.uuid, @@ -320,7 +320,7 @@ func (b *Bot) reload() error { if err != nil { return err } - b.Caller.Client.Jar = item.Jar.AsCookieJar() + b.Caller.Client.SetCookieJar(item.Jar) b.Storage.LoginInfo = item.LoginInfo b.Storage.Request = item.BaseRequest b.Caller.Client.Domain = item.WechatDomain diff --git a/client.go b/client.go index 5361932..1ac0bdf 100644 --- a/client.go +++ b/client.go @@ -15,7 +15,6 @@ import ( "path/filepath" "strconv" "strings" - "sync" "time" ) @@ -33,30 +32,44 @@ func (u UserAgentHook) BeforeRequest(req *http.Request) { req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36") } -func (u UserAgentHook) AfterRequest(response *http.Response, err error) {} +func (u UserAgentHook) AfterRequest(_ *http.Response, _ error) {} // Client http请求客户端 // 客户端需要维持Session会话 -// 并且客户端不允许跳转 type Client struct { + // 设置一些client的请求行为 + // see normalMode desktopMode + mode Mode + + // client http客户端 + client *http.Client + + // Domain 微信服务器请求域名 + // 这个参数会在登录成功后被赋值 + // 之后所有的请求都会使用这个域名 + // 在登录热登录和扫码登录时会被重新赋值 + Domain WechatDomain + + // HttpHooks 请求上下文钩子 HttpHooks HttpHooks - *http.Client - Domain WechatDomain - mode Mode - mu sync.Mutex + + // MaxRetryTimes 最大重试次数 MaxRetryTimes int } func NewClient() *Client { - timeout := 30 * time.Second - return &Client{ - Client: &http.Client{ - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - Jar: newCookieJar(), - Timeout: timeout, - }} + httpClient := &http.Client{ + // 设置客户端不自动跳转 + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + // 设置30秒超时 + // 因为微信同步消息时是一个时间长达25秒的长轮训 + Timeout: 30 * time.Second, + } + client := &Client{client: httpClient} + client.SetCookieJar(NewJar()) + return client } // DefaultClient 自动存储cookie @@ -81,7 +94,7 @@ func (c *Client) do(req *http.Request) (*http.Response, error) { err error ) for i := 0; i < c.MaxRetryTimes; i++ { - resp, err = c.Client.Do(req) + resp, err = c.client.Do(req) if err == nil { break } @@ -101,9 +114,17 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) { return c.do(req) } -// GetCookieJar 获取当前client的所有的有效的client -func (c *Client) GetCookieJar() *Jar { - return fromCookieJar(c.Client.Jar) +// Jar 返回当前client的 http.CookieJar +// this http.CookieJar must be *Jar type +func (c *Client) Jar() http.CookieJar { + return c.client.Jar +} + +// SetCookieJar 设置cookieJar +// 这里限制了cookieJar必须是Jar类型 +// 否则进行cookie序列化的时候因为字段的私有性无法进行所有字段的导出 +func (c *Client) SetCookieJar(jar *Jar) { + c.client.Jar = jar.AsCookieJar() } // GetLoginUUID 获取登录的uuid @@ -114,7 +135,7 @@ func (c *Client) GetLoginUUID() (*http.Response, error) { // GetLoginQrcode 获取登录的二维吗 func (c *Client) GetLoginQrcode(uuid string) (*http.Response, error) { path := qrcode + uuid - return c.Get(path) + return c.client.Get(path) } // CheckLogin 检查是否登录 @@ -336,7 +357,7 @@ func (c *Client) WebWxUploadMediaByChunk(file *os.File, request *BaseRequest, in path.RawQuery = params.Encode() - cookies := c.Jar.Cookies(path) + cookies := c.Jar().Cookies(path) webWxDataTicket := getWebWxDataTicket(cookies) uploadMediaRequest := map[string]interface{}{ @@ -576,7 +597,7 @@ func (c *Client) WebWxGetMedia(msg *Message, info *LoginInfo) (*http.Response, e params.Add("encryfilename", msg.EncryFileName) params.Add("fromuser", strconv.FormatInt(info.WxUin, 10)) params.Add("pass_ticket", info.PassTicket) - params.Add("webwx_data_ticket", getWebWxDataTicket(c.Jar.Cookies(path))) + params.Add("webwx_data_ticket", getWebWxDataTicket(c.Jar().Cookies(path))) path.RawQuery = params.Encode() req, _ := http.NewRequest(http.MethodGet, path.String(), nil) req.Header.Add("Referer", c.Domain.BaseHost()+"/") diff --git a/jar.go b/jar.go index 38c15d0..fd32cfb 100644 --- a/jar.go +++ b/jar.go @@ -16,7 +16,7 @@ type Jar struct { mu sync.Mutex // Entries is a set of entries, keyed by their eTLD+1 and subkeyed by - // their name/domain/path. + // their name/Domain/path. Entries map[string]map[string]entry // nextSeqNum is the next sequence number assigned to a new cookie @@ -29,15 +29,15 @@ func (j *Jar) AsCookieJar() http.CookieJar { return (*cookiejar.Jar)(unsafe.Pointer(j)) } -func newCookieJar() http.CookieJar { - jar, _ := cookiejar.New(nil) - return jar -} - func fromCookieJar(jar http.CookieJar) *Jar { return (*Jar)(unsafe.Pointer(jar.(*cookiejar.Jar))) } +func NewJar() *Jar { + jar, _ := cookiejar.New(nil) + return fromCookieJar(jar) +} + type entry struct { Name string Value string