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

refactor busboy constructor - add esm #61

Merged
merged 17 commits into from
Dec 4, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ Busboy methods

* **headers** - _object_ - These are the HTTP headers of the incoming request, which are used by individual parsers.

* **autoDestroy** - _boolean_ - Whether this stream should automatically call .destroy() on itself after ending. (Default: false).

* **highWaterMark** - _integer_ - highWaterMark to use for this Busboy instance (Default: WritableStream default).

* **fileHwm** - _integer_ - highWaterMark to use for file streams (Default: ReadableStream default).
Expand Down
83 changes: 44 additions & 39 deletions lib/main.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
const WritableStream = require('stream').Writable
const inherits = require('util').inherits

const MultipartParser = require('./types/multipart')
const UrlencodedParser = require('./types/urlencoded')
const parseParams = require('./utils').parseParams

function Busboy (opts) {
Uzlopak marked this conversation as resolved.
Show resolved Hide resolved
if (!(this instanceof Busboy)) { return new Busboy(opts) }
if (opts.highWaterMark !== undefined) { WritableStream.call(this, { autoDestroy: false, highWaterMark: opts.highWaterMark }) } else { WritableStream.call(this, { autoDestroy: false }) }

if (typeof opts !== 'object') {
Uzlopak marked this conversation as resolved.
Show resolved Hide resolved
throw new TypeError('Busboy expected an options-Object.')
}
if (typeof opts.headers !== 'object') {
throw new TypeError('Busboy expected an options-Object with headers-attribute.')
}
if (typeof opts.headers['content-type'] !== 'string') {
throw new TypeError('Missing Content-Type-header.')
}

const {
headers,
...streamOptions
} = opts

this.opts = {
autoDestroy: false,
...streamOptions
}
WritableStream.call(this, this.opts)

this._done = false
this._parser = undefined
this._parser = this.parseHeaders(headers)
Uzlopak marked this conversation as resolved.
Show resolved Hide resolved
this._finished = false

this.opts = opts
if (opts.headers && typeof opts.headers['content-type'] === 'string') { this.parseHeaders(opts.headers) } else { throw new Error('Missing Content-Type') }
}
inherits(Busboy, WritableStream)

Expand All @@ -30,45 +49,31 @@ Busboy.prototype.emit = function (ev) {
}

Busboy.prototype.parseHeaders = function (headers) {
this._parser = undefined
if (headers['content-type']) {
const parsed = parseParams(headers['content-type'])
let matched; let type
for (var i = 0; i < TYPES.length; ++i) { // eslint-disable-line no-var
type = TYPES[i]
if (typeof type.detect === 'function') { matched = type.detect(parsed) } else { matched = type.detect.test(parsed[0]) }
if (matched) { break }
}
if (matched) {
const cfg = {
limits: this.opts.limits,
isPartAFile: this.opts.isPartAFile,
headers: headers,
parsedConType: parsed,
highWaterMark: undefined,
fileHwm: undefined,
defCharset: undefined,
preservePath: false
}
if (this.opts.highWaterMark) { cfg.highWaterMark = this.opts.highWaterMark }
if (this.opts.fileHwm) { cfg.fileHwm = this.opts.fileHwm }
cfg.defCharset = this.opts.defCharset
cfg.preservePath = this.opts.preservePath
this._parser = type(this, cfg)
return
}
const parsed = parseParams(headers['content-type'])

const cfg = {
defCharset: this.opts.defCharset,
fileHwm: this.opts.fileHwm,
headers: headers,
highWaterMark: this.opts.highWaterMark,
isPartAFile: this.opts.isPartAFile,
limits: this.opts.limits,
parsedConType: parsed,
preservePath: this.opts.preservePath
}
throw new Error('Unsupported content type: ' + headers['content-type'])

if (MultipartParser.detect.test(parsed[0])) {
return new MultipartParser(this, cfg)
}
if (UrlencodedParser.detect.test(parsed[0])) {
return new UrlencodedParser(this, cfg)
}
throw new Error('Unsupported Content-Type.')
}

Busboy.prototype._write = function (chunk, encoding, cb) {
if (!this._parser) { return cb(new Error('Not ready to parse. Missing Content-Type?')) }
this._parser.write(chunk, cb)
}

const TYPES = [
require('./types/multipart'),
require('./types/urlencoded')
]

module.exports = Busboy
module.exports.default = Busboy
46 changes: 46 additions & 0 deletions test/busboy-constructor.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const Busboy = require('../lib/main')
const { expect } = require('chai')

describe('busboy-constructor', () => {
it('should throw an Error if no options are provided', () => {
expect(() => new Busboy()).to.throw('Busboy expected an options-Object.')
})
it('should throw an Error if options does not contain headers', () => {
expect(() => new Busboy({})).to.throw('Busboy expected an options-Object with headers-attribute.')
})
it('if busboy is called without new-operator, still creates a busboy instance', () => {
const busboyInstance = Busboy({ headers: { 'content-type': 'application/x-www-form-urlencoded' } })
expect(busboyInstance).to.be.instanceOf(Busboy)
})
it('should throw an Error if content-type is not set', () => {
expect(() => new Busboy({ headers: {} })).to.throw('Missing Content-Type-header.')
})
it('should throw an Error if content-type is unsupported', () => {
expect(() => new Busboy({ headers: { 'content-type': 'unsupported' } })).to.throw('Unsupported Content-Type.')
})
it('should not throw an Error if content-type is multipart', () => {
expect(() => new Busboy({ headers: { 'content-type': 'multipart/form-data' } })).to.not.throw('Unsupported Content-Type.')
})
it('should not throw an Error if content-type is urlencoded', () => {
expect(() => new Busboy({ headers: { 'content-type': 'application/x-www-form-urlencoded' } })).to.not.throw('Unsupported Content-Type.')
})
it('if busboy is called without stream options autoDestroy is set to false', () => {
const busboyInstance = Busboy({ headers: { 'content-type': 'application/x-www-form-urlencoded' } })
expect(busboyInstance._writableState.autoDestroy).to.be.equal(false)
})
it('if busboy is called with invalid value for stream option highWaterMark we should throw', () => {
expect(() => Busboy({ highWaterMark: 'not_allowed_value_for_highWaterMark', headers: { 'content-type': 'application/x-www-form-urlencoded' } })).to.throw('not_allowed_value_for_highWaterMark')
})
it('if busboy is called with stream options and autoDestroy:true, autoDestroy should be set to true', () => {
const busboyInstance = Busboy({ autoDestroy: true, headers: { 'content-type': 'application/x-www-form-urlencoded' } })
expect(busboyInstance._writableState.autoDestroy).to.be.equal(true)
})
it('busboy should be initialized with private attribute _done set as false', () => {
const busboyInstance = Busboy({ headers: { 'content-type': 'application/x-www-form-urlencoded' } })
expect(busboyInstance._done).to.be.equal(false)
})
it('busboy should be initialized with private attribute _finished set as false', () => {
const busboyInstance = Busboy({ headers: { 'content-type': 'application/x-www-form-urlencoded' } })
expect(busboyInstance._finished).to.be.equal(false)
})
})