@push.rocks/smartproxy
Version:
A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.
297 lines • 27.8 kB
JavaScript
import * as plugins from '../../plugins.js';
import { logger } from '../../core/utils/logger.js';
const maxChallengeRelayMessageBytes = 1024 * 1024;
const maxChallengeRelayIdBytes = 256;
export class ChallengeProviderRelayServer {
providers;
providerTimeoutMs;
maxActiveProviderOperations;
maxUnsettledProviderOperations;
activeProviderOperations = 0;
unsettledProviderOperations = 0;
server = null;
socketPath;
activeSockets = new Set();
startPromise;
constructor(providers, optionsArg = {}) {
this.providers = providers;
this.socketPath = `/tmp/smartproxy-challenge-relay-${process.pid}-${plugins.crypto.randomBytes(8).toString('hex')}.sock`;
this.providerTimeoutMs = Math.max(1, optionsArg.providerTimeoutMs ?? 5_000);
this.maxActiveProviderOperations = Math.max(1, optionsArg.maxActiveProviderOperations ?? 1024);
this.maxUnsettledProviderOperations = Math.max(1, optionsArg.maxUnsettledProviderOperations ?? this.maxActiveProviderOperations);
}
getSocketPath() {
return this.socketPath;
}
async start() {
if (this.server)
return;
if (this.startPromise)
return this.startPromise;
this.startPromise = this.startInternal();
try {
await this.startPromise;
}
finally {
this.startPromise = undefined;
}
}
async startInternal() {
try {
await plugins.fs.promises.unlink(this.socketPath);
}
catch {
// Ignore stale socket cleanup failures for missing files.
}
const server = plugins.net.createServer((socket) => {
this.activeSockets.add(socket);
socket.once('close', () => this.activeSockets.delete(socket));
this.handleConnection(socket);
});
server.on('error', (err) => {
logger.log('error', `ChallengeProviderRelayServer error: ${err.message}`, { component: 'challenge-provider-relay-server' });
});
await new Promise((resolve, reject) => {
const handleError = (err) => reject(err);
server.once('error', handleError);
server.listen(this.socketPath, () => {
server.off('error', handleError);
this.server = server;
logger.log('info', `ChallengeProviderRelayServer listening on ${this.socketPath}`, { component: 'challenge-provider-relay-server' });
resolve();
});
}).catch((err) => {
server.close();
throw err;
});
}
async stop() {
if (this.startPromise && !this.server) {
await this.startPromise.catch(() => undefined);
}
for (const socket of this.activeSockets) {
socket.destroy();
}
this.activeSockets.clear();
const server = this.server;
if (!server)
return;
this.server = null;
await new Promise((resolve) => {
server.close(() => {
plugins.fs.unlink(this.socketPath, () => resolve());
});
});
}
handleConnection(socket) {
let buffer = Buffer.alloc(0);
socket.setTimeout(Math.max(10_000, this.providerTimeoutMs + 1_000));
socket.on('timeout', () => socket.destroy());
socket.on('error', (err) => {
logger.log('warn', `Challenge relay socket error: ${err.message}`, { component: 'challenge-provider-relay-server' });
});
socket.on('data', (chunk) => {
buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk], buffer.length + chunk.length);
if (buffer.length > maxChallengeRelayMessageBytes) {
socket.removeAllListeners('data');
this.writeResponse(socket, { success: false, error: 'Challenge relay request exceeds maximum size' });
buffer = Buffer.alloc(0);
socket.end();
return;
}
const newlineIndex = buffer.indexOf(0x0a);
if (newlineIndex === -1)
return;
const line = buffer.subarray(0, newlineIndex).toString('utf8');
socket.removeAllListeners('data');
this.dispatchLine(line)
.then((response) => this.writeResponse(socket, response))
.catch((err) => this.writeResponse(socket, { success: false, error: err.message }))
.finally(() => socket.end());
});
}
async dispatchLine(lineArg) {
let request;
try {
request = JSON.parse(lineArg);
}
catch {
return { success: false, error: 'Invalid challenge relay JSON' };
}
const provider = this.providers.get(request.providerId);
if (!provider) {
return {
id: request.id,
success: false,
error: `Challenge provider '${request.providerId}' is not registered`,
};
}
switch (request.operation) {
case 'assess':
return { id: request.id, success: true, result: await this.withProviderTimeout(() => provider.assess(request.request), 'assess') };
case 'render':
return { id: request.id, success: true, result: await this.withProviderTimeout(() => provider.render(request.request), 'render') };
case 'verify':
return { id: request.id, success: true, result: await this.withProviderTimeout(() => provider.verify(request.request), 'verify') };
default:
const operationValue = request.operation;
return { id: request.id, success: false, error: `Unsupported challenge relay operation '${String(operationValue)}'` };
}
}
async withProviderTimeout(operationArg, operationNameArg) {
if (this.activeProviderOperations >= this.maxActiveProviderOperations) {
throw new Error(`Challenge provider ${operationNameArg} rejected because too many operations are active`);
}
if (this.unsettledProviderOperations >= this.maxUnsettledProviderOperations) {
throw new Error(`Challenge provider ${operationNameArg} rejected because too many timed-out operations are still settling`);
}
let timer;
let activeReleased = false;
let unsettledReleased = false;
const releaseActive = () => {
if (!activeReleased) {
activeReleased = true;
this.activeProviderOperations -= 1;
}
};
const releaseUnsettled = () => {
if (!unsettledReleased) {
unsettledReleased = true;
this.unsettledProviderOperations -= 1;
}
};
this.activeProviderOperations += 1;
this.unsettledProviderOperations += 1;
const providerPromise = Promise.resolve().then(operationArg);
const trackedProviderPromise = providerPromise.then((result) => {
releaseActive();
releaseUnsettled();
return result;
}, (err) => {
releaseActive();
releaseUnsettled();
throw err;
});
trackedProviderPromise.catch(() => undefined);
try {
return await Promise.race([
trackedProviderPromise,
new Promise((_resolve, reject) => {
timer = setTimeout(() => {
releaseActive();
reject(new Error(`Challenge provider ${operationNameArg} timed out after ${this.providerTimeoutMs}ms`));
}, this.providerTimeoutMs);
}),
]);
}
finally {
if (timer)
clearTimeout(timer);
}
}
writeResponse(socket, responseArg) {
let response = responseArg;
if (this.estimateJsonByteLength(response, maxChallengeRelayMessageBytes) === undefined) {
response = this.createErrorResponse(responseArg.id, 'Challenge relay response exceeds maximum size');
}
let serializedResponse;
try {
serializedResponse = JSON.stringify(response);
}
catch {
serializedResponse = JSON.stringify(this.createErrorResponse(responseArg.id, 'Challenge relay response is not serializable'));
}
if (Buffer.byteLength(serializedResponse, 'utf8') > maxChallengeRelayMessageBytes) {
serializedResponse = JSON.stringify({
success: false,
error: 'Challenge relay response exceeds maximum size',
});
}
socket.write(`${serializedResponse}\n`);
}
createErrorResponse(idArg, errorArg) {
const response = {
success: false,
error: errorArg,
};
if (idArg && Buffer.byteLength(idArg, 'utf8') <= maxChallengeRelayIdBytes) {
response.id = idArg;
}
return response;
}
estimateJsonByteLength(valueArg, maxBytesArg, seenArg = new Set()) {
const bytes = this.estimateJsonByteLengthInternal(valueArg, maxBytesArg, seenArg);
return bytes !== undefined && bytes <= maxBytesArg ? bytes : undefined;
}
estimateJsonByteLengthInternal(valueArg, maxBytesArg, seenArg) {
if (valueArg === null)
return 4;
if (typeof valueArg === 'string')
return this.estimateJsonStringByteLength(valueArg, maxBytesArg);
if (typeof valueArg === 'number')
return Number.isFinite(valueArg) ? String(valueArg).length : 4;
if (typeof valueArg === 'boolean')
return valueArg ? 4 : 5;
if (typeof valueArg === 'undefined')
return 4;
if (typeof valueArg === 'bigint' || typeof valueArg === 'symbol' || typeof valueArg === 'function')
return undefined;
if (typeof valueArg !== 'object')
return undefined;
if (seenArg.has(valueArg))
return undefined;
seenArg.add(valueArg);
try {
let totalBytes = 2;
if (Array.isArray(valueArg)) {
for (let i = 0; i < valueArg.length; i++) {
const childBytes = this.estimateJsonByteLengthInternal(valueArg[i], maxBytesArg, seenArg);
if (childBytes === undefined)
return undefined;
totalBytes += childBytes + (i > 0 ? 1 : 0);
if (totalBytes > maxBytesArg)
return undefined;
}
return totalBytes;
}
let propertyCount = 0;
for (const [key, value] of Object.entries(valueArg)) {
if (typeof value === 'undefined' || typeof value === 'function' || typeof value === 'symbol')
continue;
const keyBytes = this.estimateJsonStringByteLength(key, maxBytesArg);
const valueBytes = this.estimateJsonByteLengthInternal(value, maxBytesArg, seenArg);
if (keyBytes === undefined || valueBytes === undefined)
return undefined;
totalBytes += keyBytes + valueBytes + 1 + (propertyCount > 0 ? 1 : 0);
propertyCount += 1;
if (totalBytes > maxBytesArg)
return undefined;
}
return totalBytes;
}
finally {
seenArg.delete(valueArg);
}
}
estimateJsonStringByteLength(valueArg, maxBytesArg) {
let bytes = Buffer.byteLength(valueArg, 'utf8') + 2;
if (bytes > maxBytesArg)
return undefined;
for (let i = 0; i < valueArg.length; i++) {
const code = valueArg.charCodeAt(i);
if (code === 0x22 || code === 0x5c) {
bytes += 1;
}
else if (code === 0x08 || code === 0x09 || code === 0x0a || code === 0x0c || code === 0x0d) {
bytes += 1;
}
else if (code < 0x20) {
bytes += 5;
}
if (bytes > maxBytesArg)
return undefined;
}
return bytes;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhbGxlbmdlLXByb3ZpZGVyLXJlbGF5LXNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvY2hhbGxlbmdlLXByb3ZpZGVyLXJlbGF5LXNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQWtCcEQsTUFBTSw2QkFBNkIsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBQ2xELE1BQU0sd0JBQXdCLEdBQUcsR0FBRyxDQUFDO0FBUXJDLE1BQU0sT0FBTyw0QkFBNEI7SUFZN0I7SUFYRixpQkFBaUIsQ0FBUztJQUMxQiwyQkFBMkIsQ0FBUztJQUNwQyw4QkFBOEIsQ0FBUztJQUN2Qyx3QkFBd0IsR0FBRyxDQUFDLENBQUM7SUFDN0IsMkJBQTJCLEdBQUcsQ0FBQyxDQUFDO0lBQ2hDLE1BQU0sR0FBOEIsSUFBSSxDQUFDO0lBQ3pDLFVBQVUsQ0FBUztJQUNuQixhQUFhLEdBQUcsSUFBSSxHQUFHLEVBQXNCLENBQUM7SUFDOUMsWUFBWSxDQUFpQjtJQUVyQyxZQUNVLFNBQWlFLEVBQ3pFLGFBQW1ELEVBQUU7UUFEN0MsY0FBUyxHQUFULFNBQVMsQ0FBd0Q7UUFHekUsSUFBSSxDQUFDLFVBQVUsR0FBRyxtQ0FBbUMsT0FBTyxDQUFDLEdBQUcsSUFBSSxPQUFPLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQztRQUN6SCxJQUFJLENBQUMsaUJBQWlCLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsVUFBVSxDQUFDLGlCQUFpQixJQUFJLEtBQUssQ0FBQyxDQUFDO1FBQzVFLElBQUksQ0FBQywyQkFBMkIsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxVQUFVLENBQUMsMkJBQTJCLElBQUksSUFBSSxDQUFDLENBQUM7UUFDL0YsSUFBSSxDQUFDLDhCQUE4QixHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLFVBQVUsQ0FBQyw4QkFBOEIsSUFBSSxJQUFJLENBQUMsMkJBQTJCLENBQUMsQ0FBQztJQUNuSSxDQUFDO0lBRU0sYUFBYTtRQUNsQixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDekIsQ0FBQztJQUVNLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLElBQUksSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPO1FBQ3hCLElBQUksSUFBSSxDQUFDLFlBQVk7WUFBRSxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUM7UUFDaEQsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDekMsSUFBSSxDQUFDO1lBQ0gsTUFBTSxJQUFJLENBQUMsWUFBWSxDQUFDO1FBQzFCLENBQUM7Z0JBQVMsQ0FBQztZQUNULElBQUksQ0FBQyxZQUFZLEdBQUcsU0FBUyxDQUFDO1FBQ2hDLENBQUM7SUFDSCxDQUFDO0lBRU8sS0FBSyxDQUFDLGFBQWE7UUFDekIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxPQUFPLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3BELENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCwwREFBMEQ7UUFDNUQsQ0FBQztRQUVELE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUU7WUFDakQsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDL0IsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztZQUM5RCxJQUFJLENBQUMsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDaEMsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO1lBQ3pCLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHVDQUF1QyxHQUFHLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsaUNBQWlDLEVBQUUsQ0FBQyxDQUFDO1FBQzlILENBQUMsQ0FBQyxDQUFDO1FBRUgsTUFBTSxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUMxQyxNQUFNLFdBQVcsR0FBRyxDQUFDLEdBQVUsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ2hELE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1lBQ2xDLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxHQUFHLEVBQUU7Z0JBQ2xDLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLFdBQVcsQ0FBQyxDQUFDO2dCQUNqQyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztnQkFDckIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsNkNBQTZDLElBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxpQ0FBaUMsRUFBRSxDQUFDLENBQUM7Z0JBQ3JJLE9BQU8sRUFBRSxDQUFDO1lBQ1osQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRTtZQUNmLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sR0FBRyxDQUFDO1FBQ1osQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU0sS0FBSyxDQUFDLElBQUk7UUFDZixJQUFJLElBQUksQ0FBQyxZQUFZLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDdEMsTUFBTSxJQUFJLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNqRCxDQUFDO1FBRUQsS0FBSyxNQUFNLE1BQU0sSUFBSSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDeEMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ25CLENBQUM7UUFDRCxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBRTNCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDM0IsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPO1FBQ3BCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1FBQ25CLE1BQU0sSUFBSSxPQUFPLENBQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUNsQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRTtnQkFDaEIsT0FBTyxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3RELENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU8sZ0JBQWdCLENBQUMsTUFBMEI7UUFDakQsSUFBSSxNQUFNLEdBQTRCLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdEQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsaUJBQWlCLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNwRSxNQUFNLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUM3QyxNQUFNLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO1lBQ3pCLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLGlDQUFpQyxHQUFHLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsaUNBQWlDLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZILENBQUMsQ0FBQyxDQUFDO1FBRUgsTUFBTSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFhLEVBQUUsRUFBRTtZQUNsQyxNQUFNLEdBQUcsTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsRUFBRSxNQUFNLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUNwRyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEdBQUcsNkJBQTZCLEVBQUUsQ0FBQztnQkFDbEQsTUFBTSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNsQyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLDhDQUE4QyxFQUFFLENBQUMsQ0FBQztnQkFDdEcsTUFBTSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pCLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQztnQkFDYixPQUFPO1lBQ1QsQ0FBQztZQUVELE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDMUMsSUFBSSxZQUFZLEtBQUssQ0FBQyxDQUFDO2dCQUFFLE9BQU87WUFFaEMsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsWUFBWSxDQUFDLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQy9ELE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUNsQyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQztpQkFDcEIsSUFBSSxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQztpQkFDeEQsS0FBSyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFHLEdBQWEsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO2lCQUM3RixPQUFPLENBQUMsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFDakMsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU8sS0FBSyxDQUFDLFlBQVksQ0FBQyxPQUFlO1FBQ3hDLElBQUksT0FBK0IsQ0FBQztRQUNwQyxJQUFJLENBQUM7WUFDSCxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQTJCLENBQUM7UUFDMUQsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLE9BQU8sRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSw4QkFBOEIsRUFBRSxDQUFDO1FBQ25FLENBQUM7UUFFRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDeEQsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ2QsT0FBTztnQkFDTCxFQUFFLEVBQUUsT0FBTyxDQUFDLEVBQUU7Z0JBQ2QsT0FBTyxFQUFFLEtBQUs7Z0JBQ2QsS0FBSyxFQUFFLHVCQUF1QixPQUFPLENBQUMsVUFBVSxxQkFBcUI7YUFDdEUsQ0FBQztRQUNKLENBQUM7UUFFRCxRQUFRLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUMxQixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxFQUFFLEVBQUUsRUFBRSxPQUFPLENBQUMsRUFBRSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQXlELENBQUMsRUFBRSxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQ3ZMLEtBQUssUUFBUTtnQkFDWCxPQUFPLEVBQUUsRUFBRSxFQUFFLE9BQU8sQ0FBQyxFQUFFLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBeUQsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDdkwsS0FBSyxRQUFRO2dCQUNYLE9BQU8sRUFBRSxFQUFFLEVBQUUsT0FBTyxDQUFDLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxNQUFNLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUF5RCxDQUFDLEVBQUUsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUN2TDtnQkFDRSxNQUFNLGNBQWMsR0FBSSxPQUFtQyxDQUFDLFNBQVMsQ0FBQztnQkFDdEUsT0FBTyxFQUFFLEVBQUUsRUFBRSxPQUFPLENBQUMsRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLDBDQUEwQyxNQUFNLENBQUMsY0FBYyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQzFILENBQUM7SUFDSCxDQUFDO0lBRU8sS0FBSyxDQUFDLG1CQUFtQixDQUFJLFlBQThCLEVBQUUsZ0JBQTBDO1FBQzdHLElBQUksSUFBSSxDQUFDLHdCQUF3QixJQUFJLElBQUksQ0FBQywyQkFBMkIsRUFBRSxDQUFDO1lBQ3RFLE1BQU0sSUFBSSxLQUFLLENBQUMsc0JBQXNCLGdCQUFnQixrREFBa0QsQ0FBQyxDQUFDO1FBQzVHLENBQUM7UUFDRCxJQUFJLElBQUksQ0FBQywyQkFBMkIsSUFBSSxJQUFJLENBQUMsOEJBQThCLEVBQUUsQ0FBQztZQUM1RSxNQUFNLElBQUksS0FBSyxDQUFDLHNCQUFzQixnQkFBZ0Isb0VBQW9FLENBQUMsQ0FBQztRQUM5SCxDQUFDO1FBRUQsSUFBSSxLQUFnRCxDQUFDO1FBQ3JELElBQUksY0FBYyxHQUFHLEtBQUssQ0FBQztRQUMzQixJQUFJLGlCQUFpQixHQUFHLEtBQUssQ0FBQztRQUM5QixNQUFNLGFBQWEsR0FBRyxHQUFHLEVBQUU7WUFDekIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUNwQixjQUFjLEdBQUcsSUFBSSxDQUFDO2dCQUN0QixJQUFJLENBQUMsd0JBQXdCLElBQUksQ0FBQyxDQUFDO1lBQ3JDLENBQUM7UUFDSCxDQUFDLENBQUM7UUFDRixNQUFNLGdCQUFnQixHQUFHLEdBQUcsRUFBRTtZQUM1QixJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztnQkFDdkIsaUJBQWlCLEdBQUcsSUFBSSxDQUFDO2dCQUN6QixJQUFJLENBQUMsMkJBQTJCLElBQUksQ0FBQyxDQUFDO1lBQ3hDLENBQUM7UUFDSCxDQUFDLENBQUM7UUFDRixJQUFJLENBQUMsd0JBQXdCLElBQUksQ0FBQyxDQUFDO1FBQ25DLElBQUksQ0FBQywyQkFBMkIsSUFBSSxDQUFDLENBQUM7UUFDdEMsTUFBTSxlQUFlLEdBQUcsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUM3RCxNQUFNLHNCQUFzQixHQUFHLGVBQWUsQ0FBQyxJQUFJLENBQ2pELENBQUMsTUFBTSxFQUFFLEVBQUU7WUFDVCxhQUFhLEVBQUUsQ0FBQztZQUNoQixnQkFBZ0IsRUFBRSxDQUFDO1lBQ25CLE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUMsRUFDRCxDQUFDLEdBQUcsRUFBRSxFQUFFO1lBQ04sYUFBYSxFQUFFLENBQUM7WUFDaEIsZ0JBQWdCLEVBQUUsQ0FBQztZQUNuQixNQUFNLEdBQUcsQ0FBQztRQUNaLENBQUMsQ0FDRixDQUFDO1FBQ0Ysc0JBQXNCLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzlDLElBQUksQ0FBQztZQUNILE9BQU8sTUFBTSxPQUFPLENBQUMsSUFBSSxDQUFDO2dCQUN4QixzQkFBc0I7Z0JBQ3RCLElBQUksT0FBTyxDQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sRUFBRSxFQUFFO29CQUNsQyxLQUFLLEdBQUcsVUFBVSxDQUNoQixHQUFHLEVBQUU7d0JBQ0gsYUFBYSxFQUFFLENBQUM7d0JBQ2hCLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsZ0JBQWdCLG9CQUFvQixJQUFJLENBQUMsaUJBQWlCLElBQUksQ0FBQyxDQUFDLENBQUM7b0JBQzFHLENBQUMsRUFDRCxJQUFJLENBQUMsaUJBQWlCLENBQ3ZCLENBQUM7Z0JBQ0osQ0FBQyxDQUFDO2FBQ0gsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztnQkFBUyxDQUFDO1lBQ1QsSUFBSSxLQUFLO2dCQUFFLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNqQyxDQUFDO0lBQ0gsQ0FBQztJQUVPLGFBQWEsQ0FBQyxNQUEwQixFQUFFLFdBQW9DO1FBQ3BGLElBQUksUUFBUSxHQUFHLFdBQVcsQ0FBQztRQUMzQixJQUFJLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxRQUFRLEVBQUUsNkJBQTZCLENBQUMsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN2RixRQUFRLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLFdBQVcsQ0FBQyxFQUFFLEVBQUUsK0NBQStDLENBQUMsQ0FBQztRQUN2RyxDQUFDO1FBRUQsSUFBSSxrQkFBMEIsQ0FBQztRQUMvQixJQUFJLENBQUM7WUFDSCxrQkFBa0IsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2hELENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxrQkFBa0IsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxXQUFXLENBQUMsRUFBRSxFQUFFLDhDQUE4QyxDQUFDLENBQUMsQ0FBQztRQUNoSSxDQUFDO1FBRUQsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDLGtCQUFrQixFQUFFLE1BQU0sQ0FBQyxHQUFHLDZCQUE2QixFQUFFLENBQUM7WUFDbEYsa0JBQWtCLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztnQkFDbEMsT0FBTyxFQUFFLEtBQUs7Z0JBQ2QsS0FBSyxFQUFFLCtDQUErQzthQUN2RCxDQUFDLENBQUM7UUFDTCxDQUFDO1FBRUQsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLGtCQUFrQixJQUFJLENBQUMsQ0FBQztJQUMxQyxDQUFDO0lBRU8sbUJBQW1CLENBQUMsS0FBeUIsRUFBRSxRQUFnQjtRQUNyRSxNQUFNLFFBQVEsR0FBNEI7WUFDeEMsT0FBTyxFQUFFLEtBQUs7WUFDZCxLQUFLLEVBQUUsUUFBUTtTQUNoQixDQUFDO1FBQ0YsSUFBSSxLQUFLLElBQUksTUFBTSxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLElBQUksd0JBQXdCLEVBQUUsQ0FBQztZQUMxRSxRQUFRLENBQUMsRUFBRSxHQUFHLEtBQUssQ0FBQztRQUN0QixDQUFDO1FBQ0QsT0FBTyxRQUFRLENBQUM7SUFDbEIsQ0FBQztJQUVPLHNCQUFzQixDQUM1QixRQUFpQixFQUNqQixXQUFtQixFQUNuQixVQUFVLElBQUksR0FBRyxFQUFVO1FBRTNCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyw4QkFBOEIsQ0FBQyxRQUFRLEVBQUUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ2xGLE9BQU8sS0FBSyxLQUFLLFNBQVMsSUFBSSxLQUFLLElBQUksV0FBVyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUN6RSxDQUFDO0lBRU8sOEJBQThCLENBQ3BDLFFBQWlCLEVBQ2pCLFdBQW1CLEVBQ25CLE9BQW9CO1FBRXBCLElBQUksUUFBUSxLQUFLLElBQUk7WUFBRSxPQUFPLENBQUMsQ0FBQztRQUNoQyxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVE7WUFBRSxPQUFPLElBQUksQ0FBQyw0QkFBNEIsQ0FBQyxRQUFRLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDbEcsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRO1lBQUUsT0FBTyxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDakcsSUFBSSxPQUFPLFFBQVEsS0FBSyxTQUFTO1lBQUUsT0FBTyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzNELElBQUksT0FBTyxRQUFRLEtBQUssV0FBVztZQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzlDLElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxPQUFPLFFBQVEsS0FBSyxVQUFVO1lBQUUsT0FBTyxTQUFTLENBQUM7UUFDckgsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRO1lBQUUsT0FBTyxTQUFTLENBQUM7UUFDbkQsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQztZQUFFLE9BQU8sU0FBUyxDQUFDO1FBRTVDLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdEIsSUFBSSxDQUFDO1lBQ0gsSUFBSSxVQUFVLEdBQUcsQ0FBQyxDQUFDO1lBQ25CLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO2dCQUM1QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO29CQUN6QyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsOEJBQThCLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBQztvQkFDMUYsSUFBSSxVQUFVLEtBQUssU0FBUzt3QkFBRSxPQUFPLFNBQVMsQ0FBQztvQkFDL0MsVUFBVSxJQUFJLFVBQVUsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7b0JBQzNDLElBQUksVUFBVSxHQUFHLFdBQVc7d0JBQUUsT0FBTyxTQUFTLENBQUM7Z0JBQ2pELENBQUM7Z0JBQ0QsT0FBTyxVQUFVLENBQUM7WUFDcEIsQ0FBQztZQUVELElBQUksYUFBYSxHQUFHLENBQUMsQ0FBQztZQUN0QixLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFtQyxDQUFDLEVBQUUsQ0FBQztnQkFDL0UsSUFBSSxPQUFPLEtBQUssS0FBSyxXQUFXLElBQUksT0FBTyxLQUFLLEtBQUssVUFBVSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVE7b0JBQUUsU0FBUztnQkFDdkcsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLDRCQUE0QixDQUFDLEdBQUcsRUFBRSxXQUFXLENBQUMsQ0FBQztnQkFDckUsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLDhCQUE4QixDQUFDLEtBQUssRUFBRSxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BGLElBQUksUUFBUSxLQUFLLFNBQVMsSUFBSSxVQUFVLEtBQUssU0FBUztvQkFBRSxPQUFPLFNBQVMsQ0FBQztnQkFDekUsVUFBVSxJQUFJLFFBQVEsR0FBRyxVQUFVLEdBQUcsQ0FBQyxHQUFHLENBQUMsYUFBYSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDdEUsYUFBYSxJQUFJLENBQUMsQ0FBQztnQkFDbkIsSUFBSSxVQUFVLEdBQUcsV0FBVztvQkFBRSxPQUFPLFNBQVMsQ0FBQztZQUNqRCxDQUFDO1lBQ0QsT0FBTyxVQUFVLENBQUM7UUFDcEIsQ0FBQztnQkFBUyxDQUFDO1lBQ1QsT0FBTyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMzQixDQUFDO0lBQ0gsQ0FBQztJQUVPLDRCQUE0QixDQUFDLFFBQWdCLEVBQUUsV0FBbUI7UUFDeEUsSUFBSSxLQUFLLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3BELElBQUksS0FBSyxHQUFHLFdBQVc7WUFBRSxPQUFPLFNBQVMsQ0FBQztRQUUxQyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ3pDLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDcEMsSUFBSSxJQUFJLEtBQUssSUFBSSxJQUFJLElBQUksS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDbkMsS0FBSyxJQUFJLENBQUMsQ0FBQztZQUNiLENBQUM7aUJBQU0sSUFBSSxJQUFJLEtBQUssSUFBSSxJQUFJLElBQUksS0FBSyxJQUFJLElBQUksSUFBSSxLQUFLLElBQUksSUFBSSxJQUFJLEtBQUssSUFBSSxJQUFJLElBQUksS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDN0YsS0FBSyxJQUFJLENBQUMsQ0FBQztZQUNiLENBQUM7aUJBQU0sSUFBSSxJQUFJLEdBQUcsSUFBSSxFQUFFLENBQUM7Z0JBQ3ZCLEtBQUssSUFBSSxDQUFDLENBQUM7WUFDYixDQUFDO1lBQ0QsSUFBSSxLQUFLLEdBQUcsV0FBVztnQkFBRSxPQUFPLFNBQVMsQ0FBQztRQUM1QyxDQUFDO1FBRUQsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0NBQ0YifQ==