httplib.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. package httplib
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "crypto/tls"
  6. "encoding/json"
  7. "encoding/xml"
  8. "io"
  9. "io/ioutil"
  10. "log"
  11. "mime/multipart"
  12. "net"
  13. "net/http"
  14. "net/http/cookiejar"
  15. "net/http/httputil"
  16. "net/url"
  17. "os"
  18. "path"
  19. "strings"
  20. "sync"
  21. "time"
  22. )
  23. var defaultSetting = HTTPSettings{
  24. UserAgent: "GoServer",
  25. ConnectTimeout: 60 * time.Second, //尝试连接超时时间
  26. Gzip: true,
  27. DumpBody: true,
  28. Transport: &http.Transport{
  29. MaxIdleConns: 100, //控制空闲的最大数量(保持活动)跨所有主机的连接。零意味着没有限制
  30. MaxIdleConnsPerHost: 20, //控制每个主机要保持的最大空闲连接
  31. MaxConnsPerHost: 20, //每个主机的连接数,0不限制
  32. IdleConnTimeout: time.Second * 60, //连接最大空闲时常60秒
  33. },
  34. Retries: 2,
  35. RetryDelay: time.Millisecond * 200,
  36. }
  37. var defaultCookieJar http.CookieJar
  38. var settingMutex sync.Mutex
  39. // createDefaultCookie creates a global cookiejar to store cookies.
  40. func createDefaultCookie() {
  41. settingMutex.Lock()
  42. defer settingMutex.Unlock()
  43. defaultCookieJar, _ = cookiejar.New(nil)
  44. }
  45. // SetDefaultSetting Overwrite default settings
  46. func SetDefaultSetting(setting HTTPSettings) {
  47. settingMutex.Lock()
  48. defer settingMutex.Unlock()
  49. defaultSetting = setting
  50. }
  51. // NewRequest return *HttpRequest with specific method
  52. func NewRequest(rawurl, method string) *HTTPRequest {
  53. var resp http.Response
  54. u, err := url.Parse(rawurl)
  55. if err != nil {
  56. log.Println("Httplib:", err)
  57. }
  58. return &HTTPRequest{
  59. url: rawurl,
  60. req: &http.Request{
  61. URL: u,
  62. Method: method,
  63. Header: make(http.Header),
  64. Proto: "HTTP/1.1",
  65. ProtoMajor: 1,
  66. ProtoMinor: 1,
  67. },
  68. params: map[string][]string{},
  69. files: map[string]string{},
  70. setting: defaultSetting,
  71. resp: &resp,
  72. }
  73. }
  74. // Get returns *HttpRequest with GET method.
  75. func Get(url string) *HTTPRequest {
  76. return NewRequest(url, "GET")
  77. }
  78. // Post returns *HttpRequest with POST method.
  79. func Post(url string) *HTTPRequest {
  80. return NewRequest(url, "POST")
  81. }
  82. // Put returns *HttpRequest with PUT method.
  83. func Put(url string) *HTTPRequest {
  84. return NewRequest(url, "PUT")
  85. }
  86. // Delete returns *HttpRequest DELETE method.
  87. func Delete(url string) *HTTPRequest {
  88. return NewRequest(url, "DELETE")
  89. }
  90. // Head returns *HttpRequest with HEAD method.
  91. func Head(url string) *HTTPRequest {
  92. return NewRequest(url, "HEAD")
  93. }
  94. // HTTPSettings is the http.Client setting
  95. type HTTPSettings struct {
  96. ShowDebug bool
  97. UserAgent string
  98. ConnectTimeout time.Duration
  99. ReadWriteTimeout time.Duration
  100. TLSClientConfig *tls.Config
  101. Proxy func(*http.Request) (*url.URL, error)
  102. Transport http.RoundTripper
  103. CheckRedirect func(req *http.Request, via []*http.Request) error
  104. EnableCookie bool
  105. Gzip bool
  106. DumpBody bool
  107. Retries int // if set to -1 means will retry forever
  108. RetryDelay time.Duration
  109. }
  110. // HTTPRequest provides more useful methods for requesting one url than http.Request.
  111. type HTTPRequest struct {
  112. url string
  113. req *http.Request
  114. params map[string][]string
  115. files map[string]string
  116. setting HTTPSettings
  117. resp *http.Response
  118. body []byte
  119. dump []byte
  120. }
  121. // GetRequest return the request object
  122. func (b *HTTPRequest) GetRequest() *http.Request {
  123. return b.req
  124. }
  125. // Setting Change request settings
  126. func (b *HTTPRequest) Setting(setting HTTPSettings) *HTTPRequest {
  127. b.setting = setting
  128. return b
  129. }
  130. // SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
  131. func (b *HTTPRequest) SetBasicAuth(username, password string) *HTTPRequest {
  132. b.req.SetBasicAuth(username, password)
  133. return b
  134. }
  135. // SetEnableCookie sets enable/disable cookiejar
  136. func (b *HTTPRequest) SetEnableCookie(enable bool) *HTTPRequest {
  137. b.setting.EnableCookie = enable
  138. return b
  139. }
  140. // 设置 User-Agent
  141. func (b *HTTPRequest) SetUserAgent(useragent string) *HTTPRequest {
  142. b.setting.UserAgent = useragent
  143. return b
  144. }
  145. // Debug 模式.
  146. func (b *HTTPRequest) Debug(isdebug bool) *HTTPRequest {
  147. b.setting.ShowDebug = isdebug
  148. return b
  149. }
  150. //请求错误重试次数
  151. func (b *HTTPRequest) Retries(times int) *HTTPRequest {
  152. b.setting.Retries = times
  153. return b
  154. }
  155. func (b *HTTPRequest) RetryDelay(delay time.Duration) *HTTPRequest {
  156. b.setting.RetryDelay = delay
  157. return b
  158. }
  159. // DumpBody setting whether need to Dump the Body.
  160. func (b *HTTPRequest) DumpBody(isdump bool) *HTTPRequest {
  161. b.setting.DumpBody = isdump
  162. return b
  163. }
  164. // DumpRequest return the DumpRequest
  165. func (b *HTTPRequest) DumpRequest() []byte {
  166. return b.dump
  167. }
  168. // SetTimeout sets connect time out and read-write time out for Request.
  169. func (b *HTTPRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *HTTPRequest {
  170. b.setting.ConnectTimeout = connectTimeout
  171. b.setting.ReadWriteTimeout = readWriteTimeout
  172. return b
  173. }
  174. // SetTLSClientConfig sets tls connection configurations if visiting https url.
  175. func (b *HTTPRequest) SetTLSClientConfig(config *tls.Config) *HTTPRequest {
  176. b.setting.TLSClientConfig = config
  177. return b
  178. }
  179. // Header add header item string in request.
  180. func (b *HTTPRequest) Header(key, value string) *HTTPRequest {
  181. b.req.Header.Set(key, value)
  182. return b
  183. }
  184. // SetHost set the request host
  185. func (b *HTTPRequest) SetHost(host string) *HTTPRequest {
  186. b.req.Host = host
  187. return b
  188. }
  189. // SetProtocolVersion Set the protocol version for incoming requests.
  190. // Client requests always use HTTP/1.1.
  191. func (b *HTTPRequest) SetProtocolVersion(vers string) *HTTPRequest {
  192. if len(vers) == 0 {
  193. vers = "HTTP/1.1"
  194. }
  195. major, minor, ok := http.ParseHTTPVersion(vers)
  196. if ok {
  197. b.req.Proto = vers
  198. b.req.ProtoMajor = major
  199. b.req.ProtoMinor = minor
  200. }
  201. return b
  202. }
  203. // SetCookie add cookie into request.
  204. func (b *HTTPRequest) SetCookie(cookie *http.Cookie) *HTTPRequest {
  205. b.req.Header.Add("Cookie", cookie.String())
  206. return b
  207. }
  208. // SetTransport set the setting transport
  209. func (b *HTTPRequest) SetTransport(transport http.RoundTripper) *HTTPRequest {
  210. b.setting.Transport = transport
  211. return b
  212. }
  213. // SetProxy set the http proxy
  214. // example:
  215. //
  216. // func(req *http.Request) (*url.URL, error) {
  217. // u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
  218. // return u, nil
  219. // }
  220. func (b *HTTPRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *HTTPRequest {
  221. b.setting.Proxy = proxy
  222. return b
  223. }
  224. // SetCheckRedirect specifies the policy for handling redirects.
  225. //
  226. // If CheckRedirect is nil, the Client uses its default policy,
  227. // which is to stop after 10 consecutive requests.
  228. func (b *HTTPRequest) SetCheckRedirect(redirect func(req *http.Request, via []*http.Request) error) *HTTPRequest {
  229. b.setting.CheckRedirect = redirect
  230. return b
  231. }
  232. // Param adds query param in to request.
  233. // params build query string as ?key1=value1&key2=value2...
  234. func (b *HTTPRequest) Param(key, value string) *HTTPRequest {
  235. if param, ok := b.params[key]; ok {
  236. b.params[key] = append(param, value)
  237. } else {
  238. b.params[key] = []string{value}
  239. }
  240. return b
  241. }
  242. // PostFile add a post file to the request
  243. func (b *HTTPRequest) PostFile(formname, filename string) *HTTPRequest {
  244. b.files[formname] = filename
  245. return b
  246. }
  247. // Body adds request raw body.
  248. // it supports string and []byte.
  249. func (b *HTTPRequest) Body(data interface{}) *HTTPRequest {
  250. switch t := data.(type) {
  251. case string:
  252. bf := bytes.NewBufferString(t)
  253. b.req.Body = ioutil.NopCloser(bf)
  254. b.req.ContentLength = int64(len(t))
  255. case []byte:
  256. bf := bytes.NewBuffer(t)
  257. b.req.Body = ioutil.NopCloser(bf)
  258. b.req.ContentLength = int64(len(t))
  259. }
  260. return b
  261. }
  262. // XMLBody adds request raw body encoding by XML.
  263. func (b *HTTPRequest) XMLBody(obj interface{}) (*HTTPRequest, error) {
  264. if b.req.Body == nil && obj != nil {
  265. byts, err := xml.Marshal(obj)
  266. if err != nil {
  267. return b, err
  268. }
  269. b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
  270. b.req.ContentLength = int64(len(byts))
  271. b.req.Header.Set("Content-Type", "application/xml")
  272. }
  273. return b, nil
  274. }
  275. // JSONBody adds request raw body encoding by JSON.
  276. func (b *HTTPRequest) JSONBody(obj interface{}) (*HTTPRequest, error) {
  277. if b.req.Body == nil && obj != nil {
  278. byts, err := json.Marshal(obj)
  279. if err != nil {
  280. return b, err
  281. }
  282. b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
  283. b.req.ContentLength = int64(len(byts))
  284. b.req.Header.Set("Content-Type", "application/json")
  285. }
  286. return b, nil
  287. }
  288. func (b *HTTPRequest) buildURL(paramBody string) {
  289. // build GET url with query string
  290. if b.req.Method == "GET" && len(paramBody) > 0 {
  291. if strings.Contains(b.url, "?") {
  292. b.url += "&" + paramBody
  293. } else {
  294. b.url = b.url + "?" + paramBody
  295. }
  296. return
  297. }
  298. // build POST/PUT/PATCH url and body
  299. if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH" || b.req.Method == "DELETE") && b.req.Body == nil {
  300. // with files
  301. if len(b.files) > 0 {
  302. pr, pw := io.Pipe()
  303. bodyWriter := multipart.NewWriter(pw)
  304. go func() {
  305. for formname, filename := range b.files {
  306. fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
  307. if err != nil {
  308. log.Println("Httplib:", err)
  309. }
  310. fh, err := os.Open(filename)
  311. if err != nil {
  312. log.Println("Httplib:", err)
  313. }
  314. //iocopy
  315. _, err = io.Copy(fileWriter, fh)
  316. fh.Close()
  317. if err != nil {
  318. log.Println("Httplib:", err)
  319. }
  320. }
  321. for k, v := range b.params {
  322. for _, vv := range v {
  323. bodyWriter.WriteField(k, vv)
  324. }
  325. }
  326. bodyWriter.Close()
  327. pw.Close()
  328. }()
  329. b.Header("Content-Type", bodyWriter.FormDataContentType())
  330. b.req.Body = ioutil.NopCloser(pr)
  331. b.Header("Transfer-Encoding", "chunked")
  332. return
  333. }
  334. // with params
  335. if len(paramBody) > 0 {
  336. b.Header("Content-Type", "application/x-www-form-urlencoded")
  337. b.Body(paramBody)
  338. }
  339. }
  340. }
  341. func (b *HTTPRequest) getResponse() (*http.Response, error) {
  342. if b.resp.StatusCode != 0 {
  343. return b.resp, nil
  344. }
  345. resp, err := b.DoRequest()
  346. if err != nil {
  347. return nil, err
  348. }
  349. b.resp = resp
  350. return resp, nil
  351. }
  352. // DoRequest will do the client.Do
  353. func (b *HTTPRequest) DoRequest() (resp *http.Response, err error) {
  354. var paramBody string
  355. if len(b.params) > 0 {
  356. var buf bytes.Buffer
  357. for k, v := range b.params {
  358. for _, vv := range v {
  359. buf.WriteString(url.QueryEscape(k))
  360. buf.WriteByte('=')
  361. buf.WriteString(url.QueryEscape(vv))
  362. buf.WriteByte('&')
  363. }
  364. }
  365. paramBody = buf.String()
  366. paramBody = paramBody[0 : len(paramBody)-1]
  367. }
  368. b.buildURL(paramBody)
  369. urlParsed, err := url.Parse(b.url)
  370. if err != nil {
  371. return nil, err
  372. }
  373. b.req.URL = urlParsed
  374. trans := b.setting.Transport
  375. if trans == nil {
  376. // create default transport
  377. trans = &http.Transport{
  378. MaxIdleConns: 100, //控制空闲的最大数量(保持活动)跨所有主机的连接。零意味着没有限制
  379. MaxIdleConnsPerHost: 20, //控制每个主机要保持的最大空闲连接
  380. MaxConnsPerHost: 20, //每个主机的连接数,0不限制
  381. IdleConnTimeout: time.Second * 60, //连接最大空闲时常60秒
  382. }
  383. } else {
  384. // if b.transport is *http.Transport then set the settings.
  385. if t, ok := trans.(*http.Transport); ok {
  386. if t.TLSClientConfig == nil {
  387. t.TLSClientConfig = b.setting.TLSClientConfig
  388. }
  389. if t.Proxy == nil {
  390. t.Proxy = b.setting.Proxy
  391. }
  392. }
  393. }
  394. var jar http.CookieJar
  395. if b.setting.EnableCookie {
  396. if defaultCookieJar == nil {
  397. createDefaultCookie()
  398. }
  399. jar = defaultCookieJar
  400. }
  401. client := &http.Client{
  402. Transport: trans,
  403. Jar: jar,
  404. Timeout: time.Duration(b.setting.ConnectTimeout),
  405. }
  406. if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
  407. b.req.Header.Set("User-Agent", b.setting.UserAgent)
  408. }
  409. if b.setting.CheckRedirect != nil {
  410. client.CheckRedirect = b.setting.CheckRedirect
  411. }
  412. if b.setting.ShowDebug {
  413. dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
  414. if err != nil {
  415. log.Println(err.Error())
  416. }
  417. b.dump = dump
  418. }
  419. // retries default value is 0, it will run once.
  420. // retries equal to -1, it will run forever until success
  421. // retries is setted, it will retries fixed times.
  422. // Sleeps for a 400ms in between calls to reduce spam
  423. var bodyBytes []byte
  424. if b.req.Body != nil {
  425. bodyBytes, _ = ioutil.ReadAll(b.req.Body)
  426. }
  427. for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
  428. b.req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
  429. resp, err = client.Do(b.req)
  430. if err == nil {
  431. break
  432. }
  433. time.Sleep(b.setting.RetryDelay)
  434. }
  435. return resp, err
  436. }
  437. // String returns the body string in response.
  438. // it calls Response inner.
  439. func (b *HTTPRequest) String() (string, error) {
  440. data, err := b.Bytes()
  441. if err != nil {
  442. return "", err
  443. }
  444. return string(data), nil
  445. }
  446. // Bytes returns the body []byte in response.
  447. // it calls Response inner.
  448. func (b *HTTPRequest) Bytes() ([]byte, error) {
  449. if b.body != nil {
  450. return b.body, nil
  451. }
  452. resp, err := b.getResponse()
  453. if err != nil {
  454. return nil, err
  455. }
  456. if resp.Body == nil {
  457. return nil, nil
  458. }
  459. defer resp.Body.Close()
  460. if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" {
  461. reader, err := gzip.NewReader(resp.Body)
  462. if err != nil {
  463. return nil, err
  464. }
  465. b.body, err = ioutil.ReadAll(reader)
  466. return b.body, err
  467. }
  468. b.body, err = ioutil.ReadAll(resp.Body)
  469. return b.body, err
  470. }
  471. // ToFile saves the body data in response to one file.
  472. // it calls Response inner.
  473. func (b *HTTPRequest) ToFile(filename string) error {
  474. resp, err := b.getResponse()
  475. if err != nil {
  476. return err
  477. }
  478. if resp.Body == nil {
  479. return nil
  480. }
  481. defer resp.Body.Close()
  482. err = pathExistAndMkdir(filename)
  483. if err != nil {
  484. return err
  485. }
  486. f, err := os.Create(filename)
  487. if err != nil {
  488. return err
  489. }
  490. defer f.Close()
  491. _, err = io.Copy(f, resp.Body)
  492. return err
  493. }
  494. //Check that the file directory exists, there is no automatically created
  495. func pathExistAndMkdir(filename string) (err error) {
  496. filename = path.Dir(filename)
  497. _, err = os.Stat(filename)
  498. if err == nil {
  499. return nil
  500. }
  501. if os.IsNotExist(err) {
  502. err = os.MkdirAll(filename, os.ModePerm)
  503. if err == nil {
  504. return nil
  505. }
  506. }
  507. return err
  508. }
  509. // ToJSON returns the map that marshals from the body bytes as json in response .
  510. // it calls Response inner.
  511. func (b *HTTPRequest) ToJSON(v interface{}) error {
  512. data, err := b.Bytes()
  513. if err != nil {
  514. return err
  515. }
  516. return json.Unmarshal(data, v)
  517. }
  518. // ToXML returns the map that marshals from the body bytes as xml in response .
  519. // it calls Response inner.
  520. func (b *HTTPRequest) ToXML(v interface{}) error {
  521. data, err := b.Bytes()
  522. if err != nil {
  523. return err
  524. }
  525. return xml.Unmarshal(data, v)
  526. }
  527. // Response executes request client gets response mannually.
  528. func (b *HTTPRequest) Response() (*http.Response, error) {
  529. return b.getResponse()
  530. }
  531. // TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
  532. // rwTimeout If keep online its all time around write or read
  533. func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
  534. return func(netw, addr string) (net.Conn, error) {
  535. conn, err := net.DialTimeout(netw, addr, cTimeout)
  536. if err != nil {
  537. return nil, err
  538. }
  539. err = conn.SetDeadline(time.Now().Add(rwTimeout))
  540. return conn, err
  541. }
  542. }