Skip to content

Instantly share code, notes, and snippets.

@ShawnMilo
Created July 26, 2019 19:25
Show Gist options
  • Save ShawnMilo/b2bfe4f307b412ac50ba3eb4d92ea9d6 to your computer and use it in GitHub Desktop.
Save ShawnMilo/b2bfe4f307b412ac50ba3eb4d92ea9d6 to your computer and use it in GitHub Desktop.
Simple use of cookies in Go
package main
import (
"crypto/rand"
"fmt"
"log"
"net/http"
"sync"
"time"
)
var sessions = make(map[string]time.Time)
var sessionLock sync.RWMutex
func init() {
go expireSessions()
}
func main() {
http.HandleFunc("/", index)
http.HandleFunc("/login", login)
http.ListenAndServe(":8000", nil)
}
func index(w http.ResponseWriter, r *http.Request) {
sessionID := getSessionID(r)
if !sessionValid(sessionID) {
anon(w, r)
return
}
w.Write(page(fmt.Sprintf("You are logged in: %q", sessionID)))
}
func anon(w http.ResponseWriter, r *http.Request) {
w.Write(page("You are not logged in."))
}
func login(w http.ResponseWriter, r *http.Request) {
p := r.FormValue("password")
if p == "secret" {
sessionID, err := UUID4()
if err != nil {
log.Printf("Failed to create UUID: %s\n", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
c := http.Cookie{Name: "SessionID", Value: sessionID}
http.SetCookie(w, &c)
createSession(sessionID)
http.Redirect(w, r, "/", http.StatusFound)
return
}
w.Write(page("Incorrect password."))
}
func sessionValid(sessionID string) bool {
sessionLock.RLock()
defer sessionLock.RUnlock()
_, found := sessions[sessionID]
return found
}
func expireSessions() {
for {
time.Sleep(time.Second)
sessionLock.RLock()
for k, v := range sessions {
if v.Before(time.Now()) {
go deleteSession(k)
}
}
sessionLock.RUnlock()
}
}
func deleteSession(key string) {
sessionLock.Lock()
defer sessionLock.Unlock()
delete(sessions, key)
}
func createSession(key string) {
sessionLock.Lock()
defer sessionLock.Unlock()
sessions[key] = time.Now().Add(time.Second * 5)
}
func UUID4() (string, error) {
b := make([]byte, 16)
_, err := rand.Read(b[:])
if err != nil {
return "", err
}
// Set the two most significant bits (bits 6 and 7) of the
// clock_seq_hi_and_reserved to zero and one, respectively.
b[8] = (b[8] | 0x40) & 0x7F
// Set the four most significant bits (bits 12 through 15) of the
// time_hi_and_version field to the 4-bit version number.
b[6] = (b[6] & 0xF) | (4 << 4)
// Return unparsed version of the generated UUID sequence.
return fmt.Sprintf("%x-%x-%x-%x-%x",
b[0:4], b[4:6], b[6:8], b[8:10], b[10:]), nil
}
func getSessionID(r *http.Request) string {
c, err := r.Cookie("SessionID")
if err != nil {
log.Printf("Error getting session cookie: %s\n", err)
return ""
}
return c.Value
}
var template = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Login demo</title>
<meta name="viewport" content="width-device-width, initial-scale=1">
</head>
<body>
<div>
<p>%s</p>
<p><a href="/">home</a></p>
<p><a href="/login?password=secret">log in</a></p>
</div>
</body>
</html>
`
func page(content string) []byte {
return []byte(fmt.Sprintf(template, content))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment