github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/web/session.go (about)

     1  // Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package web
     6  
     7  import (
     8  	"crypto/rand"
     9  	"fmt"
    10  	"log"
    11  	"net/http"
    12  	"sync"
    13  	"time"
    14  )
    15  
    16  // SessionManager manager sessions.
    17  type SessionManager struct {
    18  	mutex      sync.RWMutex
    19  	mutexOnEnd sync.RWMutex
    20  	sessionMap map[string]*Session
    21  	onStart    func(*Session)
    22  	onTouch    func(*Session)
    23  	onEnd      func(*Session)
    24  	path       string
    25  	timeout    uint
    26  }
    27  
    28  // Session stores the id and value for a session.
    29  type Session struct {
    30  	Id    string
    31  	Value interface{}
    32  
    33  	manager *SessionManager
    34  	res     http.ResponseWriter
    35  	expire  int64
    36  }
    37  
    38  func (session *Session) Cookie() string {
    39  	tm := time.Unix(session.expire, 0).UTC()
    40  	return fmt.Sprintf(
    41  		"SessionId=%s; path=%s; expires=%s;",
    42  		session.Id,
    43  		session.manager.path,
    44  		tm.Format("Fri, 02-Jan-2006 15:04:05 -0700"),
    45  	)
    46  }
    47  
    48  func (session *Session) Abandon() {
    49  	if _, found := session.manager.sessionMap[session.Id]; found {
    50  		delete((*session.manager).sessionMap, session.Id)
    51  	}
    52  	if session.res != nil {
    53  		session.res.Header().Set(
    54  			"Set-Cookie", fmt.Sprintf("SessionId=; path=%s;", session.manager.path),
    55  		)
    56  	}
    57  }
    58  
    59  func NewSessionManager(logger *log.Logger) *SessionManager {
    60  	p := &SessionManager{
    61  		path:       "/",
    62  		sessionMap: make(map[string]*Session),
    63  		timeout:    300,
    64  	}
    65  	go func(p *SessionManager) {
    66  		for { // never stop !!!
    67  			l := time.Now().Unix()
    68  			if f := p.onEnd; f != nil {
    69  				p.mutexOnEnd.Lock()
    70  				for id, v := range p.sessionMap {
    71  					if v.expire < l {
    72  						if logger != nil {
    73  							logger.Printf("Expired session(id:%s)", id)
    74  						}
    75  						f(v)
    76  						delete(p.sessionMap, id)
    77  					}
    78  				}
    79  				p.mutexOnEnd.Unlock()
    80  			} else {
    81  				for id, v := range p.sessionMap {
    82  					if v.expire < l {
    83  						if logger != nil {
    84  							logger.Printf("Expired session(id:%s)", id)
    85  						}
    86  						delete(p.sessionMap, id)
    87  					}
    88  				}
    89  			}
    90  			time.Sleep(time.Second)
    91  		}
    92  	}(p)
    93  	return p
    94  }
    95  
    96  func (p *SessionManager) Has(id string) (found bool) {
    97  	_, found = p.sessionMap[id]
    98  	return
    99  }
   100  
   101  func (p *SessionManager) GetSessionById(id string) (session *Session) {
   102  	p.mutex.Lock()
   103  	defer p.mutex.Unlock()
   104  	if id == "" || !p.Has(id) {
   105  		b := make([]byte, 16)
   106  		if _, err := rand.Read(b); err != nil {
   107  			return
   108  		}
   109  		id = fmt.Sprintf("%x", b)
   110  	}
   111  	tm := time.Unix(time.Now().Unix()+int64(p.timeout), 0).UTC()
   112  	var found bool
   113  	if session, found = p.sessionMap[id]; found {
   114  		session.expire = tm.Unix()
   115  		if f := p.onTouch; f != nil {
   116  			f(session)
   117  		}
   118  		return
   119  	} else {
   120  		session = &Session{Id: id, expire: tm.Unix(), manager: p}
   121  		p.sessionMap[id] = session
   122  		if f := p.onStart; f != nil {
   123  			f(session)
   124  		}
   125  	}
   126  	return
   127  }
   128  
   129  func (p *SessionManager) GetSession(res http.ResponseWriter, req *http.Request) (session *Session) {
   130  	if c, _ := req.Cookie("SessionId"); c != nil {
   131  		session = p.GetSessionById(c.Value)
   132  	} else {
   133  		session = p.GetSessionById("")
   134  	}
   135  	if res != nil {
   136  		session.res = res
   137  		res.Header().Add("Set-Cookie",
   138  			fmt.Sprintf("SessionId=%s; path=%s; expires=%s;",
   139  				session.Id,
   140  				session.manager.path,
   141  				time.Unix(session.expire, 0).UTC().Format(
   142  					"Fri, 02-Jan-2006 15:04:05 GMT",
   143  				),
   144  			),
   145  		)
   146  	}
   147  	return
   148  }
   149  
   150  func (p *SessionManager) Abandon() {
   151  	p.mutex.Lock()
   152  	defer p.mutex.Unlock()
   153  	if f := p.onEnd; f != nil {
   154  		p.mutexOnEnd.Lock()
   155  		for id, v := range p.sessionMap {
   156  			f(v)
   157  			delete(p.sessionMap, id)
   158  		}
   159  		p.mutexOnEnd.Unlock()
   160  	} else {
   161  		for id, _ := range p.sessionMap {
   162  			delete(p.sessionMap, id)
   163  		}
   164  	}
   165  }
   166  
   167  func (p *SessionManager) OnStart(f func(*Session)) {
   168  	p.mutex.Lock()
   169  	defer p.mutex.Unlock()
   170  	p.onStart = f
   171  }
   172  
   173  func (p *SessionManager) OnTouch(f func(*Session)) {
   174  	p.mutex.Lock()
   175  	defer p.mutex.Unlock()
   176  	p.onTouch = f
   177  }
   178  
   179  func (p *SessionManager) OnEnd(f func(*Session)) {
   180  	p.mutex.Lock()
   181  	defer p.mutex.Unlock()
   182  	p.onEnd = f
   183  }
   184  
   185  func (p *SessionManager) SetTimeout(t uint) {
   186  	p.mutex.Lock()
   187  	defer p.mutex.Unlock()
   188  	p.timeout = t
   189  }
   190  
   191  func (p *SessionManager) GetTimeout() uint {
   192  	p.mutex.Lock()
   193  	defer p.mutex.Unlock()
   194  	return p.timeout
   195  }
   196  
   197  func (p *SessionManager) SetPath(t string) {
   198  	p.mutex.Lock()
   199  	defer p.mutex.Unlock()
   200  	p.path = t
   201  }
   202  
   203  func (p *SessionManager) GetPath() string {
   204  	p.mutex.Lock()
   205  	defer p.mutex.Unlock()
   206  	return p.path
   207  }