UNPKG

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