♻ Extract response handler
This commit is contained in:
parent
e4c2754493
commit
c821327a6e
4 changed files with 59 additions and 33 deletions
48
src/cdn/funcs/handleResponse.ts
Normal file
48
src/cdn/funcs/handleResponse.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import * as http from 'http';
|
||||
|
||||
/** Too avoid */
|
||||
const MAX_MEMORY_SIZE = 100 * 1024 * 1024;
|
||||
|
||||
export default function handleResponse(
|
||||
resolve: (value: ArrayBuffer) => void,
|
||||
reject: (reason: string) => void,
|
||||
response: http.IncomingMessage,
|
||||
) {
|
||||
//Check that file exists (200 HTTP status code)
|
||||
if (response.statusCode !== 200) {
|
||||
return reject(`Expected status code 200 but received ${response.statusCode}`);
|
||||
}
|
||||
|
||||
//Check file size
|
||||
const headerLength = Number(response.headers['content-length']);
|
||||
if (headerLength > MAX_MEMORY_SIZE) {
|
||||
return reject('File size too large to be handled in memory.');
|
||||
}
|
||||
|
||||
//If we receive a part of the response, store it
|
||||
const chunkList: Buffer[] = [];
|
||||
let totalLength = 0;
|
||||
response.on('data', (chunk: Buffer) => {
|
||||
totalLength += chunk.length;
|
||||
|
||||
//Exit early if we received too much data
|
||||
if (totalLength > headerLength) {
|
||||
return reject(`Expected length ${headerLength} but received at least ${totalLength}`);
|
||||
}
|
||||
|
||||
//Add chunk to array
|
||||
chunkList.push(chunk);
|
||||
});
|
||||
|
||||
//If we finished reading response, check for correctness and return it
|
||||
response.on('end', () => {
|
||||
//Check that length is correct
|
||||
if (totalLength !== headerLength) {
|
||||
return reject(`Expected length ${headerLength} but received ${totalLength}`);
|
||||
}
|
||||
|
||||
//Return file contents as ArrayBuffer
|
||||
const fileContents = Buffer.concat(chunkList, totalLength);
|
||||
return resolve(fileContents.buffer as ArrayBuffer);
|
||||
});
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/* --- DNS resolver for the CDN storing SWTOR's patch files ---
|
||||
/* --- DNS resolver for the CDN storing SWTOR’s patch files ---
|
||||
|
|
||||
| The SWTOR launcher first connects to cdn-patch.swtor.com,
|
||||
| there the traffic is split onto Akamai and Level3, and
|
||||
|
@ -28,7 +28,7 @@
|
|||
|
||||
import { exec} from 'child_process';
|
||||
import * as dns from 'dns';
|
||||
import { IDnsResult } from '../interfaces/IDnsResult';
|
||||
import { IDnsResult } from '../../interfaces/IDnsResult';
|
||||
|
||||
//TODO: send e-mail with the error
|
||||
const assert = (cond: boolean) => { if (!cond) { console.warn('Assert failed'); } };
|
||||
|
@ -39,7 +39,7 @@ async function resolveDns(domain: string): Promise<IDnsResult[]> {
|
|||
//check given string for correctness to prevent injection attacks
|
||||
if (!domain.match(/^[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,3}$/)) { return resolve([]); }
|
||||
|
||||
//Check Level3/North_America separetely
|
||||
//Check Level3/North_America separately
|
||||
if (domain !== 'cdn-patch.swtor.com') {
|
||||
dns.resolve4(domain, { ttl: true }, (err, result) => {
|
||||
return resolve(result.map(({ address, ttl }) => ({ address, ttl, type: 'level3-us' as IDnsResult['type'] })));
|
|
@ -1,41 +1,19 @@
|
|||
import * as http from 'http';
|
||||
import handleResponse from './funcs/handleResponse';
|
||||
|
||||
const MAX_MEMORY_SIZE = 100 * 1024 * 1024;
|
||||
|
||||
/** Downloads the given URL into memory and returns it as an ArrayBuffer. Throws error if download fails or file is too large to be handled in memory. */
|
||||
export default function getUrlContents({ host, path }: {host: string, path: string}): Promise<ArrayBuffer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = http.request({
|
||||
family: 4,
|
||||
host,
|
||||
path,
|
||||
}, (response) => {
|
||||
if (response.statusCode !== 200) {
|
||||
return reject(`Expected status code 200 but received ${response.statusCode}`);
|
||||
}
|
||||
const headerLength = Number(response.headers['content-length']);
|
||||
if (headerLength > MAX_MEMORY_SIZE) {
|
||||
reject('File size too large to be handled in memory.');
|
||||
}, handleResponse.bind(null, resolve, (reason: string) => { request.abort(); reject(reason); }));
|
||||
|
||||
//In case of connection errors, exit early
|
||||
request.on('error', (error) => {
|
||||
request.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
const chunkList: Buffer[] = [];
|
||||
let totalLength = 0;
|
||||
response.on('data', (chunk: Buffer) => {
|
||||
chunkList.push(chunk);
|
||||
totalLength += chunk.length;
|
||||
});
|
||||
response.on('end', () => {
|
||||
if (totalLength !== headerLength) {
|
||||
return reject(`Expected length ${headerLength} but received ${totalLength}`);
|
||||
}
|
||||
const fileContents = Buffer.concat(chunkList, totalLength);
|
||||
resolve(fileContents.buffer as ArrayBuffer);
|
||||
});
|
||||
});
|
||||
|
||||
request.on('error', (e) => {
|
||||
reject(e);
|
||||
return reject(error);
|
||||
});
|
||||
request.end();
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { IServerEntry } from '../interfaces/IDnsResult';
|
||||
import resolveDns from './resolveDns';
|
||||
import resolveDns from './funcs/resolveDns';
|
||||
|
||||
/** Time when this script started, for delta time calculations */
|
||||
const startTime = Date.now();
|
||||
|
|
Loading…
Reference in a new issue