diff --git a/examples/bodyParser.js b/examples/bodyParser.js deleted file mode 100644 index a90569edd..000000000 --- a/examples/bodyParser.js +++ /dev/null @@ -1,40 +0,0 @@ - -var connect = require('../') - , http = require('http'); - -// visit form.html - -var app = connect() - .use(connect.static(__dirname + '/public')) - .use(connect.bodyParser()) - .use(form) - .use(upload); - -function form(req, res, next){ - if ('GET' != req.method) return next(); - res.statusCode = 302; - res.setHeader('Location', 'form.html'); - res.end(); -} - -function upload(req, res){ - res.setHeader('Content-Type', 'text/html'); - res.write('

thanks ' + req.body.name + '

'); - res.write(''); -} - -http.Server(app).listen(3000); -console.log('Server started on port 3000'); \ No newline at end of file diff --git a/examples/limit.js b/examples/limit.js deleted file mode 100644 index dfdc5933b..000000000 --- a/examples/limit.js +++ /dev/null @@ -1,41 +0,0 @@ - -var connect = require('../') - , http = require('http'); - -// visit form.html - -var app = connect() - .use(connect.static(__dirname + '/public')) - .use(connect.limit('5mb')) - .use(connect.bodyParser()) - .use(form) - .use(upload); - -function form(req, res, next){ - if ('GET' != req.method) return next(); - res.statusCode = 302; - res.setHeader('Location', 'form.html'); - res.end(); -} - -function upload(req, res){ - res.setHeader('Content-Type', 'text/html'); - res.write('

thanks ' + req.body.name + '

