package service import ( "strings" "unicode/utf8" "github.com/gin-gonic/gin" ) // maxPersistedSessionIDLength bounds the persisted client session identifier to the // usage_logs.session_id column width (VARCHAR(255)). Longer values are rejected so // distinct identifiers can never alias through truncation. const maxPersistedSessionIDLength = 255 // clientSessionIDHeaders extends the OpenAI-compatible sticky-session signals with // native protocol identifiers that are safe to persist but must not alter OpenAI // scheduling behavior. var clientSessionIDHeaders = append( append([]string(nil), explicitOpenAIHeaderSessionNames...), claudeCodeSessionHeader, ) // ExtractClientSessionID resolves the explicit client-provided session identifier from // request headers for usage-log correlation and returns it sanitized. It is // protocol-agnostic and shared by every gateway handler so all supported protocols // record session_id through one seam. Returns "" when no valid identifier is present. // // This value feeds only usage_logs.session_id persistence. It does NOT affect sticky // routing, account selection, request_id semantics, or upstream prompt caching, which // keep their own (intentionally broader) session-signal resolution. func ExtractClientSessionID(c *gin.Context) string { if c == nil || c.Request == nil { return "" } for _, header := range clientSessionIDHeaders { if sessionID := sanitizeSessionID(c.GetHeader(header)); sessionID != "" { return sessionID } } if isGrokRequestContext(c) { if sessionID := sanitizeSessionID(c.GetHeader(grokConversationIDHeader)); sessionID != "" { return sessionID } } return "" } // sanitizeSessionID normalizes a raw client-supplied session identifier for safe // persistence: it trims surrounding whitespace, rejects the value outright if it // contains any control character (CR/LF/tab/NUL/…) so a log- or header-injection style // payload cannot slip into stored correlation data, and rejects values longer than // the DB column bound. Absent or invalid input yields "". func sanitizeSessionID(raw string) string { if !utf8.ValidString(raw) { return "" } trimmed := strings.TrimSpace(raw) if trimmed == "" { return "" } count := 0 for _, r := range trimmed { if r < 0x20 || r == 0x7f { // An explicit correlation id never legitimately contains control // characters; drop the whole value rather than persist a mangled or // partially-injected identifier. return "" } count++ if count > maxPersistedSessionIDLength { return "" } } return trimmed }