Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fomdata): added support for spec-compliant FormData & Blob types; (
- Loading branch information
1 parent
65e8d1e
commit 6ac574e
Showing
9 changed files
with
304 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
import FormData from 'form-data'; | ||
export default FormData; | ||
import _FormData from 'form-data'; | ||
export default typeof FormData !== 'undefined' ? FormData : _FormData; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import {Readable} from 'stream'; | ||
import utils from "../utils.js"; | ||
import readBlob from "./readBlob.js"; | ||
|
||
const BOUNDARY_ALPHABET = utils.ALPHABET.ALPHA_DIGIT + '-_'; | ||
|
||
const textEncoder = new TextEncoder(); | ||
|
||
const CRLF = '\r\n'; | ||
const CRLF_BYTES = textEncoder.encode(CRLF); | ||
const CRLF_BYTES_COUNT = 2; | ||
|
||
class FormDataPart { | ||
constructor(name, value) { | ||
const {escapeName} = this.constructor; | ||
const isStringValue = utils.isString(value); | ||
|
||
let headers = `Content-Disposition: form-data; name="${escapeName(name)}"${ | ||
!isStringValue && value.name ? `; filename="${escapeName(value.name)}"` : '' | ||
}${CRLF}`; | ||
|
||
if (isStringValue) { | ||
value = textEncoder.encode(String(value).replace(/\r?\n|\r\n?/g, CRLF)); | ||
} else { | ||
headers += `Content-Type: ${value.type || "application/octet-stream"}${CRLF}` | ||
} | ||
|
||
this.headers = textEncoder.encode(headers + CRLF); | ||
|
||
this.contentLength = isStringValue ? value.byteLength : value.size; | ||
|
||
this.size = this.headers.byteLength + this.contentLength + CRLF_BYTES_COUNT; | ||
|
||
this.name = name; | ||
this.value = value; | ||
} | ||
|
||
async *encode(){ | ||
yield this.headers; | ||
|
||
const {value} = this; | ||
|
||
if(utils.isTypedArray(value)) { | ||
yield value; | ||
} else { | ||
yield* readBlob(value); | ||
} | ||
|
||
yield CRLF_BYTES; | ||
} | ||
|
||
static escapeName(name) { | ||
return String(name).replace(/[\r\n"]/g, (match) => ({ | ||
'\r' : '%0D', | ||
'\n' : '%0A', | ||
'"' : '%22', | ||
}[match])); | ||
} | ||
} | ||
|
||
const formDataToStream = (form, headersHandler, options) => { | ||
const { | ||
tag = 'form-data-boundary', | ||
size = 25, | ||
boundary = tag + '-' + utils.generateString(size, BOUNDARY_ALPHABET) | ||
} = options || {}; | ||
|
||
if(!utils.isFormData(form)) { | ||
throw TypeError('FormData instance required'); | ||
} | ||
|
||
if (boundary.length < 1 || boundary.length > 70) { | ||
throw Error('boundary must be 10-70 characters long') | ||
} | ||
|
||
const boundaryBytes = textEncoder.encode('--' + boundary + CRLF); | ||
const footerBytes = textEncoder.encode('--' + boundary + '--' + CRLF + CRLF); | ||
let contentLength = footerBytes.byteLength; | ||
|
||
const parts = Array.from(form.entries()).map(([name, value]) => { | ||
const part = new FormDataPart(name, value); | ||
contentLength += part.size; | ||
return part; | ||
}); | ||
|
||
contentLength += boundaryBytes.byteLength * parts.length; | ||
|
||
contentLength = utils.toFiniteNumber(contentLength); | ||
|
||
const computedHeaders = { | ||
'Content-Type': `multipart/form-data; boundary=${boundary}` | ||
} | ||
|
||
if (Number.isFinite(contentLength)) { | ||
computedHeaders['Content-Length'] = contentLength; | ||
} | ||
|
||
headersHandler && headersHandler(computedHeaders); | ||
|
||
return Readable.from((async function *() { | ||
for(const part of parts) { | ||
yield boundaryBytes; | ||
yield* part.encode(); | ||
} | ||
|
||
yield footerBytes; | ||
})()); | ||
}; | ||
|
||
export default formDataToStream; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
const {asyncIterator} = Symbol; | ||
|
||
const readBlob = async function* (blob) { | ||
if (blob.stream) { | ||
yield* blob.stream() | ||
} else if (blob.arrayBuffer) { | ||
yield await blob.arrayBuffer() | ||
} else if (blob[asyncIterator]) { | ||
yield* asyncIterator.call(blob); | ||
} else { | ||
yield blob; | ||
} | ||
} | ||
|
||
export default readBlob; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.