diff --git a/package.json b/package.json index 09ff83d..1691896 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "chai-as-promised": "^7.1.1", "chai-iterator": "^3.0.2", "chai-string": "^1.5.0", - "coveralls": "^3.0.11", + "coveralls": "^3.0.11", "form-data": "^3.0.0", "mocha": "^7.1.1", "nyc": "^15.0.1", diff --git a/src/request.js b/src/request.js index 08d46f7..19b79d3 100644 --- a/src/request.js +++ b/src/request.js @@ -12,6 +12,7 @@ import Stream from 'stream'; import Headers, {exportNodeCompatibleHeaders} from './headers'; import Body, {clone, extractContentType, getTotalBytes} from './body'; import {isAbortSignal} from './utils/is'; +import {getSearch} from './utils/get-search'; const INTERNALS = Symbol('Request internals'); @@ -258,9 +259,11 @@ export function getNodeRequestOptions(request) { // HTTP-network fetch step 4.2 // chunked encoding is handled by Node.js - // manually spread the URL object instead of spread syntax + const search = getSearch(parsedURL); + + // Manually spread the URL object instead of spread syntax const requestOptions = { - path: parsedURL.pathname + parsedURL.search, + path: parsedURL.pathname + search, pathname: parsedURL.pathname, hostname: parsedURL.hostname, protocol: parsedURL.protocol, diff --git a/src/utils/get-search.js b/src/utils/get-search.js new file mode 100644 index 0000000..b3844d8 --- /dev/null +++ b/src/utils/get-search.js @@ -0,0 +1,9 @@ +export function getSearch(parsedURL) { + if (parsedURL.search) { + return parsedURL.search; + } + + const lastOffset = parsedURL.href.length - 1; + const hash = parsedURL.hash || (parsedURL.href[lastOffset] === '#' ? '#' : ''); + return parsedURL.href[lastOffset - hash.length] === '?' ? '?' : ''; +} diff --git a/test/main.js b/test/main.js index 4f6134b..81e1273 100644 --- a/test/main.js +++ b/test/main.js @@ -1909,6 +1909,39 @@ describe('node-fetch', () => { }); }); + it('should keep `?` sign in URL when no params are given', () => { + const url = `${base}question?`; + const urlObject = new URL(url); + const request = new Request(urlObject); + return fetch(request).then(res => { + expect(res.url).to.equal(url); + expect(res.ok).to.be.true; + expect(res.status).to.equal(200); + }); + }); + + it('if params are given, do not modify anything', () => { + const url = `${base}question?a=1`; + const urlObject = new URL(url); + const request = new Request(urlObject); + return fetch(request).then(res => { + expect(res.url).to.equal(url); + expect(res.ok).to.be.true; + expect(res.status).to.equal(200); + }); + }); + + it('should preserve the hash (#) symbol', () => { + const url = `${base}question?#`; + const urlObject = new URL(url); + const request = new Request(urlObject); + return fetch(request).then(res => { + expect(res.url).to.equal(url); + expect(res.ok).to.be.true; + expect(res.status).to.equal(200); + }); + }); + it('should support reading blob as text', () => { return new Response('hello') .blob() diff --git a/test/utils/server.js b/test/utils/server.js index 14f5af4..0616d35 100644 --- a/test/utils/server.js +++ b/test/utils/server.js @@ -49,6 +49,12 @@ export default class TestServer { res.end('world'); } + if (p.includes('question')) { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end('ok'); + } + if (p === '/plain') { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain');