package imgbundler import ( "bytes" "context" "encoding/base64" "fmt" "io/ioutil" "mime" "net/http" "net/url" "os" "path" "regexp" "strings" "sync" "time" "golang.org/x/xerrors" "oss.terrastruct.com/xdefer" "oss.terrastruct.com/d2/lib/xmain" ) const maxImageSize int64 = 1 << 25 // 33_554_432 var imageRegex = regexp.MustCompile(` 0 { return svg, xerrors.Errorf("%v", errhrefs) } return svg, nil } svg = bytes.Replace(svg, repl.from, repl.to, 1) } } } // filterImageElements finds all image elements in imgs that are eligible // for bundling in the current context. func filterImageElements(imgs [][][]byte, isRemote bool) [][][]byte { imgs2 := imgs[:0] for _, img := range imgs { href := string(img[1]) // Skip already bundled images. if strings.HasPrefix(href, "data:") { continue } u, err := url.Parse(href) isRemoteImg := err == nil && strings.HasPrefix(u.Scheme, "http") if isRemoteImg == isRemote { imgs2 = append(imgs2, img) } } return imgs2 } var httpClient = &http.Client{} func httpGet(ctx context.Context, href string) ([]byte, string, error) { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() req, err := http.NewRequestWithContext(ctx, "GET", href, nil) if err != nil { return nil, "", err } resp, err := httpClient.Do(req) if err != nil { return nil, "", err } defer resp.Body.Close() if resp.StatusCode != 200 { return nil, "", fmt.Errorf("expected status 200 but got %d %s", resp.StatusCode, resp.Status) } r := http.MaxBytesReader(nil, resp.Body, maxImageSize) buf, err := ioutil.ReadAll(r) if err != nil { return nil, "", err } return buf, resp.Header.Get("Content-Type"), nil } // sniffMimeType sniffs the mime type of href based on its file extension and contents. func sniffMimeType(href, buf []byte, isRemote bool) string { p := string(href) if isRemote { u, err := url.Parse(p) if err != nil { p = "" } else { p = u.Path } } mimeType := mime.TypeByExtension(path.Ext(p)) if mimeType == "" { mimeType = http.DetectContentType(buf) } return mimeType }