Chain creates an Extractor that tries multiple extractors in order until one succeeds. This implements a fallback pattern where multiple extraction sources are attempted in sequence. The function: - Tries each extractor in the order provided - Returns the first successful extraction (non-empty valu
(extractors ...Extractor)
| 500 | // Order extractors by security preference. Most secure sources (headers, cookies) |
| 501 | // should be attempted before less secure ones (query params, form data). |
| 502 | func Chain(extractors ...Extractor) Extractor { |
| 503 | if len(extractors) == 0 { |
| 504 | return Extractor{ |
| 505 | Extract: func(fiber.Ctx) (string, error) { |
| 506 | return "", ErrNotFound |
| 507 | }, |
| 508 | Source: SourceCustom, |
| 509 | Key: "", |
| 510 | Chain: []Extractor{}, |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | // Use the source and key from the first extractor as the primary |
| 515 | primarySource := extractors[0].Source |
| 516 | primaryKey := extractors[0].Key |
| 517 | guardKey := chainGuardKey{id: new(byte)} |
| 518 | |
| 519 | return Extractor{ |
| 520 | Extract: func(c fiber.Ctx) (string, error) { |
| 521 | if active, ok := c.Locals(guardKey).(bool); ok && active { |
| 522 | return "", ErrChainCycle |
| 523 | } |
| 524 | |
| 525 | c.Locals(guardKey, true) |
| 526 | defer c.Locals(guardKey, false) |
| 527 | |
| 528 | var lastErr error // last error encountered (including ErrNotFound) |
| 529 | |
| 530 | for _, extractor := range extractors { |
| 531 | if extractor.Extract == nil { |
| 532 | continue |
| 533 | } |
| 534 | v, err := extractor.Extract(c) |
| 535 | if err == nil && v != "" { |
| 536 | return v, nil |
| 537 | } |
| 538 | if err != nil { |
| 539 | lastErr = err |
| 540 | } |
| 541 | } |
| 542 | if lastErr != nil { |
| 543 | return "", lastErr |
| 544 | } |
| 545 | return "", ErrNotFound |
| 546 | }, |
| 547 | Source: primarySource, |
| 548 | Key: primaryKey, |
| 549 | Chain: append([]Extractor(nil), extractors...), // Defensive copy for introspection |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | // isValidToken68 checks if a string is a valid token68 per RFC 7235/9110. |
| 554 | func isValidToken68(token string) bool { |