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.

461 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. // 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. // Headers returns headers in request.
  145. func (r *Request) Headers() http.Header {
  146. return r.req.Header
  147. }
  148. // SetProtocolVersion sets the protocol version for incoming requests.
  149. // Client requests always use HTTP/1.1.
  150. func (r *Request) SetProtocolVersion(vers string) *Request {
  151. if len(vers) == 0 {
  152. vers = "HTTP/1.1"
  153. }
  154. major, minor, ok := http.ParseHTTPVersion(vers)
  155. if ok {
  156. r.req.Proto = vers
  157. r.req.ProtoMajor = major
  158. r.req.ProtoMinor = minor
  159. }
  160. return r
  161. }
  162. // SetCookie add cookie into request.
  163. func (r *Request) SetCookie(cookie *http.Cookie) *Request {
  164. r.req.Header.Add("Cookie", cookie.String())
  165. return r
  166. }
  167. // SetTransport sets transport to
  168. func (r *Request) SetTransport(transport http.RoundTripper) *Request {
  169. r.setting.Transport = transport
  170. return r
  171. }
  172. // SetProxy sets http proxy
  173. // example:
  174. //
  175. // func(req *http.Request) (*url.URL, error) {
  176. // u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
  177. // return u, nil
  178. // }
  179. func (r *Request) SetProxy(proxy func(*http.Request) (*url.URL, error)) *Request {
  180. r.setting.Proxy = proxy
  181. return r
  182. }
  183. // Param adds query param in to request.
  184. // params build query string as ?key1=value1&key2=value2...
  185. func (r *Request) Param(key, value string) *Request {
  186. r.params[key] = value
  187. return r
  188. }
  189. // PostFile uploads file via http
  190. func (r *Request) PostFile(formname, filename string) *Request {
  191. r.files[formname] = filename
  192. return r
  193. }
  194. // Body adds request raw body.
  195. // it supports string and []byte.
  196. func (r *Request) Body(data interface{}) *Request {
  197. switch t := data.(type) {
  198. case string:
  199. bf := bytes.NewBufferString(t)
  200. r.req.Body = ioutil.NopCloser(bf)
  201. r.req.ContentLength = int64(len(t))
  202. case []byte:
  203. bf := bytes.NewBuffer(t)
  204. r.req.Body = ioutil.NopCloser(bf)
  205. r.req.ContentLength = int64(len(t))
  206. }
  207. return r
  208. }
  209. func (r *Request) getResponse() (*http.Response, error) {
  210. if r.resp.StatusCode != 0 {
  211. return r.resp, nil
  212. }
  213. var paramBody string
  214. if len(r.params) > 0 {
  215. var buf bytes.Buffer
  216. for k, v := range r.params {
  217. buf.WriteString(url.QueryEscape(k))
  218. buf.WriteByte('=')
  219. buf.WriteString(url.QueryEscape(v))
  220. buf.WriteByte('&')
  221. }
  222. paramBody = buf.String()
  223. paramBody = paramBody[0 : len(paramBody)-1]
  224. }
  225. if r.req.Method == "GET" && len(paramBody) > 0 {
  226. if strings.Index(r.url, "?") != -1 {
  227. r.url += "&" + paramBody
  228. } else {
  229. r.url = r.url + "?" + paramBody
  230. }
  231. } else if r.req.Method == "POST" && r.req.Body == nil {
  232. if len(r.files) > 0 {
  233. pr, pw := io.Pipe()
  234. bodyWriter := multipart.NewWriter(pw)
  235. go func() {
  236. for formname, filename := range r.files {
  237. fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
  238. if err != nil {
  239. log.Fatal(err)
  240. }
  241. fh, err := os.Open(filename)
  242. if err != nil {
  243. log.Fatal(err)
  244. }
  245. //iocopy
  246. _, err = io.Copy(fileWriter, fh)
  247. fh.Close()
  248. if err != nil {
  249. log.Fatal(err)
  250. }
  251. }
  252. for k, v := range r.params {
  253. bodyWriter.WriteField(k, v)
  254. }
  255. bodyWriter.Close()
  256. pw.Close()
  257. }()
  258. r.Header("Content-Type", bodyWriter.FormDataContentType())
  259. r.req.Body = ioutil.NopCloser(pr)
  260. } else if len(paramBody) > 0 {
  261. r.Header("Content-Type", "application/x-www-form-urlencoded")
  262. r.Body(paramBody)
  263. }
  264. }
  265. url, err := url.Parse(r.url)
  266. if err != nil {
  267. return nil, err
  268. }
  269. r.req.URL = url
  270. trans := r.setting.Transport
  271. if trans == nil {
  272. // create default transport
  273. proxy := r.setting.Proxy
  274. if proxy == nil {
  275. proxy = http.ProxyFromEnvironment
  276. }
  277. trans = &http.Transport{
  278. TLSClientConfig: r.setting.TLSClientConfig,
  279. Proxy: proxy,
  280. Dial: TimeoutDialer(r.setting.ConnectTimeout, r.setting.ReadWriteTimeout),
  281. }
  282. } else {
  283. // if r.transport is *http.Transport then set the settings.
  284. if t, ok := trans.(*http.Transport); ok {
  285. if t.TLSClientConfig == nil {
  286. t.TLSClientConfig = r.setting.TLSClientConfig
  287. }
  288. if t.Proxy == nil {
  289. t.Proxy = r.setting.Proxy
  290. }
  291. if t.Dial == nil {
  292. t.Dial = TimeoutDialer(r.setting.ConnectTimeout, r.setting.ReadWriteTimeout)
  293. }
  294. }
  295. }
  296. var jar http.CookieJar
  297. if r.setting.EnableCookie {
  298. if defaultCookieJar == nil {
  299. createDefaultCookie()
  300. }
  301. jar = defaultCookieJar
  302. } else {
  303. jar = nil
  304. }
  305. client := &http.Client{
  306. Transport: trans,
  307. Jar: jar,
  308. }
  309. if len(r.setting.UserAgent) > 0 && len(r.req.Header.Get("User-Agent")) == 0 {
  310. r.req.Header.Set("User-Agent", r.setting.UserAgent)
  311. }
  312. if r.setting.ShowDebug {
  313. dump, err := httputil.DumpRequest(r.req, true)
  314. if err != nil {
  315. println(err.Error())
  316. }
  317. println(string(dump))
  318. }
  319. resp, err := client.Do(r.req)
  320. if err != nil {
  321. return nil, err
  322. }
  323. r.resp = resp
  324. return resp, nil
  325. }
  326. // String returns the body string in response.
  327. // it calls Response inner.
  328. func (r *Request) String() (string, error) {
  329. data, err := r.Bytes()
  330. if err != nil {
  331. return "", err
  332. }
  333. return string(data), nil
  334. }
  335. // Bytes returns the body []byte in response.
  336. // it calls Response inner.
  337. func (r *Request) Bytes() ([]byte, error) {
  338. if r.body != nil {
  339. return r.body, nil
  340. }
  341. resp, err := r.getResponse()
  342. if err != nil {
  343. return nil, err
  344. }
  345. if resp.Body == nil {
  346. return nil, nil
  347. }
  348. defer resp.Body.Close()
  349. data, err := ioutil.ReadAll(resp.Body)
  350. if err != nil {
  351. return nil, err
  352. }
  353. r.body = data
  354. return data, nil
  355. }
  356. // ToFile saves the body data in response to one file.
  357. // it calls Response inner.
  358. func (r *Request) ToFile(filename string) error {
  359. f, err := os.Create(filename)
  360. if err != nil {
  361. return err
  362. }
  363. defer f.Close()
  364. resp, err := r.getResponse()
  365. if err != nil {
  366. return err
  367. }
  368. if resp.Body == nil {
  369. return nil
  370. }
  371. defer resp.Body.Close()
  372. _, err = io.Copy(f, resp.Body)
  373. return err
  374. }
  375. // ToJSON returns the map that marshals from the body bytes as json in response .
  376. // it calls Response inner.
  377. func (r *Request) ToJSON(v interface{}) error {
  378. data, err := r.Bytes()
  379. if err != nil {
  380. return err
  381. }
  382. err = json.Unmarshal(data, v)
  383. return err
  384. }
  385. // ToXML returns the map that marshals from the body bytes as xml in response .
  386. // it calls Response inner.
  387. func (r *Request) ToXML(v interface{}) error {
  388. data, err := r.Bytes()
  389. if err != nil {
  390. return err
  391. }
  392. err = xml.Unmarshal(data, v)
  393. return err
  394. }
  395. // Response executes request client gets response manually.
  396. func (r *Request) Response() (*http.Response, error) {
  397. return r.getResponse()
  398. }
  399. // TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
  400. func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
  401. return func(netw, addr string) (net.Conn, error) {
  402. conn, err := net.DialTimeout(netw, addr, cTimeout)
  403. if err != nil {
  404. return nil, err
  405. }
  406. conn.SetDeadline(time.Now().Add(rwTimeout))
  407. return conn, nil
  408. }
  409. }