## Phase 8: Enterprise Extractor Improvements ✅ - 14 security extractors (TLS, JWT, SQL injection, XSS, etc.) - 10 framework-specific extractors (Spring, Django, Rails, etc.) - Config file security detection (YAML, TOML) ## Phase 9: Autonomous Extractor Generation ✅ - Shadow mode executor with TP/FP tracking - Graduation pipeline with confidence thresholds - Auto-rollback on regression detection - Cross-project pattern syncing ## UAT Suite Complete (14 scripts, 90 tests) - test-core-detection.sh (6 tests) - test-declarative-extractors.sh (5 tests) - test-domain-frameworks.sh (5 tests) - test-domain-unreal.sh (3 tests) - test-llm-extraction.sh (6 tests) - test-eval-harness.sh (5 tests) - test-cross-language.sh (3 tests) - test-precommit-performance.sh (4 tests) - test-output-formats.sh (8 tests) - test-drift-detection.sh (6 tests) - test-exit-codes.sh (12 tests) + 3 more scripts ## Other Changes - Updated roadmap to mark Phase 8-9 complete - Added .gitignore entries for build artifacts - Updated pre-commit: 800 line limit, exclude tests/data/cmd Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
92 lines
2.6 KiB
Go
92 lines
2.6 KiB
Go
// Package httpkit provides a shared HTTP client with retry, timeout, and error
|
|
// classification for outbound API calls.
|
|
//
|
|
// All outbound HTTP in this codebase should use httpkit.Client instead of
|
|
// raw http.Client or http.DefaultClient. This ensures consistent timeout,
|
|
// retry, body lifecycle, and error classification.
|
|
//
|
|
// Basic usage:
|
|
//
|
|
// client := httpkit.NewClient(httpkit.Config{
|
|
// Timeout: 30 * time.Second,
|
|
// MaxRetries: 3,
|
|
// Headers: map[string]string{"Authorization": "Bearer " + apiKey},
|
|
// })
|
|
//
|
|
// body, err := client.Do(ctx, httpkit.Request{
|
|
// Method: http.MethodPost,
|
|
// URL: baseURL + "/chat/completions",
|
|
// Body: chatReq,
|
|
// })
|
|
// if errors.Is(err, httpkit.ErrRateLimit) { ... }
|
|
package httpkit
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
)
|
|
|
|
// Sentinel errors classify HTTP failures into retryable categories.
|
|
// Use errors.Is to check against these from any wrapped error.
|
|
var (
|
|
ErrTimeout = errors.New("httpkit: timeout")
|
|
ErrRateLimit = errors.New("httpkit: rate limited")
|
|
ErrServerError = errors.New("httpkit: server error")
|
|
ErrUnauthorized = errors.New("httpkit: unauthorized")
|
|
ErrForbidden = errors.New("httpkit: forbidden")
|
|
ErrBadRequest = errors.New("httpkit: bad request")
|
|
ErrNotFound = errors.New("httpkit: not found")
|
|
)
|
|
|
|
// APIError represents a non-2xx HTTP response.
|
|
// Unwrap returns the classified sentinel error for use with errors.Is.
|
|
type APIError struct {
|
|
StatusCode int
|
|
Body string
|
|
sentinel error
|
|
}
|
|
|
|
func (e *APIError) Error() string {
|
|
body := e.Body
|
|
if len(body) > 200 {
|
|
body = body[:200] + "..."
|
|
}
|
|
return fmt.Sprintf("HTTP %d: %s", e.StatusCode, body)
|
|
}
|
|
|
|
// Unwrap returns the classified sentinel error, enabling errors.Is checks
|
|
// like errors.Is(err, httpkit.ErrRateLimit).
|
|
func (e *APIError) Unwrap() error {
|
|
return e.sentinel
|
|
}
|
|
|
|
// ClassifyStatus maps an HTTP status code to a sentinel error.
|
|
// Returns nil for unrecognized or successful status codes.
|
|
func ClassifyStatus(code int) error {
|
|
switch {
|
|
case code == http.StatusUnauthorized:
|
|
return ErrUnauthorized
|
|
case code == http.StatusForbidden:
|
|
return ErrForbidden
|
|
case code == http.StatusNotFound:
|
|
return ErrNotFound
|
|
case code == http.StatusTooManyRequests:
|
|
return ErrRateLimit
|
|
case code >= 400 && code < 500:
|
|
return ErrBadRequest
|
|
case code >= 500:
|
|
return ErrServerError
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// IsRetryable reports whether err indicates a condition that may succeed on retry.
|
|
// Retryable conditions: rate limits (429), server errors (5xx), and timeouts.
|
|
func IsRetryable(err error) bool {
|
|
return errors.Is(err, ErrRateLimit) ||
|
|
errors.Is(err, ErrServerError) ||
|
|
errors.Is(err, ErrTimeout)
|
|
}
|