You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

449 lines
10 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  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. "log"
  14. "mime/multipart"
  15. "net"
  16. "net/http"
  17. "net/http/cookiejar"
  18. "net/http/httputil"
  19. "net/url"
  20. "os"
  21. "strings"
  22. "sync"
  23. "time"
  24. )
  25. var defaultSetting = Settings{false, "GogsServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false}
  26. var defaultCookieJar http.CookieJar
  27. var settingMutex sync.Mutex
  28. // createDefaultCookie creates a global cookiejar to store cookies.
  29. func createDefaultCookie() {
  30. settingMutex.Lock()
  31. defer settingMutex.Unlock()
  32. defaultCookieJar, _ = cookiejar.New(nil)
  33. }
  34. // Overwrite default settings
  35. func SetDefaultSetting(setting Settings) {
  36. settingMutex.Lock()
  37. defer settingMutex.Unlock()
  38. defaultSetting = setting
  39. if defaultSetting.ConnectTimeout == 0 {
  40. defaultSetting.ConnectTimeout = 60 * time.Second
  41. }
  42. if defaultSetting.ReadWriteTimeout == 0 {
  43. defaultSetting.ReadWriteTimeout = 60 * time.Second
  44. }
  45. }
  46. // return *Request with specific method
  47. func newRequest(url, method string) *Request {
  48. var resp http.Response
  49. req := http.Request{
  50. Method: method,
  51. Header: make(http.Header),
  52. Proto: "HTTP/1.1",
  53. ProtoMajor: 1,
  54. ProtoMinor: 1,
  55. }
  56. return &Request{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil}
  57. }
  58. // Get returns *Request with GET method.
  59. func Get(url string) *Request {
  60. return newRequest(url, "GET")
  61. }
  62. // Post returns *Request with POST method.
  63. func Post(url string) *Request {
  64. return newRequest(url, "POST")
  65. }
  66. // Put returns *Request with PUT method.
  67. func Put(url string) *Request {
  68. return newRequest(url, "PUT")
  69. }
  70. // Delete returns *Request DELETE method.
  71. func Delete(url string) *Request {
  72. return newRequest(url, "DELETE")
  73. }
  74. // Head returns *Request with HEAD method.
  75. func Head(url string) *Request {
  76. return newRequest(url, "HEAD")
  77. }
  78. type Settings struct {
  79. ShowDebug bool
  80. UserAgent string
  81. ConnectTimeout time.Duration
  82. ReadWriteTimeout time.Duration
  83. TlsClientConfig *tls.Config
  84. Proxy func(*http.Request) (*url.URL, error)
  85. Transport http.RoundTripper
  86. EnableCookie bool
  87. }
  88. // HttpRequest provides more useful methods for requesting one url than http.Request.
  89. type Request struct {
  90. url string
  91. req *http.Request
  92. params map[string]string
  93. files map[string]string
  94. setting Settings
  95. resp *http.Response
  96. body []byte
  97. }
  98. // Change request settings
  99. func (r *Request) Setting(setting Settings) *Request {
  100. r.setting = setting
  101. return r
  102. }
  103. // SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
  104. func (r *Request) SetBasicAuth(username, password string) *Request {
  105. r.req.SetBasicAuth(username, password)
  106. return r
  107. }
  108. // SetEnableCookie sets enable/disable cookiejar
  109. func (r *Request) SetEnableCookie(enable bool) *Request {
  110. r.setting.EnableCookie = enable
  111. return r
  112. }
  113. // SetUserAgent sets User-Agent header field
  114. func (r *Request) SetUserAgent(useragent string) *Request {
  115. r.setting.UserAgent = useragent
  116. return r
  117. }
  118. // Debug sets show debug or not when executing request.
  119. func (r *Request) Debug(isdebug bool) *Request {
  120. r.setting.ShowDebug = isdebug
  121. return r
  122. }
  123. // SetTimeout sets connect time out and read-write time out for BeegoRequest.
  124. func (r *Request) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *Request {
  125. r.setting.ConnectTimeout = connectTimeout
  126. r.setting.ReadWriteTimeout = readWriteTimeout
  127. return r
  128. }
  129. // SetTLSClientConfig sets tls connection configurations if visiting https url.
  130. func (r *Request) SetTLSClientConfig(config *tls.Config) *Request {
  131. r.setting.TlsClientConfig = config
  132. return r
  133. }
  134. // Header add header item string in request.
  135. func (r *Request) Header(key, value string) *Request {
  136. r.req.Header.Set(key, value)
  137. return r
  138. }
  139. func (r *Request) Headers() http.Header {
  140. return r.req.Header
  141. }
  142. // Set the protocol version for incoming requests.
  143. // Client requests always use HTTP/1.1.
  144. func (r *Request) SetProtocolVersion(vers string) *Request {
  145. if len(vers) == 0 {
  146. vers = "HTTP/1.1"
  147. }
  148. major, minor, ok := http.ParseHTTPVersion(vers)
  149. if ok {
  150. r.req.Proto = vers
  151. r.req.ProtoMajor = major
  152. r.req.ProtoMinor = minor
  153. }
  154. return r
  155. }
  156. // SetCookie add cookie into request.
  157. func (r *Request) SetCookie(cookie *http.Cookie) *Request {
  158. r.req.Header.Add("Cookie", cookie.String())
  159. return r
  160. }
  161. // Set transport to
  162. func (r *Request) SetTransport(transport http.RoundTripper) *Request {
  163. r.setting.Transport = transport
  164. return r
  165. }
  166. // Set http proxy
  167. // example:
  168. //
  169. // func(req *http.Request) (*url.URL, error) {
  170. // u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
  171. // return u, nil
  172. // }
  173. func (r *Request) SetProxy(proxy func(*http.Request) (*url.URL, error)) *Request {
  174. r.setting.Proxy = proxy
  175. return r
  176. }
  177. // Param adds query param in to request.
  178. // params build query string as ?key1=value1&key2=value2...
  179. func (r *Request) Param(key, value string) *Request {
  180. r.params[key] = value
  181. return r
  182. }
  183. func (r *Request) PostFile(formname, filename string) *Request {
  184. r.files[formname] = filename
  185. return r
  186. }
  187. // Body adds request raw body.
  188. // it supports string and []byte.
  189. func (r *Request) Body(data interface{}) *Request {
  190. switch t := data.(type) {
  191. case string:
  192. bf := bytes.NewBufferString(t)
  193. r.req.Body = ioutil.NopCloser(bf)
  194. r.req.ContentLength = int64(len(t))
  195. case []byte:
  196. bf := bytes.NewBuffer(t)
  197. r.req.Body = ioutil.NopCloser(bf)
  198. r.req.ContentLength = int64(len(t))
  199. }
  200. return r
  201. }
  202. func (r *Request) getResponse() (*http.Response, error) {
  203. if r.resp.StatusCode != 0 {
  204. return r.resp, nil
  205. }
  206. var paramBody string
  207. if len(r.params) > 0 {
  208. var buf bytes.Buffer
  209. for k, v := range r.params {
  210. buf.WriteString(url.QueryEscape(k))
  211. buf.WriteByte('=')
  212. buf.WriteString(url.QueryEscape(v))
  213. buf.WriteByte('&')
  214. }
  215. paramBody = buf.String()
  216. paramBody = paramBody[0 : len(paramBody)-1]
  217. }
  218. if r.req.Method == "GET" && len(paramBody) > 0 {
  219. if strings.Index(r.url, "?") != -1 {
  220. r.url += "&" + paramBody
  221. } else {
  222. r.url = r.url + "?" + paramBody
  223. }
  224. } else if r.req.Method == "POST" && r.req.Body == nil {
  225. if len(r.files) > 0 {
  226. pr, pw := io.Pipe()
  227. bodyWriter := multipart.NewWriter(pw)
  228. go func() {
  229. for formname, filename := range r.files {
  230. fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
  231. if err != nil {
  232. log.Fatal(err)
  233. }
  234. fh, err := os.Open(filename)
  235. if err != nil {
  236. log.Fatal(err)
  237. }
  238. //iocopy
  239. _, err = io.Copy(fileWriter, fh)
  240. fh.Close()
  241. if err != nil {
  242. log.Fatal(err)
  243. }
  244. }
  245. for k, v := range r.params {
  246. bodyWriter.WriteField(k, v)
  247. }
  248. bodyWriter.Close()
  249. pw.Close()
  250. }()
  251. r.Header("Content-Type", bodyWriter.FormDataContentType())
  252. r.req.Body = ioutil.NopCloser(pr)
  253. } else if len(paramBody) > 0 {
  254. r.Header("Content-Type", "application/x-www-form-urlencoded")
  255. r.Body(paramBody)
  256. }
  257. }
  258. url, err := url.Parse(r.url)
  259. if err != nil {
  260. return nil, err
  261. }
  262. r.req.URL = url
  263. trans := r.setting.Transport
  264. if trans == nil {
  265. // create default transport
  266. trans = &http.Transport{
  267. TLSClientConfig: r.setting.TlsClientConfig,
  268. Proxy: r.setting.Proxy,
  269. Dial: TimeoutDialer(r.setting.ConnectTimeout, r.setting.ReadWriteTimeout),
  270. }
  271. } else {
  272. // if r.transport is *http.Transport then set the settings.
  273. if t, ok := trans.(*http.Transport); ok {
  274. if t.TLSClientConfig == nil {
  275. t.TLSClientConfig = r.setting.TlsClientConfig
  276. }
  277. if t.Proxy == nil {
  278. t.Proxy = r.setting.Proxy
  279. }
  280. if t.Dial == nil {
  281. t.Dial = TimeoutDialer(r.setting.ConnectTimeout, r.setting.ReadWriteTimeout)
  282. }
  283. }
  284. }
  285. var jar http.CookieJar
  286. if r.setting.EnableCookie {
  287. if defaultCookieJar == nil {
  288. createDefaultCookie()
  289. }
  290. jar = defaultCookieJar
  291. } else {
  292. jar = nil
  293. }
  294. client := &http.Client{
  295. Transport: trans,
  296. Jar: jar,
  297. }
  298. if len(r.setting.UserAgent) > 0 && len(r.req.Header.Get("User-Agent")) == 0 {
  299. r.req.Header.Set("User-Agent", r.setting.UserAgent)
  300. }
  301. if r.setting.ShowDebug {
  302. dump, err := httputil.DumpRequest(r.req, true)
  303. if err != nil {
  304. println(err.Error())
  305. }
  306. println(string(dump))
  307. }
  308. resp, err := client.Do(r.req)
  309. if err != nil {
  310. return nil, err
  311. }
  312. r.resp = resp
  313. return resp, nil
  314. }
  315. // String returns the body string in response.
  316. // it calls Response inner.
  317. func (r *Request) String() (string, error) {
  318. data, err := r.Bytes()
  319. if err != nil {
  320. return "", err
  321. }
  322. return string(data), nil
  323. }
  324. // Bytes returns the body []byte in response.
  325. // it calls Response inner.
  326. func (r *Request) Bytes() ([]byte, error) {
  327. if r.body != nil {
  328. return r.body, nil
  329. }
  330. resp, err := r.getResponse()
  331. if err != nil {
  332. return nil, err
  333. }
  334. if resp.Body == nil {
  335. return nil, nil
  336. }
  337. defer resp.Body.Close()
  338. data, err := ioutil.ReadAll(resp.Body)
  339. if err != nil {
  340. return nil, err
  341. }
  342. r.body = data
  343. return data, nil
  344. }
  345. // ToFile saves the body data in response to one file.
  346. // it calls Response inner.
  347. func (r *Request) ToFile(filename string) error {
  348. f, err := os.Create(filename)
  349. if err != nil {
  350. return err
  351. }
  352. defer f.Close()
  353. resp, err := r.getResponse()
  354. if err != nil {
  355. return err
  356. }
  357. if resp.Body == nil {
  358. return nil
  359. }
  360. defer resp.Body.Close()
  361. _, err = io.Copy(f, resp.Body)
  362. return err
  363. }
  364. // ToJson returns the map that marshals from the body bytes as json in response .
  365. // it calls Response inner.
  366. func (r *Request) ToJson(v interface{}) error {
  367. data, err := r.Bytes()
  368. if err != nil {
  369. return err
  370. }
  371. err = json.Unmarshal(data, v)
  372. return err
  373. }
  374. // ToXml returns the map that marshals from the body bytes as xml in response .
  375. // it calls Response inner.
  376. func (r *Request) ToXml(v interface{}) error {
  377. data, err := r.Bytes()
  378. if err != nil {
  379. return err
  380. }
  381. err = xml.Unmarshal(data, v)
  382. return err
  383. }
  384. // Response executes request client gets response mannually.
  385. func (r *Request) Response() (*http.Response, error) {
  386. return r.getResponse()
  387. }
  388. // TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
  389. func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
  390. return func(netw, addr string) (net.Conn, error) {
  391. conn, err := net.DialTimeout(netw, addr, cTimeout)
  392. if err != nil {
  393. return nil, err
  394. }
  395. conn.SetDeadline(time.Now().Add(rwTimeout))
  396. return conn, nil
  397. }
  398. }