diff options
Diffstat (limited to 'session.go')
| -rw-r--r-- | session.go | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/session.go b/session.go new file mode 100644 index 0000000..a06a344 --- /dev/null +++ b/session.go @@ -0,0 +1,142 @@ +package main + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "net/http" + "sync" + "time" +) + +type TSession struct { + SessionID []byte + IPAddress string + Expires time.Time + AccountID int +} + +// IMPORTANT(fusion): Ideally you'd save sessions in a database to reduce memory +// usage and to make them persistent with server restarts. In reality, we should +// have a low amount of sessions and a high server uptime, making the memory usage +// here minimal. We can always turn this into a LRU cache with a set maximum number +// of sessions. + +var ( + g_SessionsMutex sync.Mutex + g_Sessions []TSession +) + +func GenerateSessionID() []byte { + var SessionID [32]byte + _, Err := rand.Read(SessionID[:]) + if Err != nil { + g_LogErr.Printf("Failed to generate session id: %v", Err) + return nil + } + + return SessionID[:] +} + +func GetRequestSessionID(Request *http.Request) []byte { + Cookie, Err := Request.Cookie("GOSESSID") + if Err != nil { + return nil + } + + SessionID, Err := hex.DecodeString(Cookie.Value) + if Err != nil { + g_LogErr.Printf("Failed to decode session id: %v", Err) + return nil + } + + if len(SessionID) != 32 { + g_LogErr.Printf("Invalid session id size %v (expected 32)", len(SessionID)) + return nil + } + + return SessionID +} + +func SessionLookup(SessionID []byte, IPAddress string) int { + AccountID := 0 + if SessionID != nil && IPAddress != "" { + g_SessionsMutex.Lock() + defer g_SessionsMutex.Unlock() + for Index := 0; Index < len(g_Sessions); Index += 1 { + Session := &g_Sessions[Index] + + if time.Until(Session.Expires) <= 0 { + g_Sessions = SwapAndPop(g_Sessions, Index) + Index -= 1 + continue + } + + if bytes.Equal(Session.SessionID, SessionID) && Session.IPAddress == IPAddress { + AccountID = Session.AccountID + break + } + } + } + return AccountID +} + +func SessionStart(Context *THttpRequestContext, AccountID int) { + if AccountID <= 0 { + g_LogErr.Printf("Trying to start session with invalid account id %v", AccountID) + return + } + + SessionID := make([]byte, 32) + if _, Err := rand.Read(SessionID); Err != nil { + g_LogErr.Printf("Failed to generate session id: %v", Err) + return + } + + Context.SessionID = SessionID + Context.AccountID = AccountID + Expires := time.Now().Add(time.Hour) + http.SetCookie(Context.Writer, &http.Cookie{ + Name: "GOSESSID", + Value: hex.EncodeToString(SessionID), + Path: "/", + Expires: Expires, + Secure: false, // TODO(fusion): Enable this when HTTPS is enabled (?). + HttpOnly: true, + }) + + g_SessionsMutex.Lock() + defer g_SessionsMutex.Unlock() + g_Sessions = append(g_Sessions, + TSession{ + SessionID: SessionID, + IPAddress: Context.IPAddress, + Expires: Expires, + AccountID: AccountID, + }) +} + +func SessionEnd(Context *THttpRequestContext) { + if Context.SessionID == nil { + return + } + + http.SetCookie(Context.Writer, &http.Cookie{ + Name: "GOSESSID", + Value: "", + Path: "/", + Expires: time.Unix(0, 0), + }) + + g_SessionsMutex.Lock() + defer g_SessionsMutex.Unlock() + for Index := 0; Index < len(g_Sessions); Index += 1 { + Session := &g_Sessions[Index] + if bytes.Equal(Session.SessionID, Context.SessionID) && Session.IPAddress == Context.IPAddress { + g_Sessions[Index] = g_Sessions[len(g_Sessions)-1] + g_Sessions[len(g_Sessions)-1] = TSession{} + g_Sessions = g_Sessions[:len(g_Sessions)-1] + break + } + } +} |
