78 lines
2 KiB
Go
78 lines
2 KiB
Go
package utils
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
debugDir = "failed_requests"
|
|
maxSaveBody = 5 * 1024 * 1024 // 5 MiB, configurable
|
|
filePerm = 0640
|
|
dirPerm = 0750
|
|
timestampFormat = "20060102T150405Z" // safe for filenames
|
|
)
|
|
|
|
// helper: generate a small random hex id
|
|
func randomHex(n int) string {
|
|
b := make([]byte, n)
|
|
_, _ = rand.Read(b)
|
|
return hex.EncodeToString(b)
|
|
}
|
|
|
|
// save a failing request atomically with metadata
|
|
func saveFailedRequest(prefix, url string, reqHeaders http.Header, respHeaders http.Header, status int, body []byte, errMsg string) (string, error) {
|
|
if err := os.MkdirAll(debugDir, dirPerm); err != nil {
|
|
return "", fmt.Errorf("mkdir debug dir: %w", err)
|
|
}
|
|
|
|
ts := time.Now().UTC().Format(timestampFormat)
|
|
id := randomHex(6)
|
|
base := fmt.Sprintf("%s_%s_%s", prefix, ts, id)
|
|
|
|
// Truncate body if too large
|
|
truncated := false
|
|
if len(body) > maxSaveBody {
|
|
body = body[:maxSaveBody]
|
|
truncated = true
|
|
}
|
|
|
|
meta := map[string]interface{}{
|
|
"timestamp_utc": ts,
|
|
"url": url,
|
|
"status": status,
|
|
"error": errMsg,
|
|
"truncated": truncated,
|
|
"req_headers": reqHeaders,
|
|
"resp_headers": respHeaders,
|
|
}
|
|
|
|
metaBytes, _ := json.MarshalIndent(meta, "", " ")
|
|
|
|
metaTmp := filepath.Join(debugDir, base+".meta.json.tmp")
|
|
bodyTmp := filepath.Join(debugDir, base+".body.tmp")
|
|
metaPath := filepath.Join(debugDir, base+".meta.json")
|
|
bodyPath := filepath.Join(debugDir, base+".body")
|
|
|
|
// write temp + rename (atomic on POSIX filesystems)
|
|
if err := os.WriteFile(metaTmp, metaBytes, filePerm); err != nil {
|
|
return "", fmt.Errorf("write meta tmp: %w", err)
|
|
}
|
|
if err := os.Rename(metaTmp, metaPath); err != nil {
|
|
return "", fmt.Errorf("rename meta: %w", err)
|
|
}
|
|
if err := os.WriteFile(bodyTmp, body, filePerm); err != nil {
|
|
return "", fmt.Errorf("write body tmp: %w", err)
|
|
}
|
|
if err := os.Rename(bodyTmp, bodyPath); err != nil {
|
|
return "", fmt.Errorf("rename body: %w", err)
|
|
}
|
|
|
|
return base, nil
|
|
}
|