11'use strict'
22
3- const { webidl } = require ( './webidl ' )
3+ const { toUSVString , isUSVString , bufferToLowerCasedHeaderName } = require ( '../../core/util ' )
44const { utf8DecodeBytes } = require ( './util' )
55const { HTTP_TOKEN_CODEPOINTS , isomorphicDecode } = require ( './data-url' )
66const { isFileLike, File : UndiciFile } = require ( './file' )
77const { makeEntry } = require ( './formdata' )
88const assert = require ( 'node:assert' )
9- const { isAscii , File : NodeFile } = require ( 'node:buffer' )
9+ const { File : NodeFile } = require ( 'node:buffer' )
1010
1111const File = globalThis . File ?? NodeFile ?? UndiciFile
1212
@@ -15,6 +15,18 @@ const filenameBuffer = Buffer.from('; filename')
1515const dd = Buffer . from ( '--' )
1616const ddcrlf = Buffer . from ( '--\r\n' )
1717
18+ /**
19+ * @param {string } chars
20+ */
21+ function isAsciiString ( chars ) {
22+ for ( let i = 0 ; i < chars . length ; ++ i ) {
23+ if ( ( chars . charCodeAt ( i ) & ~ 0x7F ) !== 0 ) {
24+ return false
25+ }
26+ }
27+ return true
28+ }
29+
1830/**
1931 * @see https://andreubotella.github.io/multipart-form-data/#multipart-form-data-boundary
2032 * @param {string } boundary
@@ -30,7 +42,7 @@ function validateBoundary (boundary) {
3042 // - it is composed by bytes in the ranges 0x30 to 0x39, 0x41 to 0x5A, or
3143 // 0x61 to 0x7A, inclusive (ASCII alphanumeric), or which are 0x27 ('),
3244 // 0x2D (-) or 0x5F (_).
33- for ( let i = 0 ; i < boundary . length ; i ++ ) {
45+ for ( let i = 0 ; i < length ; ++ i ) {
3446 const cp = boundary . charCodeAt ( i )
3547
3648 if ( ! (
@@ -58,12 +70,12 @@ function escapeFormDataName (name, encoding = 'utf-8', isFilename = false) {
5870 // 1. If isFilename is true:
5971 if ( isFilename ) {
6072 // 1.1. Set name to the result of converting name into a scalar value string.
61- name = webidl . converters . USVString ( name )
73+ name = toUSVString ( name )
6274 } else {
6375 // 2. Otherwise:
6476
6577 // 2.1. Assert: name is a scalar value string.
66- assert ( name === webidl . converters . USVString ( name ) )
78+ assert ( isUSVString ( name ) )
6779
6880 // 2.2. Replace every occurrence of U+000D (CR) not followed by U+000A (LF),
6981 // and every occurrence of U+000A (LF) not preceded by U+000D (CR), in
@@ -94,14 +106,16 @@ function multipartFormDataParser (input, mimeType) {
94106 // 1. Assert: mimeType’s essence is "multipart/form-data".
95107 assert ( mimeType !== 'failure' && mimeType . essence === 'multipart/form-data' )
96108
109+ const boundaryString = mimeType . parameters . get ( 'boundary' )
110+
97111 // 2. If mimeType’s parameters["boundary"] does not exist, return failure.
98112 // Otherwise, let boundary be the result of UTF-8 decoding mimeType’s
99113 // parameters["boundary"].
100- if ( ! mimeType . parameters . has ( 'boundary' ) ) {
114+ if ( boundaryString === undefined ) {
101115 return 'failure'
102116 }
103117
104- const boundary = Buffer . from ( `--${ mimeType . parameters . get ( 'boundary' ) } ` , 'utf8' )
118+ const boundary = Buffer . from ( `--${ boundaryString } ` , 'utf8' )
105119
106120 // 3. Let entry list be an empty entry list.
107121 const entryList = [ ]
@@ -200,7 +214,10 @@ function multipartFormDataParser (input, mimeType) {
200214 contentType ??= 'text/plain'
201215
202216 // 5.10.2. If contentType is not an ASCII string, set contentType to the empty string.
203- if ( ! isAscii ( Buffer . from ( contentType ) ) ) {
217+
218+ // Note: `buffer.isAscii` can be used at zero-cost, but converting a string to a buffer is a high overhead.
219+ // Content-Type is a relatively small string, so it is faster to use `String#charCodeAt`.
220+ if ( ! isAsciiString ( contentType ) ) {
204221 contentType = ''
205222 }
206223
@@ -214,8 +231,8 @@ function multipartFormDataParser (input, mimeType) {
214231 }
215232
216233 // 5.12. Assert: name is a scalar value string and value is either a scalar value string or a File object.
217- assert ( name === webidl . converters . USVString ( name ) )
218- assert ( ( typeof value === 'string' && value === webidl . converters . USVString ( value ) ) || isFileLike ( value ) )
234+ assert ( isUSVString ( name ) )
235+ assert ( ( typeof value === 'string' && isUSVString ( value ) ) || isFileLike ( value ) )
219236
220237 // 5.13. Create an entry with name and value, and append it to entry list.
221238 entryList . push ( makeEntry ( name , value , filename ) )
@@ -280,7 +297,7 @@ function parseMultipartFormDataHeaders (input, position) {
280297 )
281298
282299 // 2.8. Byte-lowercase header name and switch on the result:
283- switch ( new TextDecoder ( ) . decode ( headerName ) . toLowerCase ( ) ) {
300+ switch ( bufferToLowerCasedHeaderName ( headerName ) ) {
284301 case 'content-disposition' : {
285302 // 1. Set name and filename to null.
286303 name = filename = null
@@ -428,10 +445,9 @@ function parseMultipartFormDataName (input, position) {
428445 */
429446function collectASequenceOfBytes ( condition , input , position ) {
430447 const result = [ ]
431- let index = 0
432448
433449 while ( position . position < input . length && condition ( input [ position . position ] ) ) {
434- result [ index ++ ] = input [ position . position ]
450+ result . push ( input [ position . position ] )
435451
436452 position . position ++
437453 }
0 commit comments