'); - res.write(''); -} - -http.Server(app).listen(3000); -console.log('Server started on port 3000'); \ No newline at end of file diff --git a/examples/public/form.html b/examples/public/form.html deleted file mode 100644 index f37565578..000000000 --- a/examples/public/form.html +++ /dev/null @@ -1,5 +0,0 @@ -
- - - -
\ No newline at end of file diff --git a/examples/upload-stream.js b/examples/upload-stream.js deleted file mode 100644 index b53db01d2..000000000 --- a/examples/upload-stream.js +++ /dev/null @@ -1,37 +0,0 @@ - -/** - * Module dependencies. - */ - -var connect = require('../'); -var fs = require('fs'); - -connect() - .use(connect.bodyParser({ defer: true })) - .use(form) - .use(upload) - .listen(3000); - -function form(req, res, next) { - if ('GET' !== req.method) return next(); - res.setHeader('Content-Type', 'text/html'); - res.end('
' - + '' - + '' - + '
'); -} - -function upload(req, res, next) { - if ('POST' !== req.method) return next(); - - req.form.on('part', function(part){ - // transfer to s3 etc - console.log('upload %s %s', part.name, part.filename); - var out = fs.createWriteStream('/tmp/' + part.filename); - part.pipe(out); - }); - - req.form.on('close', function(){ - res.end('uploaded!'); - }); -} \ No newline at end of file diff --git a/examples/upload.js b/examples/upload.js deleted file mode 100644 index 4aaff3527..000000000 --- a/examples/upload.js +++ /dev/null @@ -1,29 +0,0 @@ - -/** - * Module dependencies. - */ - -var connect = require('../'); - -connect() - .use(connect.bodyParser()) - .use(form) - .use(upload) - .listen(3000); - -function form(req, res, next) { - if ('GET' !== req.method) return next(); - res.setHeader('Content-Type', 'text/html'); - res.end('
' - + '' - + '' - + '
'); -} - -function upload(req, res, next) { - if ('POST' !== req.method) return next(); - req.files.images.forEach(function(file){ - console.log(' uploaded : %s %skb : %s', file.originalFilename, file.size / 1024 | 0, file.path); - }); - res.end('Thanks'); -} \ No newline at end of file diff --git a/lib/middleware/bodyParser.js b/lib/middleware/bodyParser.js index 14481f56a..8cdee7dcd 100644 --- a/lib/middleware/bodyParser.js +++ b/lib/middleware/bodyParser.js @@ -10,9 +10,8 @@ * Module dependencies. */ -var multipart = require('./multipart') - , urlencoded = require('./urlencoded') - , json = require('./json'); +var urlencoded = require('./urlencoded'); +var json = require('./json'); /** * Body parser: @@ -52,17 +51,13 @@ var multipart = require('./multipart') */ exports = module.exports = function bodyParser(options){ - var _urlencoded = urlencoded(options) - , _multipart = multipart(options) - , _json = json(options); + var _urlencoded = urlencoded(options); + var _json = json(options); return function bodyParser(req, res, next) { _json(req, res, function(err){ if (err) return next(err); - _urlencoded(req, res, function(err){ - if (err) return next(err); - _multipart(req, res, next); - }); + _urlencoded(req, res, next); }); } }; \ No newline at end of file diff --git a/lib/middleware/limit.js b/lib/middleware/limit.js deleted file mode 100644 index 2b895f97b..000000000 --- a/lib/middleware/limit.js +++ /dev/null @@ -1,89 +0,0 @@ - -/*! - * Connect - limit - * Copyright(c) 2011 TJ Holowaychuk - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var utils = require('../utils'), - brokenPause = utils.brokenPause; - -/** - * Limit: - * - * Status: Deprecated. This middleware will be removed in Connect 3.0. - * If you still wish to use some type of limit middleware, - * you may be interested in: - * - * - [raw-body](https://github.com/stream-utils/raw-body) - * - * Limit request bodies to the given size in `bytes`. - * - * A string representation of the bytesize may also be passed, - * for example "5mb", "200kb", "1gb", etc. - * - * connect() - * .use(connect.limit('5.5mb')) - * .use(handleImageUpload) - * - * @param {Number|String} bytes - * @return {Function} - * @api public - */ - -module.exports = function limit(bytes){ - if ('string' == typeof bytes) bytes = utils.parseBytes(bytes); - if ('number' != typeof bytes) throw new Error('limit() bytes required'); - - if (process.env.NODE_ENV !== 'test') { - console.warn('connect.limit() will be removed in connect 3.0'); - } - - return function limit(req, res, next){ - var received = 0 - , len = req.headers['content-length'] - ? parseInt(req.headers['content-length'], 10) - : null; - - // self-awareness - if (req._limit) return next(); - req._limit = true; - - // limit by content-length - if (len && len > bytes) return next(utils.error(413)); - - // limit - if (brokenPause) { - listen(); - } else { - req.on('newListener', function handler(event) { - if (event !== 'data') return; - - req.removeListener('newListener', handler); - // Start listening at the end of the current loop - // otherwise the request will be consumed too early. - // Sideaffect is `limit` will miss the first chunk, - // but that's not a big deal. - // Unfortunately, the tests don't have large enough - // request bodies to test this. - process.nextTick(listen); - }); - }; - - next(); - - function listen() { - req.on('data', function(chunk) { - received += Buffer.isBuffer(chunk) - ? chunk.length : - Buffer.byteLength(chunk); - - if (received > bytes) req.destroy(); - }); - }; - }; -}; \ No newline at end of file diff --git a/lib/middleware/multipart.js b/lib/middleware/multipart.js deleted file mode 100644 index 8bf663892..000000000 --- a/lib/middleware/multipart.js +++ /dev/null @@ -1,171 +0,0 @@ -/*! - * Connect - multipart - * Copyright(c) 2010 Sencha Inc. - * Copyright(c) 2011 TJ Holowaychuk - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var multiparty = require('multiparty') - , _limit = require('./limit') - , utils = require('../utils') - , qs = require('qs'); - -/** - * Multipart: - * - * Status: Deprecated. The multipart parser will be removed in Connect 3.0. - * Please use one of the following parsers/middleware directly: - * - * - [formidable](https://github.com/felixge/node-formidable) - * - [connect-multiparty](https://github.com/superjoe30/connect-multiparty) or [multiparty] - * - [connect-busboy](https://github.com/mscdex/connect-busboy) or [busboy](https://github.com/mscdex/busboy) - * - * Parse multipart/form-data request bodies, - * providing the parsed object as `req.body` - * and `req.files`. - * - * Configuration: - * - * The options passed are merged with [multiparty](https://github.com/superjoe30/node-multiparty)'s - * `Form` object, allowing you to configure the upload directory, - * size limits, etc. For example if you wish to change the upload dir do the following. - * - * app.use(connect.multipart({ uploadDir: path })); - * - * Options: - * - * - `limit` byte limit defaulting to [100mb] - * - `defer` defers processing and exposes the multiparty form object as `req.form`. - * `next()` is called without waiting for the form's "end" event. - * This option is useful if you need to bind to the "progress" or "part" events, for example. - * - * Temporary Files: - * - * By default temporary files are used, stored in `os.tmpDir()`. These - * are not automatically garbage collected, you are in charge of moving them - * or deleting them. When `defer` is not used and these files are created you - * may refernce them via the `req.files` object. - * - * req.files.images.forEach(function(file){ - * console.log(' uploaded : %s %skb : %s', file.originalFilename, file.size / 1024 | 0, file.path); - * }); - * - * It is highly recommended to monitor and clean up tempfiles in any production - * environment, you may use tools like [reap](https://github.com/visionmedia/reap) - * to do so. - * - * Streaming: - * - * When `defer` is used files are _not_ streamed to tmpfiles, you may - * access them via the "part" events and stream them accordingly: - * - * req.form.on('part', function(part){ - * // transfer to s3 etc - * console.log('upload %s %s', part.name, part.filename); - * var out = fs.createWriteStream('/tmp/' + part.filename); - * part.pipe(out); - * }); - * - * req.form.on('close', function(){ - * res.end('uploaded!'); - * }); - * - * @param {Object} options - * @return {Function} - * @api public - */ - -exports = module.exports = function(options){ - options = options || {}; - - if (process.env.NODE_ENV !== 'test') { - console.warn('connect.multipart() will be removed in connect 3.0'); - console.warn('visit https://github.com/senchalabs/connect/wiki/Connect-3.0 for alternatives'); - } - - var limit = _limit(options.limit || '100mb'); - - return function multipart(req, res, next) { - if (req._body) return next(); - req.body = req.body || {}; - req.files = req.files || {}; - - if (!utils.hasBody(req)) return next(); - - // ignore GET - if ('GET' == req.method || 'HEAD' == req.method) return next(); - - // check Content-Type - if ('multipart/form-data' != utils.mime(req)) return next(); - - // flag as parsed - req._body = true; - - // parse - limit(req, res, function(err){ - if (err) return next(err); - - var form = new multiparty.Form(options) - , data = {} - , files = {} - , done; - - Object.keys(options).forEach(function(key){ - form[key] = options[key]; - }); - - function ondata(name, val, data){ - if (Array.isArray(data[name])) { - data[name].push(val); - } else if (data[name]) { - data[name] = [data[name], val]; - } else { - data[name] = val; - } - } - - form.on('field', function(name, val){ - ondata(name, val, data); - }); - - if (!options.defer) { - form.on('file', function(name, val){ - val.name = val.originalFilename; - val.type = val.headers['content-type'] || null; - ondata(name, val, files); - }); - } - - form.on('error', function(err){ - if (!options.defer) { - err.status = 400; - next(err); - } - done = true; - }); - - form.on('close', function(){ - if (done) return; - try { - req.body = qs.parse(data); - req.files = qs.parse(files); - } catch (err) { - form.emit('error', err); - return; - } - if (!options.defer) next(); - }); - - form.parse(req); - - if (options.defer) { - req.form = form; - next(); - } - }); - } -}; diff --git a/lib/middleware/staticCache.js b/lib/middleware/staticCache.js deleted file mode 100644 index 66d3c1f62..000000000 --- a/lib/middleware/staticCache.js +++ /dev/null @@ -1,238 +0,0 @@ - -/*! - * Connect - staticCache - * Copyright(c) 2011 Sencha Inc. - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var utils = require('../utils') - , Cache = require('../cache') - , fresh = require('fresh'); - -/** - * Static cache: - * - * Status: Deprecated. This middleware will be removed in - * Connect 3.0. You may be interested in: - * - * - [st](https://github.com/isaacs/st) - * - * Enables a memory cache layer on top of - * the `static()` middleware, serving popular - * static files. - * - * By default a maximum of 128 objects are - * held in cache, with a max of 256k each, - * totalling ~32mb. - * - * A Least-Recently-Used (LRU) cache algo - * is implemented through the `Cache` object, - * simply rotating cache objects as they are - * hit. This means that increasingly popular - * objects maintain their positions while - * others get shoved out of the stack and - * garbage collected. - * - * Benchmarks: - * - * static(): 2700 rps - * node-static: 5300 rps - * static() + staticCache(): 7500 rps - * - * Options: - * - * - `maxObjects` max cache objects [128] - * - `maxLength` max cache object length 256kb - * - * @param {Object} options - * @return {Function} - * @api public - */ - -module.exports = function staticCache(options){ - var options = options || {} - , cache = new Cache(options.maxObjects || 128) - , maxlen = options.maxLength || 1024 * 256; - - if (process.env.NODE_ENV !== 'test') { - console.warn('connect.staticCache() is deprecated and will be removed in 3.0'); - console.warn('use varnish or similar reverse proxy caches.'); - } - - return function staticCache(req, res, next){ - var key = cacheKey(req) - , ranges = req.headers.range - , hasCookies = req.headers.cookie - , hit = cache.get(key); - - // cache static - // TODO: change from staticCache() -> cache() - // and make this work for any request - req.on('static', function(stream){ - var headers = res._headers - , cc = utils.parseCacheControl(headers['cache-control'] || '') - , contentLength = headers['content-length'] - , hit; - - // dont cache set-cookie responses - if (headers['set-cookie']) return hasCookies = true; - - // dont cache when cookies are present - if (hasCookies) return; - - // ignore larger files - if (!contentLength || contentLength > maxlen) return; - - // don't cache partial files - if (headers['content-range']) return; - - // dont cache items we shouldn't be - // TODO: real support for must-revalidate / no-cache - if ( cc['no-cache'] - || cc['no-store'] - || cc['private'] - || cc['must-revalidate']) return; - - // if already in cache then validate - if (hit = cache.get(key)){ - if (headers.etag == hit[0].etag) { - hit[0].date = new Date; - return; - } else { - cache.remove(key); - } - } - - // validation notifiactions don't contain a steam - if (null == stream) return; - - // add the cache object - var arr = []; - - // store the chunks - stream.on('data', function(chunk){ - arr.push(chunk); - }); - - // flag it as complete - stream.on('end', function(){ - var cacheEntry = cache.add(key); - delete headers['x-cache']; // Clean up (TODO: others) - cacheEntry.push(200); - cacheEntry.push(headers); - cacheEntry.push.apply(cacheEntry, arr); - }); - }); - - if (req.method == 'GET' || req.method == 'HEAD') { - if (ranges) { - next(); - } else if (!hasCookies && hit && !mustRevalidate(req, hit)) { - res.setHeader('X-Cache', 'HIT'); - respondFromCache(req, res, hit); - } else { - res.setHeader('X-Cache', 'MISS'); - next(); - } - } else { - next(); - } - } -}; - -/** - * Respond with the provided cached value. - * TODO: Assume 200 code, that's iffy. - * - * @param {Object} req - * @param {Object} res - * @param {Object} cacheEntry - * @return {String} - * @api private - */ - -function respondFromCache(req, res, cacheEntry) { - var status = cacheEntry[0] - , headers = utils.merge({}, cacheEntry[1]) - , content = cacheEntry.slice(2); - - headers.age = (new Date - new Date(headers.date)) / 1000 || 0; - - switch (req.method) { - case 'HEAD': - res.writeHead(status, headers); - res.end(); - break; - case 'GET': - if (utils.conditionalGET(req) && fresh(req.headers, headers)) { - headers['content-length'] = 0; - res.writeHead(304, headers); - res.end(); - } else { - res.writeHead(status, headers); - - function write() { - while (content.length) { - if (false === res.write(content.shift())) { - res.once('drain', write); - return; - } - } - res.end(); - } - - write(); - } - break; - default: - // This should never happen. - res.writeHead(500, ''); - res.end(); - } -} - -/** - * Determine whether or not a cached value must be revalidated. - * - * @param {Object} req - * @param {Object} cacheEntry - * @return {String} - * @api private - */ - -function mustRevalidate(req, cacheEntry) { - var cacheHeaders = cacheEntry[1] - , reqCC = utils.parseCacheControl(req.headers['cache-control'] || '') - , cacheCC = utils.parseCacheControl(cacheHeaders['cache-control'] || '') - , cacheAge = (new Date - new Date(cacheHeaders.date)) / 1000 || 0; - - if ( cacheCC['no-cache'] - || cacheCC['must-revalidate'] - || cacheCC['proxy-revalidate']) return true; - - if (reqCC['no-cache']) return true; - - if (null != reqCC['max-age']) return reqCC['max-age'] < cacheAge; - - if (null != cacheCC['max-age']) return cacheCC['max-age'] < cacheAge; - - return false; -} - -/** - * The key to use in the cache. For now, this is the URL path and query. - * - * 'http://example.com?key=value' -> '/?key=value' - * - * @param {Object} req - * @return {String} - * @api private - */ - -function cacheKey(req) { - return utils.parseUrl(req).path; -} diff --git a/package.json b/package.json index 298cd72c7..d0ad42f72 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,7 @@ "debug": ">= 0.7.3 < 1", "methods": "0.1.0", "raw-body": "1.1.2", - "negotiator": "0.3.0", - "multiparty": "2.2.0" + "negotiator": "0.3.0" }, "devDependencies": { "should": ">= 2.0.2 < 3", diff --git a/support/docs.jade b/support/docs.jade deleted file mode 100644 index 69d083a94..000000000 --- a/support/docs.jade +++ /dev/null @@ -1,38 +0,0 @@ -doctype html -html - head - title Connect - High quality middleware for node.js - meta(http-equiv="Content-Type", content="text/html; charset=utf-8") - link(rel='stylesheet', href='style.css') - script(src='jquery.js') - script(src='docs.js') - body - #content - h1 Connect - for comment in comments - unless ignore(comment) - .comment(id=id(comment)) - h2= title(comment) - .description!= comment.description.full - - if comment.tags.length - ul.tags - for tag in comment.tags - if tag.types - if 'param' == tag.type - li #{tag.types.join(' | ')} #{tag.name} #{tag.description} - else - li returns #{tag.types.join(' | ')} #{tag.description} - else if tag.name - li #{tag.name} #{tag.description} - - if comment.code - h3 Source - pre - code!= comment.code - - ul#menu - for comment in comments - unless ignore(comment) - li - a(href='#' + id(comment))= title(comment) diff --git a/support/docs.js b/support/docs.js deleted file mode 100644 index fb70d95c4..000000000 --- a/support/docs.js +++ /dev/null @@ -1,46 +0,0 @@ - -var fs = require('fs') - , jade = require('jade'); - -var tmpl = fs.readFileSync('support/docs.jade', 'utf8'); -var fn = jade.compile(tmpl); - -var json = ''; -process.stdin.setEncoding('utf8'); -process.stdin.on('data', function(chunk){ - json += chunk; -}).on('end', function(){ - json = JSON.parse(json); - render(json); -}).resume(); - -function title(comment) { - if (!comment.ctx) return ''; - if (~comment.ctx.string.indexOf('module.exports')) return ''; - if (~comment.ctx.string.indexOf('prototype')) { - return comment.ctx.string.replace('.prototype.', '#'); - } else { - return comment.ctx.string; - } -} - -function id(comment) { - if (!comment.ctx) return ''; - return comment.ctx.string - .replace('()', ''); -} - -function ignore(comment) { - return comment.ignore - || (comment.ctx && ~comment.ctx.string.indexOf('__proto__')) - || ~comment.description.full.indexOf('Module dependencies'); -} - -function render(obj) { - process.stdout.write(fn({ - comments: obj - , ignore: ignore - , title: title - , id: id - })); -} \ No newline at end of file diff --git a/test/bodyParser.js b/test/bodyParser.js index 828db2bdc..beb4f868a 100644 --- a/test/bodyParser.js +++ b/test/bodyParser.js @@ -41,261 +41,4 @@ describe('connect.bodyParser()', function(){ done(); }); }) - - describe('with multipart/form-data', function(){ - it('should populate req.body', function(done){ - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('{"user":"Tobi"}'); - done(); - }); - }) - - it('should support files', function(done){ - var app = connect(); - - app.use(connect.bodyParser()); - - app.use(function(req, res){ - assert('Tobi' == req.body.user.name); - req.files.text.originalFilename.should.equal('foo.txt'); - req.files.text.path.should.include('.txt'); - res.end(req.files.text.originalFilename); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user[name]"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="text"; filename="foo.txt"\r\n') - .write('\r\n') - .write('some text here') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('foo.txt'); - done(); - }); - }) - - it('should expose options to multiparty', function(done){ - var app = connect(); - - app.use(connect.bodyParser({ - keepExtensions: true - })); - - app.use(function(req, res){ - assert('Tobi' == req.body.user.name); - assert(~req.files.text.path.indexOf('.txt')); - res.end(req.files.text.originalFilename); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user[name]"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="text"; filename="foo.txt"\r\n') - .write('\r\n') - .write('some text here') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('foo.txt'); - done(); - }); - }) - - it('should work with multiple fields', function(done){ - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="age"\r\n') - .write('\r\n') - .write('1') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('{"user":"Tobi","age":"1"}'); - done(); - }); - }) - - it('should support nesting', function(done){ - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user[name][first]"\r\n') - .write('\r\n') - .write('tobi') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="user[name][last]"\r\n') - .write('\r\n') - .write('holowaychuk') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="user[age]"\r\n') - .write('\r\n') - .write('1') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="species"\r\n') - .write('\r\n') - .write('ferret') - .write('\r\n--foo--') - .end(function(res){ - var obj = JSON.parse(res.body); - obj.user.age.should.equal('1'); - obj.user.name.should.eql({ first: 'tobi', last: 'holowaychuk' }); - obj.species.should.equal('ferret'); - done(); - }); - }) - - it('should support multiple files of the same name', function(done){ - var app = connect(); - - app.use(connect.bodyParser()); - - app.use(function(req, res){ - req.files.text.should.have.length(2); - assert(req.files.text[0]); - assert(req.files.text[1]); - res.end(); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="text"; filename="foo.txt"\r\n') - .write('\r\n') - .write('some text here') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="text"; filename="bar.txt"\r\n') - .write('\r\n') - .write('some more text stuff') - .write('\r\n--foo--') - .end(function(res){ - res.statusCode.should.equal(200); - done(); - }); - }) - - it('should support nested files', function(done){ - var app = connect(); - - app.use(connect.bodyParser()); - - app.use(function(req, res){ - Object.keys(req.files.docs).should.have.length(2); - req.files.docs.foo.originalFilename.should.equal('foo.txt'); - req.files.docs.bar.originalFilename.should.equal('bar.txt'); - res.end(); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="docs[foo]"; filename="foo.txt"\r\n') - .write('\r\n') - .write('some text here') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="docs[bar]"; filename="bar.txt"\r\n') - .write('\r\n') - .write('some more text stuff') - .write('\r\n--foo--') - .end(function(res){ - res.statusCode.should.equal(200); - done(); - }); - }) - - it('should next(err) on multipart failure', function(done){ - var app = connect(); - - app.use(connect.bodyParser()); - - app.use(function(req, res){ - res.end('whoop'); - }); - - app.use(function(err, req, res, next){ - err.message.should.equal('parser error, 16 of 28 bytes parsed'); - res.statusCode = 500; - res.end(); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-filename="foo.txt"\r\n') - .write('\r\n') - .write('some text here') - .write('Content-Disposition: form-data; name="text"; filename="bar.txt"\r\n') - .write('\r\n') - .write('some more text stuff') - .write('\r\n--foo--') - .end(function(res){ - res.statusCode.should.equal(500); - done(); - }); - }) - }) - - // I'm too lazy to test this in both `.json()` and `.urlencoded()` - describe('verify', function () { - it('should throw 403 if verification fails', function (done) { - var app = connect(); - - app.use(connect.bodyParser({ - verify: function () { - throw new Error(); - } - })) - - app.request() - .post('/') - .set('Content-Type', 'application/json') - .write('{"user":"tobi"}') - .expect(403, done); - }) - - it('should not throw if verification does not throw', function (done) { - var app = connect(); - - app.use(connect.bodyParser({ - verify: function () {} - })) - - app.use(function (req, res, next) { - res.statusCode = 204; - res.end(); - }) - - app.request() - .post('/') - .set('Content-Type', 'application/json') - .write('{"user":"tobi"}') - .expect(204, done); - }) - }) }) diff --git a/test/limit.js b/test/limit.js deleted file mode 100644 index 8eeb1c053..000000000 --- a/test/limit.js +++ /dev/null @@ -1,30 +0,0 @@ - -var connect = require('../'); - -var app = connect(); - -app.use(connect.limit('5kb')); - -app.use(function(req, res){ - res.end('stuff'); -}); - -describe('connect.limit()', function(){ - describe('when Content-Length is below', function(){ - it('should bypass limit()', function(done){ - app.request() - .post('/') - .set('Content-Length', 500) - .expect(200, done); - }) - }) - - describe('when Content-Length is too large', function(){ - it('should respond with 413', function(done){ - app.request() - .post('/') - .set('Content-Length', 10 * 1024) - .expect(413, done); - }) - }) -}) \ No newline at end of file diff --git a/test/multipart.js b/test/multipart.js deleted file mode 100644 index 919c29459..000000000 --- a/test/multipart.js +++ /dev/null @@ -1,300 +0,0 @@ - -var connect = require('../'); -var assert = require('assert'); -var should = require('./shared'); - -var app = connect(); - -app.use(connect.multipart({ limit: '20mb' })); - -app.use(function(req, res){ - res.end(JSON.stringify(req.body)); -}); - -describe('connect.multipart()', function(){ - should['default request body'](app); - should['limit body to']('20mb', 'multipart/form-data', app); - - it('should ignore GET', function(done){ - app.request() - .get('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('{}'); - done(); - }); - }) - - describe('with multipart/form-data', function(){ - it('should populate req.body', function(done){ - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('{"user":"Tobi"}'); - done(); - }); - }) - - it('should support files', function(done){ - var app = connect(); - - app.use(connect.multipart()); - - app.use(function(req, res){ - assert('Tobi' == req.body.user.name); - assert('text/plain' == req.files.text.type); - res.end(req.files.text.name); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user[name]"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="text"; filename="foo.txt"\r\n') - .write('Content-Type: text/plain\r\n') - .write('\r\n') - .write('some text here') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('foo.txt'); - done(); - }); - }) - - it('should expose options to multiparty', function(done){ - var app = connect(); - - app.use(connect.multipart({ - keepExtensions: true - })); - - app.use(function(req, res){ - assert('Tobi' == req.body.user.name); - assert(~req.files.text.path.indexOf('.txt')); - res.end(req.files.text.originalFilename); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user[name]"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="text"; filename="foo.txt"\r\n') - .write('\r\n') - .write('some text here') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('foo.txt'); - done(); - }); - }) - - it('should work with multiple fields', function(done){ - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="age"\r\n') - .write('\r\n') - .write('1') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('{"user":"Tobi","age":"1"}'); - done(); - }); - }) - - it('should support nesting', function(done){ - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user[name][first]"\r\n') - .write('\r\n') - .write('tobi') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="user[name][last]"\r\n') - .write('\r\n') - .write('holowaychuk') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="user[age]"\r\n') - .write('\r\n') - .write('1') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="species"\r\n') - .write('\r\n') - .write('ferret') - .write('\r\n--foo--') - .end(function(res){ - var obj = JSON.parse(res.body); - obj.user.age.should.equal('1'); - obj.user.name.should.eql({ first: 'tobi', last: 'holowaychuk' }); - obj.species.should.equal('ferret'); - done(); - }); - }) - - it('should support multiple files of the same name', function(done){ - var app = connect(); - - app.use(connect.multipart()); - - app.use(function(req, res){ - req.files.text.should.have.length(2); - assert(req.files.text[0]); - assert(req.files.text[1]); - res.end(); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="text"; filename="foo.txt"\r\n') - .write('\r\n') - .write('some text here') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="text"; filename="bar.txt"\r\n') - .write('\r\n') - .write('some more text stuff') - .write('\r\n--foo--') - .end(function(res){ - res.statusCode.should.equal(200); - done(); - }); - }) - - it('should support nested files', function(done){ - var app = connect(); - - app.use(connect.multipart()); - - app.use(function(req, res){ - Object.keys(req.files.docs).should.have.length(2); - req.files.docs.foo.originalFilename.should.equal('foo.txt'); - req.files.docs.bar.originalFilename.should.equal('bar.txt'); - res.end(); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="docs[foo]"; filename="foo.txt"\r\n') - .write('\r\n') - .write('some text here') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="docs[bar]"; filename="bar.txt"\r\n') - .write('\r\n') - .write('some more text stuff') - .write('\r\n--foo--') - .end(function(res){ - res.statusCode.should.equal(200); - done(); - }); - }) - - it('should next(err) on multipart failure', function(done){ - var app = connect(); - - app.use(connect.multipart()); - - app.use(function(req, res){ - res.end('whoop'); - }); - - app.use(function(err, req, res, next){ - err.message.should.equal('Expected alphabetic character, received 61'); - res.statusCode = err.status; - res.end('bad request'); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-filename="foo.txt"\r\n') - .write('\r\n') - .write('some text here') - .write('Content-Disposition: form-data; name="text"; filename="bar.txt"\r\n') - .write('\r\n') - .write('some more text stuff') - .write('\r\n--foo--') - .end(function(res){ - res.statusCode.should.equal(400); - res.body.should.equal('bad request'); - done(); - }); - }) - - it('should default req.files to {}', function(done){ - var app = connect(); - - app.use(connect.multipart()); - - app.use(function(req, res){ - res.end(JSON.stringify(req.files)); - }); - - app.request() - .post('/') - .end(function(res){ - res.body.should.equal('{}'); - done(); - }); - }) - - it('should defer processing if `defer` is set', function(done){ - var app = connect(); - - app.use(connect.multipart({ defer: true })); - - app.use(function(req, res){ - JSON.stringify(req.body).should.equal("{}"); - req.form.on('close', function() { - res.end(JSON.stringify(req.body)); - }); - }); - - app.request() - .post('/') - .set('Content-Type', 'multipart/form-data; boundary=foo') - .write('--foo\r\n') - .write('Content-Disposition: form-data; name="user"\r\n') - .write('\r\n') - .write('Tobi') - .write('\r\n--foo\r\n') - .write('Content-Disposition: form-data; name="age"\r\n') - .write('\r\n') - .write('1') - .write('\r\n--foo--') - .end(function(res){ - res.body.should.equal('{"user":"Tobi","age":"1"}'); - done(); - }); - }) - - }) -}) diff --git a/test/server.js b/test/server.js index afb976fea..bf19b1889 100644 --- a/test/server.js +++ b/test/server.js @@ -23,11 +23,10 @@ describe('app', function(){ it('should allow old-style constructor middleware', function(done){ var app = connect( connect.json() - , connect.multipart() , connect.urlencoded() , function(req, res){ res.end(JSON.stringify(req.body)) }); - app.stack.should.have.length(4); + app.stack.should.have.length(3); app.request() .post('/') @@ -39,10 +38,9 @@ describe('app', function(){ it('should allow old-style .createServer()', function(){ var app = connect.createServer( connect.json() - , connect.multipart() , connect.urlencoded()); - app.stack.should.have.length(3); + app.stack.should.have.length(2); }) it('should work as middlware', function(done){