You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					182 lines
				
				5.5 KiB
			
		
		
			
		
	
	
					182 lines
				
				5.5 KiB
			| 
											3 years ago
										 | 'use strict' | ||
|  | 
 | ||
|  | const assert = require('assert') | ||
|  | const http = require('http') | ||
|  | const https = require('https') | ||
|  | 
 | ||
|  | const { kState, kOptions } = require('./symbols') | ||
|  | const { FST_ERR_HTTP2_INVALID_VERSION, FST_ERR_REOPENED_CLOSE_SERVER, FST_ERR_REOPENED_SERVER } = require('./errors') | ||
|  | 
 | ||
|  | function createServer (options, httpHandler) { | ||
|  |   assert(options, 'Missing options') | ||
|  |   assert(httpHandler, 'Missing http handler') | ||
|  | 
 | ||
|  |   let server = null | ||
|  |   if (options.serverFactory) { | ||
|  |     server = options.serverFactory(httpHandler, options) | ||
|  |   } else if (options.http2) { | ||
|  |     if (options.https) { | ||
|  |       server = http2().createSecureServer(options.https, httpHandler) | ||
|  |     } else { | ||
|  |       server = http2().createServer(httpHandler) | ||
|  |     } | ||
|  |     server.on('session', sessionTimeout(options.http2SessionTimeout)) | ||
|  |   } else { | ||
|  |     // this is http1
 | ||
|  |     if (options.https) { | ||
|  |       server = https.createServer(options.https, httpHandler) | ||
|  |     } else { | ||
|  |       server = http.createServer(httpHandler) | ||
|  |     } | ||
|  |     server.keepAliveTimeout = options.keepAliveTimeout | ||
|  |     server.requestTimeout = options.requestTimeout | ||
|  |     // we treat zero as null
 | ||
|  |     // and null is the default setting from nodejs
 | ||
|  |     // so we do not pass the option to server
 | ||
|  |     if (options.maxRequestsPerSocket > 0) { | ||
|  |       server.maxRequestsPerSocket = options.maxRequestsPerSocket | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (!options.serverFactory) { | ||
|  |     server.setTimeout(options.connectionTimeout) | ||
|  |   } | ||
|  | 
 | ||
|  |   return { server, listen } | ||
|  | 
 | ||
|  |   // `this` is the Fastify object
 | ||
|  |   function listen () { | ||
|  |     const normalizeListenArgs = (args) => { | ||
|  |       if (args.length === 0) { | ||
|  |         return { port: 0, host: 'localhost' } | ||
|  |       } | ||
|  | 
 | ||
|  |       const cb = typeof args[args.length - 1] === 'function' ? args.pop() : undefined | ||
|  |       const options = { cb } | ||
|  | 
 | ||
|  |       const firstArg = args[0] | ||
|  |       const argsLength = args.length | ||
|  |       const lastArg = args[argsLength - 1] | ||
|  |       /* Deal with listen (options) || (handle[, backlog]) */ | ||
|  |       if (typeof firstArg === 'object' && firstArg !== null) { | ||
|  |         options.backlog = argsLength > 1 ? lastArg : undefined | ||
|  |         Object.assign(options, firstArg) | ||
|  |       } else if (typeof firstArg === 'string' && isNaN(firstArg)) { | ||
|  |         /* Deal with listen (pipe[, backlog]) */ | ||
|  |         options.path = firstArg | ||
|  |         options.backlog = argsLength > 1 ? lastArg : undefined | ||
|  |       } else { | ||
|  |         /* Deal with listen ([port[, host[, backlog]]]) */ | ||
|  |         options.port = argsLength >= 1 && firstArg ? firstArg : 0 | ||
|  |         // This will listen to what localhost is.
 | ||
|  |         // It can be 127.0.0.1 or ::1, depending on the operating system.
 | ||
|  |         // Fixes https://github.com/fastify/fastify/issues/1022.
 | ||
|  |         options.host = argsLength >= 2 && args[1] ? args[1] : 'localhost' | ||
|  |         options.backlog = argsLength >= 3 ? args[2] : undefined | ||
|  |       } | ||
|  | 
 | ||
|  |       return options | ||
|  |     } | ||
|  | 
 | ||
|  |     const listenOptions = normalizeListenArgs(Array.from(arguments)) | ||
|  |     const cb = listenOptions.cb | ||
|  | 
 | ||
|  |     const wrap = err => { | ||
|  |       server.removeListener('error', wrap) | ||
|  |       if (!err) { | ||
|  |         const address = logServerAddress() | ||
|  |         cb(null, address) | ||
|  |       } else { | ||
|  |         this[kState].listening = false | ||
|  |         cb(err, null) | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     const listenPromise = (listenOptions) => { | ||
|  |       if (this[kState].listening && this[kState].closing) { | ||
|  |         return Promise.reject(new FST_ERR_REOPENED_CLOSE_SERVER()) | ||
|  |       } else if (this[kState].listening) { | ||
|  |         return Promise.reject(new FST_ERR_REOPENED_SERVER()) | ||
|  |       } | ||
|  | 
 | ||
|  |       return this.ready().then(() => { | ||
|  |         let errEventHandler | ||
|  |         const errEvent = new Promise((resolve, reject) => { | ||
|  |           errEventHandler = (err) => { | ||
|  |             this[kState].listening = false | ||
|  |             reject(err) | ||
|  |           } | ||
|  |           server.once('error', errEventHandler) | ||
|  |         }) | ||
|  |         const listen = new Promise((resolve, reject) => { | ||
|  |           server.listen(listenOptions, () => { | ||
|  |             server.removeListener('error', errEventHandler) | ||
|  |             resolve(logServerAddress()) | ||
|  |           }) | ||
|  |           // we set it afterwards because listen can throw
 | ||
|  |           this[kState].listening = true | ||
|  |         }) | ||
|  | 
 | ||
|  |         return Promise.race([ | ||
|  |           errEvent, // e.g invalid port range error is always emitted before the server listening
 | ||
|  |           listen | ||
|  |         ]) | ||
|  |       }) | ||
|  |     } | ||
|  | 
 | ||
|  |     const logServerAddress = () => { | ||
|  |       let address = server.address() | ||
|  |       const isUnixSocket = typeof address === 'string' | ||
|  |       /* istanbul ignore next */ | ||
|  |       if (!isUnixSocket) { | ||
|  |         if (address.address.indexOf(':') === -1) { | ||
|  |           address = address.address + ':' + address.port | ||
|  |         } else { | ||
|  |           address = '[' + address.address + ']:' + address.port | ||
|  |         } | ||
|  |       } | ||
|  |       /* istanbul ignore next */ | ||
|  |       address = (isUnixSocket ? '' : ('http' + (this[kOptions].https ? 's' : '') + '://')) + address | ||
|  |       this.log.info('Server listening at ' + address) | ||
|  |       return address | ||
|  |     } | ||
|  | 
 | ||
|  |     if (cb === undefined) return listenPromise(listenOptions) | ||
|  | 
 | ||
|  |     this.ready(err => { | ||
|  |       if (err != null) return cb(err) | ||
|  | 
 | ||
|  |       if (this[kState].listening && this[kState].closing) { | ||
|  |         return cb(new FST_ERR_REOPENED_CLOSE_SERVER(), null) | ||
|  |       } else if (this[kState].listening) { | ||
|  |         return cb(new FST_ERR_REOPENED_SERVER(), null) | ||
|  |       } | ||
|  | 
 | ||
|  |       server.once('error', wrap) | ||
|  |       server.listen(listenOptions, wrap) | ||
|  | 
 | ||
|  |       this[kState].listening = true | ||
|  |     }) | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function http2 () { | ||
|  |   try { | ||
|  |     return require('http2') | ||
|  |   } catch (err) { | ||
|  |     throw new FST_ERR_HTTP2_INVALID_VERSION() | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function sessionTimeout (timeout) { | ||
|  |   return function (session) { | ||
|  |     session.setTimeout(timeout, close) | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function close () { | ||
|  |   this.close() | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = { createServer } |