UNPKG

@achingbrain/nat-port-mapper

Version:
98 lines 3.41 kB
import { setMaxListeners } from 'node:events'; import { NS_SOAP } from './constants.js'; import { fetchXML } from './fetch.js'; import { getNamespace } from './utils.js'; export class Device { service; shutdownController; constructor(service) { this.service = service; // used to terminate network operations on shutdown this.shutdownController = new AbortController(); setMaxListeners(Infinity, this.shutdownController.signal); } async run(device, action, args, options) { const info = this.getService(device); const requestBody = `<?xml version="1.0"?> <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <s:Body> <u:${action} xmlns:u="${info.service}">${args.map((args) => ` <${args[0]}>${args[1] ?? ''}</${args[0]}>`).join('')} </u:${action}> </s:Body> </s:Envelope>`; const responseBody = await fetchXML(new URL(info.controlURL), { ...options, method: 'POST', headers: { 'Content-Type': 'text/xml', 'Content-Length': requestBody.length.toString(), SOAPAction: JSON.stringify(info.service + '#' + action) }, body: requestBody }); const soapns = getNamespace(responseBody, NS_SOAP); return responseBody[soapns + 'Body']; } getService(type) { const [service] = this.parseDescription(this.service.details).services .filter(device => device.serviceType === type); // Use the first available service if (service?.controlURL == null || service.SCPDURL == null) { throw new Error('Service not found'); } const base = new URL(this.service.location); function addPrefix(u) { let uri; try { uri = new URL(u); } catch (err) { // Is only the path of the URL uri = new URL(u, base.href); } uri.host = uri.host ?? base.host; uri.protocol = uri.protocol ?? base.protocol; return uri.toString(); } return { service: service.serviceType, SCPDURL: addPrefix(service.SCPDURL), controlURL: addPrefix(service.controlURL) }; } parseDescription(info) { const services = []; const devices = []; function toArray(item) { return Array.isArray(item) ? item : [item]; } function traverseServices(service) { if (service == null) { return; } services.push(service); } function traverseDevices(device) { if (device == null) { return; } devices.push(device); if (device.deviceList?.device != null) { toArray(device.deviceList.device).forEach(traverseDevices); } if (device.serviceList?.service != null) { toArray(device.serviceList.service).forEach(traverseServices); } } traverseDevices(info.device); return { services, devices }; } close() { this.shutdownController.abort(); } } //# sourceMappingURL=device.js.map