102 lines
2.3 KiB
Go
102 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/kordondev/meeting/throttling"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const burstLimit = 3
|
|
const rateLimit = time.Second * 3
|
|
const correctResult = "12"
|
|
|
|
func main() {
|
|
ctx := context.Background()
|
|
var throttles sync.Map
|
|
|
|
http.HandleFunc("/", serveIndexFile)
|
|
http.HandleFunc("/input.txt", serveInputFile)
|
|
http.HandleFunc("/result", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
cctxV, ok := getThrottleContext(&throttles, r.RemoteAddr)
|
|
|
|
if !ok {
|
|
cctx, cancel := context.WithCancel(ctx)
|
|
throttle := throttling.CreateThrottle(cctx, burstLimit, rateLimit)
|
|
a := throttleWithCancel{
|
|
throttle: throttle,
|
|
cancel: cancel,
|
|
}
|
|
cctxV = context.WithValue(ctx, "throttle", a)
|
|
throttles.Store(r.RemoteAddr, cctxV)
|
|
go func() {
|
|
time.Sleep(time.Minute * 20)
|
|
throttles.Delete(r.RemoteAddr)
|
|
cancel()
|
|
}()
|
|
}
|
|
|
|
tryResult(cctxV, w, r)
|
|
})
|
|
|
|
err := http.ListenAndServe(":3333", nil)
|
|
if err != nil {
|
|
fmt.Printf("http.ListenAndServe() failed with %s\n", err)
|
|
}
|
|
}
|
|
|
|
type throttleWithCancel struct {
|
|
throttle <-chan time.Time
|
|
cancel context.CancelFunc
|
|
}
|
|
|
|
func serveIndexFile(w http.ResponseWriter, r *http.Request) {
|
|
http.ServeFile(w, r, "static/index.html")
|
|
}
|
|
|
|
func serveInputFile(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Println(time.Now(), "Load input file", r.RemoteAddr)
|
|
http.ServeFile(w, r, "static/input.txt")
|
|
}
|
|
|
|
func tryResult(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
clientResult := r.URL.Query().Get("result")
|
|
name := r.URL.Query().Get("name")
|
|
fmt.Println(time.Now(), "Submit result", clientResult, r.RemoteAddr, name)
|
|
throttle := ctx.Value("throttle").(throttleWithCancel).throttle
|
|
payload := throttling.Payload{
|
|
R: r,
|
|
W: w,
|
|
ClientResult: clientResult,
|
|
}
|
|
|
|
throttling.CallFunction(ctx, &CheckResult{}, &payload, throttle)
|
|
}
|
|
|
|
type CheckResult struct {
|
|
}
|
|
|
|
func (*CheckResult) Call(payload *throttling.Payload) {
|
|
w := payload.W
|
|
r := payload.R
|
|
|
|
fmt.Println(time.Now(), "Serve now for", r.RemoteAddr)
|
|
if payload.ClientResult == correctResult {
|
|
http.ServeFile(w, r, "static/success.html")
|
|
return
|
|
}
|
|
http.ServeFile(w, r, "static/fail.html")
|
|
}
|
|
|
|
func getThrottleContext(throttles *sync.Map, RemoteAddr string) (context.Context, bool) {
|
|
ctx1, ok := throttles.Load(RemoteAddr)
|
|
if !ok {
|
|
return nil, ok
|
|
}
|
|
return ctx1.(context.Context), true
|
|
|
|
}
|