This commit is contained in:
Skillz
2020-03-14 11:03:56 -04:00
4 changed files with 83 additions and 12 deletions

3
module/asyncutil.ts Normal file
View File

@@ -0,0 +1,3 @@
export function sleep (timeout: number) {
return new Promise(resolve => setTimeout(resolve, timeout));
}

View File

@@ -1,19 +1,27 @@
import Client from "../module/client.ts"
import { RequestMethod } from "../types/fetch.ts"
import { Ratelimiter } from './ratelimiter';
// type RequestBody = string | Blob | ArrayBufferView | ArrayBuffer | FormData | URLSearchParams | null | undefined
export default class DiscordRequestManager {
public ratelimiter = new Ratelimiter();
constructor(public client: Client) {
this.client = client
}
async get(url: string, body?: unknown) {
const response = await fetch(this.resolveURL(url), {
headers: this.getDiscordHeaders(),
body: body ? JSON.stringify(body) : undefined
})
return await response.json()
return this.runMethod(RequestMethod.Get, url, body);
}
protected async addBucket (headers: Headers) {
this.ratelimiter.addBucket(headers.get('X-RateLimit-Bucket')!, {
retryAfter: parseInt(headers.get('X-RateLimit-Retry-After')!),
limit: parseInt(headers.get('X-RateLimit-Limit')!),
remaining: parseInt(headers.get('X-RateLimit-Remaining')!),
reset: parseInt(headers.get('X-RateLimit-Reset')!)
});
}
async post(url: string, body?: unknown) {
@@ -32,16 +40,17 @@ export default class DiscordRequestManager {
return this.runMethod(RequestMethod.Put, url, body)
}
async runMethod(method: RequestMethod, url: string, body?: unknown) {
const response = await fetch(this.resolveURL(url), {
protected async baseCreateRequestForMethod (method: RequestMethod, url: string, body?: unknown) {
return fetch(this.resolveURL(url), {
method,
headers: this.getDiscordHeaders(),
body: body ? JSON.stringify(body) : undefined
})
}
const json = await response.json()
return json
async runMethod(method: RequestMethod, url: string, body?: unknown) {
const response = await this.baseCreateRequestForMethod(method, url, body);
return response.json();
}
// A hook for the RouteAwareRequestManager to override URLs.

View File

@@ -1,3 +1,41 @@
import { sleep } from './asyncutil';
export interface Ratelimit {
retryAfter: number;
limit: number;
remaining: number;
reset: number;
}
export class Ratelimiter {
}
public buckets: Record<string, Ratelimit> = {};
awaitRatelimit (ratelimit: Ratelimit): Promise<unknown> {
if (ratelimit.remaining === 0) {
return sleep(ratelimit.retryAfter);
}
return Promise.resolve();
}
addBucket (bucket: string, ratelimit: Ratelimit) {
if (this.buckets[bucket]) {
return;
}
// Otherwise, add this ratelimit to the registry.
this.buckets[bucket] = ratelimit;
}
async awaitBucket (bucket: string) {
if (this.buckets[bucket]) {
// POSSIBLE MEMORY LEAK: Some buckets might never get cleaned up.
await this.awaitRatelimit(this.buckets[bucket]);
// IIRC, we avoid `delete` so v8 doesn't deoptimize this?
this.buckets[bucket] = undefined as any;
}
return Promise.resolve();
}
}

View File

@@ -2,8 +2,13 @@ import DiscordRequestManager from "./discord-request-manager.ts"
import Client from "./client.ts"
import { resolveURLs } from "./url.ts"
import { baseEndpoints } from "../constants/discord.ts"
import { Ratelimit, Ratelimiter } from './ratelimiter';
import { RequestMethod } from '../types/fetch';
export class RouteAwareDiscordRequestManager extends DiscordRequestManager {
protected currentRatelimit?: Ratelimit;
public ratelimiter = new Ratelimiter();
constructor(public client: Client, public routeName: string) {
super(client)
}
@@ -11,6 +16,22 @@ export class RouteAwareDiscordRequestManager extends DiscordRequestManager {
protected resolveURL(url: string) {
return resolveURLs(baseEndpoints.BASE_URL, this.routeName, url)
}
async runMethod (method: RequestMethod, url: string, body?: unknown) {
if (this.currentRatelimit) {
await this.ratelimiter.awaitRatelimit(this.currentRatelimit);
}
const response = await this.baseCreateRequestForMethod(method, url, body);
// Capture the ratelimit from this request in our cute little store.
return response.json();
}
protected createRatelimitFromRequest (request: Request) {
}
}
export class RoutedDiscordRequestManager {