Skip to content

Commit 904fe77

Browse files
authored
fix: add cancellation to AsyncIterable correctly and leak (#4887)
* fix: add cancellation to AsyncIterable correctly and leak * Go * Go * Go * Hola * Go
1 parent 90011f0 commit 904fe77

File tree

9 files changed

+52
-21
lines changed

9 files changed

+52
-21
lines changed

.changeset/brown-vans-search.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@graphql-tools/executor-http': patch
3+
'@graphql-tools/utils': patch
4+
---
5+
6+
Fix leak on Node 14 and add cancellation to async iterables correctly

.changeset/cuddly-wasps-listen.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-tools/delegate': patch
3+
---
4+
5+
Fix handling variables

.github/workflows/tests.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ jobs:
127127
- name: Build Packages
128128
run: yarn build
129129
- name: Test
130-
run: yarn jest --no-watchman --ci browser
131-
env:
132-
TEST_BROWSER: true
130+
uses: nick-fields/retry@v2
131+
with:
132+
timeout_minutes: 10
133+
max_attempts: 5
134+
command: TEST_BROWSER=true yarn jest --no-watchman --ci browser

packages/delegate/src/finalizeGatewayRequest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
SelectionSetNode,
1717
TypeInfo,
1818
TypeNameMetaFieldDef,
19-
valueFromAST,
19+
valueFromASTUntyped,
2020
VariableDefinitionNode,
2121
versionInfo as graphqlVersionInfo,
2222
visit,
@@ -225,7 +225,7 @@ function updateArguments(
225225
let value: any;
226226
const existingValueNode = argumentNodeMap[argName]?.value;
227227
if (existingValueNode != null) {
228-
value = valueFromAST(existingValueNode, argType, variableValues);
228+
value = valueFromASTUntyped(existingValueNode, variableValues);
229229
}
230230
if (value == null) {
231231
value = serializeInputValue(argType, newArgs[argName]);

packages/executors/http/src/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import {
88
SyncExecutor,
99
} from '@graphql-tools/utils';
1010
import { GraphQLResolveInfo, print } from 'graphql';
11-
import { cancelNeeded } from './addCancelToResponseStream.js';
1211
import { isLiveQueryOperationDefinitionNode } from './isLiveQueryOperationDefinitionNode.js';
1312
import { prepareGETUrl } from './prepareGETUrl.js';
1413
import { ValueOrPromise } from 'value-or-promise';
1514
import { createFormDataFromVariables } from './createFormDataFromVariables.js';
1615
import { handleEventStreamResponse } from './handleEventStreamResponse.js';
1716
import { handleMultipartMixedResponse } from './handleMultipartMixedResponse.js';
1817
import { fetch as defaultFetch, AbortController } from '@whatwg-node/fetch';
18+
import { cancelNeeded } from './addCancelToResponseStream.js';
1919

2020
export type SyncFetchFn = (url: string, init?: RequestInit, context?: any, info?: GraphQLResolveInfo) => SyncResponse;
2121
export type SyncResponse = Omit<Response, 'json' | 'text'> & {
@@ -78,7 +78,7 @@ export function buildHTTPExecutor(
7878
export function buildHTTPExecutor(options?: HTTPExecutorOptions): Executor<any, HTTPExecutorOptions> {
7979
const executor = (request: ExecutionRequest<any, any, any, HTTPExecutorOptions>) => {
8080
const fetchFn = request.extensions?.fetch ?? options?.fetch ?? defaultFetch;
81-
const controller = cancelNeeded() ? new AbortController() : undefined;
81+
let controller: AbortController | undefined;
8282
let method = request.extensions?.method || options?.method || 'POST';
8383

8484
const operationAst = getOperationASTFromRequest(request);
@@ -113,13 +113,18 @@ export function buildHTTPExecutor(options?: HTTPExecutorOptions): Executor<any,
113113

114114
let timeoutId: any;
115115
if (options?.timeout) {
116+
controller = new AbortController();
116117
timeoutId = setTimeout(() => {
117118
if (!controller?.signal.aborted) {
118119
controller?.abort();
119120
}
120121
}, options.timeout);
121122
}
122123

124+
if (!controller && cancelNeeded()) {
125+
controller = new AbortController();
126+
}
127+
123128
return new ValueOrPromise(() => {
124129
switch (method) {
125130
case 'GET': {

packages/loaders/url/tests/url-loader-browser.spec.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -259,14 +259,11 @@ describe('[url-loader] webpack bundle compat', () => {
259259
document as any
260260
);
261261

262-
expect(results).toEqual([
263-
{ data: { countdown: [] } },
264-
{ data: { countdown: [3] } },
265-
{ data: { countdown: [3, 2] } },
266-
{ data: { countdown: [3, 2, 1] } },
267-
{ data: { countdown: [3, 2, 1, 0] } },
268-
{ data: { countdown: [3, 2, 1, 0] } },
269-
]);
262+
expect(results[0]).toEqual({ data: { countdown: [] } });
263+
expect(results[1]).toEqual({ data: { countdown: [3] } });
264+
expect(results[2]).toEqual({ data: { countdown: [3, 2] } });
265+
expect(results[3]).toEqual({ data: { countdown: [3, 2, 1] } });
266+
expect(results[4]).toEqual({ data: { countdown: [3, 2, 1, 0] } });
270267
});
271268

272269
it('handles SSE subscription operations', async () => {

packages/loaders/url/tests/yoga-compat.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { useEngine } from '@envelop/core';
1212
import { useDeferStream } from '@graphql-yoga/plugin-defer-stream';
1313

1414
describe('Yoga Compatibility', () => {
15+
jest.setTimeout(10000);
1516
const loader = new UrlLoader();
1617
let httpServer: http.Server;
1718
const liveQueryStore = new InMemoryLiveQueryStore();
@@ -151,6 +152,7 @@ describe('Yoga Compatibility', () => {
151152
if (httpServer !== undefined) {
152153
await new Promise<void>(resolve => httpServer.close(() => resolve()));
153154
}
155+
await sleep(1000);
154156
});
155157

156158
it('should handle defer', async () => {

packages/utils/src/withCancel.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,11 @@ export function getAsyncIterableWithCancel<T, TAsyncIterable extends AsyncIterab
4949
const existingPropValue = Reflect.get(asyncIterable, prop, receiver);
5050
if (Symbol.asyncIterator === prop) {
5151
return function asyncIteratorFactory() {
52-
const asyncIterator: AsyncIterator<T> = Reflect.apply(
53-
existingPropValue[Symbol.asyncIterator],
54-
asyncIterable,
55-
[]
56-
);
52+
const asyncIterator: AsyncIterator<T> = Reflect.apply(existingPropValue as any, asyncIterable as any, []);
5753
return getAsyncIteratorWithCancel(asyncIterator, onCancel);
5854
};
5955
} else if (typeof existingPropValue === 'function') {
60-
return proxyMethodFactory(asyncIterable, existingPropValue[Symbol.asyncIterator]);
56+
return proxyMethodFactory<any, any>(asyncIterable, existingPropValue);
6157
}
6258
return existingPropValue;
6359
},

yarn.lock

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,24 @@
16701670
dependencies:
16711671
giscus "^1.2.3"
16721672

1673+
"@graphql-tools/executor@0.0.9":
1674+
version "0.0.9"
1675+
resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.9.tgz#f55f8cbe12d7989b0ff58cca9a041fbdde3dbd40"
1676+
integrity sha512-qLhQWXTxTS6gbL9INAQa4FJIqTd2tccnbs4HswOx35KnyLaLtREuQ8uTfU+5qMrRIBhuzpGdkP2ssqxLyOJ5rA==
1677+
dependencies:
1678+
"@graphql-tools/utils" "9.1.1"
1679+
"@graphql-typed-document-node/core" "3.1.1"
1680+
"@repeaterjs/repeater" "3.0.4"
1681+
tslib "^2.4.0"
1682+
value-or-promise "1.0.11"
1683+
1684+
"@graphql-tools/utils@9.1.1":
1685+
version "9.1.1"
1686+
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.1.1.tgz#b47ea8f0d18c038c5c1c429e72caa5c25039fbab"
1687+
integrity sha512-DXKLIEDbihK24fktR2hwp/BNIVwULIHaSTNTNhXS+19vgT50eX9wndx1bPxGwHnVBOONcwjXy0roQac49vdt/w==
1688+
dependencies:
1689+
tslib "^2.4.0"
1690+
16731691
"@graphql-tools/utils@^8.5.2", "@graphql-tools/utils@^8.8.0":
16741692
version "8.13.1"
16751693
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.13.1.tgz#b247607e400365c2cd87ff54654d4ad25a7ac491"

0 commit comments

Comments
 (0)