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 }