UNPKG

4.81 kBJavaScriptView Raw
1'use strict'
2
3const assert = require('assert')
4const http = require('http')
5const https = require('https')
6
7const { kState, kOptions } = require('./symbols')
8const {
9 codes: { FST_ERR_HTTP2_INVALID_VERSION }
10} = require('./errors')
11
12function createServer (options, httpHandler) {
13 assert(options, 'Missing options')
14 assert(httpHandler, 'Missing http handler')
15
16 var server = null
17 if (options.serverFactory) {
18 server = options.serverFactory(httpHandler, options)
19 } else if (options.https) {
20 if (options.http2) {
21 server = http2().createSecureServer(options.https, httpHandler)
22 } else {
23 server = https.createServer(options.https, httpHandler)
24 }
25 } else if (options.http2) {
26 server = http2().createServer(httpHandler)
27 server.on('session', sessionTimeout(options.http2SessionTimeout))
28 } else {
29 server = http.createServer(httpHandler)
30 }
31
32 return { server, listen }
33
34 // `this` is the Fastify object
35 function listen () {
36 const normalizeListenArgs = (args) => {
37 if (args.length === 0) {
38 return { port: 0, host: 'localhost' }
39 }
40
41 const cb = typeof args[args.length - 1] === 'function' ? args.pop() : undefined
42 const options = { cb: cb }
43
44 const firstArg = args[0]
45 const argsLength = args.length
46 const lastArg = args[argsLength - 1]
47 /* Deal with listen (options) || (handle[, backlog]) */
48 if (typeof firstArg === 'object' && firstArg !== null) {
49 options.backlog = argsLength > 1 ? lastArg : undefined
50 Object.assign(options, firstArg)
51 } else if (typeof firstArg === 'string' && isNaN(firstArg)) {
52 /* Deal with listen (pipe[, backlog]) */
53 options.path = firstArg
54 options.backlog = argsLength > 1 ? lastArg : undefined
55 } else {
56 /* Deal with listen ([port[, host[, backlog]]]) */
57 options.port = argsLength >= 1 && firstArg ? firstArg : 0
58 // This will listen to what localhost is.
59 // It can be 127.0.0.1 or ::1, depending on the operating system.
60 // Fixes https://github.com/fastify/fastify/issues/1022.
61 options.host = argsLength >= 2 && args[1] ? args[1] : 'localhost'
62 options.backlog = argsLength >= 3 ? args[2] : undefined
63 }
64
65 return options
66 }
67
68 const listenOptions = normalizeListenArgs(Array.from(arguments))
69 const cb = listenOptions.cb
70
71 const wrap = err => {
72 server.removeListener('error', wrap)
73 if (!err) {
74 const address = logServerAddress()
75 cb(null, address)
76 } else {
77 this[kState].listening = false
78 cb(err, null)
79 }
80 }
81
82 const listenPromise = (listenOptions) => {
83 if (this[kState].listening) {
84 return Promise.reject(new Error('Fastify is already listening'))
85 }
86
87 return this.ready().then(() => {
88 var errEventHandler
89 var errEvent = new Promise((resolve, reject) => {
90 errEventHandler = (err) => {
91 this[kState].listening = false
92 reject(err)
93 }
94 server.once('error', errEventHandler)
95 })
96 var listen = new Promise((resolve, reject) => {
97 server.listen(listenOptions, () => {
98 server.removeListener('error', errEventHandler)
99 resolve(logServerAddress())
100 })
101 // we set it afterwards because listen can throw
102 this[kState].listening = true
103 })
104
105 return Promise.race([
106 errEvent, // e.g invalid port range error is always emitted before the server listening
107 listen
108 ])
109 })
110 }
111
112 const logServerAddress = () => {
113 var address = server.address()
114 const isUnixSocket = typeof address === 'string'
115 if (!isUnixSocket) {
116 if (address.address.indexOf(':') === -1) {
117 address = address.address + ':' + address.port
118 } else {
119 address = '[' + address.address + ']:' + address.port
120 }
121 }
122 address = (isUnixSocket ? '' : ('http' + (this[kOptions].https ? 's' : '') + '://')) + address
123 this.log.info('Server listening at ' + address)
124 return address
125 }
126
127 if (cb === undefined) return listenPromise(listenOptions)
128
129 this.ready(err => {
130 if (err != null) return cb(err)
131
132 if (this[kState].listening) {
133 return cb(new Error('Fastify is already listening'), null)
134 }
135
136 server.once('error', wrap)
137 server.listen(listenOptions, wrap)
138
139 this[kState].listening = true
140 })
141 }
142}
143
144function http2 () {
145 try {
146 return require('http2')
147 } catch (err) {
148 throw new FST_ERR_HTTP2_INVALID_VERSION()
149 }
150}
151
152function sessionTimeout (timeout) {
153 return function (session) {
154 session.setTimeout(timeout, close)
155 }
156}
157
158function close () {
159 this.close()
160}
161
162module.exports = { createServer }