You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Improved fetch with retry on all three platforms
This commit is contained in:
		| @@ -101,7 +101,9 @@ class OneDriveLoginScreenComponent extends React.Component { | ||||
| } | ||||
|  | ||||
| const mapStateToProps = (state) => { | ||||
| 	return {}; | ||||
| 	return { | ||||
| 		theme: state.settings.theme, | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| const OneDriveLoginScreen = connect(mapStateToProps)(OneDriveLoginScreenComponent); | ||||
|   | ||||
							
								
								
									
										69
									
								
								ElectronClient/app/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										69
									
								
								ElectronClient/app/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "joplin-desktop", | ||||
|   "version": "0.0.1", | ||||
|   "version": "0.10.0", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
| @@ -104,7 +104,7 @@ | ||||
|       "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "color-convert": "1.9.0" | ||||
|         "color-convert": "1.9.1" | ||||
|       } | ||||
|     }, | ||||
|     "anymatch": { | ||||
| @@ -124,17 +124,16 @@ | ||||
|       "integrity": "sha1-ZBqlXft9am8KgUHEucCqULbCTdU=" | ||||
|     }, | ||||
|     "app-package-builder": { | ||||
|       "version": "1.3.3", | ||||
|       "resolved": "https://registry.npmjs.org/app-package-builder/-/app-package-builder-1.3.3.tgz", | ||||
|       "integrity": "sha512-aBcCNFQ4GDQ0AOYvTmd/g2L2zoV+RnK6TQnNm3IoakdonQ4iqiyOZFGc6i53SYEnB5JRcZEdpO7RjXeO8Nr9sg==", | ||||
|       "version": "1.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/app-package-builder/-/app-package-builder-1.5.0.tgz", | ||||
|       "integrity": "sha512-CmmmXq543Ru7TEcAQShmmI9Z0NgKZ54KOR3BGM+LTEw/v2VjgCQ5qrkkoCA+0LaYgJfPFg/40PGMHnDaAZ6g4w==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "bluebird-lst": "1.0.5", | ||||
|         "builder-util": "3.2.0", | ||||
|         "builder-util-runtime": "2.5.0", | ||||
|         "builder-util": "3.2.2", | ||||
|         "builder-util-runtime": "3.2.0", | ||||
|         "fs-extra-p": "4.4.4", | ||||
|         "int64-buffer": "0.1.9", | ||||
|         "js-yaml": "3.10.0", | ||||
|         "rabin-bindings": "1.7.3" | ||||
|       } | ||||
|     }, | ||||
| @@ -811,14 +810,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "builder-util": { | ||||
|       "version": "3.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-3.2.0.tgz", | ||||
|       "integrity": "sha512-TWvixCYS4fyZgrn4tSeKfsO1j13zX8MGWRgLdsWP0op4SD0Lp4NJJVJjvwBzHIfOCcxpQsszgbcSMBE2lrMVjA==", | ||||
|       "version": "3.2.2", | ||||
|       "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-3.2.2.tgz", | ||||
|       "integrity": "sha512-4t/EtpYYp5wNaNlI4cgKbHEY/oNtnKtuEn201DNcJlgLkoV8RrwSOo6RJbDOKY+RTf0IVmbEZ2ZHbyy2/KGKRQ==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "7zip-bin": "2.2.7", | ||||
|         "bluebird-lst": "1.0.5", | ||||
|         "builder-util-runtime": "2.5.0", | ||||
|         "builder-util-runtime": "3.2.0", | ||||
|         "chalk": "2.3.0", | ||||
|         "debug": "3.1.0", | ||||
|         "fs-extra-p": "4.4.4", | ||||
| @@ -835,9 +834,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "builder-util-runtime": { | ||||
|       "version": "2.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-2.5.0.tgz", | ||||
|       "integrity": "sha512-1D/jB5FDc8RP8UxPvUo7UwcChNiDviJks57LUkWeGY8TLCoQLljDUamOBAUYXeZ4HnAqwuTsVgWgx5jxmayl5Q==", | ||||
|       "version": "3.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-3.2.0.tgz", | ||||
|       "integrity": "sha512-VRvyyLiZZSBjcUTqEsHlBJSK0s6uVQChO7kbmVeU6QmSJ7TtsotNQELO6lbahwZMAQ4Z/haCKhlLBDdhW+3aqA==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "bluebird-lst": "1.0.5", | ||||
| @@ -1005,9 +1004,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "color-convert": { | ||||
|       "version": "1.9.0", | ||||
|       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", | ||||
|       "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", | ||||
|       "version": "1.9.1", | ||||
|       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", | ||||
|       "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "color-name": "1.1.3" | ||||
| @@ -1251,13 +1250,13 @@ | ||||
|       "integrity": "sha1-R/31ZzSKF+wl/L8LnkRjSKdvn7U=" | ||||
|     }, | ||||
|     "dmg-builder": { | ||||
|       "version": "2.1.5", | ||||
|       "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-2.1.5.tgz", | ||||
|       "integrity": "sha512-KbldWIhaP3XSI7sYPGrBcNwSPxvB2ImIMSqz1tKcIqVZ9P+9UvYtdIJvM6Og6DSyAQlwX37E0soKwD/eddGFUA==", | ||||
|       "version": "2.1.6", | ||||
|       "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-2.1.6.tgz", | ||||
|       "integrity": "sha512-5gYVdJJPnATbnMAv/ufMd2qqbb8wWLyl77am1EKUL+5ZZjCkujps1inHcq8+s+G/iJJqGn1ko4I7RBvhd2AV/w==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "bluebird-lst": "1.0.5", | ||||
|         "builder-util": "3.2.0", | ||||
|         "builder-util": "3.2.2", | ||||
|         "debug": "3.1.0", | ||||
|         "fs-extra-p": "4.4.4", | ||||
|         "iconv-lite": "0.4.19", | ||||
| @@ -1389,26 +1388,26 @@ | ||||
|       } | ||||
|     }, | ||||
|     "electron-builder": { | ||||
|       "version": "19.43.4", | ||||
|       "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-19.43.4.tgz", | ||||
|       "integrity": "sha512-rGpJwOAHgIEZ6Ck0ogxViQ0/NtAS7hzlh72xY31QazCjgSwKLAkWHGHP+T4U12EzVqFOmI3pD1i6/eBeAEUMuw==", | ||||
|       "version": "19.45.4", | ||||
|       "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-19.45.4.tgz", | ||||
|       "integrity": "sha512-WuhPBFFU/rdx9ehwS8tOnZfvzTuip7UzZCRZVQy0Bimypv9ZQ65WuCAx8MSy4gLDQ1et63Vrl+fUvtXGQIZI8g==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "7zip-bin": "2.2.7", | ||||
|         "app-package-builder": "1.3.3", | ||||
|         "app-package-builder": "1.5.0", | ||||
|         "asar-integrity": "0.2.3", | ||||
|         "async-exit-hook": "2.0.1", | ||||
|         "bluebird-lst": "1.0.5", | ||||
|         "builder-util": "3.2.0", | ||||
|         "builder-util-runtime": "2.5.0", | ||||
|         "builder-util": "3.2.2", | ||||
|         "builder-util-runtime": "3.2.0", | ||||
|         "chalk": "2.3.0", | ||||
|         "chromium-pickle-js": "0.2.0", | ||||
|         "debug": "3.1.0", | ||||
|         "dmg-builder": "2.1.5", | ||||
|         "dmg-builder": "2.1.6", | ||||
|         "ejs": "2.5.7", | ||||
|         "electron-download-tf": "4.3.4", | ||||
|         "electron-osx-sign": "0.4.7", | ||||
|         "electron-publish": "19.43.0", | ||||
|         "electron-publish": "19.45.0", | ||||
|         "fs-extra-p": "4.4.4", | ||||
|         "hosted-git-info": "2.5.0", | ||||
|         "is-ci": "1.0.10", | ||||
| @@ -1493,14 +1492,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "electron-publish": { | ||||
|       "version": "19.43.0", | ||||
|       "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-19.43.0.tgz", | ||||
|       "integrity": "sha512-gsFmUsdzfB2EXtMHJD+jRJgvREgUUJasGbZsoaRiJQr5kJZruUQOkIq+NMRsN9o3eQvu0fhxdotDhH4W+U1ebA==", | ||||
|       "version": "19.45.0", | ||||
|       "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-19.45.0.tgz", | ||||
|       "integrity": "sha512-hubOvL3bvgDoITy0hgh+FSbC2Nx1OYDeE4ipzSnEEa6xzjS9svROxUiyyPncbL9O1E+VreJTDyNWaYsYaUg8jw==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "bluebird-lst": "1.0.5", | ||||
|         "builder-util": "3.2.0", | ||||
|         "builder-util-runtime": "2.5.0", | ||||
|         "builder-util": "3.2.2", | ||||
|         "builder-util-runtime": "3.2.0", | ||||
|         "chalk": "2.3.0", | ||||
|         "fs-extra-p": "4.4.4", | ||||
|         "mime": "2.0.3" | ||||
|   | ||||
| @@ -5,7 +5,9 @@ | ||||
|   "main": "main.js", | ||||
|   "scripts": { | ||||
|     "test": "echo \"Error: no test specified\" && exit 1", | ||||
|     "postinstall": "electron-builder install-app-deps" | ||||
|     "postinstall": "electron-builder install-app-deps", | ||||
|     "pack": "electron-builder --dir", | ||||
|     "dist": "electron-builder" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
| @@ -16,12 +18,15 @@ | ||||
|   "bugs": { | ||||
|     "url": "https://github.com/laurent22/joplin/issues" | ||||
|   }, | ||||
|  "build": { | ||||
|    "appId": "net.cozic.joplin-desktop" | ||||
|  }, | ||||
|   "homepage": "https://github.com/laurent22/joplin#readme", | ||||
|   "devDependencies": { | ||||
|     "babel-cli": "^6.26.0", | ||||
|     "babel-preset-react": "^6.24.1", | ||||
|     "electron": "^1.7.9", | ||||
|     "electron-builder": "^19.43.4", | ||||
|     "electron-builder": "^19.45.4", | ||||
|     "electron-rebuild": "^1.6.0" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|   | ||||
							
								
								
									
										6
									
								
								ElectronClient/run-prod.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ElectronClient/run-prod.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| #!/bin/bash | ||||
| ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" | ||||
| cd "$ROOT_DIR" | ||||
| ./build.sh || exit 1 | ||||
| cd "$ROOT_DIR/app" | ||||
| ./node_modules/.bin/electron . --log-level debug "$@" | ||||
| @@ -1,7 +1,5 @@ | ||||
| const { shim } = require('lib/shim.js'); | ||||
|  | ||||
| const tcpPortUsed = require('tcp-port-used'); | ||||
|  | ||||
| const netUtils = {}; | ||||
|  | ||||
| netUtils.ip = async () => { | ||||
| @@ -15,6 +13,8 @@ netUtils.ip = async () => { | ||||
| } | ||||
|  | ||||
| netUtils.findAvailablePort = async (possiblePorts, extraRandomPortsToTry = 20) => { | ||||
| 	const tcpPortUsed = require('tcp-port-used'); | ||||
|  | ||||
| 	for (let i = 0; i < extraRandomPortsToTry; i++) { | ||||
| 		possiblePorts.push(Math.floor(8000 + Math.random() * 2000)); | ||||
| 	} | ||||
|   | ||||
| @@ -177,32 +177,8 @@ class OneDriveApi { | ||||
| 					response = await shim.fetchBlob(url, options); | ||||
| 				} | ||||
| 			} catch (error) { | ||||
| 				// let canRetry = true; | ||||
|  | ||||
| 				// if (error.message == 'Network request failed') { | ||||
| 				// 	// Unfortunately the error 'Network request failed' doesn't have a type | ||||
| 				// 	// or error code, so hopefully that message won't change and is not localized | ||||
| 				// } else if (error.code == 'ECONNRESET') { | ||||
| 				// 	// request to https://public-ch3302....1fab24cb1bd5f.md failed, reason: socket hang up" | ||||
| 				// } else if (error.code == 'ENOTFOUND') { | ||||
| 				// 	// OneDrive (or Node?) sometimes sends back a "not found" error for resources | ||||
| 				// 	// that definitely exist and in this case repeating the request works. | ||||
| 				// 	// Error is: | ||||
| 				// 	// request to https://graph.microsoft.com/v1.0/drive/special/approot failed, reason: getaddrinfo ENOTFOUND graph.microsoft.com graph.microsoft.com:443 | ||||
| 				// } else if (error.message.indexOf('network timeout') === 0) { | ||||
| 				// 	// network timeout at: https://public-ch3302...859f9b0e3ab.md | ||||
| 				// } else { | ||||
| 				// 	canRetry = false; | ||||
| 				// } | ||||
|  | ||||
| 				// if (canRetry) { | ||||
| 				// 	this.logger().info('Got error code ' + error.code + ': ' + error.message + ' - retrying (' + i + ')...'); | ||||
| 				// 	await time.sleep((i + 1) * 3); | ||||
| 				// 	continue; | ||||
| 				// } else { | ||||
| 				this.logger().error('Got unhandled error:', error ? error.code : '', error ? error.message : '', error); | ||||
| 				throw error; | ||||
| 				//} | ||||
| 			} | ||||
|  | ||||
| 			if (!response.ok) { | ||||
|   | ||||
| @@ -5,36 +5,43 @@ const { FileApiDriverLocal } = require('lib/file-api-driver-local.js'); | ||||
| const { time } = require('lib/time-utils.js'); | ||||
| const { setLocale, defaultLocale, closestSupportedLocale } = require('lib/locale.js'); | ||||
|  | ||||
| function fetchRequestCanBeRetried(error) { | ||||
| 	if (!error) return false; | ||||
| // // Node requests can go wrong is so many different ways and with so | ||||
| // // many different error messages... This handler inspects the error | ||||
| // // and decides whether the request can safely be repeated or not. | ||||
| // function fetchRequestCanBeRetried(error) { | ||||
| // 	if (!error) return false; | ||||
|  | ||||
| 	// Unfortunately the error 'Network request failed' doesn't have a type | ||||
| 	// or error code, so hopefully that message won't change and is not localized | ||||
| 	if (error.message == 'Network request failed') return true; | ||||
| // 	// Unfortunately the error 'Network request failed' doesn't have a type | ||||
| // 	// or error code, so hopefully that message won't change and is not localized | ||||
| // 	if (error.message == 'Network request failed') return true; | ||||
|  | ||||
| 	// request to https://public-ch3302....1fab24cb1bd5f.md failed, reason: socket hang up" | ||||
| 	if (error.code == 'ECONNRESET') return true; | ||||
| // 	// request to https://public-ch3302....1fab24cb1bd5f.md failed, reason: socket hang up" | ||||
| // 	if (error.code == 'ECONNRESET') return true; | ||||
|  | ||||
| 	// OneDrive (or Node?) sometimes sends back a "not found" error for resources | ||||
| 	// that definitely exist and in this case repeating the request works. | ||||
| 	// Error is: | ||||
| 	// request to https://graph.microsoft.com/v1.0/drive/special/approot failed, reason: getaddrinfo ENOTFOUND graph.microsoft.com graph.microsoft.com:443		 | ||||
| 	if (error.code == 'ENOTFOUND') return true; | ||||
| // 	// OneDrive (or Node?) sometimes sends back a "not found" error for resources | ||||
| // 	// that definitely exist and in this case repeating the request works. | ||||
| // 	// Error is: | ||||
| // 	// request to https://graph.microsoft.com/v1.0/drive/special/approot failed, reason: getaddrinfo ENOTFOUND graph.microsoft.com graph.microsoft.com:443		 | ||||
| // 	if (error.code == 'ENOTFOUND') return true; | ||||
|  | ||||
| 	// network timeout at: https://public-ch3302...859f9b0e3ab.md | ||||
| 	if (error.message && error.message.indexOf('network timeout') === 0) return true; | ||||
| // 	// network timeout at: https://public-ch3302...859f9b0e3ab.md | ||||
| // 	if (error.message && error.message.indexOf('network timeout') === 0) return true; | ||||
|  | ||||
| 	// name: 'FetchError', | ||||
| 	// message: 'request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443', | ||||
| 	// type: 'system', | ||||
| 	// errno: 'EAI_AGAIN', | ||||
| 	// code: 'EAI_AGAIN' } } reason: { FetchError: request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443 | ||||
| 	// | ||||
| 	// It's a Microsoft error: "A temporary failure in name resolution occurred." | ||||
| 	if (error.code == 'EAI_AGAIN') return true; | ||||
| // 	// name: 'FetchError', | ||||
| // 	// message: 'request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443', | ||||
| // 	// type: 'system', | ||||
| // 	// errno: 'EAI_AGAIN', | ||||
| // 	// code: 'EAI_AGAIN' } } reason: { FetchError: request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443 | ||||
| // 	// | ||||
| // 	// It's a Microsoft error: "A temporary failure in name resolution occurred." | ||||
| // 	if (error.code == 'EAI_AGAIN') return true; | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
| // 	// request to https://public-...8fd8bc6bb68e9c4d17a.md failed, reason: connect ETIMEDOUT 204.79.197.213:443 | ||||
| // 	// Code: ETIMEDOUT | ||||
| // 	if (error.code === 'ETIMEDOUT') return true; | ||||
|  | ||||
| // 	return false; | ||||
| // } | ||||
|  | ||||
| function shimInit() { | ||||
| 	shim.fs = fs; | ||||
| @@ -110,31 +117,35 @@ function shimInit() { | ||||
| 	} | ||||
|  | ||||
| 	shim.fetch = async function(url, options = null) { | ||||
| 		if (!options) options = {}; | ||||
| 		if (!options.timeout) options.timeout = 1000 * 120; // ms | ||||
| 		if (!('maxRetry' in options)) options.maxRetry = 5; | ||||
| 		return shim.fetchWithRetry(() => { | ||||
| 			return nodeFetch(url, options) | ||||
| 		}, options); | ||||
|  | ||||
| 		let retryCount = 0; | ||||
| 		while (true) { | ||||
| 			try { | ||||
| 				const response = await nodeFetch(url, options); | ||||
| 				return response; | ||||
| 			} catch (error) { | ||||
| 				if (fetchRequestCanBeRetried(error)) { | ||||
| 					retryCount++; | ||||
| 					if (retryCount > options.maxRetry) throw error; | ||||
| 					await time.sleep(retryCount * 3); | ||||
| 				} else { | ||||
| 					throw error; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// if (!options) options = {}; | ||||
| 		// if (!options.timeout) options.timeout = 1000 * 120; // ms | ||||
| 		// if (!('maxRetry' in options)) options.maxRetry = 5; | ||||
|  | ||||
| 		// let retryCount = 0; | ||||
| 		// while (true) { | ||||
| 		// 	try { | ||||
| 		// 		const response = await nodeFetch(url, options); | ||||
| 		// 		return response; | ||||
| 		// 	} catch (error) { | ||||
| 		// 		if (fetchRequestCanBeRetried(error)) { | ||||
| 		// 			retryCount++; | ||||
| 		// 			if (retryCount > options.maxRetry) throw error; | ||||
| 		// 			await time.sleep(retryCount * 3); | ||||
| 		// 		} else { | ||||
| 		// 			throw error; | ||||
| 		// 		} | ||||
| 		// 	} | ||||
| 		// } | ||||
| 	} | ||||
| 	 | ||||
| 	shim.fetchBlob = async function(url, options) { | ||||
| 		if (!options || !options.path) throw new Error('fetchBlob: target file path is missing'); | ||||
| 		if (!options.method) options.method = 'GET'; | ||||
| 		if (!('maxRetry' in options)) options.maxRetry = 5; | ||||
| 		//if (!('maxRetry' in options)) options.maxRetry = 5; | ||||
|  | ||||
| 		const urlParse = require('url').parse; | ||||
|  | ||||
| @@ -192,21 +203,23 @@ function shimInit() { | ||||
| 			}); | ||||
| 		}; | ||||
|  | ||||
| 		let retryCount = 0; | ||||
| 		while (true) { | ||||
| 			try { | ||||
| 				const response = await doFetchOperation(); | ||||
| 				return response; | ||||
| 			} catch (error) { | ||||
| 				if (fetchRequestCanBeRetried(error)) { | ||||
| 					retryCount++; | ||||
| 					if (retryCount > options.maxRetry) throw error; | ||||
| 					await time.sleep(retryCount * 3); | ||||
| 				} else { | ||||
| 					throw error; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return shim.fetchWithRetry(doFetchOperation, options); | ||||
|  | ||||
| 		// let retryCount = 0; | ||||
| 		// while (true) { | ||||
| 		// 	try { | ||||
| 		// 		const response = await doFetchOperation(); | ||||
| 		// 		return response; | ||||
| 		// 	} catch (error) { | ||||
| 		// 		if (fetchRequestCanBeRetried(error)) { | ||||
| 		// 			retryCount++; | ||||
| 		// 			if (retryCount > options.maxRetry) throw error; | ||||
| 		// 			await time.sleep(retryCount * 3); | ||||
| 		// 		} else { | ||||
| 		// 			throw error; | ||||
| 		// 		} | ||||
| 		// 	} | ||||
| 		// } | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,32 @@ function shimInit() { | ||||
| 	shim.setInterval = PoorManIntervals.setInterval; | ||||
| 	shim.clearInterval = PoorManIntervals.clearInterval; | ||||
|  | ||||
| 	shim.fetch = async function(url, options = null) { | ||||
| 		return shim.fetchWithRetry(() => { | ||||
| 			return shim.nativeFetch_(url, options) | ||||
| 		}, options); | ||||
|  | ||||
| 		// if (!options) options = {}; | ||||
| 		// if (!options.timeout) options.timeout = 1000 * 120; // ms | ||||
| 		// if (!('maxRetry' in options)) options.maxRetry = 5; | ||||
|  | ||||
| 		// let retryCount = 0; | ||||
| 		// while (true) { | ||||
| 		// 	try { | ||||
| 		// 		const response = await nodeFetch(url, options); | ||||
| 		// 		return response; | ||||
| 		// 	} catch (error) { | ||||
| 		// 		if (fetchRequestCanBeRetried(error)) { | ||||
| 		// 			retryCount++; | ||||
| 		// 			if (retryCount > options.maxRetry) throw error; | ||||
| 		// 			await time.sleep(retryCount * 3); | ||||
| 		// 		} else { | ||||
| 		// 			throw error; | ||||
| 		// 		} | ||||
| 		// 	} | ||||
| 		// } | ||||
| 	} | ||||
|  | ||||
| 	shim.fetchBlob = async function(url, options) { | ||||
| 		if (!options || !options.path) throw new Error('fetchBlob: target file path is missing'); | ||||
|  | ||||
| @@ -21,10 +47,17 @@ function shimInit() { | ||||
|  | ||||
| 		delete options.path; | ||||
|  | ||||
| 		try { | ||||
| 			let response = await RNFetchBlob.config({ | ||||
| 		const doFetchBlob = () => { | ||||
| 			return RNFetchBlob.config({ | ||||
| 				path: localFilePath | ||||
| 			}).fetch(method, url, headers); | ||||
| 		} | ||||
|  | ||||
| 		try { | ||||
| 			const response = await shim.fetchWithRetry(doFetchBlob, options); | ||||
| 			// let response = await RNFetchBlob.config({ | ||||
| 			// 	path: localFilePath | ||||
| 			// }).fetch(method, url, headers); | ||||
|  | ||||
| 			// Returns an object that's roughtly compatible with a standard Response object | ||||
| 			let output = { | ||||
|   | ||||
| @@ -30,7 +30,70 @@ shim.isElectron = () => { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| shim.fetch = typeof fetch !== 'undefined' ? fetch : null; | ||||
| // Node requests can go wrong is so many different ways and with so | ||||
| // many different error messages... This handler inspects the error | ||||
| // and decides whether the request can safely be repeated or not. | ||||
| function fetchRequestCanBeRetried(error) { | ||||
|     if (!error) return false; | ||||
|  | ||||
|     // Unfortunately the error 'Network request failed' doesn't have a type | ||||
|     // or error code, so hopefully that message won't change and is not localized | ||||
|     if (error.message == 'Network request failed') return true; | ||||
|  | ||||
|     // request to https://public-ch3302....1fab24cb1bd5f.md failed, reason: socket hang up" | ||||
|     if (error.code == 'ECONNRESET') return true; | ||||
|  | ||||
|     // OneDrive (or Node?) sometimes sends back a "not found" error for resources | ||||
|     // that definitely exist and in this case repeating the request works. | ||||
|     // Error is: | ||||
|     // request to https://graph.microsoft.com/v1.0/drive/special/approot failed, reason: getaddrinfo ENOTFOUND graph.microsoft.com graph.microsoft.com:443       | ||||
|     if (error.code == 'ENOTFOUND') return true; | ||||
|  | ||||
|     // network timeout at: https://public-ch3302...859f9b0e3ab.md | ||||
|     if (error.message && error.message.indexOf('network timeout') === 0) return true; | ||||
|  | ||||
|     // name: 'FetchError', | ||||
|     // message: 'request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443', | ||||
|     // type: 'system', | ||||
|     // errno: 'EAI_AGAIN', | ||||
|     // code: 'EAI_AGAIN' } } reason: { FetchError: request to https://api.ipify.org/?format=json failed, reason: getaddrinfo EAI_AGAIN api.ipify.org:443 | ||||
|     // | ||||
|     // It's a Microsoft error: "A temporary failure in name resolution occurred." | ||||
|     if (error.code == 'EAI_AGAIN') return true; | ||||
|  | ||||
|     // request to https://public-...8fd8bc6bb68e9c4d17a.md failed, reason: connect ETIMEDOUT 204.79.197.213:443 | ||||
|     // Code: ETIMEDOUT | ||||
|     if (error.code === 'ETIMEDOUT') return true; | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| shim.fetchWithRetry = async function(fetchFn, options = null) { | ||||
|     const { time } = require('lib/time-utils.js'); | ||||
|  | ||||
|     if (!options) options = {}; | ||||
|     if (!options.timeout) options.timeout = 1000 * 120; // ms | ||||
|     if (!('maxRetry' in options)) options.maxRetry = 5; | ||||
|  | ||||
|     let retryCount = 0; | ||||
|     while (true) { | ||||
|         try { | ||||
|             const response = await fetchFn(); | ||||
|             return response; | ||||
|         } catch (error) { | ||||
|             if (fetchRequestCanBeRetried(error)) { | ||||
|                 retryCount++; | ||||
|                 if (retryCount > options.maxRetry) throw error; | ||||
|                 await time.sleep(retryCount * 3); | ||||
|             } else { | ||||
|                 throw error; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| shim.nativeFetch_ = typeof fetch !== 'undefined' ? fetch : null; | ||||
| shim.fetch = () => { throw new Error('Not implemented'); } | ||||
| shim.FormData = typeof FormData !== 'undefined' ? FormData : null; | ||||
| shim.fs = null; | ||||
| shim.FileApiDriverLocal = null; | ||||
|   | ||||
| @@ -35,6 +35,7 @@ | ||||
| 				"CliClient/tests/fuzzing", | ||||
| 				"CliClient/tests/src", | ||||
| 				"CliClient/tests/sync", | ||||
| 				"ElectronClient/dist", | ||||
| 				"ElectronClient/build", | ||||
| 				"ElectronClient/app/lib", | ||||
| 				"ElectronClient/app/locale", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user