♻ 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,
|
| The SWTOR launcher first connects to cdn-patch.swtor.com,
|
||||||
| there the traffic is split onto Akamai and Level3, and
|
| there the traffic is split onto Akamai and Level3, and
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
import { exec} from 'child_process';
|
import { exec} from 'child_process';
|
||||||
import * as dns from 'dns';
|
import * as dns from 'dns';
|
||||||
import { IDnsResult } from '../interfaces/IDnsResult';
|
import { IDnsResult } from '../../interfaces/IDnsResult';
|
||||||
|
|
||||||
//TODO: send e-mail with the error
|
//TODO: send e-mail with the error
|
||||||
const assert = (cond: boolean) => { if (!cond) { console.warn('Assert failed'); } };
|
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
|
//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([]); }
|
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') {
|
if (domain !== 'cdn-patch.swtor.com') {
|
||||||
dns.resolve4(domain, { ttl: true }, (err, result) => {
|
dns.resolve4(domain, { ttl: true }, (err, result) => {
|
||||||
return resolve(result.map(({ address, ttl }) => ({ address, ttl, type: 'level3-us' as IDnsResult['type'] })));
|
return resolve(result.map(({ address, ttl }) => ({ address, ttl, type: 'level3-us' as IDnsResult['type'] })));
|
|
@ -1,41 +1,19 @@
|
||||||
import * as http from 'http';
|
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> {
|
export default function getUrlContents({ host, path }: {host: string, path: string}): Promise<ArrayBuffer> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const request = http.request({
|
const request = http.request({
|
||||||
family: 4,
|
family: 4,
|
||||||
host,
|
host,
|
||||||
path,
|
path,
|
||||||
}, (response) => {
|
}, handleResponse.bind(null, resolve, (reason: string) => { request.abort(); reject(reason); }));
|
||||||
if (response.statusCode !== 200) {
|
|
||||||
return reject(`Expected status code 200 but received ${response.statusCode}`);
|
//In case of connection errors, exit early
|
||||||
}
|
request.on('error', (error) => {
|
||||||
const headerLength = Number(response.headers['content-length']);
|
|
||||||
if (headerLength > MAX_MEMORY_SIZE) {
|
|
||||||
reject('File size too large to be handled in memory.');
|
|
||||||
request.abort();
|
request.abort();
|
||||||
return;
|
return reject(error);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
request.end();
|
request.end();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { IServerEntry } from '../interfaces/IDnsResult';
|
import { IServerEntry } from '../interfaces/IDnsResult';
|
||||||
import resolveDns from './resolveDns';
|
import resolveDns from './funcs/resolveDns';
|
||||||
|
|
||||||
/** Time when this script started, for delta time calculations */
|
/** Time when this script started, for delta time calculations */
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
Loading…
Reference in a new issue