-
Notifications
You must be signed in to change notification settings - Fork 2
/
net.js
153 lines (120 loc) · 3.51 KB
/
net.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import * as network from "node:net"
import fetch from "node-fetch"
import isValidHostname from "is-valid-hostname"
import util from "./util.js"
import xml from "./xml.js"
var error = null
var hostname = {
ip: null,
port: null
}
// https://dmitripavlutin.com/timeout-fetch-request/
async function fetchWithTimeout(resource, options = {}) {
let { timeout = 60000 } = options
let controller = new AbortController()
let id = setTimeout(() => controller.abort(), timeout)
let response = await fetch(resource, {
...options,
signal: controller.signal
})
clearTimeout(id)
return response
}
function sanitizeHostname(hostname) {
if (!hostname.length) {
return null
}
let split = hostname.split(":")
if (split.length < 0 || split.length > 2) {
return null
}
if (!network.isIPv4(split[0])) {
// Perhaps its a hostname?
if (!isValidHostname(split[0])) {
return null
}
}
let ip = split[0]
let port = 64989
if (split.length == 2) {
port = parseInt(split[1])
if (isNaN(port)) {
return null
}
if (port < 0 || port > 65535) {
return null
}
}
return { ip, port }
}
function formatHostname(hostname) {
return hostname.ip + (hostname.port == 64989 ? "" : ":" + hostname.port)
}
function getFormattedHostname() {
return formatHostname(hostname)
}
function isConnected() {
return hostname.ip !== null && hostname.port !== null
}
// This function sucks (and is a bit of a hack)
function fault() {
let result = error
error = null
return result
}
// TODO: Wouldn't this be better as a constructor for a class?
async function connect(ip, port) {
// We do this instead of send() to get a more verbose output upon failure
try {
let response = await fetchWithTimeout(`http://${ip}:${port}/`, {
method: "POST",
body: xml.generateEnvelope([{ "HelloWorld": null }]),
timeout: 60000,
headers: {
"Content-Type": "application/xml"
}
})
let text = await response.text()
let parsed = xml.parseEnvelope(text)
if (!parsed.success) {
throw `Error in response (${parsed.error})`
}
} catch (e) {
error = `${util.red("Failed to connect to RCCService instance")}: ${e}`
return false
}
hostname = { ip, port }
util.setTerminalTitle(`rcctalk - ${getFormattedHostname()}`)
return true
}
function disconnect() {
hostname = { ip: null, port: null }
util.setTerminalTitle("rcctalk")
}
async function send(data) {
let envelope = xml.generateEnvelope(data)
let response = null
try {
response = await fetchWithTimeout(`http://${hostname.ip}:${hostname.port}/`, {
method: "POST",
body: envelope,
timeout: 60000,
headers: {
"Content-Type": "application/xml"
}
})
} catch (e) {
error = `${util.red("Failed to send data to RCCService instance")}: ${e}`
response = null
}
if (response != null) {
let parsed = xml.parseEnvelope(await response.text())
if (parsed.error) {
error = `${util.red("Error")}: ${parsed.error}`
return null
}
response = parsed.data
}
return response
}
export default { sanitizeHostname, formatHostname, getFormattedHostname, isConnected, fault, connect, disconnect, send }