You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

259 lines
7.1 KiB

  1. package handlers
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. "net/http/cookiejar"
  7. "net/http/httptest"
  8. "testing"
  9. "time"
  10. )
  11. var (
  12. store SessionStore
  13. secret = "butchered at birth"
  14. )
  15. func TestSession(t *testing.T) {
  16. stores := map[string]SessionStore{
  17. "memory": NewMemoryStore(1),
  18. "redis": NewRedisStore(&RedisStoreOptions{
  19. Network: "tcp",
  20. Address: ":6379",
  21. Database: 1,
  22. KeyPrefix: "sess",
  23. }),
  24. }
  25. for k, v := range stores {
  26. t.Logf("testing session with %s store\n", k)
  27. store = v
  28. t.Log("SessionExists")
  29. testSessionExists(t)
  30. t.Log("SessionPersists")
  31. testSessionPersists(t)
  32. t.Log("SessionExpires")
  33. testSessionExpires(t)
  34. t.Log("SessionBeforeExpires")
  35. testSessionBeforeExpires(t)
  36. t.Log("PanicIfNoSecret")
  37. testPanicIfNoSecret(t)
  38. t.Log("InvalidPath")
  39. testInvalidPath(t)
  40. t.Log("ValidSubPath")
  41. testValidSubPath(t)
  42. t.Log("SecureOverHttp")
  43. testSecureOverHttp(t)
  44. }
  45. }
  46. func setupTest(f func(w http.ResponseWriter, r *http.Request), ckPath string, secure bool, maxAge int) *httptest.Server {
  47. opts := NewSessionOptions(store, secret)
  48. if ckPath != "" {
  49. opts.CookieTemplate.Path = ckPath
  50. }
  51. opts.CookieTemplate.Secure = secure
  52. opts.CookieTemplate.MaxAge = maxAge
  53. h := SessionHandler(http.HandlerFunc(f), opts)
  54. return httptest.NewServer(h)
  55. }
  56. func doRequest(u string, newJar bool) *http.Response {
  57. var err error
  58. if newJar {
  59. http.DefaultClient.Jar, err = cookiejar.New(new(cookiejar.Options))
  60. if err != nil {
  61. panic(err)
  62. }
  63. }
  64. res, err := http.Get(u)
  65. if err != nil {
  66. panic(err)
  67. }
  68. return res
  69. }
  70. func testSessionExists(t *testing.T) {
  71. s := setupTest(func(w http.ResponseWriter, r *http.Request) {
  72. ssn, ok := GetSession(w)
  73. if assertTrue(ok, "expected session to be non-nil, got nil", t) {
  74. ssn.Data["foo"] = "bar"
  75. assertTrue(ssn.Data["foo"] == "bar", fmt.Sprintf("expected ssn[foo] to be 'bar', got %v", ssn.Data["foo"]), t)
  76. }
  77. w.Write([]byte("ok"))
  78. }, "", false, 0)
  79. defer s.Close()
  80. res := doRequest(s.URL, true)
  81. assertStatus(http.StatusOK, res.StatusCode, t)
  82. assertBody([]byte("ok"), res, t)
  83. assertTrue(len(res.Cookies()) == 1, fmt.Sprintf("expected response to have 1 cookie, got %d", len(res.Cookies())), t)
  84. }
  85. func testSessionPersists(t *testing.T) {
  86. cnt := 0
  87. s := setupTest(func(w http.ResponseWriter, r *http.Request) {
  88. ssn, ok := GetSession(w)
  89. if !ok {
  90. panic("session not found!")
  91. }
  92. if cnt == 0 {
  93. ssn.Data["foo"] = "bar"
  94. w.Write([]byte("ok"))
  95. cnt++
  96. } else {
  97. w.Write([]byte(ssn.Data["foo"].(string)))
  98. }
  99. }, "", false, 0)
  100. defer s.Close()
  101. // 1st call, set the session value
  102. res := doRequest(s.URL, true)
  103. assertStatus(http.StatusOK, res.StatusCode, t)
  104. assertBody([]byte("ok"), res, t)
  105. // 2nd call, get the session value
  106. res = doRequest(s.URL, false)
  107. assertStatus(http.StatusOK, res.StatusCode, t)
  108. assertBody([]byte("bar"), res, t)
  109. assertTrue(len(res.Cookies()) == 0, fmt.Sprintf("expected 2nd response to have 0 cookie, got %d", len(res.Cookies())), t)
  110. }
  111. func testSessionExpires(t *testing.T) {
  112. cnt := 0
  113. s := setupTest(func(w http.ResponseWriter, r *http.Request) {
  114. ssn, ok := GetSession(w)
  115. if !ok {
  116. panic("session not found!")
  117. }
  118. if cnt == 0 {
  119. w.Write([]byte(ssn.ID()))
  120. cnt++
  121. } else {
  122. w.Write([]byte(ssn.ID()))
  123. }
  124. }, "", false, 1) // Expire in 1 second
  125. defer s.Close()
  126. // 1st call, set the session value
  127. res := doRequest(s.URL, true)
  128. assertStatus(http.StatusOK, res.StatusCode, t)
  129. id1, err := ioutil.ReadAll(res.Body)
  130. if err != nil {
  131. panic(err)
  132. }
  133. res.Body.Close()
  134. time.Sleep(1001 * time.Millisecond)
  135. // 2nd call, get the session value
  136. res = doRequest(s.URL, false)
  137. assertStatus(http.StatusOK, res.StatusCode, t)
  138. id2, err := ioutil.ReadAll(res.Body)
  139. if err != nil {
  140. panic(err)
  141. }
  142. res.Body.Close()
  143. sid1, sid2 := string(id1), string(id2)
  144. assertTrue(len(res.Cookies()) == 1, fmt.Sprintf("expected 2nd response to have 1 cookie, got %d", len(res.Cookies())), t)
  145. assertTrue(sid1 != sid2, "expected session IDs to be different, got same", t)
  146. }
  147. func testSessionBeforeExpires(t *testing.T) {
  148. s := setupTest(func(w http.ResponseWriter, r *http.Request) {
  149. ssn, ok := GetSession(w)
  150. if !ok {
  151. panic("session not found!")
  152. }
  153. w.Write([]byte(ssn.ID()))
  154. }, "", false, 1) // Expire in 1 second
  155. defer s.Close()
  156. // 1st call, set the session value
  157. res := doRequest(s.URL, true)
  158. assertStatus(http.StatusOK, res.StatusCode, t)
  159. id1, err := ioutil.ReadAll(res.Body)
  160. if err != nil {
  161. panic(err)
  162. }
  163. res.Body.Close()
  164. time.Sleep(500 * time.Millisecond)
  165. // 2nd call, get the session value
  166. res = doRequest(s.URL, false)
  167. assertStatus(http.StatusOK, res.StatusCode, t)
  168. id2, err := ioutil.ReadAll(res.Body)
  169. if err != nil {
  170. panic(err)
  171. }
  172. res.Body.Close()
  173. sid1, sid2 := string(id1), string(id2)
  174. assertTrue(len(res.Cookies()) == 0, fmt.Sprintf("expected 2nd response to have no cookie, got %d", len(res.Cookies())), t)
  175. assertTrue(sid1 == sid2, "expected session IDs to be the same, got different", t)
  176. }
  177. func testPanicIfNoSecret(t *testing.T) {
  178. defer assertPanic(t)
  179. SessionHandler(http.NotFoundHandler(), NewSessionOptions(nil, ""))
  180. }
  181. func testInvalidPath(t *testing.T) {
  182. s := setupTest(func(w http.ResponseWriter, r *http.Request) {
  183. _, ok := GetSession(w)
  184. assertTrue(!ok, "expected session to be nil, got non-nil", t)
  185. w.Write([]byte("ok"))
  186. }, "/foo", false, 0)
  187. defer s.Close()
  188. res := doRequest(s.URL, true)
  189. assertStatus(http.StatusOK, res.StatusCode, t)
  190. assertBody([]byte("ok"), res, t)
  191. assertTrue(len(res.Cookies()) == 0, fmt.Sprintf("expected response to have no cookie, got %d", len(res.Cookies())), t)
  192. }
  193. func testValidSubPath(t *testing.T) {
  194. s := setupTest(func(w http.ResponseWriter, r *http.Request) {
  195. _, ok := GetSession(w)
  196. assertTrue(ok, "expected session to be non-nil, got nil", t)
  197. w.Write([]byte("ok"))
  198. }, "/foo", false, 0)
  199. defer s.Close()
  200. res := doRequest(s.URL+"/foo/bar", true)
  201. assertStatus(http.StatusOK, res.StatusCode, t)
  202. assertBody([]byte("ok"), res, t)
  203. assertTrue(len(res.Cookies()) == 1, fmt.Sprintf("expected response to have 1 cookie, got %d", len(res.Cookies())), t)
  204. }
  205. func testSecureOverHttp(t *testing.T) {
  206. s := setupTest(func(w http.ResponseWriter, r *http.Request) {
  207. _, ok := GetSession(w)
  208. assertTrue(ok, "expected session to be non-nil, got nil", t)
  209. w.Write([]byte("ok"))
  210. }, "", true, 0)
  211. defer s.Close()
  212. res := doRequest(s.URL, true)
  213. assertStatus(http.StatusOK, res.StatusCode, t)
  214. assertBody([]byte("ok"), res, t)
  215. assertTrue(len(res.Cookies()) == 0, fmt.Sprintf("expected response to have no cookie, got %d", len(res.Cookies())), t)
  216. }
  217. // TODO : commented, certificate problem
  218. func xtestSecureOverHttps(t *testing.T) {
  219. opts := NewSessionOptions(store, secret)
  220. opts.CookieTemplate.Secure = true
  221. h := SessionHandler(http.HandlerFunc(
  222. func(w http.ResponseWriter, r *http.Request) {
  223. _, ok := GetSession(w)
  224. assertTrue(ok, "expected session to be non-nil, got nil", t)
  225. w.Write([]byte("ok"))
  226. }), opts)
  227. s := httptest.NewTLSServer(h)
  228. defer s.Close()
  229. res := doRequest(s.URL, true)
  230. assertStatus(http.StatusOK, res.StatusCode, t)
  231. assertBody([]byte("ok"), res, t)
  232. assertTrue(len(res.Cookies()) == 1, fmt.Sprintf("expected response to have 1 cookie, got %d", len(res.Cookies())), t)
  233. }