From: jordan lavatai Date: Tue, 4 Jul 2017 22:02:32 +0000 (-0700) Subject: works on localhost, time to test on the server X-Git-Url: https://www.kengrimes.com/gitweb/?p=henge%2Fkiak.git;a=commitdiff_plain;h=0ff5eabbfe914a66b7f6b2a7f22f6469895ab826 works on localhost, time to test on the server --- diff --git a/client.js b/client.js index d389945..eab3187 100644 --- a/client.js +++ b/client.js @@ -3,40 +3,7 @@ const root = document.createElement('div') document.title = "Strapp.io Client" body.appendChild(root) document.body = body -const conf = {"iceServers": [{ "url": "stun:stun.1.google.com:19302" }] } -/* Poll the server. Send get request, wait for timeout, send another request. - Do this until...? Can be used for either reconnecting or waiting for answer*/ -function pollServerTimeout(url, data, resolve, reject) { - console.log(`Polling server ${url} with ${data}`) - const request = new XMLHttpRequest() - request.open('GET', url, true) - request.setRequestHeader('Content-Type', 'application/json' ) - request.setRequestHeader('X-Strapp-Type', JSON.stringify(data)) - request.onreadystatechange = () => { - if (request.status === 200) { - if(request.readyState === 4) { - console.log('Client: Recieved answer from Host') - console.log(request) - resolve(request.response) - } - } - else if (request.status === 504) { - console.log('timed out, resending') - pollServerTimeout(url, data, resolve, reject) - } - else { - reject('server unhandled response of status ' + request.status) - } - } - request.send() -} - -/* TODO: All this does is wrap a function in a promise */ -function pollServer(url, clientPubKey, func) { - return new Promise((resolve, reject) => { - func(url, clientPubKey, resolve, reject ) - }) -} +const conf = {"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }] } /* TODO: duplicate in both client.js and host.js */ function getPublicKey() { @@ -72,53 +39,143 @@ function getPublicKey() { } -/* Create, set, and get client Offer. Poll server for host answer. - Set host answer as client remoteDescription */ -const cpc = new RTCPeerConnection(conf) -cpc.oniceconnectionstatechange = () => { - console.log('iceConnectionState = ' + cpc.iceConnectionState) +function postServer(url, data) { + const request = new XMLHttpRequest() + request.open('POST', url, true) + request.setRequestHeader('Content-Type', 'application/json' ) + request.setRequestHeader('X-Strapp-Type', 'ice-candidate-submission') + request.send(data) } -cpc.createOffer().then((offer) => { - return cpc.setLocalDescription(offer) -}) - .then(() => { - console.log('sessionDescriptionInit = ' + cpc.localDescription) - getPublicKey().then((cpk) => { - console.log('cpk is' + cpk) - let offer = { - cmd: '> sdp pubKey', - sdp: cpc.localDescription, - pubKey: cpk - } - cpc.onicecandidate = (event) => { - if (event.candidate) { - console.log('Client: Sending ice candidate to host') - pollServer(window.location, wsock.send(JSON.stringify({ - cmd: '> ice pubkey', - ice: event.candidate, - pubKey: cpk /* TODO: do we need to send this? */ - })), pollServerTimeout) - } - else { - /* Set up data channel here */ - console.log('Client: Finished setting up ICE candidates') + +/* TODO: All this does is wrap a function in a promise */ +function pollServer(url, clientPubKey, func) { + return new Promise((resolve, reject) => { + func(url, clientPubKey, resolve, reject ) + }) +} + +/* Poll the server. Send get request, wait for timeout, send another request. + Do this until...? Can be used for either reconnecting or waiting for answer*/ +function pollServerForAnswer(url, data, resolve, reject) { + const request = new XMLHttpRequest() + request.open('GET', url, true) + /* But there is no JSON? */ + request.setRequestHeader('Content-Type', 'application/json' ) + request.setRequestHeader('X-Strapp-Type', 'client-sdp-offer') + request.setRequestHeader('X-Client-Offer', JSON.stringify(data)) + request.onreadystatechange = () => { + if (request.status === 200) { + if(request.readyState === 4) { + console.log('Client: Recieved response from Host') + console.log(request) + resolve(request.response) } } - /* TODO: start polling for ice candidates, and then addIceCandidate() to cpc */ - //pollServer(window.location, {ice}, pollServerTimeout) + else if (request.status === 504) { + console.log('timed out, resending') + pollServerTimeout(url, data, resolve, reject) + } + else { + reject('server unhandled response of status ' + request.status) + } + } + request.send() +} +/* Poll server for ice candidates until ice is complete */ +function pollServerForICECandidate(cpc) { + window.setInterval(() => { + if (cpc.iceGatheringState !== 'complete') { + return new Promise((resolve, reject) => { + console.log('Client: Requesting ICE Candidates from server') + const request = new XMLHttpRequest() + request.open('GET', window.location, true) + request.setRequestHeader('Content-Type', 'application/json' ) + request.setRequestHeader('X-Strapp-Type', 'ice-candidate-request') + request.onreadystatechange = () => { + if (request.status === 200) { + if(request.readyState === 4) { + console.log('Client: Recieved ICE Candidate from Host') + resolve(request.response) + } + } + else if (request.status === 204) { + console.log('Ice Candidate unavailable, trying again in one second') + } + else { + reject('server unhandled response of status ' + request.status) + } + } + request.send(cpc.pubKey) + }).then((response) => { + cpc.addIceCandidate(response.candidate) + }).catch((err) => { + console.log('pollServerForICECandidate: ' + err) + }) + } + else { + clearTimeout() + } + }, 2000) +} - /* Poll for answer */ - return pollServer(window.location, offer, pollServerTimeout) - }).then((serverResponse) => { - const answer = JSON.parse(serverResponse) - console.log(answer) - /* TODO: State machine to parse answer */ - console.log('Setting Remote Description') - cpc.setRemoteDescription(answer.sdp) - }).catch( (err) => { - console.log('error in sdp handshake: ' + err) - }) -}).catch((err) => { - console.log(err) +/* Create, set, and get client Offer. Poll server for host answer. + Set host answer as client remoteDescription */ +getPublicKey().then((cpk) => { + const cpc = new RTCPeerConnection(conf) + /* Start data channel */ + sendChannel = cpc.createDataChannel("sendChannel"); + sendChannel.onopen = () => { + console.log('client data channel on line') + sendChannel.onmessage = (message) => { + console.log(message.data) + } + sendChannel.send('Hi from the Client') + }; + /* Start polling for ice candidate */ + + + console.log(cpc.iceConnectionState) + cpc.oniceconnectionstatechange = () => { + console.log('iceConnectionState = ' + cpc.iceConnectionState) + } + + + + cpc.onnegotiationneeded = () => { + cpc.createOffer().then((offer) => { + return cpc.setLocalDescription(offer) + }) + .then(() => { + console.log('Client: Sending offer to host') + let offer = { + cmd: '> sdp pubKey', + sdp: cpc.localDescription, + pubKey: cpk.n + } + return pollServer(window.location, offer, pollServerForAnswer) + }).then((serverResponse) => { + const answer = JSON.parse(serverResponse) + console.log('Client: received host answer') + cpc.setRemoteDescription(answer.sdp).then(() => { + console.log('Client: Polling for ICE candidates') + pollServerForICECandidate(cpc) + }) + cpc.onicecandidate = (event) => { + if (event.candidate) { + console.log('Client: Sending ice candidate to host') + postServer(window.location, JSON.stringify({ + cmd: '> ice pubkey', + ice: event.candidate, + pubKey: cpk.n + })) + } + else { + console.log('Client: No more Ice Candidates to send') + } + } + }).catch( (err) => { + console.log('error in sdp handshake: ' + err) + }) + } }) diff --git a/host.js b/host.js index 34d7c9d..9b84f5c 100644 --- a/host.js +++ b/host.js @@ -1,7 +1,9 @@ document.title = "Strapp.io Host" -const conf = {"iceServers": [{ "url": "stun:stun.1.google.com:19302" }] } +const conf = {"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }] } const clients = new Map([]) -const hpk = getPublicKey() +const iceCandidates = [] +let dataChannel + /* TODO: duplicate in both client.js and host.jhs */ function getPublicKey() { @@ -36,54 +38,78 @@ function getPublicKey() { }) } - function handleNewClientConnection(offer) { /* New Client Connection*/ hpc = new RTCPeerConnection(conf) + //console.log(offer) + clients.set(offer.pubKey, hpc) hpc.setRemoteDescription(offer.sdp) .then(() => { hpc.createAnswer().then((answer) => { return hpc.setLocalDescription(answer) }) .then(() => { - hpk.then(() => { + getPublicKey().then((hpk) => { hpc.onicecandidate = (event) => { if (event.candidate) { - console.log('Host: Sending ice candidate to client') - wsock.send(JSON.stringify({ + iceCandidates.push(JSON.stringify({ cmd: '< ice pubKey', ice: event.candidate, - pubKey: hpk /* TODO: do we need to send this? */ + hostPubKey: hpk.n, /* TODO: do we need to send this? */ + clientPubKey: offer.pubKey, + iceCandidateAvailable: true })) } else { - console.log('Host: Finished setting up ICE candidates') + console.log('Host: Finished sending ICE candidates') } } - console.log('Host: Sending answer to Host') + console.log('Host: Sending answer to Client') wsock.send(JSON.stringify({ cmd: '< sdp pubKey', sdp: hpc.localDescription, - pubKey: hpk + hostPubKey: hpk.n, + clientPubKey: offer.pubKey })) - clients.set(offer.pubKey, { - hostsdp: hpc.localDescription, - clientsdp: hpc.remoteDescription - }) - + hpc.ondatachannel = (evt) => { + dataChannel = evt.channel + console.log(evt) + dataChannel.onmessage = (msg) => { + console.log(msg.data) + } + dataChannel.send('Hi from the host') + } + hpc.oniceconnectionstatechange = () => { + console.log('iceConnectionState = ' + hpc.iceConnectionState) + } }) }).catch((err) => { console.log(`error in host answer ${err}`) }) }) + + + } -function handleNewIceCandidate(msg) { +function handleNewIceSubmission(msg) { + console.log('Host: Adding new ice candidate') const hpc = clients.get(msg.pubKey) let candidate = new RTCIceCandidate(msg.ice) hpc.addIceCandidate(candidate) } +function handleIceRequest(msg) { + console.log('Host: Sending ice candidate to client') + const hpc = clients.get(msg) + const iceCandidates = iceCandidates.pop() + if (iceCandidate !== undefined) { + wsock.send(iceCandidate) + } else { + wsock.send('no ice candidates') + } + +} if ("WebSocket" in window) { document.addEventListener('DOMContentLoaded', (event) => { wsock = new WebSocket(`${_strapp_protocol}://${window.location.hostname}:${_strapp_port}`) @@ -92,30 +118,30 @@ if ("WebSocket" in window) { } wsock.onmessage = (serverMsg) => { - /* msg is either offer or ice candidate */ - console.log(serverMsg.data) + /* msg is either offer or ice candidate or ice candidate request*/ + + let msg = JSON.parse(serverMsg.data) - const clientID = msg.pubKey - const msgType = msg.hasOwnProperty('sdp') ? 'o' : 'i' + const clientID = msg.pubKey || msg + + /* TODO: redo this trash */ if (clients.has(clientID)) { - switch(msgType) { - case 'o': - console.log('client exist && sending an offer == error') - break - case 'i': - handleNewIceCandidate(msg) - break + if (msg.ice) { + handleNewIceSubmission(msg) + } else if (msg.sdp) { + //handleRepeatedOffer + } else { + handleIceRequest(msg) } } else { - switch(msgType) { - case 'o': + if (msg.ice) { + console.log('Host: Client that doesnt exist is sending ice submissions') + } else if (msg.sdp) { handleNewClientConnection(msg) - break - case 'i': - console.log('client !exist && ice candidate === error') - break + } else { + console.log('Host: Client that doesnt exist is sending ice requests') } } } diff --git a/main.js b/main.js index 35cffef..9187a77 100644 --- a/main.js +++ b/main.js @@ -42,6 +42,8 @@ const router = { const htArgv = request.url.slice(1).split("?") let routePath = htArgv[0].split('/') let routeName = routePath[0] + + if (routeName === '' || routeName === 'index.html') serveFile(opts['index']) else if (routeName in opts['bindings']) { @@ -60,11 +62,16 @@ const router = { } /* TODO: Handle reconnecting host */ else if (routeName in router.routes) { - const route = router.routes[routeName] + const clients = route['clients'] + const headerData = request.headers['x-strapp-type'] + + if (route.socket === undefined ) { + console.log('route socket undefined') + } /* Client is INIT GET */ - if (request.headers['x-strapp-type'] == undefined) { + if (headerData === undefined) { console.log('client init GET') response.writeHead(200, { 'Content-Type': 'text/html' }) response.write(`${router.skelPage[0]}${router.clientJS}${router.skelPage[1]}`) @@ -72,36 +79,93 @@ const router = { //TODO: if route.socket == undefined: have server delay this send until host connects // (this happens when a client connects to an active route with no currently-online host) } - else { /* Client sent offer, waiting for answer */ - console.log('Server: Sending client offer to host') - route.socket.send(request.headers['x-strapp-type']) - route.socket.on('message', (hostResponse) => { - console.log('Server: Sending host answer to client') - console.log(hostResponse) - response.writeHead(200, { 'Content-Type': 'application/json' }) - response.write(hostResponse) - response.end() + else if (headerData.localeCompare('ice-candidate-request') === 0){ + console.log('Server: received ice-candidate-request from Client') + let data = [] + request.on('data', (chunk) => { + data.push(chunk) + }).on('end', () => { + data = Buffer.concat(data).toString(); + console.log('Sending ice-candidate-request to Host' + data) + clients.set(data, response) + route.socket.send(data) + }) + } + else if (headerData.localeCompare('ice-candidate-submission') === 0) { + console.log('Server: recieved ice-candidate-submission from Client') + let data = [] + request.on('data', (chunk) => { + data.push(chunk) + }).on('end', () => { + console.log('Sending ice-candidate-submission to Host' + data) + data = Buffer.concat(data).toString(); + clients.set(JSON.parse(data)['pubKey'], response) + route.socket.send(data) }) } + else if (headerData.localeCompare('client-sdp-offer') === 0){ /* Client sent offer, waiting for answer */ + console.log('Server: Sending client offer to host') + clients.set(JSON.parse(request.headers['x-client-offer'])['pubKey'], response) + route.socket.send(request.headers['x-client-offer']) + } else { + console.log('Unhandled stuff') + console.log(request.headers) + } } else { router.routes[routeName] = true const newRoute = {} + newRoute.clients = new Map([]) newRoute.host = request.headers['x-forwarded-for'] || request.connection.remoteAddress getport().then( (port) => { newRoute.port = port if (opts['no-tls']) - newRoute.httpd = http.createServer() + newRoute.httpd = http.createServer() else - newRoute.httpd = https.createServer(router.httpsOpts) + newRoute.httpd = https.createServer(router.httpsOpts) newRoute.httpd.listen(newRoute.port) newRoute.wsd = new ws.Server( { server: newRoute.httpd } ) newRoute.wsd.on('connection', (sock) => { + console.log(`${routeName} server has been established`) newRoute.socket = sock - sock.on('message', (msg) => { console.log(`[${newRoute.host}] ${msg}`) }) + + /* Handle all messages from host */ + sock.on('message', (hostMessage) => { + hostMessage = JSON.parse(hostMessage) + response = newRoute.clients.get(hostMessage['clientPubKey']) + + /* If the host response is a answer */ + if (hostMessage['cmd'].localeCompare('< sdp pubKey') === 0) { + console.log('Server: Sending host answer to client') + response.writeHead(200, { 'Content-Type': 'application/json' }) + response.write(JSON.stringify(hostMessage)) + response.end() + } + else if (hostMessage['cmd'].localeCompare('< ice pubKey') === 0){ + /* if the host response is an ice candidate */ + console.log('Server: Sending host ice candidate') + let iceCandidateAvailable = hostMessage['iceCandidateAvailable'] + /* If there are any ice candidates, send them back */ + if (iceCandidateAvailable) { + response.writeHead('200', {'x-strapp-type': 'ice-candidate-available'}) + response.write(JSON.stringify(hostMessage)) + response.end() + } + else { /* If not, srequest processed successfully, but there isnt anything yet*/ + console.log('Server: No ice candidate available for response') + response.writeHead('204', {'x-strapp-type': 'ice-candidate-unavailable'}) + response.end() + } + } + else { + console.log('unhandled message cmd from host') + } + + }) }) + console.log(`Listening for websocket ${newRoute.host} on port ${newRoute.port}`) router.routes[routeName] = newRoute }).then(() => { @@ -114,6 +178,7 @@ const router = { }) } + } }