forked from microsoft/pxt
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsocketbridge.ts
More file actions
103 lines (89 loc) · 2.64 KB
/
socketbridge.ts
File metadata and controls
103 lines (89 loc) · 2.64 KB
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
import Cloud = pxt.Cloud;
import U = pxt.Util;
import { isNativeHost } from "./cmds";
let iface: pxt.worker.Iface
let bridgeBySocket: pxt.Map<TCPSocket> = {}
interface OOB {
op: string;
result: {
socket: string;
data?: string;
encoding?: string;
}
}
function onOOB(v: OOB) {
let b = U.lookup(bridgeBySocket, v.result.socket)
if (b) {
b.onOOB(v)
} else {
console.error("Dropping data for " + v.result.socket)
}
}
export function tryInit() {
if (pxt.BrowserUtils.isLocalHost() && Cloud.localToken) {
pxt.mkTCPSocket = (h, p) => new TCPSocket(h, p)
}
}
function init() {
if (!iface) {
if (!pxt.BrowserUtils.isLocalHost() || !Cloud.localToken)
U.userError(lf("TCP sockets not available here"))
pxt.debug('initializing tcp pipe');
iface = pxt.worker.makeWebSocket(
`ws://localhost:${pxt.options.wsPort}/${Cloud.localToken}/tcp`, onOOB)
}
}
class TCPSocket implements pxt.TCPIO {
onData = (v: Uint8Array) => { };
onError = (e: Error) => { };
sockId: string;
constructor(public host: string, public port: number) {
}
onOOB(v: OOB) {
if (v.op == "data") {
const d = U.stringToUint8Array(atob(v.result.data))
this.onData(d)
} else if (v.op == "close") {
this.sockId = null;
delete bridgeBySocket[v.result.socket]
}
}
error(msg: string) {
let err = new Error(U.lf("TCP error on socket {0}:{1} ({2})", this.host, this.port, msg))
this.onError(err)
throw err
}
disconnectAsync(): Promise<void> {
if (!this.sockId)
return Promise.resolve()
return iface.opAsync("close", {
socket: this.sockId
}).then(() => {
this.sockId = null
})
}
sendPacketAsync(pkt: Uint8Array): Promise<void> {
if (!this.sockId)
U.userError("Not connected")
return iface.opAsync("send", {
socket: this.sockId,
data: U.toHex(pkt),
encoding: "hex"
})
}
sendSerialAsync(buf: Uint8Array, useStdErr: boolean): Promise<void> {
return Promise.reject(new Error("No serial on socket"))
}
connectAsync(): Promise<void> {
init()
if (this.sockId)
return Promise.resolve()
return iface.opAsync("open", { host: this.host, port: this.port })
.then(res => {
if (!res.socket)
this.error(res.errorMessage)
this.sockId = res.socket
bridgeBySocket[this.sockId] = this
})
}
}