pinokiod 8.2.31 → 8.2.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -30,6 +30,14 @@ function openPath(label, port, to) {
|
|
|
30
30
|
return `/anywhere/peers/${encodeURIComponent(label)}/open?port=${Number(port)}&to=${encodeURIComponent(to || '/')}`
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// The headers go out as the incoming request's headers. Setting them in http-proxy's proxyReq
|
|
34
|
+
// hook instead would throw: with the proxy agent the hook fires once the CONNECT completes, and
|
|
35
|
+
// by then the outgoing headers are already written.
|
|
36
|
+
function mark(req) {
|
|
37
|
+
for (const name of STRIPPED_HEADERS) delete req.headers[name]
|
|
38
|
+
req.headers[FORWARDED_HEADER] = '1'
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
class Forwarders {
|
|
34
42
|
// proxyUrl: () => 'http://127.0.0.1:<port>' or null while the daemon is down
|
|
35
43
|
// serverPort: () => pinokiod's own port
|
|
@@ -74,8 +82,6 @@ class Forwarders {
|
|
|
74
82
|
const label = String(dnsName).split('.')[0]
|
|
75
83
|
const target = `${this.scheme}://${dnsName}:${port}`
|
|
76
84
|
const proxy = httpProxy.createProxyServer({ target, agent: this.agent(), changeOrigin: true, secure: true, ws: true })
|
|
77
|
-
proxy.on('proxyReq', (proxyReq) => { proxyReq.setHeader(FORWARDED_HEADER, '1') })
|
|
78
|
-
proxy.on('proxyReqWs', (proxyReq) => { proxyReq.setHeader(FORWARDED_HEADER, '1') })
|
|
79
85
|
proxy.on('error', (error, req, res) => {
|
|
80
86
|
const message = `Could not reach ${dnsName}: ${error && error.message ? error.message : error}`
|
|
81
87
|
if (res && typeof res.writeHead === 'function' && !res.headersSent) {
|
|
@@ -85,7 +91,6 @@ class Forwarders {
|
|
|
85
91
|
res.destroy()
|
|
86
92
|
}
|
|
87
93
|
})
|
|
88
|
-
const strip = (req) => { for (const name of STRIPPED_HEADERS) delete req.headers[name] }
|
|
89
94
|
const server = http.createServer((req, res) => {
|
|
90
95
|
const url = new URL(req.url, 'http://127.0.0.1')
|
|
91
96
|
if (url.pathname === OPEN_PATH) {
|
|
@@ -100,11 +105,11 @@ class Forwarders {
|
|
|
100
105
|
res.end()
|
|
101
106
|
return
|
|
102
107
|
}
|
|
103
|
-
|
|
108
|
+
mark(req)
|
|
104
109
|
proxy.web(req, res)
|
|
105
110
|
})
|
|
106
111
|
server.on('upgrade', (req, socket, head) => {
|
|
107
|
-
|
|
112
|
+
mark(req)
|
|
108
113
|
proxy.ws(req, socket, head)
|
|
109
114
|
})
|
|
110
115
|
// every connection on both sides, upgraded ones included, so stopAll can end a stream a
|
|
@@ -138,7 +143,7 @@ class Forwarders {
|
|
|
138
143
|
}
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
// Resolves { status, body }; rejects on a transport failure or when the whole
|
|
146
|
+
// Resolves { status, headers, raw, body }; rejects on a transport failure or when the whole
|
|
142
147
|
// request, the proxy's CONNECT included, exceeds the deadline.
|
|
143
148
|
function requestThroughProxy(agent, url, { method = 'GET', headers = {}, body = null, timeoutMs = 4000 } = {}) {
|
|
144
149
|
return new Promise((resolve, reject) => {
|
|
@@ -150,7 +155,11 @@ function requestThroughProxy(agent, url, { method = 'GET', headers = {}, body =
|
|
|
150
155
|
const req = lib.request(target, { method, headers, agent }, (res) => {
|
|
151
156
|
const chunks = []
|
|
152
157
|
res.on('data', (chunk) => chunks.push(chunk))
|
|
153
|
-
res.on('end', () => {
|
|
158
|
+
res.on('end', () => {
|
|
159
|
+
clearTimeout(deadline)
|
|
160
|
+
const raw = Buffer.concat(chunks)
|
|
161
|
+
resolve({ status: res.statusCode, headers: res.headers, raw, body: raw.toString('utf8') })
|
|
162
|
+
})
|
|
154
163
|
res.on('error', (error) => { clearTimeout(deadline); reject(error) })
|
|
155
164
|
})
|
|
156
165
|
req.on('error', (error) => { clearTimeout(deadline); reject(error) })
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -1069,7 +1069,7 @@ class Server {
|
|
|
1069
1069
|
anywherePeerAllowed(req) {
|
|
1070
1070
|
if (!this.isLocalRequest(req)) return false
|
|
1071
1071
|
const origin = req.get("Origin")
|
|
1072
|
-
if (!origin) return true
|
|
1072
|
+
if (!origin || origin === "https://pinokio.co") return true
|
|
1073
1073
|
try { return new URL(origin).host.toLowerCase() === String(req.get("host")).toLowerCase() } catch (_) { return false }
|
|
1074
1074
|
}
|
|
1075
1075
|
// the reason the peer routes cannot work right now, or null
|
|
@@ -1096,6 +1096,7 @@ class Server {
|
|
|
1096
1096
|
const apps = Array.isArray(status.apps) ? status.apps : []
|
|
1097
1097
|
return {
|
|
1098
1098
|
name: label,
|
|
1099
|
+
address: `https://${peer.dnsName}`,
|
|
1099
1100
|
apps: apps.map((app) => {
|
|
1100
1101
|
const { remote, ...rest } = app
|
|
1101
1102
|
return Object.assign(rest, { open: this.anywherePeerOpenPath(label, remote) })
|
|
@@ -1135,31 +1136,35 @@ class Server {
|
|
|
1135
1136
|
if (location.origin !== new URL(base).origin) location = new URL(base)
|
|
1136
1137
|
return { status: 302, headers: { Location: location.href, "Cache-Control": "no-store" } }
|
|
1137
1138
|
}
|
|
1138
|
-
//
|
|
1139
|
-
|
|
1139
|
+
// /anywhere/peers/<name>/<path>: the peer's own API through the daemon's proxy (API spec, Relays).
|
|
1140
|
+
// JSON routes get the peer's published addresses turned into this node's open routes, absolute,
|
|
1141
|
+
// since the client may be pinokio.co on another origin; an asset is passed through as it is.
|
|
1142
|
+
async relayAnywherePeer(name, method, relayPath, body = {}) {
|
|
1140
1143
|
const anywhere = this.kernel.anywhere
|
|
1141
1144
|
const peer = anywhere.peerFor(name)
|
|
1142
1145
|
if (!peer) return { status: 400, body: { error: `Not a computer on your network: ${name}` } }
|
|
1143
1146
|
const unavailable = this.anywherePeerUnavailable()
|
|
1144
1147
|
if (unavailable) return { status: 502, body: { error: unavailable } }
|
|
1148
|
+
const json = relayPath.startsWith("anywhere/")
|
|
1145
1149
|
let answer
|
|
1146
1150
|
try {
|
|
1147
|
-
answer = await anywhere.forwarders.request(`https://${peer.dnsName}
|
|
1148
|
-
method
|
|
1149
|
-
|
|
1151
|
+
answer = await anywhere.forwarders.request(`https://${peer.dnsName}/${relayPath}`, method === "POST"
|
|
1152
|
+
? { method, headers: { "Content-Type": "application/json" }, body: JSON.stringify(body || {}), timeoutMs: 30000 }
|
|
1153
|
+
: { method, timeoutMs: 8000 })
|
|
1150
1154
|
} catch (error) {
|
|
1151
1155
|
return { status: 502, body: { error: `Could not reach ${peer.name}: ${error && error.message ? error.message : error}` } }
|
|
1152
1156
|
}
|
|
1157
|
+
if (!json) return { status: answer.status, raw: answer.raw, contentType: answer.headers["content-type"] || "application/octet-stream" }
|
|
1153
1158
|
let parsed
|
|
1154
1159
|
try { parsed = JSON.parse(answer.body) } catch (_) {
|
|
1155
1160
|
return { status: 502, body: { error: `${peer.name} did not answer with JSON (HTTP ${answer.status})` } }
|
|
1156
1161
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
if (host === peer.dnsName.toLowerCase()) parsed.url = this.anywherePeerOpenPath(peer.dnsName.split(".")[0], parsed.url)
|
|
1162
|
+
const own = (url) => { try { return new URL(url).hostname.toLowerCase() === peer.dnsName.toLowerCase() } catch (_) { return false } }
|
|
1163
|
+
const route = (url) => `http://127.0.0.1:${anywhere.serverPort()}${this.anywherePeerOpenPath(peer.dnsName.split(".")[0], url)}`
|
|
1164
|
+
if (parsed && Array.isArray(parsed.apps)) {
|
|
1165
|
+
for (const app of parsed.apps) if (app && typeof app.remote === "string" && own(app.remote)) app.remote = route(app.remote)
|
|
1162
1166
|
}
|
|
1167
|
+
if (parsed && parsed.action === "open" && typeof parsed.url === "string" && own(parsed.url)) parsed.url = route(parsed.url)
|
|
1163
1168
|
return { status: answer.status, body: parsed }
|
|
1164
1169
|
}
|
|
1165
1170
|
// the app's menu as launch targets: scripts and web addresses, never interface links;
|
|
@@ -7002,6 +7007,12 @@ class Server {
|
|
|
7002
7007
|
this.kernel.vault.automaticScans.supported)
|
|
7003
7008
|
next()
|
|
7004
7009
|
})
|
|
7010
|
+
// pinokio.co's Remote tab reaches the peer routes from a browser on this computer; Chrome's
|
|
7011
|
+
// local network access wants this on the preflight, which the cors middleware below answers
|
|
7012
|
+
this.app.use("/anywhere/peers", (req, res, next) => {
|
|
7013
|
+
if (req.get("Access-Control-Request-Private-Network")) res.set("Access-Control-Allow-Private-Network", "true")
|
|
7014
|
+
next()
|
|
7015
|
+
})
|
|
7005
7016
|
this.app.use(cors({
|
|
7006
7017
|
origin: '*'
|
|
7007
7018
|
}));
|
|
@@ -14512,7 +14523,8 @@ class Server {
|
|
|
14512
14523
|
try {
|
|
14513
14524
|
const answer = await fn(req)
|
|
14514
14525
|
if (answer.headers) res.set(answer.headers)
|
|
14515
|
-
if (answer.
|
|
14526
|
+
if (answer.raw) res.status(answer.status).type(answer.contentType).send(answer.raw)
|
|
14527
|
+
else if (answer.body === undefined) res.status(answer.status).end()
|
|
14516
14528
|
else res.status(answer.status).json(answer.body)
|
|
14517
14529
|
} catch (error) {
|
|
14518
14530
|
res.status(500).json({ error: error && error.message ? error.message : String(error) })
|
|
@@ -14520,8 +14532,13 @@ class Server {
|
|
|
14520
14532
|
})
|
|
14521
14533
|
this.app.get("/anywhere/peers", anywherePeerRoute(() => this.composeAnywherePeers()))
|
|
14522
14534
|
this.app.get("/anywhere/peers/:name/open", anywherePeerRoute((req) => this.anywherePeerOpen(req.params.name, req.query)))
|
|
14523
|
-
|
|
14524
|
-
|
|
14535
|
+
// the peer's own API under its prefix: a client swaps the peer's address for this prefix and changes nothing else
|
|
14536
|
+
const anywhereRelay = (method, relayPath) => anywherePeerRoute((req) => this.relayAnywherePeer(req.params.name, method, relayPath(req), req.body))
|
|
14537
|
+
this.app.get("/anywhere/peers/:name/anywhere/status", anywhereRelay("GET", () => "anywhere/status"))
|
|
14538
|
+
this.app.get("/anywhere/peers/:name/anywhere/apps", anywhereRelay("GET", () => "anywhere/apps"))
|
|
14539
|
+
this.app.post("/anywhere/peers/:name/anywhere/apps/launch", anywhereRelay("POST", () => "anywhere/apps/launch"))
|
|
14540
|
+
this.app.post("/anywhere/peers/:name/anywhere/apps/stop", anywhereRelay("POST", () => "anywhere/apps/stop"))
|
|
14541
|
+
this.app.get("/anywhere/peers/:name/asset/*", anywhereRelay("GET", (req) => "asset/" + encodeURI(req.params[0])))
|
|
14525
14542
|
const anywhereAction = (label, fn) => ex(async (req, res) => {
|
|
14526
14543
|
const anywhere = this.kernel.anywhere
|
|
14527
14544
|
if (!anywhere) {
|
|
@@ -517,7 +517,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
517
517
|
button.disabled = true
|
|
518
518
|
othersNote = ''
|
|
519
519
|
try {
|
|
520
|
-
const response = await fetch(`/anywhere/peers/${encodeURIComponent(peer)}/apps/stop`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: button.dataset.stop }) })
|
|
520
|
+
const response = await fetch(`/anywhere/peers/${encodeURIComponent(peer)}/anywhere/apps/stop`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: button.dataset.stop }) })
|
|
521
521
|
const answer = await response.json().catch(() => ({}))
|
|
522
522
|
if (!response.ok) othersNote = `Could not stop it on ${peer}: ${answer.error || `HTTP ${response.status}`}`
|
|
523
523
|
} catch (e) {
|
|
@@ -55,6 +55,39 @@ test('a forwarder relays requests with the forwarded header, the peer as Host, a
|
|
|
55
55
|
assert.equal(target.seen[0].headers['x-pinokio-source-host'], undefined)
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
+
// A forward proxy for plain http targets: the real ProxyAgent sends it absolute URLs, and
|
|
59
|
+
// connects asynchronously, which is what broke the proxyReq-hook version in production.
|
|
60
|
+
async function forwardProxy(t) {
|
|
61
|
+
const server = http.createServer((req, res) => {
|
|
62
|
+
const target = new URL(req.url)
|
|
63
|
+
const upstream = http.request({ host: target.hostname, port: target.port, path: target.pathname + target.search, method: req.method, headers: req.headers }, (answer) => {
|
|
64
|
+
res.writeHead(answer.statusCode, answer.headers)
|
|
65
|
+
answer.pipe(res)
|
|
66
|
+
})
|
|
67
|
+
upstream.on('error', (error) => { res.writeHead(502); res.end(error.message) })
|
|
68
|
+
req.pipe(upstream)
|
|
69
|
+
})
|
|
70
|
+
server.listen(0, '127.0.0.1')
|
|
71
|
+
await once(server, 'listening')
|
|
72
|
+
t.after(() => { server.closeAllConnections(); server.close() })
|
|
73
|
+
return `http://127.0.0.1:${server.address().port}`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
test('through the real proxy agent, whose connection completes later, the forwarded header still goes out and nothing throws', async (t) => {
|
|
77
|
+
const target = await peer(t)
|
|
78
|
+
const proxyUrl = await forwardProxy(t)
|
|
79
|
+
const forwarders = new Forwarders({ proxyUrl: () => proxyUrl, serverPort: () => 42000, scheme: 'http' })
|
|
80
|
+
t.after(() => forwarders.stopAll())
|
|
81
|
+
const local = await forwarders.open('127.0.0.1', target.port)
|
|
82
|
+
const answer = await get(local, '/ui/?a=1', { 'X-Pinokio-Peer': '1' })
|
|
83
|
+
assert.equal(answer.status, 200)
|
|
84
|
+
const body = JSON.parse(answer.body)
|
|
85
|
+
assert.equal(body.forwarded, '1')
|
|
86
|
+
assert.equal(body.peer, null)
|
|
87
|
+
assert.equal(body.host, `127.0.0.1:${target.port}`)
|
|
88
|
+
assert.equal(target.seen.length, 1)
|
|
89
|
+
})
|
|
90
|
+
|
|
58
91
|
test('two opens of the same peer and port at once share one forwarder', async (t) => {
|
|
59
92
|
const target = await peer(t)
|
|
60
93
|
const forwarders = new Forwarders({ proxyUrl: () => null, serverPort: () => 42000, scheme: 'http' })
|
|
@@ -71,7 +71,7 @@ test('with none found the section says so, and a stop is relayed to the peer the
|
|
|
71
71
|
stop.click()
|
|
72
72
|
await new Promise((resolve) => setImmediate(resolve)); await new Promise((resolve) => setImmediate(resolve))
|
|
73
73
|
const posted = h.state.calls.find((call) => call.method === 'POST')
|
|
74
|
-
assert.deepEqual([posted.url, posted.body], ['/anywhere/peers/pinokio-1/apps/stop', '{"id":"comfy"}'])
|
|
74
|
+
assert.deepEqual([posted.url, posted.body], ['/anywhere/peers/pinokio-1/anywhere/apps/stop', '{"id":"comfy"}'])
|
|
75
75
|
assert.equal(h.state.calls.filter((call) => call.url === '/anywhere/peers').length, 4, 'load, two polls, and once more after the stop')
|
|
76
76
|
assert.equal(h.section().querySelector('.anywhere-error'), null)
|
|
77
77
|
h.state.stopFails = true
|
|
@@ -43,7 +43,7 @@ test('peers lists the online computers that answered a status, with apps linked
|
|
|
43
43
|
})
|
|
44
44
|
const out = await Server.prototype.composeAnywherePeers.call(fake(anywhere))
|
|
45
45
|
assert.equal(out.status, 200)
|
|
46
|
-
assert.deepEqual(out.body, { peers: [{ name: 'pinokio-1', apps: [
|
|
46
|
+
assert.deepEqual(out.body, { peers: [{ name: 'pinokio-1', address: 'https://pinokio-1.tail1.ts.net', apps: [
|
|
47
47
|
{ id: 'comfy', name: 'ComfyUI', state: 'ready', exposed: true, open: '/anywhere/peers/pinokio-1/open?port=43000&to=%2Fui%2F%3Fa%3D1%23v' },
|
|
48
48
|
{ id: 'forge', name: 'Forge', state: 'starting', exposed: false, open: null },
|
|
49
49
|
] }] })
|
|
@@ -83,6 +83,28 @@ test('open validates the peer, the port and the path, then redirects to the forw
|
|
|
83
83
|
assert.deepEqual(unavailable, { status: 502, body: { error: 'Turn Remote off and on to reach your other Pinokios' } }, 'a daemon from before the proxy existed')
|
|
84
84
|
})
|
|
85
85
|
|
|
86
|
+
test('the relays carry the peer\'s API under its prefix, with its own addresses made absolute open routes', async () => {
|
|
87
|
+
const anywhere = anywhereWith({
|
|
88
|
+
'https://pinokio-1.tail1.ts.net/anywhere/status': json({ phase: 'ready', apps: [
|
|
89
|
+
{ id: 'comfy', name: 'ComfyUI', remote: 'https://pinokio-1.tail1.ts.net:43000/ui/?a=1#v' },
|
|
90
|
+
{ id: 'ext', name: 'Ext', remote: 'https://example.com/' },
|
|
91
|
+
{ id: 'forge', name: 'Forge', remote: null },
|
|
92
|
+
] }),
|
|
93
|
+
'https://pinokio-1.tail1.ts.net/anywhere/apps': json({ apps: [{ id: 'comfy', name: 'ComfyUI', icon: '/asset/api/comfy/icon.png' }] }),
|
|
94
|
+
'https://pinokio-1.tail1.ts.net/asset/api/comfy/icon.png': async () => ({ status: 200, headers: { 'content-type': 'image/png' }, raw: Buffer.from([0x89, 0x50]), body: '' }),
|
|
95
|
+
})
|
|
96
|
+
const server = fake(anywhere)
|
|
97
|
+
const status = await Server.prototype.relayAnywherePeer.call(server, 'pinokio-1', 'GET', 'anywhere/status')
|
|
98
|
+
assert.equal(status.status, 200)
|
|
99
|
+
assert.deepEqual(status.body.apps.map((app) => app.remote), [
|
|
100
|
+
'http://127.0.0.1:42000/anywhere/peers/pinokio-1/open?port=43000&to=%2Fui%2F%3Fa%3D1%23v', 'https://example.com/', null,
|
|
101
|
+
], 'the peer\'s own address becomes an absolute open route; anything else passes through')
|
|
102
|
+
const catalog = await Server.prototype.relayAnywherePeer.call(server, 'pinokio-1', 'GET', 'anywhere/apps')
|
|
103
|
+
assert.deepEqual(catalog.body, { apps: [{ id: 'comfy', name: 'ComfyUI', icon: '/asset/api/comfy/icon.png' }] })
|
|
104
|
+
const icon = await Server.prototype.relayAnywherePeer.call(server, 'pinokio-1', 'GET', 'asset/api/comfy/icon.png')
|
|
105
|
+
assert.deepEqual([icon.status, icon.contentType, [...icon.raw]], [200, 'image/png', [0x89, 0x50]], 'an asset is passed through as bytes')
|
|
106
|
+
})
|
|
107
|
+
|
|
86
108
|
test('launch and stop relay the peer\'s status and body, turning an open URL into the open route', async () => {
|
|
87
109
|
const sent = []
|
|
88
110
|
const anywhere = anywhereWith({
|
|
@@ -90,20 +112,20 @@ test('launch and stop relay the peer\'s status and body, turning an open URL int
|
|
|
90
112
|
'https://pinokio-1.tail1.ts.net/anywhere/apps/stop': json({ error: 'Unknown app: x' }, 400),
|
|
91
113
|
})
|
|
92
114
|
const server = fake(anywhere)
|
|
93
|
-
const launch = await Server.prototype.
|
|
94
|
-
assert.deepEqual(launch, { status: 200, body: { action: 'open', url: '/anywhere/peers/pinokio-1/open?port=43000&to=%2F' } })
|
|
115
|
+
const launch = await Server.prototype.relayAnywherePeer.call(server, 'pinokio-1', 'POST', 'anywhere/apps/launch', { id: 'comfy' })
|
|
116
|
+
assert.deepEqual(launch, { status: 200, body: { action: 'open', url: 'http://127.0.0.1:42000/anywhere/peers/pinokio-1/open?port=43000&to=%2F' } })
|
|
95
117
|
assert.equal(sent[0].method, 'POST')
|
|
96
118
|
assert.equal(sent[0].body, '{"id":"comfy"}')
|
|
97
119
|
assert.equal(sent[0].headers['Content-Type'], 'application/json')
|
|
98
|
-
assert.deepEqual(await Server.prototype.
|
|
99
|
-
assert.equal((await Server.prototype.
|
|
120
|
+
assert.deepEqual(await Server.prototype.relayAnywherePeer.call(server, 'pinokio-1', 'POST', 'anywhere/apps/stop', { id: 'x' }), { status: 400, body: { error: 'Unknown app: x' } })
|
|
121
|
+
assert.equal((await Server.prototype.relayAnywherePeer.call(server, 'nobody', 'POST', 'anywhere/apps/stop', {})).status, 400)
|
|
100
122
|
})
|
|
101
123
|
|
|
102
124
|
test('a launch whose default is an outside address passes through unchanged', async () => {
|
|
103
125
|
const anywhere = anywhereWith({
|
|
104
126
|
'https://pinokio-1.tail1.ts.net/anywhere/apps/launch': json({ action: 'open', url: 'https://github.com/x/y' }),
|
|
105
127
|
})
|
|
106
|
-
const launch = await Server.prototype.
|
|
128
|
+
const launch = await Server.prototype.relayAnywherePeer.call(fake(anywhere), 'pinokio-1', 'POST', 'anywhere/apps/launch', { id: 'docs' })
|
|
107
129
|
assert.deepEqual(launch.body, { action: 'open', url: 'https://github.com/x/y' })
|
|
108
130
|
})
|
|
109
131
|
|
|
@@ -113,12 +135,12 @@ test('a peer that cannot be reached or answers without JSON relays as 502', asyn
|
|
|
113
135
|
'https://other.tail1.ts.net/anywhere/apps/stop': async () => ({ status: 404, headers: {}, body: '<html>' }),
|
|
114
136
|
})
|
|
115
137
|
const server = fake(anywhere)
|
|
116
|
-
const down = await Server.prototype.
|
|
138
|
+
const down = await Server.prototype.relayAnywherePeer.call(server, 'pinokio-1', 'POST', 'anywhere/apps/stop', { id: 'x' })
|
|
117
139
|
assert.equal(down.status, 502)
|
|
118
140
|
assert.match(down.body.error, /Could not reach STUDIO: no answer/)
|
|
119
|
-
const old = await Server.prototype.
|
|
141
|
+
const old = await Server.prototype.relayAnywherePeer.call(server, 'other', 'POST', 'anywhere/apps/stop', { id: 'x' })
|
|
120
142
|
assert.deepEqual(old, { status: 502, body: { error: 'other did not answer with JSON (HTTP 404)' } })
|
|
121
|
-
const stopped = await Server.prototype.
|
|
143
|
+
const stopped = await Server.prototype.relayAnywherePeer.call(fake(anywhereWith({}, { proxy: false })), 'other', 'POST', 'anywhere/apps/stop', {})
|
|
122
144
|
assert.equal(stopped.status, 502)
|
|
123
145
|
})
|
|
124
146
|
|
|
@@ -133,7 +155,9 @@ test('the peer routes answer a browser on this computer only, never a tailnet cl
|
|
|
133
155
|
assert.equal(allowed(request({ remote: '::1', host: '[::1]:42000', origin: 'http://[::1]:42000' })), true)
|
|
134
156
|
assert.equal(allowed(request({ remote: '::ffff:127.0.0.1', host: '127.0.0.1:42000', origin: 'http://127.0.0.1:42000' })), true)
|
|
135
157
|
assert.equal(allowed(request({ origin: 'http://localhost:3000' })), false, 'another local site is not this page')
|
|
136
|
-
assert.equal(allowed(request({ origin: 'https://pinokio.co' })),
|
|
158
|
+
assert.equal(allowed(request({ origin: 'https://pinokio.co' })), true, 'the Remote tab in a browser on this computer')
|
|
159
|
+
assert.equal(allowed(request({ origin: 'https://pinokio.co', headers: { 'X-Forwarded-For': '100.1.1.1' } })), false, 'but not through Serve')
|
|
160
|
+
assert.equal(allowed(request({ origin: 'https://pinokio.co.evil' })), false)
|
|
137
161
|
assert.equal(allowed(request({ origin: 'not a url' })), false)
|
|
138
162
|
assert.equal(allowed(request({ host: 'pinokio.tail1.ts.net' })), false, 'the node\'s own name')
|
|
139
163
|
assert.equal(allowed(request({ headers: { 'X-Forwarded-For': '100.1.1.1' } })), false, 'through Serve with a forged loopback Host')
|