[Flight][Reply] Close Response after creating root chunk (#27634)
creating the root after closing the response can lead to a promise that never rejects. This is not intended use of the decodeReply API but if pathalogical cases where you pass a raw FormData into this fucntion with no zero chunk it can hang forever. This reordering causes a connection error instead --------- Co-authored-by: Zack Tanner <zacktanner@gmail.com>
Josh Story committed
Oct 31, 2023 at 18:31 UTC
08a39539fc6922cdb9e2405029127a1911b1b809
8 files changed
+24
-7
packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js
+2
-1
@@ -162,8 +162,9 @@ function decodeReply<T>(
162
body = form;
163
}
164
const response = createResponse(moduleBasePath, '', body);
165
+ const root = getRoot<T>(response);
166
close(response);
166
- return getRoot(response);
167
+ return root;
168
}
169
170
export {
packages/react-server-dom-turbopack/src/ReactFlightDOMServerBrowser.js
+2
-1
@@ -93,8 +93,9 @@ function decodeReply<T>(
93
body = form;
94
}
95
const response = createResponse(turbopackMap, '', body);
96
+ const root = getRoot<T>(response);
97
close(response);
97
- return getRoot(response);
98
+ return root;
99
}
100
101
export {renderToReadableStream, decodeReply, decodeAction};
packages/react-server-dom-turbopack/src/ReactFlightDOMServerEdge.js
+2
-1
@@ -93,8 +93,9 @@ function decodeReply<T>(
93
body = form;
94
}
95
const response = createResponse(turbopackMap, '', body);
96
+ const root = getRoot<T>(response);
97
close(response);
97
- return getRoot(response);
98
+ return root;
99
}
100
101
export {renderToReadableStream, decodeReply, decodeAction};
packages/react-server-dom-turbopack/src/ReactFlightDOMServerNode.js
+2
-1
@@ -158,8 +158,9 @@ function decodeReply<T>(
158
body = form;
159
}
160
const response = createResponse(turbopackMap, '', body);
161
+ const root = getRoot<T>(response);
162
close(response);
162
- return getRoot(response);
163
+ return root;
164
}
165
166
export {
packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js
+2
-1
@@ -100,8 +100,9 @@ function decodeReply<T>(
100
body = form;
101
}
102
const response = createResponse(webpackMap, '', body);
103
+ const root = getRoot<T>(response);
104
close(response);
104
- return getRoot(response);
105
+ return root;
106
}
107
108
export {renderToReadableStream, decodeReply, decodeAction, decodeFormState};
packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js
+2
-1
@@ -100,8 +100,9 @@ function decodeReply<T>(
100
body = form;
101
}
102
const response = createResponse(webpackMap, '', body);
103
+ const root = getRoot<T>(response);
104
close(response);
104
- return getRoot(response);
105
+ return root;
106
}
107
108
export {renderToReadableStream, decodeReply, decodeAction, decodeFormState};
packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js
+2
-1
@@ -181,8 +181,9 @@ function decodeReply<T>(
181
body = form;
182
}
183
const response = createResponse(webpackMap, '', body);
184
+ const root = getRoot<T>(response);
185
close(response);
185
- return getRoot(response);
186
+ return root;
187
}
188
189
export {
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReply-test.js
+10
@@ -231,4 +231,14 @@ describe('ReactFlightDOMReply', () => {
231
expect(s2.has('hi')).toBe(true);
232
expect(s2).toEqual(s);
233
});
234
+
235
+ it('does not hang indefinitely when calling decodeReply with FormData', async () => {
236
+ let error;
237
+ try {
238
+ await ReactServerDOMServer.decodeReply(new FormData(), webpackServerMap);
239
+ } catch (e) {
240
+ error = e;
241
+ }
242
+ expect(error.message).toBe('Connection closed.');
243
+ });
244
});