mirror of
https://github.com/WorldEditAxe/eaglerproxy.git
synced 2024-11-08 06:56:03 -08:00
fix some bugs
This commit is contained in:
parent
4b465802d1
commit
26275fa0d7
|
@ -15,8 +15,8 @@ export const config: Config = {
|
|||
cache: {
|
||||
useCache: true,
|
||||
folderName: "skinCache",
|
||||
skinCacheLifetime: 60 * 1000,
|
||||
skinCachePruneInterval: 5000,
|
||||
skinCacheLifetime: 60 * 60 * 1000,
|
||||
skinCachePruneInterval: 10 * 60 * 1000,
|
||||
},
|
||||
},
|
||||
motd: true
|
||||
|
|
|
@ -20,7 +20,8 @@ export default class DiskDB<T extends any> {
|
|||
|
||||
public async filter(f: (v: T) => boolean) {
|
||||
for (const file of await fs.readdir(this.folder)) {
|
||||
if (!f(this.decoder(await fs.readFile(file)))) await fs.rm(file);
|
||||
const fp = path.join(this.folder, file);
|
||||
if (!f(this.decoder(await fs.readFile(fp)))) await fs.rm(fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
63
src/proxy/ratelimit/ExponentialBackoffRequestController.ts
Normal file
63
src/proxy/ratelimit/ExponentialBackoffRequestController.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
const wait = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
||||
|
||||
export default class ExponentialBackoffRequestController {
|
||||
public queue: Task[];
|
||||
public flushQueueAfterTries: number;
|
||||
public baseDelay: number;
|
||||
ended: boolean;
|
||||
aborted: boolean;
|
||||
|
||||
constructor(baseDelay: number = 3000, triesBeforeFlush: number = 10) {
|
||||
this.flushQueueAfterTries = triesBeforeFlush;
|
||||
this.baseDelay = baseDelay;
|
||||
this.queue = [];
|
||||
this.ended = false;
|
||||
this.aborted = false;
|
||||
setTimeout(() => this.tick(), 0);
|
||||
}
|
||||
|
||||
private async tick() {
|
||||
while (true) {
|
||||
if (this.ended) break;
|
||||
for (const task of this.queue) {
|
||||
if (this.ended || this.aborted) break;
|
||||
let times = 0,
|
||||
breakOut = false;
|
||||
while (true) {
|
||||
try {
|
||||
await task();
|
||||
break;
|
||||
} catch (err) {
|
||||
times++;
|
||||
await wait(this.baseDelay * 2 ** times);
|
||||
if (times > this.flushQueueAfterTries) {
|
||||
this.queue.forEach((task) => task(new Error("Controller overload!")));
|
||||
breakOut = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (breakOut) break;
|
||||
}
|
||||
if (this.aborted) this.aborted = false;
|
||||
this.queue = [];
|
||||
await wait(1);
|
||||
}
|
||||
}
|
||||
|
||||
public end() {
|
||||
this.ended = true;
|
||||
}
|
||||
|
||||
public flush() {
|
||||
this.aborted = false;
|
||||
this.queue.forEach((task) => task(new Error("Aborted")));
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
public queueTask(task: Task): void {
|
||||
this.queue.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
type Task = (err?: object) => void | Promise<void>;
|
|
@ -11,6 +11,7 @@ import { Logger } from "../../logger.js";
|
|||
import fetch from "node-fetch";
|
||||
import Jimp from "jimp";
|
||||
import { ImageEditor } from "./ImageEditor.js";
|
||||
import ExponentialBackoffRequestController from "../ratelimit/ExponentialBackoffRequestController.js";
|
||||
|
||||
// TODO: convert all functions to use MineProtocol's UUID manipulation functions
|
||||
|
||||
|
@ -81,7 +82,10 @@ export namespace EaglerSkins {
|
|||
return new Promise<Buffer>(async (res, rej) => {
|
||||
const skin = await fetch(skinUrl);
|
||||
if (skin.status != 200) {
|
||||
rej(`Tried to fetch ${skinUrl}, got HTTP ${skin.status} instead!`);
|
||||
rej({
|
||||
url: skinUrl,
|
||||
status: skin.status,
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
res(Buffer.from(await skin.arrayBuffer()));
|
||||
|
@ -89,6 +93,20 @@ export namespace EaglerSkins {
|
|||
});
|
||||
}
|
||||
|
||||
export function safeDownloadSkin(skinUrl: string, backoff: ExponentialBackoffRequestController): Promise<Buffer> {
|
||||
return new Promise((res, rej) => {
|
||||
backoff.queueTask(async (err) => {
|
||||
if (err) return rej(err);
|
||||
try {
|
||||
res(await downloadSkin(skinUrl));
|
||||
} catch (err) {
|
||||
if (err.status == 429) throw new Error("Ratelimited!");
|
||||
else rej("Unexpected HTTP status code: " + err.status);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function readClientDownloadSkinRequestPacket(message: Buffer): ClientDownloadSkinRequest {
|
||||
const ret: ClientDownloadSkinRequest = {
|
||||
id: null,
|
||||
|
|
|
@ -11,11 +11,13 @@ import { SCChannelMessagePacket } from "../packets/channel/SCChannelMessage.js";
|
|||
import { EaglerSkins } from "./EaglerSkins.js";
|
||||
import { ImageEditor } from "./ImageEditor.js";
|
||||
import { MineProtocol } from "../Protocol.js";
|
||||
import ExponentialBackoffRequestController from "../ratelimit/ExponentialBackoffRequestController.js";
|
||||
|
||||
export class SkinServer {
|
||||
public allowedSkinDomains: string[];
|
||||
public cache: DiskDB<CachedSkin>;
|
||||
public proxy: Proxy;
|
||||
public backoffController: ExponentialBackoffRequestController;
|
||||
public usingNative: boolean;
|
||||
public usingCache: boolean;
|
||||
private _logger: Logger;
|
||||
|
@ -29,13 +31,14 @@ export class SkinServer {
|
|||
cacheFolder,
|
||||
(v) => exportCachedSkin(v),
|
||||
(b) => readCachedSkin(b),
|
||||
(k) => k
|
||||
(k) => k.replaceAll("-", "")
|
||||
);
|
||||
}
|
||||
this.proxy = proxy ?? PROXY;
|
||||
this.usingCache = useCache;
|
||||
this.usingNative = native;
|
||||
this.lifetime = cacheLifetime;
|
||||
this.backoffController = new ExponentialBackoffRequestController();
|
||||
this._logger = new Logger("SkinServer");
|
||||
this._logger.info("Started EaglercraftX skin server.");
|
||||
if (useCache) this.deleteTask = setInterval(async () => await this.cache.filter((ent) => Date.now() < ent.expires), sweepInterval);
|
||||
|
@ -88,17 +91,16 @@ export class SkinServer {
|
|||
skin = null;
|
||||
if (this.usingCache) {
|
||||
(cacheHit = await this.cache.get(parsedPacket_1.uuid)), (skin = cacheHit != null ? cacheHit.data : null);
|
||||
|
||||
if (!skin) {
|
||||
this._logger.info("cache miss: getting skin");
|
||||
const fetched = await EaglerSkins.downloadSkin(parsedPacket_1.url);
|
||||
const fetched = await EaglerSkins.safeDownloadSkin(parsedPacket_1.url, this.backoffController);
|
||||
skin = fetched;
|
||||
await this.cache.set(parsedPacket_1.uuid, {
|
||||
uuid: parsedPacket_1.uuid,
|
||||
expires: Date.now() + this.lifetime,
|
||||
data: fetched,
|
||||
});
|
||||
this._logger.info("downloaded skin, saved to cache");
|
||||
} else this._logger.info("hit success: got skin");
|
||||
}
|
||||
} else {
|
||||
skin = await EaglerSkins.downloadSkin(parsedPacket_1.url);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user