-
-
Notifications
You must be signed in to change notification settings - Fork 443
Description
First of all thank you for the awesome library!
Wanted to share something I found when writing some unit tests on one of our integration of the ky library. We implemented a Response hook that is calling a refresh token endpoint when the response from the endpoint has a 401 error code. Inspired by the code from the ky documentation.
return async (
request: KyRequest,
_options: KyOptions,
response: KyResponse,
) => {
if (
response.status === 401 &&
!excludeUrls.some((url) => request.url.includes(url))
) {
const accessToken = await refresh()
if (accessToken) {
const headers = new Headers(request.headers)
headers.set('Authorization', `Bearer ${accessToken}`)
return ky.retry({
request: new Request(request, { headers }),
code: 'TOKEN_REFRESHED',
})
}
}
return response
}It works fine on the browser but when trying to test the feature in a node based environment (SSR, Vitest) it appeared that some of the tests were failing due to a timeout. After a 'bit' of research I found out that it was coming from a specific part of the ky library code where we're trying to cancel the response body stream before retrying the request.
Line 68 in 68a5228
| await Promise.all([ |
Basically in our response hook scenario, the body of the response is never read. And it appears that when it happens, the promise returned by the first cancel call can hang forever.
ky.request.bodyUsed: false
First call to clonedResponse.body?.cancel(): Promise { <pending> }
First call to response.body?.cancel(): Promise { <pending> }
Second call to clonedResponse.body?.cancel(): Promise { undefined }
Second call to response.body?.cancel(): Promise { undefined }
It can potentially be linked to an undici ticket I found while doing my researches. Either there's a missing clone we do not cancel or something like that: nodejs/undici#4562
It might also be linked to that PR as well: #741
In your unit tests it works because it appears that in all of them, the hook actually reads the body. Not just the headers like what we do in our implementation:
Line 966 in 68a5228
| const data = await response.clone().json(); |
Didn't find any previous report or documentation about this issue so I am not sure about what should be the best approach here. Meanwhile we will try to make sure to read the response body even if that is unnecessary for us to at least make sure that in works in node-based envs.