MoreThanText proxy server.
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.

209 lines
4.3 KiB

// morethantext/proxy/sessionmanager.go
package proxy
import (
"net/http"
"time"
)
type sessionAction uint8
const sname = "sessionid"
const rname = "requestid"
const (
sessionUpdate sessionAction = iota + 1
sessionRequest
sessionDelete
sessionList
)
type pageGenerator interface {
ServeHTTP(http.ResponseWriter, *http.Request)
}
type sessionMessage struct {
action sessionAction
sessionID string
requestID string
infoChan chan sessionInfo
listChan chan []string
}
type sessionInfo struct {
requestID string
lastUpdate time.Time
}
type sessionManager struct {
msgIn chan sessionMessage
msgOut chan sessionMessage
handler pageGenerator
store map[string]sessionInfo
maxAge time.Duration
check time.Duration
timer *time.Timer
}
func newSessionManager(handler pageGenerator) *sessionManager {
maxAge := 24 * time.Hour
check := time.Hour
sm := &sessionManager{
msgIn: make(chan sessionMessage),
msgOut: make(chan sessionMessage),
handler: handler,
store: make(map[string]sessionInfo),
maxAge: maxAge,
check: check,
timer: time.NewTimer(check),
}
go func() {
for msg := range sm.msgIn {
switch msg.action {
case sessionUpdate:
sm.store[msg.sessionID] = sessionInfo{
requestID: msg.requestID,
lastUpdate: time.Now(),
}
case sessionRequest:
msg.infoChan <- sm.store[msg.sessionID]
case sessionDelete:
delete(sm.store, msg.sessionID)
case sessionList:
count := 0
result := make([]string, len(sm.store))
for sessid := range sm.store {
result[count] = sessid
count++
}
msg.listChan <- result
}
}
}()
go func() {
for {
<-sm.timer.C
for _, sessid := range sm.sessionList() {
if sm.sessionAge(sessid) > sm.maxAge {
sm.sessionDelete(sessid)
}
}
sm.timer.Reset(sm.check)
}
}()
return sm
}
func (sm *sessionManager) newSession() (string, string) {
sessid := newID()
reqid := newID()
msg := sessionMessage{
action: sessionUpdate,
sessionID: sessid,
requestID: reqid,
}
sm.msgOut <- msg
return sessid, reqid
}
func (sm *sessionManager) sessionDelete(sessid string) {
msg := sessionMessage{
action: sessionDelete,
sessionID: sessid,
}
sm.msgOut <- msg
}
func (sm *sessionManager) setMaxAge(maxAge time.Duration) {
sm.maxAge = maxAge
}
func (sm *sessionManager) setCheckTime(check time.Duration) {
sm.check = check
sm.timer.Reset(check)
}
func (sm *sessionManager) getSessionInfo(sessid string) sessionInfo {
infoChan := make(chan sessionInfo)
msg := sessionMessage{
action: sessionRequest,
sessionID: sessid,
infoChan: infoChan,
}
sm.msgOut <- msg
return <-infoChan
}
func (sm *sessionManager) getSession(sessid string) string {
request := sm.getSessionInfo(sessid)
return request.requestID
}
func (sm *sessionManager) sessionAge(sessid string) time.Duration {
request := sm.getSessionInfo(sessid)
return time.Now().Sub(request.lastUpdate)
}
func (sm *sessionManager) nextRequestID(sessid string) string {
reqid := newID()
msg := sessionMessage{
action: sessionUpdate,
sessionID: sessid,
requestID: reqid,
}
sm.msgOut <- msg
return reqid
}
func (sm *sessionManager) resetSession(sessid string) (string, string) {
sm.sessionDelete(sessid)
return sm.newSession()
}
func (sm *sessionManager) sessionList() []string {
listChan := make(chan []string)
msg := sessionMessage{
action: sessionList,
listChan: listChan,
}
sm.msgOut <- msg
return <-listChan
}
func (sm *sessionManager) createCookie(name string, value string) *http.Cookie {
return &http.Cookie{
Name: name,
Value: value,
Path: "/",
HttpOnly: true,
Secure: true,
}
}
func (sm *sessionManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var sessid, reqid string
var session, request *http.Cookie
for _, cookie := range r.Cookies() {
if cookie.Name == sname {
session = cookie
}
if cookie.Name == rname {
request = cookie
}
}
if session == nil || request == nil {
sessid, reqid = sm.newSession()
} else {
if request.Value == sm.getSession(session.Value) {
reqid = sm.nextRequestID(session.Value)
} else {
sessid, reqid = sm.resetSession(session.Value)
}
}
if sessid != "" {
http.SetCookie(w, sm.createCookie(sname, sessid))
}
http.SetCookie(w, sm.createCookie(rname, reqid))
sm.handler.ServeHTTP(w, r)
}