httplib.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // Copyright 2013 The Beego Authors. All rights reserved.
  2. // Copyright 2014 The Gogs Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package httplib
  6. import (
  7. "bytes"
  8. "crypto/tls"
  9. "encoding/json"
  10. "encoding/xml"
  11. "io"
  12. "io/ioutil"
  13. "net"
  14. "net/http"
  15. "net/http/httputil"
  16. "net/url"
  17. "os"
  18. "strings"
  19. "time"
  20. )
  21. var defaultUserAgent = "gogsServer"
  22. // Get returns *BeegoHttpRequest with GET method.
  23. func Get(url string) *BeegoHttpRequest {
  24. var req http.Request
  25. req.Method = "GET"
  26. req.Header = http.Header{}
  27. req.Header.Set("User-Agent", defaultUserAgent)
  28. return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
  29. }
  30. // Post returns *BeegoHttpRequest with POST method.
  31. func Post(url string) *BeegoHttpRequest {
  32. var req http.Request
  33. req.Method = "POST"
  34. req.Header = http.Header{}
  35. req.Header.Set("User-Agent", defaultUserAgent)
  36. return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
  37. }
  38. // Put returns *BeegoHttpRequest with PUT method.
  39. func Put(url string) *BeegoHttpRequest {
  40. var req http.Request
  41. req.Method = "PUT"
  42. req.Header = http.Header{}
  43. req.Header.Set("User-Agent", defaultUserAgent)
  44. return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
  45. }
  46. // Delete returns *BeegoHttpRequest DELETE GET method.
  47. func Delete(url string) *BeegoHttpRequest {
  48. var req http.Request
  49. req.Method = "DELETE"
  50. req.Header = http.Header{}
  51. req.Header.Set("User-Agent", defaultUserAgent)
  52. return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
  53. }
  54. // Head returns *BeegoHttpRequest with HEAD method.
  55. func Head(url string) *BeegoHttpRequest {
  56. var req http.Request
  57. req.Method = "HEAD"
  58. req.Header = http.Header{}
  59. req.Header.Set("User-Agent", defaultUserAgent)
  60. return &BeegoHttpRequest{url, &req, map[string]string{}, false, 60 * time.Second, 60 * time.Second, nil, nil, nil}
  61. }
  62. // BeegoHttpRequest provides more useful methods for requesting one url than http.Request.
  63. type BeegoHttpRequest struct {
  64. url string
  65. req *http.Request
  66. params map[string]string
  67. showdebug bool
  68. connectTimeout time.Duration
  69. readWriteTimeout time.Duration
  70. tlsClientConfig *tls.Config
  71. proxy func(*http.Request) (*url.URL, error)
  72. transport http.RoundTripper
  73. }
  74. // Debug sets show debug or not when executing request.
  75. func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest {
  76. b.showdebug = isdebug
  77. return b
  78. }
  79. // SetTimeout sets connect time out and read-write time out for BeegoRequest.
  80. func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest {
  81. b.connectTimeout = connectTimeout
  82. b.readWriteTimeout = readWriteTimeout
  83. return b
  84. }
  85. // SetTLSClientConfig sets tls connection configurations if visiting https url.
  86. func (b *BeegoHttpRequest) SetTLSClientConfig(config *tls.Config) *BeegoHttpRequest {
  87. b.tlsClientConfig = config
  88. return b
  89. }
  90. // Header add header item string in request.
  91. func (b *BeegoHttpRequest) Header(key, value string) *BeegoHttpRequest {
  92. b.req.Header.Set(key, value)
  93. return b
  94. }
  95. // SetCookie add cookie into request.
  96. func (b *BeegoHttpRequest) SetCookie(cookie *http.Cookie) *BeegoHttpRequest {
  97. b.req.Header.Add("Cookie", cookie.String())
  98. return b
  99. }
  100. // Set transport to
  101. func (b *BeegoHttpRequest) SetTransport(transport http.RoundTripper) *BeegoHttpRequest {
  102. b.transport = transport
  103. return b
  104. }
  105. // Set http proxy
  106. // example:
  107. //
  108. // func(req *http.Request) (*url.URL, error) {
  109. // u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
  110. // return u, nil
  111. // }
  112. func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHttpRequest {
  113. b.proxy = proxy
  114. return b
  115. }
  116. // Param adds query param in to request.
  117. // params build query string as ?key1=value1&key2=value2...
  118. func (b *BeegoHttpRequest) Param(key, value string) *BeegoHttpRequest {
  119. b.params[key] = value
  120. return b
  121. }
  122. // Body adds request raw body.
  123. // it supports string and []byte.
  124. func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest {
  125. switch t := data.(type) {
  126. case string:
  127. bf := bytes.NewBufferString(t)
  128. b.req.Body = ioutil.NopCloser(bf)
  129. b.req.ContentLength = int64(len(t))
  130. case []byte:
  131. bf := bytes.NewBuffer(t)
  132. b.req.Body = ioutil.NopCloser(bf)
  133. b.req.ContentLength = int64(len(t))
  134. }
  135. return b
  136. }
  137. func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
  138. var paramBody string
  139. if len(b.params) > 0 {
  140. var buf bytes.Buffer
  141. for k, v := range b.params {
  142. buf.WriteString(url.QueryEscape(k))
  143. buf.WriteByte('=')
  144. buf.WriteString(url.QueryEscape(v))
  145. buf.WriteByte('&')
  146. }
  147. paramBody = buf.String()
  148. paramBody = paramBody[0 : len(paramBody)-1]
  149. }
  150. if b.req.Method == "GET" && len(paramBody) > 0 {
  151. if strings.Index(b.url, "?") != -1 {
  152. b.url += "&" + paramBody
  153. } else {
  154. b.url = b.url + "?" + paramBody
  155. }
  156. } else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 {
  157. b.Header("Content-Type", "application/x-www-form-urlencoded")
  158. b.Body(paramBody)
  159. }
  160. url, err := url.Parse(b.url)
  161. if url.Scheme == "" {
  162. b.url = "http://" + b.url
  163. url, err = url.Parse(b.url)
  164. }
  165. if err != nil {
  166. return nil, err
  167. }
  168. b.req.URL = url
  169. if b.showdebug {
  170. dump, err := httputil.DumpRequest(b.req, true)
  171. if err != nil {
  172. println(err.Error())
  173. }
  174. println(string(dump))
  175. }
  176. trans := b.transport
  177. if trans == nil {
  178. // create default transport
  179. trans = &http.Transport{
  180. TLSClientConfig: b.tlsClientConfig,
  181. Proxy: b.proxy,
  182. Dial: TimeoutDialer(b.connectTimeout, b.readWriteTimeout),
  183. }
  184. } else {
  185. // if b.transport is *http.Transport then set the settings.
  186. if t, ok := trans.(*http.Transport); ok {
  187. if t.TLSClientConfig == nil {
  188. t.TLSClientConfig = b.tlsClientConfig
  189. }
  190. if t.Proxy == nil {
  191. t.Proxy = b.proxy
  192. }
  193. if t.Dial == nil {
  194. t.Dial = TimeoutDialer(b.connectTimeout, b.readWriteTimeout)
  195. }
  196. }
  197. }
  198. client := &http.Client{
  199. Transport: trans,
  200. }
  201. resp, err := client.Do(b.req)
  202. if err != nil {
  203. return nil, err
  204. }
  205. return resp, nil
  206. }
  207. // String returns the body string in response.
  208. // it calls Response inner.
  209. func (b *BeegoHttpRequest) String() (string, error) {
  210. data, err := b.Bytes()
  211. if err != nil {
  212. return "", err
  213. }
  214. return string(data), nil
  215. }
  216. // Bytes returns the body []byte in response.
  217. // it calls Response inner.
  218. func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
  219. resp, err := b.getResponse()
  220. if err != nil {
  221. return nil, err
  222. }
  223. if resp.Body == nil {
  224. return nil, nil
  225. }
  226. defer resp.Body.Close()
  227. data, err := ioutil.ReadAll(resp.Body)
  228. if err != nil {
  229. return nil, err
  230. }
  231. return data, nil
  232. }
  233. // ToFile saves the body data in response to one file.
  234. // it calls Response inner.
  235. func (b *BeegoHttpRequest) ToFile(filename string) error {
  236. f, err := os.Create(filename)
  237. if err != nil {
  238. return err
  239. }
  240. defer f.Close()
  241. resp, err := b.getResponse()
  242. if err != nil {
  243. return err
  244. }
  245. if resp.Body == nil {
  246. return nil
  247. }
  248. defer resp.Body.Close()
  249. _, err = io.Copy(f, resp.Body)
  250. if err != nil {
  251. return err
  252. }
  253. return nil
  254. }
  255. // ToJson returns the map that marshals from the body bytes as json in response .
  256. // it calls Response inner.
  257. func (b *BeegoHttpRequest) ToJson(v interface{}) error {
  258. data, err := b.Bytes()
  259. if err != nil {
  260. return err
  261. }
  262. err = json.Unmarshal(data, v)
  263. if err != nil {
  264. return err
  265. }
  266. return nil
  267. }
  268. // ToXml returns the map that marshals from the body bytes as xml in response .
  269. // it calls Response inner.
  270. func (b *BeegoHttpRequest) ToXML(v interface{}) error {
  271. data, err := b.Bytes()
  272. if err != nil {
  273. return err
  274. }
  275. err = xml.Unmarshal(data, v)
  276. if err != nil {
  277. return err
  278. }
  279. return nil
  280. }
  281. // Response executes request client gets response mannually.
  282. func (b *BeegoHttpRequest) Response() (*http.Response, error) {
  283. return b.getResponse()
  284. }
  285. // TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
  286. func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
  287. return func(netw, addr string) (net.Conn, error) {
  288. conn, err := net.DialTimeout(netw, addr, cTimeout)
  289. if err != nil {
  290. return nil, err
  291. }
  292. conn.SetDeadline(time.Now().Add(rwTimeout))
  293. return conn, nil
  294. }
  295. }