detached e7b7835

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 (
1313 "net/http"
1414 "os"
1515 "path/filepath"
16- "strings"
1716 "time"
1817
1918 "github.com/bnema/dumber/internal/config"
@@ -92,9 +91,15 @@ func (fc *FaviconCache) downloadFavicon(faviconURL string) error {
9291 }
9392 }
9493
95- // Create HTTP client with timeout
94+ // Create HTTP client with timeout and redirect handling
9695 client := &http.Client{
9796 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+ },
98103 }
99104
100105 resp, err := client.Get(faviconURL)
@@ -103,6 +108,11 @@ func (fc *FaviconCache) downloadFavicon(faviconURL string) error {
103108 }
104109 defer resp.Body.Close()
105110
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+
106116 if resp.StatusCode != http.StatusOK {
107117 return fmt.Errorf("failed to download favicon: status %d", resp.StatusCode)
108118 }
@@ -135,13 +145,7 @@ func (fc *FaviconCache) downloadFavicon(faviconURL string) error {
135145
136146 // convertToPNG converts a favicon file to PNG format
137147 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)
145149 if img, err := fc.tryStandardImageDecode(inputPath); err == nil {
146150 log.Printf("[favicon] Successfully decoded %s with standard library", faviconURL)
147151
@@ -152,7 +156,7 @@ func (fc *FaviconCache) convertToPNG(inputPath, outputPath, faviconURL string) e
152156 return fc.savePNG(img, outputPath)
153157 }
154158
155- // Standard decode failed, fallback to go-ico
159+ // Standard decode failed, try go-ico decoder for ICO files
156160 log.Printf("[favicon] Standard decode failed for %s, trying go-ico decoder", faviconURL)
157161 return fc.convertICOWithGoICO(inputPath, outputPath, faviconURL)
158162 }
@@ -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 timeout94+ // 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 format146 // 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 type148+ // 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-ico159+ // 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 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close