diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 366ccbf..d140282 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -11,11 +11,11 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Docker meta id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: | ghcr.io/royxiang/plexproxy @@ -23,20 +23,20 @@ jobs: type=semver,pattern={{version}} - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v6 with: build-args: | VERSION=${{ github.ref_name }} diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 4b0aa21..6c3b00e 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -14,20 +14,20 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.17 + go-version: 1.21 - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v3 + uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser - version: latest - args: release --rm-dist + version: '~> v1' + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yml b/.goreleaser.yml index e7adaff..fb7fc35 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -20,4 +20,5 @@ changelog: exclude: - "^build\\(deps\\)" - "^Merge branch" + - "^Merge pull request" - "^Revert" diff --git a/Dockerfile b/Dockerfile index 4b26685..8b399d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.17 AS builder +FROM golang:1.21 AS builder ARG VERSION diff --git a/README.md b/README.md index 3355321..a6d23aa 100644 --- a/README.md +++ b/README.md @@ -28,15 +28,13 @@ env CGO_ENABLED=0 go install -trimpath -ldflags="-s -w" github.com/RoyXiang/plex 1. Configure environment variables in your preferred way - `PLEX_BASEURL` (Required, e.g. `http://127.0.0.1:32400`) - - `REDIS_URL` (Optional, e.g. `redis://127.0.0.1:6379`) - * If you need a cache layer, set a value for it - `PLAXT_URL` (Optional, e.g. `https://plaxt.astandke.com/api?id=generate-your-own-silly`) * `PLEX_TOKEN` is required * Set it if you run an instance of [Plaxt](https://github.com/XanderStrike/goplaxt) * Or, you can set it to [the official one](https://plaxt.astandke.com/) - `PLEX_TOKEN` (Optional, if you need it, see [here](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/)) - - `STATIC_CACHE_TTL` (Optional, default: `1h`, which controls the cache TTL of static files) - - `DYNAMIC_CACHE_TTL` (Optional, default: `1s`, which controls the cache TTL of dynamic requests) + - `STATIC_CACHE_SIZE` (Optional, the cache size of static files, e.g. CSS files, images, default: `1000`) + - `STATIC_CACHE_TTL` (Optional, the cache TTL of static files, default: `72h`) - `REDIRECT_WEB_APP` (Optional, default: `true`) - `DISABLE_TRANSCODE` (Optional, default: `true`) - `NO_REQUEST_LOGS` (Optional, default: `false`) diff --git a/go.mod b/go.mod index 7bbb5be..eec962b 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,20 @@ module github.com/RoyXiang/plexproxy -go 1.17 +go 1.21 require ( - github.com/go-chi/chi/v5 v5.0.7 - github.com/go-redis/redis/v8 v8.11.5 - github.com/gorilla/handlers v1.5.1 - github.com/gorilla/mux v1.8.0 + github.com/bluele/gcache v0.0.2 + github.com/go-chi/chi/v5 v5.1.0 + github.com/gorilla/mux v1.8.1 github.com/jrudio/go-plex-client v0.0.0-20220106065909-9e1d590b99aa github.com/xanderstrike/plexhooks v0.0.0-20200926011736-c63bcd35fe3e ) require ( - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/felixge/httpsnoop v1.0.1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/onsi/ginkgo v1.16.5 // indirect + github.com/onsi/gomega v1.18.1 // indirect ) replace github.com/jrudio/go-plex-client v0.0.0-20220106065909-9e1d590b99aa => github.com/RoyXiang/go-plex-client v0.0.0-20220313053419-e24ff7ada173 diff --git a/go.sum b/go.sum index 97fd108..e405687 100644 --- a/go.sum +++ b/go.sum @@ -4,11 +4,10 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/RoyXiang/go-plex-client v0.0.0-20220313053419-e24ff7ada173 h1:/GJ+g7nvyg3HjY+J4p5Ag+TUoWjIrJxpHWzdbA4Phzw= github.com/RoyXiang/go-plex-client v0.0.0-20220313053419-e24ff7ada173/go.mod h1:twidbPLE4eUk3CgDno5uCzpnPRboBTElH+iJrQO7S4w= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= +github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -22,18 +21,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= -github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -59,10 +52,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= diff --git a/handler/const.go b/handler/const.go index 0494caa..e2facfc 100644 --- a/handler/const.go +++ b/handler/const.go @@ -9,12 +9,19 @@ const ( headerPageStart = "X-Plex-Container-Start" headerToken = "X-Plex-Token" headerUserId = "X-Plex-User-Id" + headerAccept = "Accept" headerAcceptLanguage = "Accept-Language" headerCacheControl = "Cache-Control" headerContentType = "Content-Type" headerRange = "Range" headerVary = "Vary" + headerUpgrade = "Upgrade" + + headerForwardedFor = "X-Forwarded-For" + headerRealIP = "X-Real-IP" + headerForwardedProto = "X-Forwarded-Proto" + headerForwardedScheme = "X-Forwarded-Scheme" cachePrefixDynamic = "dynamic" cachePrefixStatic = "static" diff --git a/handler/main.go b/handler/main.go index 7f10fc9..74986dd 100644 --- a/handler/main.go +++ b/handler/main.go @@ -6,14 +6,11 @@ import ( "os" "github.com/go-chi/chi/v5/middleware" - "github.com/go-redis/redis/v8" - "github.com/gorilla/handlers" "github.com/gorilla/mux" ) var ( - plexClient *PlexClient - redisClient *redis.Client + plexClient *PlexClient emptyStruct = struct{}{} ) @@ -23,8 +20,8 @@ func init() { BaseUrl: os.Getenv("PLEX_BASEURL"), Token: os.Getenv("PLEX_TOKEN"), PlaxtUrl: os.Getenv("PLAXT_URL"), + StaticCacheSize: os.Getenv("STATIC_CACHE_SIZE"), StaticCacheTtl: os.Getenv("STATIC_CACHE_TTL"), - DynamicCacheTtl: os.Getenv("DYNAMIC_CACHE_TTL"), RedirectWebApp: os.Getenv("REDIRECT_WEB_APP"), DisableTranscode: os.Getenv("DISABLE_TRANSCODE"), NoRequestLogs: os.Getenv("NO_REQUEST_LOGS"), @@ -32,42 +29,28 @@ func init() { if plexClient == nil { log.Fatalln("Please configure PLEX_BASEURL as a valid URL at first") } - - redisUrl := os.Getenv("REDIS_URL") - if redisUrl != "" { - options, err := redis.ParseURL(redisUrl) - if err == nil { - redisClient = redis.NewClient(options) - } - } } func NewRouter() http.Handler { r := mux.NewRouter() - r.Use(handlers.ProxyHeaders, normalizeMiddleware) + r.Use(normalizeMiddleware) if !plexClient.NoRequestLogs { r.Use(middleware.Logger) } r.Use(wrapMiddleware, middleware.Recoverer, trafficMiddleware) - if redisClient != nil { - // bypass cache - r.PathPrefix("/:/").Handler(plexClient) - r.PathPrefix("/library/parts/").Handler(plexClient) - - staticRouter := r.Methods(http.MethodGet).Subrouter() - staticRouter.Use(staticMiddleware) - staticRouter.Path("/library/media/{key}/chapterImages/{id}").Handler(plexClient) - staticRouter.Path("/library/metadata/{key}/art/{id}").Handler(plexClient) - staticRouter.Path("/library/metadata/{key}/thumb/{id}").Handler(plexClient) - staticRouter.Path("/photo/:/transcode").Handler(plexClient) - staticRouter.PathPrefix("/web/js/").Handler(plexClient) - staticRouter.PathPrefix("/web/static/").Handler(plexClient) - - dynamicRouter := r.Methods(http.MethodGet).Subrouter() - dynamicRouter.Use(dynamicMiddleware) - dynamicRouter.PathPrefix("/").Handler(plexClient) - } + staticRouter := r.Methods(http.MethodGet).Subrouter() + staticRouter.Use(staticMiddleware) + staticRouter.Path("/library/media/{key}/chapterImages/{id}").Handler(plexClient) + staticRouter.Path("/library/metadata/{key}/art/{id}").Handler(plexClient) + staticRouter.Path("/library/metadata/{key}/thumb/{id}").Handler(plexClient) + staticRouter.Path("/photo/:/transcode").Handler(plexClient) + staticRouter.PathPrefix("/web/js/").Handler(plexClient) + staticRouter.PathPrefix("/web/static/").Handler(plexClient) + + dynamicRouter := r.Methods(http.MethodGet).Subrouter() + dynamicRouter.Use(dynamicMiddleware) + dynamicRouter.PathPrefix("/").Handler(plexClient) r.PathPrefix("/").Handler(plexClient) return r diff --git a/handler/middleware.go b/handler/middleware.go index 1ac9d87..ea603ba 100644 --- a/handler/middleware.go +++ b/handler/middleware.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "net/http/httptest" + "net/http/httputil" "net/url" "path/filepath" "strconv" @@ -15,6 +16,7 @@ import ( "time" "github.com/RoyXiang/plexproxy/common" + "github.com/bluele/gcache" ) var ( @@ -111,7 +113,6 @@ func staticMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), cacheInfoCtxKey, &cacheInfo{ Prefix: cachePrefixStatic, - Ttl: plexClient.staticCacheTtl, }) r = r.WithContext(ctx) cacheMiddleware(next).ServeHTTP(w, r) @@ -120,26 +121,27 @@ func staticMiddleware(next http.Handler) http.Handler { func dynamicMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var ctx context.Context + nr := r switch filepath.Ext(r.URL.EscapedPath()) { - case ".css", ".ico", ".jpeg", ".jpg", ".webp": - ctx = context.WithValue(r.Context(), cacheInfoCtxKey, &cacheInfo{ + case ".css", ".ico", ".jpeg", ".jpg", ".js", ".webp": + ctx := context.WithValue(r.Context(), cacheInfoCtxKey, &cacheInfo{ Prefix: cachePrefixStatic, - Ttl: plexClient.staticCacheTtl, }) + nr = r.WithContext(ctx) case ".m3u8", ".ts": - ctx = r.Context() + break default: if rh := r.Header.Get(headerRange); rh != "" { - ctx = r.Context() + break + } else if upgrade := r.Header.Get(headerUpgrade); upgrade == "websocket" { break } - ctx = context.WithValue(r.Context(), cacheInfoCtxKey, &cacheInfo{ + ctx := context.WithValue(r.Context(), cacheInfoCtxKey, &cacheInfo{ Prefix: cachePrefixDynamic, - Ttl: plexClient.dynamicCacheTtl, }) + nr = r.WithContext(ctx) } - cacheMiddleware(next).ServeHTTP(w, r.WithContext(ctx)) + cacheMiddleware(next).ServeHTTP(w, nr) }) } @@ -151,8 +153,8 @@ func cacheMiddleware(next http.Handler) http.Handler { return } + var cache gcache.Cache var cacheKey string - ctx := context.Background() info := ctxValue.(*cacheInfo) defer func() { @@ -161,9 +163,8 @@ func cacheMiddleware(next http.Handler) http.Handler { return } var resp *http.Response - b, err := redisClient.Get(ctx, cacheKey).Bytes() - if err == nil { - reader := bufio.NewReader(bytes.NewReader(b)) + if cacheVal, err := cache.Get(cacheKey); err == nil { + reader := bufio.NewReader(bytes.NewReader(cacheVal.([]byte))) resp, _ = http.ReadResponse(reader, r) } if resp == nil { @@ -174,8 +175,11 @@ func cacheMiddleware(next http.Handler) http.Handler { w.Header().Set(headerCacheStatus, "MISS") w.WriteHeader(resp.StatusCode) _, _ = w.Write(nw.Unwrap().(*httptest.ResponseRecorder).Body.Bytes()) - if resp.StatusCode == http.StatusOK { - writeToCache(cacheKey, resp, info.Ttl) + if resp.StatusCode != http.StatusOK { + return + } + if b, err := httputil.DumpResponse(resp, true); err == nil { + cache.Set(cacheKey, b) } }() } else { @@ -183,9 +187,6 @@ func cacheMiddleware(next http.Handler) http.Handler { w.Header().Set(headerCacheStatus, "HIT") w.WriteHeader(resp.StatusCode) _, _ = io.Copy(w, resp.Body) - if info.Prefix == cachePrefixStatic { - writeToCache(cacheKey, resp, info.Ttl) - } }() } for k, v := range resp.Header { @@ -195,7 +196,10 @@ func cacheMiddleware(next http.Handler) http.Handler { params := r.URL.Query() switch info.Prefix { case cachePrefixStatic: - break + if plexClient.staticCache == nil { + return + } + cache = plexClient.staticCache case cachePrefixDynamic: if user := r.Context().Value(userCtxKey); user != nil { params.Set(headerUserId, strconv.Itoa(user.(*plexUser).Id)) @@ -205,6 +209,7 @@ func cacheMiddleware(next http.Handler) http.Handler { } else { return } + cache = plexClient.dynamicCache default: // invalid prefix return diff --git a/handler/plex.go b/handler/plex.go index b8682a0..4ce2ff0 100644 --- a/handler/plex.go +++ b/handler/plex.go @@ -2,7 +2,6 @@ package handler import ( "bytes" - "context" "crypto/tls" "encoding/json" "fmt" @@ -16,6 +15,7 @@ import ( "time" "github.com/RoyXiang/plexproxy/common" + "github.com/bluele/gcache" "github.com/jrudio/go-plex-client" "github.com/xanderstrike/plexhooks" ) @@ -24,8 +24,8 @@ type PlexConfig struct { BaseUrl string Token string PlaxtUrl string + StaticCacheSize string StaticCacheTtl string - DynamicCacheTtl string RedirectWebApp string DisableTranscode string NoRequestLogs string @@ -35,8 +35,8 @@ type PlexClient struct { proxy *httputil.ReverseProxy client *plex.Plex - staticCacheTtl time.Duration - dynamicCacheTtl time.Duration + staticCache gcache.Cache + dynamicCache gcache.Cache plaxtUrl string redirectWebApp bool @@ -78,14 +78,18 @@ func NewPlexClient(config PlexConfig) *PlexClient { plaxtUrl = u.String() } - staticCacheTtl, err := time.ParseDuration(config.StaticCacheTtl) - if err != nil { - staticCacheTtl = time.Hour + var ( + staticCacheSize int + staticCacheTtl time.Duration + ) + if staticCacheSize, err = strconv.Atoi(config.StaticCacheSize); err != nil || staticCacheSize <= 0 { + staticCacheSize = 1000 } - dynamicCacheTtl, err := time.ParseDuration(config.DynamicCacheTtl) - if err != nil { - dynamicCacheTtl = time.Second + if staticCacheTtl, err = time.ParseDuration(config.StaticCacheTtl); err != nil { + staticCacheTtl = time.Hour * 24 * 3 } + staticCache := gcache.New(staticCacheSize).LFU().Expiration(staticCacheTtl).Build() + dynamicCache := gcache.New(100).LRU().Expiration(time.Second).Build() var redirectWebApp, disableTranscode, noRequestLogs bool if b, err := strconv.ParseBool(config.RedirectWebApp); err == nil { @@ -108,8 +112,8 @@ func NewPlexClient(config PlexConfig) *PlexClient { proxy: proxy, client: client, plaxtUrl: plaxtUrl, - staticCacheTtl: staticCacheTtl, - dynamicCacheTtl: dynamicCacheTtl, + staticCache: staticCache, + dynamicCache: dynamicCache, redirectWebApp: redirectWebApp, disableTranscode: disableTranscode, NoRequestLogs: noRequestLogs, @@ -133,10 +137,7 @@ func (a sessionData) Check(b sessionData) bool { return true } if a.progress != b.progress { - if a.status == sessionPlaying { - return false - } - return true + return a.status != sessionPlaying } return false } @@ -192,28 +193,12 @@ func (c *PlexClient) fetchUsers(token string) { c.MulLock.Lock(lockKeyUsers) defer c.MulLock.Unlock(lockKeyUsers) - ctx := context.Background() - cacheKey := fmt.Sprintf("%s:token:%s", cachePrefixPlex, token) - isCacheEnabled := redisClient != nil - - if isCacheEnabled { - var user plexUser - err := redisClient.Get(ctx, cacheKey).Scan(&user) - if err == nil { - c.users[token] = &user - return - } - } - userInfo := c.GetAccountInfo(token) if userInfo.ID > 0 { user := plexUser{ Id: userInfo.ID, Username: userInfo.Username, } - if isCacheEnabled { - redisClient.Set(ctx, cacheKey, &user, 0).Val() - } c.users[token] = &user return } @@ -225,10 +210,6 @@ func (c *PlexClient) fetchUsers(token string) { Id: friend.UserId, Username: friend.Username, } - if isCacheEnabled { - cacheKey = fmt.Sprintf("%s:token:%s", cachePrefixPlex, friend.AccessToken) - redisClient.Set(ctx, cacheKey, &user, 0).Val() - } c.users[friend.AccessToken] = &user } } diff --git a/handler/utils.go b/handler/utils.go index a0941ce..0debb5c 100644 --- a/handler/utils.go +++ b/handler/utils.go @@ -2,12 +2,11 @@ package handler import ( "context" + "mime" "net/http" - "net/http/httputil" "net/url" "runtime/debug" "strings" - "time" "github.com/go-chi/chi/v5/middleware" ) @@ -20,22 +19,18 @@ func wrapResponseWriter(w http.ResponseWriter, protoMajor int) middleware.WrapRe } func modifyResponse(resp *http.Response) error { - contentType := resp.Header.Get(headerContentType) - if contentType == "" { - return nil + var mediaType string + if contentType := resp.Header.Get(headerContentType); contentType != "" { + mediaType, _, _ = mime.ParseMediaType(contentType) } - pieces := strings.Split(contentType, "/") - if len(pieces) == 0 { - return nil - } - switch pieces[0] { - case "audio", "video": - resp.Header.Set(headerCacheControl, "no-cache") - resp.Header.Set(headerVary, "*") - case "image": + switch { + case mediaType == "text/css", + mediaType == "text/javascript", + strings.HasPrefix(mediaType, "image/"), + strings.HasPrefix(mediaType, "font/"): resp.Header.Set(headerCacheControl, "public, max-age=86400, s-maxage=259200") default: - resp.Header.Set(headerCacheControl, "no-cache") + resp.Header.Set(headerCacheControl, "no-cache, no-store, no-transform, must-revalidate, private, max-age=0, s-maxage=0") } return nil } @@ -67,9 +62,37 @@ func cloneRequest(r *http.Request, headers http.Header, query url.Values) *http. nr.URL.RawQuery = query.Encode() nr.RequestURI = nr.URL.RequestURI() } + if fwd := getIP(headers); fwd != "" { + nr.RemoteAddr = fwd + } + if scheme := getScheme(headers); scheme != "" { + nr.URL.Scheme = scheme + } return nr } +func getIP(headers http.Header) (addr string) { + if fwd := headers.Get(headerForwardedFor); fwd != "" { + s := strings.Index(fwd, ", ") + if s == -1 { + s = len(fwd) + } + addr = fwd[:s] + } else if fwd = headers.Get(headerRealIP); fwd != "" { + addr = fwd + } + return +} + +func getScheme(headers http.Header) (scheme string) { + if proto := headers.Get(headerForwardedProto); proto != "" { + scheme = strings.ToLower(proto) + } else if proto = headers.Get(headerForwardedScheme); proto != "" { + scheme = strings.ToLower(proto) + } + return +} + func getAcceptContentType(r *http.Request) string { accept := r.Header.Get(headerAccept) if accept == "" { @@ -89,11 +112,3 @@ func getAcceptContentType(r *http.Request) string { } return contentTypeXml } - -func writeToCache(key string, resp *http.Response, ttl time.Duration) { - b, err := httputil.DumpResponse(resp, true) - if err != nil { - return - } - redisClient.Set(context.Background(), key, b, ttl) -}