Skip to content

Commit d493781

Browse files
committed
fix: moving form-data usage over to using the native FormData object
1 parent f28927f commit d493781

File tree

5 files changed

+159
-90
lines changed

5 files changed

+159
-90
lines changed

.jshintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"asi": true,
3+
"browser": true,
34
"node": true
45
}

package-lock.json

Lines changed: 13 additions & 76 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@
8181
"chalk": "^1.1.1",
8282
"commander": "^2.9.0",
8383
"debug": "^2.2.0",
84-
"event-stream": "3.3.4",
85-
"form-data": "3.0.0",
84+
"formdata-polyfill": "^3.0.20",
8685
"fs-readfile-promise": "^2.0.1",
8786
"fs-writefile-promise": "^1.0.3",
8887
"har-validator": "^5.0.0",

src/helpers/form-data.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**
2+
* @license https://raw.githubusercontent.com/node-fetch/node-fetch/master/LICENSE.md
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2016 - 2020 Node Fetch Team
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in all
16+
* copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
* SOFTWARE.
25+
*/
26+
27+
const carriage = '\r\n'
28+
const dashes = '-'.repeat(2)
29+
const carriageLength = Buffer.byteLength(carriage)
30+
31+
const NAME = Symbol.toStringTag
32+
33+
const isBlob = object => {
34+
return (
35+
typeof object === 'object' &&
36+
typeof object.arrayBuffer === 'function' &&
37+
typeof object.type === 'string' &&
38+
typeof object.stream === 'function' &&
39+
typeof object.constructor === 'function' &&
40+
/^(Blob|File)$/.test(object[NAME])
41+
)
42+
}
43+
44+
/**
45+
* @param {string} boundary
46+
*/
47+
const getFooter = boundary => `${dashes}${boundary}${dashes}${carriage.repeat(2)}`
48+
49+
/**
50+
* @param {string} boundary
51+
* @param {string} name
52+
* @param {*} field
53+
*
54+
* @return {string}
55+
*/
56+
function getHeader (boundary, name, field) {
57+
let header = ''
58+
59+
header += `${dashes}${boundary}${carriage}`
60+
header += `Content-Disposition: form-data; name="${name}"`
61+
62+
if (isBlob(field)) {
63+
header += `; filename="${field.name}"${carriage}`
64+
header += `Content-Type: ${field.type || 'application/octet-stream'}`
65+
}
66+
67+
return `${header}${carriage.repeat(2)}`
68+
}
69+
70+
/**
71+
* @return {string}
72+
*/
73+
module.exports.getBoundary = () => {
74+
// This generates a 50 character boundary similar to those used by Firefox.
75+
// They are optimized for boyer-moore parsing.
76+
var boundary = '--------------------------'
77+
for (var i = 0; i < 24; i++) {
78+
boundary += Math.floor(Math.random() * 10).toString(16)
79+
}
80+
81+
return boundary
82+
}
83+
84+
/**
85+
* @param {FormData} form
86+
* @param {string} boundary
87+
*/
88+
module.exports.formDataIterator = function * (form, boundary) {
89+
for (const [name, value] of form) {
90+
yield getHeader(boundary, name, value)
91+
92+
if (isBlob(value)) {
93+
yield * value.stream()
94+
} else {
95+
yield value
96+
}
97+
98+
yield carriage
99+
}
100+
101+
yield getFooter(boundary)
102+
}
103+
104+
/**
105+
* @param {FormData} form
106+
* @param {string} boundary
107+
*/
108+
module.exports.getFormDataLength = function (form, boundary) {
109+
let length = 0
110+
111+
for (const [name, value] of form) {
112+
length += Buffer.byteLength(getHeader(boundary, name, value))
113+
114+
if (isBlob(value)) {
115+
length += value.size
116+
} else {
117+
length += Buffer.byteLength(String(value))
118+
}
119+
120+
length += carriageLength
121+
}
122+
123+
length += Buffer.byteLength(getFooter(boundary))
124+
125+
return length
126+
}
127+
128+
module.exports.isBlob = isBlob

src/index.js

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
'use strict'
2+
/* eslint-env browser */
3+
4+
require('formdata-polyfill')
25

36
var debug = require('debug')('httpsnippet')
4-
var es = require('event-stream')
5-
var MultiPartForm = require('form-data')
67
var qs = require('querystring')
78
var reducer = require('./helpers/reducer')
89
var targets = require('./targets')
910
var url = require('url')
1011
var validate = require('har-validator/lib/async')
1112

13+
const { formDataIterator, isBlob } = require('./helpers/form-data.js')
14+
1215
// constructor
1316
var HTTPSnippet = function (data) {
1417
var entries
@@ -102,24 +105,25 @@ HTTPSnippet.prototype.prepare = function (request) {
102105
request.postData.mimeType = 'multipart/form-data'
103106

104107
if (request.postData.params) {
105-
var form = new MultiPartForm()
108+
var form = new FormData()
106109

107110
// easter egg
108-
form._boundary = '---011000010111000001101001'
111+
const boundary = '---011000010111000001101001'
109112

110113
request.postData.params.forEach(function (param) {
111-
form.append(param.name, param.value || '', {
112-
filename: param.fileName || null,
113-
contentType: param.contentType || null
114-
})
114+
if (isBlob(param.value)) {
115+
form.append(param.name, param.value || '', param.fileName || null)
116+
} else {
117+
form.append(param.name, param.value || '')
118+
}
115119
})
116120

117-
form.pipe(es.map(function (data, cb) {
121+
for (var data of formDataIterator(form, boundary)) {
118122
request.postData.text += data
119-
}))
123+
}
120124

121-
request.postData.boundary = form.getBoundary()
122-
request.headersObj['content-type'] = 'multipart/form-data; boundary=' + form.getBoundary()
125+
request.postData.boundary = boundary
126+
request.headersObj['content-type'] = 'multipart/form-data; boundary=' + boundary
123127
}
124128
break
125129

0 commit comments

Comments
 (0)