Skip to content
This repository was archived by the owner on Aug 5, 2020. It is now read-only.

Commit 755e080

Browse files
Fix checksum calculation in Edge
Checksum calculations were failing in Edge because the Rust WebAssembly wrapper depends on `TextDecoder`, which is not supported by the current version of Edge (even though WebAssembly itself is supported): https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/TextDecoder This commit fixes the problem by trying to load the WASM module, and falling back to checksums calculated in JavaScript if the module fails to load. This uses exceptions for control flow, but there's no obvious property to test to be confident that WASM will work, since it may depend on modules other than `TextDecoder` in the future. A possible alternative would be to polyfill `TextDecoder`, as described here: golang/go#27295 (comment) The polyfill is quite complicated, though, so the JS checksum fallback seems like a better solution, at least for now. We can revisit it if there are major performance problems.
1 parent 0cdfc61 commit 755e080

File tree

4 files changed

+67
-47
lines changed

4 files changed

+67
-47
lines changed

js-src/bootstrap.ts

Lines changed: 0 additions & 2 deletions
This file was deleted.

js-src/index.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,45 @@
11
import { updateFileStatuses } from "./fileStatus";
2-
import { upload } from "./upload";
2+
import {ChecksumCalculator, upload} from "./upload";
3+
4+
const wasmSupported = (() => {
5+
try {
6+
if (
7+
typeof WebAssembly === "object" &&
8+
typeof WebAssembly.instantiate === "function"
9+
) {
10+
const module = new WebAssembly.Module(
11+
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)
12+
);
13+
if (module instanceof WebAssembly.Module)
14+
return (
15+
new WebAssembly.Instance(module) instanceof
16+
WebAssembly.Instance
17+
);
18+
}
19+
} catch (e) {}
20+
return false;
21+
})();
322

