httpcaller.go 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /**
  2. Copyright 1999-2017 Alibaba Group Holding Ltd.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. CSB-HTTP-SDK based on GO language.
  13. */
  14. package csbhttp
  15. import (
  16. //
  17. "fmt"
  18. "crypto/tls"
  19. "bytes"
  20. "encoding/json"
  21. "io/ioutil"
  22. "net"
  23. "net/http"
  24. "net/url"
  25. "strings"
  26. "sync"
  27. "time"
  28. )
  29. var settingMutex sync.Mutex
  30. const (
  31. CSB_SDK_VERSION = "1.1.0"
  32. API_NAME_KEY = "_api_name"
  33. VERSION_KEY = "_api_version"
  34. ACCESS_KEY = "_api_access_key"
  35. SECRET_KEY = "_api_secret_key"
  36. SIGNATURE_KEY = "_api_signature"
  37. TIMESTAMP_KEY = "_api_timestamp"
  38. RESTFUL_PATH_SIGNATURE_KEY = "csb_restful_path_signature_key" //TODO: fix the terrible key name!
  39. )
  40. /**
  41. CSBHttp的基本设置结构
  42. */
  43. type CSBHTTPSettings struct {
  44. ShowDebug bool // "运行时是否显示调试信息"
  45. UserAgent string // "调用CSB服务的客户端代理, 默认为 csbBroker"
  46. ConnectTimeout time.Duration // "连接超时时间"
  47. ReadWriteTimeout time.Duration // "读写超时时间"
  48. Retries int // if set to -1 means will retry forever
  49. CareResponseHttpHeader bool // if return the response http headers
  50. SignPath bool
  51. /* TODO:support the following fields
  52. TLSClientConfig *tls.Config
  53. Proxy func(*http.Request) (*url.URL, error)
  54. Transport http.RoundTripper
  55. CheckRedirect func(req *http.Request, via []*http.Request) error
  56. EnableCookie bool
  57. */
  58. }
  59. var defaultSetting = CSBHTTPSettings{
  60. ShowDebug: true,
  61. UserAgent: "csbBroker",
  62. ConnectTimeout: 60 * time.Second,
  63. ReadWriteTimeout: 60 * time.Second,
  64. CareResponseHttpHeader: true,
  65. }
  66. /**
  67. 定义自己的http属性的结构来覆盖默认的设置
  68. */
  69. func SetDefaultSetting(setting CSBHTTPSettings) {
  70. settingMutex.Lock()
  71. defer settingMutex.Unlock()
  72. defaultSetting = setting
  73. }
  74. /**
  75. 内部方法: 拼接请求参数
  76. */
  77. func appendParams(reqUrl string, params string) string {
  78. if strings.Contains(reqUrl, "?") {
  79. return reqUrl + "&" + params
  80. } else {
  81. return reqUrl + "?" + params
  82. }
  83. }
  84. /**
  85. 内部方法: 将请求串中的请求参数装换为map
  86. */
  87. func parseUrlParamsMap(reqUrl string) (params map[string]string, err *HttpCallerException) {
  88. params = make(map[string]string) //must init the map
  89. if strings.Contains(reqUrl, "?") {
  90. i := strings.Index(reqUrl, "?")
  91. reqUrl = string(reqUrl[i+1:])
  92. fmt.Println(reqUrl)
  93. kvs := strings.Split(reqUrl, "&")
  94. if len(kvs) > 0 {
  95. for _, kv := range kvs {
  96. i = strings.Index(kv, "=")
  97. if i >= 0 {
  98. params[string(kv[0:i])] = string(kv[i+1:])
  99. } else {
  100. //TODO: write or throw exception
  101. fmt.Errorf("bad kv pair:", kv)
  102. }
  103. }
  104. }
  105. }
  106. return params, nil
  107. }
  108. /**
  109. 内部方法: 进行参数的签名处理
  110. */
  111. func signParams(params map[string]string, api string, version string, ak string, sk string, time_stamp string) (headMaps map[string]string) {
  112. headMaps = make(map[string]string)
  113. params[API_NAME_KEY] = api
  114. headMaps[API_NAME_KEY] = api
  115. params[VERSION_KEY] = version
  116. headMaps[VERSION_KEY] = version
  117. //https://currentmillis.com/ calc current time with varies languages
  118. //v := time.Now().UnixNano() / 1000000
  119. params[TIMESTAMP_KEY] = time_stamp
  120. headMaps[TIMESTAMP_KEY] = time_stamp
  121. if ak != "" {
  122. params[ACCESS_KEY] = ak
  123. headMaps[ACCESS_KEY] = ak
  124. delete(params, SECRET_KEY)
  125. delete(params, SIGNATURE_KEY)
  126. signValue := doSign(params, sk)
  127. headMaps[SIGNATURE_KEY] = signValue
  128. }
  129. return headMaps
  130. }
  131. /**
  132. 调用CSB开放出来的服务(后者CSB控制台的Open API),并放回结果
  133. 请求参数的内容根据 HttpParams 的定义进行设置
  134. 返回的结果包含: 调用fa返回结果串, 返回的httpheaders 和 异常
  135. 当处理正常时,异常为nil
  136. */
  137. func Invoke(params HttpParams, time_stamp string) (str string, rtnHeaders map[string][]string, hcError *HttpCallerException) {
  138. //init rtnHeaders
  139. rtnHeaders = make(map[string][]string)
  140. hcError = params.Validate()
  141. if hcError != nil {
  142. return str, rtnHeaders, hcError
  143. }
  144. _, err := url.Parse(params.requestUrl)
  145. if err != nil {
  146. return str, rtnHeaders, &HttpCallerException{CauseErr: err}
  147. }
  148. client := &http.Client{
  149. Transport: &http.Transport{
  150. TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  151. Dial: func(netw, addr string) (net.Conn, error) {
  152. conn, err := net.DialTimeout(netw, addr, defaultSetting.ConnectTimeout) //设置建立连接超时
  153. if err != nil {
  154. return nil, err
  155. }
  156. conn.SetDeadline(time.Now().Add(defaultSetting.ReadWriteTimeout)) //设置发送接受数据超时
  157. return conn, nil
  158. },
  159. ResponseHeaderTimeout: time.Second * 60,
  160. },
  161. }
  162. //resp := &http.Response{}
  163. //for request parmas
  164. data := url.Values{}
  165. if params.params != nil {
  166. for k, v := range params.params {
  167. //strParams += fmt.Sprintf("%s=%s", k, url.QueryEscape())
  168. data.Set(k, v)
  169. }
  170. }
  171. var reqStr string
  172. var method string
  173. //method := "GET"
  174. if params.method == "post" {
  175. //genSignHeaders
  176. method = "POST"
  177. } else {
  178. method = "GET"
  179. }
  180. reqUrl := params.requestUrl
  181. defaultContentType := "application/json;charset=utf-8"
  182. if params.ct.jsonBody != "" {
  183. json.Marshal(params.ct.jsonBody)
  184. //if err == nil {
  185. // return str, rtnHeaders, &HttpCallerException{Message: "failed to bad content type json string", CauseErr: err}
  186. //}
  187. reqStr = params.ct.jsonBody
  188. defaultContentType = params.ct.contentType
  189. reqUrl = appendParams(reqUrl, data.Encode())
  190. } else if params.ct.bytesBody != nil {
  191. reqStr = string(params.ct.bytesBody)
  192. defaultContentType = params.ct.contentType
  193. reqUrl = appendParams(reqUrl, data.Encode())
  194. } else {
  195. reqStr = data.Encode()
  196. }
  197. req, err := http.NewRequest(method, reqUrl, bytes.NewBufferString(reqStr))
  198. if err != nil {
  199. return str, rtnHeaders, &HttpCallerException{Message: "failed to construct http post request", CauseErr: err}
  200. }
  201. //set signature related headers
  202. urlParams, hcError := parseUrlParamsMap(params.requestUrl)
  203. if err != nil {
  204. return str, rtnHeaders, hcError
  205. }
  206. printDebug("urlparams", urlParams)
  207. mergeTwoMaps(urlParams, params.params)
  208. printDebug("merged urlparams", urlParams)
  209. signHeaders := signParams(urlParams, params.api, params.version, params.ak, params.sk, time_stamp)
  210. printDebug("signHeaders", signHeaders)
  211. req.Header.Add("Content-Type", defaultContentType)
  212. if params.headers != nil {
  213. for k, v := range params.headers {
  214. req.Header.Add(k, v)
  215. }
  216. }
  217. //add sign related headers
  218. if signHeaders != nil {
  219. for k, v := range signHeaders {
  220. req.Header.Add(k, v)
  221. }
  222. }
  223. //client.Timeout
  224. resp, err := client.Do(req)
  225. if err != nil {
  226. return str, rtnHeaders, &HttpCallerException{Message: "failed to invoke http post", CauseErr: err}
  227. }
  228. if err != nil {
  229. fmt.Println(err)
  230. }
  231. defer resp.Body.Close()
  232. body, err := ioutil.ReadAll(resp.Body)
  233. if err != nil {
  234. fmt.Println(err)
  235. }
  236. str = string(body)
  237. if defaultSetting.CareResponseHttpHeader {
  238. rtnHeaders = resp.Header
  239. }
  240. /*
  241. fmt.Println("jsonStr", jsonStr)
  242. var dat map[string]string
  243. if err := json.Unmarshal([]byte(jsonStr), &dat); err == nil {
  244. fmt.Println("token", dat["token"])
  245. } else {
  246. fmt.Println("json str to struct error")
  247. }
  248. */
  249. return str, rtnHeaders, nil
  250. }