Repository: sindresorhus/got (ts)

Issue:
Custom URL query scheme is modified
I need to perform requests against an external API that has a custom encoding scheme in the query. More specifically, it requires that the caller put an unencoded URL at the very end of the query string.

While this is perfectly valid as a URL, `got` seems to assume that query strings are formatted as `key1=value1&key2=value2` etc and is first parsing and then re-serializing the query with that assumption in mind. See:
https://github.com/sindresorhus/got/blob/1c54a03bc6a809b73970f1694d3fccd22b664997/source/normalize-arguments.js#L113

However, this means that you no longer support custom query schemes which are perfectly valid URLs? Note that the URL spec does not require a particular query encoding scheme as long as you comply with the allowed character sets.

Pull request:
fix: got should not alter query given as a string
fixes #585 

Patch:
diff --git a/source/normalize-arguments.js b/source/normalize-arguments.js
index 554c428..f163566 100644
--- a/source/normalize-arguments.js
+++ b/source/normalize-arguments.js
@@ -109,8 +109,10 @@ module.exports = (url, options, defaults) => {
 
 	const {query} = options;
 	if (!is.empty(query) || query instanceof URLSearchParamsGlobal) {
-		const queryParams = new URLSearchParamsGlobal(query);
-		options.path = `${options.path.split('?')[0]}?${queryParams.toString()}`;
+		if (!is.string(query)) {
+			options.query = (new URLSearchParamsGlobal(query)).toString();
+		}
+		options.path = `${options.path.split('?')[0]}?${options.query}`;
 		delete options.query;
 	}
 


Test patch:
diff --git a/test/arguments.js b/test/arguments.js
index ff8b2c3..faf96d7 100644
--- a/test/arguments.js
+++ b/test/arguments.js
@@ -9,26 +9,20 @@ let s;
 test.before('setup', async () => {
 	s = await createServer();
 
+	const echoUrl = (request, response) => {
+		response.end(request.url);
+	};
+
 	s.on('/', (request, response) => {
 		response.statusCode = 404;
 		response.end();
 	});
 
-	s.on('/test', (request, response) => {
-		response.end(request.url);
-	});
-
-	s.on('/?test=wow', (request, response) => {
-		response.end(request.url);
-	});
-
-	s.on('/test/foobar', (request, response) => {
-		response.end(request.url);
-	});
-
-	s.on('/?test=it’s+ok', (request, response) => {
-		response.end(request.url);
-	});
+	s.on('/test', echoUrl);
+	s.on('/?test=wow', echoUrl);
+	s.on('/test/foobar', echoUrl);
+	s.on('/?test=it’s+ok', echoUrl);
+	s.on('/?test=http://example.com?foo=bar', echoUrl);
 
 	s.on('/stream', (request, response) => {
 		response.end('ok');
@@ -51,6 +45,12 @@ test('url should be utf-8 encoded', async t => {
 	t.regex(error.message, /Parameter `url` must contain valid UTF-8 character sequences/);
 });
 
+test('string url with query is preserved', async t => {
+	const path = `/?test=http://example.com?foo=bar`;
+	const response = await got(`${s.url}${path}`);
+	t.is(response.body, path);
+});
+
 test('options are optional', async t => {
 	t.is((await got(`${s.url}/test`)).body, '/test');
 });
diff --git a/test/redirects.js b/test/redirects.js
index f56fb9c..2bbfbfe 100644
--- a/test/redirects.js
+++ b/test/redirects.js
@@ -89,7 +89,7 @@ test.before('setup', async () => {
 		response.end();
 	});
 
-	http.on('/relativeQuery?bang=', (request, response) => {
+	http.on('/relativeQuery?bang', (request, response) => {
 		response.writeHead(302, {
 			location: '/'
 		});