Repository: expressjs/connect-multiparty Branch: master Commit: 7c50f097dcc7 Files: 11 Total size: 17.2 KB Directory structure: gitextract_6_3s62n3/ ├── .editorconfig ├── .eslintignore ├── .eslintrc.yml ├── .gitignore ├── .travis.yml ├── HISTORY.md ├── LICENSE ├── README.md ├── index.js ├── package.json └── test/ └── multipart.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # http://editorconfig.org root = true [*] charset = utf-8 insert_final_newline = true trim_trailing_whitespace = true [{*.js,*.json,*.yml}] indent_size = 2 indent_style = space ================================================ FILE: .eslintignore ================================================ coverage node_modules ================================================ FILE: .eslintrc.yml ================================================ rules: eol-last: error indent: ["error", 2, { "SwitchCase": 1 }] no-trailing-spaces: error ================================================ FILE: .gitignore ================================================ .nyc_output/ coverage/ node_modules/ npm-debug.log package-lock.json ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "0.10" - "4.9" - "6.17" - "8.16" - "10.16" - "12.11" sudo: false cache: directories: - node_modules before_install: # Configure npm - | # Skip updating shrinkwrap / lock npm config set shrinkwrap false # Setup Node.js version-specific dependencies - | # eslint for linting # - remove for Node.js < 8 if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 8 ]]; then npm rm --save-dev eslint fi - | # mocha for testing # - use 3.x for Node.js < 4 # - use 5.x for Node.js < 6 if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then npm install --save-dev mocha@3.5.3 elif [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then npm install --save-dev mocha@5.2.0 fi - | # nyc for coverage # - use 10.x for Node.js < 4 # - use 11.x for Node.js < 6 if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then npm install --save-dev nyc@10.3.2 elif [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then npm install --save-dev nyc@11.9.0 fi - | # supertest for http calls # - use 2.0.0 for Node.js < 4 # - use 3.4.2 for Node.js < 6 if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then npm install --save-dev supertest@2.0.0 elif [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then npm install --save-dev supertest@3.4.2 fi # Update Node.js modules - | # Prune & rebuild node_modules if [[ -d node_modules ]]; then npm prune npm rebuild fi script: - | # Run test script npm run-script test-ci - | # Run linting if npm -ps ls eslint | grep -q eslint; then npm run-script lint fi after_script: - | # Upload coverage to coveralls, if exists if [[ -d .nyc_output ]]; then npm install --save-dev coveralls@2 nyc report --reporter=text-lcov | coveralls fi ================================================ FILE: HISTORY.md ================================================ unreleased ========== * deps: http-errors@~1.7.3 - Fix error creating objects in some environments - deps: inherits@2.0.4 - deps: setprototypeof@1.1.1 * deps: qs@~6.7.0 - Fix parsing array brackets after index * deps: safe-buffer@5.2.0 * deps: type-is@~1.6.18 - deps: mime-types@~2.1.24 - perf: prevent internal `throw` on invalid type 2.2.0 / 2018-09-14 ================== * Fix masking `multiparty` errors as 400 * deps: multiparty@~4.2.1 - Use http-errors for raised errors - Use uid-safe module to for temp file names - Update to fd-slicer 1.1.0 - perf: remove parameter reassignment 2.1.1 / 2018-06-22 ================== * deps: multiparty@~4.1.4 - Enable strict mode - Fix file extension filtering stopping on certain whitespace characters - Use safe-buffer for improved API safety * deps: qs@~6.5.2 * deps: type-is@~1.6.16 - deps: mime-types@~2.1.18 2.1.0 / 2017-10-19 ================== * deps: multiparty@~4.1.3 - Use `os.tmpdir()` instead of `os.tmpDir()` - deps: fd-slicer@~1.0.1 * deps: qs@~6.5.1 - Fix array parsing from skipping empty values - Fix compacting of nested sparse arrays - Fix parsing & compacting very deep objects * deps: type-is@~1.6.15 - Fix type error when given invalid type to match against - deps: mime-types@~2.1.15 * perf: enable strict mode 2.0.0 / 2015-07-13 ================== * Requires Node.js >= 0.10.0 * deps: multiparty@~4.1.2 * deps: on-finished@~2.3.0 * deps: qs@~4.0.0 * deps: type-is@~1.6.4 1.2.5 / 2014-10-14 ================== * Update qs to 2.2.4 * Update type-is to 1.5.2 1.2.4 / 2014-08-29 ================== * Update qs to 2.2.2 1.2.3 / 2014-08-28 ================== * Update qs to 2.2.1 1.2.2 / 2014-08-27 ================== * Update qs to 2.2.0 1.2.1 / 2014-08-07 ================== * Update multiparty to 3.3.2 * Update qs to 1.2.0 1.2.0 / 2014-08-06 ================== * Update multiparty to 3.3.1 * Update qs to 1.1.0 1.1.0 / 2014-07-03 ================== * Update multiparty to 3.3.0 * Use type-is to check Content-Type 1.0.6 / 2014-07-03 ================== * Fix callback hang in node.js 0.8 on errors 1.0.5 / 2014-06-01 ================== * Update multiparty to 3.2.8 1.0.4 / 2014-05-26 ================== * Fix error causing response to hang * Update multiparty to 3.2.6 1.0.3 / 2014-01-20 ================== * Update multiparty to 3.2 1.0.2 / 2014-01-17 ================== * Update multiparty to 3.1 1.0.1 / 2013-10-25 ================== * Update multiparty to 3.0 1.0.0 / 2013-10-25 ================== * revive ================================================ FILE: LICENSE ================================================ (The MIT License) Copyright (c) 2010 Sencha Inc. Copyright (c) 2011 TJ Holowaychuk Copyright (c) 2013 Andrew Kelley Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ > [!CAUTION] > **This repository is archived and no longer actively maintained.** > > We are no longer accepting issues, feature requests, or pull requests. > For additional support or questions, please visit the [Express.js Discussions page](https://github.com/expressjs/express/discussions). # connect-multiparty [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Build Status][travis-image]][travis-url] [![Test Coverage][coveralls-image]][coveralls-url] [connect](https://github.com/senchalabs/connect/) middleware for [multiparty](https://github.com/andrewrk/node-multiparty/). I actually recommend against using this module. It's cleaner to use the multiparty API directly. This middleware will create temp files on your server and never clean them up. Thus you should not add this middleware to all routes; only to the ones in which you want to accept uploads. And in these endpoints, be sure to delete all temp files, even the ones that you don't use. ## Usage ```js var multipart = require('connect-multiparty'); var multipartMiddleware = multipart(); app.post('/upload', multipartMiddleware, function(req, resp) { console.log(req.body, req.files); // don't forget to delete all req.files when done }); ``` If you pass options to `multipart()`, they are passed directly into multiparty. ## License [MIT](LICENSE) [coveralls-image]: https://img.shields.io/coveralls/expressjs/connect-multiparty/master.svg [coveralls-url]: https://coveralls.io/r/expressjs/connect-multiparty?branch=master [downloads-image]: https://img.shields.io/npm/dm/connect-multiparty.svg [downloads-url]: https://npmjs.org/package/connect-multiparty [npm-image]: https://img.shields.io/npm/v/connect-multiparty.svg [npm-url]: https://npmjs.org/package/connect-multiparty [travis-image]: https://img.shields.io/travis/expressjs/connect-multiparty/master.svg [travis-url]: https://travis-ci.org/expressjs/connect-multiparty ================================================ FILE: index.js ================================================ /*! * connect-multiparty * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * Copyright(c) 2013 Andrew Kelley * MIT Licensed */ 'use strict' /** * Module dependencies. * @private */ var createError = require('http-errors') var multiparty = require('multiparty'); var onFinished = require('on-finished'); var qs = require('qs'); var typeis = require('type-is'); /** * Module exports. * @public */ module.exports = multipart /** * Parse multipart/form-data request bodies, providing the parsed * object as `req.body` and `req.files`. * * The options passed are merged with [multiparty](https://github.com/pillarjs/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(multipart({ uploadDir: path })) * * @param {Object} options * @return {Function} * @public */ function multipart (options) { options = options || {}; return function multipart(req, res, next) { if (req._body) return next(); req.body = req.body || {}; req.files = req.files || {}; // ignore GET if ('GET' === req.method || 'HEAD' === req.method) return next(); // check Content-Type if (!typeis(req, 'multipart/form-data')) return next(); // flag as parsed req._body = true; // parse var form = new multiparty.Form(options); var data = {}; var files = {}; var done = false; 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); }); 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 (done) return; done = true; // set status code on error var error = createError(400, err) if (!req.readable) return next(error) // read off entire request req.resume(); onFinished(req, function(){ next(error) }); }); form.on('close', function() { if (done) return; done = true; // expand names with qs & assign req.body = qs.parse(data, { allowDots: true }) req.files = qs.parse(files, { allowDots: true }) next() }); form.parse(req); } }; ================================================ FILE: package.json ================================================ { "name": "connect-multiparty", "version": "2.2.0", "description": "multipart parsing middleware for connect using multiparty", "author": "Andrew Kelley ", "contributors": [ "Douglas Christopher Wilson " ], "license": "MIT", "repository": "expressjs/connect-multiparty", "dependencies": { "http-errors": "~1.7.3", "multiparty": "~4.2.1", "on-finished": "~2.3.0", "qs": "~6.7.0", "type-is": "~1.6.18" }, "engines": { "node": ">=0.10.0" }, "devDependencies": { "connect": "3.7.0", "deep-equal": "1.0.1", "eslint": "6.5.1", "mocha": "6.2.1", "nyc": "14.1.1", "safe-buffer": "5.2.0", "supertest": "4.0.2" }, "files": [ "HISTORY.md", "LICENSE", "README.md", "index.js" ], "scripts": { "lint": "eslint .", "test": "mocha --reporter spec", "test-ci": "nyc --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test" } } ================================================ FILE: test/multipart.js ================================================ process.env.NODE_ENV = 'test'; var Buffer = require('safe-buffer').Buffer var connect = require('connect'); var deepEqual = require('deep-equal') var multipart = require('..'); var request = require('supertest'); describe('multipart()', function(){ it('should ignore GET', function(done){ request(createServer()) .get('/body') .field('user', 'Tobi') .expect(200, {}, done) }) describe('with multipart/form-data', function(){ it('should populate req.body', function(done){ request(createServer()) .post('/body') .field('user', 'Tobi') .expect(200, { user: 'Tobi' }, done) }) it('should handle duplicated middleware', function (done) { var app = connect() .use(multipart()) .use(multipart()) .use(function (req, res) { res.setHeader('Content-Type', 'application/json; charset=utf-8') res.end(JSON.stringify(req.body)) }) request(app) .post('/body') .field('user', 'Tobi') .expect(200, { user: 'Tobi' }, done) }) it('should support files', function(done){ request(createServer()) .post('/files') .attach('text', Buffer.from('some text here'), 'foo.txt') .expect(200) .expect(shouldDeepIncludeInBody({ text: { name: 'foo.txt', originalFilename: 'foo.txt', size: 14, type: 'text/plain' } })) .end(done) }) it('should work with multiple fields', function(done){ request(createServer()) .post('/body') .field('user', 'Tobi') .field('age', '1') .expect(200, { user: 'Tobi', age: '1' }, done) }) it('should handle duplicated fields', function (done) { request(createServer()) .post('/body') .field('user', 'Tobi') .field('user', 'Loki') .field('user', 'Poki') .expect(200, { user: [ 'Tobi', 'Loki', 'Poki' ] }, done) }) it('should support nesting', function(done){ request(createServer()) .post('/body') .field('user[name][first]', 'tobi') .field('user[name][last]', 'holowaychuk') .field('user[age]', '1') .field('species', 'ferret') .expect(200, { species: 'ferret', user: { age: '1', name: { first: 'tobi', last: 'holowaychuk' } } }, done) }) it('should support multiple files of the same name', function(done){ request(createServer()) .post('/files') .attach('text', Buffer.from('some text here'), 'foo.txt') .attach('text', Buffer.from('some more text stuff'), 'bar.txt') .expect(200) .expect(shouldDeepIncludeInBody({ text: [ { name: 'foo.txt' }, { name: 'bar.txt' } ] })) .end(done) }) it('should support nested files', function(done){ request(createServer()) .post('/files') .attach('docs[foo]', Buffer.from('some text here'), 'foo.txt') .attach('docs[bar]', Buffer.from('some more text stuff'), 'bar.txt') .expect(200) .expect(shouldDeepIncludeInBody({ docs: { foo: { name: 'foo.txt' }, bar: { name: 'bar.txt' } } })) .end(done) }) it('should next(err) on multipart failure', function(done){ var app = createServer() var test = request(app).post('/') test.set('Content-Type', 'multipart/form-data; boundary=foo'); test.write('--foo\r\n'); test.write('Content-filename="foo.txt"\r\n'); test.write('\r\n'); test.write('some text here'); test.write('Content-Disposition: form-data; name="text"; filename="bar.txt"\r\n'); test.write('\r\n'); test.write('some more text stuff'); test.write('\r\n--foo--'); test.expect(400, 'BadRequestError: Expected alphabetic character, received 61', done) }) it('should not hang request on failure', function(done){ var app = createServer() var buf = Buffer.alloc(1024 * 10, '.') var test = request(app).post('/') test.set('Content-Type', 'multipart/form-data; boundary=foo'); test.write('--foo\r\n'); test.write('Content-filename="foo.txt"\r\n'); test.write('\r\n'); test.write('some text here'); test.write('Content-Disposition: form-data; name="text"; filename="bar.txt"\r\n'); test.write('\r\n'); test.write('some more text stuff'); test.write('\r\n--foo--'); test.write(buf) test.write(buf) test.write(buf) test.expect(400, 'BadRequestError: Expected alphabetic character, received 61', done) }) it('should default req.files to {}', function(done){ request(createServer()) .post('/body') .expect(200, {}, done) }) it('should return 413 on maxFilesSize exceeded', function (done) { var max = Math.pow(2, 9) request(createServer({ maxFilesSize: max })) .post('/files') .field('user[name]', 'Tobi') .attach('text', Buffer.alloc(max + 1, 'x'), 'foo.txt') .expect(413, done) }) }) }) function createServer (opts) { var app = connect() app.use(multipart(opts)) app.use('/body', function (req, res) { res.setHeader('Content-Type', 'application/json; charset=utf-8') res.end(JSON.stringify(req.body)) }) app.use('/files', function (req, res) { res.setHeader('Content-Type', 'application/json; charset=utf-8') res.end(JSON.stringify(req.files)) }) app.use(function (err, req, res, next) { res.statusCode = err.statusCode || err.status || 500 res.end(err.name + ': ' + err.message) }) return app } function shouldDeepIncludeInBody (obj) { return function (res) { deepEqual(res.body, obj) } }