Beispiel #1
0
func (clt *Client) updateToken(tk *Token, url string) (err error) {
	api.DebugPrintGetRequest(url)
	httpResp, err := clt.httpClient().Get(url)
	if err != nil {
		return
	}
	defer httpResp.Body.Close()

	if httpResp.StatusCode != http.StatusOK {
		return fmt.Errorf("http.Status: %s", httpResp.Status)
	}

	var result struct {
		Error
		Token
	}
	if err = api.DecodeJSONHttpResponse(httpResp.Body, &result); err != nil {
		return
	}
	if result.ErrCode != ErrCodeOK {
		return &result.Error
	}

	// 由于网络的延时 和 分布式服务器之间的时间可能不是绝对同步, access_token 过期时间留了一个缓冲区
	switch {
	case result.ExpiresIn > 31556952: // 60*60*24*365.2425
		return errors.New("expires_in too large: " + strconv.FormatInt(result.ExpiresIn, 10))
	case result.ExpiresIn > 60*60:
		result.ExpiresIn -= 60 * 20
	case result.ExpiresIn > 60*30:
		result.ExpiresIn -= 60 * 10
	case result.ExpiresIn > 60*15:
		result.ExpiresIn -= 60 * 5
	case result.ExpiresIn > 60*5:
		result.ExpiresIn -= 60
	case result.ExpiresIn > 60:
		result.ExpiresIn -= 20
	default:
		return errors.New("expires_in too small: " + strconv.FormatInt(result.ExpiresIn, 10))
	}

	tk.AccessToken = result.AccessToken
	tk.CreatedAt = time.Now().Unix()
	tk.ExpiresIn = result.ExpiresIn
	if result.RefreshToken != "" {
		tk.RefreshToken = result.RefreshToken
	}
	if result.OpenId != "" {
		tk.OpenId = result.OpenId
	}
	if result.UnionId != "" {
		tk.UnionId = result.UnionId
	}
	if result.Scope != "" {
		tk.Scope = result.Scope
	}
	return
}
Beispiel #2
0
func httpGetJSON(clt *http.Client, url string, response interface{}) error {
	api.DebugPrintGetRequest(url)
	httpResp, err := clt.Get(url)
	if err != nil {
		return err
	}
	defer httpResp.Body.Close()

	if httpResp.StatusCode != http.StatusOK {
		return fmt.Errorf("http.Status: %s", httpResp.Status)
	}
	return api.DecodeJSONHttpResponse(httpResp.Body, response)
}
Beispiel #3
0
func httpPostJSON(clt *http.Client, url string, body []byte, response interface{}) error {
	api.DebugPrintPostJSONRequest(url, body)
	httpResp, err := clt.Post(url, "application/json; charset=utf-8", bytes.NewReader(body))
	if err != nil {
		return err
	}
	defer httpResp.Body.Close()

	if httpResp.StatusCode != http.StatusOK {
		return fmt.Errorf("http.Status: %s", httpResp.Status)
	}
	return api.DecodeJSONHttpResponse(httpResp.Body, response)
}
Beispiel #4
0
func httpPostMultipartForm(clt *http.Client, url, bodyType string, body []byte, response interface{}) error {
	api.DebugPrintPostMultipartRequest(url, body)
	httpResp, err := clt.Post(url, bodyType, bytes.NewReader(body))
	if err != nil {
		return err
	}
	defer httpResp.Body.Close()

	if httpResp.StatusCode != http.StatusOK {
		return fmt.Errorf("http.Status: %s", httpResp.Status)
	}
	return api.DecodeJSONHttpResponse(httpResp.Body, response)
}
Beispiel #5
0
// GetUserInfo 获取用户信息.
//  accessToken: 网页授权接口调用凭证
//  openId:      用户的唯一标识
//  lang:        返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语, 如果留空 "" 则默认为 zh_CN
//  httpClient:  如果不指定则默认为 http.DefaultClient
func GetUserInfo(accessToken, openId, lang string, httpClient *http.Client) (info *UserInfo, err error) {
	switch lang {
	case "":
		lang = LanguageZhCN
	case LanguageZhCN, LanguageZhTW, LanguageEN:
	default:
		lang = LanguageZhCN
	}

	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	_url := "https://api.weixin.qq.com/sns/userinfo?access_token=" + url.QueryEscape(accessToken) +
		"&openid=" + url.QueryEscape(openId) +
		"&lang=" + lang
	api.DebugPrintGetRequest(_url)
	httpResp, err := httpClient.Get(_url)
	if err != nil {
		return
	}
	defer httpResp.Body.Close()

	if httpResp.StatusCode != http.StatusOK {
		err = fmt.Errorf("http.Status: %s", httpResp.Status)
		return
	}

	var result struct {
		oauth2.Error
		UserInfo
	}
	if err = api.DecodeJSONHttpResponse(httpResp.Body, &result); err != nil {
		return
	}
	if result.ErrCode != oauth2.ErrCodeOK {
		err = &result.Error
		return
	}
	info = &result.UserInfo
	return
}
Beispiel #6
0
func httpDownloadToWriter(clt *http.Client, url string, body []byte, buf []byte, writer io.Writer, errorResult *core.Error) (written int64, err error) {
	api.DebugPrintPostJSONRequest(url, body)
	httpResp, err := clt.Post(url, "application/json; charset=utf-8", bytes.NewReader(body))
	if err != nil {
		return 0, err
	}
	defer httpResp.Body.Close()

	if httpResp.StatusCode != http.StatusOK {
		return 0, fmt.Errorf("http.Status: %s", httpResp.Status)
	}

	buf2 := buf // 保存预先读取的少量头部信息
	switch n, err := io.ReadFull(httpResp.Body, buf2); err {
	case nil:
		break
	case io.ErrUnexpectedEOF:
		buf2 = buf2[:n]
		break
	case io.EOF: // 基本不会出现
		return 0, nil
	default:
		return 0, err
	}

	var httpRespBody io.Reader
	if len(buf2) < len(buf) {
		httpRespBody = bytes.NewReader(buf2)
	} else {
		httpRespBody = io.MultiReader(bytes.NewReader(buf2), httpResp.Body)
	}

	buf3 := trimLeft(buf2)
	if bytes.HasPrefix(buf3, errRespBeginWithCode) || bytes.HasPrefix(buf3, errRespBeginWithMsg) {
		// 返回的是错误信息
		return 0, api.DecodeJSONHttpResponse(httpRespBody, errorResult)
	} else {
		// 返回的是媒体流
		return io.Copy(writer, httpRespBody)
	}
}
Beispiel #7
0
func httpDownloadToWriter(clt *http.Client, url string, writer io.Writer, errorResult *core.Error) (written int64, err error) {
	api.DebugPrintGetRequest(url)
	httpResp, err := clt.Get(url)
	if err != nil {
		return 0, err
	}
	defer httpResp.Body.Close()

	if httpResp.StatusCode != http.StatusOK {
		return 0, fmt.Errorf("http.Status: %s", httpResp.Status)
	}

	ContentDisposition := httpResp.Header.Get("Content-Disposition")
	ContentType, _, _ := mime.ParseMediaType(httpResp.Header.Get("Content-Type"))
	if ContentDisposition != "" && ContentType != "text/plain" && ContentType != "application/json" {
		// 返回的是媒体流
		return io.Copy(writer, httpResp.Body)
	} else {
		// 返回的是错误信息
		return 0, api.DecodeJSONHttpResponse(httpResp.Body, errorResult)
	}
}
Beispiel #8
0
// Auth 检验授权凭证 access_token 是否有效.
//  accessToken: 网页授权接口调用凭证
//  openId:      用户的唯一标识
//  httpClient:  如果不指定则默认为 http.DefaultClient
func Auth(accessToken, openId string, httpClient *http.Client) (valid bool, err error) {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	_url := "https://api.weixin.qq.com/sns/auth?access_token=" + url.QueryEscape(accessToken) +
		"&openid=" + url.QueryEscape(openId)
	api.DebugPrintGetRequest(_url)
	httpResp, err := httpClient.Get(_url)
	if err != nil {
		return
	}
	defer httpResp.Body.Close()

	if httpResp.StatusCode != http.StatusOK {
		err = fmt.Errorf("http.Status: %s", httpResp.Status)
		return
	}

	var result oauth2.Error
	if err = api.DecodeJSONHttpResponse(httpResp.Body, &result); err != nil {
		return
	}

	switch result.ErrCode {
	case oauth2.ErrCodeOK:
		valid = true
		return
	case 42001, 40001, 40014, 40003:
		valid = false
		return
	default:
		err = &result
		return
	}
}
Beispiel #9
0
// updateToken 从微信服务器获取新的 access_token 并存入缓存, 同时返回该 access_token.
func (srv *DefaultAccessTokenServer) updateToken(currentToken string) (token *accessToken, cached bool, err error) {
	if currentToken != "" {
		if p := (*accessToken)(atomic.LoadPointer(&srv.tokenCache)); p != nil && currentToken != p.Token {
			return p, true, nil // 无需更改 p.ExpiresIn 参数值, cached == true 时用不到
		}
	}

	url := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + srv.appId +
		"&secret=" + srv.appSecret
	api.DebugPrintGetRequest(url)
	httpResp, err := srv.httpClient.Get(url)
	if err != nil {
		atomic.StorePointer(&srv.tokenCache, nil)
		return
	}
	defer httpResp.Body.Close()

	if httpResp.StatusCode != http.StatusOK {
		atomic.StorePointer(&srv.tokenCache, nil)
		err = fmt.Errorf("http.Status: %s", httpResp.Status)
		return
	}

	var result struct {
		Error
		accessToken
	}
	if err = api.DecodeJSONHttpResponse(httpResp.Body, &result); err != nil {
		atomic.StorePointer(&srv.tokenCache, nil)
		return
	}
	if result.ErrCode != ErrCodeOK {
		atomic.StorePointer(&srv.tokenCache, nil)
		err = &result.Error
		return
	}

	// 由于网络的延时, access_token 过期时间留有一个缓冲区
	switch {
	case result.ExpiresIn > 31556952: // 60*60*24*365.2425
		atomic.StorePointer(&srv.tokenCache, nil)
		err = errors.New("expires_in too large: " + strconv.FormatInt(result.ExpiresIn, 10))
		return
	case result.ExpiresIn > 60*60:
		result.ExpiresIn -= 60 * 10
	case result.ExpiresIn > 60*30:
		result.ExpiresIn -= 60 * 5
	case result.ExpiresIn > 60*5:
		result.ExpiresIn -= 60
	case result.ExpiresIn > 60:
		result.ExpiresIn -= 10
	default:
		atomic.StorePointer(&srv.tokenCache, nil)
		err = errors.New("expires_in too small: " + strconv.FormatInt(result.ExpiresIn, 10))
		return
	}

	tokenCopy := result.accessToken
	atomic.StorePointer(&srv.tokenCache, unsafe.Pointer(&tokenCopy))
	token = &tokenCopy
	return
}