You've already forked imgproxy
mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-12-11 23:48:13 +02:00
Add option to pass through incoming cookies in the image request.
This commit is contained in:
@@ -27,6 +27,7 @@ var (
|
|||||||
|
|
||||||
TTL int
|
TTL int
|
||||||
CacheControlPassthrough bool
|
CacheControlPassthrough bool
|
||||||
|
CookiePassthrough bool
|
||||||
SetCanonicalHeader bool
|
SetCanonicalHeader bool
|
||||||
|
|
||||||
SoReuseport bool
|
SoReuseport bool
|
||||||
@@ -87,7 +88,8 @@ var (
|
|||||||
|
|
||||||
ETagEnabled bool
|
ETagEnabled bool
|
||||||
|
|
||||||
BaseURL string
|
BaseURL string
|
||||||
|
CookieBaseURL string
|
||||||
|
|
||||||
Presets []string
|
Presets []string
|
||||||
OnlyPresets bool
|
OnlyPresets bool
|
||||||
@@ -155,6 +157,7 @@ func Reset() {
|
|||||||
|
|
||||||
TTL = 3600
|
TTL = 3600
|
||||||
CacheControlPassthrough = false
|
CacheControlPassthrough = false
|
||||||
|
CookiePassthrough = false
|
||||||
SetCanonicalHeader = false
|
SetCanonicalHeader = false
|
||||||
|
|
||||||
SoReuseport = false
|
SoReuseport = false
|
||||||
@@ -216,6 +219,7 @@ func Reset() {
|
|||||||
ETagEnabled = false
|
ETagEnabled = false
|
||||||
|
|
||||||
BaseURL = ""
|
BaseURL = ""
|
||||||
|
CookieBaseURL = ""
|
||||||
|
|
||||||
Presets = make([]string, 0)
|
Presets = make([]string, 0)
|
||||||
OnlyPresets = false
|
OnlyPresets = false
|
||||||
@@ -279,6 +283,7 @@ func Configure() error {
|
|||||||
|
|
||||||
configurators.Int(&TTL, "IMGPROXY_TTL")
|
configurators.Int(&TTL, "IMGPROXY_TTL")
|
||||||
configurators.Bool(&CacheControlPassthrough, "IMGPROXY_CACHE_CONTROL_PASSTHROUGH")
|
configurators.Bool(&CacheControlPassthrough, "IMGPROXY_CACHE_CONTROL_PASSTHROUGH")
|
||||||
|
configurators.Bool(&CookiePassthrough, "IMGPROXY_COOKIE_PASSTHROUGH")
|
||||||
configurators.Bool(&SetCanonicalHeader, "IMGPROXY_SET_CANONICAL_HEADER")
|
configurators.Bool(&SetCanonicalHeader, "IMGPROXY_SET_CANONICAL_HEADER")
|
||||||
|
|
||||||
configurators.Bool(&SoReuseport, "IMGPROXY_SO_REUSEPORT")
|
configurators.Bool(&SoReuseport, "IMGPROXY_SO_REUSEPORT")
|
||||||
@@ -360,6 +365,7 @@ func Configure() error {
|
|||||||
configurators.Bool(&ETagEnabled, "IMGPROXY_USE_ETAG")
|
configurators.Bool(&ETagEnabled, "IMGPROXY_USE_ETAG")
|
||||||
|
|
||||||
configurators.String(&BaseURL, "IMGPROXY_BASE_URL")
|
configurators.String(&BaseURL, "IMGPROXY_BASE_URL")
|
||||||
|
configurators.String(&CookieBaseURL, "IMGPROXY_COOKIE_BASE_URL")
|
||||||
|
|
||||||
configurators.StringSlice(&Presets, "IMGPROXY_PRESETS")
|
configurators.StringSlice(&Presets, "IMGPROXY_PRESETS")
|
||||||
if err := configurators.StringSliceFile(&Presets, *presetsPath); err != nil {
|
if err := configurators.StringSliceFile(&Presets, *presetsPath); err != nil {
|
||||||
|
|||||||
@@ -88,6 +88,17 @@ Also you may want imgproxy to respond with the same error message that it writes
|
|||||||
|
|
||||||
* `IMGPROXY_DEVELOPMENT_ERRORS_MODE`: when true, imgproxy will respond with detailed error messages. Not recommended for production because some errors may contain stack trace.
|
* `IMGPROXY_DEVELOPMENT_ERRORS_MODE`: when true, imgproxy will respond with detailed error messages. Not recommended for production because some errors may contain stack trace.
|
||||||
|
|
||||||
|
## Cookies
|
||||||
|
|
||||||
|
imgproxy can pass through cookies in image requests. This can be activated with `IMGPROXY_COOKIE_PASSTHROUGH`. Unfortunately a `Cookie` header doesn't contain information for which URLs these cookies are applicable, so imgproxy can only assume (or must be told).
|
||||||
|
|
||||||
|
When cookie forwarding is activated, imgproxy by default assumes the scope of the cookies to be all URLs with the same hostname/port and request scheme as given by the headers `X-Forwarded-Host`, `X-Forwarded-Port`, `X-Forwarded-Scheme` or `Host`. To change that use `IMGPROXY_COOKIE_BASE_URL`.
|
||||||
|
|
||||||
|
* `IMGPROXY_COOKIE_PASSTHROUGH`: when `true`, incoming cookies will be passed through to the image request if they are applicable for the image URL. Default: false;
|
||||||
|
|
||||||
|
* `IMGPROXY_COOKIE_BASE_URL`: when set, assume that cookies have a scope of this URL for the incoming request (instead of using the request headers). If the cookies are applicable to the image URL too, they will be passed along in the image request.
|
||||||
|
|
||||||
|
|
||||||
## Compression
|
## Compression
|
||||||
|
|
||||||
* `IMGPROXY_QUALITY`: default quality of the resulting image, percentage. Default: `80`;
|
* `IMGPROXY_QUALITY`: default quality of the resulting image, percentage. Default: `80`;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/imgproxy/imgproxy/v3/config"
|
"github.com/imgproxy/imgproxy/v3/config"
|
||||||
@@ -113,7 +114,7 @@ func headersToStore(res *http.Response) map[string]string {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestImage(imageURL string, header http.Header) (*http.Response, error) {
|
func requestImage(imageURL string, header http.Header, jar *cookiejar.Jar) (*http.Response, error) {
|
||||||
req, err := http.NewRequest("GET", imageURL, nil)
|
req, err := http.NewRequest("GET", imageURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ierrors.New(404, err.Error(), msgSourceImageIsUnreachable)
|
return nil, ierrors.New(404, err.Error(), msgSourceImageIsUnreachable)
|
||||||
@@ -127,6 +128,12 @@ func requestImage(imageURL string, header http.Header) (*http.Response, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if jar != nil {
|
||||||
|
for _, cookie := range jar.Cookies(req.URL) {
|
||||||
|
req.AddCookie(cookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
req.Header.Set("User-Agent", config.UserAgent)
|
req.Header.Set("User-Agent", config.UserAgent)
|
||||||
|
|
||||||
for k, v := range header {
|
for k, v := range header {
|
||||||
@@ -160,13 +167,13 @@ func requestImage(imageURL string, header http.Header) (*http.Response, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func download(imageURL string, header http.Header) (*ImageData, error) {
|
func download(imageURL string, header http.Header, jar *cookiejar.Jar) (*ImageData, error) {
|
||||||
// We use this for testing
|
// We use this for testing
|
||||||
if len(redirectAllRequestsTo) > 0 {
|
if len(redirectAllRequestsTo) > 0 {
|
||||||
imageURL = redirectAllRequestsTo
|
imageURL = redirectAllRequestsTo
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := requestImage(imageURL, header)
|
res, err := requestImage(imageURL, header, jar)
|
||||||
if res != nil {
|
if res != nil {
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -70,7 +71,7 @@ func loadWatermark() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(config.WatermarkURL) > 0 {
|
if len(config.WatermarkURL) > 0 {
|
||||||
Watermark, err = Download(config.WatermarkURL, "watermark", nil)
|
Watermark, err = Download(config.WatermarkURL, "watermark", nil, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +90,7 @@ func loadFallbackImage() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(config.FallbackImageURL) > 0 {
|
if len(config.FallbackImageURL) > 0 {
|
||||||
FallbackImage, err = Download(config.FallbackImageURL, "fallback image", nil)
|
FallbackImage, err = Download(config.FallbackImageURL, "fallback image", nil, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +128,8 @@ func FromFile(path, desc string) (*ImageData, error) {
|
|||||||
return imgdata, nil
|
return imgdata, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Download(imageURL, desc string, header http.Header) (*ImageData, error) {
|
func Download(imageURL, desc string, header http.Header, jar *cookiejar.Jar) (*ImageData, error) {
|
||||||
imgdata, err := download(imageURL, header)
|
imgdata, err := download(imageURL, header, jar)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if nmErr, ok := err.(*ErrorNotModified); ok {
|
if nmErr, ok := err.(*ErrorNotModified); ok {
|
||||||
nmErr.Message = fmt.Sprintf("Can't download %s: %s", desc, nmErr.Message)
|
nmErr.Message = fmt.Sprintf("Can't download %s: %s", desc, nmErr.Message)
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
|
"golang.org/x/net/publicsuffix"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -135,6 +138,48 @@ func respondWithNotModified(reqID string, r *http.Request, rw http.ResponseWrite
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cookieJarFromRequest(r *http.Request) (*cookiejar.Jar, error) {
|
||||||
|
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.CookiePassthrough && r != nil {
|
||||||
|
cookieBase := config.CookieBaseURL
|
||||||
|
|
||||||
|
if len(cookieBase) == 0 {
|
||||||
|
scheme := r.Header.Get("X-Forwarded-Proto")
|
||||||
|
if len(scheme) == 0 {
|
||||||
|
scheme = "http"
|
||||||
|
}
|
||||||
|
host := r.Header.Get("X-Forwarded-Host")
|
||||||
|
if len(host) == 0 {
|
||||||
|
host = r.Header.Get("Host")
|
||||||
|
}
|
||||||
|
if len(host) == 0 {
|
||||||
|
cookieBase = ""
|
||||||
|
} else {
|
||||||
|
port := r.Header.Get("X-Forwarded-Port")
|
||||||
|
if len(port) > 0 {
|
||||||
|
host = host + ":" + port
|
||||||
|
}
|
||||||
|
cookieBase = scheme + "://" + host + "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cookieBase) > 0 {
|
||||||
|
cookieBaseURL, err := url.Parse(cookieBase)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
jar.SetCookies(cookieBaseURL, r.Cookies())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jar, nil
|
||||||
|
}
|
||||||
|
|
||||||
func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
|
func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
|
||||||
ctx, timeoutCancel := context.WithTimeout(r.Context(), time.Duration(config.WriteTimeout)*time.Second)
|
ctx, timeoutCancel := context.WithTimeout(r.Context(), time.Duration(config.WriteTimeout)*time.Second)
|
||||||
defer timeoutCancel()
|
defer timeoutCancel()
|
||||||
@@ -175,6 +220,11 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
|
|||||||
panic(ierrors.New(404, fmt.Sprintf("Source URL is not allowed: %s", imageURL), "Invalid source"))
|
panic(ierrors.New(404, fmt.Sprintf("Source URL is not allowed: %s", imageURL), "Invalid source"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jar, err := cookieJarFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// SVG is a special case. Though saving to svg is not supported, SVG->SVG is.
|
// SVG is a special case. Though saving to svg is not supported, SVG->SVG is.
|
||||||
if !vips.SupportsSave(po.Format) && po.Format != imagetype.Unknown && po.Format != imagetype.SVG {
|
if !vips.SupportsSave(po.Format) && po.Format != imagetype.Unknown && po.Format != imagetype.SVG {
|
||||||
panic(ierrors.New(
|
panic(ierrors.New(
|
||||||
@@ -213,7 +263,7 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
originData, err := func() (*imagedata.ImageData, error) {
|
originData, err := func() (*imagedata.ImageData, error) {
|
||||||
defer metrics.StartDownloadingSegment(ctx)()
|
defer metrics.StartDownloadingSegment(ctx)()
|
||||||
return imagedata.Download(imageURL, "source image", imgRequestHeader)
|
return imagedata.Download(imageURL, "source image", imgRequestHeader, jar)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user