react-native-update 10.53.1 → 10.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-CN.md +31 -31
- package/README.md +31 -31
- package/android/jni/HDiffPatch/.travis.yml +33 -0
- package/android/jni/HDiffPatch/builds/xcode/bestParams.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/android/jni/HDiffPatch/builds/xcode/hdiffz.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/android/jni/HDiffPatch/builds/xcode/hdiffz.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/android/jni/HDiffPatch/builds/xcode/hpatchz.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/android/jni/HDiffPatch/builds/xcode/lz4.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/android/jni/HDiffPatch/builds/xcode/lzma.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/android/jni/HDiffPatch/builds/xcode/testHashClash.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/android/jni/HDiffPatch/builds/xcode/testHashClash.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/android/jni/HDiffPatch/builds/xcode/unitTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/android/jni/HDiffPatch/builds/xcode/zstd.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/android/jni/lzma/DOC/7zC.txt +187 -0
- package/android/jni/lzma/DOC/7zFormat.txt +469 -0
- package/android/jni/lzma/DOC/Methods.txt +173 -0
- package/android/jni/lzma/DOC/installer.txt +166 -0
- package/android/jni/lzma/DOC/lzma-history.txt +446 -0
- package/android/jni/lzma/DOC/lzma-sdk.txt +357 -0
- package/android/jni/lzma/DOC/lzma-specification.txt +1176 -0
- package/android/jni/lzma/DOC/lzma.txt +328 -0
- package/android/jni/lzma/bin/7zS2.sfx +0 -0
- package/android/jni/lzma/bin/7zS2con.sfx +0 -0
- package/android/jni/lzma/bin/7zSD.sfx +0 -0
- package/android/jni/lzma/bin/7zdec.exe +0 -0
- package/android/jni/lzma/bin/7zr.exe +0 -0
- package/android/jni/lzma/bin/installer/config.txt +5 -0
- package/android/jni/lzma/bin/installer/cr.bat +5 -0
- package/android/jni/lzma/bin/lzma.exe +0 -0
- package/android/jni/lzma/bin/x64/7zr.exe +0 -0
- package/android/lib/arm64-v8a/librnupdate.so +0 -0
- package/android/lib/armeabi-v7a/librnupdate.so +0 -0
- package/android/lib/x86/librnupdate.so +0 -0
- package/android/lib/x86_64/librnupdate.so +0 -0
- package/android/src/main/java/cn/reactnative/modules/update/ArchiveLimits.java +44 -0
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +124 -40
- package/android/src/main/java/cn/reactnative/modules/update/InstallRecord.java +125 -0
- package/android/src/main/java/cn/reactnative/modules/update/NativeCheckOrchestrator.java +43 -11
- package/android/src/main/java/cn/reactnative/modules/update/NativeUpdateFlow.java +2 -0
- package/android/src/main/java/cn/reactnative/modules/update/SafeZipFile.java +90 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +79 -19
- package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +21 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +33 -2
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleSupport.java +1 -0
- package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
- package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +5 -0
- package/cpp/patch_core/archive_limits.h +38 -0
- package/cpp/patch_core/install_record.h +36 -0
- package/cpp/update_flow_core/flow_json.h +1 -0
- package/cpp/update_flow_core/update_flow_core.cpp +41 -7
- package/cpp/update_flow_core/update_flow_core.h +9 -0
- package/cpp/update_flow_core/update_flow_jni.cpp +12 -0
- package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +2 -0
- package/harmony/pushy/src/main/cpp/pushy.cpp +37 -0
- package/harmony/pushy/src/main/ets/ArchiveLimits.ts +83 -0
- package/harmony/pushy/src/main/ets/DownloadTask.ts +126 -30
- package/harmony/pushy/src/main/ets/InstallRecord.ts +129 -0
- package/harmony/pushy/src/main/ets/NativeCheckOrchestrator.ts +24 -2
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +5 -0
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +12 -0
- package/harmony/pushy/src/main/ets/UpdateContext.ts +78 -15
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy.mm +384 -25
- package/ios/RCTPushy/RCTPushyDownloader.mm +32 -0
- package/package.json +25 -2
- package/src/NativePushy.ts +13 -0
- package/src/client.ts +160 -43
- package/src/core.ts +17 -3
- package/src/error.ts +3 -0
- package/src/index.ts +8 -0
- package/src/locales/en.ts +3 -0
- package/src/locales/zh.ts +2 -0
- package/src/metadata.ts +166 -0
- package/src/provider.tsx +52 -17
- package/src/type.ts +22 -0
- package/src/updateFlowCore.ts +95 -4
- package/src/utils.ts +31 -19
- package/.gitmodules +0 -6
- package/BUNDLEHASH_MIGRATION.md +0 -355
- package/NATIVE_CHECKUPDATE_DESIGN.md +0 -539
- package/NATIVE_CHECK_FOLLOWUPS.md +0 -514
- package/android/jni/HDiffPatch/test/_private_searchBestParams.cpp +0 -301
- package/android/jni/HDiffPatch/test/hpatchz_test.cpp +0 -292
- package/android/jni/HDiffPatch/test/testHashClash.cpp +0 -436
- package/android/jni/HDiffPatch/test/unit_test.cpp +0 -417
- package/biome.json +0 -59
- package/cpp/patch_core/tests/fixtures/rawstored.lzma2label.patch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/rawstored.new.bin +0 -0
- package/cpp/patch_core/tests/fixtures/rawstored.old.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v96.meta.json +0 -1
- package/cpp/patch_core/tests/fixtures/v96.streampatch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96.t.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v96.tpatch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96.tstreampatch.bin +0 -0
- package/cpp/patch_core/tests/fixtures/v96b.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98.t.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98b.hbc +0 -0
- package/cpp/patch_core/tests/fixtures/v98b.t.hbc +0 -0
- package/cpp/patch_core/tests/hbc_transform_test.cpp +0 -384
- package/cpp/patch_core/tests/patch_core_test.cpp +0 -970
- package/cpp/update_flow_core/tests/flow_vectors.json +0 -1401
- package/cpp/update_flow_core/tests/update_flow_core_test.cpp +0 -317
- package/scripts/check-expo-version.js +0 -92
package/src/provider.tsx
CHANGED
|
@@ -343,13 +343,36 @@ export const UpdateProvider = ({
|
|
|
343
343
|
if (!assertWeb()) {
|
|
344
344
|
return;
|
|
345
345
|
}
|
|
346
|
-
const { checkStrategy, autoMarkSuccess } = options;
|
|
346
|
+
const { checkStrategy, autoMarkSuccess, autoMarkSuccessDelayMs } = options;
|
|
347
347
|
let markSuccessTimer: ReturnType<typeof setTimeout> | undefined;
|
|
348
348
|
if (autoMarkSuccess) {
|
|
349
|
+
const delay =
|
|
350
|
+
typeof autoMarkSuccessDelayMs === 'number' &&
|
|
351
|
+
autoMarkSuccessDelayMs >= 0
|
|
352
|
+
? autoMarkSuccessDelayMs
|
|
353
|
+
: 1000;
|
|
349
354
|
markSuccessTimer = setTimeout(() => {
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
|
|
355
|
+
// The health check is read when the timer fires, not when it was
|
|
356
|
+
// armed, so a late setOptions still applies.
|
|
357
|
+
const { healthCheck } = options;
|
|
358
|
+
(async () => {
|
|
359
|
+
if (healthCheck) {
|
|
360
|
+
let healthy = false;
|
|
361
|
+
try {
|
|
362
|
+
healthy = (await healthCheck()) !== false;
|
|
363
|
+
} catch (e: any) {
|
|
364
|
+
log('healthCheck threw, not marking success:', e?.message || e);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (!healthy) {
|
|
368
|
+
log('healthCheck returned false, not marking success');
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// Failures are reported and surfaced via the onError subscription.
|
|
373
|
+
await markSuccess();
|
|
374
|
+
})().catch(noop);
|
|
375
|
+
}, delay);
|
|
353
376
|
}
|
|
354
377
|
if (checkStrategy === 'both' || checkStrategy === 'onAppResume') {
|
|
355
378
|
stateListener.current = AppState.addEventListener(
|
|
@@ -396,30 +419,42 @@ export const UpdateProvider = ({
|
|
|
396
419
|
const parseTestPayload = useCallback(
|
|
397
420
|
(payload: UpdateTestPayload) => {
|
|
398
421
|
if (payload?.type?.startsWith('__rnPushy')) {
|
|
422
|
+
if (options.testChannel === false) {
|
|
423
|
+
// Production builds may refuse scanned codes that would pull an
|
|
424
|
+
// arbitrary published version.
|
|
425
|
+
log('test channel disabled, ignoring payload', payload.type);
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
399
428
|
if (payload.type === '__rnPushyVersionHash') {
|
|
400
|
-
// Wrap the logger only for the duration of this test check; wrapping
|
|
401
|
-
// on any other __rnPushy* payload would never be undone (the finally
|
|
402
|
-
// below is the only restore point).
|
|
403
|
-
const logger = options.logger || (() => {});
|
|
404
|
-
options.logger = ({ type, data }) => {
|
|
405
|
-
logger({ type, data });
|
|
406
|
-
Alert.alert(type, JSON.stringify(data));
|
|
407
|
-
};
|
|
408
429
|
const toHash = payload.data;
|
|
430
|
+
if (typeof toHash !== 'string' || !toHash) {
|
|
431
|
+
log('test payload without a target hash ignored');
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
409
434
|
sharedState.toHash = toHash;
|
|
435
|
+
// No global logger swap for the duration of the check (it leaked
|
|
436
|
+
// into concurrent checks): the tester gets one alert with the
|
|
437
|
+
// outcome instead of one per logged event.
|
|
410
438
|
checkUpdate({ extra: { toHash } })
|
|
411
439
|
.then(() => {
|
|
412
|
-
|
|
440
|
+
const info = updateInfoRef.current;
|
|
441
|
+
if (info?.upToDate) {
|
|
413
442
|
Alert.alert(
|
|
414
443
|
client.t('alert_info'),
|
|
415
444
|
client.t('alert_no_update_wait')
|
|
416
445
|
);
|
|
446
|
+
} else if (info?.update) {
|
|
447
|
+
Alert.alert(
|
|
448
|
+
client.t('alert_info'),
|
|
449
|
+
JSON.stringify({
|
|
450
|
+
hash: info.hash,
|
|
451
|
+
name: info.name,
|
|
452
|
+
description: info.description,
|
|
453
|
+
})
|
|
454
|
+
);
|
|
417
455
|
}
|
|
418
456
|
})
|
|
419
|
-
.catch(noop)
|
|
420
|
-
.finally(() => {
|
|
421
|
-
options.logger = logger;
|
|
422
|
-
});
|
|
457
|
+
.catch(noop);
|
|
423
458
|
}
|
|
424
459
|
return true;
|
|
425
460
|
}
|
package/src/type.ts
CHANGED
|
@@ -162,6 +162,28 @@ export interface ClientOptions {
|
|
|
162
162
|
| null;
|
|
163
163
|
checkStrategy?: 'onAppStart' | 'onAppResume' | 'both' | null;
|
|
164
164
|
autoMarkSuccess?: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Delay before the Provider marks the running update as successful when
|
|
167
|
+
* `autoMarkSuccess` is on. Default 1000ms. Apps whose critical modules load
|
|
168
|
+
* after the first frame (navigation, remote config) should raise it — a
|
|
169
|
+
* version that crashes 3s after mount would otherwise already be marked
|
|
170
|
+
* good and never rolled back. For full control use `autoMarkSuccess: false`
|
|
171
|
+
* and call `markSuccess()` once the app is really ready.
|
|
172
|
+
*/
|
|
173
|
+
autoMarkSuccessDelayMs?: number;
|
|
174
|
+
/**
|
|
175
|
+
* Consulted when the auto-mark timer fires: return false (or throw) to
|
|
176
|
+
* skip marking this launch — the next launch's crash protection then still
|
|
177
|
+
* applies. Call `markSuccess()` yourself later once the app is healthy.
|
|
178
|
+
*/
|
|
179
|
+
healthCheck?: () => boolean | Promise<boolean>;
|
|
180
|
+
/**
|
|
181
|
+
* Whether the Provider honours test-channel payloads (`parseTestQrCode`,
|
|
182
|
+
* `__rnPushyVersionHash` deep links / QR codes) that target a specific
|
|
183
|
+
* version hash. Default true. Set to false in production builds that must
|
|
184
|
+
* not let a scanned code pull an arbitrary published version.
|
|
185
|
+
*/
|
|
186
|
+
testChannel?: boolean;
|
|
165
187
|
dismissErrorAfter?: number;
|
|
166
188
|
debug?: boolean;
|
|
167
189
|
throwError?: boolean;
|
package/src/updateFlowCore.ts
CHANGED
|
@@ -172,22 +172,113 @@ export function buildCheckRequestBody({
|
|
|
172
172
|
isDev,
|
|
173
173
|
extra,
|
|
174
174
|
}: CheckRequestInput): Record<string, any> {
|
|
175
|
+
// Caller extras are spread FIRST so they can add fields (channel, custom
|
|
176
|
+
// filters, the debug-channel toHash) but never override the SDK identity
|
|
177
|
+
// fields the server keys its decision on: a stray `packageVersion` or
|
|
178
|
+
// `hash` in extra would otherwise make the server hand out a package for
|
|
179
|
+
// the wrong binary.
|
|
175
180
|
const body: Record<string, any> = {
|
|
181
|
+
...extra,
|
|
176
182
|
packageVersion,
|
|
177
183
|
hash: currentVersion,
|
|
178
184
|
buildTime,
|
|
179
185
|
cInfo,
|
|
180
|
-
// 可消费的 diff 轨道版本(2 = hdiffv2 轨道),服务端据此门控下发
|
|
181
|
-
...(supportedDiffVersion ? { diffV: supportedDiffVersion } : {}),
|
|
182
|
-
...(bundleHash ? { bundleHash } : {}),
|
|
183
|
-
...extra,
|
|
184
186
|
};
|
|
187
|
+
// 可消费的 diff 轨道版本(2 = hdiffv2 轨道),服务端据此门控下发
|
|
188
|
+
if (supportedDiffVersion) {
|
|
189
|
+
body.diffV = supportedDiffVersion;
|
|
190
|
+
} else {
|
|
191
|
+
delete body.diffV;
|
|
192
|
+
}
|
|
193
|
+
if (bundleHash) {
|
|
194
|
+
body.bundleHash = bundleHash;
|
|
195
|
+
} else {
|
|
196
|
+
delete body.bundleHash;
|
|
197
|
+
}
|
|
185
198
|
if (isDev) {
|
|
186
199
|
delete body.buildTime;
|
|
187
200
|
}
|
|
201
|
+
// Transitional dual form: the legacy root-level spread above is what
|
|
202
|
+
// servers before 2026-08-30 read; the nested copy is what new servers read
|
|
203
|
+
// first (extra.toHash wins there) and lets caller keys stop sharing the
|
|
204
|
+
// SDK's namespace once the root spread is retired.
|
|
205
|
+
if (extra) {
|
|
206
|
+
// Match JSON.stringify semantics: undefined values do not exist on the
|
|
207
|
+
// wire, so they must not create a nested copy either (the native check
|
|
208
|
+
// cache compares serialized requests).
|
|
209
|
+
const defined: Record<string, any> = {};
|
|
210
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
211
|
+
if (value !== undefined) {
|
|
212
|
+
defined[key] = value;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (Object.keys(defined).length > 0) {
|
|
216
|
+
body.extra = defined;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
188
219
|
return body;
|
|
189
220
|
}
|
|
190
221
|
|
|
222
|
+
export interface CheckFingerprintInput {
|
|
223
|
+
appKey?: string;
|
|
224
|
+
endpoints?: string[];
|
|
225
|
+
queryUrls?: string[];
|
|
226
|
+
uuid?: string;
|
|
227
|
+
/** The exact serialized request body (identity fields + extra). */
|
|
228
|
+
body: string;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Identity of one update check. Two checks may share an in-flight response
|
|
233
|
+
* only when every input that could change the server's answer is identical.
|
|
234
|
+
*/
|
|
235
|
+
export function buildCheckFingerprint({
|
|
236
|
+
appKey,
|
|
237
|
+
endpoints,
|
|
238
|
+
queryUrls,
|
|
239
|
+
uuid,
|
|
240
|
+
body,
|
|
241
|
+
}: CheckFingerprintInput): string {
|
|
242
|
+
return JSON.stringify([
|
|
243
|
+
appKey ?? '',
|
|
244
|
+
endpoints ?? [],
|
|
245
|
+
queryUrls ?? [],
|
|
246
|
+
uuid ?? '',
|
|
247
|
+
body,
|
|
248
|
+
]);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Whether a failed download attempt is worth retrying on another mirror of
|
|
253
|
+
* the same artifact. Transport failures (DNS, connect, timeout, truncated
|
|
254
|
+
* body, 5xx) and corrupt bytes are; a patch that downloaded fine but could
|
|
255
|
+
* not be applied is not — every mirror serves the same bytes, so the next
|
|
256
|
+
* strategy is the only way forward.
|
|
257
|
+
*/
|
|
258
|
+
export function isMirrorRetryableCode(code?: string): boolean {
|
|
259
|
+
return code !== 'PATCH_FAILED';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Schema gate for a checkUpdate response (mirrors C++ IsValidCheckResult): a
|
|
264
|
+
* JSON object carrying at least one verdict field of the expected type —
|
|
265
|
+
* `upToDate` / `update` / `expired` (boolean) or `paused` (string). A 200
|
|
266
|
+
* with `{"error": ...}`, an array or an HTML page that happened to parse is
|
|
267
|
+
* a failed endpoint, not a verdict, and must not stop the endpoint fallback.
|
|
268
|
+
*/
|
|
269
|
+
export function isValidCheckResult(value: unknown): value is CheckResult {
|
|
270
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
const root = value as Record<string, unknown>;
|
|
274
|
+
return (
|
|
275
|
+
typeof root.upToDate === 'boolean' ||
|
|
276
|
+
typeof root.update === 'boolean' ||
|
|
277
|
+
typeof root.expired === 'boolean' ||
|
|
278
|
+
typeof root.paused === 'string'
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
191
282
|
export interface UpdateIdentity {
|
|
192
283
|
packageVersion: string;
|
|
193
284
|
currentVersion?: string;
|
package/src/utils.ts
CHANGED
|
@@ -66,6 +66,13 @@ const ping = isWeb
|
|
|
66
66
|
DEFAULT_FETCH_TIMEOUT_MS
|
|
67
67
|
);
|
|
68
68
|
if (status === 200) {
|
|
69
|
+
if (isProtocolDowngrade(url, finalUrl)) {
|
|
70
|
+
// The probe was redirected from https to plaintext http. Never
|
|
71
|
+
// let a downgraded final URL win the race: the winner is what
|
|
72
|
+
// the real artifact download uses.
|
|
73
|
+
log('ping rejected: https redirected to http', url, finalUrl);
|
|
74
|
+
throw Error(i18n.t('error_ping_failed'));
|
|
75
|
+
}
|
|
69
76
|
return finalUrl;
|
|
70
77
|
}
|
|
71
78
|
log('ping failed', url, status, statusText);
|
|
@@ -166,29 +173,34 @@ export const fetchWithTimeout = (
|
|
|
166
173
|
});
|
|
167
174
|
};
|
|
168
175
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
/**
|
|
177
|
+
* True when `finalUrl` (after redirects) is plaintext http while the request
|
|
178
|
+
* started on https. An https endpoint or artifact URL must never be served
|
|
179
|
+
* over http, redirected or otherwise: the update package is the supply chain
|
|
180
|
+
* boundary, and TLS is the only thing authenticating it.
|
|
181
|
+
*/
|
|
182
|
+
export const isProtocolDowngrade = (
|
|
183
|
+
requestedUrl: string,
|
|
184
|
+
finalUrl?: string | null
|
|
185
|
+
): boolean =>
|
|
186
|
+
!!finalUrl && /^https:/i.test(requestedUrl) && /^http:/i.test(finalUrl);
|
|
173
187
|
|
|
174
188
|
export const enhancedFetch = async (
|
|
175
189
|
url: string,
|
|
176
|
-
params: Parameters<typeof fetch>[1]
|
|
177
|
-
isRetry = false
|
|
190
|
+
params: Parameters<typeof fetch>[1]
|
|
178
191
|
): Promise<Response> => {
|
|
179
|
-
|
|
192
|
+
// No https -> http retry here, ever. Earlier versions replayed failed
|
|
193
|
+
// idempotent https requests over plaintext http as a "network
|
|
194
|
+
// compatibility" fallback; that silently moved endpoint discovery and
|
|
195
|
+
// package downloads outside TLS. Self-hosted plaintext deployments must
|
|
196
|
+
// configure explicit http:// URLs instead.
|
|
197
|
+
const response = await fetch(url, params).catch((e) => {
|
|
180
198
|
log('fetch error', url, e);
|
|
181
|
-
|
|
182
|
-
isRetry ||
|
|
183
|
-
(params as any)?.signal?.aborted ||
|
|
184
|
-
!url.startsWith('https:') ||
|
|
185
|
-
// Never replay non-idempotent requests (e.g. the checkUpdate POST)
|
|
186
|
-
// over plaintext http: the server may have processed the original.
|
|
187
|
-
!isIdempotentRequest(params)
|
|
188
|
-
) {
|
|
189
|
-
throw e;
|
|
190
|
-
}
|
|
191
|
-
log('trying fallback to http');
|
|
192
|
-
return enhancedFetch(url.replace(/^https:/, 'http:'), params, true);
|
|
199
|
+
throw e;
|
|
193
200
|
});
|
|
201
|
+
if (isProtocolDowngrade(url, response?.url)) {
|
|
202
|
+
log('fetch rejected: https redirected to http', url, response.url);
|
|
203
|
+
throw Error(i18n.t('error_insecure_redirect'));
|
|
204
|
+
}
|
|
205
|
+
return response;
|
|
194
206
|
};
|