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
						
					
					
				| '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 }
 |