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.

456 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. proxy := r.setting.Proxy
  270. if proxy == nil {
  271. proxy = http.ProxyFromEnvironment
  272. }
  273. trans = &http.Transport{
  274. TLSClientConfig: r.setting.TLSClientConfig,
  275. Proxy: proxy,
  276. Dial: TimeoutDialer(r.setting.ConnectTimeout, r.setting.ReadWriteTimeout),
  277. }
  278. } else {
  279. // if r.transport is *http.Transport then set the settings.
  280. if t, ok := trans.(*http.Transport); ok {
  281. if t.TLSClientConfig == nil {
  282. t.TLSClientConfig = r.setting.TLSClientConfig
  283. }
  284. if t.Proxy == nil {
  285. t.Proxy = r.setting.Proxy
  286. }
  287. if t.Dial == nil {
  288. t.Dial = TimeoutDialer(r.setting.ConnectTimeout, r.setting.ReadWriteTimeout)
  289. }
  290. }
  291. }
  292. var jar http.CookieJar
  293. if r.setting.EnableCookie {
  294. if defaultCookieJar == nil {
  295. createDefaultCookie()
  296. }
  297. jar = defaultCookieJar
  298. } else {
  299. jar = nil
  300. }
  301. client := &http.Client{
  302. Transport: trans,
  303. Jar: jar,
  304. }
  305. if len(r.setting.UserAgent) > 0 && len(r.req.Header.Get("User-Agent")) == 0 {
  306. r.req.Header.Set("User-Agent", r.setting.UserAgent)
  307. }
  308. if r.setting.ShowDebug {
  309. dump, err := httputil.DumpRequest(r.req, true)
  310. if err != nil {
  311. println(err.Error())
  312. }
  313. println(string(dump))
  314. }
  315. resp, err := client.Do(r.req)
  316. if err != nil {
  317. return nil, err
  318. }
  319. r.resp = resp
  320. return resp, nil
  321. }
  322. // String returns the body string in response.
  323. // it calls Response inner.
  324. func (r *Request) String() (string, error) {
  325. data, err := r.Bytes()
  326. if err != nil {
  327. return "", err
  328. }
  329. return string(data), nil
  330. }
  331. // Bytes returns the body []byte in response.
  332. // it calls Response inner.
  333. func (r *Request) Bytes() ([]byte, error) {
  334. if r.body != nil {
  335. return r.body, nil
  336. }
  337. resp, err := r.getResponse()
  338. if err != nil {
  339. return nil, err
  340. }
  341. if resp.Body == nil {
  342. return nil, nil
  343. }
  344. defer resp.Body.Close()
  345. data, err := ioutil.ReadAll(resp.Body)
  346. if err != nil {
  347. return nil, err
  348. }
  349. r.body = data
  350. return data, nil
  351. }
  352. // ToFile saves the body data in response to one file.
  353. // it calls Response inner.
  354. func (r *Request) ToFile(filename string) error {
  355. f, err := os.Create(filename)
  356. if err != nil {
  357. return err
  358. }
  359. defer f.Close()
  360. resp, err := r.getResponse()
  361. if err != nil {
  362. return err
  363. }
  364. if resp.Body == nil {
  365. return nil
  366. }
  367. defer resp.Body.Close()
  368. _, err = io.Copy(f, resp.Body)
  369. return err
  370. }
  371. // ToJSON returns the map that marshals from the body bytes as json in response .
  372. // it calls Response inner.
  373. func (r *Request) ToJSON(v interface{}) error {
  374. data, err := r.Bytes()
  375. if err != nil {
  376. return err
  377. }
  378. err = json.Unmarshal(data, v)
  379. return err
  380. }
  381. // ToXML returns the map that marshals from the body bytes as xml in response .
  382. // it calls Response inner.
  383. func (r *Request) ToXML(v interface{}) error {
  384. data, err := r.Bytes()
  385. if err != nil {
  386. return err
  387. }
  388. err = xml.Unmarshal(data, v)
  389. return err
  390. }
  391. // Response executes request client gets response mannually.
  392. func (r *Request) Response() (*http.Response, error) {
  393. return r.getResponse()
  394. }
  395. // TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
  396. func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
  397. return func(netw, addr string) (net.Conn, error) {
  398. conn, err := net.DialTimeout(netw, addr, cTimeout)
  399. if err != nil {
  400. return nil, err
  401. }
  402. conn.SetDeadline(time.Now().Add(rwTimeout))
  403. return conn, nil
  404. }
  405. }