1
1
'use strict'
2
2
3
- const { webidl } = require ( './webidl ' )
3
+ const { toUSVString , isUSVString , bufferToLowerCasedHeaderName } = require ( '../../core/util ' )
4
4
const { utf8DecodeBytes } = require ( './util' )
5
5
const { HTTP_TOKEN_CODEPOINTS , isomorphicDecode } = require ( './data-url' )
6
6
const { isFileLike, File : UndiciFile } = require ( './file' )
7
7
const { makeEntry } = require ( './formdata' )
8
8
const assert = require ( 'node:assert' )
9
- const { isAscii , File : NodeFile } = require ( 'node:buffer' )
9
+ const { File : NodeFile } = require ( 'node:buffer' )
10
10
11
11
const File = globalThis . File ?? NodeFile ?? UndiciFile
12
12
@@ -15,6 +15,18 @@ const filenameBuffer = Buffer.from('; filename')
15
15
const dd = Buffer . from ( '--' )
16
16
const ddcrlf = Buffer . from ( '--\r\n' )
17
17
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
+
18
30
/**
19
31
* @see https://andreubotella.github.io/multipart-form-data/#multipart-form-data-boundary
20
32
* @param {string } boundary
@@ -30,7 +42,7 @@ function validateBoundary (boundary) {
30
42
// - it is composed by bytes in the ranges 0x30 to 0x39, 0x41 to 0x5A, or
31
43
// 0x61 to 0x7A, inclusive (ASCII alphanumeric), or which are 0x27 ('),
32
44
// 0x2D (-) or 0x5F (_).
33
- for ( let i = 0 ; i < boundary . length ; i ++ ) {
45
+ for ( let i = 0 ; i < length ; ++ i ) {
34
46
const cp = boundary . charCodeAt ( i )
35
47
36
48
if ( ! (
@@ -58,12 +70,12 @@ function escapeFormDataName (name, encoding = 'utf-8', isFilename = false) {
58
70
// 1. If isFilename is true:
59
71
if ( isFilename ) {
60
72
// 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 )
62
74
} else {
63
75
// 2. Otherwise:
64
76
65
77
// 2.1. Assert: name is a scalar value string.
66
- assert ( name === webidl . converters . USVString ( name ) )
78
+ assert ( isUSVString ( name ) )
67
79
68
80
// 2.2. Replace every occurrence of U+000D (CR) not followed by U+000A (LF),
69
81
// and every occurrence of U+000A (LF) not preceded by U+000D (CR), in
@@ -94,14 +106,16 @@ function multipartFormDataParser (input, mimeType) {
94
106
// 1. Assert: mimeType’s essence is "multipart/form-data".
95
107
assert ( mimeType !== 'failure' && mimeType . essence === 'multipart/form-data' )
96
108
109
+ const boundaryString = mimeType . parameters . get ( 'boundary' )
110
+
97
111
// 2. If mimeType’s parameters["boundary"] does not exist, return failure.
98
112
// Otherwise, let boundary be the result of UTF-8 decoding mimeType’s
99
113
// parameters["boundary"].
100
- if ( ! mimeType . parameters . has ( 'boundary' ) ) {
114
+ if ( boundaryString === undefined ) {
101
115
return 'failure'
102
116
}
103
117
104
- const boundary = Buffer . from ( `--${ mimeType . parameters . get ( 'boundary' ) } ` , 'utf8' )
118
+ const boundary = Buffer . from ( `--${ boundaryString } ` , 'utf8' )
105
119
106
120
// 3. Let entry list be an empty entry list.
107
121
const entryList = [ ]
@@ -200,7 +214,10 @@ function multipartFormDataParser (input, mimeType) {
200
214
contentType ??= 'text/plain'
201
215
202
216
// 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 ) ) {
204
221
contentType = ''
205
222
}
206
223
@@ -214,8 +231,8 @@ function multipartFormDataParser (input, mimeType) {
214
231
}
215
232
216
233
// 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 ) )
219
236
220
237
// 5.13. Create an entry with name and value, and append it to entry list.
221
238
entryList . push ( makeEntry ( name , value , filename ) )
@@ -280,7 +297,7 @@ function parseMultipartFormDataHeaders (input, position) {
280
297
)
281
298
282
299
// 2.8. Byte-lowercase header name and switch on the result:
283
- switch ( new TextDecoder ( ) . decode ( headerName ) . toLowerCase ( ) ) {
300
+ switch ( bufferToLowerCasedHeaderName ( headerName ) ) {
284
301
case 'content-disposition' : {
285
302
// 1. Set name and filename to null.
286
303
name = filename = null
@@ -428,10 +445,9 @@ function parseMultipartFormDataName (input, position) {
428
445
*/
429
446
function collectASequenceOfBytes ( condition , input , position ) {
430
447
const result = [ ]
431
- let index = 0
432
448
433
449
while ( position . position < input . length && condition ( input [ position . position ] ) ) {
434
- result [ index ++ ] = input [ position . position ]
450
+ result . push ( input [ position . position ] )
435
451
436
452
position . position ++
437
453
}
0 commit comments