Skip to content

Commit e88a207

Browse files
committed
This provides a polyfill for the statusText
The React Native fetch polyfill fails to provide this. See react-native-community/fetch#14
1 parent 9448537 commit e88a207

File tree

3 files changed

+112
-28
lines changed

3 files changed

+112
-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,77 @@
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+
const HTTP_STATUS_TEXTS: Record<number, string | undefined> = {
20+
100: "Continue",
21+
101: "Switching Protocols",
22+
102: "Processing",
23+
200: "OK",
24+
201: "Created",
25+
202: "Accepted",
26+
203: "Non-Authoritative Information",
27+
204: "No Content",
28+
205: "Reset Content",
29+
206: "Partial Content",
30+
300: "Multiple Choices",
31+
301: "Moved Permanently",
32+
302: "Found",
33+
303: "See Other",
34+
304: "Not Modified",
35+
307: "Temporary Redirect",
36+
308: "Permanent Redirect",
37+
400: "Bad Request",
38+
401: "Unauthorized",
39+
402: "Payment Required",
40+
403: "Forbidden",
41+
404: "Not Found",
42+
405: "Method Not Allowed",
43+
406: "Not Acceptable",
44+
407: "Proxy Authentication Required",
45+
408: "Request Timeout",
46+
409: "Conflict",
47+
410: "Gone",
48+
411: "Length Required",
49+
412: "Precondition Failed",
50+
413: "Payload Too Large",
51+
414: "URI Too Long",
52+
415: "Unsupported Media Type",
53+
416: "Range Not Satisfiable",
54+
417: "Expectation Failed",
55+
418: "I'm a teapot",
56+
422: "Unprocessable Entity",
57+
425: "Too Early",
58+
426: "Upgrade Required",
59+
429: "Too Many Requests",
60+
431: "Request Header Fields Too Large",
61+
451: "Unavailable For Legal Reasons",
62+
500: "Internal Server Error",
63+
501: "Not Implemented",
64+
502: "Bad Gateway",
65+
503: "Service Unavailable",
66+
504: "Gateway Timeout",
67+
505: "HTTP Version Not Supported",
68+
506: "Variant Also Negotiates",
69+
507: "Insufficient Storage",
70+
508: "Loop Detected",
71+
510: "Not Extended",
72+
511: "Network Authentication Required",
73+
};
74+
75+
export function deriveStatusText(status: number): string | undefined {
76+
return HTTP_STATUS_TEXTS[status];
77+
}

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)