Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix image optimization encoding url #28045

Merged
merged 12 commits into from Aug 13, 2021
8 changes: 3 additions & 5 deletions packages/next/server/image-optimizer.ts
Expand Up @@ -69,22 +69,20 @@ export async function imageOptimizer(
}

const { headers } = req
const { url: decodedUrl, w, q } = parsedUrl.query
const { url, w, q } = parsedUrl.query
const mimeType = getSupportedMimeType(MODERN_TYPES, headers.accept)
let href: string

if (!decodedUrl) {
if (!url) {
res.statusCode = 400
res.end('"url" parameter is required')
return { finished: true }
} else if (Array.isArray(decodedUrl)) {
} else if (Array.isArray(url)) {
res.statusCode = 400
res.end('"url" parameter cannot be an array')
return { finished: true }
}

const url = encodeURI(decodedUrl)

let isAbsolute: boolean

if (url.startsWith('/')) {
Expand Down
8 changes: 8 additions & 0 deletions test/integration/image-component/unicode/next.config.js
@@ -0,0 +1,8 @@
module.exports = {
images: {
domains: [
'image-optimization-test.vercel.app',
'firebasestorage.googleapis.com',
styfle marked this conversation as resolved.
Show resolved Hide resolved
],
},
}
33 changes: 33 additions & 0 deletions test/integration/image-component/unicode/pages/index.js
@@ -0,0 +1,33 @@
import React from 'react'
import Image from 'next/image'
import img from '../public/äöü.png'

const Page = () => {
return (
<div>
<h1>Unicode Image URL</h1>
<Image id="static" src={img} />
<Image id="internal" src="/äöü.png" width={400} height={400} />
<Image
id="external"
src="https://image-optimization-test.vercel.app/äöü.png"
width={400}
height={400}
/>
<Image
id="internal-space"
src="/hello%20world.jpg"
width={200}
height={200}
/>
<Image
id="external-space"
src="https://image-optimization-test.vercel.app/hello%20world.jpg"
width={200}
height={200}
/>
</div>
)
}

export default Page
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 96 additions & 0 deletions test/integration/image-component/unicode/test/index.test.js
@@ -0,0 +1,96 @@
/* eslint-env jest */

import {
findPort,
killApp,
launchApp,
nextBuild,
nextStart,
} from 'next-test-utils'
import webdriver from 'next-webdriver'
//import fetch from 'node-fetch'
import { join } from 'path'

jest.setTimeout(1000 * 60)

const appDir = join(__dirname, '../')

let appPort
let app
let browser

function runTests() {
it('should load static unicode image', async () => {
const src = await browser.elementById('static').getAttribute('src')
expect(src).toMatch(
/_next%2Fstatic%2Fimage%2Fpublic%2F%C3%A4%C3%B6%C3%BC(.+)png/
)
const res = await fetch(src)
expect(res.status).toBe(200)
})

it('should load internal unicode image', async () => {
const src = await browser.elementById('internal').getAttribute('src')
expect(src).toMatch('/_next/image?url=%2F%C3%A4%C3%B6%C3%BC.png')
const res = await fetch(src)
expect(res.status).toBe(200)
})

it('should load external unicode image', async () => {
const src = await browser.elementById('external').getAttribute('src')
expect(src).toMatch(
'/_next/image?url=https%3A%2F%2Fimage-optimization-test.vercel.app%2F%C3%A4%C3%B6%C3%BC.png'
)
const res = await fetch(src)
expect(res.status).toBe(200)
})

it('should load internal image with space', async () => {
const src = await browser.elementById('internal-space').getAttribute('src')
expect(src).toMatch('/_next/image?url=%2Fhello%2520world.jpg')
const res = await fetch(src)
expect(res.status).toBe(200)
})

it('should load external image with space', async () => {
const src = await browser.elementById('external-space').getAttribute('src')
expect(src).toMatch(
'/_next/image?url=https%3A%2F%2Fimage-optimization-test.vercel.app%2Fhello%2520world.jpg'
)
const res = await fetch(src)
expect(res.status).toBe(200)
})
}

describe('Image Component Unicode Image URL', () => {
describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
browser = await webdriver(appPort, '/')
})
afterAll(() => {
killApp(app)
if (browser) {
browser.close()
}
})
runTests()
})

describe('server mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
browser = await webdriver(appPort, '/')
})
afterAll(() => {
killApp(app)
if (browser) {
browser.close()
}
})
runTests()
})
})
6 changes: 0 additions & 6 deletions test/integration/image-optimizer/test/index.test.js
Expand Up @@ -55,12 +55,6 @@ function runTests({ w, isDev, domains = [], ttl, isSharp }) {
expect(await res.text()).toMatch(/Image Optimizer Home/m)
})

it('should handle non-ascii characters in image url', async () => {
const query = { w, q: 90, url: '/äöü.png' }
const res = await fetchViaHTTP(appPort, '/_next/image', query, {})
expect(res.status).toBe(200)
})
styfle marked this conversation as resolved.
Show resolved Hide resolved

it('should maintain animated gif', async () => {
const query = { w, q: 90, url: '/animated.gif' }
const res = await fetchViaHTTP(appPort, '/_next/image', query, {})
Expand Down