// Save implements wcg.SessionStore#Save
func (s *SessionStore) Save(sess *wcg.Session) error {
	encoded, err := sess.Encode()
	if err != nil {
		return err
	}
	item := &EncodedSession{
		[]byte(encoded),
	}
	key := datastore.NewKey(s.ctx, "EncodedSession", string(sess.ID), 0, nil)
	_, err = datastore.Put(s.ctx, key, item)
	return err
}
Exemple #2
0
// SessionSupportWithConfig returns two middleware functions for session support, which need to be registered
// on route.Before and route.After.
//
// Example:
//
//     sprepare, scomplete := SessionSupport()
//
//     route.Before(sprepare)
//     route.After(scomplete)
//
func SessionSupportWithConfig(cfg *SessionConfig) (wcg.Handler, wcg.Handler) {
	if cfg.StoreFactory == nil {
		memorystore := wcg.NewMemorySessionStore()
		cfg.StoreFactory = func(req *wcg.Request) wcg.SessionStore {
			req.Logger.Warnf("Memory SessionStore is used so that the session data would lost suddenly.")
			req.Logger.Warnf("If you are in GAE environment (including devserver), you must use GAESessionStoreFactory")
			return memorystore
		}
	}

	// load handler loads the session data from backend storage.
	load := func(res *wcg.Response, req *wcg.Request) {
		if res.IsClosed() {
			return
		}
		var sess *wcg.Session
		var err error
		store := cfg.StoreFactory(req)
		c, err := req.SignedCookie(cfg.CookieName, cfg.Key)
		if err != nil {
			req.Logger.Debugf("Could not decode the signed cookie on %s: %v", cfg.Key, err)
		}
		if c != nil {
			if wcg.IsUUID(c.Value) {
				sess, err = store.Load(wcg.UUID(c.Value))
			}
			if err != nil {
				req.Logger.Warnf(
					"Could not load the session (id: %q) from the store (%v), use a new session.",
					err, c.Value)
				sess = nil
				c = nil
			} else {
				if sess == nil {
					req.Logger.Debugf("Session data for %q was not found on backend.", c.Value)
				} else {
					req.Logger.Debugf("Session data for %q found.", c.Value)
					expiredAt := sess.Timestamp.Add(time.Duration(cfg.MaxAge) * time.Second)
					if expiredAt.Before(time.Now()) {
						// expired
						req.Logger.Infof("Session data for %q is found but expired.", c.Value)
						if _, err := store.Delete(sess.ID); err != nil {
							req.Logger.Errorf("An error occurred while deleting the expired session (%q): %v", sess.ID, err)
						}
						sess = nil
					}
				}
			}
		}
		if c == nil {
			// no cookie found
			c = &http.Cookie{}
			c.Name = cfg.CookieName
			c.HttpOnly = cfg.HTTPOnly
			c.MaxAge = cfg.MaxAge
			if cfg.Domain != "" {
				c.Domain = cfg.Domain
			}
		}

		c.Path = cfg.Path

		// No session found
		if sess == nil {
			sess = wcg.NewSession(req)
			sess.Timestamp = time.Now()
			req.Logger.Debugf("Creating the new session.")
			store := cfg.StoreFactory(req)
			err := store.Save(sess)
			if err != nil {
				res.RenderInternalError("New Session could not be created: %v", err)
				return
			}
		} else {
			// Update the timestamp.
			sess.Timestamp = time.Now()
		}

		// Set the cookie data
		c.Value = string(sess.ID)
		c.Expires = sess.Timestamp.Add(time.Duration(cfg.MaxAge) * time.Second)
		c.MaxAge = cfg.MaxAge
		req.Session = sess
		encoded, _ := sess.Encode()
		req.SetLocal("__session_support", encoded)
		res.SetSignedCookie(c, cfg.Key)
	}

	// save handler store the cookie data into backend storage.
	save := func(res *wcg.Response, req *wcg.Request) {
		if encoded, ok := req.Local("__session_support").(string); ok {
			nencoded, _ := req.Session.Encode()
			// Store session if it is changed.
			if encoded != nencoded {
				store := cfg.StoreFactory(req)
				err := store.Save(req.Session)
				if err != nil {
					// TODO: log session error
					req.Logger.Errorf("Could not save the session data on backend: %v", err)
				} else {
					req.Logger.Debugf("Successfully stored the session data on backend.")
				}
			} else {
				req.Logger.Debugf("Session is not changed, skipped to be stored.")
			}
		}
	}
	return wcg.NewNamedHandler("Session.Load", load), wcg.NewNamedHandler("Session.Save", save)
}