Skip to content

Commit

Permalink
Add: fs option & tests. Closes pillarjs#160 Release 0.17.0
Browse files Browse the repository at this point in the history
	* Add: new option to specify which file system to use to serve files by default.
	* Add: exported erros for basic file system interface check
	* Add: basic tests via global injection
  • Loading branch information
hinell committed Apr 11, 2019
1 parent 09c2f2d commit 607915b
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 39 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ node_js:
- "3.3"
- "4.9"
- "5.12"
- "6.15"
- "6.14"
- "7.10"
- "8.13"
- "8.12"
- "9.11"
- "10.12"
sudo: false
Expand Down
7 changes: 7 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
unreleased
==========

0.17.0 / 2018-02-07
==========
* Add `fs` option
* test:
- Add test.js for programmatic testing and debugging
- Add `test:d` script command

* deps: depd@~2.0.0
- Replace internal `eval` usage with `Function` constructor
- Use instance methods on `process` to check for listeners
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
[![Windows Build][appveyor-image]][appveyor-url]
[![Test Coverage][coveralls-image]][coveralls-url]

> _This is a fork of an original [send](https://github.com/pillarjs/send) module. See changes made by this fork in [history file](./HISTORY.MD#0170--2018-02-07)._
Send is a library for streaming files from the file system as a http response
supporting partial responses (Ranges), conditional-GET negotiation (If-Match,
If-Unmodified-Since, If-None-Match, If-Modified-Since), high test coverage,
and granular events which may be leveraged to take appropriate actions in your
application or framework.

Looking to serve up entire folders mapped to URLs? Try [serve-static](https://www.npmjs.org/package/serve-static).

Looking to serve up entire folders mapped to URLs? Try [serve-static (forked version)](/serve-static).

## Installation

Expand Down Expand Up @@ -119,6 +122,15 @@ Serve files relative to `path`.
Byte offset at which the stream starts, defaults to 0. The start is inclusive,
meaning `start: 2` will include the 3rd byte in the stream.

##### fs

**Default**: [require('fs')](https://nodejs.org/api/fs.html)

File system to serve files by default.
```js
send(req, path, { fs: mockedFileSystem })
```

#### Events

The `SendStream` is an event emitter and will emit the following events:
Expand Down Expand Up @@ -171,6 +183,12 @@ $ DEBUG=send node app
$ npm install
$ npm test
```
### Debugging tests
In order to debug testing you can run programmatic mocha instance by command:
```
$ npm test:d
```
> _[package.json.scripts.test:d](./package.json)_
## Examples

Expand Down
25 changes: 20 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ var encodeUrl = require('encodeurl')
var escapeHtml = require('escape-html')
var etag = require('etag')
var fresh = require('fresh')
var fs = require('fs')
var mime = require('mime')
var ms = require('ms')
var onFinished = require('on-finished')
Expand Down Expand Up @@ -84,6 +83,10 @@ function send (req, path, options) {
return new SendStream(req, path, options)
}

var errors = {}
errors.INVALID_OPTION_FS_STAT = new TypeError('Invalid option: Incompatible file system interface: fs.stat() method is expected')
errors.INVALID_OPTION_FS_CRTSTRM = new TypeError('Invalid option: Incompatible file system interface: fs.createReadStream() method is expected')
send.errors = errors
/**
* Initialize a `SendStream` with the given `path`.
*
Expand All @@ -102,6 +105,18 @@ function SendStream (req, path, options) {
this.path = path
this.req = req

// Dynamically import `fs` if not it is not provided by config
this.fs = opts.fs || require('fs')

// Checking if provided "fs" is compatible
if (typeof this.fs.stat !== 'function') {
throw errors.INVALID_OPTION_FS_STAT
}

if (typeof this.fs.createReadStream !== 'function') {
throw errors.INVALID_OPTION_FS_CRTSTRM
}

this._acceptRanges = opts.acceptRanges !== undefined
? Boolean(opts.acceptRanges)
: true
Expand Down Expand Up @@ -718,7 +733,7 @@ SendStream.prototype.sendFile = function sendFile (path) {
var self = this

debug('stat "%s"', path)
fs.stat(path, function onstat (err, stat) {
this.fs.stat(path, function onstat (err, stat) {
if (err && err.code === 'ENOENT' && !extname(path) && path[path.length - 1] !== sep) {
// not found, check extensions
return next(err)
Expand All @@ -739,7 +754,7 @@ SendStream.prototype.sendFile = function sendFile (path) {
var p = path + '.' + self._extensions[i++]

debug('stat "%s"', p)
fs.stat(p, function (err, stat) {
self.fs.stat(p, function (err, stat) {
if (err) return next(err)
if (stat.isDirectory()) return next()
self.emit('file', p, stat)
Expand Down Expand Up @@ -767,7 +782,7 @@ SendStream.prototype.sendIndex = function sendIndex (path) {
var p = join(path, self._index[i])

debug('stat "%s"', p)
fs.stat(p, function (err, stat) {
self.fs.stat(p, function (err, stat) {
if (err) return next(err)
if (stat.isDirectory()) return next()
self.emit('file', p, stat)
Expand All @@ -793,7 +808,7 @@ SendStream.prototype.stream = function stream (path, options) {
var res = this.res

// pipe
var stream = fs.createReadStream(path, options)
var stream = this.fs.createReadStream(path, options)
this.emit('stream', stream)
stream.pipe(res)

Expand Down
64 changes: 33 additions & 31 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,47 +1,48 @@
{
"name": "send",
"description": "Better streaming static file server with Range and conditional-GET support",
"version": "0.16.2",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"name" : "send",
"description" : "Better streaming static file server with Range and conditional-GET support",
"version" : "0.17.0",
"author" : "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Douglas Christopher Wilson <doug@somethingdoug.com>",
"James Wyatt Cready <jcready@gmail.com>",
"Jesús Leganés Combarro <piranna@gmail.com>"
"Jesús Leganés Combarro <piranna@gmail.com>",
"Davrononv Alexander Alisherovich <al.neodim@gmail.com>"
],
"license": "MIT",
"license" : "MIT",
"repository": "pillarjs/send",
"keywords": [
"keywords" : [
"static",
"file",
"server"
],
"dependencies": {
"debug": "2.6.9",
"depd": "~2.0.0",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.1",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"debug" : "2.6.9",
"depd" : "~2.0.0",
"destroy" : "~1.0.4",
"encodeurl" : "~1.0.2",
"escape-html" : "~1.0.3",
"etag" : "~1.8.1",
"fresh" : "0.5.2",
"http-errors" : "~1.7.1",
"mime" : "1.6.0",
"ms" : "2.1.1",
"on-finished" : "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.5.0"
"statuses" : "~1.5.0"
},
"devDependencies": {
"after": "0.8.2",
"eslint": "5.8.0",
"after" : "0.8.2",
"eslint" : "5.8.0",
"eslint-config-standard": "12.0.0",
"eslint-plugin-import": "2.14.0",
"eslint-plugin-markdown": "1.0.0-rc.1",
"eslint-plugin-node": "7.0.1",
"eslint-plugin-promise": "4.0.1",
"eslint-plugin-import" : "2.14.0",
"eslint-plugin-markdown": "1.0.0-beta.6",
"eslint-plugin-node" : "7.0.1",
"eslint-plugin-promise" : "4.0.1",
"eslint-plugin-standard": "4.0.0",
"istanbul": "0.4.5",
"mocha": "5.2.0",
"supertest": "3.3.0"
"istanbul" : "0.4.5",
"mocha" : "5.2.0",
"supertest" : "3.3.0"
},
"files": [
"HISTORY.md",
Expand All @@ -53,9 +54,10 @@
"node": ">= 0.8.0"
},
"scripts": {
"lint": "eslint --plugin markdown --ext js,md .",
"test": "mocha --check-leaks --reporter spec --bail",
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --check-leaks --reporter spec",
"lint" : "eslint --plugin markdown --ext js,md .",
"test" : "mocha --check-leaks --reporter spec --bail",
"test:d" : "node --inspect-brk test.js",
"test-ci" : "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --check-leaks --reporter spec",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --check-leaks --reporter dot"
}
}
15 changes: 15 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var Mocha = require('mocha')
var path = require('path')

// Instantiate a Mocha instance.
var mocha = new Mocha()

var testDir = '/test/'
mocha.addFile(
path.join(__dirname, testDir, 'send.js')
)
// Run the tests.
mocha.run(function (failures) {
// exit with non-zero status if there were failures
process.exitCode = failures ? 1 : 0
})
31 changes: 31 additions & 0 deletions test/send.js
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,37 @@ describe('send(file, options)', function () {
})
})
})

describe('fs', function () {
var FS_STAT = send.errors.INVALID_OPTION_FS_STAT
var CRTSTRM = send.errors.INVALID_OPTION_FS_CRTSTRM
it('must provide fs.stat()', function (done) {
try {
send(void 0, '', { fs: { createReadStream: function () {} } })
} catch (err) {
if (err === FS_STAT) {
done()
} else {
done(new Error('Must throw INVALID_OPTION_FS_STAT'))
}
return
}
done()
})
it('must implement fs.createReadStream()', function (done) {
try {
send(void 0, '', { fs: { stat: function () {} } })
} catch (err) {
if (err === CRTSTRM) {
done()
} else {
done(new Error('Must throw INVALID_OPTION_FS_CRTSTRM'))
}
return
}
done()
})
})
})

describe('send.mime', function () {
Expand Down

0 comments on commit 607915b

Please sign in to comment.