client.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. package common
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io/ioutil"
  6. "log"
  7. "net/http"
  8. "net/url"
  9. "strings"
  10. "time"
  11. "github.com/denverdino/aliyungo/util"
  12. )
  13. // RemovalPolicy.N add index to array item
  14. // RemovalPolicy=["a", "b"] => RemovalPolicy.1="a" RemovalPolicy.2="b"
  15. type FlattenArray []string
  16. // string contains underline which will be replaced with dot
  17. // SystemDisk_Category => SystemDisk.Category
  18. type UnderlineString string
  19. // A Client represents a client of ECS services
  20. type Client struct {
  21. AccessKeyId string //Access Key Id
  22. AccessKeySecret string //Access Key Secret
  23. debug bool
  24. httpClient *http.Client
  25. endpoint string
  26. version string
  27. serviceCode string
  28. regionID Region
  29. businessInfo string
  30. userAgent string
  31. }
  32. // NewClient creates a new instance of ECS client
  33. func (client *Client) Init(endpoint, version, accessKeyId, accessKeySecret string) {
  34. client.AccessKeyId = accessKeyId
  35. client.AccessKeySecret = accessKeySecret + "&"
  36. client.debug = false
  37. client.httpClient = &http.Client{}
  38. client.endpoint = endpoint
  39. client.version = version
  40. }
  41. func (client *Client) NewInit(endpoint, version, accessKeyId, accessKeySecret, serviceCode string, regionID Region) {
  42. client.Init(endpoint, version, accessKeyId, accessKeySecret)
  43. client.serviceCode = serviceCode
  44. client.regionID = regionID
  45. client.setEndpointByLocation(regionID, serviceCode, accessKeyId, accessKeySecret)
  46. }
  47. //NewClient using location service
  48. func (client *Client) setEndpointByLocation(region Region, serviceCode, accessKeyId, accessKeySecret string) {
  49. locationClient := NewLocationClient(accessKeyId, accessKeySecret)
  50. ep := locationClient.DescribeOpenAPIEndpoint(region, serviceCode)
  51. if ep == "" {
  52. ep = loadEndpointFromFile(region, serviceCode)
  53. }
  54. if ep != "" {
  55. client.endpoint = ep
  56. }
  57. }
  58. // SetEndpoint sets custom endpoint
  59. func (client *Client) SetEndpoint(endpoint string) {
  60. client.endpoint = endpoint
  61. }
  62. // SetEndpoint sets custom version
  63. func (client *Client) SetVersion(version string) {
  64. client.version = version
  65. }
  66. func (client *Client) SetRegionID(regionID Region) {
  67. client.regionID = regionID
  68. }
  69. //SetServiceCode sets serviceCode
  70. func (client *Client) SetServiceCode(serviceCode string) {
  71. client.serviceCode = serviceCode
  72. }
  73. // SetAccessKeyId sets new AccessKeyId
  74. func (client *Client) SetAccessKeyId(id string) {
  75. client.AccessKeyId = id
  76. }
  77. // SetAccessKeySecret sets new AccessKeySecret
  78. func (client *Client) SetAccessKeySecret(secret string) {
  79. client.AccessKeySecret = secret + "&"
  80. }
  81. // SetDebug sets debug mode to log the request/response message
  82. func (client *Client) SetDebug(debug bool) {
  83. client.debug = debug
  84. }
  85. // SetBusinessInfo sets business info to log the request/response message
  86. func (client *Client) SetBusinessInfo(businessInfo string) {
  87. if strings.HasPrefix(businessInfo, "/") {
  88. client.businessInfo = businessInfo
  89. } else if businessInfo != "" {
  90. client.businessInfo = "/" + businessInfo
  91. }
  92. }
  93. // SetUserAgent sets user agent to the request/response message
  94. func (client *Client) SetUserAgent(userAgent string) {
  95. client.userAgent = userAgent
  96. }
  97. // Invoke sends the raw HTTP request for ECS services
  98. func (client *Client) Invoke(action string, args interface{}, response interface{}) error {
  99. request := Request{}
  100. request.init(client.version, action, client.AccessKeyId)
  101. query := util.ConvertToQueryValues(request)
  102. util.SetQueryValues(args, &query)
  103. // Sign request
  104. signature := util.CreateSignatureForRequest(ECSRequestMethod, &query, client.AccessKeySecret)
  105. // Generate the request URL
  106. requestURL := client.endpoint + "?" + query.Encode() + "&Signature=" + url.QueryEscape(signature)
  107. httpReq, err := http.NewRequest(ECSRequestMethod, requestURL, nil)
  108. if err != nil {
  109. return GetClientError(err)
  110. }
  111. // TODO move to util and add build val flag
  112. httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo)
  113. httpReq.Header.Set("User-Agent", httpReq.UserAgent()+ " " +client.userAgent)
  114. t0 := time.Now()
  115. httpResp, err := client.httpClient.Do(httpReq)
  116. t1 := time.Now()
  117. if err != nil {
  118. return GetClientError(err)
  119. }
  120. statusCode := httpResp.StatusCode
  121. if client.debug {
  122. log.Printf("Invoke %s %s %d (%v)", ECSRequestMethod, requestURL, statusCode, t1.Sub(t0))
  123. }
  124. defer httpResp.Body.Close()
  125. body, err := ioutil.ReadAll(httpResp.Body)
  126. if err != nil {
  127. return GetClientError(err)
  128. }
  129. if client.debug {
  130. var prettyJSON bytes.Buffer
  131. err = json.Indent(&prettyJSON, body, "", " ")
  132. log.Println(string(prettyJSON.Bytes()))
  133. }
  134. if statusCode >= 400 && statusCode <= 599 {
  135. errorResponse := ErrorResponse{}
  136. err = json.Unmarshal(body, &errorResponse)
  137. ecsError := &Error{
  138. ErrorResponse: errorResponse,
  139. StatusCode: statusCode,
  140. }
  141. return ecsError
  142. }
  143. err = json.Unmarshal(body, response)
  144. //log.Printf("%++v", response)
  145. if err != nil {
  146. return GetClientError(err)
  147. }
  148. return nil
  149. }
  150. // Invoke sends the raw HTTP request for ECS services
  151. func (client *Client) InvokeByFlattenMethod(action string, args interface{}, response interface{}) error {
  152. request := Request{}
  153. request.init(client.version, action, client.AccessKeyId)
  154. query := util.ConvertToQueryValues(request)
  155. util.SetQueryValueByFlattenMethod(args, &query)
  156. // Sign request
  157. signature := util.CreateSignatureForRequest(ECSRequestMethod, &query, client.AccessKeySecret)
  158. // Generate the request URL
  159. requestURL := client.endpoint + "?" + query.Encode() + "&Signature=" + url.QueryEscape(signature)
  160. httpReq, err := http.NewRequest(ECSRequestMethod, requestURL, nil)
  161. if err != nil {
  162. return GetClientError(err)
  163. }
  164. // TODO move to util and add build val flag
  165. httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo)
  166. httpReq.Header.Set("User-Agent", httpReq.UserAgent()+ " " +client.userAgent)
  167. t0 := time.Now()
  168. httpResp, err := client.httpClient.Do(httpReq)
  169. t1 := time.Now()
  170. if err != nil {
  171. return GetClientError(err)
  172. }
  173. statusCode := httpResp.StatusCode
  174. if client.debug {
  175. log.Printf("Invoke %s %s %d (%v)", ECSRequestMethod, requestURL, statusCode, t1.Sub(t0))
  176. }
  177. defer httpResp.Body.Close()
  178. body, err := ioutil.ReadAll(httpResp.Body)
  179. if err != nil {
  180. return GetClientError(err)
  181. }
  182. if client.debug {
  183. var prettyJSON bytes.Buffer
  184. err = json.Indent(&prettyJSON, body, "", " ")
  185. log.Println(string(prettyJSON.Bytes()))
  186. }
  187. if statusCode >= 400 && statusCode <= 599 {
  188. errorResponse := ErrorResponse{}
  189. err = json.Unmarshal(body, &errorResponse)
  190. ecsError := &Error{
  191. ErrorResponse: errorResponse,
  192. StatusCode: statusCode,
  193. }
  194. return ecsError
  195. }
  196. err = json.Unmarshal(body, response)
  197. //log.Printf("%++v", response)
  198. if err != nil {
  199. return GetClientError(err)
  200. }
  201. return nil
  202. }
  203. // Invoke sends the raw HTTP request for ECS services
  204. //改进了一下上面那个方法,可以使用各种Http方法
  205. //2017.1.30 增加了一个path参数,用来拓展访问的地址
  206. func (client *Client) InvokeByAnyMethod(method, action, path string, args interface{}, response interface{}) error {
  207. request := Request{}
  208. request.init(client.version, action, client.AccessKeyId)
  209. data := util.ConvertToQueryValues(request)
  210. util.SetQueryValues(args, &data)
  211. // Sign request
  212. signature := util.CreateSignatureForRequest(method, &data, client.AccessKeySecret)
  213. data.Add("Signature", signature)
  214. // Generate the request URL
  215. var (
  216. httpReq *http.Request
  217. err error
  218. )
  219. if method == http.MethodGet {
  220. requestURL := client.endpoint + path + "?" + data.Encode()
  221. //fmt.Println(requestURL)
  222. httpReq, err = http.NewRequest(method, requestURL, nil)
  223. } else {
  224. //fmt.Println(client.endpoint + path)
  225. httpReq, err = http.NewRequest(method, client.endpoint+path, strings.NewReader(data.Encode()))
  226. httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  227. }
  228. if err != nil {
  229. return GetClientError(err)
  230. }
  231. // TODO move to util and add build val flag
  232. httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo)
  233. httpReq.Header.Set("User-Agent", httpReq.Header.Get("User-Agent")+ " " +client.userAgent)
  234. t0 := time.Now()
  235. httpResp, err := client.httpClient.Do(httpReq)
  236. t1 := time.Now()
  237. if err != nil {
  238. return GetClientError(err)
  239. }
  240. statusCode := httpResp.StatusCode
  241. if client.debug {
  242. log.Printf("Invoke %s %s %d (%v) %v", ECSRequestMethod, client.endpoint, statusCode, t1.Sub(t0), data.Encode())
  243. }
  244. defer httpResp.Body.Close()
  245. body, err := ioutil.ReadAll(httpResp.Body)
  246. if err != nil {
  247. return GetClientError(err)
  248. }
  249. if client.debug {
  250. var prettyJSON bytes.Buffer
  251. err = json.Indent(&prettyJSON, body, "", " ")
  252. log.Println(string(prettyJSON.Bytes()))
  253. }
  254. if statusCode >= 400 && statusCode <= 599 {
  255. errorResponse := ErrorResponse{}
  256. err = json.Unmarshal(body, &errorResponse)
  257. ecsError := &Error{
  258. ErrorResponse: errorResponse,
  259. StatusCode: statusCode,
  260. }
  261. return ecsError
  262. }
  263. err = json.Unmarshal(body, response)
  264. //log.Printf("%++v", response)
  265. if err != nil {
  266. return GetClientError(err)
  267. }
  268. return nil
  269. }
  270. // GenerateClientToken generates the Client Token with random string
  271. func (client *Client) GenerateClientToken() string {
  272. return util.CreateRandomString()
  273. }
  274. func GetClientErrorFromString(str string) error {
  275. return &Error{
  276. ErrorResponse: ErrorResponse{
  277. Code: "AliyunGoClientFailure",
  278. Message: str,
  279. },
  280. StatusCode: -1,
  281. }
  282. }
  283. func GetClientError(err error) error {
  284. return GetClientErrorFromString(err.Error())
  285. }