(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler)
| 182 | } |
| 183 | |
| 184 | func (b *Botshed) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { |
| 185 | if r.Method != http.MethodGet || !strings.HasPrefix(r.URL.Path, "/explain") { |
| 186 | return next.ServeHTTP(w, r) |
| 187 | } |
| 188 | cmd := r.URL.Query().Get("cmd") |
| 189 | bnPtr := b.basenames.Load() |
| 190 | cannedPtr := b.canned.Load() |
| 191 | if bnPtr == nil || cannedPtr == nil { |
| 192 | b.bootPassthroughs.Add(1) |
| 193 | return next.ServeHTTP(w, r) |
| 194 | } |
| 195 | if b.readyLogged.CompareAndSwap(false, true) { |
| 196 | b.logger.Info("botshed: ready", |
| 197 | zap.Uint64("boot_passthroughs", b.bootPassthroughs.Load()), |
| 198 | ) |
| 199 | } |
| 200 | bn := *bnPtr |
| 201 | canned := *cannedPtr |
| 202 | |
| 203 | shed, hits := shouldShed(cmd, bn) |
| 204 | if !shed { |
| 205 | return next.ServeHTTP(w, r) |
| 206 | } |
| 207 | |
| 208 | idx := fnv32(cmd) % uint32(len(canned)) |
| 209 | body := canned[idx] |
| 210 | w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 211 | w.Header().Set("Cache-Control", "public, max-age=86400") |
| 212 | w.WriteHeader(http.StatusOK) |
| 213 | _, _ = w.Write(body) |
| 214 | |
| 215 | cmdSnip := cmd |
| 216 | if len(cmdSnip) > 80 { |
| 217 | cmdSnip = cmdSnip[:80] |
| 218 | } |
| 219 | b.logger.Info("botshed: shed", |
| 220 | zap.String("client_ip", clientIP(r)), |
| 221 | zap.String("country", r.Header.Get("CF-IPCountry")), |
| 222 | zap.String("cmd", cmdSnip), |
| 223 | zap.Int("hits", hits), |
| 224 | zap.Uint32("canned_idx", idx), |
| 225 | ) |
| 226 | return nil |
| 227 | } |
| 228 | |
| 229 | // shouldShed is the single source of truth for the detector decision. |
| 230 | // ServeHTTP and the unit tests both call through here so the rule cannot |
nothing calls this directly
no test coverage detected