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

Mimetype #143

Merged
merged 33 commits into from May 22, 2015
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
04eaa9f
added .opml file as text fixture
dotnetCarpenter May 11, 2015
e22dc92
test mime package
dotnetCarpenter May 11, 2015
285215b
test custom mime type(s) via mime
dotnetCarpenter May 11, 2015
5966dbb
bumb version to 0.8.0 - new feature 'custom mimetypes'
dotnetCarpenter May 11, 2015
140872f
tried to test what happens if a wrong path is given to .types file bu…
dotnetCarpenter May 11, 2015
81d1820
documentation
dotnetCarpenter May 11, 2015
3a13400
fixed loading of .types file and test that ecstatic throws if path to…
dotnetCarpenter May 11, 2015
303ea14
fix alias for mime-type being wrongly lowercased before comparison
dotnetCarpenter May 11, 2015
0db475f
remove teardown function as it does not work
dotnetCarpenter May 12, 2015
71b4fa6
unit test for the new feature 'custom mime-types' - with the ultimate…
dotnetCarpenter May 12, 2015
1d5de3e
fixed change in .types has to be reflected in unit test
dotnetCarpenter May 12, 2015
1462c90
added unit test that fails - .types file location should be independe…
dotnetCarpenter May 12, 2015
84ec0b5
better do this in a point release as I'm importing ecstatic in http-s…
dotnetCarpenter May 12, 2015
80d315d
implementation and tests for reading the .types file from an arbitrar…
dotnetCarpenter May 12, 2015
813ea25
updated dependencies
dotnetCarpenter May 12, 2015
65da86d
remove default root path - it's confusing
dotnetCarpenter May 12, 2015
126a06e
updated tests to run with tap 1.0 and on windows
dotnetCarpenter May 12, 2015
e18ff2a
change test command to work with tap 1.0
dotnetCarpenter May 12, 2015
554ee19
reverting back to originale implementation for root
dotnetCarpenter May 12, 2015
20cf567
try to fix npm test on travis (it's different from windows)
dotnetCarpenter May 12, 2015
ee676d1
try to create a test command that works both on windows and linux
dotnetCarpenter May 12, 2015
ffbeac3
resolved test error due to platform differency - resolution: test for…
dotnetCarpenter May 12, 2015
617639b
fix npm test on linux (travis)
dotnetCarpenter May 12, 2015
6bcaf4c
update tap to version 1.0.3 - now we can run the test cross-platform …
dotnetCarpenter May 12, 2015
7a23cef
change to npm test to what-ever @isaacs says :)
dotnetCarpenter May 12, 2015
8945400
get test to run on linux
dotnetCarpenter May 12, 2015
a680345
pass the malformed tests on linux
dotnetCarpenter May 12, 2015
b0ac06e
finish going through all of the unit tests
dotnetCarpenter May 12, 2015
367152c
testing isaacs/node-tap#128 on travis
dotnetCarpenter May 12, 2015
d03c691
cleanup
dotnetCarpenter May 12, 2015
51414b2
cleanup functional test and removed side effects (creation of weird f…
dotnetCarpenter May 13, 2015
1b49d5d
@jfhbrook what do you think about the handleError option? Should we n…
dotnetCarpenter May 13, 2015
4356c6e
lets speed things up and prepare for ecma6
dotnetCarpenter May 13, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions ChangeLog.md
@@ -1,3 +1,12 @@
2015/05/13 Version 0.8.2
- Updated dev depedencies and fixed all failing tests

2015/05/12 Version 0.8.1
- Fixed an issue where .types file is in another directory that the root directory

2015/05/11 Version 0.8.0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should probably add something about the changelog in the contributing guidelines---I like to manage this myself while bumping and publishing releases. But the notes here are really helpful! I'll just end up munging this a bit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do as you like. I put them there for you

- Add ability to define custom mime-types, inline or with Apache .types file

2015/05/09 Version 0.7.6
- Fix double encoding in directory listings

Expand Down
13 changes: 11 additions & 2 deletions README.md
@@ -1,4 +1,4 @@
# Ecstatic [![build status](https://secure.travis-ci.org/jfhbrook/node-ecstatic.png)](http://travis-ci.org/jfhbrook/node-ecstatic)
# Ecstatic [![build status](https://secure.travis-ci.org/jfhbrook/node-ecstatic.png)](http://travis-ci.org/jfhbrook/node-ecstatic) [![dependencies status](https://david-dm.org/jfhbrook/node-ecstatic.svg)](https://david-dm.org/jfhbrook/node-ecstatic)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A huge issue I had, was updating from tap 0.4 to 1.0. Since I spend a few hours doing that, I though a badge was in order :). It's a free service that tells us about the health of our dependencies. See here: https://david-dm.org/jfhbrook/node-ecstatic#info=devDependencies&view=table (and don't worry - it will look better when you merge this PR)


![](http://imgur.com/vhub5.png)

Expand Down Expand Up @@ -72,7 +72,9 @@ var opts = {
si : false,
defaultExt : 'html',
gzip : false,
serverHeader : true
serverHeader : true,
contentType : 'application/octet-stream',
mimeTypes : undefined
}
```

Expand Down Expand Up @@ -139,6 +141,13 @@ on all responses served by ecstatic.
Set `opts.contentType` in order to change default Content-Type header value.
Defaults to **application/octet-stream**.

### `opts.mimeTypes`

Add new or override one or more mime-types. This affects the HTTP Content-Type header.
Can either be a path to a [`.types`](http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types) file or an object hash of type(s).

ecstatic({ mimeType: { 'mime-type': ['file_extension', 'file_extension'] } })
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfhbrook, does this kind of doc make sense? An example would be ecstatic({ mimeType: { 'node/ecstatic+fun': ['ecs', 'fun'] } }) to get content-type: node/ecstatic+fun; charset=UTF-8 with .esc and .fun files.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I think that makes sense.


### `opts.handleError`

Turn **off** handleErrors to allow fall-through with `opts.handleError === false`, Defaults to **true**.
Expand Down
13 changes: 12 additions & 1 deletion lib/ecstatic.js
Expand Up @@ -31,6 +31,17 @@ var ecstatic = module.exports = function (dir, options) {
opts.root = dir;
if (defaultExt && /^\./.test(defaultExt)) defaultExt = defaultExt.replace(/^\./, '');

// Support hashes and .types files in mimeTypes @since 0.8
if (opts.mimeTypes) {
if (typeof opts.mimeTypes === 'string') {
//TODO: should handleError have any effect here?
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfhbrook what do you think about the handleError option? Should we not throw if it is true?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think throwing is still appropriate.

mime.load(opts.mimeTypes); // will throw if path is wrong as intended
} else if (typeof opts.mimeTypes === 'object') {
mime.define(opts.mimeTypes);
}
}


return function middleware (req, res, next) {

// Strip any null bytes from the url
Expand Down Expand Up @@ -188,7 +199,7 @@ var ecstatic = module.exports = function (dir, options) {
status['500'](res, next, { error: err });
});
res.on('close', function () {
fstream.destroy();
fstream.destroy();
});
res.writeHead(206, {
'Content-Range': 'bytes ' + start + '-' + end + '/' + total,
Expand Down
60 changes: 41 additions & 19 deletions lib/ecstatic/opts.js
Expand Up @@ -10,15 +10,20 @@ module.exports = function (opts) {
gzip = false,
defaultExt = '.html',
handleError = true,
serverHeader = true;
contentType = 'application/octet-stream';
serverHeader = true,
contentType = 'application/octet-stream',
mimeTypes;

function isDeclared(k) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulling this out is probably a good idea. I approve.

return typeof opts[k] !== 'undefined' && opts[k] !== null;
}

if (opts) {
[
'autoIndex',
'autoindex'
].some(function (k) {
if (typeof opts[k] !== 'undefined' && opts[k] !== null) {
if (isDeclared(k)) {
autoIndex = opts[k];
return true;
}
Expand All @@ -28,7 +33,7 @@ module.exports = function (opts) {
'showDir',
'showdir'
].some(function (k) {
if (typeof opts[k] !== 'undefined' && opts[k] !== null) {
if (isDeclared(k)) {
showDir = opts[k];
return true;
}
Expand All @@ -39,7 +44,7 @@ module.exports = function (opts) {
'humanreadable',
'human-readable'
].some(function (k) {
if (typeof opts[k] !== 'undefined' && opts[k] !== null) {
if (isDeclared(k)) {
humanReadable = opts[k];
return true;
}
Expand All @@ -49,7 +54,7 @@ module.exports = function (opts) {
'si',
'index'
].some(function (k) {
if (typeof opts[k] !== 'undefined' && opts[k] !== null) {
if (isDeclared(k)) {
si = opts[k];
return true;
}
Expand All @@ -76,32 +81,48 @@ module.exports = function (opts) {
'handleError',
'handleerror'
].some(function (k) {
if (typeof opts[k] !== 'undefined' && opts[k] !== null) {
handleError = opts[k];
return true;
}
if (isDeclared(k)) {
handleError = opts[k];
return true;
}
});

[
'serverHeader',
'serverheader',
'server-header'
].some(function (k) {
if (typeof opts[k] !== 'undefined' && opts[k] !== null) {
serverHeader = opts[k];
return true;
}
if (isDeclared(k)) {
serverHeader = opts[k];
return true;
}
});

[
'contentType',
'contenttype',
'content-type'
].some(function (k) {
if (typeof opts[k] !== 'undefined' && opts[k] !== null) {
contentType = opts[k];
return true;
}
if (isDeclared(k)) {
contentType = opts[k];
return true;
}
});

[
'mimetype',
'mimetypes',
'mimeType',
'mimeTypes',
'mime-type',
'mime-types',
'mime-Type',
'mime-Types'
].some(function (k) {
if (isDeclared(k)) {
mimeTypes = opts[k];
return true;
}
});

}
Expand All @@ -117,6 +138,7 @@ module.exports = function (opts) {
gzip: gzip,
handleError: handleError,
serverHeader: serverHeader,
contentType: contentType
contentType: contentType,
mimeTypes: mimeTypes
};
};
11 changes: 6 additions & 5 deletions package.json
Expand Up @@ -2,7 +2,7 @@
"author": "Joshua Holbrook <josh@nodejitsu.com> (http://jesusabdullah.net)",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: change email address

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it made me think that you guys sat together and made http-server and ecstatic. I even went to freenode.org#nodejitsu and asked around for you a few times.

"name": "ecstatic",
"description": "A simple static file server middleware that works with both Express and Flatiron",
"version": "0.7.6",
"version": "0.8.2",
"homepage": "https://github.com/jfhbrook/node-ecstatic",
"repository": {
"type": "git",
Expand All @@ -28,10 +28,11 @@
"url-join": "0.0.1"
},
"devDependencies": {
"tap": "^0.4.13",
"eol": "^0.2.0",
"express": "^4.12.3",
"mkdirp": "^0.5.0",
"request": "^2.49.0",
"express": "^3.0.6",
"union": "^0.3.8",
"mkdirp": "^0.5.0"
"tap": "^1.0.3",
"union": "^0.4.4"
}
}
22 changes: 12 additions & 10 deletions test/content-type.js
@@ -1,26 +1,28 @@
var test = require('tap').test,
ecstatic = require('../'),
http = require('http'),
request = require('request');
request = require('request'),
ecstatic = require('../');

test('default default contentType', function(t) {
var server = http.createServer(ecstatic({
root: __dirname + '/public/',
contentType: 'text/plain'
}));
try {
var server = http.createServer(ecstatic({
root: __dirname + '/public/',
contentType: 'text/plain'
}));
} catch (e) {
t.fail(e.message);
t.end();
}

t.plan(3);

t.on('end', function() { server.close(); });

server.listen(0, function() {
var port = server.address().port;
request.get('http://localhost:' + port + '/f_f', function(err, res, body) {
t.ifError(err);
t.equal(res.statusCode, 200);
t.equal(res.headers['content-type'], 'text/plain; charset=UTF-8');
t.end();
server.close(function() { t.end(); });
});
});
});

2 changes: 1 addition & 1 deletion test/core-error.js
Expand Up @@ -11,7 +11,7 @@ var root = __dirname + '/public',

mkdirp.sync(root + '/emptyDir');

var cases = require('./common-cases-error');
var cases = require('./secret/common-cases-error');

test('core', function (t) {
var filenames = Object.keys(cases);
Expand Down
9 changes: 5 additions & 4 deletions test/core.js
Expand Up @@ -4,14 +4,15 @@ var test = require('tap').test,
request = require('request'),
mkdirp = require('mkdirp'),
fs = require('fs'),
path = require('path');
path = require('path'),
eol = require('eol');

var root = __dirname + '/public',
baseDir = 'base';

mkdirp.sync(root + '/emptyDir');

var cases = require('./common-cases');
var cases = require('./secret/common-cases');

test('core', function (t) {
var filenames = Object.keys(cases);
Expand Down Expand Up @@ -52,11 +53,11 @@ test('core', function (t) {
}

if (r.body !== undefined) {
t.equal(body, r.body, 'body for `' + file + '`');
t.equal(eol.lf(body), r.body, 'body for `' + file + '`');
}

if (r.location !== undefined) {
t.equal(res.headers.location, path.join('/', baseDir, r.location), 'location for `' + file + '`');
t.equal(path.normalize(res.headers.location), path.join('/', baseDir, r.location), 'location for `' + file + '`');
}

if (--pending === 0) {
Expand Down
28 changes: 28 additions & 0 deletions test/custom-content-type-file-secret.js
@@ -0,0 +1,28 @@
var test = require('tap').test,
http = require('http'),
request = require('request'),
ecstatic = require('../');

test('custom contentType via .types file', function(t) {
try {
var server = http.createServer(ecstatic({
root: __dirname + '/public/',
mimetypes: __dirname + '/secret/custom_mime_type.types'
}));
} catch (e) {
t.fail(e.message);
t.end();
}

t.plan(3)

server.listen(0, function() {
var port = server.address().port;
request.get('http://localhost:' + port + '/custom_mime_type.opml', function(err, res, body) {
t.ifError(err);
t.equal(res.statusCode, 200, 'custom_mime_type.opml should be found');
t.equal(res.headers['content-type'], 'application/secret; charset=utf-8');
server.close(function() { t.end(); });
});
});
});
45 changes: 45 additions & 0 deletions test/custom-content-type-file.js
@@ -0,0 +1,45 @@
var test = require('tap').test,
http = require('http'),
request = require('request'),
ecstatic = require('../');

function setup(opts) {
return http.createServer(ecstatic(opts));
}

test('throws when custom contentType .types file does not exist', function(t) {
t.plan(1);

t.throws(
setup.bind(null, {
root: __dirname + '/public/',
mimeTypes: 'this_file_does_not_exist.types'
})
);

});

test('custom contentType via .types file', function(t) {
try {
var server = setup({
root: __dirname + '/public',
'mime-types': __dirname + '/public/custom_mime_type.types'
});
} catch (e) {
t.fail(e.message);
t.end();
}

t.plan(3)

server.listen(0, function() {
var port = server.address().port;

request.get('http://localhost:' + port + '/custom_mime_type.opml', function(err, res, body) {
t.ifError(err);
t.equal(res.statusCode, 200, 'custom_mime_type.opml should be found');
t.equal(res.headers['content-type'], 'application/foo; charset=utf-8');
server.close(function() { t.end(); });
});
});
});