package session import ( "context" "errors" "net/http" "sync" "go.neonxp.ru/mux" "go.neonxp.ru/mux/middleware" "go.neonxp.ru/objectid" ) type Config struct { SessionCookie string Path string Domain string Secure bool HttpOnly bool MaxAge int } var DefaultConfig Config = Config{ SessionCookie: "_session", Path: "/", Domain: "", Secure: false, HttpOnly: true, MaxAge: 30 * 3600, } func Middleware(config Config, storer Store) mux.Middleware { if storer == nil { storer = &MemoryStore{store: sync.Map{}} } return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var ( sessionID string values Value ) cookie, err := r.Cookie(config.SessionCookie) switch { case err == nil: sessionID = cookie.Value values = storer.Load(r.Context(), sessionID) case errors.Is(err, http.ErrNoCookie): sessionID = objectid.New().String() values = Value{} } http.SetCookie(w, &http.Cookie{ Name: config.SessionCookie, Value: sessionID, Path: config.Path, Domain: config.Domain, Secure: config.Secure, HttpOnly: config.HttpOnly, MaxAge: config.MaxAge, }) ctx := context.WithValue(r.Context(), middleware.SessionValueKey, &values) ctx = context.WithValue(ctx, middleware.SessionIDKey, sessionID) ctx = context.WithValue(ctx, middleware.SessionConfigKey, config) ctx = context.WithValue(ctx, middleware.SessionStorerKey, storer) h.ServeHTTP(w, r.WithContext(ctx)) storer.Save(r.Context(), sessionID, values) }) } } func FromRequest(r *http.Request) *Value { return r.Context().Value(middleware.SessionValueKey).(*Value) } func Clear(w http.ResponseWriter, r *http.Request) { storer := r.Context().Value(middleware.SessionStorerKey).(Store) sessionID := r.Context().Value(middleware.SessionIDKey).(string) storer.Remove(r.Context(), sessionID) config := r.Context().Value(middleware.SessionConfigKey).(Config) http.SetCookie(w, &http.Cookie{ Name: config.SessionCookie, Value: sessionID, Path: config.Path, Domain: config.Domain, Secure: config.Secure, HttpOnly: config.HttpOnly, MaxAge: -1, }) }