84 lines
1.5 KiB
Go
84 lines
1.5 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"hash"
|
|
"html/template"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var TPL *template.Template
|
|
|
|
const (
|
|
SECRET = "QDFuO2FyRHhGKEg5QlI4XDMlM2sK"
|
|
COUNTER_COOKIE string = "counter"
|
|
)
|
|
|
|
func displayPage(res http.ResponseWriter, req *http.Request) {
|
|
count, cookie := updateCounter(req)
|
|
http.SetCookie(res, cookie)
|
|
TPL.Execute(res, count)
|
|
}
|
|
|
|
func updateCounter(req *http.Request) (uint64, *http.Cookie) {
|
|
count := fromCookie(req)
|
|
count++
|
|
val := toCookieVal(count)
|
|
cookie := &http.Cookie{
|
|
Name: COUNTER_COOKIE,
|
|
Value: val,
|
|
}
|
|
return count, cookie
|
|
}
|
|
|
|
func fromCookie(req *http.Request) uint64 {
|
|
cookie, err := req.Cookie(COUNTER_COOKIE)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
sp := strings.Split(cookie.Value, "|")
|
|
if len(sp) != 2 {
|
|
return 0
|
|
}
|
|
c, err := strconv.Atoi(sp[0])
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
if !checkMac(sp[0], sp[1]) {
|
|
return 0
|
|
}
|
|
return uint64(c)
|
|
}
|
|
|
|
func checkMac(val string, macStr string) bool {
|
|
var mac []byte
|
|
fmt.Sscanf(macStr, "%x", &mac)
|
|
return hmac.Equal(mac, cookieHash(val).Sum(nil))
|
|
}
|
|
|
|
func cookieHash(val string) hash.Hash {
|
|
mac := hmac.New(sha256.New, []byte(SECRET))
|
|
mac.Write([]byte(val))
|
|
return mac
|
|
}
|
|
|
|
func toCookieVal(count uint64) string {
|
|
val := strconv.FormatUint(count, 10)
|
|
return val + "|" + fmt.Sprintf("%x", cookieHash(val).Sum(nil))
|
|
}
|
|
|
|
func main() {
|
|
var err error
|
|
TPL, err = template.ParseFiles("tpl.html")
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
http.HandleFunc("/", displayPage)
|
|
http.ListenAndServe(":9000", nil)
|
|
}
|