refactor: modularize codebase — deduplicate, extract, clean up
- Unify duplicate uTLS transports into shared internal/transport package - Extract shared version constant into internal/version - Move LoadDefaultCredentials from config to auth (remove config→auth import) - Deduplicate handler.go: extract telemetry/error helpers (324→268 lines) - Break up main.go::run() into initCredential/initEmbedded - Eliminate logging.Config duplication (use config.LoggingConfig directly) - Extract logWriter to embedded/log.go, SSE fixtures to consts in sniff.go - Use uTLS client for usage polling (consistent TLS fingerprint) - Handle sjson.SetBytes errors in sanitize.go instead of silently swallowing - Document reverse-engineered magic values in billing.go - Unexport Credential.CooldownUntil (internal state) - Replace hardcoded auth bypass paths with map in server.go
This commit is contained in:
+92
-134
@@ -2,6 +2,7 @@ package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
@@ -18,6 +19,15 @@ import (
|
||||
"github.com/fujin/anthropic-proxy/internal/telemetry"
|
||||
)
|
||||
|
||||
// requestInfo bundles common request context passed to logging/telemetry helpers.
|
||||
type requestInfo struct {
|
||||
model string
|
||||
stream bool
|
||||
cred *auth.Credential
|
||||
body []byte
|
||||
originalBody []byte
|
||||
}
|
||||
|
||||
func HandleMessages(pool *auth.Pool, profile *SniffedProfile, getSanitizer func() *Sanitizer, tracker *ratelimit.Tracker) gin.HandlerFunc {
|
||||
upstream := NewUpstreamClient(profile)
|
||||
|
||||
@@ -61,6 +71,7 @@ func handleNonStream(c *gin.Context, upstream *UpstreamClient, san *Sanitizer, p
|
||||
startTime := time.Now()
|
||||
model := gjson.GetBytes(body, "model").String()
|
||||
ctx := c.Request.Context()
|
||||
ri := requestInfo{model: model, stream: false, cred: cred, body: body, originalBody: originalBody}
|
||||
|
||||
telemetry.RequestBodySize.Record(ctx, int64(len(body)),
|
||||
metric.WithAttributes(attribute.String("model", model), attribute.Bool("stream", false)))
|
||||
@@ -69,85 +80,25 @@ func handleNonStream(c *gin.Context, upstream *UpstreamClient, san *Sanitizer, p
|
||||
latencyMs := float64(time.Since(startTime).Milliseconds())
|
||||
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("credential", cred.Email).
|
||||
Str("model", model).
|
||||
Bool("stream", false).
|
||||
Str("request_body_original", string(originalBody)).
|
||||
Str("request_body_sanitized", string(body)).
|
||||
Int("request_body_size", len(body)).
|
||||
Float64("latency_ms", latencyMs).
|
||||
Msg("upstream connection error")
|
||||
|
||||
telemetry.UpstreamErrors.Add(ctx, 1,
|
||||
metric.WithAttributes(
|
||||
attribute.String("error_type", "connection"),
|
||||
attribute.String("credential", cred.Email),
|
||||
attribute.Int("status_code", http.StatusBadGateway),
|
||||
))
|
||||
telemetry.RequestCounter.Add(ctx, 1,
|
||||
metric.WithAttributes(
|
||||
attribute.String("model", model),
|
||||
attribute.Bool("stream", false),
|
||||
attribute.Int("status_code", http.StatusBadGateway),
|
||||
))
|
||||
telemetry.RequestDuration.Record(ctx, latencyMs,
|
||||
metric.WithAttributes(attribute.String("model", model), attribute.Bool("stream", false), attribute.Int("status_code", http.StatusBadGateway)))
|
||||
|
||||
recordConnectionError(ctx, err, ri, latencyMs)
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": "upstream request failed"})
|
||||
return
|
||||
}
|
||||
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("model", model),
|
||||
attribute.Bool("stream", false),
|
||||
attribute.Int("status_code", statusCode),
|
||||
}
|
||||
|
||||
telemetry.RequestCounter.Add(ctx, 1, metric.WithAttributes(attrs...))
|
||||
telemetry.RequestDuration.Record(ctx, latencyMs, metric.WithAttributes(attrs...))
|
||||
recordRequestMetrics(ctx, ri, statusCode, latencyMs)
|
||||
|
||||
if statusCode >= 400 {
|
||||
pool.MarkFailure(cred, statusCode)
|
||||
telemetry.CredentialCooldowns.Add(ctx, 1,
|
||||
metric.WithAttributes(attribute.Int("status_code", statusCode)))
|
||||
errorType := gjson.GetBytes(respBody, "error.type").String()
|
||||
errorMessage := gjson.GetBytes(respBody, "error.message").String()
|
||||
log.Error().
|
||||
Int("status", statusCode).
|
||||
Str("error_type", errorType).
|
||||
Str("error_message", errorMessage).
|
||||
Str("response_body", string(respBody)).
|
||||
Str("request_id", headers.Get("X-Request-Id")).
|
||||
Float64("latency_ms", latencyMs).
|
||||
Str("credential", cred.Email).
|
||||
Str("model", model).
|
||||
Bool("stream", false).
|
||||
Str("request_body_original", string(originalBody)).
|
||||
Str("request_body_sanitized", string(body)).
|
||||
Int("request_body_size", len(body)).
|
||||
Str("request_headers", logging.RedactHeaders(c.Request.Header)).
|
||||
Msg("upstream error")
|
||||
|
||||
telemetry.UpstreamErrors.Add(ctx, 1,
|
||||
metric.WithAttributes(
|
||||
attribute.Int("status_code", statusCode),
|
||||
attribute.String("error_type", errorType),
|
||||
attribute.String("credential", cred.Email),
|
||||
))
|
||||
recordUpstreamError(ctx, statusCode, respBody, headers.Get("X-Request-Id"), latencyMs, ri, c.Request.Header)
|
||||
} else {
|
||||
pool.MarkSuccess(cred)
|
||||
respBody = san.DesanitizeResponse(respBody)
|
||||
|
||||
inputTokens := gjson.GetBytes(respBody, "usage.input_tokens").Int()
|
||||
outputTokens := gjson.GetBytes(respBody, "usage.output_tokens").Int()
|
||||
tokenAttrs := metric.WithAttributes(
|
||||
attribute.String("model", model),
|
||||
attribute.String("credential", cred.Email),
|
||||
)
|
||||
telemetry.TokensInput.Add(ctx, inputTokens, tokenAttrs)
|
||||
telemetry.TokensOutput.Add(ctx, outputTokens, tokenAttrs)
|
||||
recordTokenUsage(ctx, model, cred, inputTokens, outputTokens)
|
||||
if tracker != nil {
|
||||
tracker.UpdateFromHeaders(headers)
|
||||
}
|
||||
@@ -174,6 +125,7 @@ func handleStream(c *gin.Context, upstream *UpstreamClient, san *Sanitizer, pool
|
||||
startTime := time.Now()
|
||||
model := gjson.GetBytes(body, "model").String()
|
||||
ctx := c.Request.Context()
|
||||
ri := requestInfo{model: model, stream: true, cred: cred, body: body, originalBody: originalBody}
|
||||
|
||||
telemetry.StreamRequests.Add(ctx, 1, metric.WithAttributes(attribute.String("model", model)))
|
||||
telemetry.RequestBodySize.Record(ctx, int64(len(body)),
|
||||
@@ -182,32 +134,7 @@ func handleStream(c *gin.Context, upstream *UpstreamClient, san *Sanitizer, pool
|
||||
resp, err := upstream.ExecuteStream(ctx, cred, body)
|
||||
if err != nil {
|
||||
latencyMs := float64(time.Since(startTime).Milliseconds())
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("credential", cred.Email).
|
||||
Str("model", model).
|
||||
Bool("stream", true).
|
||||
Str("request_body_original", string(originalBody)).
|
||||
Str("request_body_sanitized", string(body)).
|
||||
Int("request_body_size", len(body)).
|
||||
Float64("latency_ms", latencyMs).
|
||||
Msg("upstream connection error")
|
||||
|
||||
telemetry.UpstreamErrors.Add(ctx, 1,
|
||||
metric.WithAttributes(
|
||||
attribute.String("error_type", "connection"),
|
||||
attribute.String("credential", cred.Email),
|
||||
attribute.Int("status_code", http.StatusBadGateway),
|
||||
))
|
||||
telemetry.RequestCounter.Add(ctx, 1,
|
||||
metric.WithAttributes(
|
||||
attribute.String("model", model),
|
||||
attribute.Bool("stream", true),
|
||||
attribute.Int("status_code", http.StatusBadGateway),
|
||||
))
|
||||
telemetry.RequestDuration.Record(ctx, latencyMs,
|
||||
metric.WithAttributes(attribute.String("model", model), attribute.Bool("stream", true), attribute.Int("status_code", http.StatusBadGateway)))
|
||||
|
||||
recordConnectionError(ctx, err, ri, latencyMs)
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": "upstream stream request failed"})
|
||||
return
|
||||
}
|
||||
@@ -219,37 +146,8 @@ func handleStream(c *gin.Context, upstream *UpstreamClient, san *Sanitizer, pool
|
||||
metric.WithAttributes(attribute.Int("status_code", resp.StatusCode)))
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
latencyMs := float64(time.Since(startTime).Milliseconds())
|
||||
errorType := gjson.GetBytes(respBody, "error.type").String()
|
||||
errorMessage := gjson.GetBytes(respBody, "error.message").String()
|
||||
log.Error().
|
||||
Int("status", resp.StatusCode).
|
||||
Str("error_type", errorType).
|
||||
Str("error_message", errorMessage).
|
||||
Str("response_body", string(respBody)).
|
||||
Str("request_id", resp.Header.Get("X-Request-Id")).
|
||||
Float64("latency_ms", latencyMs).
|
||||
Str("credential", cred.Email).
|
||||
Str("model", model).
|
||||
Bool("stream", true).
|
||||
Str("request_body_original", string(originalBody)).
|
||||
Str("request_body_sanitized", string(body)).
|
||||
Int("request_body_size", len(body)).
|
||||
Str("request_headers", logging.RedactHeaders(c.Request.Header)).
|
||||
Msg("upstream error")
|
||||
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("model", model),
|
||||
attribute.Bool("stream", true),
|
||||
attribute.Int("status_code", resp.StatusCode),
|
||||
}
|
||||
telemetry.RequestCounter.Add(ctx, 1, metric.WithAttributes(attrs...))
|
||||
telemetry.RequestDuration.Record(ctx, latencyMs, metric.WithAttributes(attrs...))
|
||||
telemetry.UpstreamErrors.Add(ctx, 1,
|
||||
metric.WithAttributes(
|
||||
attribute.Int("status_code", resp.StatusCode),
|
||||
attribute.String("error_type", errorType),
|
||||
attribute.String("credential", cred.Email),
|
||||
))
|
||||
recordRequestMetrics(ctx, ri, resp.StatusCode, latencyMs)
|
||||
recordUpstreamError(ctx, resp.StatusCode, respBody, resp.Header.Get("X-Request-Id"), latencyMs, ri, c.Request.Header)
|
||||
|
||||
c.Data(resp.StatusCode, resp.Header.Get("Content-Type"), respBody)
|
||||
return
|
||||
@@ -290,21 +188,10 @@ func handleStream(c *gin.Context, upstream *UpstreamClient, san *Sanitizer, pool
|
||||
}
|
||||
|
||||
latencyMs := float64(time.Since(startTime).Milliseconds())
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("model", model),
|
||||
attribute.Bool("stream", true),
|
||||
attribute.Int("status_code", http.StatusOK),
|
||||
}
|
||||
telemetry.RequestCounter.Add(ctx, 1, metric.WithAttributes(attrs...))
|
||||
telemetry.RequestDuration.Record(ctx, latencyMs, metric.WithAttributes(attrs...))
|
||||
recordRequestMetrics(ctx, ri, http.StatusOK, latencyMs)
|
||||
|
||||
if inputTokens > 0 || outputTokens > 0 {
|
||||
tokenAttrs := metric.WithAttributes(
|
||||
attribute.String("model", model),
|
||||
attribute.String("credential", cred.Email),
|
||||
)
|
||||
telemetry.TokensInput.Add(ctx, inputTokens, tokenAttrs)
|
||||
telemetry.TokensOutput.Add(ctx, outputTokens, tokenAttrs)
|
||||
recordTokenUsage(ctx, model, cred, inputTokens, outputTokens)
|
||||
if tracker != nil {
|
||||
tracker.UpdateFromHeaders(resp.Header)
|
||||
}
|
||||
@@ -322,3 +209,74 @@ func handleStream(c *gin.Context, upstream *UpstreamClient, san *Sanitizer, pool
|
||||
log.Error().Err(err).Msg("stream scan error")
|
||||
}
|
||||
}
|
||||
|
||||
// recordConnectionError logs and records metrics for upstream connection failures.
|
||||
func recordConnectionError(ctx context.Context, err error, ri requestInfo, latencyMs float64) {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("credential", ri.cred.Email).
|
||||
Str("model", ri.model).
|
||||
Bool("stream", ri.stream).
|
||||
Str("request_body_original", string(ri.originalBody)).
|
||||
Str("request_body_sanitized", string(ri.body)).
|
||||
Int("request_body_size", len(ri.body)).
|
||||
Float64("latency_ms", latencyMs).
|
||||
Msg("upstream connection error")
|
||||
|
||||
telemetry.UpstreamErrors.Add(ctx, 1,
|
||||
metric.WithAttributes(
|
||||
attribute.String("error_type", "connection"),
|
||||
attribute.String("credential", ri.cred.Email),
|
||||
attribute.Int("status_code", http.StatusBadGateway),
|
||||
))
|
||||
recordRequestMetrics(ctx, ri, http.StatusBadGateway, latencyMs)
|
||||
}
|
||||
|
||||
// recordUpstreamError logs and records metrics for upstream HTTP error responses.
|
||||
func recordUpstreamError(ctx context.Context, statusCode int, respBody []byte, requestID string, latencyMs float64, ri requestInfo, requestHeaders http.Header) {
|
||||
errorType := gjson.GetBytes(respBody, "error.type").String()
|
||||
errorMessage := gjson.GetBytes(respBody, "error.message").String()
|
||||
log.Error().
|
||||
Int("status", statusCode).
|
||||
Str("error_type", errorType).
|
||||
Str("error_message", errorMessage).
|
||||
Str("response_body", string(respBody)).
|
||||
Str("request_id", requestID).
|
||||
Float64("latency_ms", latencyMs).
|
||||
Str("credential", ri.cred.Email).
|
||||
Str("model", ri.model).
|
||||
Bool("stream", ri.stream).
|
||||
Str("request_body_original", string(ri.originalBody)).
|
||||
Str("request_body_sanitized", string(ri.body)).
|
||||
Int("request_body_size", len(ri.body)).
|
||||
Str("request_headers", logging.RedactHeaders(requestHeaders)).
|
||||
Msg("upstream error")
|
||||
|
||||
telemetry.UpstreamErrors.Add(ctx, 1,
|
||||
metric.WithAttributes(
|
||||
attribute.Int("status_code", statusCode),
|
||||
attribute.String("error_type", errorType),
|
||||
attribute.String("credential", ri.cred.Email),
|
||||
))
|
||||
}
|
||||
|
||||
// recordRequestMetrics records the request counter and duration histogram.
|
||||
func recordRequestMetrics(ctx context.Context, ri requestInfo, statusCode int, latencyMs float64) {
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("model", ri.model),
|
||||
attribute.Bool("stream", ri.stream),
|
||||
attribute.Int("status_code", statusCode),
|
||||
}
|
||||
telemetry.RequestCounter.Add(ctx, 1, metric.WithAttributes(attrs...))
|
||||
telemetry.RequestDuration.Record(ctx, latencyMs, metric.WithAttributes(attrs...))
|
||||
}
|
||||
|
||||
// recordTokenUsage records token consumption metrics.
|
||||
func recordTokenUsage(ctx context.Context, model string, cred *auth.Credential, inputTokens, outputTokens int64) {
|
||||
tokenAttrs := metric.WithAttributes(
|
||||
attribute.String("model", model),
|
||||
attribute.String("credential", cred.Email),
|
||||
)
|
||||
telemetry.TokensInput.Add(ctx, inputTokens, tokenAttrs)
|
||||
telemetry.TokensOutput.Add(ctx, outputTokens, tokenAttrs)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user