Browse Source

Use `WebSocketPool` for raw WebSocket connections

master
buttercat1791 8 months ago
parent
commit
c79b844f65
  1. 20
      src/lib/components/EventInput.svelte
  2. 15
      src/lib/ndk.ts
  3. 35
      src/lib/utils/community_checker.ts
  4. 70
      src/lib/utils/relayDiagnostics.ts

20
src/lib/components/EventInput.svelte

@ -22,6 +22,7 @@
import { activeInboxRelays, activeOutboxRelays } from "$lib/ndk"; import { activeInboxRelays, activeOutboxRelays } from "$lib/ndk";
import { Button } from "flowbite-svelte"; import { Button } from "flowbite-svelte";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { WebSocketPool } from "$lib/data_structures/websocket_pool";
let kind = $state<number>(30023); let kind = $state<number>(30023);
let tags = $state<[string, string][]>([]); let tags = $state<[string, string][]>([]);
@ -306,17 +307,14 @@
for (const relayUrl of relays) { for (const relayUrl of relays) {
try { try {
const ws = new WebSocket(relayUrl); const ws = await WebSocketPool.instance.acquire(relayUrl);
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
ws.close(); WebSocketPool.instance.release(ws);
reject(new Error("Timeout")); reject(new Error("Timeout"));
}, 5000); }, 5000);
ws.onopen = () => {
ws.send(JSON.stringify(["EVENT", signedEvent]));
};
ws.onmessage = (e) => { ws.onmessage = (e) => {
const [type, id, ok, message] = JSON.parse(e.data); const [type, id, ok, message] = JSON.parse(e.data);
if (type === "OK" && id === signedEvent.id) { if (type === "OK" && id === signedEvent.id) {
@ -324,20 +322,14 @@
if (ok) { if (ok) {
published = true; published = true;
relaysPublished.push(relayUrl); relaysPublished.push(relayUrl);
ws.close(); WebSocketPool.instance.release(ws);
resolve(); resolve();
} else { } else {
ws.close(); WebSocketPool.instance.release(ws);
reject(new Error(message)); reject(new Error(message));
} }
} }
}; };
ws.onerror = () => {
clearTimeout(timeout);
ws.close();
reject(new Error("WebSocket error"));
};
}); });
if (published) break; if (published) break;
} catch (e) { } catch (e) {

15
src/lib/ndk.ts

@ -21,6 +21,7 @@ export { testRelayConnection };
import { userStore } from "./stores/userStore.ts"; import { userStore } from "./stores/userStore.ts";
import { userPubkey } from "./stores/authStore.Svelte.ts"; import { userPubkey } from "./stores/authStore.Svelte.ts";
import { startNetworkStatusMonitoring, stopNetworkStatusMonitoring } from "./stores/networkStore.ts"; import { startNetworkStatusMonitoring, stopNetworkStatusMonitoring } from "./stores/networkStore.ts";
import { WebSocketPool } from "./data_structures/websocket_pool.ts";
export const ndkInstance: Writable<NDK> = writable(); export const ndkInstance: Writable<NDK> = writable();
export const ndkSignedIn = writable(false); export const ndkSignedIn = writable(false);
@ -199,16 +200,14 @@ export function checkWebSocketSupport(): void {
// Test if secure WebSocket is supported // Test if secure WebSocket is supported
try { try {
const testWs = new WebSocket("wss://echo.websocket.org"); WebSocketPool.instance.acquire("wss://echo.websocket.org").then((ws) => {
testWs.onopen = () => {
console.debug("[NDK.ts] ✓ Secure WebSocket (wss://) is supported"); console.debug("[NDK.ts] ✓ Secure WebSocket (wss://) is supported");
testWs.close(); WebSocketPool.instance.release(ws);
}; }).catch((_) => {
testWs.onerror = () => {
console.warn("[NDK.ts] ✗ Secure WebSocket (wss://) may not be supported"); console.warn("[NDK.ts] ✗ Secure WebSocket (wss://) may not be supported");
}; });
} catch (error) { } catch {
console.warn("[NDK.ts] ✗ WebSocket test failed:", error); console.warn("[NDK.ts] ✗ WebSocket test failed");
} }
} }

35
src/lib/utils/community_checker.ts

