Skip to content

Commit

Permalink
Replace image optimizer IPC call with request handler (#61471)
Browse files Browse the repository at this point in the history
This ensures we don't do un-necessary IPC calls during image optimizing
and leverage the internal request handler for fetching internal static
images/resources.

x-ref: #58248

Closes NEXT-2318
  • Loading branch information
ijjk authored and styfle committed Feb 28, 2024
1 parent f8fe70d commit 179d14e
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 71 deletions.
85 changes: 43 additions & 42 deletions packages/next/src/server/lib/router-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,48 +139,6 @@ export async function initialize(opts: {
renderServer.instance =
require('./render-server') as typeof import('./render-server')

const renderServerOpts: Parameters<RenderServer['initialize']>[0] = {
port: opts.port,
dir: opts.dir,
hostname: opts.hostname,
minimalMode: opts.minimalMode,
dev: !!opts.dev,
server: opts.server,
isNodeDebugging: !!opts.isNodeDebugging,
serverFields: developmentBundler?.serverFields || {},
experimentalTestProxy: !!opts.experimentalTestProxy,
experimentalHttpsServer: !!opts.experimentalHttpsServer,
bundlerService: devBundlerService,
startServerSpan: opts.startServerSpan,
}

// pre-initialize workers
const handlers = await renderServer.instance.initialize(renderServerOpts)

const logError = async (
type: 'uncaughtException' | 'unhandledRejection',
err: Error | undefined
) => {
if (isPostpone(err)) {
// React postpones that are unhandled might end up logged here but they're
// not really errors. They're just part of rendering.
return
}
await developmentBundler?.logErrorWithOriginalStack(err, type)
}

process.on('uncaughtException', logError.bind(null, 'uncaughtException'))
process.on('unhandledRejection', logError.bind(null, 'unhandledRejection'))

const resolveRoutes = getResolveRoutes(
fsChecker,
config,
opts,
renderServer.instance,
renderServerOpts,
developmentBundler?.ensureMiddleware
)

const requestHandlerImpl: WorkerRequestHandler = async (req, res) => {
if (compress) {
// @ts-expect-error not express req/res
Expand Down Expand Up @@ -574,6 +532,49 @@ export async function initialize(opts: {
}
requestHandlers[opts.dir] = requestHandler

const renderServerOpts: Parameters<RenderServer['initialize']>[0] = {
port: opts.port,
dir: opts.dir,
hostname: opts.hostname,
minimalMode: opts.minimalMode,
dev: !!opts.dev,
server: opts.server,
isNodeDebugging: !!opts.isNodeDebugging,
serverFields: developmentBundler?.serverFields || {},
experimentalTestProxy: !!opts.experimentalTestProxy,
experimentalHttpsServer: !!opts.experimentalHttpsServer,
bundlerService: devBundlerService,
startServerSpan: opts.startServerSpan,
}
renderServerOpts.serverFields.routerServerHandler = requestHandlerImpl

// pre-initialize workers
const handlers = await renderServer.instance.initialize(renderServerOpts)

const logError = async (
type: 'uncaughtException' | 'unhandledRejection',
err: Error | undefined
) => {
if (isPostpone(err)) {
// React postpones that are unhandled might end up logged here but they're
// not really errors. They're just part of rendering.
return
}
await developmentBundler?.logErrorWithOriginalStack(err, type)
}

process.on('uncaughtException', logError.bind(null, 'uncaughtException'))
process.on('unhandledRejection', logError.bind(null, 'unhandledRejection'))

const resolveRoutes = getResolveRoutes(
fsChecker,
config,
opts,
renderServer.instance,
renderServerOpts,
developmentBundler?.ensureMiddleware
)

const upgradeHandler: WorkerUpgradeHandler = async (req, socket, head) => {
try {
req.on('error', (_err) => {
Expand Down
36 changes: 7 additions & 29 deletions packages/next/src/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ import { getTracer } from './lib/trace/tracer'
import { NextNodeServerSpan } from './lib/trace/constants'
import { nodeFs } from './lib/node-fs-methods'
import { getRouteRegex } from '../shared/lib/router/utils/route-regex'
import { invokeRequest } from './lib/server-ipc/invoke-request'
import { filterReqHeaders, ipcForbiddenHeaders } from './lib/server-ipc/utils'
import { pipeToNodeResponse } from './pipe-readable'
import { createRequestResponseMocks } from './lib/mock-request'
import { NEXT_RSC_UNION_QUERY } from '../client/components/app-router-headers'
Expand Down Expand Up @@ -171,6 +169,10 @@ export default class NextNodeServer extends BaseServer {
page: string
re: RegExp
}[]
private routerServerHandler?: (
req: IncomingMessage,
res: ServerResponse
) => void

constructor(options: Options) {
// Initialize super class
Expand Down Expand Up @@ -555,35 +557,11 @@ export default class NextNodeServer extends BaseServer {
throw new Error(`Invariant attempted to optimize _next/image itself`)
}

const protocol = this.serverOptions.experimentalHttpsServer
? 'https'
: 'http'

const invokeRes = await invokeRequest(
`${protocol}:https://${this.fetchHostname || 'localhost'}:${this.port}${
newReq.url || ''
}`,
{
method: newReq.method || 'GET',
headers: newReq.headers,
signal: signalFromNodeResponse(res.originalResponse),
}
)
const filteredResHeaders = filterReqHeaders(
toNodeOutgoingHttpHeaders(invokeRes.headers),
ipcForbiddenHeaders
)

for (const key of Object.keys(filteredResHeaders)) {
newRes.setHeader(key, filteredResHeaders[key] || '')
if (!this.routerServerHandler) {
throw new Error(`Invariant missing routerServerHandler`)
}
newRes.statusCode = invokeRes.status || 200

if (invokeRes.body) {
await pipeToNodeResponse(invokeRes.body, newRes)
} else {
res.send()
}
await this.routerServerHandler(newReq, newRes)
return
}

Expand Down

0 comments on commit 179d14e

Please sign in to comment.