cookies.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package cookies
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/http/cookiejar"
  6. "net/url"
  7. "slices"
  8. "sync"
  9. "golang.org/x/net/publicsuffix"
  10. "github.com/imgproxy/imgproxy/v3/httpheaders"
  11. "github.com/imgproxy/imgproxy/v3/ierrors"
  12. )
  13. // Cookies represents a cookies manager
  14. type Cookies struct {
  15. config *Config
  16. }
  17. // cookieJar is a cookie jar that stores all cookies in memory
  18. // and doesn't care about domains and paths
  19. type cookieJar struct {
  20. entries []*http.Cookie
  21. mu sync.RWMutex
  22. }
  23. // New creates a new Cookies instance
  24. func New(config *Config) (*Cookies, error) {
  25. if err := config.Validate(); err != nil {
  26. return nil, err
  27. }
  28. cookies := &Cookies{config: config}
  29. return cookies, nil
  30. }
  31. // SetCookies stores the cookies in the jar. For each source cookie it creates
  32. // a new cookie with only Name, Value and Quoted fields set.
  33. func (j *cookieJar) SetCookies(u *url.URL, cookies []*http.Cookie) {
  34. j.mu.Lock()
  35. defer j.mu.Unlock()
  36. // Remove all unimportant cookie params
  37. for _, c := range cookies {
  38. j.entries = append(j.entries, &http.Cookie{
  39. Name: c.Name,
  40. Value: c.Value,
  41. Quoted: c.Quoted,
  42. })
  43. }
  44. }
  45. // Cookies returns all stored cookies
  46. func (j *cookieJar) Cookies(u *url.URL) []*http.Cookie {
  47. j.mu.RLock()
  48. defer j.mu.RUnlock()
  49. // NOTE: do we need to clone, or we could just return a ref?
  50. return slices.Clone(j.entries)
  51. }
  52. // JarFromRequest creates a cookie jar from the given HTTP request
  53. func (c *Cookies) JarFromRequest(r *http.Request) (jar http.CookieJar, err error) {
  54. // If cookie passthrough is disabled, return nil jar
  55. if !c.config.CookiePassthrough {
  56. return nil, nil
  57. }
  58. if c.config.CookiePassthroughAll {
  59. jar = &cookieJar{}
  60. } else {
  61. jar, err = cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
  62. if err != nil {
  63. return nil, err
  64. }
  65. }
  66. if r == nil {
  67. return jar, nil
  68. }
  69. var cookieBase *url.URL
  70. if !c.config.CookiePassthroughAll {
  71. if len(c.config.CookieBaseURL) > 0 {
  72. if cookieBase, err = url.Parse(c.config.CookieBaseURL); err != nil {
  73. return nil, ierrors.Wrap(cookieError(fmt.Sprintf("can't parse cookie base URL: %s", err)), 0)
  74. }
  75. }
  76. if cookieBase == nil {
  77. scheme := r.Header.Get(httpheaders.XForwardedProto)
  78. if len(scheme) == 0 {
  79. scheme = "http"
  80. }
  81. host := r.Header.Get(httpheaders.XForwardedHost)
  82. if len(host) == 0 {
  83. host = r.Header.Get(httpheaders.Host)
  84. }
  85. if len(host) == 0 {
  86. host = r.Host
  87. }
  88. if len(host) == 0 {
  89. return jar, nil
  90. }
  91. port := r.Header.Get(httpheaders.XForwardedPort)
  92. if len(port) > 0 {
  93. host = host + ":" + port
  94. }
  95. cookieBase = &url.URL{
  96. Scheme: scheme,
  97. Host: host,
  98. }
  99. }
  100. }
  101. jar.SetCookies(cookieBase, r.Cookies())
  102. return jar, nil
  103. }