Skip to content

Commit

Permalink
fix: "failed to fetch()" error (#491)
Browse files Browse the repository at this point in the history
* add try-catch for fetch()

* use poll function instead try-catch

* style: naming

* fix: poll and resolve for fetched failed

* add try-catch for fetch()

* use poll function instead try-catch

* style: naming

* merge + misc

* deletes unused import

* fix: check for failed fetch promise

* since it's not blocking error - console.err(message) instead 'toast'

* switch to console.err for spooded mime type too

Co-authored-by: stew-ro <v-stewro@microsoft.com>
  • Loading branch information
alex-krasn and stew-ro authored Aug 14, 2020
1 parent bea552b commit 530545c
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/common/localization/en-us.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ export const english: IAppStrings = {
incorrectFileExtension: {
attention: "Attention!",
text: "- extension of this file doesn't correspond MIME type. Please check file:",
failedToFetch: "Failed to fetch ${fileName} for mime type validation",
},
},
assetError: "Unable to load asset",
Expand Down
1 change: 1 addition & 0 deletions src/common/localization/es-cl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ export const spanish: IAppStrings = {
incorrectFileExtension: {
attention: "¡Atención!",
text: "-- la extensión de este archivo no corresponde al tipo MIME. Por favor revise el archivo:",
failedToFetch: "No se pudo recuperar ${fileName} para la validación del tipo de mímica",
},
},
assetError: "No se puede mostrar el activo",
Expand Down
1 change: 1 addition & 0 deletions src/common/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ export interface IAppStrings {
incorrectFileExtension: {
attention: string,
text: string,
failedToFetch: string,
},
}
,
Expand Down
64 changes: 53 additions & 11 deletions src/services/assetService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ interface IMime {
pattern: (number|undefined)[];
}

// tslint:disable number-literal-format
// tslint:disable no-magic-numbers
// tslint:disable number-literal-format
// tslint:disable no-magic-numbers
const imageMimes: IMime[] = [
{
types: ["bmp"],
Expand Down Expand Up @@ -100,13 +100,14 @@ export class AssetService {
} else {
types = await this.getMimeType(filePath);
}

const corruptFileName = fileName.split("%2F").pop().replace(/%20/g, " ");
if (!types) {
console.error(interpolate(strings.editorPage.assetWarning.incorrectFileExtension.failedToFetch, { fileName: corruptFileName.toLocaleUpperCase() }));
}
// If file was renamed/spoofed - fix file extension to true MIME type and show message
if (!types.includes(assetFormat)) {
else if (!types.includes(assetFormat)) {
assetFormat = types[0];
const corruptFileName = fileName.split("%2F").pop().replace(/%20/g, " ");

toast.info(`${strings.editorPage.assetWarning.incorrectFileExtension.attention} ${corruptFileName.toLocaleUpperCase()} ${strings.editorPage.assetWarning.incorrectFileExtension.text} ${corruptFileName.toLocaleUpperCase()}`, { delay: 3000 });
console.error(`${strings.editorPage.assetWarning.incorrectFileExtension.attention} ${corruptFileName.toLocaleUpperCase()} ${strings.editorPage.assetWarning.incorrectFileExtension.text} ${corruptFileName.toLocaleUpperCase()}`);
}
}

Expand Down Expand Up @@ -144,13 +145,21 @@ export class AssetService {
}
}

// If extension of a file was spoofed, we fetch only first 4 bytes of the file and read MIME type
// If extension of a file was spoofed, we fetch only first 4 or needed amount of bytes of the file and read MIME type
public static async getMimeType(uri: string): Promise<string[]> {
const first4bytes: Response = await fetch(uri, { headers: { range: `bytes=0-${mimeBytesNeeded}` } });
const getFirst4bytes = (): Promise<Response> => this.pollForFetchAPI(() => fetch(uri, { headers: { range: `bytes=0-${mimeBytesNeeded}` } }), 1000, 200);
let first4bytes: Response;
try {
first4bytes = await getFirst4bytes()
} catch {
return new Promise<string[]>((resolve) => {
resolve(null);
});
}
const arrayBuffer: ArrayBuffer = await first4bytes.arrayBuffer();
const blob = new Blob([new Uint8Array(arrayBuffer).buffer]);
const blob: Blob = new Blob([new Uint8Array(arrayBuffer).buffer]);
const isMime = (bytes: Uint8Array, mime: IMime): boolean => {
return mime.pattern.every((p, i) => !p || bytes[i] === p);
return mime.pattern.every((p, i) => !p || bytes[i] === p);
};
const fileReader: FileReader = new FileReader();

Expand Down Expand Up @@ -430,4 +439,37 @@ export class AssetService {
const startsWithFolderPath = assetName.indexOf(`${normalizedPath}/`) === 0;
return startsWithFolderPath && assetName.lastIndexOf("/") === normalizedPath.length;
}

/**
* Poll function to repeatedly check if request succeeded
* @param func - function that will be called repeatedly
* @param timeout - timeout
* @param interval - interval
*/
private static pollForFetchAPI = (func, timeout, interval): Promise<any> => {
const endTime = Number(new Date()) + (timeout || 10000);
interval = interval || 100;

const checkSucceeded = (resolve, reject) => {
const ajax = func();
ajax.then(
response => {
if (response.ok) {
resolve(response);
} else {
// Didn't succeeded after too much time, reject
reject("Timed out, please try other file or check your network setting.");
}
}).catch(() => {
if (Number(new Date()) < endTime) {
// If the request isn't succeeded and the timeout hasn't elapsed, go again
setTimeout(checkSucceeded, interval, resolve, reject);
} else {
// Didn't succeeded after too much time, reject
reject("Timed out fetching file.");
}
});
};
return new Promise(checkSucceeded);
}
}

0 comments on commit 530545c

Please sign in to comment.