Skip to content

Commit

Permalink
Merge pull request #111 from curbengh/trailing-html
Browse files Browse the repository at this point in the history
feat: support page with trailing '.html'
  • Loading branch information
curbengh committed Jul 1, 2020
2 parents ee2805b + 96f02dd commit be420d1
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 34 deletions.
54 changes: 44 additions & 10 deletions lib/middlewares/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,55 @@ module.exports = function(app) {

if (args.s || args.static) return;

const { pretty_urls } = config;
const { trailing_index, trailing_html } = pretty_urls ? pretty_urls : {};

app.use(root, (req, res, next) => {
const { method } = req;
const { method, url: requestUrl } = req;
if (method !== 'GET' && method !== 'HEAD') return next();

let url = route.format(decodeURIComponent(req.url));
const data = route.get(url);
const extname = pathFn.extname(url);
let url = route.format(decodeURIComponent(requestUrl));
let data = route.get(url);
let extname = pathFn.extname(url);

// When the URL is `foo/index.html` but users access `foo`, redirect to `foo/`.
if (!data) {
if (extname) return next();

url = encodeURI(url);
res.statusCode = 302;
res.setHeader('Location', `${root + url}/`);
if (route.get(url + '.html')) {
// location `foo/bar.html`; request `foo/bar`; proxy to the location
extname = '.html';
data = route.get(url + extname);
res.setHeader('Content-Type', 'text/html');
req.url = encodeURI('/' + url + extname);
data.pipe(res).on('error', next);
return;
} else if (route.get(url + '/index.html')) {
// location `foo/index.html`; request `foo`; redirect to `foo/`
url = encodeURI(url);
res.statusCode = 301;
res.setHeader('Location', `${root + url}/`);
res.end('Redirecting');
return;
} else if (route.get(url.replace(/\/index\.html$/i, '') + '.html')) {
// location `foo/bar.html`; request `foo/bar/`; redirect to `foo`
// request with trailing slash is appended with index.html by route.format()
url = encodeURI(url.replace(/\/index\.html$/i, ''));
res.statusCode = 301;
res.setHeader('Location', root + url);
res.end('Redirecting');
return;
} return next();
}
if (trailing_html === false && !requestUrl.endsWith('/index.html') && requestUrl.endsWith('.html')) {
// location `foo/bar.html`; request `foo/bar.html`; redirect to `foo/bar`
url = encodeURI(url.replace(/\.html$/i, ''));
res.statusCode = 301;
res.setHeader('Location', root + url);
res.end('Redirecting');
return;
} else if (trailing_index === false && requestUrl.endsWith('/index.html')) {
// location `foo/index.html`; request `foo/index.html`; redirect to `foo/`
url = encodeURI(url.replace(/index\.html$/i, ''));
res.statusCode = 301;
res.setHeader('Location', root + url);
res.end('Redirecting');
return;
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"eslint-config-hexo": "^4.0.0",
"hexo": "^4.0.0",
"hexo-fs": "^3.0.1",
"hexo-util": "^2.1.0",
"mocha": "^8.0.1",
"nyc": "^15.0.0",
"sinon": "^9.0.2",
Expand Down
96 changes: 72 additions & 24 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,27 @@ const fs = require('hexo-fs');
const Promise = require('bluebird');
const { v4: uuidv4 } = require('uuid');
const sinon = require('sinon');
const { deepMerge } = require('hexo-util');

describe('server', () => {
const hexo = new Hexo(join(__dirname, 'server_test'), {silent: true});
const themeDir = join(hexo.base_dir, 'themes', 'test');

const defaultCfg = deepMerge(hexo.config, {
server: {
port: 4000,
log: false,
ip: '0.0.0.0',
compress: false,
header: true
}
});
const server = require('../lib/server').bind(hexo);

// Default config
hexo.config.server = {
port: 4000,
log: false,
ip: '0.0.0.0',
compress: false,
header: true
};

// Register fake generator
hexo.extend.generator.register('test', () => [
{path: '', data: 'index'},
{path: 'foo/', data: 'foo'},
{path: 'index.html', data: 'index'},
{path: 'foo/index.html', data: 'foo'},
{path: 'bar/baz.html', data: 'baz'},
{path: 'bar.jpg', data: 'bar'}
]);

Expand All @@ -46,6 +47,10 @@ describe('server', () => {
fs.writeFile(hexo.config_path, 'theme: test')
]).then(() => hexo.init()));

beforeEach(() => {
hexo.config = deepMerge(hexo.config, defaultCfg);
});

afterEach(() => hexo.unwatch());

after(() => fs.rmdir(hexo.base_dir));
Expand Down Expand Up @@ -90,9 +95,7 @@ describe('server', () => {
.expect(200)
.then(res => {
res.headers.should.not.have.property('x-powered-by');
})).finally(() => {
hexo.config.server.header = true;
});
}));
});

it('Content-Type header', () => Promise.using(prepareServer(), app => request(app).get('/bar.jpg')
Expand All @@ -105,9 +108,7 @@ describe('server', () => {
return Promise.using(
prepareServer(),
app => request(app).get('/').expect('Content-Encoding', 'gzip')
).finally(() => {
hexo.config.server.compress = false;
});
);
});

it('Disable compression if options.compress is false', () => Promise.using(prepareServer(), app => request(app).get('/')
Expand All @@ -134,9 +135,58 @@ describe('server', () => {

it('change ip setting', () => server({ip: '1.2.3.4'}).should.rejected.and.eventually.have.property('code', 'EADDRNOTAVAIL'));

it('append trailing slash', () => Promise.using(prepareServer(), app => request(app).get('/foo')
.expect('Location', '/foo/')
.expect(302, 'Redirecting')));
// location `bar/baz.html`; request `bar/baz`; proxy to the location
it('proxy to .html if available', () => {
return Promise.using(prepareServer(), app => request(app).get('/bar/baz')
.expect(200)
.expect('Content-Type', 'text/html'));
});

// location `foo/index.html`; request `foo`; redirect to `foo/`
it('append trailing slash', () => {
return Promise.using(prepareServer(), app => request(app).get('/foo')
.expect(301, 'Redirecting')
.expect('Location', '/foo/'));
});

// location `bar/baz.html`; request `bar/baz/`; redirect to `bar/baz`
it('redirects to valid path if available', () => {
return Promise.using(prepareServer(), app => request(app).get('/bar/baz/')
.expect(301, 'Redirecting')
.expect('Location', '/bar/baz'));
});

it('trailing_html (default) - no redirect', () => {
return Promise.using(prepareServer(), app => request(app).get('/bar/baz.html')
.expect(200)
.expect('Content-Type', 'text/html'));
});

// location `bar/baz.html`; request `bar/baz.html`; redirect to `bar/baz`
it('trailing_html (false) - redirect when available', () => {
hexo.config.pretty_urls.trailing_html = false;

return Promise.using(prepareServer(), app => request(app).get('/bar/baz.html')
.expect(301, 'Redirecting')
.expect('Location', '/bar/baz'));
});

// location `foo/index.html`; request `foo/index.html`; redirect to `foo/`
it('trailing_index (default) - no redirect', () => {

return Promise.using(prepareServer(), app => request(app).get('/foo/index.html')
.expect(200)
.expect('Content-Type', 'text/html'));
});

// location `foo/index.html`; request `foo/index.html`; redirect to `foo/`
it('trailing_index (false) - redirect when available', () => {
hexo.config.pretty_urls.trailing_index = false;

return Promise.using(prepareServer(), app => request(app).get('/foo/index.html')
.expect(301, 'Redirecting')
.expect('Location', '/foo/'));
});

it('don\'t append trailing slash if URL has a extension name', () => Promise.using(prepareServer(), app => request(app).get('/bar.txt')
.expect(404)));
Expand All @@ -149,9 +199,7 @@ describe('server', () => {

return Promise.using(prepareServer(), app => request(app).get('/')
.expect('Location', '/test/')
.expect(302, 'Redirecting')).finally(() => {
hexo.config.root = '/';
});
.expect(302, 'Redirecting'));
});

it('display localhost instead of 0.0.0.0', () => {
Expand Down

0 comments on commit be420d1

Please sign in to comment.