bare-url 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +4 -4
- package/lib/errors.js +80 -0
- package/lib/parse.js +348 -9
- package/lib/serialize.js +51 -2
- package/package.json +3 -2
package/index.js
CHANGED
|
@@ -71,9 +71,9 @@ const URL = exports.URL = class URL {
|
|
|
71
71
|
|
|
72
72
|
get host () {
|
|
73
73
|
if (this._url.host === null) return ''
|
|
74
|
-
if (this._url.port === null) return this._url.host
|
|
74
|
+
if (this._url.port === null) return serialize.host(this._url.host)
|
|
75
75
|
|
|
76
|
-
return this._url.host + ':' + this._url.port
|
|
76
|
+
return serialize.host(this._url.host) + ':' + this._url.port.toString(10)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
set host (value) {
|
|
@@ -87,7 +87,7 @@ const URL = exports.URL = class URL {
|
|
|
87
87
|
get hostname () {
|
|
88
88
|
if (this._url.host === null) return ''
|
|
89
89
|
|
|
90
|
-
return this._url.host
|
|
90
|
+
return serialize.host(this._url.host)
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
set hostname (value) {
|
|
@@ -228,7 +228,7 @@ exports.pathToFileURL = function pathToFileURL (pathname) {
|
|
|
228
228
|
if (pathname[pathname.length - 1] === '/') {
|
|
229
229
|
resolved += '/'
|
|
230
230
|
} else if (os.platform() === 'win32' && pathname[pathname.length - 1 === '\\']) {
|
|
231
|
-
resolved += '
|
|
231
|
+
resolved += '\\'
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
resolved = resolved
|
package/lib/errors.js
CHANGED
|
@@ -44,4 +44,84 @@ module.exports = class URLError extends Error {
|
|
|
44
44
|
static PORT_INVALID (msg = 'Invalid URL') {
|
|
45
45
|
return new URLError(msg, 'PORT_INVALID', URLError.PORT_INVALID)
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
// https://url.spec.whatwg.org/#validation-error-domain-to-ascii
|
|
49
|
+
static DOMAIN_TO_ASCII (msg = 'Invalid URL') {
|
|
50
|
+
return new URLError(msg, 'DOMAIN_TO_ASCII', URLError.DOMAIN_TO_ASCII)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// https://url.spec.whatwg.org/#domain-invalid-code-point
|
|
54
|
+
static DOMAIN_INVALID_CODE_POINT (msg = 'Invalid URL') {
|
|
55
|
+
return new URLError(msg, 'DOMAIN_INVALID_CODE_POINT', URLError.DOMAIN_INVALID_CODE_POINT)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// https://url.spec.whatwg.org/#host-invalid-code-point
|
|
59
|
+
static HOST_INVALID_CODE_POINT (msg = 'Invalid URL') {
|
|
60
|
+
return new URLError(msg, 'HOST_INVALID_CODE_POINT', URLError.HOST_INVALID_CODE_POINT)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// https://url.spec.whatwg.org/#ipv4-too-many-parts
|
|
64
|
+
static IPV4_TOO_MANY_PARTS (msg = 'Invalid URL') {
|
|
65
|
+
return new URLError(msg, 'IPV4_TOO_MANY_PARTS', URLError.IPV4_TOO_MANY_PARTS)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// https://url.spec.whatwg.org/#ipv4-non-numeric-part
|
|
69
|
+
static IPV4_NON_NUMERIC_PART (msg = 'Invalid URL') {
|
|
70
|
+
return new URLError(msg, 'IPV4_NON_NUMERIC_PART', URLError.IPV4_NON_NUMERIC_PART)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// https://url.spec.whatwg.org/#ipv4-out-of-range-part
|
|
74
|
+
static IPV4_OUT_OF_RANGE_PART (msg = 'Invalid URL') {
|
|
75
|
+
return new URLError(msg, 'IPV4_OUT_OF_RANGE_PART', URLError.IPV4_OUT_OF_RANGE_PART)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// https://url.spec.whatwg.org/#ipv6-unclosed
|
|
79
|
+
static IPV6_UNCLOSED (msg = 'Invalid URL') {
|
|
80
|
+
return new URLError(msg, 'IPV6_UNCLOSED', URLError.IPV6_UNCLOSED)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// https://url.spec.whatwg.org/#ipv6-invalid-compression
|
|
84
|
+
static IPV6_INVALID_COMPRESSION (msg = 'Invalid URL') {
|
|
85
|
+
return new URLError(msg, 'IPV6_INVALID_COMPRESSION', URLError.IPV6_INVALID_COMPRESSION)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// https://url.spec.whatwg.org/#ipv6-too-many-pieces
|
|
89
|
+
static IPV6_TOO_MANY_PIECES (msg = 'Invalid URL') {
|
|
90
|
+
return new URLError(msg, 'IPV6_TOO_MANY_PIECES', URLError.IPV6_TOO_MANY_PIECES)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// https://url.spec.whatwg.org/#ipv6-multiple-compression
|
|
94
|
+
static IPV6_MULTIPLE_COMPRESSION (msg = 'Invalid URL') {
|
|
95
|
+
return new URLError(msg, 'IPV6_MULTIPLE_COMPRESSION', URLError.IPV6_MULTIPLE_COMPRESSION)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// https://url.spec.whatwg.org/#ipv6-invalid-code-point
|
|
99
|
+
static IPV6_INVALID_CODE_POINT (msg = 'Invalid URL') {
|
|
100
|
+
return new URLError(msg, 'IPV6_INVALID_CODE_POINT', URLError.IPV6_INVALID_CODE_POINT)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// https://url.spec.whatwg.org/#ipv6-too-few-pieces
|
|
104
|
+
static IPV6_TOO_FEW_PIECES (msg = 'Invalid URL') {
|
|
105
|
+
return new URLError(msg, 'IPV6_TOO_FEW_PIECES', URLError.IPV6_TOO_FEW_PIECES)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// https://url.spec.whatwg.org/#ipv4-in-ipv6-too-many-pieces
|
|
109
|
+
static IPV4_IN_IPV6_TOO_MANY_PIECES (msg = 'Invalid URL') {
|
|
110
|
+
return new URLError(msg, 'IPV4_IN_IPV6_TOO_MANY_PIECES', URLError.IPV4_IN_IPV6_TOO_MANY_PIECES)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// https://url.spec.whatwg.org/#ipv4-in-ipv6-invalid-code-point
|
|
114
|
+
static IPV4_IN_IPV6_INVALID_CODE_POINT (msg = 'Invalid URL') {
|
|
115
|
+
return new URLError(msg, 'IPV4_IN_IPV6_INVALID_CODE_POINT', URLError.IPV4_IN_IPV6_INVALID_CODE_POINT)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// https://url.spec.whatwg.org/#ipv4-in-ipv6-out-of-range-part
|
|
119
|
+
static IPV4_IN_IPV6_OUT_OF_RANGE_PART (msg = 'Invalid URL') {
|
|
120
|
+
return new URLError(msg, 'IPV4_IN_IPV6_OUT_OF_RANGE_PART', URLError.IPV4_IN_IPV6_OUT_OF_RANGE_PART)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// https://url.spec.whatwg.org/#ipv4-in-ipv6-too-few-parts
|
|
124
|
+
static IPV4_IN_IPV6_TOO_FEW_PARTS (msg = 'Invalid URL') {
|
|
125
|
+
return new URLError(msg, 'IPV4_IN_IPV6_TOO_FEW_PARTS', URLError.IPV4_IN_IPV6_TOO_FEW_PARTS)
|
|
126
|
+
}
|
|
47
127
|
}
|
package/lib/parse.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const tr46 = require('tr46')
|
|
1
2
|
const constants = require('./constants')
|
|
2
3
|
const errors = require('./errors')
|
|
3
4
|
const infra = require('./infra')
|
|
@@ -224,10 +225,12 @@ module.exports = function parse (
|
|
|
224
225
|
continue
|
|
225
226
|
}
|
|
226
227
|
|
|
228
|
+
const encodedCodePoints = UTF8PercentEncode(buffer[i], userinfoPercentEncodeSet)
|
|
229
|
+
|
|
227
230
|
if (passwordTokenSeen) {
|
|
228
|
-
url.password +=
|
|
231
|
+
url.password += encodedCodePoints
|
|
229
232
|
} else {
|
|
230
|
-
url.username +=
|
|
233
|
+
url.username += encodedCodePoints
|
|
231
234
|
}
|
|
232
235
|
}
|
|
233
236
|
|
|
@@ -463,7 +466,7 @@ module.exports = function parse (
|
|
|
463
466
|
state = constants.STATE_FRAGMENT
|
|
464
467
|
}
|
|
465
468
|
} else {
|
|
466
|
-
buffer +=
|
|
469
|
+
buffer += UTF8PercentEncode(String.fromCharCode(c), pathPercentEncodeSet)
|
|
467
470
|
}
|
|
468
471
|
break
|
|
469
472
|
|
|
@@ -477,7 +480,7 @@ module.exports = function parse (
|
|
|
477
480
|
state = constants.STATE_FRAGMENT
|
|
478
481
|
} else {
|
|
479
482
|
if (c !== -1) {
|
|
480
|
-
url.path +=
|
|
483
|
+
url.path += UTF8PercentEncode(String.fromCharCode(c), c0ControlPercentEncodeSet)
|
|
481
484
|
}
|
|
482
485
|
}
|
|
483
486
|
break
|
|
@@ -485,7 +488,9 @@ module.exports = function parse (
|
|
|
485
488
|
// https://url.spec.whatwg.org/#query-state
|
|
486
489
|
case constants.STATE_QUERY:
|
|
487
490
|
if ((!stateOverride && c === 0x23) || c === -1) {
|
|
488
|
-
|
|
491
|
+
const percentEncodeSet = isSpecial(url) ? specialQueryPercentEncodeSet : queryPercentEncodeSet
|
|
492
|
+
|
|
493
|
+
url.query += UTF8PercentEncode(buffer, percentEncodeSet)
|
|
489
494
|
buffer = ''
|
|
490
495
|
|
|
491
496
|
if (c === 0x23) {
|
|
@@ -500,7 +505,7 @@ module.exports = function parse (
|
|
|
500
505
|
// https://url.spec.whatwg.org/#fragment-state
|
|
501
506
|
case constants.STATE_FRAGMENT:
|
|
502
507
|
if (c !== -1) {
|
|
503
|
-
url.fragment +=
|
|
508
|
+
url.fragment += UTF8PercentEncode(String.fromCharCode(c), fragmentPercentEncodeSet)
|
|
504
509
|
}
|
|
505
510
|
}
|
|
506
511
|
}
|
|
@@ -508,9 +513,241 @@ module.exports = function parse (
|
|
|
508
513
|
return url
|
|
509
514
|
}
|
|
510
515
|
|
|
511
|
-
// https://url.spec.whatwg.org/#host-
|
|
512
|
-
function parseHost (
|
|
513
|
-
|
|
516
|
+
// https://url.spec.whatwg.org/#concept-host-parser
|
|
517
|
+
function parseHost (input, isOpaque = false) {
|
|
518
|
+
if (input.charCodeAt(0) === 0x5b) {
|
|
519
|
+
if (input.charCodeAt(input.length - 1) !== 0x5d) throw errors.IPV6_UNCLOSED()
|
|
520
|
+
|
|
521
|
+
return parseIPv6(input.substring(1, input.length - 1))
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (isOpaque) return parseOpaqueHost(input)
|
|
525
|
+
|
|
526
|
+
const domain = percentDecode(Buffer.from(input, 'utf8'))
|
|
527
|
+
|
|
528
|
+
const asciiDomain = domainToASCII(domain.toString('utf8'), false)
|
|
529
|
+
|
|
530
|
+
for (const codePoint of asciiDomain) {
|
|
531
|
+
if (isForbiddenDomainCodePoint(codePoint.codePointAt(0))) {
|
|
532
|
+
throw errors.DOMAIN_INVALID_CODE_POINT()
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (endsInANumber(asciiDomain)) return parseIPv4(asciiDomain)
|
|
537
|
+
|
|
538
|
+
return asciiDomain
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// https://url.spec.whatwg.org/#ends-in-a-number-checker
|
|
542
|
+
function endsInANumber (input) {
|
|
543
|
+
const parts = input.split('.')
|
|
544
|
+
|
|
545
|
+
const last = parts[parts.length - 1]
|
|
546
|
+
|
|
547
|
+
if (last === '') {
|
|
548
|
+
if (parts.length === 1) return false
|
|
549
|
+
parts.pop()
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
try {
|
|
553
|
+
parseIPv4Number(last)
|
|
554
|
+
|
|
555
|
+
return true
|
|
556
|
+
} catch {
|
|
557
|
+
return false
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// https://url.spec.whatwg.org/#concept-ipv4-parser
|
|
562
|
+
function parseIPv4 (input) {
|
|
563
|
+
const parts = input.split('.')
|
|
564
|
+
|
|
565
|
+
if (parts[parts.length - 1] === '') {
|
|
566
|
+
if (parts.length > 1) parts.pop()
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (parts.length > 4) throw errors.IPV4_TOO_MANY_PARTS()
|
|
570
|
+
|
|
571
|
+
const numbers = []
|
|
572
|
+
|
|
573
|
+
for (const part of parts) {
|
|
574
|
+
numbers.push(parseIPv4Number(part))
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
for (let i = 0, n = numbers.length - 1; i < n; i++) {
|
|
578
|
+
if (numbers[i] > 255) {
|
|
579
|
+
throw errors.IPV4_OUT_OF_RANGE_PART()
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (numbers[numbers.length - 1] > 256 ** (5 - numbers.length)) {
|
|
584
|
+
throw errors.IPV4_OUT_OF_RANGE_PART()
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
let ipv4 = numbers.pop()
|
|
588
|
+
let counter = 0
|
|
589
|
+
|
|
590
|
+
for (const n of numbers) ipv4 += n * 256 ** (3 - counter++)
|
|
591
|
+
|
|
592
|
+
return ipv4
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// https://url.spec.whatwg.org/#ipv4-number-parser
|
|
596
|
+
function parseIPv4Number (input) {
|
|
597
|
+
if (input === '') throw errors.IPV4_NON_NUMERIC_PART()
|
|
598
|
+
|
|
599
|
+
input = input.toLowerCase()
|
|
600
|
+
|
|
601
|
+
let R = 10
|
|
602
|
+
|
|
603
|
+
if (input.length >= 2) {
|
|
604
|
+
if (input[0] === '0') {
|
|
605
|
+
if (input[1] === 'x') {
|
|
606
|
+
input.substring(2)
|
|
607
|
+
R = 16
|
|
608
|
+
} else {
|
|
609
|
+
input.substring(1)
|
|
610
|
+
R = 8
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (input === '') return 0
|
|
616
|
+
|
|
617
|
+
const n = parseInt(input, R)
|
|
618
|
+
|
|
619
|
+
if (n.toString(R) !== input) throw errors.IPV4_NON_NUMERIC_PART()
|
|
620
|
+
|
|
621
|
+
return n
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// https://url.spec.whatwg.org/#concept-ipv6-parser
|
|
625
|
+
function parseIPv6 (input) {
|
|
626
|
+
const address = [0, 0, 0, 0, 0, 0, 0, 0]
|
|
627
|
+
let pieceIndex = 0
|
|
628
|
+
let compress = null
|
|
629
|
+
let pointer = 0
|
|
630
|
+
|
|
631
|
+
if (input.charCodeAt(pointer) === 0x3a) {
|
|
632
|
+
if (input.charCodeAt(pointer + 1) !== 0x3a) {
|
|
633
|
+
throw errors.IPV6_INVALID_COMPRESSION()
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
pointer += 2
|
|
637
|
+
compress = ++pieceIndex
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
while (pointer < input.length) {
|
|
641
|
+
if (pieceIndex === 8) throw errors.IPV6_TOO_MANY_PIECES()
|
|
642
|
+
|
|
643
|
+
if (input.charCodeAt(pointer) === 0x3a) {
|
|
644
|
+
if (compress !== null) throw errors.IPV6_MULTIPLE_COMPRESSION()
|
|
645
|
+
|
|
646
|
+
pointer++
|
|
647
|
+
compress = ++pieceIndex
|
|
648
|
+
continue
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
let value = 0
|
|
652
|
+
let length = 0
|
|
653
|
+
|
|
654
|
+
while (length < 4 && infra.isASCIIHexDigit(input.charCodeAt(pointer))) {
|
|
655
|
+
value = value * 0x10 + parseInt(input[pointer], 16)
|
|
656
|
+
|
|
657
|
+
pointer++
|
|
658
|
+
length++
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (input.charCodeAt(pointer) === 0x2e) {
|
|
662
|
+
if (length === 0) throw errors.IPV4_IN_IPV6_INVALID_CODE_POINT()
|
|
663
|
+
|
|
664
|
+
pointer -= length
|
|
665
|
+
|
|
666
|
+
if (pieceIndex > 6) throw errors.IPV4_IN_IPV6_TOO_MANY_PIECES()
|
|
667
|
+
|
|
668
|
+
let numbersSeen = 0
|
|
669
|
+
|
|
670
|
+
while (pointer < input.length) {
|
|
671
|
+
let ipv4Piece = null
|
|
672
|
+
|
|
673
|
+
if (numbersSeen > 0) {
|
|
674
|
+
if (input.charCodeAt(pointer) === 0x2e && numbersSeen < 4) {
|
|
675
|
+
pointer++
|
|
676
|
+
} else {
|
|
677
|
+
throw errors.IPV4_IN_IPV6_INVALID_CODE_POINT()
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (!infra.isASCIIDigit(input.charCodeAt(pointer))) {
|
|
682
|
+
throw errors.IPV4_IN_IPV6_INVALID_CODE_POINT()
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
while (infra.isASCIIDigit(input.charCodeAt(pointer))) {
|
|
686
|
+
const number = parseInt(input[pointer], 10)
|
|
687
|
+
|
|
688
|
+
if (ipv4Piece === null) {
|
|
689
|
+
ipv4Piece = number
|
|
690
|
+
} else if (ipv4Piece === 0) {
|
|
691
|
+
throw errors.IPV4_IN_IPV6_INVALID_CODE_POINT()
|
|
692
|
+
} else {
|
|
693
|
+
ipv4Piece = ipv4Piece * 10 + number
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (ipv4Piece > 255) throw errors.IPV4_IN_IPV6_OUT_OF_RANGE_PART()
|
|
697
|
+
|
|
698
|
+
pointer++
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
address[pieceIndex] = address[pieceIndex] * 0x100 + ipv4Piece
|
|
702
|
+
|
|
703
|
+
numbersSeen++
|
|
704
|
+
|
|
705
|
+
if (numbersSeen === 2 || numbersSeen === 4) pieceIndex++
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (numbersSeen !== 4) throw errors.IPV4_IN_IPV6_TOO_FEW_PARTS()
|
|
709
|
+
|
|
710
|
+
break
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (input.charCodeAt(pointer) === 0x3a) {
|
|
714
|
+
pointer++
|
|
715
|
+
|
|
716
|
+
if (pointer === input.length) {
|
|
717
|
+
throw errors.IPV6_INVALID_CODE_POINT()
|
|
718
|
+
}
|
|
719
|
+
} else if (pointer !== input.length) {
|
|
720
|
+
throw errors.IPV6_INVALID_CODE_POINT()
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
address[pieceIndex++] = value
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (compress !== null) {
|
|
727
|
+
let swaps = pieceIndex - compress
|
|
728
|
+
|
|
729
|
+
pieceIndex = 7
|
|
730
|
+
|
|
731
|
+
while (pieceIndex !== 0 && swaps > 0) {
|
|
732
|
+
[address[pieceIndex], address[compress + swaps - 1]] = [address[compress + swaps - 1], address[pieceIndex]]
|
|
733
|
+
|
|
734
|
+
pieceIndex--
|
|
735
|
+
swaps--
|
|
736
|
+
}
|
|
737
|
+
} else if (pieceIndex !== 8) {
|
|
738
|
+
throw errors.IPV6_TOO_FEW_PIECES()
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
return address
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// https://url.spec.whatwg.org/#concept-opaque-host-parser
|
|
745
|
+
function parseOpaqueHost (input) {
|
|
746
|
+
const bytes = Buffer.from(input, 'utf8')
|
|
747
|
+
|
|
748
|
+
if (bytes.some(isForbiddenHostCodePoint)) throw errors.HOST_INVALID_CODE_POINT()
|
|
749
|
+
|
|
750
|
+
return percentEncodeAfterEncoding(bytes, c0ControlPercentEncodeSet)
|
|
514
751
|
}
|
|
515
752
|
|
|
516
753
|
// https://url.spec.whatwg.org/#special-scheme
|
|
@@ -600,3 +837,105 @@ function isSingleDotPathSegment (segment) {
|
|
|
600
837
|
function isDoubleDotPathSegment (segment) {
|
|
601
838
|
return segment === '..' || decodeURIComponent(segment) === '..'
|
|
602
839
|
}
|
|
840
|
+
|
|
841
|
+
// https://url.spec.whatwg.org/#forbidden-host-code-point
|
|
842
|
+
function isForbiddenHostCodePoint (c) {
|
|
843
|
+
return c === 0x0 || c === 0x9 || c === 0xa || c === 0xd || c === 0x20 || c === 0x23 || c === 0x2f || c === 0x3a || c === 0x3c || c === 0x3e || c === 0x3f || c === 0x40 || c === 0x5b || c === 0x5c || c === 0x5d || c === 0x5e || c === 0x7c
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// https://url.spec.whatwg.org/#forbidden-domain-code-point
|
|
847
|
+
function isForbiddenDomainCodePoint (c) {
|
|
848
|
+
return isForbiddenHostCodePoint(c) || c <= 0x1f || c === 0x25 || c === 0x7f
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// https://url.spec.whatwg.org/#concept-domain-to-ascii
|
|
852
|
+
function domainToASCII (domain, beStrict) {
|
|
853
|
+
const result = tr46.toASCII(domain, {
|
|
854
|
+
checkHyphens: false,
|
|
855
|
+
checkBidi: true,
|
|
856
|
+
checkJoiners: true,
|
|
857
|
+
useSTD3ASCIIRules: beStrict,
|
|
858
|
+
verifyDNSLength: beStrict
|
|
859
|
+
})
|
|
860
|
+
|
|
861
|
+
if (result === null || result === '') throw errors.DOMAIN_TO_ASCII()
|
|
862
|
+
|
|
863
|
+
return result
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// https://url.spec.whatwg.org/#percent-encode
|
|
867
|
+
function percentEncode (byte) {
|
|
868
|
+
return `%${byte.toString(16).toUpperCase().padStart(2, '0')}`
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// https://url.spec.whatwg.org/#string-percent-encode-after-encoding
|
|
872
|
+
function percentEncodeAfterEncoding (bytes, percentEncodeSet) {
|
|
873
|
+
let output = ''
|
|
874
|
+
|
|
875
|
+
for (const byte of bytes) {
|
|
876
|
+
if (percentEncodeSet(byte)) {
|
|
877
|
+
output += percentEncode(byte)
|
|
878
|
+
} else {
|
|
879
|
+
output += String.fromCharCode(byte)
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
return output
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// https://url.spec.whatwg.org/#percent-decode
|
|
887
|
+
function percentDecode (input) {
|
|
888
|
+
const output = Buffer.alloc(input.byteLength)
|
|
889
|
+
let outputIndex = 0
|
|
890
|
+
|
|
891
|
+
for (let i = 0, n = input.byteLength; i < n; i++) {
|
|
892
|
+
const byte = input[i]
|
|
893
|
+
|
|
894
|
+
if (byte !== 0x25) {
|
|
895
|
+
output[outputIndex++] = byte
|
|
896
|
+
} else if (byte === 0x25 && (!infra.isASCIIHexDigit(input[i + 1]) || !infra.isASCIIHexDigit(input[i + 2]))) {
|
|
897
|
+
output[outputIndex++] = byte
|
|
898
|
+
} else {
|
|
899
|
+
const bytePoint = parseInt(String.fromCodePoint(input[i + 1], input[i + 2]), 16)
|
|
900
|
+
output[outputIndex++] = bytePoint
|
|
901
|
+
i += 2
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
return output.subarray(0, outputIndex)
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// https://url.spec.whatwg.org/#string-utf-8-percent-encode
|
|
909
|
+
function UTF8PercentEncode (input, percentEncodeSet) {
|
|
910
|
+
return percentEncodeAfterEncoding(Buffer.from(input, 'utf8'), percentEncodeSet)
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// https://url.spec.whatwg.org/#c0-control-percent-encode-set
|
|
914
|
+
function c0ControlPercentEncodeSet (c) {
|
|
915
|
+
return c <= 0x1f || c > 0x7e
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// https://url.spec.whatwg.org/#fragment-percent-encode-set
|
|
919
|
+
function fragmentPercentEncodeSet (c) {
|
|
920
|
+
return c0ControlPercentEncodeSet(c) || c === 0x20 || c === 0x22 || c === 0x3c || c === 0x3e || c === 0x60
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// https://url.spec.whatwg.org/#query-percent-encode-set
|
|
924
|
+
function queryPercentEncodeSet (c) {
|
|
925
|
+
return c0ControlPercentEncodeSet(c) || c === 0x20 || c === 0x22 || c === 0x23 || c === 0x3c || c === 0x3e
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// https://url.spec.whatwg.org/#special-query-percent-encode-set
|
|
929
|
+
function specialQueryPercentEncodeSet (c) {
|
|
930
|
+
return queryPercentEncodeSet(c) || c === 0x27
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// https://url.spec.whatwg.org/#path-percent-encode-set
|
|
934
|
+
function pathPercentEncodeSet (c) {
|
|
935
|
+
return queryPercentEncodeSet(c) || c === 0x3f || c === 0x60 || c === 0x7b || c === 0x7d
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// https://url.spec.whatwg.org/#userinfo-percent-encode-set
|
|
939
|
+
function userinfoPercentEncodeSet (c) {
|
|
940
|
+
return pathPercentEncodeSet(c) || c === 0x2f || c === 0x3a || c === 0x3b || c === 0x3d || c === 0x40 || c === 0x5b || c === 0x5c || c === 0x5d || c === 0x5e || c === 0x7c
|
|
941
|
+
}
|
package/lib/serialize.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// https://url.spec.whatwg.org/#url-serializing
|
|
2
|
-
module.exports = function serialize (url, excludeFragment = false) {
|
|
2
|
+
module.exports = exports = function serialize (url, excludeFragment = false) {
|
|
3
3
|
let output = url.scheme + ':'
|
|
4
4
|
|
|
5
5
|
if (url.host !== null) {
|
|
@@ -15,7 +15,7 @@ module.exports = function serialize (url, excludeFragment = false) {
|
|
|
15
15
|
output += '@'
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
output += url.host
|
|
18
|
+
output += serializeHost(url.host)
|
|
19
19
|
|
|
20
20
|
if (url.port !== null) {
|
|
21
21
|
output += ':' + url.port.toString(10)
|
|
@@ -47,3 +47,52 @@ module.exports = function serialize (url, excludeFragment = false) {
|
|
|
47
47
|
|
|
48
48
|
return output
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
// https://url.spec.whatwg.org/#concept-host-serializer
|
|
52
|
+
const serializeHost = exports.host = function serializeHost (host) {
|
|
53
|
+
if (typeof host === 'number') return serializeIPv4(host)
|
|
54
|
+
if (typeof host !== 'string') return '[' + serializeIPv6(host) + ']'
|
|
55
|
+
|
|
56
|
+
return host
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// https://url.spec.whatwg.org/#concept-ipv4-serializer
|
|
60
|
+
function serializeIPv4 (address) {
|
|
61
|
+
let output = ''
|
|
62
|
+
let n = address
|
|
63
|
+
|
|
64
|
+
for (let i = 1; i <= 4; i++) {
|
|
65
|
+
output = (n % 256).toString(10) + output
|
|
66
|
+
|
|
67
|
+
if (i !== 4) output = '.' + output
|
|
68
|
+
|
|
69
|
+
n = n / 256 | 0
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return output
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// https://url.spec.whatwg.org/#concept-ipv6-serializer
|
|
76
|
+
function serializeIPv6 (address) {
|
|
77
|
+
let output = ''
|
|
78
|
+
const compress = 8 // TODO Find first longest 0 sequence
|
|
79
|
+
let ignore0 = false
|
|
80
|
+
|
|
81
|
+
for (let pieceIndex = 0; pieceIndex <= 7; pieceIndex++) {
|
|
82
|
+
if (ignore0 && address[pieceIndex] === 0) continue
|
|
83
|
+
|
|
84
|
+
ignore0 = false
|
|
85
|
+
|
|
86
|
+
if (compress === pieceIndex) {
|
|
87
|
+
output += pieceIndex === 0 ? '::' : ':'
|
|
88
|
+
ignore0 = true
|
|
89
|
+
continue
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
output += address[pieceIndex].toString(16)
|
|
93
|
+
|
|
94
|
+
if (pieceIndex !== 7) output += ':'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return output
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-url",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "URL parser for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"homepage": "https://github.com/holepunchto/bare-url",
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"bare-os": "^2.0.0",
|
|
25
|
-
"bare-path": "^2.0.0"
|
|
25
|
+
"bare-path": "^2.0.0",
|
|
26
|
+
"tr46": "^5.0.0"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"brittle": "^3.3.2",
|