Skip to content

Commit d0418c3

Browse files
committed
Fetch statusText polyfill (#5530)
* This provides a polyfill for the statusText The React Native fetch polyfill fails to provide this. See react-native-community/fetch#14 * Update status-text.ts
1 parent f87d0a8 commit d0418c3

File tree

3 files changed

+115
-28
lines changed

3 files changed

+115
-28
lines changed

packages/realm-network-transport/src/DefaultNetworkTransport.ts

+31-24
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
////////////////////////////////////////////////////////////////////////////
1818

1919
import { makeRequestBodyIterable } from "./IterableReadableStream";
20+
import { deriveStatusText } from "./status-text";
2021
import type {
2122
NetworkTransport,
2223
Request,
@@ -36,6 +37,29 @@ export class DefaultNetworkTransport implements NetworkTransport {
3637
"Content-Type": "application/json",
3738
};
3839

40+
private static createTimeoutSignal(timeoutMs: number | undefined) {
41+
if (typeof timeoutMs === "number") {
42+
const controller = new DefaultNetworkTransport.AbortController();
43+
// Call abort after a specific number of milliseconds
44+
const timeout = setTimeout(() => {
45+
controller.abort();
46+
}, timeoutMs);
47+
return {
48+
signal: controller.signal,
49+
cancelTimeout: () => {
50+
clearTimeout(timeout);
51+
},
52+
};
53+
} else {
54+
return {
55+
signal: undefined,
56+
cancelTimeout: () => {
57+
/* No-op */
58+
},
59+
};
60+
}
61+
}
62+
3963
constructor() {
4064
if (!DefaultNetworkTransport.fetch) {
4165
throw new Error("DefaultNetworkTransport.fetch must be set before it's used");
@@ -68,42 +92,25 @@ export class DefaultNetworkTransport implements NetworkTransport {
6892

6993
public async fetch<RequestBody = unknown>(request: Request<RequestBody>): Promise<FetchResponse> {
7094
const { timeoutMs, url, ...rest } = request;
71-
const { signal, cancelTimeout } = this.createTimeoutSignal(timeoutMs);
95+
const { signal, cancelTimeout } = DefaultNetworkTransport.createTimeoutSignal(timeoutMs);
7296
try {
7397
// Awaiting the response to cancel timeout on errors
7498
const response = await DefaultNetworkTransport.fetch(url, {
7599
...DefaultNetworkTransport.extraFetchOptions,
76100
signal, // Used to signal timeouts
77101
...rest,
78102
});
103+
// A bug in the React Native fetch polyfill leaves the statusText empty
104+
if (response.statusText === "") {
105+
const statusText = deriveStatusText(response.status);
106+
// @ts-expect-error Assigning to a read-only property
107+
response.statusText = statusText;
108+
}
79109
// Wraps the body of the request in an iterable interface
80110
return makeRequestBodyIterable(response);
81111
} finally {
82112
// Whatever happens, cancel any timeout
83113
cancelTimeout();
84114
}
85115
}
86-
87-
private createTimeoutSignal(timeoutMs: number | undefined) {
88-
if (typeof timeoutMs === "number") {
89-
const controller = new DefaultNetworkTransport.AbortController();
90-
// Call abort after a specific number of milliseconds
91-
const timeout = setTimeout(() => {
92-
controller.abort();
93-
}, timeoutMs);
94-
return {
95-
signal: controller.signal,
96-
cancelTimeout: () => {
97-
clearTimeout(timeout);
98-
},
99-
};
100-
} else {
101-
return {
102-
signal: undefined,
103-
cancelTimeout: () => {
104-
/* No-op */
105-
},
106-
};
107-
}
108-
}
109116
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Copyright 2023 Realm Inc.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
//
17+
////////////////////////////////////////////////////////////////////////////
18+
19+
/**
20+
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
21+
*/
22+
const HTTP_STATUS_TEXTS: Record<number, string | undefined> = {
23+
100: "Continue",
24+
101: "Switching Protocols",
25+
102: "Processing",
26+
200: "OK",
27+
201: "Created",
28+
202: "Accepted",
29+
203: "Non-Authoritative Information",
30+
204: "No Content",
31+
205: "Reset Content",
32+
206: "Partial Content",
33+
300: "Multiple Choices",
34+
301: "Moved Permanently",
35+
302: "Found",
36+
303: "See Other",
37+
304: "Not Modified",
38+
307: "Temporary Redirect",
39+
308: "Permanent Redirect",
40+
400: "Bad Request",
41+
401: "Unauthorized",
42+
402: "Payment Required",
43+
403: "Forbidden",
44+
404: "Not Found",
45+
405: "Method Not Allowed",
46+
406: "Not Acceptable",
47+
407: "Proxy Authentication Required",
48+
408: "Request Timeout",
49+
409: "Conflict",
50+
410: "Gone",
51+
411: "Length Required",
52+
412: "Precondition Failed",
53+
413: "Payload Too Large",
54+
414: "URI Too Long",
55+
415: "Unsupported Media Type",
56+
416: "Range Not Satisfiable",
57+
417: "Expectation Failed",
58+
418: "I'm a teapot",
59+
422: "Unprocessable Entity",
60+
425: "Too Early",
61+
426: "Upgrade Required",
62+
429: "Too Many Requests",
63+
431: "Request Header Fields Too Large",
64+
451: "Unavailable For Legal Reasons",
65+
500: "Internal Server Error",
66+
501: "Not Implemented",
67+
502: "Bad Gateway",
68+
503: "Service Unavailable",
69+
504: "Gateway Timeout",
70+
505: "HTTP Version Not Supported",
71+
506: "Variant Also Negotiates",
72+
507: "Insufficient Storage",
73+
508: "Loop Detected",
74+
510: "Not Extended",
75+
511: "Network Authentication Required",
76+
};
77+
78+
export function deriveStatusText(status: number): string | undefined {
79+
return HTTP_STATUS_TEXTS[status];
80+
}

packages/realm/src/app-services/User.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,11 @@ export class User<
268268
serviceName,
269269
);
270270

271-
const { body, ok, status, statusText } = await network.fetch(request);
272-
assert(ok, `Request failed: ${statusText} (${status})`);
273-
assert(body, "Expected a body in the response");
271+
const response = await network.fetch(request);
272+
assert(response.ok, () => `Request failed: ${response.statusText} (${response.status})`);
273+
assert(response.body, "Expected a body in the response");
274274

275-
return body;
275+
return response.body;
276276
}
277277

278278
/**

0 commit comments

Comments
 (0)