Skip to content

Commit

Permalink
Merge branch 'canary' into middleware-preflight-error-hard-navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] committed Mar 8, 2022
2 parents 460393a + d3cd00c commit f26ddd5
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 19 deletions.
10 changes: 8 additions & 2 deletions docs/api-reference/next/image.md
Expand Up @@ -374,7 +374,7 @@ module.exports = {

The default [Image Optimization API](#loader-configuration) will automatically detect the browser's supported image formats via the request's `Accept` header.

If the `Accept` head matches more than one of the configured formats, the first match in the array is used. Therefore, the array order matters. If there is no match, the Image Optimization API will fallback to the original image's format.
If the `Accept` head matches more than one of the configured formats, the first match in the array is used. Therefore, the array order matters. If there is no match (or the source image is [animated](#animated-images)), the Image Optimization API will fallback to the original image's format.

If no configuration is provided, the default below is used.

Expand Down Expand Up @@ -408,7 +408,7 @@ The expiration (or rather Max Age) is defined by either the [`minimumCacheTTL`](

- You can configure [`minimumCacheTTL`](#minimum-cache-ttl) to increase the cache duration when the upstream image does not include `Cache-Control` header or the value is very low.
- You can configure [`deviceSizes`](#device-sizes) and [`imageSizes`](#device-sizes) to reduce the total number of possible generated images.
- You can configure [formats](/docs/basic-features/image-optimization.md#acceptable-formats) to disable multiple formats in favor of a single image format.
- You can configure [formats](#acceptable-formats) to disable multiple formats in favor of a single image format.

### Minimum Cache TTL

Expand Down Expand Up @@ -455,6 +455,12 @@ module.exports = {
}
```

### Animated Images

The default [loader](#loader) will automatically bypass Image Optimization for animated images and serve the image as-is.

Auto-detection for animated files is best-effort and supports GIF, APNG, and WebP. If you want to explicitly bypass Image Optimization for a given animated image, use the [unoptimized](#unoptimized) prop.

## Related

For an overview of the Image component features and usage guidelines, see:
Expand Down
19 changes: 9 additions & 10 deletions examples/blog/theme.config.js
Expand Up @@ -2,20 +2,19 @@ const YEAR = new Date().getFullYear()

export default {
footer: (
<small style={{ display: 'block', marginTop: '8rem' }}>
<time>{YEAR}</time> © Your Name.
<a href="/feed.xml">RSS</a>
<footer>
<small>
<time>{YEAR}</time> © Your Name.
<a href="/feed.xml">RSS</a>
</small>
<style jsx>{`
footer {
margin-top: 8rem;
}
a {
float: right;
}
@media screen and (max-width: 480px) {
article {
padding-top: 2rem;
padding-bottom: 4rem;
}
}
`}</style>
</small>
</footer>
)
}
8 changes: 7 additions & 1 deletion packages/next/server/body-streams.ts
Expand Up @@ -58,14 +58,20 @@ export function clonableBodyForRequest<T extends IncomingMessage>(
) {
let bufferedBodyStream: BodyStream | null = null

const endPromise = new Promise((resolve, reject) => {
incomingMessage.on('end', resolve)
incomingMessage.on('error', reject)
})

return {
/**
* Replaces the original request body if necessary.
* This is done because once we read the body from the original request,
* we can't read it again.
*/
finalize(): void {
async finalize(): Promise<void> {
if (bufferedBodyStream) {
await endPromise
replaceRequestBody(
incomingMessage,
bodyStreamToNodeStream(bufferedBodyStream)
Expand Down
2 changes: 1 addition & 1 deletion packages/next/server/next-server.ts
Expand Up @@ -1314,7 +1314,7 @@ export default class NextNodeServer extends BaseServer {
}
}

originalBody?.finalize()
await originalBody?.finalize()

return result
}
Expand Down
8 changes: 5 additions & 3 deletions packages/next/shared/lib/router/router.ts
Expand Up @@ -1165,8 +1165,9 @@ export default class Router implements BaseRouter {
* request as it is not necessary.
*/
if (
(options as any)._h !== 1 ||
isDynamicRoute(removePathTrailingSlash(pathname))
(!options.shallow || (options as any)._h === 1) &&
((options as any)._h !== 1 ||
isDynamicRoute(removePathTrailingSlash(pathname)))
) {
const effect = await this._preflightRequest({
as,
Expand Down Expand Up @@ -1864,8 +1865,9 @@ export default class Router implements BaseRouter {
locale: string | undefined
isPreview: boolean
}): Promise<PreflightEffect> {
const asPathname = pathNoQueryHash(options.as)
const cleanedAs = delLocale(
hasBasePath(options.as) ? delBasePath(options.as) : options.as,
hasBasePath(asPathname) ? delBasePath(asPathname) : asPathname,
options.locale
)

Expand Down
Expand Up @@ -65,7 +65,10 @@ export async function middleware(request) {
return NextResponse.rewrite(url)
}

if (url.pathname === '/rewrites/rewrite-me-without-hard-navigation') {
if (
url.pathname === '/rewrites/rewrite-me-without-hard-navigation' ||
url.searchParams.get('path') === 'rewrite-me-without-hard-navigation'
) {
url.searchParams.set('middleware', 'foo')
url.pathname =
request.cookies['about-bypass'] === '1'
Expand Down
17 changes: 17 additions & 0 deletions test/integration/middleware/core/pages/rewrites/index.js
@@ -1,6 +1,8 @@
import Link from 'next/link'
import { useRouter } from 'next/router'

export default function Home() {
const router = useRouter()
return (
<div>
<p className="title">Home Page</p>
Expand Down Expand Up @@ -32,6 +34,21 @@ export default function Home() {
<Link href="/rewrites/about?override=internal">
<a id="override-with-internal-rewrite">Rewrite me to internal path</a>
</Link>
<div />
<a
href=""
id="link-to-shallow-push"
onClick={(e) => {
e.preventDefault()
router.push(
'/rewrites?path=rewrite-me-without-hard-navigation&message=refreshed',
undefined,
{ shallow: true }
)
}}
>
Do not rewrite me
</a>
</div>
)
}
11 changes: 11 additions & 0 deletions test/integration/middleware/core/test/index.test.js
Expand Up @@ -488,6 +488,17 @@ function rewriteTests(log, locale = '') {
const element = await browser.elementByCss('.title')
expect(await element.text()).toEqual('About Bypassed Page')
})

it(`${locale} should not call middleware with shallow push`, async () => {
const browser = await webdriver(context.appPort, '/rewrites')
await browser.elementByCss('#link-to-shallow-push').click()
await browser.waitForCondition(
'new URL(window.location.href).searchParams.get("path") === "rewrite-me-without-hard-navigation"'
)
await expect(async () => {
await browser.waitForElementByCss('.refreshed', 500)
}).rejects.toThrow()
})
}

function redirectTests(locale = '') {
Expand Down
32 changes: 31 additions & 1 deletion test/production/reading-request-body-in-middleware/index.test.ts
Expand Up @@ -16,7 +16,11 @@ describe('reading request body in middleware', () => {
return new Response('No body', { status: 400 });
}
const json = await request.json();
let json;
if (!request.nextUrl.searchParams.has("no_reading")) {
json = await request.json();
}
if (request.nextUrl.searchParams.has("next")) {
const res = NextResponse.next();
Expand Down Expand Up @@ -141,4 +145,30 @@ describe('reading request body in middleware', () => {
})
expect(response.headers.get('x-from-root-middleware')).toEqual('1')
})

it('passes the body to the api endpoint when no body is consumed on middleware', async () => {
const response = await fetchViaHTTP(
next.url,
'/api/hi',
{
next: '1',
no_reading: '1',
},
{
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
foo: 'bar',
}),
}
)
expect(response.status).toEqual(200)
expect(await response.json()).toEqual({
foo: 'bar',
api: true,
})
expect(response.headers.get('x-from-root-middleware')).toEqual('1')
})
})

0 comments on commit f26ddd5

Please sign in to comment.