package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "time" ) type Proxy struct { client *http.Client cfg Config metrics *Metrics } func NewProxy(cfg Config, metrics *Metrics) *Proxy { return &Proxy{ client: &http.Client{Timeout: cfg.RequestTimeout}, cfg: cfg, metrics: metrics, } } type pushResponse struct { Status string `json:"status"` } func (p *Proxy) Forward(w http.ResponseWriter, r *http.Request, backendURL, path string, body []byte, platform string) { start := time.Now() target := backendURL + path req, err := http.NewRequestWithContext(r.Context(), http.MethodPost, target, bytes.NewReader(body)) if err != nil { p.metrics.observeUpstream(platform, "error", time.Since(start)) writePushError(w, fmt.Sprintf("failed to create upstream request: %v", err)) return } req.Header.Set("Content-Type", "application/json") resp, err := p.client.Do(req) if err != nil { p.metrics.observeUpstream(platform, "error", time.Since(start)) writePushError(w, fmt.Sprintf("upstream request failed: %v", err)) return } defer func() { _ = resp.Body.Close() }() respBody, err := io.ReadAll(resp.Body) if err != nil { p.metrics.observeUpstream(platform, "error", time.Since(start)) writePushError(w, fmt.Sprintf("failed to read upstream response: %v", err)) return } p.metrics.observeUpstream(platform, upstreamOutcome(resp.StatusCode, respBody), time.Since(start)) for k, vals := range resp.Header { for _, v := range vals { w.Header().Add(k, v) } } if w.Header().Get("Content-Type") == "" { w.Header().Set("Content-Type", "application/json") } w.WriteHeader(resp.StatusCode) _, _ = w.Write(respBody) } func upstreamOutcome(statusCode int, body []byte) string { var resp pushResponse if err := json.Unmarshal(body, &resp); err == nil { switch resp.Status { case "OK": return "success" case "FAIL": return "fail" } } if statusCode >= 400 { return "fail" } return "success" } func writePushError(w http.ResponseWriter, message string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) payload, _ := json.Marshal(map[string]string{ "status": "FAIL", "error": message, }) _, _ = w.Write(payload) }