Skip to content

Commit

Permalink
fix: ensure edge functions work with HTTPS mode (#5409)
Browse files Browse the repository at this point in the history
* fix: communicate with Deno server over HTTP

* chore: define complications

* refactor: small optimisation
  • Loading branch information
eduardoboucas authored Jan 20, 2023
1 parent 7e27e7a commit 89754a1
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 18 deletions.
1 change: 0 additions & 1 deletion src/lib/edge-functions/headers.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const headers = {
ForwardedHost: 'x-forwarded-host',
ForwardedProtocol: 'x-forwarded-proto',
Functions: 'x-nf-edge-functions',
Geo: 'x-nf-geo',
Passthrough: 'x-nf-passthrough',
Expand Down
13 changes: 3 additions & 10 deletions src/lib/edge-functions/proxy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,21 @@ export const initializeProxy = async ({
geolocationMode,
getUpdatedConfig,
inspectSettings,
mainPort,
offline,
passthroughPort,
projectDir,
settings,
siteInfo,
state,
}) => {
const { functions: internalFunctions, importMap, path: internalFunctionsPath } = await getInternalFunctions()
const { port: mainPort } = settings
const userFunctionsPath = config.build.edge_functions
const isolatePort = await getAvailablePort()

// Initializes the server, bootstrapping the Deno CLI and downloading it from
// the network if needed. We don't want to wait for that to be completed, or
// the command will be left hanging.
const server = prepareServer({
certificatePath: settings.https ? settings.https.certFilePath : undefined,
config,
configPath,
directories: [internalFunctionsPath, userFunctionsPath].filter(Boolean),
Expand Down Expand Up @@ -132,7 +131,7 @@ export const initializeProxy = async ({

req[headersSymbol] = {
[headers.Functions]: functionNames.join(','),
[headers.ForwardedHost]: `localhost:${mainPort}`,
[headers.ForwardedHost]: `localhost:${passthroughPort}`,
[headers.Passthrough]: 'passthrough',
[headers.RequestID]: generateUUID(),
[headers.IP]: LOCAL_HOST,
Expand All @@ -142,18 +141,13 @@ export const initializeProxy = async ({
req[headersSymbol][headers.DebugLogging] = '1'
}

if (settings.https) {
req[headersSymbol][headers.ForwardedProtocol] = 'https'
}

return `http://${LOCAL_HOST}:${isolatePort}`
}
}

export const isEdgeFunctionsRequest = (req) => req[headersSymbol] !== undefined

const prepareServer = async ({
certificatePath,
config,
configPath,
directories,
Expand All @@ -173,7 +167,6 @@ const prepareServer = async ({
const distImportMapPath = getPathInProject([DIST_IMPORT_MAP_PATH])
const runIsolate = await bundler.serve({
...getDownloadUpdateFunctions(),
certificatePath,
debug: env.NETLIFY_DENO_DEBUG === 'true',
distImportMapPath,
formatExportTypeError: (name) =>
Expand Down
33 changes: 26 additions & 7 deletions src/utils/proxy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import contentType from 'content-type'
import cookie from 'cookie'
import { get } from 'dot-prop'
import generateETag from 'etag'
import getAvailablePort from 'get-port'
import httpProxy from 'http-proxy'
import { createProxyMiddleware } from 'http-proxy-middleware'
import jwtDecode from 'jwt-decode'
Expand Down Expand Up @@ -545,6 +546,7 @@ export const startProxy = async function ({
siteInfo,
state,
}) {
const secondaryServerPort = settings.https ? await getAvailablePort() : null
const functionsServer = settings.functionsPort ? `http://127.0.0.1:${settings.functionsPort}` : null
const edgeFunctionsProxy = await initializeEdgeFunctionsProxy({
config,
Expand All @@ -555,9 +557,10 @@ export const startProxy = async function ({
geoCountry,
getUpdatedConfig,
inspectSettings,
mainPort: settings.port,
offline,
passthroughPort: secondaryServerPort || settings.port,
projectDir,
settings,
siteInfo,
state,
})
Expand Down Expand Up @@ -586,16 +589,32 @@ export const startProxy = async function ({
functionsServer,
edgeFunctionsProxy,
})
const server = settings.https
const primaryServer = settings.https
? https.createServer({ cert: settings.https.cert, key: settings.https.key }, onRequestWithOptions)
: http.createServer(onRequestWithOptions)

server.on('upgrade', function onUpgrade(req, socket, head) {
const onUpgrade = function onUpgrade(req, socket, head) {
proxy.ws(req, socket, head)
})
}

primaryServer.on('upgrade', onUpgrade)
primaryServer.listen({ port: settings.port })

const eventQueue = [once(primaryServer, 'listening')]

// If we're running the main server on HTTPS, we need to start a secondary
// server on HTTP for receiving passthrough requests from edge functions.
// This lets us run the Deno server on HTTP and avoid the complications of
// Deno talking to Node on HTTPS with potentially untrusted certificates.
if (secondaryServerPort) {
const secondaryServer = http.createServer(onRequestWithOptions)

secondaryServer.on('upgrade', onUpgrade)
secondaryServer.listen({ port: secondaryServerPort })

eventQueue.push(once(secondaryServer, 'listening'))
}

server.listen({ port: settings.port })
await once(server, 'listening')
await Promise.all(eventQueue)

const scheme = settings.https ? 'https' : 'http'
return `${scheme}://localhost:${settings.port}`
Expand Down

1 comment on commit 89754a1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 Benchmark results

  • Package size: 267 MB

Please sign in to comment.