1
1
const assert = require ( 'assert' )
2
2
const { atob } = require ( 'buffer' )
3
- const { isValidHTTPToken , isomorphicDecode } = require ( './util' )
3
+ const { isomorphicDecode } = require ( './util' )
4
4
5
5
const encoder = new TextEncoder ( )
6
6
7
- // Regex
8
- const HTTP_TOKEN_CODEPOINTS = / ^ [ ! # $ % & ' * + - . ^ _ | ~ A - z 0 - 9 ] + $ /
7
+ /**
8
+ * @see https://mimesniff.spec.whatwg.org/#http-token-code-point
9
+ */
10
+ const HTTP_TOKEN_CODEPOINTS = / ^ [ ! # $ % & ' * + - . ^ _ | ~ A - Z a - z 0 - 9 ] + $ /
9
11
const HTTP_WHITESPACE_REGEX = / ( \u000A | \u000D | \u0009 | \u0020 ) / // eslint-disable-line
10
- // https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point
11
- const HTTP_QUOTED_STRING_TOKENS = / ^ ( \u0009 | \x { 0020 } - \x { 007 E} | \x { 0080 } - \x { 00 FF} ) + $ / // eslint-disable-line
12
+ /**
13
+ * @see https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point
14
+ */
15
+ const HTTP_QUOTED_STRING_TOKENS = / [ \u0009 | \u0020 - \u007E | \u0080 - \u00FF ] / // eslint-disable-line
12
16
13
17
// https://fetch.spec.whatwg.org/#data-url-processor
14
18
/** @param {URL } dataURL */
@@ -38,14 +42,12 @@ function dataURLProcessor (dataURL) {
38
42
39
43
// 6. Strip leading and trailing ASCII whitespace
40
44
// from mimeType.
41
- // Note: This will only remove U+0020 SPACE code
42
- // points, if any.
43
45
// Undici implementation note: we need to store the
44
46
// length because if the mimetype has spaces removed,
45
47
// the wrong amount will be sliced from the input in
46
48
// step #9
47
49
const mimeTypeLength = mimeType . length
48
- mimeType = mimeType . replace ( / ^ ( \u0020 ) + | ( \u0020 ) + $ / g , '' )
50
+ mimeType = removeASCIIWhitespace ( mimeType , true , true )
49
51
50
52
// 7. If position is past the end of input, then
51
53
// return failure
@@ -233,7 +235,7 @@ function percentDecode (input) {
233
235
function parseMIMEType ( input ) {
234
236
// 1. Remove any leading and trailing HTTP whitespace
235
237
// from input.
236
- input = input . trim ( )
238
+ input = removeHTTPWhitespace ( input , true , true )
237
239
238
240
// 2. Let position be a position variable for input,
239
241
// initially pointing at the start of input.
@@ -274,25 +276,28 @@ function parseMIMEType (input) {
274
276
)
275
277
276
278
// 8. Remove any trailing HTTP whitespace from subtype.
277
- subtype = subtype . trimEnd ( )
279
+ subtype = removeHTTPWhitespace ( subtype , false , true )
278
280
279
281
// 9. If subtype is the empty string or does not solely
280
282
// contain HTTP token code points, then return failure.
281
283
if ( subtype . length === 0 || ! HTTP_TOKEN_CODEPOINTS . test ( subtype ) ) {
282
284
return 'failure'
283
285
}
284
286
287
+ const typeLowercase = type . toLowerCase ( )
288
+ const subtypeLowercase = subtype . toLowerCase ( )
289
+
285
290
// 10. Let mimeType be a new MIME type record whose type
286
291
// is type, in ASCII lowercase, and subtype is subtype,
287
292
// in ASCII lowercase.
288
293
// https://mimesniff.spec.whatwg.org/#mime-type
289
294
const mimeType = {
290
- type : type . toLowerCase ( ) ,
291
- subtype : subtype . toLowerCase ( ) ,
295
+ type : typeLowercase ,
296
+ subtype : subtypeLowercase ,
292
297
/** @type {Map<string, string> } */
293
298
parameters : new Map ( ) ,
294
299
// https://mimesniff.spec.whatwg.org/#mime-type-essence
295
- essence : `${ type } /${ subtype } `
300
+ essence : `${ typeLowercase } /${ subtypeLowercase } `
296
301
}
297
302
298
303
// 11. While position is not past the end of input:
@@ -370,8 +375,7 @@ function parseMIMEType (input) {
370
375
)
371
376
372
377
// 2. Remove any trailing HTTP whitespace from parameterValue.
373
- // Note: it says "trailing" whitespace; leading is fine.
374
- parameterValue = parameterValue . trimEnd ( )
378
+ parameterValue = removeHTTPWhitespace ( parameterValue , false , true )
375
379
376
380
// 3. If parameterValue is the empty string, then continue.
377
381
if ( parameterValue . length === 0 ) {
@@ -388,7 +392,7 @@ function parseMIMEType (input) {
388
392
if (
389
393
parameterName . length !== 0 &&
390
394
HTTP_TOKEN_CODEPOINTS . test ( parameterName ) &&
391
- ! HTTP_QUOTED_STRING_TOKENS . test ( parameterValue ) &&
395
+ ( parameterValue . length === 0 || HTTP_QUOTED_STRING_TOKENS . test ( parameterValue ) ) &&
392
396
! mimeType . parameters . has ( parameterName )
393
397
) {
394
398
mimeType . parameters . set ( parameterName , parameterValue )
@@ -522,11 +526,11 @@ function collectAnHTTPQuotedString (input, position, extractValue) {
522
526
*/
523
527
function serializeAMimeType ( mimeType ) {
524
528
assert ( mimeType !== 'failure' )
525
- const { type , subtype , parameters } = mimeType
529
+ const { parameters , essence } = mimeType
526
530
527
531
// 1. Let serialization be the concatenation of mimeType’s
528
532
// type, U+002F (/), and mimeType’s subtype.
529
- let serialization = ` ${ type } / ${ subtype } `
533
+ let serialization = essence
530
534
531
535
// 2. For each name → value of mimeType’s parameters:
532
536
for ( let [ name , value ] of parameters . entries ( ) ) {
@@ -541,7 +545,7 @@ function serializeAMimeType (mimeType) {
541
545
542
546
// 4. If value does not solely contain HTTP token code
543
547
// points or value is the empty string, then:
544
- if ( ! isValidHTTPToken ( value ) ) {
548
+ if ( ! HTTP_TOKEN_CODEPOINTS . test ( value ) ) {
545
549
// 1. Precede each occurence of U+0022 (") or
546
550
// U+005C (\) in value with U+005C (\).
547
551
value = value . replace ( / ( \\ | " ) / g, '\\$1' )
@@ -561,6 +565,59 @@ function serializeAMimeType (mimeType) {
561
565
return serialization
562
566
}
563
567
568
+ /**
569
+ * @see https://fetch.spec.whatwg.org/#http-whitespace
570
+ * @param {string } char
571
+ */
572
+ function isHTTPWhiteSpace ( char ) {
573
+ return char === '\r' || char === '\n' || char === '\t' || char === ' '
574
+ }
575
+
576
+ /**
577
+ * @see https://fetch.spec.whatwg.org/#http-whitespace
578
+ * @param {string } str
579
+ */
580
+ function removeHTTPWhitespace ( str , leading = true , trailing = true ) {
581
+ let lead = 0
582
+ let trail = str . length - 1
583
+
584
+ if ( leading ) {
585
+ for ( ; lead < str . length && isHTTPWhiteSpace ( str [ lead ] ) ; lead ++ ) ;
586
+ }
587
+
588
+ if ( trailing ) {
589
+ for ( ; trail > 0 && isHTTPWhiteSpace ( str [ trail ] ) ; trail -- ) ;
590
+ }
591
+
592
+ return str . slice ( lead , trail + 1 )
593
+ }
594
+
595
+ /**
596
+ * @see https://infra.spec.whatwg.org/#ascii-whitespace
597
+ * @param {string } char
598
+ */
599
+ function isASCIIWhitespace ( char ) {
600
+ return char === '\r' || char === '\n' || char === '\t' || char === '\f' || char === ' '
601
+ }
602
+
603
+ /**
604
+ * @see https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
605
+ */
606
+ function removeASCIIWhitespace ( str , leading = true , trailing = true ) {
607
+ let lead = 0
608
+ let trail = str . length - 1
609
+
610
+ if ( leading ) {
611
+ for ( ; lead < str . length && isASCIIWhitespace ( str [ lead ] ) ; lead ++ ) ;
612
+ }
613
+
614
+ if ( trailing ) {
615
+ for ( ; trail > 0 && isASCIIWhitespace ( str [ trail ] ) ; trail -- ) ;
616
+ }
617
+
618
+ return str . slice ( lead , trail + 1 )
619
+ }
620
+
564
621
module . exports = {
565
622
dataURLProcessor,
566
623
URLSerializer,
0 commit comments