From ffa7a87c22556eec4d2f56960baf8b56ea5d3d45 Mon Sep 17 00:00:00 2001 From: Tommy Chen Date: Sat, 31 Jan 2015 15:38:11 +0800 Subject: [PATCH] Check port availability before starting server --- .jshintrc | 1 + index.js | 6 ++-- lib/middlewares/gzip.js | 2 ++ lib/middlewares/header.js | 2 ++ lib/middlewares/logger.js | 2 ++ lib/middlewares/redirect.js | 2 ++ lib/middlewares/route.js | 2 ++ lib/middlewares/static.js | 2 ++ lib/server.js | 43 ++++++++++++++-------- package.json | 6 ++-- test/.jshintrc | 15 ++++++++ test/index.js | 71 +++++++++++++++++++++++++++---------- 12 files changed, 117 insertions(+), 37 deletions(-) create mode 100644 test/.jshintrc diff --git a/.jshintrc b/.jshintrc index 1e998ff..637bb4a 100644 --- a/.jshintrc +++ b/.jshintrc @@ -6,6 +6,7 @@ "trailing": true, "quotmark": "single", "undef": true, + "strict": true, "unused": "vars", "globals": { "Promise": true diff --git a/index.js b/index.js index d8a9031..d57df4d 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,8 @@ -var merge = require('utils-merge'); +'use strict'; -hexo.config.server = merge({ +var assign = require('object-assign'); + +hexo.config.server = assign({ port: 4000, log: false, ip: '0.0.0.0' diff --git a/lib/middlewares/gzip.js b/lib/middlewares/gzip.js index 37201ed..0e72ec0 100644 --- a/lib/middlewares/gzip.js +++ b/lib/middlewares/gzip.js @@ -1,3 +1,5 @@ +'use strict'; + var compress = require('compression'); module.exports = function(app){ diff --git a/lib/middlewares/header.js b/lib/middlewares/header.js index dab3efd..44837e9 100644 --- a/lib/middlewares/header.js +++ b/lib/middlewares/header.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = function(app){ app.use(function(req, res, next){ res.setHeader('X-Powered-By', 'Hexo'); diff --git a/lib/middlewares/logger.js b/lib/middlewares/logger.js index 5620065..6a68e2a 100644 --- a/lib/middlewares/logger.js +++ b/lib/middlewares/logger.js @@ -1,3 +1,5 @@ +'use strict'; + var morgan = require('morgan'); module.exports = function(app){ diff --git a/lib/middlewares/redirect.js b/lib/middlewares/redirect.js index 4d9db6e..55f574a 100644 --- a/lib/middlewares/redirect.js +++ b/lib/middlewares/redirect.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = function(app){ var root = this.config.root; if (root === '/') return; diff --git a/lib/middlewares/route.js b/lib/middlewares/route.js index 137a590..da1dbbc 100644 --- a/lib/middlewares/route.js +++ b/lib/middlewares/route.js @@ -1,3 +1,5 @@ +'use strict'; + var pathFn = require('path'); var mime = require('mime'); diff --git a/lib/middlewares/static.js b/lib/middlewares/static.js index bf7f17d..3b3713c 100644 --- a/lib/middlewares/static.js +++ b/lib/middlewares/static.js @@ -1,3 +1,5 @@ +'use strict'; + var serveStatic = require('serve-static'); module.exports = function(app){ diff --git a/lib/server.js b/lib/server.js index 5793b63..f20a0b4 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,11 +1,14 @@ +'use strict'; + var connect = require('connect'); var http = require('http'); var chalk = require('chalk'); var Promise = require('bluebird'); var format = require('util').format; var open = require('open'); +var net = require('net'); -function server(args){ +module.exports = function(args){ var app = connect(); var config = this.config; var ip = args.i || args.ip || config.server.ip || 'localhost'; @@ -13,16 +16,10 @@ function server(args){ var root = config.root; var self = this; - if (port > 65535 || port < 1){ - throw new Error('Port number ' + port + ' is invalid. Try a number between 1 and 65535.'); - } - - return this.extend.filter.exec('server_middleware', app, {context: this}).then(function(){ - if (args.$test_mode){ - return self.load(); - } else { - return self.watch(); - } + return checkPort(ip, port).then(function(){ + return self.extend.filter.exec('server_middleware', app, {context: self}); + }).then(function(){ + return self.watch(); }).then(function(){ return startServer(http.createServer(app), port, ip); }).then(function(server){ @@ -35,7 +32,7 @@ function server(args){ } return server; - }, function(err){ + }).catch(function(err){ switch (err.code){ case 'EADDRINUSE': self.log.fatal('Port %d has been used. Try other port instead.', port); @@ -46,9 +43,10 @@ function server(args){ break; } + self.unwatch(); throw err; }); -} +}; function startServer(server, port, ip){ return new Promise(function(resolve, reject){ @@ -60,4 +58,21 @@ function startServer(server, port, ip){ }); } -module.exports = server; +function checkPort(ip, port){ + return new Promise(function(resolve, reject){ + if (port > 65535 || port < 1){ + return reject(new Error('Port number ' + port + ' is invalid. Try a number between 1 and 65535.')); + } + + var server = net.createServer(); + + server.once('error', reject); + + server.once('listening', function(){ + server.close(); + resolve(); + }); + + server.listen(port, ip); + }); +} \ No newline at end of file diff --git a/package.json b/package.json index 9f82d7e..9346ba9 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,9 @@ "connect": "3.x", "mime": "^1.2.11", "morgan": "^1.5.0", + "object-assign": "^2.0.0", "open": "0.0.5", - "serve-static": "^1.7.1", - "utils-merge": "^1.0.0" + "serve-static": "^1.7.1" }, "devDependencies": { "chai": "^1.9.1", @@ -40,7 +40,7 @@ "gulp-jshint": "^1.8.6", "gulp-load-plugins": "^0.8.0", "gulp-mocha": "^2.0.0", - "hexo": "^3.0.0-beta.2", + "hexo": "^3.0.0-rc.2", "hexo-fs": "0.0.8", "jshint-stylish": "^1.0.0", "mocha": "^2.0.1", diff --git a/test/.jshintrc b/test/.jshintrc new file mode 100644 index 0000000..beaaa4b --- /dev/null +++ b/test/.jshintrc @@ -0,0 +1,15 @@ +{ + "eqnull": true, + "expr": true, + "indent": 2, + "node": true, + "mocha": true, + "trailing": true, + "quotmark": "single", + "undef": true, + "strict": true, + "unused": "vars", + "globals": { + "Promise": true + } +} \ No newline at end of file diff --git a/test/index.js b/test/index.js index 5259be6..45cac4c 100644 --- a/test/index.js +++ b/test/index.js @@ -1,9 +1,12 @@ +'use strict'; + var should = require('chai').should(); var Hexo = require('hexo'); var request = require('supertest'); var pathFn = require('path'); var fs = require('hexo-fs'); var Promise = require('bluebird'); +var net = require('net'); describe('server', function(){ var hexo = new Hexo(pathFn.join(__dirname, 'server_test'), {silent: true}); @@ -43,19 +46,27 @@ describe('server', function(){ }); }); + afterEach(function(){ + return hexo.unwatch(); + }); + after(function(){ return fs.rmdir(hexo.base_dir); }); function stopServer(app, callback){ return function(err){ - if (err) return callback(err); - app.close(callback); + app.close(function(err_){ + if (err) return callback(err); + if (err_) return callback(err_); + + callback(); + }); }; } it('X-Powered-By header', function(done){ - server({$test_mode: true}).then(function(app){ + server({}).then(function(app){ request('http://localhost:4000').get('/') .expect('X-Powered-By', 'Hexo') .expect(200, 'index', stopServer(app, done)); @@ -63,7 +74,7 @@ describe('server', function(){ }); it('Content-Type header', function(done){ - server({$test_mode: true}).then(function(app){ + server({}).then(function(app){ request('http://localhost:4000').get('/bar.jpg') .expect('Content-Type', 'image/jpeg') .end(stopServer(app, done)); @@ -72,7 +83,7 @@ describe('server', function(){ it('static asset', function(done){ fs.writeFile(pathFn.join(hexo.public_dir, 'test.html'), 'test html').then(function(){ - server({$test_mode: true}).then(function(app){ + server({}).then(function(app){ request('http://localhost:4000').get('/test.html') .expect('Content-Type', 'text/html; charset=UTF-8') .expect(200, 'test html', stopServer(app, done)); @@ -81,21 +92,45 @@ describe('server', function(){ }); it('invalid port', function(){ - try { - server({port: -100}); - } catch (err){ + return server({port: -100}).catch(function(err){ err.should.have.property('message', 'Port number -100 is invalid. Try a number between 1 and 65535.'); - } + }); + }); + + it('invalid port > 65535', function(){ + return server({port: 65536}).catch(function(err){ + err.should.have.property('message', 'Port number 65536 is invalid. Try a number between 1 and 65535.'); + }); + }); - try { - server({port: 70000}); - } catch (err){ - err.should.have.property('message', 'Port number 70000 is invalid. Try a number between 1 and 65535.'); - } + it('change port setting', function(done){ + server({port: 5000}).then(function(app){ + request('http://localhost:5000').get('/') + .expect(200, 'index', stopServer(app, done)); + }); + }); + + it('check port before starting', function(callback){ + var s = net.createServer(); + + s.listen(4000, function(){ + server({}).catch(function(err){ + err.code.should.eql('EADDRINUSE'); + callback(); + }).finally(function(){ + s.close(); + }); + }); + }); + + it('change ip setting', function(){ + return server({ip: '1.2.3.4'}).catch(function(err){ + err.code.should.eql('EADDRNOTAVAIL'); + }); }); it('append trailing slash', function(done){ - server({$test_mode: true}).then(function(app){ + server({}).then(function(app){ request('http://localhost:4000').get('/foo') .expect('Location', '/foo/') .expect(302, 'Redirecting', stopServer(app, done)); @@ -103,14 +138,14 @@ describe('server', function(){ }); it('don\'t append trailing slash if URL has a extension name', function(done){ - server({$test_mode: true}).then(function(app){ + server({}).then(function(app){ request('http://localhost:4000').get('/bar.txt') .expect(404, stopServer(app, done)); }); }); it('only send headers on HEAD request', function(done){ - server({$test_mode: true}).then(function(app){ + server({}).then(function(app){ request('http://localhost:4000').head('/') .expect(200, '', stopServer(app, done)); }); @@ -119,7 +154,7 @@ describe('server', function(){ it('redirect to root URL if root is not `/`', function(done){ hexo.config.root = '/test/'; - server({$test_mode: true}).then(function(app){ + server({}).then(function(app){ hexo.config.root = '/'; request('http://localhost:4000').get('/')