diff --git a/library/agent/Hostnames.test.ts b/library/agent/Hostnames.test.ts index 41c52b32..6a54f33e 100644 --- a/library/agent/Hostnames.test.ts +++ b/library/agent/Hostnames.test.ts @@ -6,40 +6,40 @@ t.test("it works", async () => { t.same(hostnames.asArray(), []); hostnames.add("aikido.dev", 443); - t.same(hostnames.asArray(), [{ hostname: "aikido.dev", port: 443 }]); + t.same(hostnames.asArray(), [{ hostname: "aikido.dev", port: 443, hits: 1 }]); hostnames.add("aikido.dev", 80); t.same(hostnames.asArray(), [ - { hostname: "aikido.dev", port: 443 }, - { hostname: "aikido.dev", port: 80 }, + { hostname: "aikido.dev", port: 443, hits: 1 }, + { hostname: "aikido.dev", port: 80, hits: 1 }, ]); hostnames.add("google.com", 80); t.same(hostnames.asArray(), [ - { hostname: "aikido.dev", port: 443 }, - { hostname: "aikido.dev", port: 80 }, - { hostname: "google.com", port: 80 }, + { hostname: "aikido.dev", port: 443, hits: 1 }, + { hostname: "aikido.dev", port: 80, hits: 1 }, + { hostname: "google.com", port: 80, hits: 1 }, ]); hostnames.add("google.com", undefined); t.same(hostnames.asArray(), [ - { hostname: "aikido.dev", port: 80 }, - { hostname: "google.com", port: 80 }, - { hostname: "google.com", port: undefined }, + { hostname: "aikido.dev", port: 80, hits: 1 }, + { hostname: "google.com", port: 80, hits: 1 }, + { hostname: "google.com", port: undefined, hits: 1 }, ]); hostnames.add("github.com", 80); t.same(hostnames.asArray(), [ - { hostname: "google.com", port: 80 }, - { hostname: "google.com", port: undefined }, - { hostname: "github.com", port: 80 }, + { hostname: "google.com", port: 80, hits: 1 }, + { hostname: "google.com", port: undefined, hits: 1 }, + { hostname: "github.com", port: 80, hits: 1 }, ]); hostnames.add("jetbrains.com", 80); t.same(hostnames.asArray(), [ - { hostname: "google.com", port: undefined }, - { hostname: "github.com", port: 80 }, - { hostname: "jetbrains.com", port: 80 }, + { hostname: "google.com", port: undefined, hits: 1 }, + { hostname: "github.com", port: 80, hits: 1 }, + { hostname: "jetbrains.com", port: 80, hits: 1 }, ]); hostnames.clear(); @@ -52,29 +52,57 @@ t.test("it respects max size", async () => { hostnames.add("aikido.dev", 2); t.same(hostnames.asArray(), [ - { hostname: "aikido.dev", port: 1 }, - { hostname: "aikido.dev", port: 2 }, + { hostname: "aikido.dev", port: 1, hits: 1 }, + { hostname: "aikido.dev", port: 2, hits: 1 }, ]); hostnames.add("aikido.dev", 3); hostnames.add("aikido.dev", 4); t.same(hostnames.asArray(), [ - { hostname: "aikido.dev", port: 3 }, - { hostname: "aikido.dev", port: 4 }, + { hostname: "aikido.dev", port: 3, hits: 1 }, + { hostname: "aikido.dev", port: 4, hits: 1 }, ]); hostnames.add("google.com", 1); t.same(hostnames.asArray(), [ - { hostname: "aikido.dev", port: 4 }, - { hostname: "google.com", port: 1 }, + { hostname: "aikido.dev", port: 4, hits: 1 }, + { hostname: "google.com", port: 1, hits: 1 }, ]); hostnames.add("google.com", 2); t.same(hostnames.asArray(), [ - { hostname: "google.com", port: 1 }, - { hostname: "google.com", port: 2 }, + { hostname: "google.com", port: 1, hits: 1 }, + { hostname: "google.com", port: 2, hits: 1 }, + ]); +}); + +t.test("it tracks hits", async () => { + const hostnames = new Hostnames(3); + + hostnames.add("aikido.dev", 443); + hostnames.add("aikido.dev", 443); + t.same(hostnames.asArray(), [{ hostname: "aikido.dev", port: 443, hits: 2 }]); + + hostnames.add("aikido.dev", 80); + t.same(hostnames.asArray(), [ + { hostname: "aikido.dev", port: 443, hits: 2 }, + { hostname: "aikido.dev", port: 80, hits: 1 }, + ]); + + hostnames.add("google.com", 80); + t.same(hostnames.asArray(), [ + { hostname: "aikido.dev", port: 443, hits: 2 }, + { hostname: "aikido.dev", port: 80, hits: 1 }, + { hostname: "google.com", port: 80, hits: 1 }, + ]); + + hostnames.add("aikido.dev", 443); + t.same(hostnames.asArray(), [ + { hostname: "aikido.dev", port: 443, hits: 3 }, + { hostname: "aikido.dev", port: 80, hits: 1 }, + { hostname: "google.com", port: 80, hits: 1 }, ]); }); diff --git a/library/agent/Hostnames.ts b/library/agent/Hostnames.ts index 51e64364..2d09d942 100644 --- a/library/agent/Hostnames.ts +++ b/library/agent/Hostnames.ts @@ -1,15 +1,29 @@ -type Ports = Set; +type Ports = Map; export class Hostnames { private map: Map = new Map(); constructor(private readonly maxEntries: number = 200) {} + private portKey(port: number | undefined) { + return port === undefined ? "__NO_PORT__" : port.toString(); + } + + private keyToPort(key: string) { + return key === "__NO_PORT__" ? undefined : parseInt(key, 10); + } + add(hostname: string, port: number | undefined) { + const portKey = this.portKey(port); if (!this.map.has(hostname)) { - this.map.set(hostname, new Set([port])); + this.map.set(hostname, new Map([[portKey, 1]])); } else { - this.map.get(hostname)?.add(port); + const ports = this.map.get(hostname) as Ports; + if (ports.has(portKey)) { + ports.set(portKey, ports.get(portKey)! + 1); + } else { + ports.set(portKey, 1); + } } if (this.length > this.maxEntries) { @@ -18,8 +32,10 @@ export class Hostnames { const ports: Ports = this.map.get(firstAdded) as Ports; if (ports.size > 1) { - const firstPort = ports.values().next().value; - ports.delete(firstPort); + const firstPort = ports.keys().next().value; + if (firstPort) { + ports.delete(firstPort); + } } else { this.map.delete(firstAdded); } @@ -36,7 +52,13 @@ export class Hostnames { asArray() { return Array.from(this.map.entries()).flatMap(([hostname, ports]) => - Array.from(ports).map((port) => ({ hostname, port })) + Array.from(ports.entries()).map(([port, hits]) => { + return { + hostname, + port: this.keyToPort(port), + hits, + }; + }) ); }