mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-03-17 20:17:48 +02:00
Merge branch 'feature/cookie-passthrough' of github.com:LivingLogic/imgproxy into feature/cookie-passthrough
This commit is contained in:
commit
475e67640b
@ -27,6 +27,7 @@ var (
|
||||
|
||||
TTL int
|
||||
CacheControlPassthrough bool
|
||||
CookiePassthrough bool
|
||||
SetCanonicalHeader bool
|
||||
|
||||
SoReuseport bool
|
||||
@ -87,7 +88,8 @@ var (
|
||||
|
||||
ETagEnabled bool
|
||||
|
||||
BaseURL string
|
||||
BaseURL string
|
||||
CookieBaseURL string
|
||||
|
||||
Presets []string
|
||||
OnlyPresets bool
|
||||
@ -155,6 +157,7 @@ func Reset() {
|
||||
|
||||
TTL = 3600
|
||||
CacheControlPassthrough = false
|
||||
CookiePassthrough = false
|
||||
SetCanonicalHeader = false
|
||||
|
||||
SoReuseport = false
|
||||
@ -216,6 +219,7 @@ func Reset() {
|
||||
ETagEnabled = false
|
||||
|
||||
BaseURL = ""
|
||||
CookieBaseURL = ""
|
||||
|
||||
Presets = make([]string, 0)
|
||||
OnlyPresets = false
|
||||
@ -279,6 +283,7 @@ func Configure() error {
|
||||
|
||||
configurators.Int(&TTL, "IMGPROXY_TTL")
|
||||
configurators.Bool(&CacheControlPassthrough, "IMGPROXY_CACHE_CONTROL_PASSTHROUGH")
|
||||
configurators.Bool(&CookiePassthrough, "IMGPROXY_COOKIE_PASSTHROUGH")
|
||||
configurators.Bool(&SetCanonicalHeader, "IMGPROXY_SET_CANONICAL_HEADER")
|
||||
|
||||
configurators.Bool(&SoReuseport, "IMGPROXY_SO_REUSEPORT")
|
||||
@ -360,6 +365,7 @@ func Configure() error {
|
||||
configurators.Bool(&ETagEnabled, "IMGPROXY_USE_ETAG")
|
||||
|
||||
configurators.String(&BaseURL, "IMGPROXY_BASE_URL")
|
||||
configurators.String(&CookieBaseURL, "IMGPROXY_COOKIE_BASE_URL")
|
||||
|
||||
configurators.StringSlice(&Presets, "IMGPROXY_PRESETS")
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
* `IMGPROXY_QUALITY`: default quality of the resulting image, percentage. Default: `80`;
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"time"
|
||||
|
||||
"github.com/imgproxy/imgproxy/v3/config"
|
||||
@ -113,7 +114,7 @@ func headersToStore(res *http.Response) map[string]string {
|
||||
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)
|
||||
if err != nil {
|
||||
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)
|
||||
|
||||
for k, v := range header {
|
||||
@ -160,13 +167,13 @@ func requestImage(imageURL string, header http.Header) (*http.Response, error) {
|
||||
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
|
||||
if len(redirectAllRequestsTo) > 0 {
|
||||
imageURL = redirectAllRequestsTo
|
||||
}
|
||||
|
||||
res, err := requestImage(imageURL, header)
|
||||
res, err := requestImage(imageURL, header, jar)
|
||||
if res != nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -70,7 +71,7 @@ func loadWatermark() (err error) {
|
||||
}
|
||||
|
||||
if len(config.WatermarkURL) > 0 {
|
||||
Watermark, err = Download(config.WatermarkURL, "watermark", nil)
|
||||
Watermark, err = Download(config.WatermarkURL, "watermark", nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@ -89,7 +90,7 @@ func loadFallbackImage() (err error) {
|
||||
}
|
||||
|
||||
if len(config.FallbackImageURL) > 0 {
|
||||
FallbackImage, err = Download(config.FallbackImageURL, "fallback image", nil)
|
||||
FallbackImage, err = Download(config.FallbackImageURL, "fallback image", nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@ -127,8 +128,8 @@ func FromFile(path, desc string) (*ImageData, error) {
|
||||
return imgdata, nil
|
||||
}
|
||||
|
||||
func Download(imageURL, desc string, header http.Header) (*ImageData, error) {
|
||||
imgdata, err := download(imageURL, header)
|
||||
func Download(imageURL, desc string, header http.Header, jar *cookiejar.Jar) (*ImageData, error) {
|
||||
imgdata, err := download(imageURL, header, jar)
|
||||
if err != nil {
|
||||
if nmErr, ok := err.(*ErrorNotModified); ok {
|
||||
nmErr.Message = fmt.Sprintf("Can't download %s: %s", desc, nmErr.Message)
|
||||
|
@ -3,7 +3,10 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
"strconv"
|
||||
"strings"
|
||||
"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) {
|
||||
ctx, timeoutCancel := context.WithTimeout(r.Context(), time.Duration(config.WriteTimeout)*time.Second)
|
||||
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"))
|
||||
}
|
||||
|
||||
jar, err := cookieJarFromRequest(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
panic(ierrors.New(
|
||||
@ -213,7 +263,7 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
originData, err := func() (*imagedata.ImageData, error) {
|
||||
defer metrics.StartDownloadingSegment(ctx)()
|
||||
return imagedata.Download(imageURL, "source image", imgRequestHeader)
|
||||
return imagedata.Download(imageURL, "source image", imgRequestHeader, jar)
|
||||
}()
|
||||
|
||||
if err == nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user