Merge pull request #3986 from thelounge/xpaw/upload-keepalive

Fix upload tokens expiring while uploading when TL is proxied
This commit is contained in:
Pavel Djundik 2020-07-27 11:01:44 +03:00 committed by GitHub
commit a8a2bd7755
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 8 deletions

View File

@ -9,6 +9,7 @@ class Uploader {
init() { init() {
this.xhr = null; this.xhr = null;
this.fileQueue = []; this.fileQueue = [];
this.tokenKeepAlive = null;
document.addEventListener("dragenter", (e) => this.dragEnter(e)); document.addEventListener("dragenter", (e) => this.dragEnter(e));
document.addEventListener("dragover", (e) => this.dragOver(e)); document.addEventListener("dragover", (e) => this.dragOver(e));
@ -131,6 +132,12 @@ class Uploader {
uploadNextFileInQueue(token) { uploadNextFileInQueue(token) {
const file = this.fileQueue.shift(); const file = this.fileQueue.shift();
// Tell the server that we are still upload to this token
// so it does not become invalidated and fail the upload.
// This issue only happens if The Lounge is proxied through other software
// as it may buffer the upload before the upload request will be processed by The Lounge.
this.tokenKeepAlive = setInterval(() => socket.emit("upload:ping", token), 40 * 1000);
if ( if (
store.state.settings.uploadCanvas && store.state.settings.uploadCanvas &&
file.type.startsWith("image/") && file.type.startsWith("image/") &&
@ -219,6 +226,11 @@ class Uploader {
handleResponse(response) { handleResponse(response) {
this.setProgress(0); this.setProgress(0);
if (this.tokenKeepAlive) {
clearInterval(this.tokenKeepAlive);
this.tokenKeepAlive = null;
}
if (response.error) { if (response.error) {
store.commit("currentUserVisibleError", response.error); store.commit("currentUserVisibleError", response.error);
return; return;

View File

@ -11,7 +11,9 @@ const crypto = require("crypto");
const isUtf8 = require("is-utf8"); const isUtf8 = require("is-utf8");
const log = require("../log"); const log = require("../log");
const whitelist = [ // List of allowed mime types that can be rendered in browser
// without forcing it to be downloaded
const inlineContentDispositionTypes = [
"application/ogg", "application/ogg",
"audio/midi", "audio/midi",
"audio/mpeg", "audio/mpeg",
@ -35,17 +37,33 @@ class Uploader {
socket.on("upload:auth", () => { socket.on("upload:auth", () => {
const token = uuidv4(); const token = uuidv4();
uploadTokens.set(token, true);
socket.emit("upload:auth", token); socket.emit("upload:auth", token);
// Invalidate the token in one minute // Invalidate the token in one minute
setTimeout(() => uploadTokens.delete(token), 60 * 1000); const timeout = Uploader.createTokenTimeout(token);
uploadTokens.set(token, timeout);
});
socket.on("upload:ping", (token) => {
if (typeof token !== "string") {
return;
}
let timeout = uploadTokens.get(token);
if (!timeout) {
return;
}
clearTimeout(timeout);
timeout = Uploader.createTokenTimeout(token);
uploadTokens.set(token, timeout);
}); });
} }
static isValidType(mimeType) { static createTokenTimeout(token) {
return whitelist.includes(mimeType); return setTimeout(() => uploadTokens.delete(token), 60 * 1000);
} }
static router(express) { static router(express) {
@ -72,8 +90,10 @@ class Uploader {
return res.status(404).send("Not found"); return res.status(404).send("Not found");
} }
// Force a download in the browser if it's not a whitelisted type (binary or otherwise unknown) // Force a download in the browser if it's not an allowed type (binary or otherwise unknown)
const contentDisposition = Uploader.isValidType(detectedMimeType) ? "inline" : "attachment"; const contentDisposition = inlineContentDispositionTypes.includes(detectedMimeType)
? "inline"
: "attachment";
if (detectedMimeType === "audio/vnd.wave") { if (detectedMimeType === "audio/vnd.wave") {
// Send a more common mime type for wave audio files // Send a more common mime type for wave audio files