Revert "Provide file url to autoUpdater on macos"
This commit is contained in:
parent
e14839dbc7
commit
42a0c1d931
1 changed files with 182 additions and 11 deletions
|
@ -1,8 +1,14 @@
|
||||||
// Copyright 2019 Signal Messenger, LLC
|
// Copyright 2019 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { pathToFileURL } from 'url';
|
import { createReadStream, statSync } from 'fs';
|
||||||
|
import type { IncomingMessage, Server, ServerResponse } from 'http';
|
||||||
|
import { createServer } from 'http';
|
||||||
|
import type { AddressInfo } from 'net';
|
||||||
|
|
||||||
|
import { v4 as getGuid } from 'uuid';
|
||||||
import { autoUpdater } from 'electron';
|
import { autoUpdater } from 'electron';
|
||||||
|
import got from 'got';
|
||||||
|
|
||||||
import { Updater } from './common';
|
import { Updater } from './common';
|
||||||
import { explodePromise } from '../util/explodePromise';
|
import { explodePromise } from '../util/explodePromise';
|
||||||
|
@ -48,22 +54,187 @@ export class MacOSUpdater extends Updater {
|
||||||
const { logger } = this;
|
const { logger } = this;
|
||||||
const { promise, resolve, reject } = explodePromise<void>();
|
const { promise, resolve, reject } = explodePromise<void>();
|
||||||
|
|
||||||
autoUpdater.on('error', (...args) => {
|
const token = getGuid();
|
||||||
logger.error('autoUpdater: error', ...args.map(Errors.toLogFormat));
|
const updateFileUrl = generateFileUrl();
|
||||||
|
const server = createServer();
|
||||||
|
let serverUrl: string;
|
||||||
|
|
||||||
const [error] = args;
|
server.on('error', (error: Error) => {
|
||||||
|
logger.error(`handToAutoUpdate: ${Errors.toLogFormat(error)}`);
|
||||||
|
this.shutdown(server);
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
autoUpdater.on('update-downloaded', () => {
|
|
||||||
logger.info('autoUpdater: update-downloaded event fired');
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.setFeedURL({
|
server.on(
|
||||||
url: pathToFileURL(filePath).href,
|
'request',
|
||||||
|
(request: IncomingMessage, response: ServerResponse) => {
|
||||||
|
const { url } = request;
|
||||||
|
|
||||||
|
if (url === '/') {
|
||||||
|
const absoluteUrl = `${serverUrl}${updateFileUrl}`;
|
||||||
|
writeJSONResponse(absoluteUrl, response);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url === '/token') {
|
||||||
|
writeTokenResponse(token, response);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!url || !url.startsWith(updateFileUrl)) {
|
||||||
|
this.logger.error(
|
||||||
|
`write404: Squirrel requested unexpected url '${url}'`
|
||||||
|
);
|
||||||
|
response.writeHead(404);
|
||||||
|
response.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pipeUpdateToSquirrel(filePath, server, response, reject);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
server.listen(0, '127.0.0.1', async () => {
|
||||||
|
try {
|
||||||
|
serverUrl = getServerUrl(server);
|
||||||
|
|
||||||
|
autoUpdater.on('error', (...args) => {
|
||||||
|
logger.error('autoUpdater: error', ...args.map(Errors.toLogFormat));
|
||||||
|
|
||||||
|
const [error] = args;
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
autoUpdater.on('update-downloaded', () => {
|
||||||
|
logger.info('autoUpdater: update-downloaded event fired');
|
||||||
|
this.shutdown(server);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await got.get(`${serverUrl}/token`);
|
||||||
|
if (JSON.parse(response.body).token !== token) {
|
||||||
|
throw new Error(
|
||||||
|
'autoUpdater: did not receive token back from updates server'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
autoUpdater.setFeedURL({
|
||||||
|
url: serverUrl,
|
||||||
|
headers: { 'Cache-Control': 'no-cache' },
|
||||||
|
});
|
||||||
|
autoUpdater.checkForUpdates();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
autoUpdater.checkForUpdates();
|
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private pipeUpdateToSquirrel(
|
||||||
|
filePath: string,
|
||||||
|
server: Server,
|
||||||
|
response: ServerResponse,
|
||||||
|
reject: (error: Error) => void
|
||||||
|
): void {
|
||||||
|
const { logger } = this;
|
||||||
|
|
||||||
|
const updateFileSize = getFileSize(filePath);
|
||||||
|
const readStream = createReadStream(filePath);
|
||||||
|
|
||||||
|
response.on('error', (error: Error) => {
|
||||||
|
logger.error(
|
||||||
|
`pipeUpdateToSquirrel: update file download request had an error ${Errors.toLogFormat(
|
||||||
|
error
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
this.shutdown(server);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
readStream.on('error', (error: Error) => {
|
||||||
|
logger.error(
|
||||||
|
`pipeUpdateToSquirrel: read stream error response: ${Errors.toLogFormat(
|
||||||
|
error
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
this.shutdown(server, response);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'application/zip',
|
||||||
|
'Content-Length': updateFileSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
readStream.pipe(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private shutdown(server: Server, response?: ServerResponse): void {
|
||||||
|
const { logger } = this;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (server) {
|
||||||
|
server.close();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(
|
||||||
|
`shutdown: Error closing server ${Errors.toLogFormat(error)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (response) {
|
||||||
|
response.end();
|
||||||
|
}
|
||||||
|
} catch (endError) {
|
||||||
|
logger.error(
|
||||||
|
`shutdown: couldn't end response ${Errors.toLogFormat(endError)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
function writeJSONResponse(url: string, response: ServerResponse) {
|
||||||
|
const data = Buffer.from(
|
||||||
|
JSON.stringify({
|
||||||
|
url,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Content-Length': data.byteLength,
|
||||||
|
});
|
||||||
|
response.end(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeTokenResponse(token: string, response: ServerResponse) {
|
||||||
|
const data = Buffer.from(
|
||||||
|
JSON.stringify({
|
||||||
|
token,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
response.writeHead(200, {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Content-Length': data.byteLength,
|
||||||
|
});
|
||||||
|
response.end(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServerUrl(server: Server) {
|
||||||
|
const address = server.address() as AddressInfo;
|
||||||
|
|
||||||
|
return `http://127.0.0.1:${address.port}`;
|
||||||
|
}
|
||||||
|
function generateFileUrl(): string {
|
||||||
|
return `/${getGuid()}.zip`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileSize(targetPath: string): number {
|
||||||
|
const { size } = statSync(targetPath);
|
||||||
|
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue