package main import ( "fmt" "io" "log" "net/http" "sync/atomic" "zoraxy-firewall/zoraxy_plugin" ) var breaker = newCircuitBreaker() // Metrics var ( requestsTotal uint64 requestsAllowed uint64 requestsBlocked uint64 requestsBypassed uint64 ) func metricsSnapshot() map[string]interface{} { return map[string]interface{}{ "total": atomic.LoadUint64(&requestsTotal), "allowed": atomic.LoadUint64(&requestsAllowed), "blocked": atomic.LoadUint64(&requestsBlocked), "bypassed": atomic.LoadUint64(&requestsBypassed), "circuit": circuitStateStr(breaker.State()), "enabled": getConfig().Enabled, } } func circuitStateStr(s CircuitState) string { switch s { case StateClosed: return "closed" case StateOpen: return "open" case StateHalfOpen: return "half-open" } return "unknown" } // handleInspect is the static capture handler. func handleInspect(w http.ResponseWriter, r *http.Request) { atomic.AddUint64(&requestsTotal, 1) cfg := getConfig() if !cfg.Enabled { atomic.AddUint64(&requestsBypassed, 1) w.WriteHeader(int(zoraxy_plugin.ControlStatusCode_UNHANDLED)) return } state := breaker.State() if state == StateOpen { // WAF is down — bypass directly to backend. atomic.AddUint64(&requestsBypassed, 1) log.Printf("Firewall: breaker OPEN — bypassing %s %s", r.Method, r.URL.Path) bypassToBackend(w, r) w.WriteHeader(int(zoraxy_plugin.ControlStatusCode_CAPTURED)) return } // Proxy through WAF. resp, err := proxyThroughWAF(w, r) if err != nil { atomic.AddUint64(&requestsBypassed, 1) log.Printf("Firewall: WAF unreachable — bypassing %s %s: %v", r.Method, r.URL.Path, err) breaker.RecordFailure() bypassToBackend(w, r) w.WriteHeader(int(zoraxy_plugin.ControlStatusCode_CAPTURED)) return } defer resp.Body.Close() breaker.RecordSuccess() if resp.StatusCode == 403 { atomic.AddUint64(&requestsBlocked, 1) log.Printf("Firewall: BLOCKED %s %s", r.Method, r.URL.Path) w.Header().Set("Content-Type", "text/html") w.WriteHeader(int(zoraxy_plugin.ControlStatusCode_CAPTURED)) io.Copy(w, resp.Body) return } // Pass-through: copy the WAF's response back. atomic.AddUint64(&requestsAllowed, 1) for key, vals := range resp.Header { for _, v := range vals { w.Header().Add(key, v) } } w.WriteHeader(resp.StatusCode) fmt.Fprintf(w, " ") // ensure WriteHeader is sent io.Copy(w, resp.Body) w.WriteHeader(int(zoraxy_plugin.ControlStatusCode_CAPTURED)) }