Skip to content

Commit

Permalink
fix(hmr): respect server https options when running as middleware
Browse files Browse the repository at this point in the history
If you are running vite over https, HMR works great in the standalone mode, but when in middleware mode, the middleware might be served over https and the websocket server wouldn't listen on https. This updates the websocket server to respect the config.server.https option when in middleware mode too.

 - Moves the https config into a top level key of the resolved config, so its only resolved once (and only one set of certs is generated) and can be shared between different consumers
 - Boots a secure websocket server when in middleware mode if the `config.server` https option is on
  • Loading branch information
airhorns committed Feb 16, 2021
1 parent 4fd61ab commit 28e8057
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 17 deletions.
9 changes: 9 additions & 0 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from 'fs'
import { ServerOptions as HttpsServerOptions } from 'https'
import path from 'path'
import { Plugin } from './plugin'
import Rollup from 'rollup'
Expand Down Expand Up @@ -35,6 +36,7 @@ import {
PluginContainer
} from './server/pluginContainer'
import aliasPlugin from '@rollup/plugin-alias'
import { resolveHttpsConfig } from './server/http'

const debug = createDebugger('vite:config')

Expand Down Expand Up @@ -177,6 +179,7 @@ export type ResolvedConfig = Readonly<
}
plugins: readonly Plugin[]
server: ServerOptions
https: undefined | HttpsServerOptions
build: ResolvedBuildOptions
assetsInclude: (file: string) => boolean
logger: Logger
Expand Down Expand Up @@ -292,6 +295,11 @@ export async function resolveConfig(
const optimizeCacheDir =
pkgPath && path.join(path.dirname(pkgPath), `node_modules/${DEP_CACHE_DIR}`)

// resolve https cert
const httpsOptions =
typeof config.server?.https === 'boolean' ? {} : config.server?.https
const https = httpsOptions && (await resolveHttpsConfig(httpsOptions))

const assetsFilter = config.assetsInclude
? createFilter(config.assetsInclude)
: () => false
Expand Down Expand Up @@ -348,6 +356,7 @@ export async function resolveConfig(
optimizeCacheDir,
plugins: userPlugins,
server: config.server || {},
https,
build: resolvedBuildOptions,
env: {
...userEnv,
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export async function serve(config: ResolvedConfig, port = 5000) {
})
)

const server = await resolveHttpServer(config.server, app)
const server = await resolveHttpServer(config, app)

const options = config.server || {}
const hostname = options.host || 'localhost'
Expand Down
15 changes: 6 additions & 9 deletions packages/vite/src/node/server/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,24 @@ import fs from 'fs'
import path from 'path'
import { Server as HttpServer } from 'http'
import { ServerOptions as HttpsServerOptions } from 'https'
import { ServerOptions } from '..'
import { ResolvedConfig } from '..'
import { Connect } from 'types/connect'

export async function resolveHttpServer(
{ https = false, proxy }: ServerOptions,
config: ResolvedConfig,
app: Connect.Server
): Promise<HttpServer> {
if (!https) {
if (!config.https) {
return require('http').createServer(app)
}

const httpsOptions = await resolveHttpsConfig(
typeof https === 'boolean' ? {} : https
)
if (proxy) {
if (config.server?.proxy) {
// #484 fallback to http1 when proxy is needed.
return require('https').createServer(httpsOptions, app)
return require('https').createServer(config.https, app)
} else {
return require('http2').createSecureServer(
{
...httpsOptions,
...config.https,
allowHTTP1: true
},
app
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export async function createServer(
const middlewares = connect() as Connect.Server
const httpServer = middlewareMode
? null
: await resolveHttpServer(serverConfig, middlewares)
: await resolveHttpServer(config, middlewares)
const ws = createWebSocketServer(httpServer, config)

const { ignored = [], ...watchOptions } = serverConfig.watch || {}
Expand Down
32 changes: 26 additions & 6 deletions packages/vite/src/node/server/ws.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import chalk from 'chalk'
import { Server } from 'http'
import { Server, STATUS_CODES } from 'http'
import { createServer } from 'https'
import WebSocket from 'ws'
import { ErrorPayload, HMRPayload } from 'types/hmrPayload'
import { ResolvedConfig } from '..'
Expand Down Expand Up @@ -27,12 +28,31 @@ export function createWebSocketServer(
}
})
} else {
let websocketServerOptions: WebSocket.ServerOptions = {}
const port =
(typeof config.server.hmr === 'object' && config.server.hmr.port) || 24678
if (config.https) {
// if we're serving the middlewares over https, the ws library doesn't support automatically creating an https server, so we need to do it ourselves
// create an inline https server and mount the websocket server to it
const httpsServer = createServer(config.https, (req, res) => {
const body = STATUS_CODES[426]

res.writeHead(426, {
'Content-Length': body!.length,
'Content-Type': 'text/plain'
})
res.end(body)
})

httpsServer.listen(port)
websocketServerOptions.server = httpsServer
} else {
// we don't need to serve over https, just let ws handle its own server
websocketServerOptions.port = port
}

// vite dev server in middleware mode
wss = new WebSocket.Server({
port:
(typeof config.server.hmr === 'object' && config.server.hmr.port) ||
24678
})
wss = new WebSocket.Server(websocketServerOptions)
}

wss.on('connection', (socket) => {
Expand Down

0 comments on commit 28e8057

Please sign in to comment.