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.

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