fix(favicon): add HTTP redirect handling and improve format detection
- commit
- e7b783501c1a164bdd749a781b22d64a1d48b293
- author
- bamen · · 2025-09-13 04:36 UTC
- parent
- 5c553a4
- signature
- unsigned
1 file · +14 -10
M internal/cache/favicon.go +14 -10
| @@ -13,7 +13,6 @@ import ( | |||
| 13 | 13 | "net/http" | |
| 14 | 14 | "os" | |
| 15 | 15 | "path/filepath" | |
| 16 | - | "strings" | |
| 17 | 16 | "time" | |
| 18 | 17 | ||
| 19 | 18 | "github.com/bnema/dumber/internal/config" | |
| @@ -92,9 +91,15 @@ func (fc *FaviconCache) downloadFavicon(faviconURL string) error { | |||
| 92 | 91 | } | |
| 93 | 92 | } | |
| 94 | 93 | ||
| 95 | - | // Create HTTP client with timeout | |
| 94 | + | // Create HTTP client with timeout and redirect handling | |
| 96 | 95 | client := &http.Client{ | |
| 97 | 96 | Timeout: 10 * time.Second, | |
| 97 | + | CheckRedirect: func(req *http.Request, via []*http.Request) error { | |
| 98 | + | if len(via) >= 5 { | |
| 99 | + | return fmt.Errorf("too many redirects") | |
| 100 | + | } | |
| 101 | + | return nil | |
| 102 | + | }, | |
| 98 | 103 | } | |
| 99 | 104 | ||
| 100 | 105 | resp, err := client.Get(faviconURL) | |
| @@ -103,6 +108,11 @@ func (fc *FaviconCache) downloadFavicon(faviconURL string) error { | |||
| 103 | 108 | } | |
| 104 | 109 | defer resp.Body.Close() | |
| 105 | 110 | ||
| 111 | + | // Log redirects for debugging | |
| 112 | + | if resp.Request.URL.String() != faviconURL { | |
| 113 | + | log.Printf("[favicon] Redirected from %s to %s", faviconURL, resp.Request.URL.String()) | |
| 114 | + | } | |
| 115 | + | ||
| 106 | 116 | if resp.StatusCode != http.StatusOK { | |
| 107 | 117 | return fmt.Errorf("failed to download favicon: status %d", resp.StatusCode) | |
| 108 | 118 | } | |
| @@ -135,13 +145,7 @@ func (fc *FaviconCache) downloadFavicon(faviconURL string) error { | |||
| 135 | 145 | ||
| 136 | 146 | // convertToPNG converts a favicon file to PNG format | |
| 137 | 147 | func (fc *FaviconCache) convertToPNG(inputPath, outputPath, faviconURL string) error { | |
| 138 | - | // Check if it's an ICO file based on URL or content type | |
| 139 | - | if strings.Contains(strings.ToLower(faviconURL), ".ico") { | |
| 140 | - | log.Printf("[favicon] ICO file detected for %s, using go-ico decoder", faviconURL) | |
| 141 | - | return fc.convertICOWithGoICO(inputPath, outputPath, faviconURL) | |
| 142 | - | } | |
| 143 | - | ||
| 144 | - | // Try standard Go image decoder for other formats (PNG, JPEG, GIF) | |
| 148 | + | // Try standard Go image decoder first for all formats (PNG, JPEG, GIF) | |
| 145 | 149 | if img, err := fc.tryStandardImageDecode(inputPath); err == nil { | |
| 146 | 150 | log.Printf("[favicon] Successfully decoded %s with standard library", faviconURL) | |
| 147 | 151 | ||
| @@ -152,7 +156,7 @@ func (fc *FaviconCache) convertToPNG(inputPath, outputPath, faviconURL string) e | |||
| 152 | 156 | return fc.savePNG(img, outputPath) | |
| 153 | 157 | } | |
| 154 | 158 | ||
| 155 | - | // Standard decode failed, fallback to go-ico | |
| 159 | + | // Standard decode failed, try go-ico decoder for ICO files | |
| 156 | 160 | log.Printf("[favicon] Standard decode failed for %s, trying go-ico decoder", faviconURL) | |
| 157 | 161 | return fc.convertICOWithGoICO(inputPath, outputPath, faviconURL) | |
| 158 | 162 | } | |
| @@ -13,7 +13,6 @@ import ( | |||||
| 13 | "net/http" | 13 | "net/http" | ||
| 14 | "os" | 14 | "os" | ||
| 15 | "path/filepath" | 15 | "path/filepath" | ||
| 16 | - | "strings" | |||
| 17 | "time" | 16 | "time" | ||
| 18 | 17 | ||||
| 19 | "github.com/bnema/dumber/internal/config" | 18 | "github.com/bnema/dumber/internal/config" | ||
| @@ -92,9 +91,15 @@ func (fc *FaviconCache) downloadFavicon(faviconURL string) error { | |||||
| 92 | } | 91 | } | ||
| 93 | } | 92 | } | ||
| 94 | 93 | ||||
| 95 | - | // Create HTTP client with timeout | 94 | + | // Create HTTP client with timeout and redirect handling |
| 96 | client := &http.Client{ | 95 | client := &http.Client{ | ||
| 97 | Timeout: 10 * time.Second, | 96 | Timeout: 10 * time.Second, | ||
| 97 | + | CheckRedirect: func(req *http.Request, via []*http.Request) error { | |||
| 98 | + | if len(via) >= 5 { | |||
| 99 | + | return fmt.Errorf("too many redirects") | |||
| 100 | + | } | |||
| 101 | + | return nil | |||
| 102 | + | }, | |||
| 98 | } | 103 | } | ||
| 99 | 104 | ||||
| 100 | resp, err := client.Get(faviconURL) | 105 | resp, err := client.Get(faviconURL) | ||
| @@ -103,6 +108,11 @@ func (fc *FaviconCache) downloadFavicon(faviconURL string) error { | |||||
| 103 | } | 108 | } | ||
| 104 | defer resp.Body.Close() | 109 | defer resp.Body.Close() | ||
| 105 | 110 | ||||
| 111 | + | // Log redirects for debugging | |||
| 112 | + | if resp.Request.URL.String() != faviconURL { | |||
| 113 | + | log.Printf("[favicon] Redirected from %s to %s", faviconURL, resp.Request.URL.String()) | |||
| 114 | + | } | |||
| 115 | + | ||||
| 106 | if resp.StatusCode != http.StatusOK { | 116 | if resp.StatusCode != http.StatusOK { | ||
| 107 | return fmt.Errorf("failed to download favicon: status %d", resp.StatusCode) | 117 | return fmt.Errorf("failed to download favicon: status %d", resp.StatusCode) | ||
| 108 | } | 118 | } | ||
| @@ -135,13 +145,7 @@ func (fc *FaviconCache) downloadFavicon(faviconURL string) error { | |||||
| 135 | 145 | ||||
| 136 | // convertToPNG converts a favicon file to PNG format | 146 | // convertToPNG converts a favicon file to PNG format | ||
| 137 | func (fc *FaviconCache) convertToPNG(inputPath, outputPath, faviconURL string) error { | 147 | func (fc *FaviconCache) convertToPNG(inputPath, outputPath, faviconURL string) error { | ||
| 138 | - | // Check if it's an ICO file based on URL or content type | 148 | + | // Try standard Go image decoder first for all formats (PNG, JPEG, GIF) |
| 139 | - | if strings.Contains(strings.ToLower(faviconURL), ".ico") { | |||
| 140 | - | log.Printf("[favicon] ICO file detected for %s, using go-ico decoder", faviconURL) | |||
| 141 | - | return fc.convertICOWithGoICO(inputPath, outputPath, faviconURL) | |||
| 142 | - | } | |||
| 143 | - | ||||
| 144 | - | // Try standard Go image decoder for other formats (PNG, JPEG, GIF) | |||
| 145 | if img, err := fc.tryStandardImageDecode(inputPath); err == nil { | 149 | if img, err := fc.tryStandardImageDecode(inputPath); err == nil { | ||
| 146 | log.Printf("[favicon] Successfully decoded %s with standard library", faviconURL) | 150 | log.Printf("[favicon] Successfully decoded %s with standard library", faviconURL) | ||
| 147 | 151 | ||||
| @@ -152,7 +156,7 @@ func (fc *FaviconCache) convertToPNG(inputPath, outputPath, faviconURL string) e | |||||
| 152 | return fc.savePNG(img, outputPath) | 156 | return fc.savePNG(img, outputPath) | ||
| 153 | } | 157 | } | ||
| 154 | 158 | ||||
| 155 | - | // Standard decode failed, fallback to go-ico | 159 | + | // Standard decode failed, try go-ico decoder for ICO files |
| 156 | log.Printf("[favicon] Standard decode failed for %s, trying go-ico decoder", faviconURL) | 160 | log.Printf("[favicon] Standard decode failed for %s, trying go-ico decoder", faviconURL) | ||
| 157 | return fc.convertICOWithGoICO(inputPath, outputPath, faviconURL) | 161 | return fc.convertICOWithGoICO(inputPath, outputPath, faviconURL) | ||
| 158 | } | 162 | } | ||