package homepage import ( "bytes" "context" "errors" "io" "net/http" "net/url" "strings" "time" "github.com/PuerkitoBio/goquery" "github.com/vincent-petithory/dataurl" gphttp "github.com/yusing/go-proxy/internal/net/gphttp" "github.com/yusing/go-proxy/internal/utils/strutils" ) type FetchResult struct { Icon []byte StatusCode int ErrMsg string contentType string } func (res *FetchResult) OK() bool { return res.Icon != nil } func (res *FetchResult) ContentType() string { if res.contentType == "" { if bytes.HasPrefix(res.Icon, []byte(" maxRedirectDepth { return &FetchResult{StatusCode: http.StatusBadGateway, ErrMsg: "too many redirects"} } loc = strutils.SanitizeURI(loc) if loc == "/" || loc == newReq.URL.Path { return &FetchResult{StatusCode: http.StatusBadGateway, ErrMsg: "circular redirect"} } return findIconSlow(ctx, r, loc, depth+1) } } return &FetchResult{StatusCode: c.status, ErrMsg: "upstream error: " + string(c.data)} } // return icon data if !gphttp.GetContentType(c.header).IsHTML() { return &FetchResult{Icon: c.data, contentType: c.header.Get("Content-Type")} } // try extract from "link[rel=icon]" from path "/" doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(c.data)) if err != nil { return &FetchResult{StatusCode: http.StatusInternalServerError, ErrMsg: "failed to parse html"} } ele := doc.Find("head > link[rel=icon]").First() if ele.Length() == 0 { return &FetchResult{StatusCode: http.StatusNotFound, ErrMsg: "icon element not found"} } href := ele.AttrOr("href", "") if href == "" { return &FetchResult{StatusCode: http.StatusNotFound, ErrMsg: "icon href not found"} } // https://en.wikipedia.org/wiki/Data_URI_scheme if strings.HasPrefix(href, "data:image/") { dataURI, err := dataurl.DecodeString(href) if err != nil { return &FetchResult{StatusCode: http.StatusInternalServerError, ErrMsg: "failed to decode favicon"} } return &FetchResult{Icon: dataURI.Data, contentType: dataURI.ContentType()} } switch { case strings.HasPrefix(href, "http://"), strings.HasPrefix(href, "https://"): return fetchIconAbsolute(href) default: return findIconSlow(ctx, r, href, 0) } }