From ece7ffadd6b5660872636276851913529b8c3a70 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Wed, 28 Oct 2020 15:47:36 +0000 Subject: [PATCH] Desktop: Fixes #3986: Handle gzipped CSS files when importing from clipper --- ReactNativeClient/lib/shim-init-node.js | 57 +++++++++++++++++++++++- ReactNativeClient/lib/shim-init-react.js | 22 ++++++--- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/ReactNativeClient/lib/shim-init-node.js b/ReactNativeClient/lib/shim-init-node.js index 235817a90a..7046d0be3c 100644 --- a/ReactNativeClient/lib/shim-init-node.js +++ b/ReactNativeClient/lib/shim-init-node.js @@ -13,6 +13,43 @@ const http = require('http'); const https = require('https'); const toRelative = require('relative'); const timers = require('timers'); +const zlib = require('zlib'); + +function fileExists(filePath) { + try { + return fs.statSync(filePath).isFile(); + } catch (err) { + return false; + } +} + +const gunzipFile = function(source, destination) { + if (!fileExists(source)) { + throw new Error(`No such file: ${source}`); + } + + return new Promise((resolve, reject) => { + // prepare streams + const src = fs.createReadStream(source); + const dest = fs.createWriteStream(destination); + + // extract the archive + src.pipe(zlib.createGunzip()).pipe(dest); + + // callback on extract completion + dest.on('close', function() { + resolve(); + }); + + src.on('error', () => { + reject(); + }); + + dest.on('error', () => { + reject(); + }); + }); +}; function shimInit() { shim.fsDriver = () => { @@ -365,9 +402,25 @@ function shimInit() { const request = http.request(requestOptions, function(response) { response.pipe(file); + const isGzipped = response.headers['content-encoding'] === 'gzip'; + file.on('finish', function() { - file.close(() => { - resolve(makeResponse(response)); + file.close(async () => { + if (isGzipped) { + const gzipFilePath = `${filePath}.gzip`; + await shim.fsDriver().move(filePath, gzipFilePath); + + try { + await gunzipFile(gzipFilePath, filePath); + resolve(makeResponse(response)); + } catch (error) { + cleanUpOnError(error); + } + + shim.fsDriver().remove(gzipFilePath); + } else { + resolve(makeResponse(response)); + } }); }); }); diff --git a/ReactNativeClient/lib/shim-init-react.js b/ReactNativeClient/lib/shim-init-react.js index 9f5a6b6081..a8ac2dd346 100644 --- a/ReactNativeClient/lib/shim-init-react.js +++ b/ReactNativeClient/lib/shim-init-react.js @@ -36,21 +36,31 @@ function shimInit() { }; shim.fetch = async function(url, options = null) { - // The native fetch() throws an uncatable error that crashes the app if calling it with an - // invalid URL such as '//.resource' or "http://ocloud. de" so detect if the URL is valid beforehand - // and throw a catchable error. - // Bug: https://github.com/facebook/react-native/issues/7436 + // The native fetch() throws an uncatchable error that crashes the + // app if calling it with an invalid URL such as '//.resource' or + // "http://ocloud. de" so detect if the URL is valid beforehand and + // throw a catchable error. Bug: + // https://github.com/facebook/react-native/issues/7436 const validatedUrl = urlValidator.isUri(url); if (!validatedUrl) throw new Error(`Not a valid URL: ${url}`); return shim.fetchWithRetry(() => { - // If the request has a body and it's not a GET call, and it doesn't have a Content-Type header - // we display a warning, because it could trigger a "Network request failed" error. + // If the request has a body and it's not a GET call, and it + // doesn't have a Content-Type header we display a warning, + // because it could trigger a "Network request failed" error. // https://github.com/facebook/react-native/issues/30176 if (options?.body && options?.method && options.method !== 'GET' && !options?.headers?.['Content-Type']) { console.warn('Done a non-GET fetch call without a Content-Type header. It may make the request fail.', url, options); } + // Among React Native `fetch()` many bugs, one of them is that + // it will truncate strings when they contain binary data. + // Browser fetch() or Node fetch() work fine but as always RN's + // one doesn't. There's no obvious way to fix this so we'll + // have to wait if it's eventually fixed upstream. See here for + // more info: + // https://github.com/laurent22/joplin/issues/3986#issuecomment-718019688 + return fetch(validatedUrl, options); }, options); };