cleanup: remove unnecessary require for global Buffer
[express.git] / test / res.download.js
blob82243d8b0032cb996b930dee0022469c0d9365c2
1 'use strict'
3 var after = require('after');
4 var assert = require('assert')
5 var asyncHooks = tryRequire('async_hooks')
6 var express = require('..');
7 var path = require('path')
8 var request = require('supertest');
9 var utils = require('./support/utils')
11 var FIXTURES_PATH = path.join(__dirname, 'fixtures')
13 var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
14 ? describe
15 : describe.skip
17 describe('res', function(){
18 describe('.download(path)', function(){
19 it('should transfer as an attachment', function(done){
20 var app = express();
22 app.use(function(req, res){
23 res.download('test/fixtures/user.html');
24 });
26 request(app)
27 .get('/')
28 .expect('Content-Type', 'text/html; charset=utf-8')
29 .expect('Content-Disposition', 'attachment; filename="user.html"')
30 .expect(200, '<p>{{user.name}}</p>', done)
33 it('should accept range requests', function (done) {
34 var app = express()
36 app.get('/', function (req, res) {
37 res.download('test/fixtures/user.html')
40 request(app)
41 .get('/')
42 .expect('Accept-Ranges', 'bytes')
43 .expect(200, '<p>{{user.name}}</p>', done)
46 it('should respond with requested byte range', function (done) {
47 var app = express()
49 app.get('/', function (req, res) {
50 res.download('test/fixtures/user.html')
53 request(app)
54 .get('/')
55 .set('Range', 'bytes=0-2')
56 .expect('Content-Range', 'bytes 0-2/20')
57 .expect(206, '<p>', done)
61 describe('.download(path, filename)', function(){
62 it('should provide an alternate filename', function(done){
63 var app = express();
65 app.use(function(req, res){
66 res.download('test/fixtures/user.html', 'document');
67 });
69 request(app)
70 .get('/')
71 .expect('Content-Type', 'text/html; charset=utf-8')
72 .expect('Content-Disposition', 'attachment; filename="document"')
73 .expect(200, done)
77 describe('.download(path, fn)', function(){
78 it('should invoke the callback', function(done){
79 var app = express();
80 var cb = after(2, done);
82 app.use(function(req, res){
83 res.download('test/fixtures/user.html', cb);
84 });
86 request(app)
87 .get('/')
88 .expect('Content-Type', 'text/html; charset=utf-8')
89 .expect('Content-Disposition', 'attachment; filename="user.html"')
90 .expect(200, cb);
93 describeAsyncHooks('async local storage', function () {
94 it('should presist store', function (done) {
95 var app = express()
96 var cb = after(2, done)
97 var store = { foo: 'bar' }
99 app.use(function (req, res, next) {
100 req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
101 req.asyncLocalStorage.run(store, next)
104 app.use(function (req, res) {
105 res.download('test/fixtures/name.txt', function (err) {
106 if (err) return cb(err)
108 var local = req.asyncLocalStorage.getStore()
110 assert.strictEqual(local.foo, 'bar')
111 cb()
115 request(app)
116 .get('/')
117 .expect('Content-Type', 'text/plain; charset=utf-8')
118 .expect('Content-Disposition', 'attachment; filename="name.txt"')
119 .expect(200, 'tobi', cb)
122 it('should presist store on error', function (done) {
123 var app = express()
124 var store = { foo: 'bar' }
126 app.use(function (req, res, next) {
127 req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage()
128 req.asyncLocalStorage.run(store, next)
131 app.use(function (req, res) {
132 res.download('test/fixtures/does-not-exist', function (err) {
133 var local = req.asyncLocalStorage.getStore()
135 if (local) {
136 res.setHeader('x-store-foo', String(local.foo))
139 res.send(err ? 'got ' + err.status + ' error' : 'no error')
143 request(app)
144 .get('/')
145 .expect(200)
146 .expect('x-store-foo', 'bar')
147 .expect('got 404 error')
148 .end(done)
153 describe('.download(path, options)', function () {
154 it('should allow options to res.sendFile()', function (done) {
155 var app = express()
157 app.use(function (req, res) {
158 res.download('test/fixtures/.name', {
159 dotfiles: 'allow',
160 maxAge: '4h'
164 request(app)
165 .get('/')
166 .expect(200)
167 .expect('Content-Disposition', 'attachment; filename=".name"')
168 .expect('Cache-Control', 'public, max-age=14400')
169 .expect(utils.shouldHaveBody(Buffer.from('tobi')))
170 .end(done)
173 describe('with "headers" option', function () {
174 it('should set headers on response', function (done) {
175 var app = express()
177 app.use(function (req, res) {
178 res.download('test/fixtures/user.html', {
179 headers: {
180 'X-Foo': 'Bar',
181 'X-Bar': 'Foo'
186 request(app)
187 .get('/')
188 .expect(200)
189 .expect('X-Foo', 'Bar')
190 .expect('X-Bar', 'Foo')
191 .end(done)
194 it('should use last header when duplicated', function (done) {
195 var app = express()
197 app.use(function (req, res) {
198 res.download('test/fixtures/user.html', {
199 headers: {
200 'X-Foo': 'Bar',
201 'x-foo': 'bar'
206 request(app)
207 .get('/')
208 .expect(200)
209 .expect('X-Foo', 'bar')
210 .end(done)
213 it('should override Content-Type', function (done) {
214 var app = express()
216 app.use(function (req, res) {
217 res.download('test/fixtures/user.html', {
218 headers: {
219 'Content-Type': 'text/x-custom'
224 request(app)
225 .get('/')
226 .expect(200)
227 .expect('Content-Type', 'text/x-custom')
228 .end(done)
231 it('should not set headers on 404', function (done) {
232 var app = express()
234 app.use(function (req, res) {
235 res.download('test/fixtures/does-not-exist', {
236 headers: {
237 'X-Foo': 'Bar'
242 request(app)
243 .get('/')
244 .expect(404)
245 .expect(utils.shouldNotHaveHeader('X-Foo'))
246 .end(done)
249 describe('when headers contains Content-Disposition', function () {
250 it('should be ignored', function (done) {
251 var app = express()
253 app.use(function (req, res) {
254 res.download('test/fixtures/user.html', {
255 headers: {
256 'Content-Disposition': 'inline'
261 request(app)
262 .get('/')
263 .expect(200)
264 .expect('Content-Disposition', 'attachment; filename="user.html"')
265 .end(done)
268 it('should be ignored case-insensitively', function (done) {
269 var app = express()
271 app.use(function (req, res) {
272 res.download('test/fixtures/user.html', {
273 headers: {
274 'content-disposition': 'inline'
279 request(app)
280 .get('/')
281 .expect(200)
282 .expect('Content-Disposition', 'attachment; filename="user.html"')
283 .end(done)
288 describe('with "root" option', function () {
289 it('should allow relative path', function (done) {
290 var app = express()
292 app.use(function (req, res) {
293 res.download('name.txt', {
294 root: FIXTURES_PATH
298 request(app)
299 .get('/')
300 .expect(200)
301 .expect('Content-Disposition', 'attachment; filename="name.txt"')
302 .expect(utils.shouldHaveBody(Buffer.from('tobi')))
303 .end(done)
306 it('should allow up within root', function (done) {
307 var app = express()
309 app.use(function (req, res) {
310 res.download('fake/../name.txt', {
311 root: FIXTURES_PATH
315 request(app)
316 .get('/')
317 .expect(200)
318 .expect('Content-Disposition', 'attachment; filename="name.txt"')
319 .expect(utils.shouldHaveBody(Buffer.from('tobi')))
320 .end(done)
323 it('should reject up outside root', function (done) {
324 var app = express()
326 app.use(function (req, res) {
327 var p = '..' + path.sep +
328 path.relative(path.dirname(FIXTURES_PATH), path.join(FIXTURES_PATH, 'name.txt'))
330 res.download(p, {
331 root: FIXTURES_PATH
335 request(app)
336 .get('/')
337 .expect(403)
338 .expect(utils.shouldNotHaveHeader('Content-Disposition'))
339 .end(done)
342 it('should reject reading outside root', function (done) {
343 var app = express()
345 app.use(function (req, res) {
346 res.download('../name.txt', {
347 root: FIXTURES_PATH
351 request(app)
352 .get('/')
353 .expect(403)
354 .expect(utils.shouldNotHaveHeader('Content-Disposition'))
355 .end(done)
360 describe('.download(path, filename, fn)', function(){
361 it('should invoke the callback', function(done){
362 var app = express();
363 var cb = after(2, done);
365 app.use(function(req, res){
366 res.download('test/fixtures/user.html', 'document', cb)
369 request(app)
370 .get('/')
371 .expect('Content-Type', 'text/html; charset=utf-8')
372 .expect('Content-Disposition', 'attachment; filename="document"')
373 .expect(200, cb);
377 describe('.download(path, filename, options, fn)', function () {
378 it('should invoke the callback', function (done) {
379 var app = express()
380 var cb = after(2, done)
381 var options = {}
383 app.use(function (req, res) {
384 res.download('test/fixtures/user.html', 'document', options, cb)
387 request(app)
388 .get('/')
389 .expect(200)
390 .expect('Content-Type', 'text/html; charset=utf-8')
391 .expect('Content-Disposition', 'attachment; filename="document"')
392 .end(cb)
395 it('should allow options to res.sendFile()', function (done) {
396 var app = express()
398 app.use(function (req, res) {
399 res.download('test/fixtures/.name', 'document', {
400 dotfiles: 'allow',
401 maxAge: '4h'
405 request(app)
406 .get('/')
407 .expect(200)
408 .expect('Content-Disposition', 'attachment; filename="document"')
409 .expect('Cache-Control', 'public, max-age=14400')
410 .expect(utils.shouldHaveBody(Buffer.from('tobi')))
411 .end(done)
414 describe('when options.headers contains Content-Disposition', function () {
415 it('should be ignored', function (done) {
416 var app = express()
418 app.use(function (req, res) {
419 res.download('test/fixtures/user.html', 'document', {
420 headers: {
421 'Content-Type': 'text/x-custom',
422 'Content-Disposition': 'inline'
427 request(app)
428 .get('/')
429 .expect(200)
430 .expect('Content-Type', 'text/x-custom')
431 .expect('Content-Disposition', 'attachment; filename="document"')
432 .end(done)
435 it('should be ignored case-insensitively', function (done) {
436 var app = express()
438 app.use(function (req, res) {
439 res.download('test/fixtures/user.html', 'document', {
440 headers: {
441 'content-type': 'text/x-custom',
442 'content-disposition': 'inline'
447 request(app)
448 .get('/')
449 .expect(200)
450 .expect('Content-Type', 'text/x-custom')
451 .expect('Content-Disposition', 'attachment; filename="document"')
452 .end(done)
457 describe('on failure', function(){
458 it('should invoke the callback', function(done){
459 var app = express();
461 app.use(function (req, res, next) {
462 res.download('test/fixtures/foobar.html', function(err){
463 if (!err) return next(new Error('expected error'));
464 res.send('got ' + err.status + ' ' + err.code);
468 request(app)
469 .get('/')
470 .expect(200, 'got 404 ENOENT', done);
473 it('should remove Content-Disposition', function(done){
474 var app = express()
476 app.use(function (req, res, next) {
477 res.download('test/fixtures/foobar.html', function(err){
478 if (!err) return next(new Error('expected error'));
479 res.end('failed');
483 request(app)
484 .get('/')
485 .expect(utils.shouldNotHaveHeader('Content-Disposition'))
486 .expect(200, 'failed', done)
491 function tryRequire (name) {
492 try {
493 return require(name)
494 } catch (e) {
495 return {}