Permalink
Browse files

1. If session doesn't change, which is most of the case, it should't …

…Set-Cookie all the time.

So that nginx can do proxy cache correctly.

2. Using map for session store make it impossible to compare old cookie and new cookie are the same. because of map are random accessed.
  • Loading branch information...
1 parent d968516 commit d0c63e3f8aa3290bfb9938395f32992ba07be4be @sunfmin sunfmin committed May 28, 2012
Showing with 85 additions and 17 deletions.
  1. +85 −17 sessions.go
View
@@ -10,9 +10,48 @@ import (
"hash"
"io/ioutil"
"net/http"
+ "sort"
"strings"
)
+type sessionItem struct {
+ Key string
+ Value interface{}
+}
+
+type sessionItems []sessionItem
+
+func (sis sessionItems) Len() int {
+ return len(sis)
+}
+
+func (sis sessionItems) Less(i, j int) bool {
+ return sis[i].Key < sis[j].Key
+}
+
+func (sis sessionItems) Swap(i, j int) {
+ sis[i], sis[j] = sis[j], sis[i]
+}
+
+func (sis sessionItems) ToMap() (m map[string]interface{}) {
+ m = make(map[string]interface{})
+ for _, item := range sis {
+ m[item.Key] = item.Value
+ }
+ return
+}
+
+func sessionItemsFromMap(m map[string]interface{}) (sis sessionItems) {
+ for k, v := range m {
+ sis = append(sis, sessionItem{
+ Key: k,
+ Value: v,
+ })
+ }
+ sort.Sort(sis)
+ return
+}
+
func hashCookie(data, secret string) (sum string) {
var h hash.Hash = hmac.New(sha1.New, []byte(secret))
h.Write([]byte(data))
@@ -25,10 +64,12 @@ func verifyCookie(data, secret, sum string) bool {
func decodeGob(value string) (result map[string]interface{}) {
buffer := bytes.NewBufferString(value)
+
decoder := gob.NewDecoder(buffer)
- result = make(map[string]interface{})
- decoder.Decode(&result)
- return result
+ sis := sessionItems{}
+ decoder.Decode(&sis)
+
+ return sis.ToMap()
}
// Due to a bug in golang where when using
@@ -65,10 +106,11 @@ func decodeCookie(value, secret string) (cookie map[string]interface{}) {
return cookie
}
-func encodeGob(value interface{}) (result string) {
+func encodeGob(value map[string]interface{}) (result string) {
buffer := new(bytes.Buffer)
encoder := gob.NewEncoder(buffer)
- encoder.Encode(value)
+ sis := sessionItemsFromMap(value)
+ encoder.Encode(sis)
return buffer.String()
}
@@ -95,21 +137,19 @@ func encodeCookie(value map[string]interface{}, secret string) (cookie string) {
}
func prepareSession(env Env, key, secret string) {
- for _, cookie := range env.Request().Cookies() {
- if cookie.Name == key {
- env["mango.session"] = decodeCookie(cookie.Value, secret)
- return
- }
+ value := sessionCookieValue(env, key)
+ if value == "" {
+ // Didn't find a session to decode
+ env["mango.session"] = make(map[string]interface{})
+ return
}
-
- // Didn't find a session to decode
- env["mango.session"] = make(map[string]interface{})
+ env["mango.session"] = decodeCookie(value, secret)
}
-func commitSession(headers Headers, env Env, key, secret string, options *CookieOptions) {
+func commitSession(headers Headers, env Env, key, secret string, newValue string, options *CookieOptions) {
cookie := new(http.Cookie)
cookie.Name = key
- cookie.Value = encodeCookie(env["mango.session"].(map[string]interface{}), secret)
+ cookie.Value = newValue
cookie.Path = options.Path
cookie.Domain = options.Domain
cookie.MaxAge = options.MaxAge
@@ -118,6 +158,30 @@ func commitSession(headers Headers, env Env, key, secret string, options *Cookie
headers.Add("Set-Cookie", cookie.String())
}
+func sessionCookieValue(env Env, key string) (value string) {
+ for _, cookie := range env.Request().Cookies() {
+ if cookie.Name == key {
+ value = cookie.Value
+ return
+ }
+ }
+ return
+
+}
+
+func cookieChanged(env Env, key, secret string) string {
+ oldCookieValue := sessionCookieValue(env, key)
+ value := env["mango.session"].(map[string]interface{})
+ if len(value) == 0 {
+ return ""
+ }
+ newCookieValue := encodeCookie(value, secret)
+ if oldCookieValue == newCookieValue {
+ return ""
+ }
+ return newCookieValue
+}
+
type CookieOptions struct {
Domain string
Path string
@@ -130,7 +194,11 @@ func Sessions(secret, key string, options *CookieOptions) Middleware {
return func(env Env, app App) (status Status, headers Headers, body Body) {
prepareSession(env, key, secret)
status, headers, body = app(env)
- commitSession(headers, env, key, secret, options)
- return status, headers, body
+ newValue := cookieChanged(env, key, secret)
+ if newValue == "" {
+ return
+ }
+ commitSession(headers, env, key, secret, newValue, options)
+ return
}
}

0 comments on commit d0c63e3

Please sign in to comment.