132 lines
3.0 KiB
Go
132 lines
3.0 KiB
Go
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 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
|
|
}
|
|
}
|
|
}
|