diff --git a/.gitignore b/.gitignore index 84039fe..1e32509 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ +/.vscode +go.sum coverage.coverprofile +testdata diff --git a/cookie.go b/cookie.go deleted file mode 100644 index fd6f48c..0000000 --- a/cookie.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sessions - -import "net/http" - -// newCookieFromOptions returns an http.Cookie with the options set. -func newCookieFromOptions(name, value string, options *Options) *http.Cookie { - return &http.Cookie{ - Name: name, - Value: value, - Path: options.Path, - Domain: options.Domain, - MaxAge: options.MaxAge, - Secure: options.Secure, - HttpOnly: options.HttpOnly, - Partitioned: options.Partitioned, - SameSite: options.SameSite, - } - -} diff --git a/cookie_test.go b/cookie_test.go deleted file mode 100644 index de530ca..0000000 --- a/cookie_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sessions - -import ( - "testing" -) - -// Test for creating new http.Cookie from name, value and options -func TestNewCookieFromOptions(t *testing.T) { - tests := []struct { - name string - value string - path string - domain string - maxAge int - secure bool - httpOnly bool - partitioned bool - }{ - {"", "bar", "/foo/bar", "foo.example.com", 3600, true, true, true}, - {"foo", "", "/foo/bar", "foo.example.com", 3600, true, true, true}, - {"foo", "bar", "", "foo.example.com", 3600, true, true, true}, - {"foo", "bar", "/foo/bar", "", 3600, true, true, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 0, true, true, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 3600, false, true, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, false, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, true, false}, - } - for i, v := range tests { - options := &Options{ - Path: v.path, - Domain: v.domain, - MaxAge: v.maxAge, - Secure: v.secure, - HttpOnly: v.httpOnly, - Partitioned: v.partitioned, - } - cookie := newCookieFromOptions(v.name, v.value, options) - if cookie.Name != v.name { - t.Fatalf("%v: bad cookie name: got %q, want %q", i+1, cookie.Name, v.name) - } - if cookie.Value != v.value { - t.Fatalf("%v: bad cookie value: got %q, want %q", i+1, cookie.Value, v.value) - } - if cookie.Path != v.path { - t.Fatalf("%v: bad cookie path: got %q, want %q", i+1, cookie.Path, v.path) - } - if cookie.Domain != v.domain { - t.Fatalf("%v: bad cookie domain: got %q, want %q", i+1, cookie.Domain, v.domain) - } - if cookie.MaxAge != v.maxAge { - t.Fatalf("%v: bad cookie maxAge: got %q, want %q", i+1, cookie.MaxAge, v.maxAge) - } - if cookie.Secure != v.secure { - t.Fatalf("%v: bad cookie secure: got %v, want %v", i+1, cookie.Secure, v.secure) - } - if cookie.HttpOnly != v.httpOnly { - t.Fatalf("%v: bad cookie httpOnly: got %v, want %v", i+1, cookie.HttpOnly, v.httpOnly) - } - if cookie.Partitioned != v.partitioned { - t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) - } - } -} diff --git a/doc.go b/doc.go index 946bf5c..6a7a44e 100644 --- a/doc.go +++ b/doc.go @@ -8,22 +8,22 @@ infrastructure for custom session backends. The key features are: - * Simple API: use it as an easy way to set signed (and optionally - encrypted) cookies. - * Built-in backends to store sessions in cookies or the filesystem. - * Flash messages: session values that last until read. - * Convenient way to switch session persistency (aka "remember me") and set - other attributes. - * Mechanism to rotate authentication and encryption keys. - * Multiple sessions per request, even using different backends. - * Interfaces and infrastructure for custom session backends: sessions from - different stores can be retrieved and batch-saved using a common API. + - Simple API: use it as an easy way to set signed (and optionally + encrypted) cookies. + - Built-in backends to store sessions in cookies or the filesystem. + - Flash messages: session values that last until read. + - Convenient way to switch session persistency (aka "remember me") and set + other attributes. + - Mechanism to rotate authentication and encryption keys. + - Multiple sessions per request, even using different backends. + - Interfaces and infrastructure for custom session backends: sessions from + different stores can be retrieved and batch-saved using a common API. Let's start with an example that shows the sessions API in a nutshell: import ( - "net/http" - "github.com/gorilla/sessions" + "github.com/admpub/sessions" + "github.com/webx-top/echo" ) // Note: Don't store your key in your source code. Pass it via an @@ -33,23 +33,20 @@ Let's start with an example that shows the sessions API in a nutshell: // Ensure SESSION_KEY exists in the environment, or sessions will fail. var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) - func MyHandler(w http.ResponseWriter, r *http.Request) { - // Get a session. Get() always returns a session, even if empty. - session, err := store.Get(r, "session-name") + func MyHandler(ctx echo.Context) error { + // Get a session. We're ignoring the error resulted from decoding an + // existing session: Get() always returns a session, even if empty. + session, err := store.Get(ctx, "session-name") if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + return err } // Set some session values. session.Values["foo"] = "bar" session.Values[42] = 43 // Save it before we write to the response/return from the handler. - err = session.Save(r, w) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + session.Save(ctx) + return nil } First we initialize a session store calling NewCookieStore() and passing a @@ -72,12 +69,11 @@ Ruby On Rails a few years back. When we request a flash message, it is removed from the session. To add a flash, call session.AddFlash(), and to get all flashes, call session.Flashes(). Here is an example: - func MyHandler(w http.ResponseWriter, r *http.Request) { + func MyHandler(ctx echo.Context) error { // Get a session. - session, err := store.Get(r, "session-name") + session, err := store.Get(ctx, "session-name") if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + return error } // Get the previous flashes, if any. @@ -87,11 +83,8 @@ flashes, call session.Flashes(). Here is an example: // Set a new flash. session.AddFlash("Hello, flash messages world!") } - err = session.Save(r, w) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + session.Save(ctx) + return nil } Flash messages are useful to set information to be read after a redirection, @@ -103,7 +96,8 @@ so it is easy to register new datatypes for storage in sessions: import( "encoding/gob" - "github.com/gorilla/sessions" + "github.com/admpub/sessions" + "github.com/webx-top/echo" ) type Person struct { @@ -130,11 +124,10 @@ then allow us to serialise/deserialise values of those types to and from our ses Note that because session values are stored in a map[string]interface{}, there's a need to type-assert data when retrieving it. We'll use the Person struct we registered above: - func MyHandler(w http.ResponseWriter, r *http.Request) { - session, err := store.Get(r, "session-name") + func MyHandler(ctx echo.Context) error { + session, err := store.Get(ctx, "session-name") if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + return err } // Retrieve our struct and type-assert it @@ -145,6 +138,8 @@ a need to type-assert data when retrieving it. We'll use the Person struct we re } // Now we can use our person object + + return nil } By default, session cookies last for a month. This is probably too long for @@ -186,19 +181,16 @@ at once: it's sessions.Save(). Here's an example: var store = sessions.NewCookieStore([]byte("something-very-secret")) - func MyHandler(w http.ResponseWriter, r *http.Request) { + func MyHandler(ctx echo.Context) error { // Get a session and set a value. - session1, _ := store.Get(r, "session-one") + session1, _ := store.Get(ctx, "session-one") session1.Values["foo"] = "bar" // Get another session and set another value. - session2, _ := store.Get(r, "session-two") + session2, _ := store.Get(ctx, "session-two") session2.Values[42] = 43 // Save all sessions. - err = sessions.Save(r, w) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + sessions.Save(ctx) + return nil } This is possible because when we call Get() from a session store, it adds the diff --git a/go.mod b/go.mod index 25a82cc..a8bf1b2 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,47 @@ -module github.com/gorilla/sessions +module github.com/admpub/sessions -go 1.23 +go 1.23.0 -require github.com/gorilla/securecookie v1.1.2 +toolchain go1.24.0 + +require ( + github.com/admpub/securecookie v1.3.0 + github.com/stretchr/testify v1.10.0 + github.com/webx-top/echo v1.14.5 +) + +require ( + github.com/admpub/color v1.8.1 // indirect + github.com/admpub/decimal v1.3.1 // indirect + github.com/admpub/events v1.3.6 // indirect + github.com/admpub/fsnotify v1.7.0 // indirect + github.com/admpub/go-isatty v0.0.11 // indirect + github.com/admpub/go-reuseport v0.5.0 // indirect + github.com/admpub/humanize v0.0.0-20190501023926-5f826e92c8ca // indirect + github.com/admpub/log v1.4.0 // indirect + github.com/admpub/realip v0.2.7 // indirect + github.com/admpub/timeago v1.2.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/webx-top/captcha v0.1.0 // indirect + github.com/webx-top/codec v0.3.0 // indirect + github.com/webx-top/com v1.3.25 // indirect + github.com/webx-top/poolx v0.0.0-20210912044716-5cfa2d58e380 // indirect + github.com/webx-top/tagfast v0.0.1 // indirect + github.com/webx-top/validation v0.0.3 // indirect + golang.org/x/crypto v0.34.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 285ffee..a803392 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,337 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/admpub/color v1.8.1 h1:kgx9vC9+KvQ8L1RieP1TZa2qq3lMda+duRKcpZt9BU8= +github.com/admpub/color v1.8.1/go.mod h1:3fk9BXRe+f3UjWSTgVjqUcI4Ei05fLaEPjVajrqMJhY= +github.com/admpub/decimal v1.3.1 h1:+lLbDXQoBb0fxtnMK7r3ZRFFdREV5GIUNculDQ6oSo8= +github.com/admpub/decimal v1.3.1/go.mod h1:QJItPT9L1pPsXkS6fnwWB0FHlSDE7G0x6KOQfgAWyM4= +github.com/admpub/events v1.3.6 h1:sspprUCsMj/mZUPLfs5Ol7xNUvGl2v1QlQnp1U+ouQE= +github.com/admpub/events v1.3.6/go.mod h1:i7MCA/n2a8k/SApXTw+RigSfXO/GKnQQwEty1su24Jc= +github.com/admpub/fsnotify v1.7.0 h1:pI04ANljHE5cS3fr+uXMgDG4/Cv3iye40nH/oZE8pB0= +github.com/admpub/fsnotify v1.7.0/go.mod h1:D9ecq8Ksz5grAhvRRrbN7AdVXnwYx3OxM1+xfjru4+4= +github.com/admpub/go-isatty v0.0.11 h1:jOUJR3gc3eUM4pBaHZoOWqkjr5DGYPGrp3xalspA+5U= +github.com/admpub/go-isatty v0.0.11/go.mod h1:EsPQsKLbNzPWj/avtgWBP509ZqC8PGYw2ZrQKwDCFNs= +github.com/admpub/go-reuseport v0.5.0 h1:WbIumvnr46Nlq/J1TDHz8QfCk8K//b+AImHf6VBNCow= +github.com/admpub/go-reuseport v0.5.0/go.mod h1:LfaS8MPNO8NNjvGJuEnbOkp7Tec4E0dkxPhBpfgTSx0= +github.com/admpub/humanize v0.0.0-20190501023926-5f826e92c8ca h1:9mfauR3d9p41BFnAoXmHc2z8yHcgg+3+jT1hbnQrtXo= +github.com/admpub/humanize v0.0.0-20190501023926-5f826e92c8ca/go.mod h1:eHL6IsGOvDKqPc9Zp0d0NNayoEYoJ+UC+vj6zG0lGCY= +github.com/admpub/log v1.4.0 h1:iNar5ZyjMi1ntRUtSIugEaR4pt6DZoNZH/3zw1VbR/Q= +github.com/admpub/log v1.4.0/go.mod h1:bo7BVOu/p+GdOQnv5XhPiPQ60nhLRMhYbAQgKE0gYGw= +github.com/admpub/realip v0.2.7 h1:lefF1kpA3liKqq43PzM9D2Ex2imEeix/aMjSmmyMphY= +github.com/admpub/realip v0.2.7/go.mod h1:Ini0GwP0RvSbVLPReALn5zYYRi7Z2wpi2C/7HVX9Ii4= +github.com/admpub/securecookie v1.3.0 h1:SIQfKIwb2vWsIj7m1D1ZJQfKEEKB+I7sJQbTy9k3z1k= +github.com/admpub/securecookie v1.3.0/go.mod h1:KWWRXAWYIUtoK6P7P6vuFXQ8ndFX9v1XgVBfg1zHjf4= +github.com/admpub/timeago v1.2.2 h1:aI47WNHGKNBIWODAnj4lInSPrgqBx2Wh9uHDl/+HBkc= +github.com/admpub/timeago v1.2.2/go.mod h1:5iFnUFGqk2dnVZ2LpOoUserdh2rfTlTfhqB8OIMYZos= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +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= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= -github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/webx-top/captcha v0.1.0 h1:uBTGMevM0tlII5Zyj4QbJPI6vTPI0uF+BA4zKLA8avU= +github.com/webx-top/captcha v0.1.0/go.mod h1:E7chb3O5Dqbcta3hBEGRGXGreItjbjPy72ihuqS4+d4= +github.com/webx-top/codec v0.3.0 h1:IQT59k2TMBCVG7XhpEXKgICftk3iJALm5EmeKy1xncI= +github.com/webx-top/codec v0.3.0/go.mod h1:FL2TNTorudSTop040uNCTONOlIJfl6kElJLxyoHHs/g= +github.com/webx-top/com v1.3.25 h1:PsQzHem909sao1UWFQqRU0tmijrikfwMGfQRyvWj+3c= +github.com/webx-top/com v1.3.25/go.mod h1:0MjwXQcyy1oXnWqRwabOvvbs+jP82AdR8u0ZB1y64Eg= +github.com/webx-top/echo v1.14.5 h1:HqHH9rPVkVTD5izdDWJ0MSxVUIUpe6YJZdkhVAsOo58= +github.com/webx-top/echo v1.14.5/go.mod h1:y7OwPeGRUzJPxn+zoFOXHoZ+4x7z8LOVSB87ML5fTUA= +github.com/webx-top/poolx v0.0.0-20210912044716-5cfa2d58e380 h1:YUDmvTQjrixwGCtlJFm1piLLo7lxMEBnWUqMrlnqyxM= +github.com/webx-top/poolx v0.0.0-20210912044716-5cfa2d58e380/go.mod h1:JGnKm+kSTq2yvbFHttHLvbo2kqY9wZOTXY98YGhZu6Y= +github.com/webx-top/tagfast v0.0.0-20161020041435-9a2065ce3dd2/go.mod h1:pMe3sJitHxbxX2EAI/v9HEAXjodP4c+yUVw3rbKcljI= +github.com/webx-top/tagfast v0.0.1 h1:SNC2ui+ngSCwMaQgtfAsRKFLAt0GuiquMO9jwySfsGU= +github.com/webx-top/tagfast v0.0.1/go.mod h1:vArAB9fuv8AVZ7NfWedERyyY1og7lEHkjuq+o5YuaP8= +github.com/webx-top/validation v0.0.3 h1:6vBoAp5iqjIpfFA+XoCnIzBHcuLjQzxv7MRlshptUqk= +github.com/webx-top/validation v0.0.3/go.mod h1:74lFGn3naxJl8FelK8RfCatVCKDB6G2ckG96tm3w1ug= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA= +golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/options.go b/options.go deleted file mode 100644 index 6ed7934..0000000 --- a/options.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sessions - -import "net/http" - -// Options stores configuration for a session or session store. -// -// Fields are a subset of http.Cookie fields. -type Options struct { - Path string - Domain string - // MaxAge=0 means no Max-Age attribute specified and the cookie will be - // deleted after the browser session ends. - // MaxAge<0 means delete cookie immediately. - // MaxAge>0 means Max-Age attribute present and given in seconds. - MaxAge int - Secure bool - HttpOnly bool - Partitioned bool - SameSite http.SameSite -} diff --git a/sessions.go b/sessions.go index c052b28..7af2963 100644 --- a/sessions.go +++ b/sessions.go @@ -5,11 +5,11 @@ package sessions import ( - "context" - "encoding/gob" "fmt" "net/http" "time" + + "github.com/webx-top/echo" ) // Default flashes key. @@ -20,10 +20,9 @@ const flashesKey = "_flash" // NewSession is called by session stores to create a new session instance. func NewSession(store Store, name string) *Session { return &Session{ - Values: make(map[interface{}]interface{}), - store: store, - name: name, - Options: new(Options), + Values: make(map[interface{}]interface{}), + store: store, + name: name, } } @@ -33,11 +32,10 @@ type Session struct { // user data. ID string // Values contains the user-data for the session. - Values map[interface{}]interface{} - Options *Options - IsNew bool - store Store - name string + Values map[interface{}]interface{} + IsNew bool + store Store + name string } // Flashes returns a slice of flash messages from the session. @@ -74,11 +72,16 @@ func (s *Session) AddFlash(value interface{}, vars ...string) { s.Values[key] = append(flashes, value) } +// Reload reload session data +func (s *Session) Reload(ctx echo.Context) error { + return s.store.Reload(ctx, s) +} + // Save is a convenience method to save this session. It is the same as calling // store.Save(request, response, session). You should call Save before writing to // the response or returning from the handler. -func (s *Session) Save(r *http.Request, w http.ResponseWriter) error { - return s.store.Save(r, w, s) +func (s *Session) Save(ctx echo.Context) error { + return s.store.Save(ctx, s) } // Name returns the name used to register the session. @@ -99,30 +102,30 @@ type sessionInfo struct { e error } -// contextKey is the type used to store the registry in the context. -type contextKey int - // registryKey is the key used to store the registry in the context. -const registryKey contextKey = 0 +const registryKey = `webx:mw.sessions` // GetRegistry returns a registry instance for the current request. -func GetRegistry(r *http.Request) *Registry { - var ctx = r.Context() - registry := ctx.Value(registryKey) - if registry != nil { - return registry.(*Registry) - } - newRegistry := &Registry{ - request: r, +func GetRegistry(ctx echo.Context) (registry *Registry) { + actual, loaded := ctx.Internal().Load(registryKey) + if loaded { + registry = actual.(*Registry) + return + } + registry = &Registry{ + context: ctx, sessions: make(map[string]sessionInfo), } - *r = *r.WithContext(context.WithValue(ctx, registryKey, newRegistry)) - return newRegistry + actual, loaded = ctx.Internal().LoadOrStore(registryKey, registry) + if loaded { + registry = actual.(*Registry) + } + return registry } // Registry stores sessions used during a request. type Registry struct { - request *http.Request + context echo.Context sessions map[string]sessionInfo } @@ -135,8 +138,14 @@ func (s *Registry) Get(store Store, name string) (session *Session, err error) { } if info, ok := s.sessions[name]; ok { session, err = info.s, info.e + if err != nil { + return + } } else { - session, err = store.New(s.request, name) + session, err = store.New(s.context, name) + if err != nil { + return + } session.name = name s.sessions[name] = sessionInfo{s: session, e: err} } @@ -145,14 +154,14 @@ func (s *Registry) Get(store Store, name string) (session *Session, err error) { } // Save saves all sessions registered for the current request. -func (s *Registry) Save(w http.ResponseWriter) error { +func (s *Registry) Save(ctx echo.Context) error { var errMulti MultiError for name, info := range s.sessions { session := info.s if session.store == nil { errMulti = append(errMulti, fmt.Errorf( "sessions: missing store for session %q", name)) - } else if err := session.store.Save(s.request, w, session); err != nil { + } else if err := session.store.Save(ctx, session); err != nil { errMulti = append(errMulti, fmt.Errorf( "sessions: error saving session %q -- %v", name, err)) } @@ -166,19 +175,44 @@ func (s *Registry) Save(w http.ResponseWriter) error { // Helpers -------------------------------------------------------------------- func init() { - gob.Register([]interface{}{}) + RegisterGob([]interface{}{}) } // Save saves all sessions used during the current request. -func Save(r *http.Request, w http.ResponseWriter) error { - return GetRegistry(r).Save(w) +func Save(ctx echo.Context) error { + return GetRegistry(ctx).Save(ctx) +} + +var cookieSameSiteNames = map[string]http.SameSite{ + ``: http.SameSiteDefaultMode, + `lax`: http.SameSiteLaxMode, + `strict`: http.SameSiteStrictMode, + `none`: http.SameSiteNoneMode, +} + +func CookieSameSite(sameSite string) http.SameSite { + v, y := cookieSameSiteNames[sameSite] + if y { + return v + } + return cookieSameSiteNames[``] } // NewCookie returns an http.Cookie with the options set. It also sets // the Expires field calculated based on the MaxAge value, for Internet // Explorer compatibility. -func NewCookie(name, value string, options *Options) *http.Cookie { - cookie := newCookieFromOptions(name, value, options) +func NewCookie(ctx echo.Context, name, value string) *http.Cookie { + options := ctx.CookieOptions() + cookie := &http.Cookie{ + Name: name, + Value: value, + Path: options.Path, + Domain: options.Domain, + MaxAge: options.MaxAge, + Secure: options.Secure, + SameSite: CookieSameSite(options.SameSite), + HttpOnly: options.HttpOnly, + } if options.MaxAge > 0 { d := time.Duration(options.MaxAge) * time.Second cookie.Expires = time.Now().Add(d) @@ -189,6 +223,15 @@ func NewCookie(name, value string, options *Options) *http.Cookie { return cookie } +// SetCookie for echo +// @param:ctx,key,value,maxAge(seconds),path(/),domain,secure,httpOnly,sameSite(lax/strict/default) +func SetCookie(ctx echo.Context, key string, value string, args ...interface{}) { + if len(args) == 0 { + args = append(args, ctx.CookieOptions()) + } + ctx.SetCookie(key, value, args...) +} + // Error ---------------------------------------------------------------------- // MultiError stores multiple errors. diff --git a/sessions_test.go b/sessions_test.go deleted file mode 100644 index 9476c22..0000000 --- a/sessions_test.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sessions - -import ( - "bytes" - "encoding/gob" - "net/http" - "net/http/httptest" - "strings" - "testing" -) - -// NewRecorder returns an initialized ResponseRecorder. -func NewRecorder() *httptest.ResponseRecorder { - return &httptest.ResponseRecorder{ - HeaderMap: make(http.Header), - Body: new(bytes.Buffer), - } -} - -// ---------------------------------------------------------------------------- - -type FlashMessage struct { - Type int - Message string -} - -func TestFlashes(t *testing.T) { - var req *http.Request - var rsp *httptest.ResponseRecorder - var hdr http.Header - var err error - var ok bool - var cookies []string - var session *Session - var flashes []interface{} - - store := NewCookieStore([]byte("secret-key")) - - if store.Options.SameSite != http.SameSiteNoneMode { - t.Fatalf("cookie store error: default same site is not set to None") - } - - // Round 1 ---------------------------------------------------------------- - - req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) - rsp = NewRecorder() - // Get a session. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - // Get a flash. - flashes = session.Flashes() - if len(flashes) != 0 { - t.Errorf("Expected empty flashes; Got %v", flashes) - } - // Add some flashes. - session.AddFlash("foo") - session.AddFlash("bar") - // Custom key. - session.AddFlash("baz", "custom_key") - // Save. - if err = Save(req, rsp); err != nil { - t.Fatalf("Error saving session: %v", err) - } - hdr = rsp.Header() - cookies, ok = hdr["Set-Cookie"] - if !ok || len(cookies) != 1 { - t.Fatal("No cookies. Header:", hdr) - } - - if !strings.Contains(cookies[0], "SameSite=None") || !strings.Contains(cookies[0], "Secure") { - t.Fatal("Set-Cookie does not contains SameSite=None with Secure, cookie string:", cookies[0]) - } - - if _, err = store.Get(req, "session:key"); err.Error() != "sessions: invalid character in cookie name: session:key" { - t.Fatalf("Expected error due to invalid cookie name") - } - - // Round 2 ---------------------------------------------------------------- - - req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) - req.Header.Add("Cookie", cookies[0]) - // Get a session. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - // Check all saved values. - flashes = session.Flashes() - if len(flashes) != 2 { - t.Fatalf("Expected flashes; Got %v", flashes) - } - if flashes[0] != "foo" || flashes[1] != "bar" { - t.Errorf("Expected foo,bar; Got %v", flashes) - } - flashes = session.Flashes() - if len(flashes) != 0 { - t.Errorf("Expected dumped flashes; Got %v", flashes) - } - // Custom key. - flashes = session.Flashes("custom_key") - if len(flashes) != 1 { - t.Errorf("Expected flashes; Got %v", flashes) - } else if flashes[0] != "baz" { - t.Errorf("Expected baz; Got %v", flashes) - } - flashes = session.Flashes("custom_key") - if len(flashes) != 0 { - t.Errorf("Expected dumped flashes; Got %v", flashes) - } - - // Round 3 ---------------------------------------------------------------- - // Custom type - - req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) - rsp = NewRecorder() - // Get a session. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - // Get a flash. - flashes = session.Flashes() - if len(flashes) != 0 { - t.Errorf("Expected empty flashes; Got %v", flashes) - } - // Add some flashes. - session.AddFlash(&FlashMessage{42, "foo"}) - // Save. - if err = Save(req, rsp); err != nil { - t.Fatalf("Error saving session: %v", err) - } - hdr = rsp.Header() - cookies, ok = hdr["Set-Cookie"] - if !ok || len(cookies) != 1 { - t.Fatal("No cookies. Header:", hdr) - } - - // Round 4 ---------------------------------------------------------------- - // Custom type - - req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) - req.Header.Add("Cookie", cookies[0]) - // Get a session. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - // Check all saved values. - flashes = session.Flashes() - if len(flashes) != 1 { - t.Fatalf("Expected flashes; Got %v", flashes) - } - custom := flashes[0].(FlashMessage) - if custom.Type != 42 || custom.Message != "foo" { - t.Errorf("Expected %#v, got %#v", FlashMessage{42, "foo"}, custom) - } - - // Round 5 ---------------------------------------------------------------- - // Check if a request shallow copy resets the request context data store. - - req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) - - // Get a session. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - - // Put a test value into the session data store. - session.Values["test"] = "test-value" - - // Create a shallow copy of the request. - req = req.WithContext(req.Context()) - - // Get the session again. - if session, err = store.Get(req, "session-key"); err != nil { - t.Fatalf("Error getting session: %v", err) - } - - // Check if the previous inserted value still exists. - if session.Values["test"] == nil { - t.Fatalf("Session test value is lost in the request context!") - } - - // Check if the previous inserted value has the same value. - if session.Values["test"] != "test-value" { - t.Fatalf("Session test value is changed in the request context!") - } -} - -func TestCookieStoreMapPanic(t *testing.T) { - defer func() { - err := recover() - if err != nil { - t.Fatal(err) - } - }() - - store := NewCookieStore([]byte("aaa0defe5d2839cbc46fc4f080cd7adc")) - req, err := http.NewRequest("GET", "http://www.example.com", nil) - if err != nil { - t.Fatal("failed to create request", err) - } - w := httptest.NewRecorder() - - session := NewSession(store, "hello") - - session.Values["data"] = "hello-world" - - err = session.Save(req, w) - if err != nil { - t.Fatal("failed to save session", err) - } -} - -func init() { - gob.Register(FlashMessage{}) -} diff --git a/sessions_values_test.go b/sessions_values_test.go new file mode 100644 index 0000000..53d78d0 --- /dev/null +++ b/sessions_values_test.go @@ -0,0 +1,15 @@ +package sessions + +import ( + "testing" + + "github.com/admpub/securecookie" + "github.com/stretchr/testify/require" +) + +func TestEmptyValues(t *testing.T) { + b, err := securecookie.Gob.Serialize(map[interface{}]interface{}{`A`: 1}) + require.NoError(t, err) + require.NotEmpty(t, b) + t.Logf(`%v: %d`, b, len(b)) +} diff --git a/store.go b/store.go index 24db822..7171feb 100644 --- a/store.go +++ b/store.go @@ -6,12 +6,15 @@ package sessions import ( "encoding/base32" - "net/http" + "fmt" "os" "path/filepath" + "strings" "sync" + "time" - "github.com/gorilla/securecookie" + "github.com/admpub/securecookie" + "github.com/webx-top/echo" ) const ( @@ -24,16 +27,24 @@ const ( // See CookieStore and FilesystemStore for examples. type Store interface { // Get should return a cached session. - Get(r *http.Request, name string) (*Session, error) + Get(ctx echo.Context, name string) (*Session, error) // New should create and return a new session. // // Note that New should never return a nil session, even in the case of // an error if using the Registry infrastructure to cache the session. - New(r *http.Request, name string) (*Session, error) + New(ctx echo.Context, name string) (*Session, error) + Reload(ctx echo.Context, s *Session) error // Save should persist session to the underlying store implementation. - Save(r *http.Request, w http.ResponseWriter, s *Session) error + Save(ctx echo.Context, s *Session) error + // Remove server-side data + Remove(sessionID string) error +} + +// IDGenerator session id generator +type IDGenerator interface { + GenerateID(ctx echo.Context, session *Session) (string, error) } // CookieStore ---------------------------------------------------------------- @@ -53,22 +64,13 @@ type Store interface { func NewCookieStore(keyPairs ...[]byte) *CookieStore { cs := &CookieStore{ Codecs: securecookie.CodecsFromPairs(keyPairs...), - Options: &Options{ - Path: "/", - MaxAge: 86400 * 30, - SameSite: http.SameSiteNoneMode, - Secure: true, - }, } - - cs.MaxAge(cs.Options.MaxAge) return cs } // CookieStore stores sessions using secure cookies. type CookieStore struct { - Codecs []securecookie.Codec - Options *Options // default configuration + Codecs []securecookie.Codec } // Get returns a session for the given name after adding it to the registry. @@ -78,8 +80,8 @@ type CookieStore struct { // // It returns a new session and an error if the session exists but could // not be decoded. -func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) { - return GetRegistry(r).Get(s, name) +func (s *CookieStore) Get(ctx echo.Context, name string) (*Session, error) { + return GetRegistry(ctx).Get(s, name) } // New returns a session for the given name without adding it to the registry. @@ -87,31 +89,54 @@ func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) { // The difference between New() and Get() is that calling New() twice will // decode the session data twice, while Get() registers and reuses the same // decoded session after the first call. -func (s *CookieStore) New(r *http.Request, name string) (*Session, error) { +func (s *CookieStore) New(ctx echo.Context, name string) (*Session, error) { session := NewSession(s, name) - opts := *s.Options - session.Options = &opts session.IsNew = true var err error - if c, errCookie := r.Cookie(name); errCookie == nil { - err = securecookie.DecodeMulti(name, c.Value, &session.Values, + if v := ctx.GetCookie(name); len(v) > 0 { + err = securecookie.DecodeMultiWithMaxAge( + name, v, &session.Values, + ctx.CookieOptions().MaxAge, s.Codecs...) if err == nil { session.IsNew = false + session.ID = v } } return session, err } +func (s *CookieStore) Reload(ctx echo.Context, session *Session) error { + if len(session.ID) == 0 { + return nil + } + err := securecookie.DecodeMultiWithMaxAge( + session.Name(), session.ID, &session.Values, + ctx.CookieOptions().MaxAge, + s.Codecs...) + if err == nil { + session.IsNew = false + } + return err +} + +func (s *CookieStore) GenerateID(ctx echo.Context, session *Session) (string, error) { + return securecookie.EncodeMulti(session.Name(), session.Values, + s.Codecs...) +} + // Save adds a single session to the response. -func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter, - session *Session) error { +func (s *CookieStore) Save(ctx echo.Context, session *Session) error { encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, s.Codecs...) if err != nil { return err } - http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options)) + SetCookie(ctx, session.Name(), encoded) + return nil +} + +func (s *CookieStore) Remove(sessionID string) error { return nil } @@ -119,8 +144,6 @@ func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter, // implementation. Individual sessions can be deleted by setting Options.MaxAge // = -1 for that session. func (s *CookieStore) MaxAge(age int) { - s.Options.MaxAge = age - // Set the maxAge for each securecookie instance. for _, codec := range s.Codecs { if sc, ok := codec.(*securecookie.SecureCookie); ok { @@ -140,19 +163,13 @@ var fileMutex sync.RWMutex // // See NewCookieStore() for a description of the other parameters. func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore { - if path == "" { + if len(path) == 0 { path = os.TempDir() } fs := &FilesystemStore{ Codecs: securecookie.CodecsFromPairs(keyPairs...), - Options: &Options{ - Path: "/", - MaxAge: 86400 * 30, - }, - path: path, + path: path, } - - fs.MaxAge(fs.Options.MaxAge) return fs } @@ -162,9 +179,9 @@ func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore { // // This store is still experimental and not well tested. Feedback is welcome. type FilesystemStore struct { - Codecs []securecookie.Codec - Options *Options // default configuration - path string + Codecs []securecookie.Codec + path string + debug bool } // MaxLength restricts the maximum length of new sessions to l. @@ -178,24 +195,29 @@ func (s *FilesystemStore) MaxLength(l int) { } } +func (s *FilesystemStore) SetDebug(on bool) { + s.debug = on +} + // Get returns a session for the given name after adding it to the registry. // // See CookieStore.Get(). -func (s *FilesystemStore) Get(r *http.Request, name string) (*Session, error) { - return GetRegistry(r).Get(s, name) +func (s *FilesystemStore) Get(ctx echo.Context, name string) (*Session, error) { + return GetRegistry(ctx).Get(s, name) } // New returns a session for the given name without adding it to the registry. // // See CookieStore.New(). -func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) { +func (s *FilesystemStore) New(ctx echo.Context, name string) (*Session, error) { session := NewSession(s, name) - opts := *s.Options - session.Options = &opts session.IsNew = true var err error - if c, errCookie := r.Cookie(name); errCookie == nil { - err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...) + if v := ctx.GetCookie(name); len(v) > 0 { + err = securecookie.DecodeMultiWithMaxAge( + name, v, &session.ID, + ctx.CookieOptions().MaxAge, + s.Codecs...) if err == nil { err = s.load(session) if err == nil { @@ -206,30 +228,34 @@ func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) { return session, err } +func (s *FilesystemStore) Reload(ctx echo.Context, session *Session) error { + err := s.load(session) + if err == nil { + session.IsNew = false + } + return err +} + var base32RawStdEncoding = base32.StdEncoding.WithPadding(base32.NoPadding) +func makeFileSessionID() string { + return base32RawStdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)) +} + // Save adds a single session to the response. -// -// If the Options.MaxAge of the session is <= 0 then the session file will be -// deleted from the store path. With this process it enforces the properly -// session cookie handling so no need to trust in the cookie management in the -// web browser. -func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, - session *Session) error { - // Delete if max-age is <= 0 - if session.Options.MaxAge <= 0 { - if err := s.erase(session); err != nil && !os.IsNotExist(err) { +func (s *FilesystemStore) Save(ctx echo.Context, session *Session) error { + // Delete if max-age is < 0 + if ctx.CookieOptions().MaxAge < 0 { + if err := s.erase(session); err != nil { return err } - http.SetCookie(w, NewCookie(session.Name(), "", session.Options)) + SetCookie(ctx, session.Name(), "", -1) return nil } - - if session.ID == "" { + if len(session.ID) == 0 { // Because the ID is used in the filename, encode it to // use alphanumeric characters only. - session.ID = base32RawStdEncoding.EncodeToString( - securecookie.GenerateRandomKey(32)) + session.ID = makeFileSessionID() } if err := s.save(session); err != nil { return err @@ -239,16 +265,56 @@ func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, if err != nil { return err } - http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options)) + SetCookie(ctx, session.Name(), encoded) return nil } +func (s *FilesystemStore) Remove(sessionID string) error { + if len(sessionID) == 0 { + return nil + } + filename := s.sessionFile(sessionID) + fileMutex.RLock() + defer fileMutex.RUnlock() + + err := os.Remove(filename) + if err != nil && os.IsNotExist(err) { + return nil + } + return err +} + +func (s *FilesystemStore) sessionFile(sessionID string) string { + return filepath.Join(s.path, sessionFilePrefix+filepath.Base(sessionID)) +} + +func (s *FilesystemStore) size(sessionID string) (int64, error) { + if len(sessionID) == 0 { + return 0, nil + } + filename := s.sessionFile(sessionID) + fileMutex.RLock() + defer fileMutex.RUnlock() + + fi, err := os.Stat(filename) + if err != nil { + if os.IsNotExist(err) { + return 0, nil + } + return 0, err + } + return fi.Size(), err +} + +// delete session file +func (s *FilesystemStore) erase(session *Session) error { + return s.Remove(session.ID) +} + // MaxAge sets the maximum age for the store and the underlying cookie // implementation. Individual sessions can be deleted by setting Options.MaxAge // = -1 for that session. func (s *FilesystemStore) MaxAge(age int) { - s.Options.MaxAge = age - // Set the maxAge for each securecookie instance. for _, codec := range s.Codecs { if sc, ok := codec.(*securecookie.SecureCookie); ok { @@ -257,42 +323,72 @@ func (s *FilesystemStore) MaxAge(age int) { } } +var emptyGob, _ = securecookie.Gob.Serialize(make(map[interface{}]interface{})) + +func SizeIsEmptyGob(size int64) bool { + return size == EmptyGobSize() +} + +func EmptyGobSize() int64 { + size := len(emptyGob) + return int64(size) +} + // save writes encoded session.Values to a file. func (s *FilesystemStore) save(session *Session) error { - encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, - s.Codecs...) + b, err := securecookie.Gob.Serialize(session.Values) if err != nil { return err } - filename := filepath.Join(s.path, sessionFilePrefix+filepath.Base(session.ID)) + filename := s.sessionFile(session.ID) fileMutex.Lock() defer fileMutex.Unlock() - return os.WriteFile(filename, []byte(encoded), 0600) + return os.WriteFile(filename, b, 0600) } // load reads a file and decodes its content into session.Values. func (s *FilesystemStore) load(session *Session) error { - filename := filepath.Join(s.path, sessionFilePrefix+filepath.Base(session.ID)) + filename := s.sessionFile(session.ID) fileMutex.RLock() defer fileMutex.RUnlock() - fdata, err := os.ReadFile(filepath.Clean(filename)) + fdata, err := os.ReadFile(filename) if err != nil { return err } - if err = securecookie.DecodeMulti(session.Name(), string(fdata), - &session.Values, s.Codecs...); err != nil { - return err - } - return nil + return securecookie.Gob.Deserialize(fdata, &session.Values) } -// delete session file -func (s *FilesystemStore) erase(session *Session) error { - filename := filepath.Join(s.path, sessionFilePrefix+filepath.Base(session.ID)) - - fileMutex.RLock() - defer fileMutex.RUnlock() - - err := os.Remove(filename) +func (s *FilesystemStore) DeleteExpired(maxAge float64, emptyDataAge float64) error { + if maxAge <= 0 && emptyDataAge <= 0 { + return nil + } + emptyLength := EmptyGobSize() + err := filepath.Walk(s.path, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return err + } + if !strings.HasPrefix(info.Name(), sessionFilePrefix) { + return err + } + age := time.Since(info.ModTime()).Seconds() + if age > maxAge { + if s.debug { + fmt.Printf("delete %s : {age: %v} > {maxAge: %v}\n", info.Name(), age, maxAge) + } + err = os.Remove(path) + return err + } + if emptyDataAge > 0 && emptyLength > 0 && info.Size() == emptyLength && age > emptyDataAge { + if s.debug { + fmt.Printf("delete %s : {age: %v} > {emptyDataAge: %v}\n", info.Name(), age, emptyDataAge) + } + err = os.Remove(path) + return err + } + return err + }) return err } diff --git a/store_file_test.go b/store_file_test.go new file mode 100644 index 0000000..2627434 --- /dev/null +++ b/store_file_test.go @@ -0,0 +1,47 @@ +package sessions + +import ( + "os" + "testing" + "time" + + "github.com/admpub/securecookie" + "github.com/stretchr/testify/assert" +) + +func TestFileSystemDeleteExpired(t *testing.T) { + sessionDir := `./testdata` + err := os.MkdirAll(sessionDir, os.ModePerm) + assert.NoError(t, err) + st := NewFilesystemStore(sessionDir) + st.debug = true + ss := &Session{ID: makeFileSessionID(), Values: make(map[interface{}]interface{})} + b, err := securecookie.Gob.Serialize(ss.Values) + assert.NoError(t, err) + assert.True(t, SizeIsEmptyGob(int64(len(b)))) + + err = st.save(ss) + assert.NoError(t, err) + size, err := st.size(ss.ID) + assert.NoError(t, err) + assert.True(t, SizeIsEmptyGob(size)) + info, err := os.Stat(st.sessionFile(ss.ID)) + assert.NoError(t, err) + time.Sleep(2 * time.Second) + t.Logf(`lifeTime: %.2f`, time.Since(info.ModTime()).Seconds()) + err = st.DeleteExpired(10, 1) + assert.NoError(t, err) + + ss.Values[`user`] = `user` + err = st.save(ss) + assert.NoError(t, err) + size, err = st.size(ss.ID) + assert.NoError(t, err) + assert.False(t, SizeIsEmptyGob(size)) + t.Logf(`emptyLength: %d; currentLength: %d`, EmptyGobSize(), size) + + time.Sleep(2 * time.Second) + err = st.DeleteExpired(1, 1) + assert.NoError(t, err) + time.Sleep(2 * time.Second) +} diff --git a/store_test.go b/store_test.go deleted file mode 100644 index 09580bd..0000000 --- a/store_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sessions - -import ( - "encoding/base64" - "net/http" - "net/http/httptest" - "testing" -) - -// Test for GH-8 for CookieStore -func TestGH8CookieStore(t *testing.T) { - originalPath := "/" - store := NewCookieStore() - store.Options.Path = originalPath - req, err := http.NewRequest("GET", "http://www.example.com", nil) - if err != nil { - t.Fatal("failed to create request", err) - } - - session, err := store.New(req, "hello") - if err != nil { - t.Fatal("failed to create session", err) - } - - store.Options.Path = "/foo" - if session.Options.Path != originalPath { - t.Fatalf("bad session path: got %q, want %q", session.Options.Path, originalPath) - } -} - -// Test for GH-8 for FilesystemStore -func TestGH8FilesystemStore(t *testing.T) { - originalPath := "/" - store := NewFilesystemStore("") - store.Options.Path = originalPath - req, err := http.NewRequest("GET", "http://www.example.com", nil) - if err != nil { - t.Fatal("failed to create request", err) - } - - session, err := store.New(req, "hello") - if err != nil { - t.Fatal("failed to create session", err) - } - - store.Options.Path = "/foo" - if session.Options.Path != originalPath { - t.Fatalf("bad session path: got %q, want %q", session.Options.Path, originalPath) - } -} - -// Test for GH-2. -func TestGH2MaxLength(t *testing.T) { - store := NewFilesystemStore("", []byte("some key")) - req, err := http.NewRequest("GET", "http://www.example.com", nil) - if err != nil { - t.Fatal("failed to create request", err) - } - w := httptest.NewRecorder() - - session, err := store.New(req, "my session") - if err != nil { - t.Fatal("failed to create session", err) - } - - session.Values["big"] = make([]byte, base64.StdEncoding.DecodedLen(4096*2)) - err = session.Save(req, w) - if err == nil { - t.Fatal("expected an error, got nil") - } - - store.MaxLength(4096 * 3) // A bit more than the value size to account for encoding overhead. - err = session.Save(req, w) - if err != nil { - t.Fatal("failed to Save:", err) - } -} - -// Test delete filesystem store with max-age: -1 -func TestGH8FilesystemStoreDelete(t *testing.T) { - store := NewFilesystemStore("", []byte("some key")) - req, err := http.NewRequest("GET", "http://www.example.com", nil) - if err != nil { - t.Fatal("failed to create request", err) - } - w := httptest.NewRecorder() - - session, err := store.New(req, "hello") - if err != nil { - t.Fatal("failed to create session", err) - } - - err = session.Save(req, w) - if err != nil { - t.Fatal("failed to save session", err) - } - - session.Options.MaxAge = -1 - err = session.Save(req, w) - if err != nil { - t.Fatal("failed to delete session", err) - } -} - -// Test delete filesystem store with max-age: 0 -func TestGH8FilesystemStoreDelete2(t *testing.T) { - store := NewFilesystemStore("", []byte("some key")) - req, err := http.NewRequest("GET", "http://www.example.com", nil) - if err != nil { - t.Fatal("failed to create request", err) - } - w := httptest.NewRecorder() - - session, err := store.New(req, "hello") - if err != nil { - t.Fatal("failed to create session", err) - } - - err = session.Save(req, w) - if err != nil { - t.Fatal("failed to save session", err) - } - - session.Options.MaxAge = 0 - err = session.Save(req, w) - if err != nil { - t.Fatal("failed to delete session", err) - } -} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..f31e1a8 --- /dev/null +++ b/utils.go @@ -0,0 +1,8 @@ +package sessions + +import "encoding/gob" + +func RegisterGob(v interface{}) { + defer recover() + gob.Register(v) +}