@ -1,4 +1,5 @@
import { communityRelays } from "$lib/consts"; import { communityRelays } from "$lib/consts";
import { WebSocketPool } from "../data_structures/websocket_pool.ts";
import { RELAY_CONSTANTS, SEARCH_LIMITS } from "./search_constants"; import { RELAY_CONSTANTS, SEARCH_LIMITS } from "./search_constants";
// Cache for pubkeys with kind 1 events on communityRelay // Cache for pubkeys with kind 1 events on communityRelay
@ -16,37 +17,31 @@ export async function checkCommunity(pubkey: string): Promise<boolean> {
// Try each community relay until we find one that works // Try each community relay until we find one that works
for (const relayUrl of communityRelays) { for (const relayUrl of communityRelays) {
try { try {
const ws = new WebSocket(relayUrl); const ws = await WebSocketPool.instance.acquire(relayUrl);
const result = await new Promise<boolean>((resolve) => { const result = await new Promise<boolean>((resolve) => {
ws.onopen = () => { ws.send(
ws.send( JSON.stringify([
JSON.stringify([ "REQ",
"REQ", RELAY_CONSTANTS.COMMUNITY_REQUEST_ID,
RELAY_CONSTANTS.COMMUNITY_REQUEST_ID, {
{ kinds: RELAY_CONSTANTS.COMMUNITY_REQUEST_KINDS,
kinds: RELAY_CONSTANTS.COMMUNITY_REQUEST_KINDS, authors: [pubkey],
authors: [pubkey], limit: SEARCH_LIMITS.COMMUNITY_CHECK,
limit: SEARCH_LIMITS.COMMUNITY_CHECK, },
}, ]),
]), );
);
};
ws.onmessage = (event) => { ws.onmessage = (event) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
if (data[0] === "EVENT" && data[2]?.kind === 1) { if (data[0] === "EVENT" && data[2]?.kind === 1) {
communityCache.set(pubkey, true); communityCache.set(pubkey, true);
ws.close(); WebSocketPool.instance.release(ws);
resolve(true); resolve(true);
} else if (data[0] === "EOSE") { } else if (data[0] === "EOSE") {
communityCache.set(pubkey, false); communityCache.set(pubkey, false);
ws.close(); WebSocketPool.instance.release(ws);
resolve(false); resolve(false);
} }
}; };
ws.onerror = () => {
ws.close();
resolve(false);
};
}); });
if (result) { if (result) {

70
src/lib/utils/relayDiagnostics.ts

@ -1,3 +1,4 @@
import { WebSocketPool } from "../data_structures/websocket_pool.ts";
import { activeInboxRelays, activeOutboxRelays } from "../ndk.ts"; import { activeInboxRelays, activeOutboxRelays } from "../ndk.ts";
import { TIMEOUTS } from "./search_constants.ts"; import { TIMEOUTS } from "./search_constants.ts";
import { get } from "svelte/store"; import { get } from "svelte/store";
@ -13,72 +14,37 @@ export interface RelayDiagnostic {
/** /**
* Tests connection to a single relay * Tests connection to a single relay
*/ */
export function testRelay(url: string): Promise<RelayDiagnostic> { export async function testRelay(url: string): Promise<RelayDiagnostic> {
const startTime = Date.now(); const startTime = Date.now();
const ws = await WebSocketPool.instance.acquire(url);
return new Promise((resolve) => { return new Promise((resolve) => {
const ws = new WebSocket(url);
let resolved = false;
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
if (!resolved) { WebSocketPool.instance.release(ws);
resolved = true; resolve({
ws.close(); url,
resolve({ connected: false,
url, requiresAuth: false,
connected: false, error: "Connection timeout",
requiresAuth: false, responseTime: Date.now() - startTime,
error: "Connection timeout", });
responseTime: Date.now() - startTime,
});
}
}, TIMEOUTS.RELAY_DIAGNOSTICS); }, TIMEOUTS.RELAY_DIAGNOSTICS);
ws.onopen = () => { ws.onmessage = (event) => {
if (!resolved) { const data = JSON.parse(event.data);
resolved = true; if (data[0] === "NOTICE" && data[1]?.includes("auth-required")) {
clearTimeout(timeout); clearTimeout(timeout);
ws.close(); WebSocketPool.instance.release(ws);
resolve({ resolve({
url, url,
connected: true, connected: true,
requiresAuth: false, requiresAuth: true,
responseTime: Date.now() - startTime, responseTime: Date.now() - startTime,
}); });
} }
}; }
ws.onerror = () => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
resolve({
url,
connected: false,
requiresAuth: false,
error: "WebSocket error",
responseTime: Date.now() - startTime,
});
}
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data[0] === "NOTICE" && data[1]?.includes("auth-required")) {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
ws.close();
resolve({
url,
connected: true,
requiresAuth: true,
responseTime: Date.now() - startTime,
});
}
}
};
}); });
} }
/** /**

Loading…
Cancel
Save