fatsify核心功能示例测试!!!
This commit is contained in:
355
node_modules/fastify/lib/server.js
generated
vendored
Normal file
355
node_modules/fastify/lib/server.js
generated
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
'use strict'
|
||||
|
||||
const http = require('node:http')
|
||||
const https = require('node:https')
|
||||
const http2 = require('node:http2')
|
||||
const dns = require('node:dns')
|
||||
const os = require('node:os')
|
||||
|
||||
const { kState, kOptions, kServerBindings } = require('./symbols')
|
||||
const { FSTWRN003 } = require('./warnings')
|
||||
const { onListenHookRunner } = require('./hooks')
|
||||
const {
|
||||
FST_ERR_REOPENED_CLOSE_SERVER,
|
||||
FST_ERR_REOPENED_SERVER,
|
||||
FST_ERR_LISTEN_OPTIONS_INVALID
|
||||
} = require('./errors')
|
||||
const PonyPromise = require('./promise')
|
||||
|
||||
module.exports.createServer = createServer
|
||||
|
||||
function defaultResolveServerListeningText (address) {
|
||||
return `Server listening at ${address}`
|
||||
}
|
||||
|
||||
function createServer (options, httpHandler) {
|
||||
const server = getServerInstance(options, httpHandler)
|
||||
|
||||
// `this` is the Fastify object
|
||||
function listen (
|
||||
listenOptions = { port: 0, host: 'localhost' },
|
||||
cb = undefined
|
||||
) {
|
||||
if (typeof cb === 'function') {
|
||||
if (cb.constructor.name === 'AsyncFunction') {
|
||||
FSTWRN003('listen method')
|
||||
}
|
||||
|
||||
listenOptions.cb = cb
|
||||
}
|
||||
if (listenOptions.signal) {
|
||||
if (typeof listenOptions.signal.on !== 'function' && typeof listenOptions.signal.addEventListener !== 'function') {
|
||||
throw new FST_ERR_LISTEN_OPTIONS_INVALID('Invalid options.signal')
|
||||
}
|
||||
|
||||
// copy the current signal state
|
||||
this[kState].aborted = listenOptions.signal.aborted
|
||||
|
||||
if (this[kState].aborted) {
|
||||
return this.close()
|
||||
} else {
|
||||
const onAborted = () => {
|
||||
this[kState].aborted = true
|
||||
this.close()
|
||||
}
|
||||
listenOptions.signal.addEventListener('abort', onAborted, { once: true })
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a path specified, don't default host to 'localhost' so we don't end up listening
|
||||
// on both path and host
|
||||
// See https://github.com/fastify/fastify/issues/4007
|
||||
let host
|
||||
if (listenOptions.path == null) {
|
||||
host = listenOptions.host ?? 'localhost'
|
||||
} else {
|
||||
host = listenOptions.host
|
||||
}
|
||||
if (!Object.hasOwn(listenOptions, 'host') ||
|
||||
listenOptions.host == null) {
|
||||
listenOptions.host = host
|
||||
}
|
||||
if (host === 'localhost') {
|
||||
listenOptions.cb = (err, address) => {
|
||||
if (err) {
|
||||
// the server did not start
|
||||
cb(err, address)
|
||||
return
|
||||
}
|
||||
|
||||
multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
|
||||
this[kState].listening = true
|
||||
cb(null, address)
|
||||
onListenHookRunner(this)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
listenOptions.cb = (err, address) => {
|
||||
// the server did not start
|
||||
if (err) {
|
||||
cb(err, address)
|
||||
return
|
||||
}
|
||||
this[kState].listening = true
|
||||
cb(null, address)
|
||||
onListenHookRunner(this)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/nodejs/node/issues/9390
|
||||
// If listening to 'localhost', listen to both 127.0.0.1 or ::1 if they are available.
|
||||
// If listening to 127.0.0.1, only listen to 127.0.0.1.
|
||||
// If listening to ::1, only listen to ::1.
|
||||
|
||||
if (cb === undefined) {
|
||||
const listening = listenPromise.call(this, server, listenOptions)
|
||||
return listening.then(address => {
|
||||
const { promise, resolve } = PonyPromise.withResolvers()
|
||||
if (host === 'localhost') {
|
||||
multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
|
||||
this[kState].listening = true
|
||||
resolve(address)
|
||||
onListenHookRunner(this)
|
||||
})
|
||||
} else {
|
||||
resolve(address)
|
||||
onListenHookRunner(this)
|
||||
}
|
||||
return promise
|
||||
})
|
||||
}
|
||||
|
||||
this.ready(listenCallback.call(this, server, listenOptions))
|
||||
}
|
||||
|
||||
return { server, listen }
|
||||
}
|
||||
|
||||
function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, onListen) {
|
||||
// the main server is started, we need to start the secondary servers
|
||||
this[kState].listening = false
|
||||
|
||||
// let's check if we need to bind additional addresses
|
||||
dns.lookup(listenOptions.host, { all: true }, (dnsErr, addresses) => {
|
||||
if (dnsErr || this[kState].aborted) {
|
||||
// not blocking the main server listening
|
||||
// this.log.warn('dns.lookup error:', dnsErr)
|
||||
onListen()
|
||||
return
|
||||
}
|
||||
|
||||
const isMainServerListening = mainServer.listening && serverOpts.serverFactory
|
||||
|
||||
let binding = 0
|
||||
let bound = 0
|
||||
if (!isMainServerListening) {
|
||||
const primaryAddress = mainServer.address()
|
||||
for (const adr of addresses) {
|
||||
if (adr.address !== primaryAddress.address) {
|
||||
binding++
|
||||
const secondaryOpts = Object.assign({}, listenOptions, {
|
||||
host: adr.address,
|
||||
port: primaryAddress.port,
|
||||
cb: (_ignoreErr) => {
|
||||
bound++
|
||||
|
||||
if (!_ignoreErr) {
|
||||
this[kServerBindings].push(secondaryServer)
|
||||
}
|
||||
|
||||
if (bound === binding) {
|
||||
// regardless of the error, we are done
|
||||
onListen()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const secondaryServer = getServerInstance(serverOpts, httpHandler)
|
||||
const closeSecondary = () => {
|
||||
// To avoid falling into situations where the close of the
|
||||
// secondary server is triggered before the preClose hook
|
||||
// is done running, we better wait until the main server is closed.
|
||||
// No new TCP connections are accepted
|
||||
// We swallow any error from the secondary server
|
||||
secondaryServer.close(() => {})
|
||||
if (typeof secondaryServer.closeAllConnections === 'function' && serverOpts.forceCloseConnections === true) {
|
||||
secondaryServer.closeAllConnections()
|
||||
}
|
||||
}
|
||||
|
||||
secondaryServer.on('upgrade', mainServer.emit.bind(mainServer, 'upgrade'))
|
||||
mainServer.on('unref', closeSecondary)
|
||||
mainServer.on('close', closeSecondary)
|
||||
mainServer.on('error', closeSecondary)
|
||||
this[kState].listening = false
|
||||
listenCallback.call(this, secondaryServer, secondaryOpts)()
|
||||
}
|
||||
}
|
||||
}
|
||||
// no extra bindings are necessary
|
||||
if (binding === 0) {
|
||||
onListen()
|
||||
return
|
||||
}
|
||||
|
||||
// in test files we are using unref so we need to propagate the unref event
|
||||
// to the secondary servers. It is valid only when the user is
|
||||
// listening on localhost
|
||||
const originUnref = mainServer.unref
|
||||
mainServer.unref = function () {
|
||||
originUnref.call(mainServer)
|
||||
mainServer.emit('unref')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function listenCallback (server, listenOptions) {
|
||||
const wrap = (err) => {
|
||||
server.removeListener('error', wrap)
|
||||
server.removeListener('listening', wrap)
|
||||
if (!err) {
|
||||
const address = logServerAddress.call(this, server, listenOptions.listenTextResolver || defaultResolveServerListeningText)
|
||||
listenOptions.cb(null, address)
|
||||
} else {
|
||||
this[kState].listening = false
|
||||
listenOptions.cb(err, null)
|
||||
}
|
||||
}
|
||||
|
||||
return (err) => {
|
||||
if (err != null) return listenOptions.cb(err)
|
||||
|
||||
if (this[kState].listening && this[kState].closing) {
|
||||
return listenOptions.cb(new FST_ERR_REOPENED_CLOSE_SERVER(), null)
|
||||
}
|
||||
if (this[kState].listening) {
|
||||
return listenOptions.cb(new FST_ERR_REOPENED_SERVER(), null)
|
||||
}
|
||||
|
||||
server.once('error', wrap)
|
||||
if (!this[kState].closing) {
|
||||
server.once('listening', wrap)
|
||||
server.listen(listenOptions)
|
||||
this[kState].listening = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function listenPromise (server, listenOptions) {
|
||||
if (this[kState].listening && this[kState].closing) {
|
||||
return Promise.reject(new FST_ERR_REOPENED_CLOSE_SERVER())
|
||||
}
|
||||
if (this[kState].listening) {
|
||||
return Promise.reject(new FST_ERR_REOPENED_SERVER())
|
||||
}
|
||||
|
||||
return this.ready().then(() => {
|
||||
// skip listen when aborted during ready
|
||||
if (this[kState].aborted) return
|
||||
|
||||
const { promise, resolve, reject } = PonyPromise.withResolvers()
|
||||
|
||||
const errEventHandler = (err) => {
|
||||
cleanup()
|
||||
this[kState].listening = false
|
||||
reject(err)
|
||||
}
|
||||
const listeningEventHandler = () => {
|
||||
cleanup()
|
||||
this[kState].listening = true
|
||||
resolve(logServerAddress.call(this, server, listenOptions.listenTextResolver || defaultResolveServerListeningText))
|
||||
}
|
||||
function cleanup () {
|
||||
server.removeListener('error', errEventHandler)
|
||||
server.removeListener('listening', listeningEventHandler)
|
||||
}
|
||||
server.once('error', errEventHandler)
|
||||
server.once('listening', listeningEventHandler)
|
||||
|
||||
server.listen(listenOptions)
|
||||
|
||||
return promise
|
||||
})
|
||||
}
|
||||
|
||||
function getServerInstance (options, httpHandler) {
|
||||
if (options.serverFactory) {
|
||||
// User provided server instance
|
||||
return options.serverFactory(httpHandler, options)
|
||||
}
|
||||
|
||||
// We have accepted true as a valid way to init https but node requires an options obj
|
||||
const httpsOptions = options.https === true ? {} : options.https
|
||||
|
||||
if (options.http2) {
|
||||
const server = typeof httpsOptions === 'object' ? http2.createSecureServer(httpsOptions, httpHandler) : http2.createServer(options.http, httpHandler)
|
||||
server.on('session', (session) => session.setTimeout(options.http2SessionTimeout, function closeSession () {
|
||||
this.close()
|
||||
}))
|
||||
|
||||
server.setTimeout(options.connectionTimeout)
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
// HTTP1 server instance
|
||||
const server = httpsOptions ? https.createServer(httpsOptions, httpHandler) : http.createServer(options.http, httpHandler)
|
||||
server.keepAliveTimeout = options.keepAliveTimeout
|
||||
server.requestTimeout = options.requestTimeout
|
||||
server.setTimeout(options.connectionTimeout)
|
||||
// We treat zero as null(node default) so we do not pass zero to the server instance
|
||||
if (options.maxRequestsPerSocket > 0) {
|
||||
server.maxRequestsPerSocket = options.maxRequestsPerSocket
|
||||
}
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the provided `server.address` object and returns a
|
||||
* normalized list of IP address strings. Normalization in this
|
||||
* case refers to mapping wildcard `0.0.0.0` to the list of IP
|
||||
* addresses the wildcard refers to.
|
||||
*
|
||||
* @see https://nodejs.org/docs/latest/api/net.html#serveraddress
|
||||
*
|
||||
* @param {object} A server address object as described in the
|
||||
* linked docs.
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function getAddresses (address) {
|
||||
if (address.address === '0.0.0.0') {
|
||||
return Object.values(os.networkInterfaces()).flatMap((iface) => {
|
||||
return iface.filter((iface) => iface.family === 'IPv4')
|
||||
}).sort((iface) => {
|
||||
/* c8 ignore next 2 */
|
||||
// Order the interfaces so that internal ones come first
|
||||
return iface.internal ? -1 : 1
|
||||
}).map((iface) => { return iface.address })
|
||||
}
|
||||
return [address.address]
|
||||
}
|
||||
|
||||
function logServerAddress (server, listenTextResolver) {
|
||||
let addresses
|
||||
const isUnixSocket = typeof server.address() === 'string'
|
||||
if (!isUnixSocket) {
|
||||
if (server.address().address.indexOf(':') === -1) {
|
||||
// IPv4
|
||||
addresses = getAddresses(server.address()).map((address) => address + ':' + server.address().port)
|
||||
} else {
|
||||
// IPv6
|
||||
addresses = ['[' + server.address().address + ']:' + server.address().port]
|
||||
}
|
||||
|
||||
addresses = addresses.map((address) => ('http' + (this[kOptions].https ? 's' : '') + '://') + address)
|
||||
} else {
|
||||
addresses = [server.address()]
|
||||
}
|
||||
|
||||
for (const address of addresses) {
|
||||
this.log.info(listenTextResolver(address))
|
||||
}
|
||||
return addresses[0]
|
||||
}
|
||||
Reference in New Issue
Block a user