UNPKG

6.19 kBtext/coffeescriptView Raw
1path = require("path")
2fancyLog = require("fancy-log")
3colors = require("ansi-colors")
4mapStream = require("map-stream")
5http = require("http")
6https = require("https")
7fs = require("fs")
8connect = require("connect")
9serveStatic = require('serve-static')
10serveIndex = require('serve-index')
11liveReload = require("connect-livereload")
12send = require("send")
13tiny_lr = require("tiny-lr")
14apps = []
15
16class ConnectApp
17 constructor: (options, startedCallback) ->
18 @name = options.name || "Server"
19 @port = options.port || "8080"
20 @root = options.root || path.dirname(module.parent.id)
21 @host = options.host || "localhost"
22 @debug = options.debug || false
23 @silent = options.silent || false
24 @https = options.https || false
25 @preferHttp1 = options.preferHttp1 || false;
26 @livereload = options.livereload || false
27 @middleware = options.middleware || undefined
28 @startedCallback = startedCallback || () -> {};
29 @serverInit = options.serverInit || undefined
30 @fallback = options.fallback || undefined
31 @index = options.index
32 @oldMethod("open") if options.open
33 @sockets = []
34 @app = undefined
35 @lr = undefined
36 @state = "initializing"
37 @run()
38
39 run: ->
40 if @state == "stopped"
41 return
42
43 @state = "starting"
44 @log "Starting server..."
45 @app = connect()
46
47 @handlers().forEach (middleware) =>
48 if typeof (middleware) is "object"
49 @app.use middleware[0], middleware[1]
50 else
51 @app.use middleware
52
53 @app.use serveIndex(if typeof @root == "object" then @root[0] else @root)
54
55 if @https
56
57 # use some defaults when not set. do not touch when a key is already specified
58 # see https://github.com/AveVlad/gulp-connect/issues/172
59 if typeof (@https) is 'boolean' || (!@https.key && !@https.pfx)
60
61 # change it into an object if it is not already one
62 if !(typeof (@https) is "object")
63 @https = {}
64
65 @https.key = fs.readFileSync __dirname + '/certs/server.key'
66 @https.cert = fs.readFileSync __dirname + '/certs/server.crt'
67 @https.ca = fs.readFileSync __dirname + '/certs/server.crt'
68 @https.passphrase = 'gulp'
69
70 http2 = undefined
71 if !@preferHttp1
72 try
73 http2 = require('http2')
74
75 if http2
76 @https.allowHTTP1 = true
77 @server = http2.createSecureServer(@https, @app)
78 else
79 @server = https.createServer(@https, @app)
80
81 else
82 @server = http.createServer @app
83 if @serverInit
84 @serverInit @server
85 @server.listen @port, @host, (err) =>
86 if err
87 @log "Error on starting server: #{err}"
88 else
89 @log "#{@name} started http#{if @https then 's' else ''}://#{@host}:#{@port}"
90
91 stoped = false
92 sockets = []
93
94 @server.on "close", =>
95 if (!stoped)
96 stoped = true
97 @log "#{@name} stopped"
98
99 # Log connections and request in debug
100 @server.on "connection", (socket) =>
101 @logDebug "Received incoming connection from #{socket.address().address}"
102 @sockets.push socket
103 socket.on "close", =>
104 @sockets.splice @sockets.indexOf(socket), 1
105
106 @server.on "request", (request, response) =>
107 @logDebug "Received request #{request.method} #{request.url}"
108
109 @server.on "error", (err) =>
110 @log err.toString()
111
112 stopServer = =>
113 if (!stoped)
114 @sockets.forEach (socket) =>
115 socket.destroy()
116
117 @server.close()
118 if @livereload
119 @lr.close()
120 process.nextTick( ->
121 process.exit(0)
122 )
123
124 process.on("SIGINT", stopServer)
125 process.on("exit", stopServer)
126
127 if @livereload
128 tiny_lr.Server::error = ->
129 if @https
130 @lr = tiny_lr
131 key: @https.key || fs.readFileSync __dirname + '/certs/server.key'
132 cert: @https.cert || fs.readFileSync __dirname + '/certs/server.crt'
133 else
134 @lr = tiny_lr()
135
136 @lr.listen @livereload.port
137 @log "LiveReload started on port #{@livereload.port}"
138 @state = "running";
139 @log "Running server"
140 @startedCallback()
141
142 close: ->
143 if @state == "running"
144 @log "Stopping server"
145 if @livereload
146 @lr.close()
147 @server.close()
148 @state = "stopped"
149 @log "Stopped server"
150 else if @state == "stopped"
151 @log "Server has already been stopped."
152 else
153 @log "Ignoring stop as server is in " + @state + " state."
154
155 handlers: ->
156 steps = if @middleware then @middleware.call(this, connect, @) else []
157 if @livereload
158 @livereload = {} if typeof @livereload is "boolean"
159 @livereload.port = 35729 unless @livereload.port
160 steps.unshift liveReload(@livereload)
161 if @index is true then @index = "index.html"
162 if typeof @root == "object"
163 @root.forEach (path) ->
164 steps.push serveStatic(path, {index: @index})
165 else
166 steps.push serveStatic(@root, {index: @index})
167 if @fallback
168 steps.push (req, res) =>
169 fallbackPath = @fallback
170
171 if typeof @fallback is "function"
172 fallbackPath = @fallback(req, res)
173
174 send(req, fallbackPath).pipe(res);
175
176 return steps
177
178 log: (text) ->
179 if !@silent
180 fancyLog colors.green(text)
181
182 logWarning: (text) ->
183 if !@silent
184 fancyLog colors.yellow(text)
185
186 logDebug: (text) ->
187 if @debug
188 fancyLog colors.blue(text)
189
190 oldMethod: (type) ->
191 text = 'does not work in gulp-connect v 2.*. Please read "readme" https://github.com/AveVlad/gulp-connect'
192 switch type
193 when "open" then @logWarning("Option open #{text}")
194
195module.exports =
196 server: (options = {}, startedCallback = null) ->
197 app = new ConnectApp(options, startedCallback)
198 apps.push(app)
199 app
200 reload: ->
201 mapStream (file, callback) ->
202 apps.forEach (app) =>
203 if app.livereload and typeof app.lr == "object"
204 app.lr.changed body:
205 files: file.path
206 callback null, file
207 serverClose: ->
208 apps.forEach((app) -> do app.close)
209 apps = []