Skip to content

Commit

Permalink
Prevent DataURI minification to ASCII/base64 if they result in longer…
Browse files Browse the repository at this point in the history
… data URIs, fixes #282
  • Loading branch information
tdewolff committed Jan 7, 2020
1 parent 63a86b5 commit 315e0a7
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 30 deletions.
61 changes: 33 additions & 28 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,44 @@ func Mediatype(b []byte) []byte {

// DataURI minifies a data URI and calls a minifier by the specified mediatype. Specifications: https://www.ietf.org/rfc/rfc2397.txt.
func DataURI(m *M, dataURI []byte) []byte {
if mediatype, data, err := parse.DataURI(dataURI); err == nil {
dataURI, _ = m.Bytes(string(mediatype), data)
base64Len := len(";base64") + base64.StdEncoding.EncodedLen(len(dataURI))
asciiLen := len(dataURI)
for _, c := range dataURI {
if parse.URLEncodingTable[c] {
asciiLen += 2
}
if asciiLen > base64Len {
break
}
mediatype, data, err := parse.DataURI(dataURI)
if err != nil {
return dataURI
}

data, _ = m.Bytes(string(mediatype), data)
base64Len := len(";base64") + base64.StdEncoding.EncodedLen(len(data))
asciiLen := len(data)
for _, c := range data {
if parse.URLEncodingTable[c] {
asciiLen += 2
}
if asciiLen > base64Len {
encoded := make([]byte, base64Len-len(";base64"))
base64.StdEncoding.Encode(encoded, dataURI)
dataURI = encoded
mediatype = append(mediatype, []byte(";base64")...)
} else {
dataURI = parse.EncodeURL(dataURI, parse.URLEncodingTable)
}
if len("text/plain") <= len(mediatype) && parse.EqualFold(mediatype[:len("text/plain")], []byte("text/plain")) {
mediatype = mediatype[len("text/plain"):]
break
}
for i := 0; i+len(";charset=us-ascii") <= len(mediatype); i++ {
// must start with semicolon and be followed by end of mediatype or semicolon
if mediatype[i] == ';' && parse.EqualFold(mediatype[i+1:i+len(";charset=us-ascii")], []byte("charset=us-ascii")) && (i+len(";charset=us-ascii") >= len(mediatype) || mediatype[i+len(";charset=us-ascii")] == ';') {
mediatype = append(mediatype[:i], mediatype[i+len(";charset=us-ascii"):]...)
break
}
}
if len(dataURI) < base64Len && len(dataURI) < asciiLen {
return dataURI
}
if base64Len < asciiLen {
encoded := make([]byte, base64Len-len(";base64"))
base64.StdEncoding.Encode(encoded, data)
data = encoded
mediatype = append(mediatype, []byte(";base64")...)
} else {
data = parse.EncodeURL(data, parse.URLEncodingTable)
}
if len("text/plain") <= len(mediatype) && parse.EqualFold(mediatype[:len("text/plain")], []byte("text/plain")) {
mediatype = mediatype[len("text/plain"):]
}
for i := 0; i+len(";charset=us-ascii") <= len(mediatype); i++ {
// must start with semicolon and be followed by end of mediatype or semicolon
if mediatype[i] == ';' && parse.EqualFold(mediatype[i+1:i+len(";charset=us-ascii")], []byte("charset=us-ascii")) && (i+len(";charset=us-ascii") >= len(mediatype) || mediatype[i+len(";charset=us-ascii")] == ';') {
mediatype = append(mediatype[:i], mediatype[i+len(";charset=us-ascii"):]...)
break
}
dataURI = append(append(append([]byte("data:"), mediatype...), ','), dataURI...)
}
return dataURI
return append(append(append([]byte("data:"), mediatype...), ','), data...)
}

const MaxInt = int(^uint(0) >> 1)
Expand Down
5 changes: 3 additions & 2 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ func TestDataURI(t *testing.T) {
{"data:text/svg+xml;base64,PT09PT09", "data:text/svg+xml;base64,PT09PT09"},
{"data:text/xml;version=2.0,content", "data:text/xml;version=2.0,content"},
{"data:text/xml; version = 2.0,content", "data:text/xml;version=2.0,content"},
{"data:,=====", "data:,%3D%3D%3D%3D%3D"},
{"data:,======", "data:;base64,PT09PT09"},
{"data:,%3D%3D%3D%3D%3D", "data:,%3D%3D%3D%3D%3D"},
{"data:,%3D%3D%3D%3D%3D%3D", "data:;base64,PT09PT09"},
{"data:text/x,<?xx?>", "data:text/x,%3C%3Fxx%3F%3E"},
{"data:text/other,\"<\u2318", "data:text/other,%22%3C%E2%8C%98"},
{"data:text/other,\"<\u2318>", "data:text/other;base64,IjzijJg+"},
{`data:text/svg+xml,<svg height="100" width="100"><circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /></svg>`, `data:text/svg+xml,<svg height="100" width="100"><circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /></svg>`},
}
m := New()
m.AddFunc("text/x", func(_ *M, w io.Writer, r io.Reader, _ map[string]string) error {
Expand Down

0 comments on commit 315e0a7

Please sign in to comment.