增加vendor

This commit is contained in:
hoteas
2022-05-24 13:49:25 +08:00
parent c3a24eeb3c
commit 65ece0418e
742 changed files with 509206 additions and 0 deletions
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2017-2018 Tencent Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,259 @@
// a simple golang breaker according to https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn589784(v=pandp.10)
package common
import (
"errors"
"strings"
"sync"
"time"
)
const (
defaultBackupEndpoint = "ap-guangzhou.tencentcloudapi.com"
defaultMaxFailNum = 5
defaultMaxFailPercentage = 75
defaultWindowLength = 1 * 60 * time.Second
defaultTimeout = 60 * time.Second
)
var (
// ErrOpenState is returned when the CB state is open
errOpenState = errors.New("circuit breaker is open")
)
// counter use atomic operations to ensure consistency
// Atomic operations perform better than mutex
type counter struct {
failures int
all int
consecutiveSuccesses int
consecutiveFailures int
}
func newRegionCounter() counter {
return counter{
failures: 0,
all: 0,
consecutiveSuccesses: 0,
consecutiveFailures: 0,
}
}
func (c *counter) onSuccess() {
c.all++
c.consecutiveSuccesses++
c.consecutiveFailures = 0
}
func (c *counter) onFailure() {
c.all++
c.failures++
c.consecutiveSuccesses = 0
c.consecutiveSuccesses = 0
}
func (c *counter) clear() {
c.all = 0
c.failures = 0
c.consecutiveSuccesses = 0
}
// State is a type that represents a state of CircuitBreaker.
type state int
// These constants are states of CircuitBreaker.
const (
StateClosed state = iota
StateHalfOpen
StateOpen
)
type breakerSetting struct {
// backupEndpoint
// the default is "ap-guangzhou.tencentcloudapi.com"
backupEndpoint string
// max fail nums
// the default is 5
maxFailNum int
// max fail percentage
// the default is 75/100
maxFailPercentage int
// windowInterval decides when to reset counter if the state is StateClosed
// the default is 5minutes
windowInterval time.Duration
// timeout decides when to turn StateOpen to StateHalfOpen
// the default is 60s
timeout time.Duration
// maxRequests decides when to turn StateHalfOpen to StateClosed
maxRequests int
}
type circuitBreaker struct {
// settings
breakerSetting
// read and write lock
mu sync.Mutex
// the breaker's state: closed, open, half-open
state state
// expiry time determines whether to enter the next generation
// if in StateClosed, it will be now + windowInterval
// if in StateOpen, it will be now + timeout
// if in StateHalfOpen. it will be zero
expiry time.Time
// generation decide whether add the afterRequest's request to counter
generation uint64
// counter
counter counter
}
func newRegionBreaker(set breakerSetting) (re *circuitBreaker) {
re = new(circuitBreaker)
re.breakerSetting = set
return
}
func defaultRegionBreaker() *circuitBreaker {
defaultSet := breakerSetting{
backupEndpoint: defaultBackupEndpoint,
maxFailNum: defaultMaxFailNum,
maxFailPercentage: defaultMaxFailPercentage,
windowInterval: defaultWindowLength,
timeout: defaultTimeout,
}
return newRegionBreaker(defaultSet)
}
// currentState return the current state.
// if in StateClosed and now is over expiry time, it will turn to a new generation.
// if in StateOpen and now is over expiry time, it will turn to StateHalfOpen
func (s *circuitBreaker) currentState(now time.Time) (state, uint64) {
switch s.state {
case StateClosed:
if s.expiry.Before(now) {
s.toNewGeneration(now)
}
case StateOpen:
if s.expiry.Before(now) {
s.setState(StateHalfOpen, now)
}
}
return s.state, s.generation
}
// setState set the circuitBreaker's state to newState
// and turn to new generation
func (s *circuitBreaker) setState(newState state, now time.Time) {
if s.state == newState {
return
}
s.state = newState
s.toNewGeneration(now)
}
// toNewGeneration will increase the generation and clear the counter.
// it also will reset the expiry
func (s *circuitBreaker) toNewGeneration(now time.Time) {
s.generation++
s.counter.clear()
var zero time.Time
switch s.state {
case StateClosed:
s.expiry = now.Add(s.windowInterval)
case StateOpen:
s.expiry = now.Add(s.timeout)
default: // StateHalfOpen
s.expiry = zero
}
}
// beforeRequest return the current generation; if the breaker is in StateOpen, it will also return an errOpenState
func (s *circuitBreaker) beforeRequest() (uint64, error) {
s.mu.Lock()
defer s.mu.Unlock()
now := time.Now()
state, generation := s.currentState(now)
//log.Println(s.counter)
if state == StateOpen {
return generation, errOpenState
}
return generation, nil
}
func (s *circuitBreaker) afterRequest(before uint64, success bool) {
s.mu.Lock()
defer s.mu.Unlock()
now := time.Now()
state, generation := s.currentState(now)
// the breaker has entered the next generation, the current results abandon.
if generation != before {
return
}
if success {
s.onSuccess(state, now)
} else {
s.onFailure(state, now)
}
}
func (s *circuitBreaker) onSuccess(state state, now time.Time) {
switch state {
case StateClosed:
s.counter.onSuccess()
case StateHalfOpen:
s.counter.onSuccess()
// The conditions for closing breaker are met
if s.counter.all-s.counter.failures >= s.maxRequests {
s.setState(StateClosed, now)
}
}
}
func (s *circuitBreaker) readyToOpen(c counter) bool {
failPre := float64(c.failures) / float64(c.all)
return (c.failures >= s.maxFailNum && failPre >= float64(s.maxFailPercentage)/100.0) ||
c.consecutiveFailures > 5
}
func (s *circuitBreaker) onFailure(state state, now time.Time) {
switch state {
case StateClosed:
s.counter.onFailure()
if f := s.readyToOpen(s.counter); f {
s.setState(StateOpen, now)
}
case StateHalfOpen:
s.setState(StateOpen, now)
}
}
// checkEndpoint
// valid: cvm.ap-shanghai.tencentcloudapi.com, cvm.ap-shenzhen-fs.tencentcloudapi.comcvm.tencentcloudapi.com
// invalid: cvm.tencentcloud.com
func checkEndpoint(endpoint string) bool {
ss := strings.Split(endpoint, ".")
if len(ss) != 4 && len(ss) != 3 {
return false
}
if ss[len(ss)-2] != "tencentcloudapi" {
return false
}
// ap-beijing
if len(ss) == 4 && len(strings.Split(ss[1], "-")) < 2 {
return false
}
return true
}
func renewUrl(oldDomain, region string) string {
ss := strings.Split(oldDomain, ".")
if len(ss) == 3 {
ss = append([]string{ss[0], region}, ss[1:]...)
} else if len(ss) == 4 {
ss[1] = region
}
newDomain := strings.Join(ss, ".")
return newDomain
}
@@ -0,0 +1,412 @@
package common
import (
"encoding/hex"
"encoding/json"
"fmt"
"log"
"net/http"
"net/http/httputil"
"strconv"
"strings"
"time"
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
)
const (
octetStream = "application/octet-stream"
)
type Client struct {
region string
httpClient *http.Client
httpProfile *profile.HttpProfile
profile *profile.ClientProfile
credential CredentialIface
signMethod string
unsignedPayload bool
debug bool
rb *circuitBreaker
}
func (c *Client) Send(request tchttp.Request, response tchttp.Response) (err error) {
if request.GetScheme() == "" {
request.SetScheme(c.httpProfile.Scheme)
}
if request.GetRootDomain() == "" {
request.SetRootDomain(c.httpProfile.RootDomain)
}
if request.GetDomain() == "" {
domain := c.httpProfile.Endpoint
if domain == "" {
domain = request.GetServiceDomain(request.GetService())
}
request.SetDomain(domain)
}
if request.GetHttpMethod() == "" {
request.SetHttpMethod(c.httpProfile.ReqMethod)
}
tchttp.CompleteCommonParams(request, c.GetRegion())
// reflect to inject client if field ClientToken exists and retry feature is enabled
if c.profile.NetworkFailureMaxRetries > 0 || c.profile.RateLimitExceededMaxRetries > 0 {
safeInjectClientToken(request)
}
if c.credential == nil {
// Some APIs can skip signature.
return c.sendWithoutSignature(request, response)
} else if c.profile.DisableRegionBreaker == true || c.rb == nil {
return c.sendWithSignature(request, response)
} else {
return c.sendWithRegionBreaker(request, response)
}
}
func (c *Client) sendWithRegionBreaker(request tchttp.Request, response tchttp.Response) (err error) {
defer func() {
e := recover()
if e != nil {
msg := fmt.Sprintf("%s", e)
err = tcerr.NewTencentCloudSDKError("ClientError.CircuitBreakerError", msg, "")
}
}()
ge, err := c.rb.beforeRequest()
if err == errOpenState {
newEndpoint := request.GetService() + "." + c.rb.backupEndpoint
request.SetDomain(newEndpoint)
}
err = c.sendWithSignature(request, response)
isSuccess := false
// Success is considered only when the server returns an effective response (have requestId and the code is not InternalError )
if e, ok := err.(*tcerr.TencentCloudSDKError); ok {
if e.GetRequestId() != "" && e.GetCode() != "InternalError" {
isSuccess = true
}
}
c.rb.afterRequest(ge, isSuccess)
return err
}
func (c *Client) sendWithSignature(request tchttp.Request, response tchttp.Response) (err error) {
if c.signMethod == "HmacSHA1" || c.signMethod == "HmacSHA256" {
return c.sendWithSignatureV1(request, response)
} else {
return c.sendWithSignatureV3(request, response)
}
}
func (c *Client) sendWithoutSignature(request tchttp.Request, response tchttp.Response) error {
httpRequest, err := http.NewRequestWithContext(request.GetContext(), request.GetHttpMethod(), request.GetUrl(), request.GetBodyReader())
if err != nil {
return err
}
if request.GetHttpMethod() == "POST" {
httpRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
httpResponse, err := c.sendWithRateLimitRetry(httpRequest, isRetryable(request))
if err != nil {
return err
}
err = tchttp.ParseFromHttpResponse(httpResponse, response)
return err
}
func (c *Client) sendWithSignatureV1(request tchttp.Request, response tchttp.Response) (err error) {
// TODO: not an elegant way, it should be done in common params, but finally it need to refactor
request.GetParams()["Language"] = c.profile.Language
err = tchttp.ConstructParams(request)
if err != nil {
return err
}
err = signRequest(request, c.credential, c.signMethod)
if err != nil {
return err
}
httpRequest, err := http.NewRequestWithContext(request.GetContext(), request.GetHttpMethod(), request.GetUrl(), request.GetBodyReader())
if err != nil {
return err
}
if request.GetHttpMethod() == "POST" {
httpRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
httpResponse, err := c.sendWithRateLimitRetry(httpRequest, isRetryable(request))
if err != nil {
return err
}
err = tchttp.ParseFromHttpResponse(httpResponse, response)
return err
}
func (c *Client) sendWithSignatureV3(request tchttp.Request, response tchttp.Response) (err error) {
headers := map[string]string{
"Host": request.GetDomain(),
"X-TC-Action": request.GetAction(),
"X-TC-Version": request.GetVersion(),
"X-TC-Timestamp": request.GetParams()["Timestamp"],
"X-TC-RequestClient": request.GetParams()["RequestClient"],
"X-TC-Language": c.profile.Language,
}
if c.region != "" {
headers["X-TC-Region"] = c.region
}
if c.credential.GetToken() != "" {
headers["X-TC-Token"] = c.credential.GetToken()
}
if request.GetHttpMethod() == "GET" {
headers["Content-Type"] = "application/x-www-form-urlencoded"
} else {
headers["Content-Type"] = "application/json"
}
isOctetStream := false
cr := &tchttp.CommonRequest{}
ok := false
var octetStreamBody []byte
if cr, ok = request.(*tchttp.CommonRequest); ok {
if cr.IsOctetStream() {
isOctetStream = true
// custom headers must contain Content-Type : application/octet-stream
// todo:the custom header may overwrite headers
for k, v := range cr.GetHeader() {
headers[k] = v
}
octetStreamBody = cr.GetOctetStreamBody()
}
}
if !isOctetStream && request.GetContentType() == octetStream {
isOctetStream = true
b, _ := json.Marshal(request)
var m map[string]string
_ = json.Unmarshal(b, &m)
for k, v := range m {
key := "X-" + strings.ToUpper(request.GetService()) + "-" + k
headers[key] = v
}
headers["Content-Type"] = octetStream
octetStreamBody = request.GetBody()
}
// start signature v3 process
// build canonical request string
httpRequestMethod := request.GetHttpMethod()
canonicalURI := "/"
canonicalQueryString := ""
if httpRequestMethod == "GET" {
err = tchttp.ConstructParams(request)
if err != nil {
return err
}
params := make(map[string]string)
for key, value := range request.GetParams() {
params[key] = value
}
delete(params, "Action")
delete(params, "Version")
delete(params, "Nonce")
delete(params, "Region")
delete(params, "RequestClient")
delete(params, "Timestamp")
canonicalQueryString = tchttp.GetUrlQueriesEncoded(params)
}
canonicalHeaders := fmt.Sprintf("content-type:%s\nhost:%s\n", headers["Content-Type"], headers["Host"])
signedHeaders := "content-type;host"
requestPayload := ""
if httpRequestMethod == "POST" {
if isOctetStream {
// todo Conversion comparison between string and []byte affects performance much
requestPayload = string(octetStreamBody)
} else {
b, err := json.Marshal(request)
if err != nil {
return err
}
requestPayload = string(b)
}
}
hashedRequestPayload := ""
if c.unsignedPayload {
hashedRequestPayload = sha256hex("UNSIGNED-PAYLOAD")
headers["X-TC-Content-SHA256"] = "UNSIGNED-PAYLOAD"
} else {
hashedRequestPayload = sha256hex(requestPayload)
}
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s",
httpRequestMethod,
canonicalURI,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
hashedRequestPayload)
//log.Println("canonicalRequest:", canonicalRequest)
// build string to sign
algorithm := "TC3-HMAC-SHA256"
requestTimestamp := headers["X-TC-Timestamp"]
timestamp, _ := strconv.ParseInt(requestTimestamp, 10, 64)
t := time.Unix(timestamp, 0).UTC()
// must be the format 2006-01-02, ref to package time for more info
date := t.Format("2006-01-02")
credentialScope := fmt.Sprintf("%s/%s/tc3_request", date, request.GetService())
hashedCanonicalRequest := sha256hex(canonicalRequest)
string2sign := fmt.Sprintf("%s\n%s\n%s\n%s",
algorithm,
requestTimestamp,
credentialScope,
hashedCanonicalRequest)
//log.Println("string2sign", string2sign)
// sign string
secretDate := hmacsha256(date, "TC3"+c.credential.GetSecretKey())
secretService := hmacsha256(request.GetService(), secretDate)
secretKey := hmacsha256("tc3_request", secretService)
signature := hex.EncodeToString([]byte(hmacsha256(string2sign, secretKey)))
//log.Println("signature", signature)
// build authorization
authorization := fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s",
algorithm,
c.credential.GetSecretId(),
credentialScope,
signedHeaders,
signature)
//log.Println("authorization", authorization)
headers["Authorization"] = authorization
url := request.GetScheme() + "://" + request.GetDomain() + request.GetPath()
if canonicalQueryString != "" {
url = url + "?" + canonicalQueryString
}
httpRequest, err := http.NewRequestWithContext(request.GetContext(), httpRequestMethod, url, strings.NewReader(requestPayload))
if err != nil {
return err
}
for k, v := range headers {
httpRequest.Header[k] = []string{v}
}
httpResponse, err := c.sendWithRateLimitRetry(httpRequest, isRetryable(request))
if err != nil {
return err
}
err = tchttp.ParseFromHttpResponse(httpResponse, response)
return err
}
// send http request
func (c *Client) sendHttp(request *http.Request) (response *http.Response, err error) {
if c.debug {
outBytes, err := httputil.DumpRequest(request, true)
if err != nil {
log.Printf("[ERROR] dump request failed because %s", err)
return nil, err
}
log.Printf("[DEBUG] http request = %s", outBytes)
}
response, err = c.httpClient.Do(request)
return response, err
}
func (c *Client) GetRegion() string {
return c.region
}
func (c *Client) Init(region string) *Client {
c.httpClient = &http.Client{}
c.region = region
c.signMethod = "TC3-HMAC-SHA256"
c.debug = false
log.SetFlags(log.LstdFlags | log.Lshortfile)
return c
}
func (c *Client) WithSecretId(secretId, secretKey string) *Client {
c.credential = NewCredential(secretId, secretKey)
return c
}
func (c *Client) WithCredential(cred CredentialIface) *Client {
c.credential = cred
return c
}
func (c *Client) GetCredential() CredentialIface {
return c.credential
}
func (c *Client) WithProfile(clientProfile *profile.ClientProfile) *Client {
c.profile = clientProfile
if c.profile.DisableRegionBreaker == false {
c.withRegionBreaker()
}
c.signMethod = clientProfile.SignMethod
c.unsignedPayload = clientProfile.UnsignedPayload
c.httpProfile = clientProfile.HttpProfile
c.debug = clientProfile.Debug
c.httpClient.Timeout = time.Duration(c.httpProfile.ReqTimeout) * time.Second
return c
}
func (c *Client) WithSignatureMethod(method string) *Client {
c.signMethod = method
return c
}
func (c *Client) WithHttpTransport(transport http.RoundTripper) *Client {
c.httpClient.Transport = transport
return c
}
func (c *Client) WithDebug(flag bool) *Client {
c.debug = flag
return c
}
// WithProvider use specify provider to get a credential and use it to build a client
func (c *Client) WithProvider(provider Provider) (*Client, error) {
cred, err := provider.GetCredential()
if err != nil {
return nil, err
}
return c.WithCredential(cred), nil
}
func (c *Client) withRegionBreaker() *Client {
rb := defaultRegionBreaker()
if c.profile.BackupEndpoint != "" {
rb.backupEndpoint = c.profile.BackupEndpoint
} else if c.profile.BackupEndPoint != "" {
rb.backupEndpoint = c.profile.BackupEndPoint
}
c.rb = rb
return c
}
func NewClientWithSecretId(secretId, secretKey, region string) (client *Client, err error) {
client = &Client{}
client.Init(region).WithSecretId(secretId, secretKey)
return
}
// NewClientWithProviders build client with your custom providers;
// If you don't specify the providers, it will use the DefaultProviderChain to find credential
func NewClientWithProviders(region string, providers ...Provider) (client *Client, err error) {
client = (&Client{}).Init(region)
var pc Provider
if len(providers) == 0 {
pc = DefaultProviderChain()
} else {
pc = NewProviderChain(providers)
}
return client.WithProvider(pc)
}
@@ -0,0 +1,46 @@
package common
import (
"fmt"
"crypto/rand"
"reflect"
)
const (
fieldClientToken = "ClientToken"
)
func safeInjectClientToken(obj interface{}) {
// obj Must be struct ptr
getType := reflect.TypeOf(obj)
if getType.Kind() != reflect.Ptr || getType.Elem().Kind() != reflect.Struct {
return
}
// obj Must exist named field
_, ok := getType.Elem().FieldByName(fieldClientToken)
if !ok {
return
}
field := reflect.ValueOf(obj).Elem().FieldByName(fieldClientToken)
// field Must be string ptr
if field.Kind() != reflect.Ptr {
return
}
// Set if ClientToken is nil or empty
if field.IsNil() || (field.Elem().Kind() == reflect.String && field.Elem().Len() == 0) {
uuidVal := randomClientToken()
field.Set(reflect.ValueOf(&uuidVal))
}
}
// randomClientToken generate random string as ClientToken
// ref: https://stackoverflow.com/questions/15130321/is-there-a-method-to-generate-a-uuid-with-go-language
func randomClientToken() string {
b := make([]byte, 16)
_, _ = rand.Read(b)
return fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
}
@@ -0,0 +1,36 @@
package common
import (
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
)
func NewCommonClient(cred CredentialIface, region string, clientProfile *profile.ClientProfile) (c *Client) {
return new(Client).Init(region).WithCredential(cred).WithProfile(clientProfile)
}
// SendOctetStream Invoke API with application/octet-stream content-type.
//
// Note:
// 1. only specific API can be invoked in such manner.
// 2. only TC3-HMAC-SHA256 signature method can be specified.
// 3. only POST request method can be specified
// 4. the request Must be a CommonRequest and called SetOctetStreamParameters
//
func (c *Client) SendOctetStream(request tchttp.Request, response tchttp.Response) (err error) {
if c.profile.SignMethod != "TC3-HMAC-SHA256" {
return tcerr.NewTencentCloudSDKError("ClientError", "Invalid signature method.", "")
}
if c.profile.HttpProfile.ReqMethod != "POST" {
return tcerr.NewTencentCloudSDKError("ClientError", "Invalid request method.", "")
}
//cr, ok := request.(*tchttp.CommonRequest)
//if !ok {
// return tcerr.NewTencentCloudSDKError("ClientError", "Invalid request, must be *CommonRequest!", "")
//}
//if !cr.IsOctetStream() {
// return tcerr.NewTencentCloudSDKError("ClientError", "Invalid request, does not meet the conditions for sending OctetStream", "")
//}
return c.Send(request, response)
}
@@ -0,0 +1,51 @@
package common
var creErr = "ClientError.CredentialError"
type CredentialIface interface {
GetSecretId() string
GetToken() string
GetSecretKey() string
// needRefresh() bool
// refresh()
}
type Credential struct {
SecretId string
SecretKey string
Token string
}
func (c *Credential) needRefresh() bool {
return false
}
func (c *Credential) refresh() {
}
func NewCredential(secretId, secretKey string) *Credential {
return &Credential{
SecretId: secretId,
SecretKey: secretKey,
}
}
func NewTokenCredential(secretId, secretKey, token string) *Credential {
return &Credential{
SecretId: secretId,
SecretKey: secretKey,
Token: token,
}
}
func (c *Credential) GetSecretKey() string {
return c.SecretKey
}
func (c *Credential) GetSecretId() string {
return c.SecretId
}
func (c *Credential) GetToken() string {
return c.Token
}
@@ -0,0 +1,54 @@
package common
import (
"log"
"time"
)
const ExpiredTimeout = 300
type CvmRoleCredential struct {
roleName string
expiredTime int64
tmpSecretId string
tmpSecretKey string
token string
source *CvmRoleProvider
}
func (c *CvmRoleCredential) GetSecretId() string {
if c.needRefresh() {
c.refresh()
}
return c.tmpSecretId
}
func (c *CvmRoleCredential) GetToken() string {
if c.needRefresh() {
c.refresh()
}
return c.token
}
func (c *CvmRoleCredential) GetSecretKey() string {
if c.needRefresh() {
c.refresh()
}
return c.tmpSecretKey
}
func (c *CvmRoleCredential) needRefresh() bool {
if c.tmpSecretId == "" || c.tmpSecretKey == "" || c.token == "" || c.expiredTime-ExpiredTimeout <= time.Now().Unix() {
return true
}
return false
}
func (c *CvmRoleCredential) refresh() {
newCre, err := c.source.GetCredential()
if err != nil {
log.Println(err)
return
}
*c = *newCre.(*CvmRoleCredential)
}
@@ -0,0 +1,96 @@
package common
import (
"encoding/json"
"errors"
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
"io/ioutil"
"net/http"
"time"
)
const (
metaUrl = "http://metadata.tencentyun.com/latest/meta-data/"
roleUrl = metaUrl + "cam/security-credentials/"
)
var roleNotBound = errors.New("get cvm role name failed, Please confirm whether the role is bound")
type CvmRoleProvider struct {
roleName string
}
type roleRsp struct {
TmpSecretId string `json:"TmpSecretId"`
TmpSecretKey string `json:"TmpSecretKey"`
ExpiredTime int64 `json:"ExpiredTime"`
Expiration time.Time `json:"Expiration"`
Token string `json:"Token"`
Code string `json:"Code"`
}
// NewCvmRoleProvider need you to specify the roleName of the cvm currently in use
func NewCvmRoleProvider(roleName string) *CvmRoleProvider {
return &CvmRoleProvider{roleName: roleName}
}
// DefaultCvmRoleProvider will auto get the cvm role name by accessing the metadata api
// more info please lookup: https://cloud.tencent.com/document/product/213/4934
func DefaultCvmRoleProvider() *CvmRoleProvider {
return NewCvmRoleProvider("")
}
func get(url string) ([]byte, error) {
rsp, err := http.Get(url)
if err != nil {
return nil, err
}
if rsp.StatusCode == http.StatusNotFound {
return nil, roleNotBound
}
body, err := ioutil.ReadAll(rsp.Body)
if err != nil {
return []byte{}, err
}
return body, nil
}
func (r *CvmRoleProvider) getRoleName() (string, error) {
if r.roleName != "" {
return r.roleName, nil
}
rn, err := get(roleUrl)
return string(rn), err
}
func (r *CvmRoleProvider) GetCredential() (CredentialIface, error) {
roleName, err := r.getRoleName()
if err != nil {
return nil, noCvmRole
}
// get the cvm role name by accessing the metadata api
// https://cloud.tencent.com/document/product/213/4934
body, err := get(roleUrl + roleName)
if err != nil {
return nil, err
}
rspSt := new(roleRsp)
if err = json.Unmarshal(body, rspSt); err != nil {
return nil, tcerr.NewTencentCloudSDKError(creErr, err.Error(), "")
}
if rspSt.Code != "Success" {
return nil, tcerr.NewTencentCloudSDKError(creErr, "Get credential from metadata server by role name "+roleName+" failed, code="+rspSt.Code, "")
}
cre := &CvmRoleCredential{
tmpSecretId: rspSt.TmpSecretId,
tmpSecretKey: rspSt.TmpSecretKey,
token: rspSt.Token,
roleName: roleName,
expiredTime: rspSt.ExpiredTime,
source: r,
}
return cre, nil
}
@@ -0,0 +1,40 @@
package common
import (
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
"os"
)
type EnvProvider struct {
secretIdENV string
secretKeyENV string
}
// DefaultEnvProvider return a default provider
// The default environment variable name are TENCENTCLOUD_SECRET_ID and TENCENTCLOUD_SECRET_KEY
func DefaultEnvProvider() *EnvProvider {
return &EnvProvider{
secretIdENV: "TENCENTCLOUD_SECRET_ID",
secretKeyENV: "TENCENTCLOUD_SECRET_KEY",
}
}
// NewEnvProvider uses the name of the environment variable you specified to get the credentials
func NewEnvProvider(secretIdEnvName, secretKeyEnvName string) *EnvProvider {
return &EnvProvider{
secretIdENV: secretIdEnvName,
secretKeyENV: secretKeyEnvName,
}
}
func (p *EnvProvider) GetCredential() (CredentialIface, error) {
secretId, ok1 := os.LookupEnv(p.secretIdENV)
secretKey, ok2 := os.LookupEnv(p.secretKeyENV)
if !ok1 || !ok2 {
return nil, envNotSet
}
if secretId == "" || secretKey == "" {
return nil, tcerr.NewTencentCloudSDKError(creErr, "Environmental variable ("+p.secretIdENV+" or "+p.secretKeyENV+") is empty", "")
}
return NewCredential(secretId, secretKey), nil
}
@@ -0,0 +1,38 @@
package errors
import (
"fmt"
)
type TencentCloudSDKError struct {
Code string
Message string
RequestId string
}
func (e *TencentCloudSDKError) Error() string {
if e.RequestId == "" {
return fmt.Sprintf("[TencentCloudSDKError] Code=%s, Message=%s", e.Code, e.Message)
}
return fmt.Sprintf("[TencentCloudSDKError] Code=%s, Message=%s, RequestId=%s", e.Code, e.Message, e.RequestId)
}
func NewTencentCloudSDKError(code, message, requestId string) error {
return &TencentCloudSDKError{
Code: code,
Message: message,
RequestId: requestId,
}
}
func (e *TencentCloudSDKError) GetCode() string {
return e.Code
}
func (e *TencentCloudSDKError) GetMessage() string {
return e.Message
}
func (e *TencentCloudSDKError) GetRequestId() string {
return e.RequestId
}
@@ -0,0 +1,3 @@
module github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common
go 1.14
@@ -0,0 +1,108 @@
package common
import (
"encoding/json"
"fmt"
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
)
const (
octetStream = "application/octet-stream"
)
type actionParameters map[string]interface{}
type CommonRequest struct {
*BaseRequest
// custom header, may be overwritten
header map[string]string
actionParameters
}
func NewCommonRequest(service, version, action string) (request *CommonRequest) {
request = &CommonRequest{
BaseRequest: &BaseRequest{},
actionParameters: actionParameters{},
}
request.Init().WithApiInfo(service, version, action)
return
}
// SetActionParameters set common request's actionParameters to your data.
// note: your data Must be a json-formatted string or byte array or map[string]interface{}
// note: you could not call SetActionParameters and SetOctetStreamParameters at once
func (cr *CommonRequest) SetActionParameters(data interface{}) error {
if data == nil {
return nil
}
switch data.(type) {
case []byte:
if err := json.Unmarshal(data.([]byte), &cr.actionParameters); err != nil {
msg := fmt.Sprintf("Fail to parse contenst %s to json,because: %s", data.([]byte), err)
return tcerr.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
case string:
if err := json.Unmarshal([]byte(data.(string)), &cr.actionParameters); err != nil {
msg := fmt.Sprintf("Fail to parse contenst %s to json,because: %s", data.(string), err)
return tcerr.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
case map[string]interface{}:
cr.actionParameters = data.(map[string]interface{})
default:
msg := fmt.Sprintf("Invalid data type:%T, must be one of the following: []byte, string, map[string]interface{}", data)
return tcerr.NewTencentCloudSDKError("ClientError.InvalidParameter", msg, "")
}
return nil
}
func (cr *CommonRequest) IsOctetStream() bool {
v, ok := cr.header["Content-Type"]
if !ok || v != octetStream {
return false
}
value, ok := cr.actionParameters["OctetStreamBody"]
if !ok {
return false
}
_, ok = value.([]byte)
if !ok {
return false
}
return true
}
func (cr *CommonRequest) SetHeader(header map[string]string) {
if header == nil {
return
}
cr.header = header
}
func (cr *CommonRequest) GetHeader() map[string]string {
return cr.header
}
// SetOctetStreamParameters set request body to your data, and set head Content-Type to application/octet-stream
// note: you could not call SetActionParameters and SetOctetStreamParameters on the same request
func (cr *CommonRequest) SetOctetStreamParameters(header map[string]string, body []byte) {
parameter := map[string]interface{}{}
if header == nil {
header = map[string]string{}
}
header["Content-Type"] = octetStream
cr.header = header
parameter["OctetStreamBody"] = body
cr.actionParameters = parameter
}
func (cr *CommonRequest) GetOctetStreamBody() []byte {
if cr.IsOctetStream() {
return cr.actionParameters["OctetStreamBody"].([]byte)
} else {
return nil
}
}
func (cr *CommonRequest) MarshalJSON() ([]byte, error) {
return json.Marshal(cr.actionParameters)
}
@@ -0,0 +1,26 @@
package common
import "encoding/json"
type actionResult map[string]interface{}
type CommonResponse struct {
*BaseResponse
*actionResult
}
func NewCommonResponse() (response *CommonResponse) {
response = &CommonResponse{
BaseResponse: &BaseResponse{},
actionResult: &actionResult{},
}
return
}
func (r *CommonResponse) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, r.actionResult)
}
func (r *CommonResponse) GetBody() []byte {
raw, _ := json.Marshal(r.actionResult)
return raw
}
@@ -0,0 +1,321 @@
package common
import (
"context"
"io"
//"log"
"math/rand"
"net/url"
"reflect"
"strconv"
"strings"
"time"
)
const (
POST = "POST"
GET = "GET"
HTTP = "http"
HTTPS = "https"
RootDomain = "tencentcloudapi.com"
Path = "/"
)
type Request interface {
GetAction() string
GetBodyReader() io.Reader
GetScheme() string
GetRootDomain() string
GetServiceDomain(string) string
GetDomain() string
GetHttpMethod() string
GetParams() map[string]string
GetBody() []byte
GetPath() string
GetService() string
GetUrl() string
GetVersion() string
GetContentType() string
GetContext() context.Context
SetScheme(string)
SetRootDomain(string)
SetDomain(string)
SetHttpMethod(string)
SetPath(string)
SetContentType(string)
SetBody([]byte)
SetContext(context.Context)
}
type BaseRequest struct {
context context.Context
httpMethod string
scheme string
rootDomain string
domain string
path string
params map[string]string
formParams map[string]string
service string
version string
action string
contentType string
body []byte
}
func (r *BaseRequest) GetAction() string {
return r.action
}
func (r *BaseRequest) GetHttpMethod() string {
return r.httpMethod
}
func (r *BaseRequest) GetParams() map[string]string {
return r.params
}
func (r *BaseRequest) GetPath() string {
return r.path
}
func (r *BaseRequest) GetDomain() string {
return r.domain
}
func (r *BaseRequest) GetScheme() string {
return r.scheme
}
func (r *BaseRequest) GetRootDomain() string {
return r.rootDomain
}
func (r *BaseRequest) GetServiceDomain(service string) (domain string) {
rootDomain := r.rootDomain
if rootDomain == "" {
rootDomain = RootDomain
}
domain = service + "." + rootDomain
return
}
func (r *BaseRequest) GetBody() []byte {
return r.body
}
func (r *BaseRequest) SetBody(body []byte) {
r.body = body
}
func (r *BaseRequest) GetContentType() string {
return r.contentType
}
func (r *BaseRequest) SetContentType(contentType string) {
r.contentType = contentType
}
func (r *BaseRequest) SetDomain(domain string) {
r.domain = domain
}
func (r *BaseRequest) SetScheme(scheme string) {
scheme = strings.ToLower(scheme)
switch scheme {
case HTTP:
r.scheme = HTTP
default:
r.scheme = HTTPS
}
}
func (r *BaseRequest) SetRootDomain(rootDomain string) {
r.rootDomain = rootDomain
}
func (r *BaseRequest) SetHttpMethod(method string) {
switch strings.ToUpper(method) {
case POST:
{
r.httpMethod = POST
}
case GET:
{
r.httpMethod = GET
}
default:
{
r.httpMethod = GET
}
}
}
func (r *BaseRequest) SetPath(path string) {
r.path = path
}
func (r *BaseRequest) GetService() string {
return r.service
}
func (r *BaseRequest) GetUrl() string {
if r.httpMethod == GET {
return r.GetScheme() + "://" + r.domain + r.path + "?" + GetUrlQueriesEncoded(r.params)
} else if r.httpMethod == POST {
return r.GetScheme() + "://" + r.domain + r.path
} else {
return ""
}
}
func (r *BaseRequest) GetVersion() string {
return r.version
}
func (r *BaseRequest) GetContext() context.Context {
if r.context == nil {
return context.Background()
}
return r.context
}
func (r *BaseRequest) SetContext(ctx context.Context) {
r.context = ctx
}
func GetUrlQueriesEncoded(params map[string]string) string {
values := url.Values{}
for key, value := range params {
values.Add(key, value)
}
return values.Encode()
}
func (r *BaseRequest) GetBodyReader() io.Reader {
if r.httpMethod == POST {
s := GetUrlQueriesEncoded(r.params)
return strings.NewReader(s)
} else {
return strings.NewReader("")
}
}
func (r *BaseRequest) Init() *BaseRequest {
r.domain = ""
r.path = Path
r.params = make(map[string]string)
r.formParams = make(map[string]string)
return r
}
func (r *BaseRequest) WithApiInfo(service, version, action string) *BaseRequest {
r.service = service
r.version = version
r.action = action
return r
}
func (r *BaseRequest) WithContentType(contentType string) *BaseRequest {
r.contentType = contentType
return r
}
// Deprecated, use request.GetServiceDomain instead
func GetServiceDomain(service string) (domain string) {
domain = service + "." + RootDomain
return
}
func CompleteCommonParams(request Request, region string) {
params := request.GetParams()
params["Region"] = region
if request.GetVersion() != "" {
params["Version"] = request.GetVersion()
}
params["Action"] = request.GetAction()
params["Timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
params["Nonce"] = strconv.Itoa(rand.Int())
params["RequestClient"] = "SDK_GO_1.0.364"
}
func ConstructParams(req Request) (err error) {
value := reflect.ValueOf(req).Elem()
err = flatStructure(value, req, "")
//log.Printf("[DEBUG] params=%s", req.GetParams())
return
}
func flatStructure(value reflect.Value, request Request, prefix string) (err error) {
//log.Printf("[DEBUG] reflect value: %v", value.Type())
valueType := value.Type()
for i := 0; i < valueType.NumField(); i++ {
tag := valueType.Field(i).Tag
nameTag, hasNameTag := tag.Lookup("name")
if !hasNameTag {
continue
}
field := value.Field(i)
kind := field.Kind()
if kind == reflect.Ptr && field.IsNil() {
continue
}
if kind == reflect.Ptr {
field = field.Elem()
kind = field.Kind()
}
key := prefix + nameTag
if kind == reflect.String {
s := field.String()
if s != "" {
request.GetParams()[key] = s
}
} else if kind == reflect.Bool {
request.GetParams()[key] = strconv.FormatBool(field.Bool())
} else if kind == reflect.Int || kind == reflect.Int64 {
request.GetParams()[key] = strconv.FormatInt(field.Int(), 10)
} else if kind == reflect.Uint || kind == reflect.Uint64 {
request.GetParams()[key] = strconv.FormatUint(field.Uint(), 10)
} else if kind == reflect.Float64 {
request.GetParams()[key] = strconv.FormatFloat(field.Float(), 'f', -1, 64)
} else if kind == reflect.Slice {
list := value.Field(i)
for j := 0; j < list.Len(); j++ {
vj := list.Index(j)
key := prefix + nameTag + "." + strconv.Itoa(j)
kind = vj.Kind()
if kind == reflect.Ptr && vj.IsNil() {
continue
}
if kind == reflect.Ptr {
vj = vj.Elem()
kind = vj.Kind()
}
if kind == reflect.String {
request.GetParams()[key] = vj.String()
} else if kind == reflect.Bool {
request.GetParams()[key] = strconv.FormatBool(vj.Bool())
} else if kind == reflect.Int || kind == reflect.Int64 {
request.GetParams()[key] = strconv.FormatInt(vj.Int(), 10)
} else if kind == reflect.Uint || kind == reflect.Uint64 {
request.GetParams()[key] = strconv.FormatUint(vj.Uint(), 10)
} else if kind == reflect.Float64 {
request.GetParams()[key] = strconv.FormatFloat(vj.Float(), 'f', -1, 64)
} else {
if err = flatStructure(vj, request, key+"."); err != nil {
return
}
}
}
} else {
if err = flatStructure(reflect.ValueOf(field.Interface()), request, prefix+nameTag+"."); err != nil {
return
}
}
}
return
}
@@ -0,0 +1,104 @@
package common
import (
"encoding/json"
"fmt"
"io/ioutil"
//"log"
"net/http"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
)
type Response interface {
ParseErrorFromHTTPResponse(body []byte) error
}
type BaseResponse struct {
}
type ErrorResponse struct {
Response struct {
Error struct {
Code string `json:"Code"`
Message string `json:"Message"`
} `json:"Error,omitempty"`
RequestId string `json:"RequestId"`
} `json:"Response"`
}
type DeprecatedAPIErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
CodeDesc string `json:"codeDesc"`
}
func (r *BaseResponse) ParseErrorFromHTTPResponse(body []byte) (err error) {
resp := &ErrorResponse{}
err = json.Unmarshal(body, resp)
if err != nil {
msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
if resp.Response.Error.Code != "" {
return errors.NewTencentCloudSDKError(resp.Response.Error.Code, resp.Response.Error.Message, resp.Response.RequestId)
}
deprecated := &DeprecatedAPIErrorResponse{}
err = json.Unmarshal(body, deprecated)
if err != nil {
msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
if deprecated.Code != 0 {
return errors.NewTencentCloudSDKError(deprecated.CodeDesc, deprecated.Message, "")
}
return nil
}
func ParseErrorFromHTTPResponse(body []byte) (err error) {
resp := &ErrorResponse{}
err = json.Unmarshal(body, resp)
if err != nil {
msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
if resp.Response.Error.Code != "" {
return errors.NewTencentCloudSDKError(resp.Response.Error.Code, resp.Response.Error.Message, resp.Response.RequestId)
}
deprecated := &DeprecatedAPIErrorResponse{}
err = json.Unmarshal(body, deprecated)
if err != nil {
msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
if deprecated.Code != 0 {
return errors.NewTencentCloudSDKError(deprecated.CodeDesc, deprecated.Message, "")
}
return nil
}
func ParseFromHttpResponse(hr *http.Response, response Response) (err error) {
defer hr.Body.Close()
body, err := ioutil.ReadAll(hr.Body)
if err != nil {
msg := fmt.Sprintf("Fail to read response body because %s", err)
return errors.NewTencentCloudSDKError("ClientError.IOError", msg, "")
}
if hr.StatusCode != 200 {
msg := fmt.Sprintf("Request fail with http status code: %s, with body: %s", hr.Status, body)
return errors.NewTencentCloudSDKError("ClientError.HttpStatusCodeError", msg, "")
}
//log.Printf("[DEBUG] Response Body=%s", body)
err = response.ParseErrorFromHTTPResponse(body)
if err != nil {
return
}
err = json.Unmarshal(body, &response)
if err != nil {
msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
return
}
@@ -0,0 +1,84 @@
package common
import (
"fmt"
"io/ioutil"
"strings"
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
)
var (
globalSectionName = "____GLOBAL____"
iniErr = "ClientError.INIError"
)
func openFile(path string) (data []byte, err error) {
data, err = ioutil.ReadFile(path)
if err != nil {
err = tcerr.NewTencentCloudSDKError(iniErr, err.Error(), "")
}
return
}
func parse(path string) (*sections, error) {
result := &sections{map[string]*section{}}
buf, err := openFile(path)
if err != nil {
return &sections{}, err
}
content := string(buf)
lines := strings.Split(content, "\n")
if len(lines) == 0 {
msg := fmt.Sprintf("the result of reading the %s is empty", path)
return &sections{}, tcerr.NewTencentCloudSDKError(iniErr, msg, "")
}
currentSectionName := globalSectionName
currentSection := &section{make(map[string]*value)}
for i, line := range lines {
line = strings.Replace(line, "\r", "", -1)
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
// comments
if strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") {
continue
}
// section name
if strings.HasPrefix(line, "[") {
if strings.HasSuffix(line, "]") {
tempSection := line[1 : len(line)-1]
if len(tempSection) == 0 {
msg := fmt.Sprintf("INI file %s lien %d is not valid: wrong section", path, i)
return result, tcerr.NewTencentCloudSDKError(iniErr, msg, "")
}
// Save the previous section
result.contains[currentSectionName] = currentSection
// new section
currentSectionName = tempSection
currentSection = &section{make(map[string]*value, 0)}
continue
} else {
msg := fmt.Sprintf("INI file %s lien %d is not valid: wrong section", path, i)
return result, tcerr.NewTencentCloudSDKError(iniErr, msg, "")
}
}
pos := strings.Index(line, "=")
if pos > 0 && pos < len(line)-1 {
key := line[:pos]
val := line[pos+1:]
key = strings.TrimSpace(key)
val = strings.TrimSpace(val)
v := &value{raw: val}
currentSection.content[key] = v
}
}
result.contains[currentSectionName] = currentSection
return result, nil
}
@@ -0,0 +1,83 @@
package common
import (
"fmt"
"log"
"net"
"net/http"
"reflect"
"time"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
)
const (
tplNetworkFailureRetry = "[WARN] temporary network failure, retrying (%d/%d) in %f seconds: %s"
)
func (c *Client) sendWithNetworkFailureRetry(req *http.Request, retryable bool) (resp *http.Response, err error) {
// make sure maxRetries is more than or equal 0
var maxRetries int
if retryable {
maxRetries = maxInt(c.profile.NetworkFailureMaxRetries, 0)
}
durationFunc := safeDurationFunc(c.profile.NetworkFailureRetryDuration)
for idx := 0; idx <= maxRetries; idx++ {
resp, err = c.sendHttp(req)
// retry when error occurred and retryable and not the last retry
// should not sleep on last retry even if it's retryable
if err != nil && retryable && idx < maxRetries {
if err, ok := err.(net.Error); ok && (err.Timeout() || err.Temporary()) {
duration := durationFunc(idx)
if c.debug {
log.Printf(tplNetworkFailureRetry, idx, maxRetries, duration.Seconds(), err.Error())
}
time.Sleep(duration)
continue
}
}
if err != nil {
msg := fmt.Sprintf("Fail to get response because %s", err)
err = errors.NewTencentCloudSDKError("ClientError.NetworkError", msg, "")
}
return resp, err
}
return resp, err
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
func safeDurationFunc(durationFunc profile.DurationFunc) profile.DurationFunc {
if durationFunc != nil {
return durationFunc
}
return profile.ExponentialBackoff
}
// isRetryable means if request is retryable or not,
// depends on if request has a `ClientToken` field or not,
// request with `ClientToken` means it's idempotent and retryable,
// unretryable request SHOULDN'T retry for temporary network failure
func isRetryable(obj interface{}) bool {
// obj Must be struct ptr
getType := reflect.TypeOf(obj)
if getType.Kind() != reflect.Ptr || getType.Elem().Kind() != reflect.Struct {
return false
}
// obj Must exist named field
_, ok := getType.Elem().FieldByName(fieldClientToken)
return ok
}
@@ -0,0 +1,57 @@
package profile
import (
"math"
"time"
)
type DurationFunc func(index int) time.Duration
func ConstantDurationFunc(duration time.Duration) DurationFunc {
return func(int) time.Duration {
return duration
}
}
func ExponentialBackoff(index int) time.Duration {
seconds := math.Pow(2, float64(index))
return time.Duration(seconds) * time.Second
}
type ClientProfile struct {
HttpProfile *HttpProfile
// Valid choices: HmacSHA1, HmacSHA256, TC3-HMAC-SHA256.
// Default value is TC3-HMAC-SHA256.
SignMethod string
UnsignedPayload bool
// Valid choices: zh-CN, en-US.
// Default value is zh-CN.
Language string
Debug bool
// define Whether to enable Regional auto switch
DisableRegionBreaker bool
// Deprecated. Use BackupEndpoint instead.
BackupEndPoint string
BackupEndpoint string
// define how to retry request
NetworkFailureMaxRetries int
NetworkFailureRetryDuration DurationFunc
RateLimitExceededMaxRetries int
RateLimitExceededRetryDuration DurationFunc
}
func NewClientProfile() *ClientProfile {
return &ClientProfile{
HttpProfile: NewHttpProfile(),
SignMethod: "TC3-HMAC-SHA256",
UnsignedPayload: false,
Language: "zh-CN",
Debug: false,
// now is true, will become to false in future
DisableRegionBreaker: true,
BackupEndPoint: "",
BackupEndpoint: "",
}
}
@@ -0,0 +1,21 @@
package profile
type HttpProfile struct {
ReqMethod string
ReqTimeout int
Scheme string
RootDomain string
Endpoint string
// Deprecated, use Scheme instead
Protocol string
}
func NewHttpProfile() *HttpProfile {
return &HttpProfile{
ReqMethod: "POST",
ReqTimeout: 60,
Scheme: "HTTPS",
RootDomain: "",
Endpoint: "",
}
}
@@ -0,0 +1,96 @@
package common
import (
"os"
"path/filepath"
"runtime"
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
)
const (
EnvCredentialFile = "TENCENTCLOUD_CREDENTIALS_FILE"
)
type ProfileProvider struct {
}
// DefaultProfileProvider return a default Profile provider
// profile path :
// 1. The value of the environment variable TENCENTCLOUD_CREDENTIALS_FILE
// 2. linux: ~/.tencentcloud/credentials
// windows: \c:\Users\NAME\.tencentcloud\credentials
func DefaultProfileProvider() *ProfileProvider {
return &ProfileProvider{}
}
// getHomePath return home directory according to the system.
// if the environmental variables does not exist, it will return empty string
func getHomePath() string {
// Windows
if runtime.GOOS == "windows" {
return os.Getenv("USERPROFILE")
}
// *nix
return os.Getenv("HOME")
}
func getCredentialsFilePath() string {
homePath := getHomePath()
if homePath == "" {
return homePath
}
return filepath.Join(homePath, ".tencentcloud", "credentials")
}
func checkDefaultFile() (path string, err error) {
path = getCredentialsFilePath()
if path == "" {
return path, nil
}
_, err = os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return "", nil
}
return "", err
}
return path, nil
}
func (p *ProfileProvider) GetCredential() (CredentialIface, error) {
path, ok := os.LookupEnv(EnvCredentialFile)
// if not set custom file path, will use the default path
if !ok {
var err error
path, err = checkDefaultFile()
// only when the file exist but failed read it the err is not nil
if err != nil {
return nil, tcerr.NewTencentCloudSDKError(creErr, "Failed to find profile file,"+err.Error(), "")
}
// when the path is "" means the file dose not exist
if path == "" {
return nil, fileDoseNotExist
}
// if the EnvCredentialFile is set to "", will return an error
} else if path == "" {
return nil, tcerr.NewTencentCloudSDKError(creErr, "Environment variable '"+EnvCredentialFile+"' cannot be empty", "")
}
cfg, err := parse(path)
if err != nil {
return nil, err
}
sId := cfg.section("default").key("secret_id").string()
sKey := cfg.section("default").key("secret_key").string()
// if sId and sKey is "", but the credential file exist, means an error
if sId == "" || sKey == "" {
return nil, tcerr.NewTencentCloudSDKError(creErr, "Failed to parse profile file,please confirm whether it contains \"secret_id\" and \"secret_key\" in section: \"default\" ", "")
}
return &Credential{
SecretId: sId,
SecretKey: sKey,
Token: "",
}, nil
}
@@ -0,0 +1,21 @@
package common
import tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
var (
envNotSet = tcerr.NewTencentCloudSDKError(creErr, "could not find environmental variable", "")
fileDoseNotExist = tcerr.NewTencentCloudSDKError(creErr, "could not find config file", "")
noCvmRole = tcerr.NewTencentCloudSDKError(creErr, "get cvm role name failed, Please confirm whether the role is bound", "")
)
// Provider provide credential to build client.
//
// Now there are four kinds provider:
// EnvProvider : get credential from your Variable environment
// ProfileProvider : get credential from your profile
// CvmRoleProvider : get credential from your cvm role
// RoleArnProvider : get credential from your role arn
type Provider interface {
// GetCredential get the credential interface
GetCredential() (CredentialIface, error)
}
@@ -0,0 +1,41 @@
package common
import (
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
)
type ProviderChain struct {
Providers []Provider
}
// NewProviderChain returns a provider chain in your custom order
func NewProviderChain(providers []Provider) Provider {
return &ProviderChain{
Providers: providers,
}
}
// DefaultProviderChain returns a default provider chain and try to get credentials in the following order:
// 1. Environment variable
// 2. Profile
// 3. CvmRole
// If you want to customize the search order, please use the function NewProviderChain
func DefaultProviderChain() Provider {
return NewProviderChain([]Provider{DefaultEnvProvider(), DefaultProfileProvider(), DefaultCvmRoleProvider()})
}
func (c *ProviderChain) GetCredential() (CredentialIface, error) {
for _, provider := range c.Providers {
cred, err := provider.GetCredential()
if err != nil {
if err == envNotSet || err == fileDoseNotExist || err == noCvmRole {
continue
} else {
return nil, err
}
}
return cred, err
}
return nil, tcerr.NewTencentCloudSDKError(creErr, "no credential found in every providers", "")
}
@@ -0,0 +1,58 @@
package common
import (
"bytes"
"io"
"io/ioutil"
"log"
"net/http"
"time"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
)
const (
codeLimitExceeded = "RequestLimitExceeded"
tplRateLimitRetry = "[WARN] rate limit exceeded, retrying (%d/%d) in %f seconds: %s"
)
func (c *Client) sendWithRateLimitRetry(req *http.Request, retryable bool) (resp *http.Response, err error) {
// make sure maxRetries is more than 0
maxRetries := maxInt(c.profile.RateLimitExceededMaxRetries, 0)
durationFunc := safeDurationFunc(c.profile.RateLimitExceededRetryDuration)
var shadow []byte
for idx := 0; idx <= maxRetries; idx++ {
resp, err = c.sendWithNetworkFailureRetry(req, retryable)
if err != nil {
return
}
resp.Body, shadow = shadowRead(resp.Body)
err = tchttp.ParseErrorFromHTTPResponse(shadow)
// should not sleep on last request
if err, ok := err.(*errors.TencentCloudSDKError); ok && err.Code == codeLimitExceeded && idx < maxRetries {
duration := durationFunc(idx)
if c.debug {
log.Printf(tplRateLimitRetry, idx, maxRetries, duration.Seconds(), err.Error())
}
time.Sleep(duration)
continue
}
return resp, err
}
return resp, err
}
func shadowRead(reader io.ReadCloser) (io.ReadCloser, []byte) {
val, err := ioutil.ReadAll(reader)
if err != nil {
return reader, nil
}
return ioutil.NopCloser(bytes.NewBuffer(val)), val
}
@@ -0,0 +1,62 @@
// Copyright (c) 2018 Tencent Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package regions
const (
// 曼谷
Bangkok = "ap-bangkok"
// 北京
Beijing = "ap-beijing"
// 成都
Chengdu = "ap-chengdu"
// 重庆
Chongqing = "ap-chongqing"
// 广州
Guangzhou = "ap-guangzhou"
// 广州Open
GuangzhouOpen = "ap-guangzhou-open"
// 中国香港
HongKong = "ap-hongkong"
// 雅加达
Jakarta = "ap-jakarta"
// 孟买
Mumbai = "ap-mumbai"
// 首尔
Seoul = "ap-seoul"
// 上海
Shanghai = "ap-shanghai"
// 南京
Nanjing = "ap-nanjing"
// 上海金融
ShanghaiFSI = "ap-shanghai-fsi"
// 深圳金融
ShenzhenFSI = "ap-shenzhen-fsi"
// 新加坡
Singapore = "ap-singapore"
// 东京
Tokyo = "ap-tokyo"
// 法兰克福
Frankfurt = "eu-frankfurt"
// 莫斯科
Moscow = "eu-moscow"
// 阿什本
Ashburn = "na-ashburn"
// 硅谷
SiliconValley = "na-siliconvalley"
// 多伦多
Toronto = "na-toronto"
// 圣保罗
SaoPaulo = "sa-saopaulo"
)
@@ -0,0 +1,54 @@
package common
import (
"log"
"time"
)
type RoleArnCredential struct {
roleArn string
roleSessionName string
durationSeconds int64
expiredTime int64
token string
tmpSecretId string
tmpSecretKey string
source *RoleArnProvider
}
func (c *RoleArnCredential) GetSecretId() string {
if c.needRefresh() {
c.refresh()
}
return c.tmpSecretId
}
func (c *RoleArnCredential) GetSecretKey() string {
if c.needRefresh() {
c.refresh()
}
return c.tmpSecretKey
}
func (c *RoleArnCredential) GetToken() string {
if c.needRefresh() {
c.refresh()
}
return c.token
}
func (c *RoleArnCredential) needRefresh() bool {
if c.tmpSecretKey == "" || c.tmpSecretId == "" || c.token == "" || c.expiredTime <= time.Now().Unix() {
return true
}
return false
}
func (c *RoleArnCredential) refresh() {
newCre, err := c.source.GetCredential()
if err != nil {
log.Println(err)
}
*c = *newCre.(*RoleArnCredential)
}
@@ -0,0 +1,105 @@
package common
import (
"encoding/json"
"strconv"
"time"
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/regions"
)
const (
endpoint = "sts.tencentcloudapi.com"
service = "sts"
version = "2018-08-13"
region = regions.Guangzhou
defaultSessionName = "tencentcloud-go-sdk-"
action = "AssumeRole"
defaultDurationSeconds = 7200
)
type RoleArnProvider struct {
longSecretId string
longSecretKey string
roleArn string
roleSessionName string
durationSeconds int64
}
type stsRsp struct {
Response struct {
Credentials struct {
Token string `json:"Token"`
TmpSecretId string `json:"TmpSecretId"`
TmpSecretKey string `json:"TmpSecretKey"`
} `json:"Credentials"`
ExpiredTime int `json:"ExpiredTime"`
Expiration time.Time `json:"Expiration"`
RequestId string `json:"RequestId"`
} `json:"Response"`
}
func NewRoleArnProvider(secretId, secretKey, roleArn, sessionName string, duration int64) *RoleArnProvider {
return &RoleArnProvider{
longSecretId: secretId,
longSecretKey: secretKey,
roleArn: roleArn,
roleSessionName: sessionName,
durationSeconds: duration,
}
}
// DefaultRoleArnProvider returns a RoleArnProvider that use some default options:
// 1. roleSessionName will be "tencentcloud-go-sdk-" + timestamp
// 2. durationSeconds will be 7200s
func DefaultRoleArnProvider(secretId, secretKey, roleArn string) *RoleArnProvider {
return NewRoleArnProvider(secretId, secretKey, roleArn, defaultSessionName+strconv.FormatInt(time.Now().UnixNano()/1000, 10), defaultDurationSeconds)
}
func (r *RoleArnProvider) GetCredential() (CredentialIface, error) {
if r.durationSeconds > 43200 || r.durationSeconds <= 0 {
return nil, tcerr.NewTencentCloudSDKError(creErr, "Assume Role durationSeconds should be in the range of 0~43200s", "")
}
credential := NewCredential(r.longSecretId, r.longSecretKey)
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = endpoint
cpf.HttpProfile.ReqMethod = "POST"
client := NewCommonClient(credential, region, cpf)
request := tchttp.NewCommonRequest(service, version, action)
params := map[string]interface{}{
"RoleArn": r.roleArn,
"RoleSessionName": r.roleSessionName,
"DurationSeconds": r.durationSeconds,
}
err := request.SetActionParameters(params)
if err != nil {
return nil, err
}
response := tchttp.NewCommonResponse()
err = client.Send(request, response)
if err != nil {
return nil, err
}
rspSt := new(stsRsp)
if err = json.Unmarshal(response.GetBody(), rspSt); err != nil {
return nil, tcerr.NewTencentCloudSDKError(creErr, err.Error(), "")
}
return &RoleArnCredential{
roleArn: r.roleArn,
roleSessionName: r.roleSessionName,
durationSeconds: r.durationSeconds,
expiredTime: int64(rspSt.Response.ExpiredTime) - r.durationSeconds/10*9, // credential's actual duration time is 1/10 of the original
token: rspSt.Response.Credentials.Token,
tmpSecretId: rspSt.Response.Credentials.TmpSecretId,
tmpSecretKey: rspSt.Response.Credentials.TmpSecretKey,
source: r,
}, nil
}
@@ -0,0 +1,27 @@
package common
type sections struct {
contains map[string]*section
}
func (ss sections) section(name string) *section {
s, ok := ss.contains[name]
if !ok {
s = new(section)
ss.contains[name] = s
}
return s
}
type section struct {
content map[string]*value
}
func (s *section) key(name string) *value {
v, ok := s.content[name]
if !ok {
v = new(value)
s.content[name] = v
}
return v
}
@@ -0,0 +1,90 @@
package common
import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"sort"
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
)
const (
SHA256 = "HmacSHA256"
SHA1 = "HmacSHA1"
)
func Sign(s, secretKey, method string) string {
hashed := hmac.New(sha1.New, []byte(secretKey))
if method == SHA256 {
hashed = hmac.New(sha256.New, []byte(secretKey))
}
hashed.Write([]byte(s))
return base64.StdEncoding.EncodeToString(hashed.Sum(nil))
}
func sha256hex(s string) string {
b := sha256.Sum256([]byte(s))
return hex.EncodeToString(b[:])
}
func hmacsha256(s, key string) string {
hashed := hmac.New(sha256.New, []byte(key))
hashed.Write([]byte(s))
return string(hashed.Sum(nil))
}
func signRequest(request tchttp.Request, credential CredentialIface, method string) (err error) {
if method != SHA256 {
method = SHA1
}
checkAuthParams(request, credential, method)
s := getStringToSign(request)
signature := Sign(s, credential.GetSecretKey(), method)
request.GetParams()["Signature"] = signature
return
}
func checkAuthParams(request tchttp.Request, credential CredentialIface, method string) {
params := request.GetParams()
params["SecretId"] = credential.GetSecretId()
if token := credential.GetToken(); len(token) != 0 {
params["Token"] = token
}
params["SignatureMethod"] = method
delete(params, "Signature")
}
func getStringToSign(request tchttp.Request) string {
method := request.GetHttpMethod()
domain := request.GetDomain()
path := request.GetPath()
var buf bytes.Buffer
buf.WriteString(method)
buf.WriteString(domain)
buf.WriteString(path)
buf.WriteString("?")
params := request.GetParams()
// sort params
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
for i := range keys {
k := keys[i]
buf.WriteString(k)
buf.WriteString("=")
buf.WriteString(params[k])
buf.WriteString("&")
}
buf.Truncate(buf.Len() - 1)
return buf.String()
}
@@ -0,0 +1,5 @@
# wrong section name
[]
invalidKey1 =
;wrong section
[default
@@ -0,0 +1,11 @@
app=tencentcloud-sdk-go
# default
[default]
key1 = value1
key2 = 2
;section 2
[custom]
customKey1 = 3.1415
customKey2 = true
@@ -0,0 +1,95 @@
package common
func IntPtr(v int) *int {
return &v
}
func Int64Ptr(v int64) *int64 {
return &v
}
func UintPtr(v uint) *uint {
return &v
}
func Uint64Ptr(v uint64) *uint64 {
return &v
}
func Float64Ptr(v float64) *float64 {
return &v
}
func BoolPtr(v bool) *bool {
return &v
}
func StringPtr(v string) *string {
return &v
}
func StringValues(ptrs []*string) []string {
values := make([]string, len(ptrs))
for i := 0; i < len(ptrs); i++ {
if ptrs[i] != nil {
values[i] = *ptrs[i]
}
}
return values
}
func IntPtrs(vals []int) []*int {
ptrs := make([]*int, len(vals))
for i := 0; i < len(vals); i++ {
ptrs[i] = &vals[i]
}
return ptrs
}
func Int64Ptrs(vals []int64) []*int64 {
ptrs := make([]*int64, len(vals))
for i := 0; i < len(vals); i++ {
ptrs[i] = &vals[i]
}
return ptrs
}
func UintPtrs(vals []uint) []*uint {
ptrs := make([]*uint, len(vals))
for i := 0; i < len(vals); i++ {
ptrs[i] = &vals[i]
}
return ptrs
}
func Uint64Ptrs(vals []uint64) []*uint64 {
ptrs := make([]*uint64, len(vals))
for i := 0; i < len(vals); i++ {
ptrs[i] = &vals[i]
}
return ptrs
}
func Float64Ptrs(vals []float64) []*float64 {
ptrs := make([]*float64, len(vals))
for i := 0; i < len(vals); i++ {
ptrs[i] = &vals[i]
}
return ptrs
}
func BoolPtrs(vals []bool) []*bool {
ptrs := make([]*bool, len(vals))
for i := 0; i < len(vals); i++ {
ptrs[i] = &vals[i]
}
return ptrs
}
func StringPtrs(vals []string) []*string {
ptrs := make([]*string, len(vals))
for i := 0; i < len(vals); i++ {
ptrs[i] = &vals[i]
}
return ptrs
}
@@ -0,0 +1,63 @@
package common
import (
"fmt"
"strconv"
tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
)
type value struct {
raw string
}
func (v *value) int() (int, error) {
i, e := strconv.Atoi(v.raw)
if e != nil {
msg := fmt.Sprintf("failed to parsing %s to int: %s", v.raw, e.Error())
e = tcerr.NewTencentCloudSDKError(iniErr, msg, "")
}
return i, e
}
func (v *value) int64() (int64, error) {
i, e := strconv.ParseInt(v.raw, 10, 64)
if e != nil {
msg := fmt.Sprintf("failed to parsing %s to int64: %s", v.raw, e.Error())
e = tcerr.NewTencentCloudSDKError(iniErr, msg, "")
}
return i, e
}
func (v *value) string() string {
return v.raw
}
func (v *value) bool() (bool, error) {
switch v.raw {
case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On":
return true, nil
case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off":
return false, nil
}
errorMsg := fmt.Sprintf("failed to parsing \"%s\" to Bool: invalid syntax", v.raw)
return false, tcerr.NewTencentCloudSDKError(iniErr, errorMsg, "")
}
func (v *value) float32() (float32, error) {
f, e := strconv.ParseFloat(v.raw, 32)
if e != nil {
msg := fmt.Sprintf("failed to parse %s to Float32: %s", v.raw, e.Error())
e = tcerr.NewTencentCloudSDKError(iniErr, msg, "")
}
return float32(f), e
}
func (v *value) float64() (float64, error) {
f, e := strconv.ParseFloat(v.raw, 64)
if e != nil {
msg := fmt.Sprintf("failed to parse %s to Float64: %s", v.raw, e.Error())
e = tcerr.NewTencentCloudSDKError(iniErr, msg, "")
}
return f, e
}
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2017-2018 Tencent Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,115 @@
// Copyright (c) 2017-2018 THL A29 Limited, a Tencent company. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v20181119
const (
// 此产品的特有错误码
// 帐号已欠费。
FAILEDOPERATION_ARREARSERROR = "FailedOperation.ArrearsError"
// 今日次数达到限制。
FAILEDOPERATION_COUNTLIMITERROR = "FailedOperation.CountLimitError"
// 检测失败。
FAILEDOPERATION_DETECTFAILED = "FailedOperation.DetectFailed"
// 文件下载失败。
FAILEDOPERATION_DOWNLOADERROR = "FailedOperation.DownLoadError"
// 图片内容为空。
FAILEDOPERATION_EMPTYIMAGEERROR = "FailedOperation.EmptyImageError"
// 引擎识别超时。
FAILEDOPERATION_ENGINERECOGNIZETIMEOUT = "FailedOperation.EngineRecognizeTimeout"
// 身份证信息不合法(身份证号、姓名字段校验非法等)。
FAILEDOPERATION_IDCARDINFOILLEGAL = "FailedOperation.IdCardInfoIllegal"
// 图片模糊。
FAILEDOPERATION_IMAGEBLUR = "FailedOperation.ImageBlur"
// 图片解码失败。
FAILEDOPERATION_IMAGEDECODEFAILED = "FailedOperation.ImageDecodeFailed"
// 照片未检测到名片。
FAILEDOPERATION_IMAGENOBUSINESSCARD = "FailedOperation.ImageNoBusinessCard"
// 图片中未检测到身份证。
FAILEDOPERATION_IMAGENOIDCARD = "FailedOperation.ImageNoIdCard"
// 图片中未检测到文本。
FAILEDOPERATION_IMAGENOTEXT = "FailedOperation.ImageNoText"
// 图片尺寸过大,请参考输出参数中关于图片大小限制的说明。
FAILEDOPERATION_IMAGESIZETOOLARGE = "FailedOperation.ImageSizeTooLarge"
// 发票数据不一致。
FAILEDOPERATION_INVOICEMISMATCH = "FailedOperation.InvoiceMismatch"
// 输入的Language不支持。
FAILEDOPERATION_LANGUAGENOTSUPPORT = "FailedOperation.LanguageNotSupport"
// 照片中存在多张卡。
FAILEDOPERATION_MULTICARDERROR = "FailedOperation.MultiCardError"
// 非香港身份证。
FAILEDOPERATION_NOHKIDCARD = "FailedOperation.NoHKIDCard"
// 非马来身份证。
FAILEDOPERATION_NOMASIDCARD = "FailedOperation.NoMASIDCard"
// 非护照。
FAILEDOPERATION_NOPASSPORT = "FailedOperation.NoPassport"
// OCR识别失败。
FAILEDOPERATION_OCRFAILED = "FailedOperation.OcrFailed"
// 查询无记录。
FAILEDOPERATION_QUERYNORECORD = "FailedOperation.QueryNoRecord"
// 未知错误。
FAILEDOPERATION_UNKNOWERROR = "FailedOperation.UnKnowError"
// 服务未开通。
FAILEDOPERATION_UNOPENERROR = "FailedOperation.UnOpenError"
// 内部错误。
INTERNALERROR = "InternalError"
// Config不是有效的JSON格式。
INVALIDPARAMETER_CONFIGFORMATERROR = "InvalidParameter.ConfigFormatError"
// 图片解码失败。
INVALIDPARAMETER_ENGINEIMAGEDECODEFAILED = "InvalidParameter.EngineImageDecodeFailed"
// 无效的GTIN。
INVALIDPARAMETER_INVALIDGTINERROR = "InvalidParameter.InvalidGTINError"
// 参数值错误。
INVALIDPARAMETERVALUE_INVALIDPARAMETERVALUELIMIT = "InvalidParameterValue.InvalidParameterValueLimit"
// 文件内容太大。
LIMITEXCEEDED_TOOLARGEFILEERROR = "LimitExceeded.TooLargeFileError"
// 发票不存在。
RESOURCENOTFOUND_NOINVOICE = "ResourceNotFound.NoInvoice"
// 不支持当天发票查询。
RESOURCENOTFOUND_NOTSUPPORTCURRENTINVOICEQUERY = "ResourceNotFound.NotSupportCurrentInvoiceQuery"
// 计费状态异常。
RESOURCESSOLDOUT_CHARGESTATUSEXCEPTION = "ResourcesSoldOut.ChargeStatusException"
)
File diff suppressed because it is too large Load Diff