redirect.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // Copyright (c) 2015-2019 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
  2. // resty source code and usage is governed by a MIT style
  3. // license that can be found in the LICENSE file.
  4. package resty
  5. import (
  6. "errors"
  7. "fmt"
  8. "net"
  9. "net/http"
  10. "strings"
  11. )
  12. type (
  13. // RedirectPolicy to regulate the redirects in the resty client.
  14. // Objects implementing the RedirectPolicy interface can be registered as
  15. //
  16. // Apply function should return nil to continue the redirect jounery, otherwise
  17. // return error to stop the redirect.
  18. RedirectPolicy interface {
  19. Apply(req *http.Request, via []*http.Request) error
  20. }
  21. // The RedirectPolicyFunc type is an adapter to allow the use of ordinary functions as RedirectPolicy.
  22. // If f is a function with the appropriate signature, RedirectPolicyFunc(f) is a RedirectPolicy object that calls f.
  23. RedirectPolicyFunc func(*http.Request, []*http.Request) error
  24. )
  25. // Apply calls f(req, via).
  26. func (f RedirectPolicyFunc) Apply(req *http.Request, via []*http.Request) error {
  27. return f(req, via)
  28. }
  29. // NoRedirectPolicy is used to disable redirects in the HTTP client
  30. // resty.SetRedirectPolicy(NoRedirectPolicy())
  31. func NoRedirectPolicy() RedirectPolicy {
  32. return RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
  33. return errors.New("auto redirect is disabled")
  34. })
  35. }
  36. // FlexibleRedirectPolicy is convenient method to create No of redirect policy for HTTP client.
  37. // resty.SetRedirectPolicy(FlexibleRedirectPolicy(20))
  38. func FlexibleRedirectPolicy(noOfRedirect int) RedirectPolicy {
  39. return RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
  40. if len(via) >= noOfRedirect {
  41. return fmt.Errorf("stopped after %d redirects", noOfRedirect)
  42. }
  43. checkHostAndAddHeaders(req, via[0])
  44. return nil
  45. })
  46. }
  47. // DomainCheckRedirectPolicy is convenient method to define domain name redirect rule in resty client.
  48. // Redirect is allowed for only mentioned host in the policy.
  49. // resty.SetRedirectPolicy(DomainCheckRedirectPolicy("host1.com", "host2.org", "host3.net"))
  50. func DomainCheckRedirectPolicy(hostnames ...string) RedirectPolicy {
  51. hosts := make(map[string]bool)
  52. for _, h := range hostnames {
  53. hosts[strings.ToLower(h)] = true
  54. }
  55. fn := RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
  56. if ok := hosts[getHostname(req.URL.Host)]; !ok {
  57. return errors.New("redirect is not allowed as per DomainCheckRedirectPolicy")
  58. }
  59. return nil
  60. })
  61. return fn
  62. }
  63. func getHostname(host string) (hostname string) {
  64. if strings.Index(host, ":") > 0 {
  65. host, _, _ = net.SplitHostPort(host)
  66. }
  67. hostname = strings.ToLower(host)
  68. return
  69. }
  70. // By default Golang will not redirect request headers
  71. // after go throughing various discussion comments from thread
  72. // https://github.com/golang/go/issues/4800
  73. // go-resty will add all the headers during a redirect for the same host
  74. func checkHostAndAddHeaders(cur *http.Request, pre *http.Request) {
  75. curHostname := getHostname(cur.URL.Host)
  76. preHostname := getHostname(pre.URL.Host)
  77. if strings.EqualFold(curHostname, preHostname) {
  78. for key, val := range pre.Header {
  79. cur.Header[key] = val
  80. }
  81. } else { // only library User-Agent header is added
  82. cur.Header.Set(hdrUserAgentKey, fmt.Sprintf(hdrUserAgentValue, Version))
  83. }
  84. }