package sdk import ( "bytes" "context" "crypto/tls" "fmt" "golib.gaore.com/GaoreGo/haiwai-common-sdk-go/sdk/auth" "golib.gaore.com/GaoreGo/haiwai-common-sdk-go/sdk/auth/credentials" "golib.gaore.com/GaoreGo/haiwai-common-sdk-go/sdk/requests" "golib.gaore.com/GaoreGo/haiwai-common-sdk-go/sdk/responses" "golib.gaore.com/GaoreGo/haiwai-common-sdk-go/sdk/utils" "net" "net/http" "net/http/httputil" "net/url" "os" "regexp" "runtime" "strings" "time" ) var Version = "0.0.1" var defaultConnectTimeout = 5 * time.Second var defaultReadTimeout = 10 * time.Second var defaultDomain = ".gaore.com.hk" var DefaultUserAgent = fmt.Sprintf("GaoreGoSdk (%s;%s) Golang/%s Core/%s", runtime.GOOS, runtime.GOARCH, strings.Trim(runtime.Version(), "go"), Version) var debug utils.Debug func init() { debug = utils.Init("sdk") } type Client struct { Host string httpClient *http.Client isInsecure bool signer auth.Signer readTimeout time.Duration connectTimeout time.Duration config *Config httpProxy string httpsProxy string noProxy string } func (client *Client) GetNoProxy() string { return client.noProxy } func (client *Client) SetNoProxy(noProxy string) { client.noProxy = noProxy } func (client *Client) GetHttpsProxy() string { return client.httpsProxy } func (client *Client) SetHttpsProxy(httpsProxy string) { client.httpsProxy = httpsProxy } func (client *Client) SetHttpProxy(httpProxy string) { client.httpProxy = httpProxy } func (client *Client) GetHttpProxy() string { return client.httpProxy } func (client *Client) GetHTTPSInsecure() bool { return client.isInsecure } func (client *Client) InitClientConfig() (config *Config) { if client.config != nil { return client.config } else { return NewConfig() } } func (client *Client) InitWithAccessKey(accessKeyId, accessKeySecret, accessKeyFrom string) (err error) { config := client.InitWithConfig() credential := &credentials.BaseCredential{ AccessKeyId: accessKeyId, AccessKeySecret: accessKeySecret, AccessKeyFrom: accessKeyFrom, } return client.InitWithOptions(config, credential) } func (client *Client) InitWithAliAppcode(accessKeyId, accessKeySecret string, env ...string) (err error) { config := client.InitWithConfig() credential := credentials.NewAliAppcodeCredential(accessKeyId, accessKeySecret) if len(env) > 0 { config.Env = env[0] } return client.InitWithOptions(config, credential) } func (client *Client) InitWithStsToken(accessKeyId, accessKeySecret, accessKeyFrom string) (err error) { config := client.InitWithConfig() credential := &credentials.StdTokenCredential{ AccessKeyId: accessKeyId, AccessKeySecret: accessKeySecret, AccessKeyFrom: accessKeyFrom, } return client.InitWithOptions(config, credential) } func (client *Client) InitWithOptions(config *Config, credential auth.Credential) (err error) { client.httpClient = &http.Client{} client.config = config if config.Transport != nil { client.httpClient.Transport = config.Transport } else if config.HttpTransport != nil { client.httpClient.Transport = config.HttpTransport } if config.Timeout > 0 { client.httpClient.Timeout = config.Timeout } client.signer, err = auth.NewSignerWithCredential(credential, client.ProcessCommonRequestWithSigner) return } func (client *Client) InitWithConfig() (config *Config) { if client.config != nil { return client.config } else { return NewConfig() } } func (client *Client) ProcessCommonRequestWithSigner(request *requests.CommonRequest, signerInterface interface{}) (response *responses.CommonResponse, err error) { if signer, isSigner := signerInterface.(auth.Signer); isSigner { response = responses.NewCommonResponse() err = client.DoActionWithSigner(request, response, signer) return } panic("should not be here") } func Timeout(connectTimeout time.Duration) func(ctx context.Context, net, addr string) (c net.Conn, err error) { return func(ctx context.Context, network, address string) (c net.Conn, err error) { return (&net.Dialer{ Timeout: connectTimeout, DualStack: true, }).DialContext(ctx, network, address) } } func (client *Client) setTimeOut(request requests.AcsRequest) { readTimeout, connectTimeout := client.getTimeOut(request) client.httpClient.Timeout = readTimeout if trans, ok := client.httpClient.Transport.(*http.Transport); ok && trans != nil { trans.DialContext = Timeout(connectTimeout) client.httpClient.Transport = trans } else if client.httpClient.Transport == nil { client.httpClient.Transport = &http.Transport{ DialContext: Timeout(connectTimeout), } } } func (client *Client) getTimeOut(request requests.AcsRequest) (time.Duration, time.Duration) { readTimeOut := defaultReadTimeout connectTimeOut := defaultConnectTimeout reqReadTimeout := request.GetReadTimeout() reqConnectTimeout := request.GetConnectTimeout() if reqReadTimeout != 0*time.Millisecond { readTimeOut = reqReadTimeout } else if client.readTimeout != 0*time.Microsecond { readTimeOut = client.readTimeout } else if client.httpClient.Timeout != 0 { readTimeOut = client.httpClient.Timeout } if reqConnectTimeout != 0*time.Microsecond { connectTimeOut = reqConnectTimeout } else if client.connectTimeout != 0*time.Millisecond { connectTimeOut = client.connectTimeout } return readTimeOut, connectTimeOut } func (client *Client) getHTTPSInsecure(request requests.AcsRequest) (insecure bool) { if request.GetHTTPSInsecure() != nil { insecure = *request.GetHTTPSInsecure() } else { insecure = client.GetHTTPSInsecure() } return } func (client *Client) DoAction(request requests.AcsRequest, response responses.AcsResponse) (err error) { return client.DoActionWithSigner(request, response, nil) } func (client *Client) DoActionWithSigner(request requests.AcsRequest, response responses.AcsResponse, signer auth.Signer) (err error) { httpRequest, err := client.buildRequestWithSigner(request, signer) if err != nil { return err } client.setTimeOut(request) proxy, err := client.getHttpProxy(httpRequest.URL.Scheme) if err != nil { return err } noProxy := client.getNoProxy(httpRequest.URL.Scheme) var flag bool for _, value := range noProxy { if strings.HasPrefix(value, "*") { value = fmt.Sprint(".%s", value) } noProxyReg, err := regexp.Compile(value) if err != nil { return err } if noProxyReg.MatchString(httpRequest.Host) { flag = true break } } if trans, ok := client.httpClient.Transport.(*http.Transport); ok && trans != nil { if trans.TLSClientConfig != nil { trans.TLSClientConfig.InsecureSkipVerify = client.getHTTPSInsecure(request) } else { trans.TLSClientConfig = &tls.Config{ InsecureSkipVerify: client.getHTTPSInsecure(request), } } if proxy != nil && !flag { trans.Proxy = http.ProxyURL(proxy) } client.httpClient.Transport = trans } dump, err := httputil.DumpRequest(httpRequest, true) debug("client %s", bytes.NewBuffer(dump).String()) var httpResponse *http.Response httpResponse, err = hookDo(client.httpClient.Do)(httpRequest) if err != nil { return } err = responses.Unmarshal(response, httpResponse, request.GetAcceptFormat()) return } func (client *Client) buildRequestWithSigner(request requests.AcsRequest, signer auth.Signer) (httpRequest *http.Request, err error) { // init param domain := request.GetDomain() if strings.Index(domain.Default, ".") < 0 { domain.Default += defaultDomain request.SetDomain(domain) } if request.GetScheme() == "" { request.SetScheme(client.config.Scheme) } if request.GetEnv() == "" && client.config.Env != "" { request.SetEnv(client.config.Env) } err = requests.InitParam(request) if err != nil { return } // build signature var finalSigner auth.Signer if signer != nil { finalSigner = signer } else { finalSigner = client.signer } err = auth.Sign(request, finalSigner) if err != nil { return } // build request requestMethod := request.GetMethod() requestUrl := request.BuildUrl() body := request.GetBodyReader() httpRequest, err = http.NewRequest(requestMethod, requestUrl, body) if err != nil { return } for key, val := range request.GetHeaders() { httpRequest.Header[key] = []string{val} } if host, isContainsHost := request.GetHeaders()["host"]; isContainsHost { httpRequest.Host = host } userAgent := DefaultUserAgent httpRequest.Header.Set("User-Agent", userAgent) return } func (client *Client) getHttpProxy(scheme string) (proxy *url.URL, err error) { switch scheme { case "https": if client.GetHttpsProxy() != "" { proxy, err = url.Parse(client.httpsProxy) } else if rawurl := os.Getenv("HTTPS_PROXY"); rawurl != "" { proxy, err = url.Parse(rawurl) } else if rawurl := os.Getenv("https_proxy"); rawurl != "" { proxy, err = url.Parse(rawurl) } default: if client.GetHttpProxy() != "" { proxy, err = url.Parse(client.httpProxy) } else if rawurl := os.Getenv("HTTP_PROXY"); rawurl != "" { proxy, err = url.Parse(rawurl) } else if rawurl := os.Getenv("http_proxy"); rawurl != "" { proxy, err = url.Parse(rawurl) } } return } func (client *Client) getNoProxy(scheme string) []string { var urls []string if client.GetNoProxy() != "" { urls = strings.Split(client.noProxy, ",") } else if rawurl := os.Getenv("NO_PROXY"); rawurl != "" { urls = strings.Split(rawurl, ",") } else if rawurl := os.Getenv("no_proxy"); rawurl != "" { urls = strings.Split(rawurl, ",") } return urls } func hookDo(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { return fn }