c7f16b5d by astaxie

Merge pull request #551 from steamonimo/develop

session provider for postgresql
2 parents c921b0aa 8d1268c0
1 package session
2
3 /*
4
5 beego session provider for postgresql
6 -------------------------------------
7
8 depends on github.com/lib/pq:
9
10 go install github.com/lib/pq
11
12
13 needs this table in your database:
14
15 CREATE TABLE session (
16 session_key char(64) NOT NULL,
17 session_data bytea,
18 session_expiry timestamp NOT NULL,
19 CONSTRAINT session_key PRIMARY KEY(session_key)
20 );
21
22
23 will be activated with these settings in app.conf:
24
25 SessionOn = true
26 SessionProvider = postgresql
27 SessionSavePath = "user=a password=b dbname=c sslmode=disable"
28 SessionName = session
29
30 */
31
32 import (
33 "database/sql"
34 "net/http"
35 "sync"
36 "time"
37 _ "github.com/lib/pq"
38 )
39
40 var postgresqlpder = &PostgresqlProvider{}
41
42 // postgresql session store
43 type PostgresqlSessionStore struct {
44 c *sql.DB
45 sid string
46 lock sync.RWMutex
47 values map[interface{}]interface{}
48 }
49
50 // set value in postgresql session.
51 // it is temp value in map.
52 func (st *PostgresqlSessionStore) Set(key, value interface{}) error {
53 st.lock.Lock()
54 defer st.lock.Unlock()
55 st.values[key] = value
56 return nil
57 }
58
59 // get value from postgresql session
60 func (st *PostgresqlSessionStore) Get(key interface{}) interface{} {
61 st.lock.RLock()
62 defer st.lock.RUnlock()
63 if v, ok := st.values[key]; ok {
64 return v
65 } else {
66 return nil
67 }
68 return nil
69 }
70
71 // delete value in postgresql session
72 func (st *PostgresqlSessionStore) Delete(key interface{}) error {
73 st.lock.Lock()
74 defer st.lock.Unlock()
75 delete(st.values, key)
76 return nil
77 }
78
79 // clear all values in postgresql session
80 func (st *PostgresqlSessionStore) Flush() error {
81 st.lock.Lock()
82 defer st.lock.Unlock()
83 st.values = make(map[interface{}]interface{})
84 return nil
85 }
86
87 // get session id of this postgresql session store
88 func (st *PostgresqlSessionStore) SessionID() string {
89 return st.sid
90 }
91
92 // save postgresql session values to database.
93 // must call this method to save values to database.
94 func (st *PostgresqlSessionStore) SessionRelease(w http.ResponseWriter) {
95 defer st.c.Close()
96 b, err := encodeGob(st.values)
97 if err != nil {
98 return
99 }
100 st.c.Exec("UPDATE session set session_data=$1, session_expiry=$2 where session_key=$3",
101 b, time.Now().Format(time.RFC3339), st.sid)
102
103 }
104
105 // postgresql session provider
106 type PostgresqlProvider struct {
107 maxlifetime int64
108 savePath string
109 }
110
111 // connect to postgresql
112 func (mp *PostgresqlProvider) connectInit() *sql.DB {
113 db, e := sql.Open("postgres", mp.savePath)
114 if e != nil {
115 return nil
116 }
117 return db
118 }
119
120 // init postgresql session.
121 // savepath is the connection string of postgresql.
122 func (mp *PostgresqlProvider) SessionInit(maxlifetime int64, savePath string) error {
123 mp.maxlifetime = maxlifetime
124 mp.savePath = savePath
125 return nil
126 }
127
128 // get postgresql session by sid
129 func (mp *PostgresqlProvider) SessionRead(sid string) (SessionStore, error) {
130 c := mp.connectInit()
131 row := c.QueryRow("select session_data from session where session_key=$1", sid)
132 var sessiondata []byte
133 err := row.Scan(&sessiondata)
134 if err == sql.ErrNoRows {
135 _, err = c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)",
136 sid, "", time.Now().Format(time.RFC3339))
137
138 if err != nil {
139 return nil, err
140 }
141 } else if err != nil {
142 return nil, err
143 }
144
145 var kv map[interface{}]interface{}
146 if len(sessiondata) == 0 {
147 kv = make(map[interface{}]interface{})
148 } else {
149 kv, err = decodeGob(sessiondata)
150 if err != nil {
151 return nil, err
152 }
153 }
154 rs := &PostgresqlSessionStore{c: c, sid: sid, values: kv}
155 return rs, nil
156 }
157
158 // check postgresql session exist
159 func (mp *PostgresqlProvider) SessionExist(sid string) bool {
160 c := mp.connectInit()
161 defer c.Close()
162 row := c.QueryRow("select session_data from session where session_key=$1", sid)
163 var sessiondata []byte
164 err := row.Scan(&sessiondata)
165
166 if err == sql.ErrNoRows {
167 return false
168 } else {
169 return true
170 }
171 }
172
173 // generate new sid for postgresql session
174 func (mp *PostgresqlProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
175 c := mp.connectInit()
176 row := c.QueryRow("select session_data from session where session_key=$1", oldsid)
177 var sessiondata []byte
178 err := row.Scan(&sessiondata)
179 if err == sql.ErrNoRows {
180 c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)",
181 oldsid, "", time.Now().Format(time.RFC3339))
182 }
183 c.Exec("update session set session_key=$1 where session_key=$2", sid, oldsid)
184 var kv map[interface{}]interface{}
185 if len(sessiondata) == 0 {
186 kv = make(map[interface{}]interface{})
187 } else {
188 kv, err = decodeGob(sessiondata)
189 if err != nil {
190 return nil, err
191 }
192 }
193 rs := &PostgresqlSessionStore{c: c, sid: sid, values: kv}
194 return rs, nil
195 }
196
197 // delete postgresql session by sid
198 func (mp *PostgresqlProvider) SessionDestroy(sid string) error {
199 c := mp.connectInit()
200 c.Exec("DELETE FROM session where session_key=$1", sid)
201 c.Close()
202 return nil
203 }
204
205 // delete expired values in postgresql session
206 func (mp *PostgresqlProvider) SessionGC() {
207 c := mp.connectInit()
208 c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime)
209 c.Close()
210 return
211 }
212
213 // count values in postgresql session
214 func (mp *PostgresqlProvider) SessionAll() int {
215 c := mp.connectInit()
216 defer c.Close()
217 var total int
218 err := c.QueryRow("SELECT count(*) as num from session").Scan(&total)
219 if err != nil {
220 return 0
221 }
222 return total
223 }
224
225 func init() {
226 Register("postgresql", postgresqlpder)
227 }
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!