UNPKG

@mikkel-ol/tunnelmole

Version:

Tunnelmole, an open source ngrok alternative. Instant public URLs for any http/https based application. Available as a command line application or as an NPM dependency for your code. Stable and maintained. Good test coverage. Works behind firewalls

101 lines (80 loc) 3.54 kB
import { Options } from "../options.js"; import InitialiseMessage from "../messages/initialise-message.js"; import { initialise } from "../messages/types.js"; import { getClientId } from "../identity/client-id-service.js"; import { getApiKey } from "../identity/api-key-service.js"; import validator from "validator"; import { messageHandlers } from "../../message-handlers.js"; import HostipWebSocket from "./host-ip-websocket.js"; import config from "../../config.js"; import { getConnectionInfo } from "./connection-info-service.js"; import log from "../logging/log.js"; const connect = (options: Options): HostipWebSocket => { const websocket = new HostipWebSocket(config.hostip.endpoint); const websocketIsReady = websocket.readyState === 1; const sendInitialiseMessage = async () => { log("Sending initialise message"); // Give the server basic information on the Node version and if we are using CLI or not (in which case, tunnelmole is being run from JS code) const connectionInfo = await getConnectionInfo(); const initialiseMessage: InitialiseMessage = { type: initialise, clientId: await getClientId(), connectionInfo, }; // Set api key if we have one available const apiKey = await getApiKey(); if (typeof apiKey === "string") { initialiseMessage.apiKey = apiKey; } // Handle passed subdomain param if present let domain = options.domain ?? undefined; if (typeof domain === "string") { // Remove protocols in case they were passed by mistake as the "domain" domain = domain.replace("http://", ""); domain = domain.replace("https://", ""); if (!validator.isURL(domain)) { console.info( "Invalid domain name passed, please use the format mydomain.tunnelmole.net", ); return Promise.resolve(); } const domainParts = domain.split("."); const subdomain = domainParts[0]; initialiseMessage.subdomain = subdomain; } websocket.sendMessage(initialiseMessage); }; // There seems to be a bug where on a second run, the websocket is re-used and is in a ready state // Send initialise message now if this is the case, otherwise set the open event to trigger the initialise message if (websocketIsReady) { sendInitialiseMessage(); } else { websocket.on("open", sendInitialiseMessage); // Could potentially improve this to websocket.on('once'), but it will need testing } websocket.on("message", (text: string) => { const message = JSON.parse(text); if (typeof message.type !== "string") { console.error("Invalid message, type is missing or invalid"); } // Errors should be handled in the handler itself. If it gets here it will be thrown. if (typeof messageHandlers[message.type] !== "function") { console.error("Handler not found for message type " + message.type); } const handler = messageHandlers[message.type]; handler(message, websocket, options); }); // Log messages if debug is enabled // Potential improvement: Combine with the other websocket.on('message') above. Requires testing, working well for now websocket.on("message", (text: string) => { const message = JSON.parse(text); log(Date.now() + " Received " + message.type + " message:", "info"); log(message, "info"); }); // Log errors websocket.on("error", (error) => { log(Date.now() + "Caught an error:", "error"); console.error(error); }); return websocket; }; export { connect };