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.

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