From 439fb82d2a07cece417a18c47e37cfdeaaf38db7 Mon Sep 17 00:00:00 2001 From: Szymon Marczak <36894700+szmarczak@users.noreply.github.com> Date: Mon, 2 Aug 2021 09:02:19 +0200 Subject: [PATCH] Fix relative URLs when paginating --- documentation/4-pagination.md | 4 +++- source/core/index.ts | 4 ++-- source/core/options.ts | 12 +++++++++--- source/core/response.ts | 1 + test/pagination.ts | 12 ++++++++++-- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/documentation/4-pagination.md b/documentation/4-pagination.md index b4b760fc7..32459bf32 100644 --- a/documentation/4-pagination.md +++ b/documentation/4-pagination.md @@ -87,7 +87,9 @@ console.log(results); const next = parsed.find(entry => entry.parameters.rel === 'next' || entry.parameters.rel === '"next"'); if (next) { - return {url: next.reference}; + return { + url: new URL(next.reference, response.requestUrl) + }; } return false; diff --git a/source/core/index.ts b/source/core/index.ts index 64bc90a4b..806748543 100644 --- a/source/core/index.ts +++ b/source/core/index.ts @@ -1103,7 +1103,7 @@ export default class Request extends Duplex implements RequestEvents { try { // We can't do `await fn(...)`, // because stream `error` event can be emitted before `Promise.resolve()`. - let requestOrResponse = fn(url, this._requestOptions); + let requestOrResponse = fn!(url, this._requestOptions); if (is.promise(requestOrResponse)) { requestOrResponse = await requestOrResponse; @@ -1111,7 +1111,7 @@ export default class Request extends Duplex implements RequestEvents { // Fallback if (is.undefined(requestOrResponse)) { - requestOrResponse = options.getFallbackRequestFunction()(url, this._requestOptions); + requestOrResponse = options.getFallbackRequestFunction()!(url, this._requestOptions); if (is.promise(requestOrResponse)) { requestOrResponse = await requestOrResponse; diff --git a/source/core/options.ts b/source/core/options.ts index 6a9377b26..98a464185 100644 --- a/source/core/options.ts +++ b/source/core/options.ts @@ -691,7 +691,9 @@ const defaultInternals: Options['_internals'] = { const next = parsed.find(entry => entry.parameters.rel === 'next' || entry.parameters.rel === '"next"'); if (next) { - return {url: next.reference}; + return { + url: new URL(next.reference, response.requestUrl), + }; } return false; @@ -2224,13 +2226,17 @@ export default class Options { return this.getFallbackRequestFunction(); } - return request!; + return request; } getFallbackRequestFunction() { const url = this._internals.url as (URL | undefined); - if (url!.protocol === 'https:') { + if (!url) { + return; + } + + if (url.protocol === 'https:') { if (this._internals.http2) { if (major < 15 || (major === 15 && minor < 10)) { const error = new Error('To use the `http2` option, install Node.js 15.10.0 or above'); diff --git a/source/core/response.ts b/source/core/response.ts index 314bf4775..a02ece88d 100644 --- a/source/core/response.ts +++ b/source/core/response.ts @@ -1,3 +1,4 @@ +import type {URL} from 'url'; import type {IncomingMessageWithTimings, Timings} from '@szmarczak/http-timer'; import {RequestError} from './errors.js'; import type {ParseJsonFunction, ResponseType} from './options.js'; diff --git a/test/pagination.ts b/test/pagination.ts index 78cd21c37..9fd4e6fdd 100644 --- a/test/pagination.ts +++ b/test/pagination.ts @@ -17,14 +17,14 @@ const resetPagination = { shouldContinue: undefined, }; -const attachHandler = (server: ExtendedHttpTestServer, count: number): void => { +const attachHandler = (server: ExtendedHttpTestServer, count: number, {relative} = {relative: false}): void => { server.get('/', (request, response) => { // eslint-disable-next-line unicorn/prevent-abbreviations const searchParams = new URLSearchParams(request.url.split('?')[1]); const page = Number(searchParams.get('page')) || 1; if (page < count) { - response.setHeader('link', `<${server.url}/?page=${page + 1}>; rel="next"`); + response.setHeader('link', `<${relative ? '' : server.url}/?page=${page + 1}>; rel="next"`); } response.end(`[${page <= count ? page : ''}]`); @@ -720,6 +720,14 @@ test('calls init hooks on pagination', withServer, async (t, server) => { ]); }); +test('retrieves all elements - relative url', withServer, async (t, server, got) => { + attachHandler(server, 2, {relative: true}); + + const result = await got.paginate.all(''); + + t.deepEqual(result, [1, 2]); +}); + test('throws when transform does not return an array', withServer, async (t, server) => { server.get('/', (_request, response) => { response.end(JSON.stringify(''));