Skip to content
This repository has been archived by the owner on Oct 10, 2020. It is now read-only.

Commit

Permalink
bweb: improve file serving. add cookie parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
chjj committed Dec 28, 2017
1 parent 8d94431 commit f557414
Show file tree
Hide file tree
Showing 13 changed files with 509 additions and 136 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ server.on('socket', (socket) => {
});

server.use(server.bodyParser());
server.use(server.cookieParser());
server.use(server.jsonRPC());
server.use(server.router());
server.use('/static', server.file(__dirname));
server.use('/static', server.fileServer(__dirname));

server.get('/', async (req, res) => {
res.html(200, '<a href="/static">static</a>');
Expand Down
10 changes: 9 additions & 1 deletion lib/hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@

const assert = require('assert');

/**
* Hook
*/

class Hook {
/**
* Hook
* Create a hook.
* @constructor
* @ignore
*/
Expand Down Expand Up @@ -39,4 +43,8 @@ class Hook {
}
}

/*
* Expose
*/

module.exports = Hook;
98 changes: 42 additions & 56 deletions lib/middleware/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,18 @@
const assert = require('assert');
const fs = require('fs');
const Path = require('path');
const mime = require('../mime');

/**
* Static file middleware.
* @param {String} prefix
* @returns {Function}
*/

function file(prefix) {
function fileServer(prefix) {
assert(typeof prefix === 'string');

return async (req, res) => {
if (req.method !== 'GET')
if (req.method !== 'GET' && req.method !== 'HEAD')
return;

const file = Path.join(prefix, req.pathname);
Expand All @@ -43,33 +42,11 @@ function file(prefix) {
return;
}

if (!stat.isFile()) {
const err = new Error('Cannot access file.');
err.statusCode = 403;
throw err;
try {
await res.sendFile(file);
} catch (e) {
throw wrapError(e);
}

res.setStatus(200);
res.setType(mime.file(file));
res.setLength(stat.size);

const stream = fs.createReadStream(file);

stream.on('error', (err) => {
try {
stream.destroy();
} catch (e) {
;
}

try {
res.destroy();
} catch (e) {
;
}
});

res.read(stream);
};
}

Expand Down Expand Up @@ -102,37 +79,46 @@ function fsReaddir(file) {
}

function wrapError(e) {
if (e.code === 'ENOENT') {
const err = new Error('File not found.');
err.code = e.code;
err.statusCode = 404;
return err;
}

if (e.code === 'EACCES' || e.code === 'EPERM') {
const err = new Error('Cannot access file.');
err.code = e.code;
err.statusCode = 403;
return err;
}

if (e.code === 'EMFILE') {
const err = new Error('Too many open files.');
err.code = e.code;
if (!e.code) {
const err = new Error('Internal server error.');
err.statusCode = 500;
return err;
}

if (e.code) {
const err = new Error('Cannot access file.');
err.code = e.code;
err.statusCode = 400;
return err;
switch (e.code) {
case 'ENOENT':
case 'ENAMETOOLONG':
case 'ENOTDIR':
case 'EISDIR': {
const err = new Error('File not found.');
err.code = e.code;
err.syscall = e.syscall;
err.statusCode = 404;
return err;
}
case 'EACCES':
case 'EPERM': {
const err = new Error('Cannot access file.');
err.code = e.code;
err.syscall = e.syscall;
err.statusCode = 403;
return err;
}
case 'EMFILE': {
const err = new Error('Too many open files.');
err.code = e.code;
err.syscall = e.syscall;
err.statusCode = 500;
return err;
}
default: {
const err = new Error('Cannot access file.');
err.code = e.code;
err.syscall = e.syscall;
err.statusCode = 500;
return err;
}
}

const err = new Error('Internal server error.');
err.statusCode = 500;
return err;
}

function escapeHTML(html) {
Expand Down Expand Up @@ -198,4 +184,4 @@ async function dir2html(parent, title, prefix) {
* Expose
*/

module.exports = file;
module.exports = fileServer;
3 changes: 2 additions & 1 deletion lib/middleware/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

exports.basicAuth = require('./basicauth');
exports.bodyParser = require('./bodyparser');
exports.cookieParser = require('./cookie');
exports.cors = require('./cors');
exports.file = require('./file');
exports.fileServer = require('./file');
exports.jsonRPC = require('./jsonrpc');
exports.router = require('./router');
112 changes: 78 additions & 34 deletions lib/mime.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,60 +9,104 @@
const assert = require('assert');

const types = {
'json': ['application/json', true],
'form': ['application/x-www-form-urlencoded', true],
'html': ['text/html', true],
'xhtml': ['application/xhtml+xml', true],
'xml': ['application/xml', true],
'js': ['application/javascript', true],
'css': ['text/css', true],
'txt': ['text/plain', true],
'md': ['text/plain', true],
'atom': ['application/atom+xml', true],
'bin': ['application/octet-stream', false],
'bmp': ['image/bmp', false],
'css': ['text/css', true],
'dat': ['application/octet-stream', false],
'form': ['application/x-www-form-urlencoded', true],
'gif': ['image/gif', false],
'gz': ['application/x-gzip', false],
'htc': ['text/x-component', true],
'html': ['text/html', true],
'ico': ['image/x-icon', false],
'jpg': ['image/jpeg', false],
'jpeg': ['image/jpeg', false],
'js': ['application/javascript', true],
'json': ['application/json', true],
'log': ['text/plain', true],
'manifest': ['text/cache-manifest', false],
'mathml': ['application/mathml+xml', true],
'md': ['text/plain', true],
'mkv': ['video/x-matroska', false],
'mml': ['application/mathml+xml', true],
'mp3': ['audio/mpeg', false],
'mp4': ['video/mp4', false],
'mpeg': ['video/mpeg', false],
'mpg': ['video/mpeg', false],
'oga': ['audio/ogg', false],
'ogg': ['application/ogg', false],
'ogv': ['video/ogg', false],
'otf': ['font/otf', false],
'pdf': ['application/pdf', false],
'png': ['image/png', false],
'ico': ['image/x-icon', false],
'rdf': ['application/rdf+xml', true],
'rss': ['application/rss+xml', true],
'svg': ['image/svg+xml', false],
'pdf': ['application/pdf', false],
'swf': ['application/x-shockwave-flash', false],
'tar': ['application/x-tar', false],
'torrent': ['application/x-bittorrent', false],
'txt': ['text/plain', true],
'ttf': ['font/ttf', false],
'wav': ['audio/wav', false],
'mp3': ['audio/mpeg', false],
'ogg': ['audio/ogg', false],
'mp4': ['video/mp4', false],
'mkv': ['video/x-matroska', false],
'webm': ['video/webm', false]
'webm': ['video/webm', false],
'woff': ['font/x-woff', false],
'xhtml': ['application/xhtml+xml', true],
'xbl': ['application/xml', true],
'xml': ['application/xml', true],
'xsl': ['application/xml', true],
'xslt': ['application/xslt+xml', true],
'zip': ['application/zip', false]
};

const extensions = {
'text/x-json': 'json',
'application/json': 'json',
'application/atom+xml': 'atom',
'application/octet-stream': 'bin',
'image/bmp': 'bmp',
'text/css': 'css',
'application/x-www-form-urlencoded': 'form',
'image/gif': 'gif',
'application/x-gzip': 'gz',
'text/x-component': 'htc',
'text/html': 'html',
'application/xhtml+xml': 'xhtml',
'image/x-icon': 'ico',
'image/jpeg': 'jpeg',
'text/javascript': 'js',
'application/javascript': 'js',
'text/css': 'css',
'text/x-json': 'json',
'application/json': 'json',
'text/plain': 'txt',
'application/octet-stream': 'bin',
'image/gif': 'gif',
'image/jpeg': 'jpg',
'image/png': 'png',
'image/x-icon': 'ico',
'image/svg+xml': 'svg',
'text/cache-manifest': 'manifest',
'application/mathml+xml': 'mml',
'video/x-matroska': 'mkv',
'audio/x-matroska': 'mkv',
'audio/mpeg': 'mp3',
'audio/mpa': 'mp3',
'video/mp4': 'mp4',
'video/mpeg': 'mpg',
'audio/ogg': 'oga',
'application/ogg': 'ogg',
'video/ogg': 'ogv',
'font/otf': 'otf',
'application/pdf': 'pdf',
'application/x-pdf': 'pdf',
'image/png': 'png',
'application/rdf+xml': 'rdf',
'application/rss+xml': 'rss',
'image/svg+xml': 'svg',
'application/x-shockwave-flash': 'swf',
'application/x-tar': 'tar',
'application/x-bittorrent': 'torrent',
'font/ttf': 'ttf',
'audio/wav': 'wav',
'audio/wave': 'wav',
'audio/mpeg': 'mp3',
'audio/mpa': 'mp3',
'audio/ogg': 'ogg',
'video/ogg': 'ogg',
'video/mp4': 'mp4',
'video/x-matroska': 'mkv',
'audio/x-matroska': 'mkv',
'video/webm': 'webm',
'audio/webm': 'webm',
'video/webm': 'webm'
'font/x-woff': 'woff',
'application/xhtml+xml': 'xhtml',
'application/xml': 'xsl',
'application/xslt+xml': 'xslt',
'application/zip': 'zip'
};

// Filename to extension
Expand Down
14 changes: 12 additions & 2 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ const EventEmitter = require('events');
const mime = require('./mime');
const {parseURL} = require('./util');

/**
* Request
*/

class Request extends EventEmitter {
/**
* Request
* Create a request.
* @constructor
* @ignore
*/
Expand All @@ -35,10 +39,12 @@ class Request extends EventEmitter {
this.query = Object.create(null);
this.params = Object.create(null);
this.body = Object.create(null);
this.cookies = Object.create(null);
this.hasBody = false;
this.username = null;
this.readable = true;
this.writable = false;
this.admin = false;
this.wallet = null;
this.init(req, res, url);
}

Expand Down Expand Up @@ -117,4 +123,8 @@ class Request extends EventEmitter {
}
}

/*
* Expose
*/

module.exports = Request;
Loading

0 comments on commit f557414

Please sign in to comment.