Files
Gitea/modules/session/redis.go
T
silverwind cf89ecd887 refactor!: move go-chi/session into Gitea (#39504)
The `gitea.com/go-chi/session` package only exists for Gitea, so it
moves into `modules/session` to fix its bugs directly. Fixes the flake
in
https://github.com/go-gitea/gitea/actions/runs/36726154500/job/109923538400.

- Sessions are only written back when changed, so a read-only request
can't revert a concurrent change or restore a logged-out session, like
https://github.com/go-macaron/session/commit/ae808a4a4660c802965c834299ab08f167effd12
- The session cookie is only set once a session holds data
- Every backend refreshes the expiry on load and file sessions are
written atomically
- Also fix  https://github.com/go-gitea/gitea/issues/36176

## ⚠️ BREAKING ⚠️

* the `mysql`, `postgres`, `couchbase` and `memcache` session providers
are removed, use `file`, `db` or `redis` instead
* login-related cookies are renamed to `gitea_session` and
`gitea_remember`, if you'd like to use the old names, set `COOKIE_NAME`
and `COOKIE_REMEMBER_NAME` in app.ini

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-02 21:08:14 +02:00

64 lines
1.6 KiB
Go

// Copyright 2013 Beego Authors
// Copyright 2014 The Macaron Authors
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package session
import (
"errors"
"time"
"gitea.dev/modules/graceful"
"gitea.dev/modules/nosql"
"github.com/redis/go-redis/v9"
)
type redisBackend struct {
client redis.UniversalClient
prefix string
maxLifetime time.Duration
}
func newRedisBackend(config string, maxLifetime int64) (*redisBackend, error) {
uri := nosql.ToRedisURI(config)
b := &redisBackend{
client: nosql.GetManager().GetRedisClient(uri.String()),
prefix: uri.Query().Get("prefix"),
maxLifetime: time.Duration(maxLifetime) * time.Second,
}
return b, b.client.Ping(graceful.GetManager().ShutdownContext()).Err()
}
func (b *redisBackend) load(sid string) ([]byte, error) {
ctx := graceful.GetManager().HammerContext()
var get *redis.StringCmd
_, err := b.client.Pipelined(ctx, func(pipe redis.Pipeliner) error {
get = pipe.Get(ctx, b.prefix+sid)
pipe.Expire(ctx, b.prefix+sid, b.maxLifetime)
return nil
})
if errors.Is(err, redis.Nil) {
return nil, nil
}
if err != nil {
return nil, err
}
return get.Bytes()
}
func (b *redisBackend) save(sid string, data []byte, create bool) error {
ctx := graceful.GetManager().HammerContext()
if create {
return b.client.Set(ctx, b.prefix+sid, data, b.maxLifetime).Err()
}
return b.client.SetXX(ctx, b.prefix+sid, data, b.maxLifetime).Err()
}
func (b *redisBackend) destroy(sid string) error {
return b.client.Del(graceful.GetManager().HammerContext(), b.prefix+sid).Err()
}
func (*redisBackend) gc() {}