Link Previews
This commit is contained in:
parent
91ef39e482
commit
813924685e
36 changed files with 2298 additions and 134 deletions
|
@ -5,9 +5,7 @@ const { Agent } = require('https');
|
|||
|
||||
const is = require('@sindresorhus/is');
|
||||
|
||||
/* global Buffer: false */
|
||||
/* global setTimeout: false */
|
||||
/* global log: false */
|
||||
/* global Buffer, setTimeout, log, _ */
|
||||
|
||||
/* eslint-disable more/no-then, no-bitwise, no-nested-ternary */
|
||||
|
||||
|
@ -166,12 +164,28 @@ const agents = {
|
|||
auth: null,
|
||||
};
|
||||
|
||||
function getContentType(response) {
|
||||
if (response.headers && response.headers.get) {
|
||||
return response.headers.get('content-type');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function _promiseAjax(providedUrl, options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = providedUrl || `${options.host}/${options.path}`;
|
||||
log.info(
|
||||
`${options.type} ${url}${options.unauthenticated ? ' (unauth)' : ''}`
|
||||
);
|
||||
if (options.disableLogs) {
|
||||
log.info(
|
||||
`${options.type} [REDACTED_URL]${
|
||||
options.unauthenticated ? ' (unauth)' : ''
|
||||
}`
|
||||
);
|
||||
} else {
|
||||
log.info(
|
||||
`${options.type} ${url}${options.unauthenticated ? ' (unauth)' : ''}`
|
||||
);
|
||||
}
|
||||
const timeout =
|
||||
typeof options.timeout !== 'undefined' ? options.timeout : 10000;
|
||||
|
||||
|
@ -195,7 +209,12 @@ function _promiseAjax(providedUrl, options) {
|
|||
const fetchOptions = {
|
||||
method: options.type,
|
||||
body: options.data || null,
|
||||
headers: { 'X-Signal-Agent': 'OWD' },
|
||||
headers: {
|
||||
'User-Agent': 'Signal Desktop (+https://signal.org/download)',
|
||||
'X-Signal-Agent': 'OWD',
|
||||
...options.headers,
|
||||
},
|
||||
redirect: options.redirect,
|
||||
agent,
|
||||
ca: options.certificateAuthority,
|
||||
timeout,
|
||||
|
@ -238,13 +257,20 @@ function _promiseAjax(providedUrl, options) {
|
|||
response.headers.get('Content-Type') === 'application/json'
|
||||
) {
|
||||
resultPromise = response.json();
|
||||
} else if (options.responseType === 'arraybuffer') {
|
||||
} else if (
|
||||
options.responseType === 'arraybuffer' ||
|
||||
options.responseType === 'arraybufferwithdetails'
|
||||
) {
|
||||
resultPromise = response.buffer();
|
||||
} else {
|
||||
resultPromise = response.text();
|
||||
}
|
||||
|
||||
return resultPromise.then(result => {
|
||||
if (options.responseType === 'arraybuffer') {
|
||||
if (
|
||||
options.responseType === 'arraybuffer' ||
|
||||
options.responseType === 'arraybufferwithdetails'
|
||||
) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
result = result.buffer.slice(
|
||||
result.byteOffset,
|
||||
|
@ -254,8 +280,17 @@ function _promiseAjax(providedUrl, options) {
|
|||
if (options.responseType === 'json') {
|
||||
if (options.validateResponse) {
|
||||
if (!_validateResponse(result, options.validateResponse)) {
|
||||
log.error(options.type, url, response.status, 'Error');
|
||||
reject(
|
||||
if (options.disableLogs) {
|
||||
log.info(
|
||||
options.type,
|
||||
'[REDACTED_URL]',
|
||||
response.status,
|
||||
'Error'
|
||||
);
|
||||
} else {
|
||||
log.error(options.type, url, response.status, 'Error');
|
||||
}
|
||||
return reject(
|
||||
HTTPError(
|
||||
'promiseAjax: invalid response',
|
||||
response.status,
|
||||
|
@ -267,23 +302,47 @@ function _promiseAjax(providedUrl, options) {
|
|||
}
|
||||
}
|
||||
if (response.status >= 0 && response.status < 400) {
|
||||
log.info(options.type, url, response.status, 'Success');
|
||||
resolve(result, response.status);
|
||||
if (options.disableLogs) {
|
||||
log.info(
|
||||
options.type,
|
||||
'[REDACTED_URL]',
|
||||
response.status,
|
||||
'Success'
|
||||
);
|
||||
} else {
|
||||
log.info(options.type, url, response.status, 'Success');
|
||||
}
|
||||
if (options.responseType === 'arraybufferwithdetails') {
|
||||
return resolve({
|
||||
data: result,
|
||||
contentType: getContentType(response),
|
||||
response,
|
||||
});
|
||||
}
|
||||
return resolve(result, response.status);
|
||||
}
|
||||
|
||||
if (options.disableLogs) {
|
||||
log.info(options.type, '[REDACTED_URL]', response.status, 'Error');
|
||||
} else {
|
||||
log.error(options.type, url, response.status, 'Error');
|
||||
reject(
|
||||
HTTPError(
|
||||
'promiseAjax: error response',
|
||||
response.status,
|
||||
result,
|
||||
options.stack
|
||||
)
|
||||
);
|
||||
}
|
||||
return reject(
|
||||
HTTPError(
|
||||
'promiseAjax: error response',
|
||||
response.status,
|
||||
result,
|
||||
options.stack
|
||||
)
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
log.error(options.type, url, 0, 'Error');
|
||||
if (options.disableLogs) {
|
||||
log.error(options.type, '[REDACTED_URL]', 0, 'Error');
|
||||
} else {
|
||||
log.error(options.type, url, 0, 'Error');
|
||||
}
|
||||
const stack = `${e.stack}\nInitial stack:\n${options.stack}`;
|
||||
reject(HTTPError('promiseAjax catch', 0, e.toString(), stack));
|
||||
});
|
||||
|
@ -342,7 +401,13 @@ module.exports = {
|
|||
};
|
||||
|
||||
// We first set up the data that won't change during this session of the app
|
||||
function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
|
||||
function initialize({
|
||||
url,
|
||||
cdnUrl,
|
||||
certificateAuthority,
|
||||
contentProxyUrl,
|
||||
proxyUrl,
|
||||
}) {
|
||||
if (!is.string(url)) {
|
||||
throw new Error('WebAPI.initialize: Invalid server url');
|
||||
}
|
||||
|
@ -352,6 +417,9 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
|
|||
if (!is.string(certificateAuthority)) {
|
||||
throw new Error('WebAPI.initialize: Invalid certificateAuthority');
|
||||
}
|
||||
if (!is.string(contentProxyUrl)) {
|
||||
throw new Error('WebAPI.initialize: Invalid contentProxyUrl');
|
||||
}
|
||||
|
||||
// Thanks to function-hoisting, we can put this return statement before all of the
|
||||
// below function definitions.
|
||||
|
@ -372,8 +440,6 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
|
|||
getAttachment,
|
||||
getAvatar,
|
||||
getDevices,
|
||||
getSenderCertificate,
|
||||
registerSupportForUnauthenticatedDelivery,
|
||||
getKeysForNumber,
|
||||
getKeysForNumberUnauth,
|
||||
getMessageSocket,
|
||||
|
@ -381,15 +447,19 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
|
|||
getProfile,
|
||||
getProfileUnauth,
|
||||
getProvisioningSocket,
|
||||
getProxiedSize,
|
||||
getSenderCertificate,
|
||||
makeProxiedRequest,
|
||||
putAttachment,
|
||||
registerKeys,
|
||||
registerSupportForUnauthenticatedDelivery,
|
||||
removeSignalingKey,
|
||||
requestVerificationSMS,
|
||||
requestVerificationVoice,
|
||||
sendMessages,
|
||||
sendMessagesUnauth,
|
||||
setSignedPreKey,
|
||||
updateDeviceName,
|
||||
removeSignalingKey,
|
||||
};
|
||||
|
||||
function _ajax(param) {
|
||||
|
@ -799,6 +869,47 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
|
|||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
async function getProxiedSize(url) {
|
||||
const result = await _outerAjax(url, {
|
||||
processData: false,
|
||||
responseType: 'arraybufferwithdetails',
|
||||
proxyUrl: contentProxyUrl,
|
||||
type: 'HEAD',
|
||||
disableLogs: true,
|
||||
});
|
||||
|
||||
const { response } = result;
|
||||
if (!response.headers || !response.headers.get) {
|
||||
throw new Error('getProxiedSize: Problem retrieving header value');
|
||||
}
|
||||
|
||||
const size = response.headers.get('content-length');
|
||||
return parseInt(size, 10);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
function makeProxiedRequest(url, options = {}) {
|
||||
const { returnArrayBuffer, start, end } = options;
|
||||
let headers;
|
||||
|
||||
if (_.isNumber(start) && _.isNumber(end)) {
|
||||
headers = {
|
||||
Range: `bytes=${start}-${end}`,
|
||||
};
|
||||
}
|
||||
|
||||
return _outerAjax(url, {
|
||||
processData: false,
|
||||
responseType: returnArrayBuffer ? 'arraybufferwithdetails' : null,
|
||||
proxyUrl: contentProxyUrl,
|
||||
type: 'GET',
|
||||
redirect: 'follow',
|
||||
disableLogs: true,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
function getMessageSocket() {
|
||||
log.info('opening message socket', url);
|
||||
const fixedScheme = url
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue