iOS 14 support, bump to 0.3.1 (#63)

Use `accept` header first, then `user-agent` header.
Add complete test case.

This should resolve part of #61
This commit is contained in:
Benny 2020-12-05 16:42:18 +08:00 committed by GitHub
parent 9136beb307
commit 37d8d8ba13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 90 additions and 12 deletions

View File

@ -14,7 +14,8 @@ It will convert `jpg,jpeg,png` files by default, this can be customized by editi
> e.g When you visit `https://your.website/pics/tsuki.jpg`it will serve as `image/webp` format without changing the URL. > e.g When you visit `https://your.website/pics/tsuki.jpg`it will serve as `image/webp` format without changing the URL.
> >
> For Safari and Opera users, the original image will be used. > ~~For Safari and Opera users, the original image will be used.~~
> We've now support Safari/Chrome/Firefox on iOS 14/iPadOS 14
## Simple Usage Steps ## Simple Usage Steps

View File

@ -21,7 +21,7 @@ var (
prefetch, proxyMode bool prefetch, proxyMode bool
config Config config Config
version = "0.3.0" version = "0.3.1"
) )
const ( const (

View File

@ -143,13 +143,26 @@ func getCompressionRate(RawImagePath string, webpAbsPath string) string {
return fmt.Sprintf(`%.2f`, compressionRate) return fmt.Sprintf(`%.2f`, compressionRate)
} }
func goOrigin(UA string) bool { func goOrigin(header, ua string) bool {
// We'll first check accept headers, if accept headers is false, we'll then go to UA part
if headerOrigin(header) && uaOrigin(ua) {
return true
} else {
return false
}
}
func uaOrigin(ua string) bool {
// iOS 14 and iPadOS 14 supports webp, the identification token is iPhone OS 14_2_1 and CPU OS 14_2
// for more information, please check test case // for more information, please check test case
if strings.Contains(UA, "Firefox") || strings.Contains(UA, "Chrome") { if strings.Contains(ua, "iPhone OS 14") || strings.Contains(ua, "CPU OS 14") {
// this is iOS 14/iPadOS 14
return false
} else if strings.Contains(ua, "Firefox") || strings.Contains(ua, "Chrome") {
// Chrome or firefox on macOS Windows // Chrome or firefox on macOS Windows
} else if strings.Contains(UA, "Android") || strings.Contains(UA, "Linux") { } else if strings.Contains(ua, "Android") || strings.Contains(ua, "Linux") {
// on Android and Linux // on Android and Linux
} else if strings.Contains(UA, "FxiOS") || strings.Contains(UA, "CriOS") { } else if strings.Contains(ua, "FxiOS") || strings.Contains(ua, "CriOS") {
//firefox and Chrome on iOS //firefox and Chrome on iOS
return true return true
} else { } else {
@ -157,3 +170,14 @@ func goOrigin(UA string) bool {
} }
return false return false
} }
func headerOrigin(header string) bool {
// Webkit is really weird especially on iOS, it doesn't even send out effective accept headers.
// Head to test case if you want to know more
if strings.Contains(header, "image/webp") {
return false
} else {
// go to origin
return true
}
}

View File

@ -64,9 +64,38 @@ func TestGenEtag(t *testing.T) {
} }
func TestGoOrigin(t *testing.T) { func TestGoOrigin(t *testing.T) {
// this is a complete test case for webp compatibility
// func goOrigin(header, ua string) bool
// UNLESS YOU KNOW WHAT YOU ARE DOING, DO NOT CHANGE THE TEST CASE MAPPING HERE.
var testCase = map[[2]string]bool{
// macOS Catalina Safari
[2]string{"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15"}: true,
// macOS Chrome
[2]string{"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.88 Safari/537.36"}: false,
// iOS14 Safari
[2]string{"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Mozilla/5.0 (iPhone; CPU iPhone OS 14_2_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Mobile/15E148 Safari/604.1"}: false,
// iOS14 Chrome
[2]string{"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Mozilla/5.0 (iPhone; CPU iPhone OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1"}: false,
// iPadOS 14 Safari
[2]string{"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Mozilla/5.0 (iPad; CPU OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Mobile/15E148 Safari/604.1"}: false,
// iPadOS 14 Chrome
[2]string{"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Mozilla/5.0 (iPad; CPU OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1"}: false,
// Warning: these three are real capture headers - I don't have iOS/iPadOS prior to version 14
[2]string{"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15"}: true,
[2]string{"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Mozilla/5.0 (iPad; CPU OS 10_15_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/25.0 Mobile/15E148 Safari/605.1.15"}: true,
[2]string{"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/83.0.4103.63 Mobile/15E148 Safari/604.1"}: true,
}
for value, is := range testCase {
assert.Equalf(t, is, goOrigin(value[0], value[1]), "[%v]:[%s]", value, is)
}
}
func TestUaOrigin(t *testing.T) {
// reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox // reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
// https://developer.chrome.com/multidevice/user-agent#chrome_for_ios_user_agent // https://developer.chrome.com/multidevice/user-agent#chrome_for_ios_user_agent
// UNLESS YOU KNOW WHAT YOU ARE DOING, DO NOT CHANGE THE TEST CASE MAPPING HERE.
var testCase = map[string]bool{ var testCase = map[string]bool{
// Chrome on Windows, macOS, linux, iOS and Android // Chrome on Windows, macOS, linux, iOS and Android
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36": false, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36": false,
@ -98,14 +127,37 @@ func TestGoOrigin(t *testing.T) {
// Others // Others
"PostmanRuntime/7.26.1": true, "PostmanRuntime/7.26.1": true,
"curl/7.64.1": true, "curl/7.64.1": true,
// these four are captured from iOS14/iPadOS14, which supports webp
"Mozilla/5.0 (iPhone; CPU iPhone OS 14_2_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Mobile/15E148 Safari/604.1": false,
"Mozilla/5.0 (iPhone; CPU iPhone OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1\n": false,
"Mozilla/5.0 (iPad; CPU OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Mobile/15E148 Safari/604.1": false,
"Mozilla/5.0 (iPad; CPU OS 14_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1": false,
} }
for browser, is := range testCase { for browser, is := range testCase {
assert.Equalf(t, is, goOrigin(browser), "[%v]:[%s]", is, browser) assert.Equalf(t, is, uaOrigin(browser), "[%v]:[%s]", is, browser)
} }
} }
func TestHeaderOrigin(t *testing.T) {
// Chrome: Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8
// Safari 版本14.0.1 (15610.2.11.51.10, 15610): Accept: image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5
// Safari Technology Preview Release 116 (Safari 14.1, WebKit 15611.1.5.3) Accept: image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5
// UNLESS YOU KNOW WHAT YOU ARE DOING, DO NOT CHANGE THE TEST CASE MAPPING HERE.
var testCase = map[string]bool{
"image/avif,image/webp,image/apng,image/*,*/*;q=0.8": false,
"*/*": true,
"image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5": true,
"I don't know what it is:-)": true,
}
for header, is := range testCase {
assert.Equalf(t, is, headerOrigin(header), "[%v]:[%s]", is, header)
}
}
func TestChanErr(t *testing.T) { func TestChanErr(t *testing.T) {
var value = 2 var value = 2
var testC = make(chan int, 2) var testC = make(chan int, 2)

View File

@ -19,12 +19,13 @@ func convert(c *fiber.Ctx) error {
var rawImageAbs = path.Join(config.ImgPath, reqURI) // /home/xxx/mypic/123.jpg var rawImageAbs = path.Join(config.ImgPath, reqURI) // /home/xxx/mypic/123.jpg
var imgFilename = path.Base(reqURI) // pure filename, 123.jpg var imgFilename = path.Base(reqURI) // pure filename, 123.jpg
var finalFile string // We'll only need one c.sendFile() var finalFile string // We'll only need one c.sendFile()
var UA = c.Get("User-Agent") var ua = c.Get("User-Agent")
log.Debugf("Incoming connection from %s@%s with %s", UA, c.IP(), imgFilename) var accept = c.Get("accept")
log.Debugf("Incoming connection from %s@%s with %s", ua, c.IP(), imgFilename)
needOrigin := goOrigin(UA) needOrigin := goOrigin(accept, ua)
if needOrigin { if needOrigin {
log.Infof("A Safari/IE/whatever user has arrived...%s", UA) log.Infof("A Safari/IE/whatever user has arrived...%s", ua)
// Check for Safari users. If they're Safari, just simply ignore everything. // Check for Safari users. If they're Safari, just simply ignore everything.
etag := genEtag(rawImageAbs) etag := genEtag(rawImageAbs)
c.Set("ETag", etag) c.Set("ETag", etag)

View File

@ -1,5 +1,5 @@
Name: webp-server Name: webp-server
Version: 0.3.0 Version: 0.3.1
Release: 1%{?dist} Release: 1%{?dist}
Summary: Go version of WebP Server. A tool that will serve your JPG/PNGs as WebP format with compression, on-the-fly. Summary: Go version of WebP Server. A tool that will serve your JPG/PNGs as WebP format with compression, on-the-fly.