Skip to content

Commit ace7536

Browse files
authored
fix: properly encode url with unicode characters (#1291)
* fix: properly encode url with unicode characters * release: 2.6.3
1 parent 152214c commit ace7536

File tree

6 files changed

+79
-8
lines changed

6 files changed

+79
-8
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ Changelog
55

66
# 2.x release
77

8+
## v2.6.3
9+
10+
- Fix: properly encode url with unicode characters
11+
812
## v2.6.2
913

1014
- Fix: used full filename for main in package.json

package.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "node-fetch",
3-
"version": "2.6.2",
3+
"version": "2.6.3",
44
"description": "A light-weight module that brings window.fetch to node.js",
55
"main": "lib/index.js",
66
"browser": "./browser.js",
@@ -36,6 +36,9 @@
3636
"url": "https://github.com/bitinn/node-fetch/issues"
3737
},
3838
"homepage": "https://github.com/bitinn/node-fetch",
39+
"dependencies": {
40+
"whatwg-url": "^5.0.0"
41+
},
3942
"devDependencies": {
4043
"@ungap/url-search-params": "^0.1.2",
4144
"abort-controller": "^1.1.0",
@@ -60,7 +63,6 @@
6063
"rollup": "^0.63.4",
6164
"rollup-plugin-babel": "^3.0.7",
6265
"string-to-arraybuffer": "^1.0.2",
63-
"teeny-request": "3.7.0",
64-
"whatwg-url": "^5.0.0"
66+
"teeny-request": "3.7.0"
6567
}
6668
}

rollup.config.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import isBuiltin from 'is-builtin-module';
22
import babel from 'rollup-plugin-babel';
3+
import packageJson from './package.json';
34
import tweakDefault from './build/rollup-plugin';
45

56
process.env.BABEL_ENV = 'rollup';
67

8+
const dependencies = Object.keys(packageJson.dependencies);
9+
710
export default {
811
input: 'src/index.js',
912
output: [
@@ -18,6 +21,6 @@ export default {
1821
tweakDefault()
1922
],
2023
external: function (id) {
21-
return isBuiltin(id);
24+
return dependencies.includes(id) || isBuiltin(id);
2225
}
2326
};

src/request.js

+37-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import Url from 'url';
1111
import Stream from 'stream';
12+
import {URL} from 'whatwg-url';
1213
import Headers, { exportNodeCompatibleHeaders } from './headers.js';
1314
import Body, { clone, extractContentType, getTotalBytes } from './body';
1415

@@ -18,6 +19,39 @@ const INTERNALS = Symbol('Request internals');
1819
const parse_url = Url.parse;
1920
const format_url = Url.format;
2021

22+
/**
23+
* Wrapper around `new URL` to handle arbitrary URLs
24+
*
25+
* @param {string} urlStr
26+
* @return {void}
27+
*/
28+
function parseURL(urlStr) {
29+
/*
30+
Check whether the URL is absolute or not
31+
32+
Scheme: https://tools.ietf.org/html/rfc3986#section-3.1
33+
Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3
34+
*/
35+
if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.exec(urlStr)) {
36+
const url = new URL(urlStr);
37+
38+
return {
39+
path: url.pathname,
40+
pathname: url.pathname,
41+
hostname: url.hostname,
42+
protocol: url.protocol,
43+
port: url.port,
44+
hash: url.hash,
45+
search: url.search,
46+
query: url.query,
47+
href: url.href,
48+
}
49+
}
50+
51+
// Fallback to old implementation for arbitrary URLs
52+
return parse_url(urlStr);
53+
}
54+
2155
const streamDestructionSupported = 'destroy' in Stream.Readable.prototype;
2256

2357
/**
@@ -59,14 +93,14 @@ export default class Request {
5993
// in order to support Node.js' Url objects; though WHATWG's URL objects
6094
// will fall into this branch also (since their `toString()` will return
6195
// `href` property anyway)
62-
parsedURL = parse_url(input.href);
96+
parsedURL = parseURL(input.href);
6397
} else {
6498
// coerce input to a string before attempting to parse
65-
parsedURL = parse_url(`${input}`);
99+
parsedURL = parseURL(`${input}`);
66100
}
67101
input = {};
68102
} else {
69-
parsedURL = parse_url(input.url);
103+
parsedURL = parseURL(input.url);
70104
}
71105

72106
let method = init.method || input.method || 'GET';

test/server.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export default class TestServer {
3232
}
3333

3434
router(req, res) {
35-
let p = parse(req.url).pathname;
35+
let p = decodeURIComponent(parse(req.url).pathname);
3636

3737
if (p === '/hello') {
3838
res.statusCode = 200;
@@ -384,6 +384,12 @@ export default class TestServer {
384384
});
385385
req.pipe(parser);
386386
}
387+
388+
if (p === '/issues/1290/ひらがな') {
389+
res.statusCode = 200;
390+
res.setHeader('Content-Type', 'text/plain');
391+
res.end('Success');
392+
}
387393
}
388394
}
389395

test/test.js

+22
Original file line numberDiff line numberDiff line change
@@ -2845,3 +2845,25 @@ describe('external encoding', () => {
28452845
});
28462846
});
28472847
});
2848+
2849+
describe('issue #1290', function() {
2850+
it('should handle escaped unicode in URLs', () => {
2851+
const url = `${base}issues/1290/%E3%81%B2%E3%82%89%E3%81%8C%E3%81%AA`;
2852+
return fetch(url).then((res) => {
2853+
expect(res.status).to.equal(200);
2854+
return res.text().then(result => {
2855+
expect(result).to.equal('Success');
2856+
});
2857+
});
2858+
});
2859+
2860+
it('should handle unicode in URLs', () => {
2861+
const url = `${base}issues/1290/ひらがな`;
2862+
return fetch(url).then((res) => {
2863+
expect(res.status).to.equal(200);
2864+
return res.text().then(result => {
2865+
expect(result).to.equal('Success');
2866+
});
2867+
});
2868+
});
2869+
});

0 commit comments

Comments
 (0)