150 lines
3.0 KiB
Go
150 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"github.com/gorilla/handlers"
|
|
"github.com/gorilla/mux"
|
|
)
|
|
|
|
// Counter mutex for counter
|
|
type counter struct {
|
|
mu sync.Mutex
|
|
n int
|
|
}
|
|
|
|
// Global counter
|
|
var C counter
|
|
|
|
// Add value to counter
|
|
func (c *counter) Add() {
|
|
c.mu.Lock()
|
|
c.n++
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
// Get value from counter
|
|
func (c *counter) Get() int {
|
|
c.mu.Lock()
|
|
n := c.n
|
|
c.mu.Unlock()
|
|
return n
|
|
}
|
|
|
|
// Reset counter
|
|
func (c *counter) Reset() {
|
|
c.mu.Lock()
|
|
c.n = 0
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
// getenv reads an environment variable named k and returns it as type D
|
|
func getenv[D ~string | int](k string, d D) D {
|
|
v := os.Getenv(k)
|
|
if len(v) == 0 {
|
|
return d
|
|
}
|
|
var r any
|
|
switch any(d).(type) {
|
|
case string:
|
|
r = v
|
|
case int:
|
|
i, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
log.Fatalf("Invalid Value, %s not a valid integer", k)
|
|
}
|
|
r = i
|
|
default:
|
|
log.Fatalf("Invalid Value, %T not valid", k)
|
|
}
|
|
return r.(D)
|
|
}
|
|
|
|
// serve evaluates if the limit of requests is reached
|
|
func serve(l int) bool {
|
|
return C.Get() < l
|
|
}
|
|
|
|
// reverse returns a reversed byte array of c
|
|
func reverse[C ~[]E, E any](c C) C {
|
|
for i, j := 0, len(c)-1; i < j; i, j = i+1, j-1 {
|
|
c[i], c[j] = c[j], c[i]
|
|
}
|
|
return c
|
|
}
|
|
|
|
// handler generates the echo server response
|
|
func handler(l int) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
defer C.Add()
|
|
if !serve(l) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if r.Header.Get("Content-Type") == "application/json" {
|
|
v := make(map[string]interface{})
|
|
err := json.NewDecoder(r.Body).Decode(&v)
|
|
if err != nil {
|
|
http.Error(w, "why did you do that?", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
fmt.Fprintf(w, "%s", v)
|
|
return
|
|
}
|
|
v, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
http.Error(w, "Error reading body", http.StatusBadRequest)
|
|
}
|
|
if r.Header.Get("Reverse") == "true" {
|
|
v = reverse(v)
|
|
}
|
|
fmt.Fprintf(w, "%s", v)
|
|
}
|
|
}
|
|
|
|
// httpHealth is a bad implementation of a health check
|
|
func httpHealth(l int) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if !serve(l) {
|
|
http.Error(w, fmt.Sprintf(`{"status":"FAIL","requests":"%v"}`, C.Get()), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
fmt.Fprintf(w, `{"status":"ok","requests":"%v"}`, C.Get())
|
|
}
|
|
}
|
|
|
|
// reset resets the request counter
|
|
func reset(rt string) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
r.ParseForm()
|
|
t := r.FormValue("TOKEN")
|
|
if len(t) == 0 || t != rt {
|
|
http.Error(w, "Bad request, invalid token", http.StatusBadRequest)
|
|
return
|
|
}
|
|
C.Reset()
|
|
fmt.Fprintf(w, `{"status":"ok","requests":"%v"}`, C.Get())
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
l := getenv("MAX_REQUESTS", 500)
|
|
t := getenv("TOKEN", "token")
|
|
r := mux.NewRouter()
|
|
r.HandleFunc("/", handler(l)).
|
|
Methods("POST")
|
|
r.HandleFunc("/healthz", httpHealth(l)).
|
|
Methods("GET")
|
|
r.HandleFunc("/reset", reset(t)).
|
|
Methods("PUT")
|
|
logger := handlers.LoggingHandler(os.Stdout, r)
|
|
log.Fatal(http.ListenAndServe(":8080", logger))
|
|
}
|