blob: 1ef632058c0895a1de265d02251c303fd87592b8 [file] [log] [blame]
Alex Perryb3b50792020-01-18 16:13:45 -08001import {aos.web_proxy} from '../web_proxy_generated';
2
3// Analogous to the Connection class in //aos/network/web_proxy.h. Because most
4// of the apis are native in JS, it is much simpler.
5export class Connection {
6 private webSocketConnection: WebSocket|null = null;
7 private rtcPeerConnection: RTCPeerConnection|null = null;
8 private dataChannel: DataChannel|null = null;
9 private webSocketUrl: string;
10
11 constructor() {
12 const server = location.host;
13 this.webSocketUrl = `ws://${server}/ws`;
14 }
15
16 connect(): void {
17 this.webSocketConnection = new WebSocket(this.webSocketUrl);
18 this.webSocketConnection.binaryType = 'arraybuffer';
19 this.webSocketConnection.addEventListener(
20 'open', () => this.onWebSocketOpen());
21 this.webSocketConnection.addEventListener(
22 'message', (e) => this.onWebSocketMessage(e));
23 }
24
25 // Handle messages on the DataChannel. Will delegate to various handlers for
26 // different message types.
27 onDataChannelMessage(e: MessageEvent): void {
28 console.log(e);
29 }
30
31 onIceCandidate(e: RTCPeerConnectionIceEvent): void {
32 console.log('Created ice candidate', e);
33 if (!e.candidate) {
34 return;
35 }
36 const candidate = e.candidate;
37 const builder = new flatbuffers.Builder(512);
38 const candidateString = builder.createString(candidate.candidate);
39 const sdpMidString = builder.createString(candidate.sdpMid);
40
41 const iceFb = aos.web_proxy.WebSocketIce.createWebSocketIce(
42 builder, candidateString, sdpMidString, candidate.sdpMLineIndex);
43 const messageFb = aos.web_proxy.WebSocketMessage.createWebSocketMessage(
44 builder, aos.web_proxy.Payload.WebSocketIce, iceFb);
45 builder.finish(messageFb);
46 const array = builder.asUint8Array();
47 this.webSocketConnection.send(array.buffer.slice(array.byteOffset));
48 }
49
50 // Called for new SDPs. Make sure to set it locally and remotely.
51 onOfferCreated(description: RTCSessionDescription): void {
52 console.log('Created offer', description);
53 this.rtcPeerConnection.setLocalDescription(description);
54 const builder = new flatbuffers.Builder(512);
55 const offerString = builder.createString(description.sdp);
56
57 const webSocketSdp = aos.web_proxy.WebSocketSdp.createWebSocketSdp(
58 builder, aos.web_proxy.SdpType.OFFER, offerString);
59 const message = aos.web_proxy.WebSocketMessage.createWebSocketMessage(
60 builder, aos.web_proxy.Payload.WebSocketSdp, webSocketSdp);
61 builder.finish(message);
62 const array = builder.asUint8Array();
63 this.webSocketConnection.send(array.buffer.slice(array.byteOffset));
64 }
65
66 // We now have a websocket, so start setting up the peer connection. We only
67 // want a DataChannel, so create it and then create an offer to send.
68 onWebSocketOpen(): void {
69 this.rtcPeerConnection = new RTCPeerConnection({});
70 this.dataChannel = this.rtcPeerConnection.createDataChannel('dc');
71 this.dataChannel.addEventListener(
72 'message', (e) => this.onDataChannelMessage(e));
73 window.dc = this.dataChannel;
74 this.rtcPeerConnection.addEventListener(
75 'icecandidate', (e) => this.onIceCandidate(e));
76 this.rtcPeerConnection.createOffer().then(
77 (offer) => this.onOfferCreated(offer));
78 }
79
80 // When we receive a websocket message, we need to determine what type it is
81 // and handle appropriately. Either by setting the remote description or
82 // adding the remote ice candidate.
83 onWebSocketMessage(e: MessageEvent): void {
84 console.log('ws: ', e);
85 const buffer = new Uint8Array(e.data)
86 const fbBuffer = new flatbuffers.ByteBuffer(buffer);
87 const message =
88 aos.web_proxy.WebSocketMessage.getRootAsWebSocketMessage(fbBuffer);
89 switch (message.payloadType()) {
90 case aos.web_proxy.Payload.WebSocketSdp:
91 console.log('got an sdp message');
92 const sdpFb = message.payload(new aos.web_proxy.WebSocketSdp());
93 if (sdpFb.type() !== aos.web_proxy.SdpType.ANSWER) {
94 console.log('got something other than an answer back');
95 break;
96 }
97 this.rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(
98 {'type': 'answer', 'sdp': sdpFb.payload()}));
99 break;
100 case aos.web_proxy.Payload.WebSocketIce:
101 console.log('got an ice message');
102 const iceFb = message.payload(new aos.web_proxy.WebSocketIce());
103 const candidate = {} as RTCIceCandidateInit;
104 candidate.candidate = iceFb.candidate();
105 candidate.sdpMid = iceFb.sdpMid();
106 candidate.sdpMLineIndex = iceFb.sdpMLineIndex();
107 this.rtcPeerConnection.addIceCandidate(candidate);
108 break;
109 default:
110 console.log('got an unknown message');
111 break;
112 }
113 }
114}