aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/gorilla/sessions
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gorilla/sessions')
-rw-r--r--vendor/github.com/gorilla/sessions/LICENSE27
-rw-r--r--vendor/github.com/gorilla/sessions/README.md79
-rw-r--r--vendor/github.com/gorilla/sessions/doc.go199
-rw-r--r--vendor/github.com/gorilla/sessions/lex.go102
-rw-r--r--vendor/github.com/gorilla/sessions/sessions.go238
-rw-r--r--vendor/github.com/gorilla/sessions/store.go270
6 files changed, 915 insertions, 0 deletions
diff --git a/vendor/github.com/gorilla/sessions/LICENSE b/vendor/github.com/gorilla/sessions/LICENSE
new file mode 100644
index 0000000..0e5fb87
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/sessions/README.md b/vendor/github.com/gorilla/sessions/README.md
new file mode 100644
index 0000000..2779904
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/README.md
@@ -0,0 +1,79 @@
+sessions
+========
+[![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.png?branch=master)](https://travis-ci.org/gorilla/sessions)
+
+gorilla/sessions provides cookie and filesystem sessions and 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.
+
+Let's start with an example that shows the sessions API in a nutshell:
+
+```go
+ import (
+ "net/http"
+ "github.com/gorilla/sessions"
+ )
+
+ var store = sessions.NewCookieStore([]byte("something-very-secret"))
+
+ func MyHandler(w http.ResponseWriter, r *http.Request) {
+ // Get a session. We're ignoring the error resulted from decoding an
+ // existing session: Get() always returns a session, even if empty.
+ session, _ := store.Get(r, "session-name")
+ // Set some session values.
+ session.Values["foo"] = "bar"
+ session.Values[42] = 43
+ // Save it before we write to the response/return from the handler.
+ session.Save(r, w)
+ }
+```
+
+First we initialize a session store calling `NewCookieStore()` and passing a
+secret key used to authenticate the session. Inside the handler, we call
+`store.Get()` to retrieve an existing session or a new one. Then we set some
+session values in session.Values, which is a `map[interface{}]interface{}`.
+And finally we call `session.Save()` to save the session in the response.
+
+Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
+with
+[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler)
+as or else you will leak memory! An easy way to do this is to wrap the top-level
+mux when calling http.ListenAndServe:
+
+More examples are available [on the Gorilla
+website](http://www.gorillatoolkit.org/pkg/sessions).
+
+## Store Implementations
+
+Other implementations of the `sessions.Store` interface:
+
+* [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB
+* [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt
+* [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase
+* [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS
+* [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache
+* [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache on GAE
+* [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB
+* [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL
+* [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL
+* [github.com/boj/redistore](https://github.com/boj/redistore) - Redis
+* [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB
+* [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak
+* [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite
+* [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite)
+
+## License
+
+BSD licensed. See the LICENSE file for details.
diff --git a/vendor/github.com/gorilla/sessions/doc.go b/vendor/github.com/gorilla/sessions/doc.go
new file mode 100644
index 0000000..af8c89a
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/doc.go
@@ -0,0 +1,199 @@
+// 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 gorilla/sessions provides cookie and filesystem sessions and
+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.
+
+Let's start with an example that shows the sessions API in a nutshell:
+
+ import (
+ "net/http"
+ "github.com/gorilla/sessions"
+ )
+
+ var store = sessions.NewCookieStore([]byte("something-very-secret"))
+
+ func MyHandler(w http.ResponseWriter, r *http.Request) {
+ // 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(r, "session-name")
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // Set some session values.
+ session.Values["foo"] = "bar"
+ session.Values[42] = 43
+ // Save it before we write to the response/return from the handler.
+ session.Save(r, w)
+ }
+
+First we initialize a session store calling NewCookieStore() and passing a
+secret key used to authenticate the session. Inside the handler, we call
+store.Get() to retrieve an existing session or a new one. Then we set some
+session values in session.Values, which is a map[interface{}]interface{}.
+And finally we call session.Save() to save the session in the response.
+
+Note that in production code, we should check for errors when calling
+session.Save(r, w), and either display an error message or otherwise handle it.
+
+Save must be called before writing to the response, otherwise the session
+cookie will not be sent to the client.
+
+Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
+with context.ClearHandler as or else you will leak memory! An easy way to do this
+is to wrap the top-level mux when calling http.ListenAndServe:
+
+ http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
+
+The ClearHandler function is provided by the gorilla/context package.
+
+That's all you need to know for the basic usage. Let's take a look at other
+options, starting with flash messages.
+
+Flash messages are session values that last until read. The term appeared with
+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) {
+ // Get a session.
+ session, err := store.Get(r, "session-name")
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // Get the previously flashes, if any.
+ if flashes := session.Flashes(); len(flashes) > 0 {
+ // Use the flash values.
+ } else {
+ // Set a new flash.
+ session.AddFlash("Hello, flash messages world!")
+ }
+ session.Save(r, w)
+ }
+
+Flash messages are useful to set information to be read after a redirection,
+like after form submissions.
+
+There may also be cases where you want to store a complex datatype within a
+session, such as a struct. Sessions are serialised using the encoding/gob package,
+so it is easy to register new datatypes for storage in sessions:
+
+ import(
+ "encoding/gob"
+ "github.com/gorilla/sessions"
+ )
+
+ type Person struct {
+ FirstName string
+ LastName string
+ Email string
+ Age int
+ }
+
+ type M map[string]interface{}
+
+ func init() {
+
+ gob.Register(&Person{})
+ gob.Register(&M{})
+ }
+
+As it's not possible to pass a raw type as a parameter to a function, gob.Register()
+relies on us passing it a value of the desired type. In the example above we've passed
+it a pointer to a struct and a pointer to a custom type representing a
+map[string]interface. (We could have passed non-pointer values if we wished.) This will
+then allow us to serialise/deserialise values of those types to and from our sessions.
+
+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")
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // Retrieve our struct and type-assert it
+ val := session.Values["person"]
+ var person = &Person{}
+ if person, ok := val.(*Person); !ok {
+ // Handle the case that it's not an expected type
+ }
+
+ // Now we can use our person object
+ }
+
+By default, session cookies last for a month. This is probably too long for
+some cases, but it is easy to change this and other attributes during
+runtime. Sessions can be configured individually or the store can be
+configured and then all sessions saved using it will use that configuration.
+We access session.Options or store.Options to set a new configuration. The
+fields are basically a subset of http.Cookie fields. Let's change the
+maximum age of a session to one week:
+
+ session.Options = &sessions.Options{
+ Path: "/",
+ MaxAge: 86400 * 7,
+ HttpOnly: true,
+ }
+
+Sometimes we may want to change authentication and/or encryption keys without
+breaking existing sessions. The CookieStore supports key rotation, and to use
+it you just need to set multiple authentication and encryption keys, in pairs,
+to be tested in order:
+
+ var store = sessions.NewCookieStore(
+ []byte("new-authentication-key"),
+ []byte("new-encryption-key"),
+ []byte("old-authentication-key"),
+ []byte("old-encryption-key"),
+ )
+
+New sessions will be saved using the first pair. Old sessions can still be
+read because the first pair will fail, and the second will be tested. This
+makes it easy to "rotate" secret keys and still be able to validate existing
+sessions. Note: for all pairs the encryption key is optional; set it to nil
+or omit it and and encryption won't be used.
+
+Multiple sessions can be used in the same request, even with different
+session backends. When this happens, calling Save() on each session
+individually would be cumbersome, so we have a way to save all sessions
+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) {
+ // Get a session and set a value.
+ session1, _ := store.Get(r, "session-one")
+ session1.Values["foo"] = "bar"
+ // Get another session and set another value.
+ session2, _ := store.Get(r, "session-two")
+ session2.Values[42] = 43
+ // Save all sessions.
+ sessions.Save(r, w)
+ }
+
+This is possible because when we call Get() from a session store, it adds the
+session to a common registry. Save() uses it to save all registered sessions.
+*/
+package sessions
diff --git a/vendor/github.com/gorilla/sessions/lex.go b/vendor/github.com/gorilla/sessions/lex.go
new file mode 100644
index 0000000..4bbbe10
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/lex.go
@@ -0,0 +1,102 @@
+// This file contains code adapted from the Go standard library
+// https://github.com/golang/go/blob/39ad0fd0789872f9469167be7fe9578625ff246e/src/net/http/lex.go
+
+package sessions
+
+import "strings"
+
+var isTokenTable = [127]bool{
+ '!': true,
+ '#': true,
+ '$': true,
+ '%': true,
+ '&': true,
+ '\'': true,
+ '*': true,
+ '+': true,
+ '-': true,
+ '.': true,
+ '0': true,
+ '1': true,
+ '2': true,
+ '3': true,
+ '4': true,
+ '5': true,
+ '6': true,
+ '7': true,
+ '8': true,
+ '9': true,
+ 'A': true,
+ 'B': true,
+ 'C': true,
+ 'D': true,
+ 'E': true,
+ 'F': true,
+ 'G': true,
+ 'H': true,
+ 'I': true,
+ 'J': true,
+ 'K': true,
+ 'L': true,
+ 'M': true,
+ 'N': true,
+ 'O': true,
+ 'P': true,
+ 'Q': true,
+ 'R': true,
+ 'S': true,
+ 'T': true,
+ 'U': true,
+ 'W': true,
+ 'V': true,
+ 'X': true,
+ 'Y': true,
+ 'Z': true,
+ '^': true,
+ '_': true,
+ '`': true,
+ 'a': true,
+ 'b': true,
+ 'c': true,
+ 'd': true,
+ 'e': true,
+ 'f': true,
+ 'g': true,
+ 'h': true,
+ 'i': true,
+ 'j': true,
+ 'k': true,
+ 'l': true,
+ 'm': true,
+ 'n': true,
+ 'o': true,
+ 'p': true,
+ 'q': true,
+ 'r': true,
+ 's': true,
+ 't': true,
+ 'u': true,
+ 'v': true,
+ 'w': true,
+ 'x': true,
+ 'y': true,
+ 'z': true,
+ '|': true,
+ '~': true,
+}
+
+func isToken(r rune) bool {
+ i := int(r)
+ return i < len(isTokenTable) && isTokenTable[i]
+}
+
+func isNotToken(r rune) bool {
+ return !isToken(r)
+}
+
+func isCookieNameValid(raw string) bool {
+ if raw == "" {
+ return false
+ }
+ return strings.IndexFunc(raw, isNotToken) < 0
+}
diff --git a/vendor/github.com/gorilla/sessions/sessions.go b/vendor/github.com/gorilla/sessions/sessions.go
new file mode 100644
index 0000000..a837c08
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/sessions.go
@@ -0,0 +1,238 @@
+// 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/gob"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/gorilla/context"
+)
+
+// Default flashes key.
+const flashesKey = "_flash"
+
+// Options --------------------------------------------------------------------
+
+// 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.
+ // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
+ // MaxAge>0 means Max-Age attribute present and given in seconds.
+ MaxAge int
+ Secure bool
+ HttpOnly bool
+}
+
+// Session --------------------------------------------------------------------
+
+// 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,
+ }
+}
+
+// Session stores the values and optional configuration for a session.
+type Session struct {
+ ID string
+ Values map[interface{}]interface{}
+ Options *Options
+ IsNew bool
+ store Store
+ name string
+}
+
+// Flashes returns a slice of flash messages from the session.
+//
+// A single variadic argument is accepted, and it is optional: it defines
+// the flash key. If not defined "_flash" is used by default.
+func (s *Session) Flashes(vars ...string) []interface{} {
+ var flashes []interface{}
+ key := flashesKey
+ if len(vars) > 0 {
+ key = vars[0]
+ }
+ if v, ok := s.Values[key]; ok {
+ // Drop the flashes and return it.
+ delete(s.Values, key)
+ flashes = v.([]interface{})
+ }
+ return flashes
+}
+
+// AddFlash adds a flash message to the session.
+//
+// A single variadic argument is accepted, and it is optional: it defines
+// the flash key. If not defined "_flash" is used by default.
+func (s *Session) AddFlash(value interface{}, vars ...string) {
+ key := flashesKey
+ if len(vars) > 0 {
+ key = vars[0]
+ }
+ var flashes []interface{}
+ if v, ok := s.Values[key]; ok {
+ flashes = v.([]interface{})
+ }
+ s.Values[key] = append(flashes, value)
+}
+
+// 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)
+}
+
+// Name returns the name used to register the session.
+func (s *Session) Name() string {
+ return s.name
+}
+
+// Store returns the session store used to register the session.
+func (s *Session) Store() Store {
+ return s.store
+}
+
+// Registry -------------------------------------------------------------------
+
+// sessionInfo stores a session tracked by the registry.
+type sessionInfo struct {
+ s *Session
+ 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
+
+// GetRegistry returns a registry instance for the current request.
+func GetRegistry(r *http.Request) *Registry {
+ registry := context.Get(r, registryKey)
+ if registry != nil {
+ return registry.(*Registry)
+ }
+ newRegistry := &Registry{
+ request: r,
+ sessions: make(map[string]sessionInfo),
+ }
+ context.Set(r, registryKey, newRegistry)
+ return newRegistry
+}
+
+// Registry stores sessions used during a request.
+type Registry struct {
+ request *http.Request
+ sessions map[string]sessionInfo
+}
+
+// Get registers and returns a session for the given name and session store.
+//
+// It returns a new session if there are no sessions registered for the name.
+func (s *Registry) Get(store Store, name string) (session *Session, err error) {
+ if !isCookieNameValid(name) {
+ return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name)
+ }
+ if info, ok := s.sessions[name]; ok {
+ session, err = info.s, info.e
+ } else {
+ session, err = store.New(s.request, name)
+ session.name = name
+ s.sessions[name] = sessionInfo{s: session, e: err}
+ }
+ session.store = store
+ return
+}
+
+// Save saves all sessions registered for the current request.
+func (s *Registry) Save(w http.ResponseWriter) 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 {
+ errMulti = append(errMulti, fmt.Errorf(
+ "sessions: error saving session %q -- %v", name, err))
+ }
+ }
+ if errMulti != nil {
+ return errMulti
+ }
+ return nil
+}
+
+// Helpers --------------------------------------------------------------------
+
+func init() {
+ gob.Register([]interface{}{})
+}
+
+// Save saves all sessions used during the current request.
+func Save(r *http.Request, w http.ResponseWriter) error {
+ return GetRegistry(r).Save(w)
+}
+
+// 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 := &http.Cookie{
+ Name: name,
+ Value: value,
+ Path: options.Path,
+ Domain: options.Domain,
+ MaxAge: options.MaxAge,
+ Secure: options.Secure,
+ HttpOnly: options.HttpOnly,
+ }
+ if options.MaxAge > 0 {
+ d := time.Duration(options.MaxAge) * time.Second
+ cookie.Expires = time.Now().Add(d)
+ } else if options.MaxAge < 0 {
+ // Set it to the past to expire now.
+ cookie.Expires = time.Unix(1, 0)
+ }
+ return cookie
+}
+
+// Error ----------------------------------------------------------------------
+
+// MultiError stores multiple errors.
+//
+// Borrowed from the App Engine SDK.
+type MultiError []error
+
+func (m MultiError) Error() string {
+ s, n := "", 0
+ for _, e := range m {
+ if e != nil {
+ if n == 0 {
+ s = e.Error()
+ }
+ n++
+ }
+ }
+ switch n {
+ case 0:
+ return "(0 errors)"
+ case 1:
+ return s
+ case 2:
+ return s + " (and 1 other error)"
+ }
+ return fmt.Sprintf("%s (and %d other errors)", s, n-1)
+}
diff --git a/vendor/github.com/gorilla/sessions/store.go b/vendor/github.com/gorilla/sessions/store.go
new file mode 100644
index 0000000..ba3b9e9
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/store.go
@@ -0,0 +1,270 @@
+// 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/base32"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+
+ "github.com/gorilla/securecookie"
+)
+
+// Store is an interface for custom session stores.
+//
+// See CookieStore and FilesystemStore for examples.
+type Store interface {
+ // Get should return a cached session.
+ Get(r *http.Request, 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)
+
+ // Save should persist session to the underlying store implementation.
+ Save(r *http.Request, w http.ResponseWriter, s *Session) error
+}
+
+// CookieStore ----------------------------------------------------------------
+
+// NewCookieStore returns a new CookieStore.
+//
+// Keys are defined in pairs to allow key rotation, but the common case is
+// to set a single authentication key and optionally an encryption key.
+//
+// The first key in a pair is used for authentication and the second for
+// encryption. The encryption key can be set to nil or omitted in the last
+// pair, but the authentication key is required in all pairs.
+//
+// It is recommended to use an authentication key with 32 or 64 bytes.
+// The encryption key, if set, must be either 16, 24, or 32 bytes to select
+// AES-128, AES-192, or AES-256 modes.
+//
+// Use the convenience function securecookie.GenerateRandomKey() to create
+// strong keys.
+func NewCookieStore(keyPairs ...[]byte) *CookieStore {
+ cs := &CookieStore{
+ Codecs: securecookie.CodecsFromPairs(keyPairs...),
+ Options: &Options{
+ Path: "/",
+ MaxAge: 86400 * 30,
+ },
+ }
+
+ cs.MaxAge(cs.Options.MaxAge)
+ return cs
+}
+
+// CookieStore stores sessions using secure cookies.
+type CookieStore struct {
+ Codecs []securecookie.Codec
+ Options *Options // default configuration
+}
+
+// Get returns a session for the given name after adding it to the registry.
+//
+// It returns a new session if the sessions doesn't exist. Access IsNew on
+// the session to check if it is an existing session or a new one.
+//
+// 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)
+}
+
+// New returns a session for the given name without adding it to the registry.
+//
+// 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) {
+ 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,
+ s.Codecs...)
+ if err == nil {
+ session.IsNew = false
+ }
+ }
+ return session, err
+}
+
+// Save adds a single session to the response.
+func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter,
+ 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))
+ return nil
+}
+
+// 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 *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 {
+ sc.MaxAge(age)
+ }
+ }
+}
+
+// FilesystemStore ------------------------------------------------------------
+
+var fileMutex sync.RWMutex
+
+// NewFilesystemStore returns a new FilesystemStore.
+//
+// The path argument is the directory where sessions will be saved. If empty
+// it will use os.TempDir().
+//
+// See NewCookieStore() for a description of the other parameters.
+func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore {
+ if path == "" {
+ path = os.TempDir()
+ }
+ fs := &FilesystemStore{
+ Codecs: securecookie.CodecsFromPairs(keyPairs...),
+ Options: &Options{
+ Path: "/",
+ MaxAge: 86400 * 30,
+ },
+ path: path,
+ }
+
+ fs.MaxAge(fs.Options.MaxAge)
+ return fs
+}
+
+// FilesystemStore stores sessions in the filesystem.
+//
+// It also serves as a reference for custom stores.
+//
+// This store is still experimental and not well tested. Feedback is welcome.
+type FilesystemStore struct {
+ Codecs []securecookie.Codec
+ Options *Options // default configuration
+ path string
+}
+
+// MaxLength restricts the maximum length of new sessions to l.
+// If l is 0 there is no limit to the size of a session, use with caution.
+// The default for a new FilesystemStore is 4096.
+func (s *FilesystemStore) MaxLength(l int) {
+ for _, c := range s.Codecs {
+ if codec, ok := c.(*securecookie.SecureCookie); ok {
+ codec.MaxLength(l)
+ }
+ }
+}
+
+// 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)
+}
+
+// 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) {
+ 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 err == nil {
+ err = s.load(session)
+ if err == nil {
+ session.IsNew = false
+ }
+ }
+ }
+ return session, err
+}
+
+// Save adds a single session to the response.
+func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter,
+ session *Session) error {
+ if session.ID == "" {
+ // Because the ID is used in the filename, encode it to
+ // use alphanumeric characters only.
+ session.ID = strings.TrimRight(
+ base32.StdEncoding.EncodeToString(
+ securecookie.GenerateRandomKey(32)), "=")
+ }
+ if err := s.save(session); err != nil {
+ return err
+ }
+ encoded, err := securecookie.EncodeMulti(session.Name(), session.ID,
+ s.Codecs...)
+ if err != nil {
+ return err
+ }
+ http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
+ return nil
+}
+
+// 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 {
+ sc.MaxAge(age)
+ }
+ }
+}
+
+// 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...)
+ if err != nil {
+ return err
+ }
+ filename := filepath.Join(s.path, "session_"+session.ID)
+ fileMutex.Lock()
+ defer fileMutex.Unlock()
+ return ioutil.WriteFile(filename, []byte(encoded), 0600)
+}
+
+// load reads a file and decodes its content into session.Values.
+func (s *FilesystemStore) load(session *Session) error {
+ filename := filepath.Join(s.path, "session_"+session.ID)
+ fileMutex.RLock()
+ defer fileMutex.RUnlock()
+ fdata, err := ioutil.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
+}