Files
hpnn-proto/backend/main.go
T

105 lines
3.4 KiB
Go
Raw Normal View History

// Бэкенд стенда hpnn_v2: показывает имя хоста, дату и время, а также реальный
// адрес клиента. Последнее — ключевая проверка схемы DNAT-без-SNAT: если в
// поле client стоит адрес из клиентской подсети, а не адрес балансировщика,
// значит трансляция источника нигде не выполняется.
package main
import (
"fmt"
"log"
"net"
"net/http"
"os"
"strconv"
"sync/atomic"
"time"
)
var requests atomic.Uint64
func localAddr(r *http.Request) string {
if a, ok := r.Context().Value(http.LocalAddrContextKey).(net.Addr); ok {
return a.String()
}
return "unknown"
}
func main() {
listen := os.Getenv("LISTEN")
if listen == "" {
listen = ":8080"
}
host, err := os.Hostname()
if err != nil {
host = "unknown"
}
mux := http.NewServeMux()
// Одна строка на запрос: удобно читать вывод цикла из 20 curl.
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
n := requests.Add(1)
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintf(w, "backend=%-6s time=%s client=%-21s served=%-21s req=%d\n",
host,
time.Now().Format("2006-01-02 15:04:05 MST"),
r.RemoteAddr,
localAddr(r),
n)
})
// Развёрнутый вид — для ручного осмотра в браузере.
mux.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) {
n := requests.Add(1)
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintf(w, "Имя хоста : %s\n", host)
fmt.Fprintf(w, "Дата и время : %s\n", time.Now().Format("2006-01-02 15:04:05 MST -07:00"))
fmt.Fprintf(w, "Адрес клиента : %s\n", r.RemoteAddr)
fmt.Fprintf(w, "Адрес назначения: %s\n", localAddr(r))
fmt.Fprintf(w, "Host-заголовок : %s\n", r.Host)
fmt.Fprintf(w, "Запрос № : %d\n", n)
})
// Цель health-проб балансировщика.
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintln(w, "ok")
})
// Длинный ответ: тело отдаётся по строке в секунду. Нужен, чтобы держать
// открытую сессию во время смены состава пула и проверять, что уже
// установленное соединение не рвётся. /slow?seconds=20
mux.HandleFunc("/slow", func(w http.ResponseWriter, r *http.Request) {
seconds := 10
if v := r.URL.Query().Get("seconds"); v != "" {
if n, err := strconv.Atoi(v); err == nil && n > 0 && n <= 120 {
seconds = n
}
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
flusher, _ := w.(http.Flusher)
for i := 1; i <= seconds; i++ {
fmt.Fprintf(w, "backend=%s tick=%d/%d time=%s\n", host, i, seconds,
time.Now().Format("15:04:05"))
if flusher != nil {
flusher.Flush()
}
select {
case <-r.Context().Done():
return
case <-time.After(time.Second):
}
}
})
srv := &http.Server{
Addr: listen,
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
}
log.Printf("backend %s слушает %s", host, listen)
if err := srv.ListenAndServe(); err != nil {
log.Fatal(err)
}
}