Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push state option #194

Closed
wants to merge 11 commits into from
Closed
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ This will install `http-server` globally so that it may be run from the command

`-P` or `--proxy` Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com

`--push-state` Enable HTML5 push state mode where all URIs respond with index.html. (defaults to 'False')

`-S` or `--ssl` Enable https.

`-C` or `--cert` Path to ssl cert file (default: cert.pem).
Expand Down
14 changes: 13 additions & 1 deletion bin/http-server
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ if (argv.h || argv.help) {
' -U --utc Use UTC time format in log messages.',
'',
' -P --proxy Fallback proxy if the request cannot be resolved. e.g.: http://someurl.com',
'',
' --push-state Enable HTML5 push state mode where all URIs respond with index.html. [false]',
' -S --ssl Enable https.',
' -C --cert Path to ssl cert file (default: cert.pem).',
' -K --key Path to ssl key file (default: key.pem).',
Expand Down Expand Up @@ -101,6 +101,14 @@ function listen(port) {
proxy: proxy
};

if (argv['push-state']) {
options.pushState = true;

if (options.proxy || options.autoIndex) {
logger.info('Proxy and Auto Indexing are not supported with Push State.'.red);
}
}

if (argv.cors) {
options.cors = true;
}
Expand Down Expand Up @@ -135,6 +143,10 @@ function listen(port) {
logger.info('Unhandled requests will be served from: ' + proxy);
}

if (options.pushState) {
logger.info('HTML5 Push State is enabled, all URLs will be changed to the index page.');
}

logger.info('Hit CTRL-C to stop the server');
if (argv.o) {
opener(
Expand Down
28 changes: 26 additions & 2 deletions lib/http-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function HttpServer(options) {
this.showDir = options.showDir !== 'false';
this.autoIndex = options.autoIndex !== 'false';
this.contentType = options.contentType || 'application/octet-stream';
this.pushState = options.pushState || false;

if (options.ext) {
this.ext = options.ext === true
Expand Down Expand Up @@ -86,15 +87,17 @@ function HttpServer(options) {
});
}

before.push(ecstatic({
var ecstaticMiddleware = ecstatic({
root: this.root,
cache: this.cache,
showDir: this.showDir,
autoIndex: this.autoIndex,
defaultExt: this.ext,
contentType: this.contentType,
handleError: typeof options.proxy !== 'string'
}));
});

before.push(ecstaticMiddleware);

if (typeof options.proxy === 'string') {
var proxy = httpProxy.createProxyServer({});
Expand All @@ -106,6 +109,27 @@ function HttpServer(options) {
});
}

if (options.pushState) {
// When push state is enabled, rewrite all the URLs to the index page if the status code is a 404.
before.push(function (req, res, next) {
var currentURL = req.url;
var indexPage = '/index.html';

// Only add paramters when they exist.
var parameterStartIndex = currentURL.indexOf('?');
if (parameterStartIndex >= 0) {
var currentParameters = currentURL.slice(parameterStartIndex);

req.url = indexPage + currentParameters;
}
else {
req.url = indexPage;
}

ecstaticMiddleware(req, res, next);
});
}

var serverOptions = {
before: before,
headers: this.headers,
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/pushStateRoot/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// JavaScript File Exists
1 change: 1 addition & 0 deletions test/fixtures/pushStateRoot/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pushState Enabled.
53 changes: 53 additions & 0 deletions test/http-server-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,58 @@ vows.describe('http-server').addBatch({
assert.equal(res.statusCode, 204);
}
}
},
'When push-state is enabled': {
topic: function () {
var pushStateRoot = path.join(__dirname, 'fixtures', 'pushStateRoot');

var server = httpServer.createServer({
root: pushStateRoot,
pushState: true
});
// NOTE: using 8083 because the 808[1-2] are both active and not yet shutdown.
server.listen(8083);

this.callback(null, server);
},
'and a non-existant file is requested': {
topic: function () {
request('http://127.0.0.1:8083/404', this.callback);
},
'status code should be 200': function (res) {
assert.equal(res.statusCode, 200);
},
'and file content': {
topic: function (res, body) {
var self = this;
var pushStateRoot = path.join(__dirname, 'fixtures', 'pushStateRoot');

fs.readFile(path.join(pushStateRoot, 'index.html'), 'utf8', function (err, data) {
self.callback(err, data, body);
});
},
'should match content of served file': function (err, file, body) {
assert.equal(body.trim(), file.trim());
}
}
},
'and a file exists': {
topic: function () {
request('http://127.0.0.1:8083/app.js', this.callback);
},
'and file content': {
topic: function (res, body) {
var self = this;
var pushStateRoot = path.join(__dirname, 'fixtures', 'pushStateRoot');

fs.readFile(path.join(pushStateRoot, 'app.js'), 'utf8', function (err, data) {
self.callback(err, data, body);
});
},
'should match content of served file': function (err, file, body) {
assert.equal(body.trim(), file.trim());
}
}
}
}
}).export(module);