Skip to content

Commit

Permalink
Support constructor strings with ipv6 addresses. (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
wanderview committed Jan 6, 2022
1 parent aa22a93 commit 0a6eefc
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 2 deletions.
21 changes: 20 additions & 1 deletion src/url-pattern-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ export class Parser {
// The current nest depth of `{ }` pattern groupings.
private groupDepth: number = 0;

// The current nesting depth of `[ ]` in hostname patterns.
private hostnameIPv6BracketDepth: number = 0;

// True if we should apply parse rules as if this is a "standard" URL. If
// false then this is treated as a "not a base URL".
private shouldTreatAsStandardURL: boolean = false;
Expand Down Expand Up @@ -242,8 +245,16 @@ export class Parser {
break;

case State.HOSTNAME:
// Track whether we are inside ipv6 address brackets.
if (this.isIPv6Open()) {
this.hostnameIPv6BracketDepth += 1;
} else if (this.isIPv6Close()) {
this.hostnameIPv6BracketDepth -= 1;
}

// If we find a `:` then we transition to the port component state.
if (this.isPortPrefix()) {
// However, we ignore `:` when parsing an ipv6 address.
if (this.isPortPrefix() && !this.hostnameIPv6BracketDepth) {
this.changeState(State.PORT, /*skip=*/1);
}

Expand Down Expand Up @@ -468,6 +479,14 @@ export class Parser {
return this.tokenList[this.tokenIndex].type == 'CLOSE';
}

private isIPv6Open(): boolean {
return this.isNonSpecialPatternChar(this.tokenIndex, '[');
}

private isIPv6Close(): boolean {
return this.isNonSpecialPatternChar(this.tokenIndex, ']');
}

private makeComponentString(): string {
const token: LexToken = this.tokenList[this.tokenIndex];
const componentCharStart = this.safeToken(this.componentStart).index;
Expand Down
2 changes: 1 addition & 1 deletion src/url-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ export function hostnameEncodeCallback(input: string): string {
if (input === '') {
return input;
}
if (/[#%/:<>?@[\]\\|]/g.test(input)) {
if (input[0] !== '[' && /[#%/:<>?@[\]\\|]/g.test(input)) {
throw(new TypeError(`Invalid hostname '${input}'`));
}
const url = new URL('https://example.com');
Expand Down
49 changes: 49 additions & 0 deletions urlpatterntestdata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2085,6 +2085,55 @@
"pathname": { "input": "/data:channel.html", "groups": {} }
}
},
{
"pattern": [ "http://[\\:\\:1]/" ],
"inputs": [ "http://[::1]/" ],
"exactly_empty_components": [ "username", "password", "port", "search",
"hash" ],
"expected_obj": {
"protocol": "http",
"hostname": "[\\:\\:1]",
"pathname": "/"
},
"expected_match": {
"protocol": { "input": "http", "groups": {} },
"hostname": { "input": "[::1]", "groups": {} },
"pathname": { "input": "/", "groups": {} }
}
},
{
"pattern": [ "http://[\\:\\:1]:8080/" ],
"inputs": [ "http://[::1]:8080/" ],
"exactly_empty_components": [ "username", "password", "search", "hash" ],
"expected_obj": {
"protocol": "http",
"hostname": "[\\:\\:1]",
"port": "8080",
"pathname": "/"
},
"expected_match": {
"protocol": { "input": "http", "groups": {} },
"hostname": { "input": "[::1]", "groups": {} },
"port": { "input": "8080", "groups": {} },
"pathname": { "input": "/", "groups": {} }
}
},
{
"pattern": [ "http://[\\:\\:a]/" ],
"inputs": [ "http://[::a]/" ],
"exactly_empty_components": [ "username", "password", "port", "search",
"hash" ],
"expected_obj": {
"protocol": "http",
"hostname": "[\\:\\:a]",
"pathname": "/"
},
"expected_match": {
"protocol": { "input": "http", "groups": {} },
"hostname": { "input": "[::a]", "groups": {} },
"pathname": { "input": "/", "groups": {} }
}
},
{
"pattern": [ "https://foo{{@}}example.com" ],
"inputs": [ "https://foo@example.com" ],
Expand Down

0 comments on commit 0a6eefc

Please sign in to comment.