check / check (push) Failing after 2s
REPO_POLICIES.md requires cmd/ to be thin: one main.go whose body is a single call into internal/ or pkg/. The image, the handler, the server and their test move unchanged into internal/loadtestorigin, whose Run does what main did; cmd/loadtest-origin/main.go only calls it. Model: opus-5-5
88 lines
2.1 KiB
Go
88 lines
2.1 KiB
Go
// Package loadtestorigin is the upstream host script/loadtest points pixad
|
|
// at, run by cmd/loadtest-origin. It answers every request, whatever its path,
|
|
// with the same generated JPEG, so each new path is a new source image for
|
|
// pixad to fetch, and it logs one line per request, so its log counts pixad's
|
|
// fetches.
|
|
package loadtestorigin
|
|
|
|
import (
|
|
"bytes"
|
|
"image"
|
|
"image/color"
|
|
"image/jpeg"
|
|
"log/slog"
|
|
"math"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
listenAddress = ":80"
|
|
readHeaderTimeout = 10 * time.Second
|
|
imageWidth = 1600
|
|
imageHeight = 1200
|
|
jpegQuality = 85
|
|
)
|
|
|
|
// Run makes the image and serves it on port 80 until the server fails, then
|
|
// exits the process with status 1.
|
|
func Run() {
|
|
photo, err := makeJPEG()
|
|
if err != nil {
|
|
slog.Error("cannot make the image", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
server := &http.Server{
|
|
Addr: listenAddress,
|
|
Handler: newHandler(photo),
|
|
ReadHeaderTimeout: readHeaderTimeout,
|
|
}
|
|
|
|
err = server.ListenAndServe()
|
|
slog.Error("server stopped", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// newHandler answers every request with photo and logs the request's path.
|
|
func newHandler(photo []byte) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
slog.Info("request", "path", r.URL.Path)
|
|
w.Header().Set("Content-Type", "image/jpeg")
|
|
_, _ = w.Write(photo)
|
|
})
|
|
}
|
|
|
|
// makeJPEG draws colour gradients crossed with a fine pattern, so the image
|
|
// has detail to decode and does not compress to almost nothing.
|
|
func makeJPEG() ([]byte, error) {
|
|
img := image.NewRGBA(image.Rect(0, 0, imageWidth, imageHeight))
|
|
|
|
// red and green count up from 0 to 255 and wrap around, along each row
|
|
// and down the image.
|
|
var green uint8
|
|
|
|
for y := range imageHeight {
|
|
var red uint8
|
|
|
|
for x := range imageWidth {
|
|
img.SetRGBA(x, y, color.RGBA{
|
|
R: red, G: green, B: red ^ green, A: math.MaxUint8,
|
|
})
|
|
red++
|
|
}
|
|
|
|
green++
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
err := jpeg.Encode(&buf, img, &jpeg.Options{Quality: jpegQuality})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
}
|