package favicon import ( "bufio" "bytes" "context" "errors" "io" "net" "net/http" "net/url" "path" "strings" "sync" "time" "github.com/PuerkitoBio/goquery" "github.com/vincent-petithory/dataurl" U "github.com/yusing/go-proxy/internal/api/v1/utils" "github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/homepage" "github.com/yusing/go-proxy/internal/logging" gphttp "github.com/yusing/go-proxy/internal/net/http" "github.com/yusing/go-proxy/internal/route/routes" route "github.com/yusing/go-proxy/internal/route/types" "github.com/yusing/go-proxy/internal/task" "github.com/yusing/go-proxy/internal/utils" ) type content struct { header http.Header data []byte status int } type fetchResult struct { icon []byte contentType string statusCode int errMsg string } func newContent() *content { return &content{ header: make(http.Header), } } func (c *content) Header() http.Header { return c.header } func (c *content) Write(data []byte) (int, error) { c.data = append(c.data, data...) return len(data), nil } func (c *content) WriteHeader(statusCode int) { c.status = statusCode } func (c *content) Hijack() (net.Conn, *bufio.ReadWriter, error) { return nil, nil, errors.New("not supported") } func (res *fetchResult) OK() bool { return res.icon != nil } func (res *fetchResult) ContentType() string { if res.contentType == "" { if bytes.HasPrefix(res.icon, []byte(" 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 { logging.Error().Err(err). Str("route", r.TargetName()). Msg("failed to decode favicon") return &fetchResult{statusCode: http.StatusInternalServerError, errMsg: "internal error"} } return &fetchResult{icon: dataURI.Data, contentType: dataURI.ContentType()} } switch { case strings.HasPrefix(href, "http://"), strings.HasPrefix(href, "https://"): return fetchIconAbsolute(href) default: return findIconSlow(r, req, path.Clean(href)) } }