| @@ -10,6 +10,19 @@ import ( |
| 10 | "time" | 10 | "time" |
| 11 | ) | 11 | ) |
| 12 | | 12 | |
| | 13 | +type imageContentTypePrefixes []string |
| | 14 | + |
| | 15 | +func (p imageContentTypePrefixes) matches(contentType string) bool { |
| | 16 | + for _, prefix := range p { |
| | 17 | + if strings.HasPrefix(contentType, prefix) { |
| | 18 | + return true |
| | 19 | + } |
| | 20 | + } |
| | 21 | + return false |
| | 22 | +} |
| | 23 | + |
| | 24 | +var imageContentTypes = imageContentTypePrefixes{"image/"} |
| | 25 | + |
| 13 | type DiscoveryResult struct { | 26 | type DiscoveryResult struct { |
| 14 | FeedURLs []string | 27 | FeedURLs []string |
| 15 | Favicon string | 28 | Favicon string |
| @@ -92,7 +105,9 @@ func findFavicon(ctx context.Context, base *url.URL, links []string) string { |
| 92 | continue | 105 | continue |
| 93 | } | 106 | } |
| 94 | if u, err := base.Parse(href); err == nil { | 107 | if u, err := base.Parse(href); err == nil { |
| 95 | - return cleanFavicon(u.String()) | 108 | + if checkContentType(ctx, u.String()) { |
| | 109 | + return cleanFavicon(u.String()) |
| | 110 | + } |
| 96 | } | 111 | } |
| 97 | } | 112 | } |
| 98 | | 113 | |
| @@ -104,7 +119,7 @@ func findFavicon(ctx context.Context, base *url.URL, links []string) string { |
| 104 | for _, path := range faviconPaths { | 119 | for _, path := range faviconPaths { |
| 105 | u, _ := url.Parse(path) | 120 | u, _ := url.Parse(path) |
| 106 | resolved := origin.ResolveReference(u) | 121 | resolved := origin.ResolveReference(u) |
| 107 | - req, err := http.NewRequestWithContext(ctx, http.MethodHead, resolved.String(), nil) | 122 | + req, err := http.NewRequestWithContext(ctx, http.MethodGet, resolved.String(), nil) |
| 108 | if err != nil { | 123 | if err != nil { |
| 109 | continue | 124 | continue |
| 110 | } | 125 | } |
| @@ -113,13 +128,26 @@ func findFavicon(ctx context.Context, base *url.URL, links []string) string { |
| 113 | continue | 128 | continue |
| 114 | } | 129 | } |
| 115 | resp.Body.Close() | 130 | resp.Body.Close() |
| 116 | - if resp.StatusCode == http.StatusOK { | 131 | + if resp.StatusCode == http.StatusOK && imageContentTypes.matches(resp.Header.Get("Content-Type")) { |
| 117 | return cleanFavicon(resolved.String()) | 132 | return cleanFavicon(resolved.String()) |
| 118 | } | 133 | } |
| 119 | } | 134 | } |
| 120 | return "" | 135 | return "" |
| 121 | } | 136 | } |
| 122 | | 137 | |
| | 138 | +func checkContentType(ctx context.Context, url string) bool { |
| | 139 | + req, err := http.NewRequestWithContext(ctx, http.MethodHead, url, nil) |
| | 140 | + if err != nil { |
| | 141 | + return false |
| | 142 | + } |
| | 143 | + resp, err := discoverClient.Do(req) |
| | 144 | + if err != nil { |
| | 145 | + return false |
| | 146 | + } |
| | 147 | + resp.Body.Close() |
| | 148 | + return resp.StatusCode == http.StatusOK && imageContentTypes.matches(resp.Header.Get("Content-Type")) |
| | 149 | +} |
| | 150 | + |
| 123 | func extractHref(link string) string { | 151 | func extractHref(link string) string { |
| 124 | m := hrefRe.FindStringSubmatch(link) | 152 | m := hrefRe.FindStringSubmatch(link) |
| 125 | if len(m) >= 2 { | 153 | if len(m) >= 2 { |
| @@ -152,4 +180,4 @@ func fetchHTML(ctx context.Context, siteURL string) (*url.URL, string) { |
| 152 | | 180 | |
| 153 | base := resp.Request.URL | 181 | base := resp.Request.URL |
| 154 | return base, string(body) | 182 | return base, string(body) |
| 155 | -} | 183 | +} |
| \ No newline at end of file | | \ No newline at end of file |