423
window.onload = function() {
24+
if (wasmSupported) {
25+
// @ts-ignore
26+
import("@nationalarchives/checksum-calculator")
27+
.then(checksumModule => {
28+
renderModules(checksumModule);
29+
})
30+
.catch(e => {
31+
console.error("Error importing checksum module:", e);
32+
renderModules()
33+
});
34+
} else {
35+
renderModules();
36+
}
37+
};
38+
39+
const renderModules = (checksumCalculator?: ChecksumCalculator) => {
540
const uploadContainer: HTMLDivElement | null = document.querySelector(".upload-form");
641
if (uploadContainer) {
7-
upload();
42+
upload(checksumCalculator);
843
}
944
const fileStatusContainer: HTMLDivElement | null = document.querySelector(".file-status");
1045
if (fileStatusContainer) {

js-src/upload/index.ts

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import Axios from "axios";
22
import * as S3 from "aws-sdk/clients/s3";
3-
import * as wasm from "@nationalarchives/checksum-calculator";
43
import { generateHash } from "./checksum";
54

5+
export interface ChecksumCalculator {
6+
generate_checksum(blob: any): any;
7+
}
8+
69
interface HTMLInputTarget extends EventTarget {
710
files?: InputElement;
811
}
@@ -36,26 +39,7 @@ export interface IWebkitEntry extends DataTransferItem {
3639
file: (success: (file: File) => void) => void;
3740
}
3841

39-
const wasmSupported = (() => {
40-
try {
41-
if (
42-
typeof WebAssembly === "object" &&
43-
typeof WebAssembly.instantiate === "function"
44-
) {
45-
const module = new WebAssembly.Module(
46-
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)
47-
);
48-
if (module instanceof WebAssembly.Module)
49-
return (
50-
new WebAssembly.Instance(module) instanceof
51-
WebAssembly.Instance
52-
);
53-
}
54-
} catch (e) {}
55-
return false;
56-
})();
57-
58-
const upload: () => void = () => {
42+
const upload: (checksumCalculator?: ChecksumCalculator) => void = (checksumCalculator) => {
5943
const uploadForm: HTMLFormElement | null = document.querySelector(
6044
"#file-upload-form"
6145
);
@@ -68,7 +52,7 @@ const upload: () => void = () => {
6852
ev.preventDefault();
6953
const target: HTMLInputTarget | null = ev.currentTarget;
7054
const files: TdrFile[] = target!.files!.files!;
71-
processFiles(files)
55+
processFiles(files, checksumCalculator)
7256
.then(() => {
7357
if (commenceUploadForm) {
7458
commenceUploadForm.submit();
@@ -87,9 +71,25 @@ const upload: () => void = () => {
8771
});
8872
});
8973
}
74+
75+
const onDrop: (e: DragEvent) => void = async e => {
76+
e.preventDefault();
77+
e.stopPropagation();
78+
setIsDragging(false);
79+
const dataTransferItems: DataTransferItemList = e.dataTransfer!.items;
80+
81+
//Assume one folder in the drag and drop for now
82+
const files: TdrFile[] = await getAllFiles(
83+
dataTransferItems[0].webkitGetAsEntry(),
84+
[]
85+
);
86+
processFiles(files, checksumCalculator);
87+
};
88+
9089
const dragAndDrop: HTMLDivElement | null = document.querySelector(
9190
".govuk-file-drop"
9291
);
92+
9393
if (dragAndDrop) {
9494
dragAndDrop.ondragover = onDragOver;
9595
dragAndDrop.ondragleave = () => setIsDragging(false);
@@ -168,26 +168,13 @@ const getAllFiles: (
168168
return fileInfoInput;
169169
};
170170

171-
const onDrop: (e: DragEvent) => void = async e => {
172-
e.preventDefault();
173-
e.stopPropagation();
174-
setIsDragging(false);
175-
const dataTransferItems: DataTransferItemList = e.dataTransfer!.items;
176-
177-
//Assume one folder in the drag and drop for now
178-
const files: TdrFile[] = await getAllFiles(
179-
dataTransferItems[0].webkitGetAsEntry(),
180-
[]
181-
);
182-
processFiles(files);
183-
};
184-
185171
const getFileInfo: (
186-
tdrFile: TdrFile
187-
) => Promise<CreateFileInput> = async tdrFile => {
172+
tdrFile: TdrFile,
173+
checksumCalculator?: ChecksumCalculator
174+
) => Promise<CreateFileInput> = async (tdrFile, checksumCalculator) => {
188175
let clientSideChecksum;
189-
if (wasmSupported) {
190-
clientSideChecksum = await wasm.generate_checksum(tdrFile);
176+
if (checksumCalculator) {
177+
clientSideChecksum = await checksumCalculator.generate_checksum(tdrFile);
191178
} else {
192179
clientSideChecksum = await generateHash(tdrFile);
193180
}
@@ -248,7 +235,7 @@ function uploadFileData(batches: CreateFileInput[][], consignmentId: number) {
248235
return Promise.all(responses);
249236
}
250237

251-
async function processFiles(files: TdrFile[]) {
238+
async function processFiles(files: TdrFile[], checksumCalculator?: ChecksumCalculator) {
252239
const fileInfoList: CreateFileInput[] = [];
253240
const urlParams: URLSearchParams = new URLSearchParams(
254241
window.location.search
@@ -266,7 +253,7 @@ async function processFiles(files: TdrFile[]) {
266253
console.log(`Got file info for ${fileInfoCount} files`);
267254
}
268255

269-
const fileInfo: CreateFileInput = await getFileInfo(tdrFile);
256+
const fileInfo: CreateFileInput = await getFileInfo(tdrFile, checksumCalculator);
270257

271258
fileInfoList.push(fileInfo);
272259
filePathToFile[fileInfo.path!] = tdrFile;

webpack.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const path = require('path');
22
const webpack = require('webpack');
33

44
module.exports = {
5-
entry: './js-src/bootstrap.ts',
5+
entry: './js-src/index.ts',
66
output: {
77
filename: 'main.js',
88
path: path.resolve(__dirname, 'public/javascripts'),

0 commit comments

Comments
 (0)