1
1
import { readFile } from 'fs'
2
2
import { createServer as createHttpsServer } from 'https'
3
3
import { createServer } from 'http'
4
- import { resolve } from 'path'
4
+ import { resolve , posix } from 'path'
5
5
6
6
import mime from 'mime'
7
7
import opener from 'opener'
@@ -23,9 +23,16 @@ function serve (options = { contentBase: '' }) {
23
23
options . openPage = options . openPage || ''
24
24
mime . default_type = 'text/plain'
25
25
26
+ if ( options . mimeTypes ) {
27
+ mime . define ( options . mimeTypes , true )
28
+ }
29
+
26
30
const requestListener = ( request , response ) => {
27
31
// Remove querystring
28
- const urlPath = decodeURI ( request . url . split ( '?' ) [ 0 ] )
32
+ const unsafePath = decodeURI ( request . url . split ( '?' ) [ 0 ] )
33
+
34
+ // Don't allow path traversal
35
+ const urlPath = posix . normalize ( unsafePath )
29
36
30
37
Object . keys ( options . headers ) . forEach ( ( key ) => {
31
38
response . setHeader ( key , options . headers [ key ] )
@@ -72,11 +79,10 @@ function serve (options = { contentBase: '' }) {
72
79
server = createServer ( requestListener ) . listen ( options . port , options . host )
73
80
}
74
81
75
- // assemble url for error and info messages
76
- const protocol = ( options . https ? 'https' : 'http' )
77
- const hostname = options . host || 'localhost'
78
- const url = protocol + '://' + hostname + ':' + options . port
82
+ // Assemble url for error and info messages
83
+ const url = ( options . https ? 'https' : 'http' ) + '://' + ( options . host || 'localhost' ) + ':' + options . port
79
84
85
+ // Handle common server errors
80
86
server . on ( 'error' , e => {
81
87
if ( e . code === 'EADDRINUSE' ) {
82
88
console . error ( url + ' is in use, either stop the other server or use a different port.' )
@@ -86,18 +92,20 @@ function serve (options = { contentBase: '' }) {
86
92
}
87
93
} )
88
94
89
- let running = options . verbose === false
95
+ let first = true
90
96
91
97
return {
92
98
name : 'serve' ,
93
99
generateBundle ( ) {
94
- if ( ! running ) {
95
- running = true
100
+ if ( first ) {
101
+ first = false
96
102
97
103
// Log which url to visit
98
- options . contentBase . forEach ( base => {
99
- console . log ( green ( url ) + ' -> ' + resolve ( base ) )
100
- } )
104
+ if ( options . verbose !== false ) {
105
+ options . contentBase . forEach ( base => {
106
+ console . log ( green ( url ) + ' -> ' + resolve ( base ) )
107
+ } )
108
+ }
101
109
102
110
// Open browser
103
111
if ( options . open ) {
@@ -147,7 +155,7 @@ function green (text) {
147
155
return '\u001b[1m\u001b[32m' + text + '\u001b[39m\u001b[22m'
148
156
}
149
157
150
- function closeServerOnTermination ( ) {
158
+ function closeServerOnTermination ( ) {
151
159
const terminationSignals = [ 'SIGINT' , 'SIGTERM' , 'SIGQUIT' , 'SIGHUP' ]
152
160
terminationSignals . forEach ( signal => {
153
161
process . on ( signal , ( ) => {
0 commit comments