From: ken Date: Fri, 7 Jul 2017 01:21:49 +0000 (+0000) Subject: screen sharinggit add .! X-Git-Url: https://www.kengrimes.com/gitweb/?p=henge%2Fkiak.git;a=commitdiff_plain;h=49487d947260500f71a7e125c705cef45e998db2 screen sharinggit add .! --- diff --git a/client.js b/client.js index f38f3ab..9b58c5d 100644 --- a/client.js +++ b/client.js @@ -1,193 +1,225 @@ -const body = document.createElement('body') -const root = document.createElement('div') -document.title = "Strapp.io Client" -const conf = {"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }] } -let dataChannel - -/* TODO: duplicate in both client.js and host.js */ -function getPublicKey() { - return new Promise( (resolve, reject) => { - /* Check local storage for public key */ - if (!window.localStorage.getItem('public-key')) { - /* If doesn't exist, generate public and private key pair, store in - local storage */ - crypto.subtle.generateKey( - { name:'RSA-OAEP', - modulusLength: 2048, - publicExponent: new Uint8Array([0x01, 0x00, 0x01]), - hash: {name: "SHA-256"} - }, - true, - ['encrypt', 'decrypt'] - ).then((keyPair) => { - /* TODO: Do we need to store the private key as well? */ - crypto.subtle.exportKey('jwk', keyPair.publicKey) - .then((exportedKey) => { - window.localStorage.setItem('publicKey', exportedKey) - console.log('public key is' + window.localStorage.getItem('publicKey')) - resolve(exportedKey) - }) - - }) - } - else { - resolve(window.localStorage.getItem('publicKey')) - } -}) - -} - -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) -} - -/* TODO: All this does is wrap a function in a promise. Allows pollServerForAnswer -to call itself recursively with the same 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 Answer from Host') - console.log(request) - resolve(request.response) - } - } - else if (request.status === 504) { - console.log('timed out, resending') - pollServerForAnswer(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, url, pubKey) { - let intervalID = window.setInterval(() => { - if (cpc.iceConnectionState.localeCompare('connected') !== 0 - && cpc.iceConnectionState.localeCompare('completed') !== 0) { - console.log('Client: Polling server begin for intervalID = ' + intervalID) - console.log('Client: Requesting ICE Candidates from server') - const request = new XMLHttpRequest() - request.open('GET', url, true) - request.setRequestHeader('Content-Type', 'application/json' ) - request.setRequestHeader('X-Strapp-Type', 'ice-candidate-request') - request.setRequestHeader('X-client-pubkey', pubKey) - request.onreadystatechange = () => { - if (request.status === 200) { - if(request.readyState === 4) { - console.log('Client: Recieved ICE response from Host') - let response = JSON.parse(request.response) - switch(response['iceState']) { - case "a": - cpc.addIceCandidate(new RTCIceCandidate(response.ice)) - break - case "g": /* Gathering so let interval keep polling */ - break - case "c": /* host iceState == Complete, stop bugging it */ - clearInterval(intervalID) - clearTimeout() - break - default: - console.log('Unhandled iceState in pollServerForICECandidate()' + response['iceState']) - break - } - } - } - else { - console.log('server unhandled response of status ' + request.status) - clearInterval(intervalID) - } - } - request.send() - } - else { - clearTimeout() - clearInterval(intervalID) - } - }, 5000) -} - -/* Create and send offer -> Send ICE Candidates -> Poll for ICE Candidates */ -getPublicKey().then((cpk) => { - console.log('Client: Create and send offer') - const cpc = new RTCPeerConnection(conf) - - cpc.oniceconnectionstatechange = () => { - console.log('iceConnectionState = ' + cpc.iceConnectionState) - } - - cpc.onnegotiationneeded = () => { - console.log('negotiation needed!') - 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: Polling for ICE candidates') - pollServerForICECandidate(cpc, window.location, cpk.n) - cpc.setRemoteDescription(answer.sdp) - 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) - }) - } - /* Start data channel */ - dataChannel = cpc.createDataChannel("sendChannel"); - dataChannel.onmessage = (msg) => { - /* Get mediaStream from host and add it to the video */ - let video = document.querySelector('') - } - dataChannel.onopen = () => { - dataChannel.send(`Hi from the Client`) - document.write(' ') - } - -}) -document.addEventListener('DOMContentLoaded', () => { - - document.body.innerHTML = `` - -}); +const body = document.createElement('body') +const root = document.createElement('div') +document.title = "Strapp.io Client" +const conf = {"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }] } +let dataChannel +let hostScreen + +/* TODO: duplicate in both client.js and host.js */ +function getPublicKey() { + return new Promise( (resolve, reject) => { + /* Check local storage for public key */ + if (!window.localStorage.getItem('public-key')) { + /* If doesn't exist, generate public and private key pair, store in + local storage */ + crypto.subtle.generateKey( + { name:'RSA-OAEP', + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: {name: "SHA-256"} + }, + true, + ['encrypt', 'decrypt'] + ).then((keyPair) => { + /* TODO: Do we need to store the private key as well? */ + crypto.subtle.exportKey('jwk', keyPair.publicKey) + .then((exportedKey) => { + window.localStorage.setItem('publicKey', exportedKey) + console.log('public key is' + window.localStorage.getItem('publicKey')) + resolve(exportedKey) + }) + + }) + } + else { + resolve(window.localStorage.getItem('publicKey')) + } +}) + +} + +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) +} + +/* TODO: All this does is wrap a function in a promise. Allows pollServerForAnswer +to call itself recursively with the same 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 Answer from Host') + console.log(request) + resolve(request.response) + } + } + else if (request.status === 504) { + console.log('timed out, resending') + pollServerForAnswer(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, url, pubKey) { + let intervalID = window.setInterval(() => { + if (cpc.iceConnectionState.localeCompare('connected') !== 0 + && cpc.iceConnectionState.localeCompare('completed') !== 0) { + console.log('Client: Polling server begin for intervalID = ' + intervalID) + console.log('Client: Requesting ICE Candidates from server') + const request = new XMLHttpRequest() + request.open('GET', url, true) + request.setRequestHeader('Content-Type', 'application/json' ) + request.setRequestHeader('X-Strapp-Type', 'ice-candidate-request') + request.setRequestHeader('X-client-pubkey', pubKey) + request.onreadystatechange = () => { + if (request.status === 200) { + if(request.readyState === 4) { + console.log('Client: Recieved ICE response from Host') + let response = JSON.parse(request.response) + switch(response['iceState']) { + case "a": + cpc.addIceCandidate(new RTCIceCandidate(response.ice)) + break + case "g": /* Gathering so let interval keep polling */ + break + case "c": /* host iceState == Complete, stop bugging it */ + clearInterval(intervalID) + clearTimeout() + break + default: + console.log('Unhandled iceState in pollServerForICECandidate()' + response['iceState']) + break + } + } + } + else { + console.log('server unhandled response of status ' + request.status) + clearInterval(intervalID) + } + } + request.send() + } + else { + clearTimeout() + clearInterval(intervalID) + } + }, 5000) +} + +/* Create and send offer -> Send ICE Candidates -> Poll for ICE Candidates */ +getPublicKey().then((cpk) => { + console.log('Client: Create and send offer') + const cpc = new RTCPeerConnection(conf) + + cpc.oniceconnectionstatechange = () => { + console.log('iceConnectionState = ' + cpc.iceConnectionState) + } + + cpc.onnegotiationneeded = () => { + console.log('negotiation needed!') + 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: Polling for ICE candidates') + pollServerForICECandidate(cpc, window.location, cpk.n) + cpc.setRemoteDescription(answer.sdp) + 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) + }) + } + /* Start data channel */ + dataChannel = cpc.createDataChannel("sendChannel"); + cpc.ontrack = (event) => { + console.log(`track event is ${event}`) + let remoteRTPSenders = cpc.getSenders() + let remoteRTPReceivers = cpc.getReceivers() + console.log(remoteRTPReceivers) + /* Add each remoteRTPSenders.track to own stream */ + let video = document.querySelector('video') + video.autoplay = true + console.log(video) + hostScreen = new MediaStream([remoteRTPReceivers[0].track]) + if(!video.srcObject) { + video.srcObject = hostScreen + } + console.log(hostScreen.getVideoTracks()) + console.log(video.srcObject) + video.play() + video.onloadedmetadata = () => { + video.play() + } + } + dataChannel.onmessage = (msg) => { + /* Get mediaStream from host and add it to the video */ + let hostMessage = JSON.parse(msg.data) + cpc.setRemoteDescription(hostMessage.sdp).then(() => { + cpc.createAnswer().then((answer) => { + return cpc.setLocalDescription(answer) + }).then(() => { + dataChannel.send(JSON.stringify({ + "cmd": "> screen dataChannel", + "sdp": cpc.localDescription + })) + }) + }) + + + } + dataChannel.onopen = () => { + document.body.innerHTML = (`
`) + } + +}) +document.addEventListener('DOMContentLoaded', () => { + + document.body.innerHTML = `` + +}); diff --git a/host.js b/host.js index 81aabc6..9814793 100644 --- a/host.js +++ b/host.js @@ -1,181 +1,213 @@ -document.title = "Strapp.io Host" - - -const conf = {"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }] } -const clients = new Map([]) -const iceCandidates = [] -let dataChannel - - -/* TODO: duplicate in both client.js and host.jhs */ -function getPublicKey() { - return new Promise( (resolve, reject) => { - /* Check local storage for public key */ - if (!window.localStorage.getItem('public-key')) { - console.log('public key is undefined') - /* If doesn't exist, generate public and private key pair, store in - local storage */ - crypto.subtle.generateKey( - { name:'RSA-OAEP', - modulusLength: 2048, - publicExponent: new Uint8Array([0x01, 0x00, 0x01]), - hash: {name: "SHA-256"} - }, - true, - ['encrypt', 'decrypt'] - ).then((keyPair) => { - /* TODO: Do we need to store the private key as well? */ - crypto.subtle.exportKey('jwk', keyPair.publicKey) - .then((exportedKey) => { - window.localStorage.setItem('publicKey', exportedKey) - console.log('public key is' + window.localStorage.getItem('publicKey')) - resolve(exportedKey) - }) - - }) - } - else { - resolve(window.localStorage.getItem('publicKey')) - } - }) -} - -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(() => { - getPublicKey().then((hpk) => { - hpc.onicecandidate = (event) => { - if (event.candidate) { - console.log('Host: Allocating ice candidate for client') - iceCandidates.push(JSON.stringify({ - cmd: "< ice pubKey", - ice: event.candidate, - hostPubKey: hpk.n, /* TODO: do we need to send this? */ - clientPubKey: offer.pubKey, - iceState: "a" - })) - } - else { - console.log('Host: Finished sending ICE candidates') - console.log(hpc) - } - } - console.log('Host: Sending answer to Client') - wsock.send(JSON.stringify({ - cmd: '< sdp pubKey', - sdp: hpc.localDescription, - hostPubKey: hpk.n, - clientPubKey: offer.pubKey - })) - hpc.ondatachannel = (evt) => { - dataChannel = evt.channel - console.log(evt) - dataChannel.onmessage = (msg) => { - console.log(msg.data) - } - dataChannel.onopen = () => { - dataChannel.send(`Hi ${offer.pubKey} -host`) - } - } - hpc.oniceconnectionstatechange = () => { - console.log('iceConnectionState = ' + hpc.iceConnectionState) - } - }) - }).catch((err) => { - console.log(`error in host answer ${err}`) - }) - }) - -} - -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: Handling ice candidate request') - console.log(iceCandidates) - const hpc = clients.get(msg.pubKey) - const iceCandidate = iceCandidates.pop() - if (iceCandidate !== undefined) { - wsock.send(iceCandidate) - } else { - if (hpc.iceGatheringState.localeCompare('gathering') === 0) { - wsock.send(`{"cmd" : "< ice pubKey", "clientPubKey":"${msg.pubKey}", "iceState": "g"}`) - } - else if (hpc.iceGatheringState.localeCompare('complete') === 0) { - wsock.send(`{"cmd" : "< ice pubKey", "clientPubKey":"${msg.pubKey}", "iceState": "c"}`) - } - - } - -} -if ("WebSocket" in window) { - document.addEventListener('DOMContentLoaded', (event) => { - wsock = new WebSocket(`${_strapp_protocol}://${window.location.hostname}:${_strapp_port}`) - wsock.onopen = () => { - console.log(`Strapped to ${_strapp_protocol}://${window.location.hostname}:${_strapp_port}`) - } - - wsock.onmessage = (serverMsg) => { - /* msg is either offer or ice candidate or ice candidate request*/ - - /* What if data null? */ - let msg = JSON.parse(serverMsg.data) - - const clientID = msg.pubKey - - /* TODO: redo this trash */ - if (clients.has(clientID)) { - if (msg.ice) { - handleNewIceSubmission(msg) - } else if (msg.sdp) { - //handleRepeatedOffer - } else { - handleIceRequest(msg) - } - } - else { - if (msg.ice) { - console.log('Host: Client that doesnt exist is sending ice submissions') - } else if (msg.sdp) { - handleNewClientConnection(msg) - } else { - console.log('Host: Client that doesnt exist is sending ice requests') - } - } - } - document.body.innerHTML = '
Choose options for client
' - - navigator.mediaDevices.getUserMedia({ video : { mediaSource: "screen", // whole screen sharing - // mediaSource: "window", // choose a window to share - // mediaSource: "application", // choose a window to share - width: {max: '1920'}, - height: {max: '1080'}, - frameRate: {max: '10'}} }) - .then(function(mediaStream) { - let video = document.querySelector('video') - video.srcObject = mediaStream - video.onloadedmetadata = function(e) { - video.play() - } - }) - .catch(function(err) { console.log(err); }); // always check for errors at the end. - }) -} -else { - document.addEventListener('DOMContentLoaded', () => { - document.body.innerHTML = 'Websockets not supported in your browser' - }) -} +document.title = "Strapp.io Host" + + +const conf = {"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }] } +const clients = new Map([]) +const iceCandidates = [] +let dataChannel +let screenStream /* TODO: Remove if can access localStreams */ +let tracks = [] + + +/* TODO: duplicate in both client.js and host.jhs */ +function getPublicKey() { + return new Promise( (resolve, reject) => { + /* Check local storage for public key */ + if (!window.localStorage.getItem('public-key')) { + console.log('public key is undefined') + /* If doesn't exist, generate public and private key pair, store in + local storage */ + crypto.subtle.generateKey( + { name:'RSA-OAEP', + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: {name: "SHA-256"} + }, + true, + ['encrypt', 'decrypt'] + ).then((keyPair) => { + /* TODO: Do we need to store the private key as well? */ + crypto.subtle.exportKey('jwk', keyPair.publicKey) + .then((exportedKey) => { + window.localStorage.setItem('publicKey', exportedKey) + console.log('public key is' + window.localStorage.getItem('publicKey')) + resolve(exportedKey) + }) + + }) + } + else { + resolve(window.localStorage.getItem('publicKey')) + } + }) +} + +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(() => { + getPublicKey().then((hpk) => { + hpc['hpk'] = hpk + hpc.onicecandidate = (event) => { + if (event.candidate) { + console.log('Host: Allocating ice candidate for client') + iceCandidates.push(JSON.stringify({ + cmd: "< ice pubKey", + ice: event.candidate, + hostPubKey: hpk.n, /* TODO: do we need to send this? */ + clientPubKey: offer.pubKey, + iceState: "a" + })) + } + else { + console.log('Host: Finished sending ICE candidates') + } + } + console.log('Host: Sending answer to Client') + wsock.send(JSON.stringify({ + cmd: '< sdp pubKey', + sdp: hpc.localDescription, + hostPubKey: hpk.n, + clientPubKey: offer.pubKey + })) + + + }) + }).catch((err) => { + console.log(`error in host answer ${err}`) + }) + }) + hpc.oniceconnectionstatechange = () => { + console.log('iceConnectionState = ' + hpc.iceConnectionState) + } + hpc.ondatachannel = (evt) => { + dataChannel = evt.channel + dataChannel.onmessage = (msg) => { + let clientMessage = JSON.parse(msg.data) + console.log(clientMessage) + hpc.setRemoteDescription(clientMessage.sdp).then(() => { + console.log('should be streaming now') + }) + } + dataChannel.onopen = () => { + screenStream.getTracks().forEach( (track) => { + hpc.addTrack(track, screenStream) + }) + console.log(hpc.getSenders()) + /* Create offer */ + hpc.createOffer().then((offer) => { + return hpc.setLocalDescription(offer) + }).then( () => { + dataChannel.send(JSON.stringify({ + "cmd": "< screen dataChannel", + "sdp": hpc.localDescription, + "pubKey": hpc['hpk'].n + })) + }) + } + } + hpc.onnegotiationneeded = () => { + console.log('negotiation needed') + } + +} + +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: Handling ice candidate request') + console.log(iceCandidates) + const hpc = clients.get(msg.pubKey) + const iceCandidate = iceCandidates.pop() + if (iceCandidate !== undefined) { + wsock.send(iceCandidate) + } else { + if (hpc.iceGatheringState.localeCompare('gathering') === 0) { + wsock.send(`{"cmd" : "< ice pubKey", "clientPubKey":"${msg.pubKey}", "iceState": "g"}`) + } + else if (hpc.iceGatheringState.localeCompare('complete') === 0) { + wsock.send(`{"cmd" : "< ice pubKey", "clientPubKey":"${msg.pubKey}", "iceState": "c"}`) + } + + } + +} +if ("WebSocket" in window) { + document.addEventListener('DOMContentLoaded', (event) => { + document.body.innerHTML = '
Choose options for client
' + navigator.mediaDevices.getUserMedia({ + video : { mediaSource: "screen", + width: {max: '1920'}, + height: {max: '1080'}, + frameRate: {max: '10'}} }) + .then(function(mediaStream) { + let video = document.querySelector('video') + screenStream = mediaStream + console.log(mediaStream) + video.srcObject = mediaStream + console.log('Grabbed media') + video.onloadedmetadata = function(e) { + console.log(e) + video.play() + } + }) + .catch(function(err) { + document.body.innerHTML = 'Help me help you. Reload the page and allow screen sharing!' + console.log(err); + }); // always check for errors at the end. + + wsock = new WebSocket(`${_strapp_protocol}://${window.location.hostname}:${_strapp_port}`) + wsock.onopen = () => { + console.log(`Strapped to ${_strapp_protocol}://${window.location.hostname}:${_strapp_port}`) + } + + wsock.onmessage = (serverMsg) => { + /* msg is either offer or ice candidate or ice candidate request*/ + + /* What if data null? */ + let msg = JSON.parse(serverMsg.data) + + const clientID = msg.pubKey + + /* TODO: redo this trash */ + if (clients.has(clientID)) { + if (msg.ice) { + handleNewIceSubmission(msg) + } else if (msg.sdp) { + //handleRepeatedOffer + } else { + handleIceRequest(msg) + } + } + else { + if (msg.ice) { + console.log('Host: Client that doesnt exist is sending ice submissions') + } else if (msg.sdp) { + handleNewClientConnection(msg) + } else { + console.log('Host: Client that doesnt exist is sending ice requests') + } + } + } + + + + }) +} +else { + document.addEventListener('DOMContentLoaded', () => { + document.body.innerHTML = 'Websockets not supported in your browser' + }) +} diff --git a/main.js b/main.js index 321af88..136aee1 100644 --- a/main.js +++ b/main.js @@ -1,220 +1,220 @@ -/** -* @file Node entry and main driver -* @author Jordan Lavatai, Ken Grimes -* @version 0.0.1 -* @license AGPL-3.0 -* @copyright loljk 2017 -* @summary HTTP(S) Router that uses the first directory in the requested URL -* as the route name -*/ -const fs = require('fs') -const ws = require('ws') -const path = require('path') -const http = require('http') -const https = require('https') -const getport = require('get-port') -const mime = require('mime') -const opts = require('./opts.js') - -const router = { - skelPage: fs.readFileSync('./skel.html', { encoding: 'utf8' }).split(''), - clientJS: fs.readFileSync(opts['client-js']), - hostJS: fs.readFileSync(opts['host-js']), - routes: {}, - httpsOpt: undefined, - httpd: undefined, - wsProtocol: opts['no-tls'] ? 'ws' : 'wss', - respond: (request,response) => { - console.log('server handling request') - const serveFile = (fPath) => { - fs.readFile(fPath, { encoding: 'utf8' }, (err, data) => { - if (err || data == undefined) { - response.writeHead(404) - response.end() - } - else { - response.writeHead(200, { 'Content-Type': mime.lookup(fPath) }) - response.write(data) - response.end() - } - }) - } - 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']) { - let localPath = path.normalize(opts['bindings'][routeName].concat(path.sep + routePath.slice(1).join(path.sep))) - if (localPath.includes(opts['bindings'][routeName])) { - fs.readdir(localPath, (err, files) => { - if (err) - serveFile(localPath) - else - serveFile(`${localPath}/index.html`) - }) - } - else { - console.log(`SEC: ${localPath} references files not in route`) - } - } - /* 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'] - - - - - /* Client is INIT GET */ - 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]}`) - response.end() - //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 if (headerData.localeCompare('ice-candidate-request') === 0) { - console.log('Server: received ice-candidate-request from Client') - let pubKey = request.headers['x-client-pubkey'] - clients.set(pubKey, response) - pubKey = '{ "pubKey": "' + pubKey + '" }' - route.socket.send(pubKey) - } - 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 = 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() - else - 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 - - /* 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: Handling host ICE message') - let iceState = hostMessage['iceState'] - /* If there are any ice candidates, send them back */ - switch(iceState) { - case "a": - response.writeHead('200', {'x-strapp-type': 'ice-candidate-available'}) - response.write(JSON.stringify(hostMessage)) - response.end() - break - case "g": - console.log('Server: Host is still gathering candidates, keep trying') - response.writeHead('200', {'x-strapp-type': 'ice-state-gathering'}) - response.write(JSON.stringify(hostMessage)) - response.end() - break - case "c": - console.log('Server: Host has completed gathering candidates') - response.writeHead('200', {'x-strapp-type': 'ice-state-complete'}) - response.write(JSON.stringify(hostMessage)) - response.end() - break - default: - console.log('unhandled iceState from host') - break - } - } - - }) - }) - - console.log(`Listening for websocket ${newRoute.host} on port ${newRoute.port}`) - router.routes[routeName] = newRoute - }).then(() => { - response.writeHead(200, { 'Content-Type': 'text/html' }) - response.write(`${router.skelPage[0]}` + - `\tconst _strapp_port = ${newRoute.port}\n` + - `\tconst _strapp_protocol = '${router.wsProtocol}'\n` + - `${router.hostJS}\n${router.skelPage[1]}`) - response.end() - }) - } - - - } - } - - /** - * @summary Boot up the router. With TLS, we must wait for file reads to sync. - */ - if (!opts['no-tls']) { - console.log('tls') - let filesRead = 0 - let key = undefined - let cert = undefined - const syncRead = () => { - if (++filesRead == 2) { - if (key == undefined) - console.log(`ERR: Key ${opts['ca-key']} inaccessible, tls will fail`) - if(cert == undefined) - console.log(`ERR: Cert ${opts['ca-cert']} inaccessible, tls will fail`) - else if (key != undefined) { - router.httpsOpts = { cert: cert, key: key} - router.httpd = https.createServer(router.httpsOpts, router.respond) - .listen(opts['port']) - } - } - } - fs.readFile(opts['ca-key'], { encoding: 'utf8' }, (err, data) => { - if (!err) key = data - syncRead() - }) - fs.readFile(opts['ca-cert'], { encoding: 'utf8' }, (err, data) => { - if (!err) cert = data - syncRead() - }) - } - else - router.httpd = http.createServer(router.respond).listen(opts['port']) - - //TODO: if ("electron" in process.versions) open a local renderwindow, and route to it +/** +* @file Node entry and main driver +* @author Jordan Lavatai, Ken Grimes +* @version 0.0.1 +* @license AGPL-3.0 +* @copyright loljk 2017 +* @summ ary HTTP(S) Router that uses the first directory in the requested URL +* as the route name +*/ +const fs = require('fs') +const ws = require('ws') +const path = require('path') +const http = require('http') +const https = require('https') +const getport = require('get-port') +const mime = require('mime') +const opts = require('./opts.js') + +const router = { + skelPage: fs.readFileSync('./skel.html', { encoding: 'utf8' }).split(''), + clientJS: fs.readFileSync(opts['client-js']), + hostJS: fs.readFileSync(opts['host-js']), + routes: {}, + httpsOpt: undefined, + httpd: undefined, + wsProtocol: opts['no-tls'] ? 'ws' : 'wss', + respond: (request,response) => { + console.log('server handling request') + const serveFile = (fPath) => { + fs.readFile(fPath, { encoding: 'utf8' }, (err, data) => { + if (err || data == undefined) { + response.writeHead(404) + response.end() + } + else { + response.writeHead(200, { 'Content-Type': mime.lookup(fPath) }) + response.write(data) + response.end() + } + }) + } + 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']) { + let localPath = path.normalize(opts['bindings'][routeName].concat(path.sep + routePath.slice(1).join(path.sep))) + if (localPath.includes(opts['bindings'][routeName])) { + fs.readdir(localPath, (err, files) => { + if (err) + serveFile(localPath) + else + serveFile(`${localPath}/index.html`) + }) + } + else { + console.log(`SEC: ${localPath} references files not in route`) + } + } + /* 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'] + + + + + /* Client is INIT GET */ + 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]}`) + response.end() + //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 if (headerData.localeCompare('ice-candidate-request') === 0) { + console.log('Server: received ice-candidate-request from Client') + let pubKey = request.headers['x-client-pubkey'] + clients.set(pubKey, response) + pubKey = '{ "pubKey": "' + pubKey + '" }' + route.socket.send(pubKey) + } + 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 = 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() + else + 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 + + /* 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: Handling host ICE message') + let iceState = hostMessage['iceState'] + /* If there are any ice candidates, send them back */ + switch(iceState) { + case "a": + response.writeHead('200', {'x-strapp-type': 'ice-candidate-available'}) + response.write(JSON.stringify(hostMessage)) + response.end() + break + case "g": + console.log('Server: Host is still gathering candidates, keep trying') + response.writeHead('200', {'x-strapp-type': 'ice-state-gathering'}) + response.write(JSON.stringify(hostMessage)) + response.end() + break + case "c": + console.log('Server: Host has completed gathering candidates') + response.writeHead('200', {'x-strapp-type': 'ice-state-complete'}) + response.write(JSON.stringify(hostMessage)) + response.end() + break + default: + console.log('unhandled iceState from host') + break + } + } + + }) + }) + + console.log(`Listening for websocket ${newRoute.host} on port ${newRoute.port}`) + router.routes[routeName] = newRoute + }).then(() => { + response.writeHead(200, { 'Content-Type': 'text/html' }) + response.write(`${router.skelPage[0]}` + + `\tconst _strapp_port = ${newRoute.port}\n` + + `\tconst _strapp_protocol = '${router.wsProtocol}'\n` + + `${router.hostJS}\n${router.skelPage[1]}`) + response.end() + }) + } + + + } + } + + /** + * @summary Boot up the router. With TLS, we must wait for file reads to sync. + */ + if (!opts['no-tls']) { + console.log('tls') + let filesRead = 0 + let key = undefined + let cert = undefined + const syncRead = () => { + if (++filesRead == 2) { + if (key == undefined) + console.log(`ERR: Key ${opts['ca-key']} inaccessible, tls will fail`) + if(cert == undefined) + console.log(`ERR: Cert ${opts['ca-cert']} inaccessible, tls will fail`) + else if (key != undefined) { + router.httpsOpts = { cert: cert, key: key} + router.httpd = https.createServer(router.httpsOpts, router.respond) + .listen(opts['port']) + } + } + } + fs.readFile(opts['ca-key'], { encoding: 'utf8' }, (err, data) => { + if (!err) key = data + syncRead() + }) + fs.readFile(opts['ca-cert'], { encoding: 'utf8' }, (err, data) => { + if (!err) cert = data + syncRead() + }) + } + else + router.httpd = http.createServer(router.respond).listen(opts['port']) + + //TODO: if ("electron" in process.versions) open a local renderwindow, and route to it diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7d8e3ad --- /dev/null +++ b/package-lock.json @@ -0,0 +1,677 @@ +{ + "name": "strapp", + "version": "0.0.1", + "lockfileVersion": 1, + "dependencies": { + "@types/node": { + "version": "https://registry.npmjs.org/@types/node/-/node-7.0.32.tgz", + "integrity": "sha1-av5sZlIKTDFmI6FK7xI5CNAbS7o=" + }, + "ajv": { + "version": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=" + }, + "ansi-regex": { + "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "array-find-index": { + "version": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "asn1": { + "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "asynckit": { + "version": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "balanced-match": { + "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true + }, + "boom": { + "version": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=" + }, + "brace-expansion": { + "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=" + }, + "builtin-modules": { + "version": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "camelcase": { + "version": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "camelcase-keys": { + "version": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=" + }, + "caseless": { + "version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "co": { + "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=" + }, + "concat-map": { + "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dependencies": { + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", + "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=" + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=" + } + } + }, + "core-util-is": { + "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=" + }, + "currently-unhandled": { + "version": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=" + }, + "dashdash": { + "version": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dependencies": { + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=" + }, + "decamelize": { + "version": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "deep-extend": { + "version": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" + }, + "delayed-stream": { + "version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "ecc-jsbn": { + "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true + }, + "electron": { + "version": "https://registry.npmjs.org/electron/-/electron-1.6.11.tgz", + "integrity": "sha1-vnnA69zv7bW/KBF0CYAPpTus7/o=" + }, + "electron-download": { + "version": "https://registry.npmjs.org/electron-download/-/electron-download-3.3.0.tgz", + "integrity": "sha1-LP1U1pZsAZxNSa1l++Zcyc3vaMg=" + }, + "error-ex": { + "version": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=" + }, + "es6-promise": { + "version": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.0.tgz", + "integrity": "sha1-3aA8qPn4m8WX5omEKSnee6jOvfA=" + }, + "extend": { + "version": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extract-zip": { + "version": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.5.tgz", + "integrity": "sha1-maBnNbbqIOqbcF13ms/8yHz/BEA=", + "dependencies": { + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=" + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + } + } + }, + "extsprintf": { + "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=" + }, + "fd-slicer": { + "version": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=" + }, + "find-up": { + "version": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=" + }, + "forever-agent": { + "version": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=" + }, + "fs-extra": { + "version": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=" + }, + "fs.realpath": { + "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "get-port": { + "version": "https://registry.npmjs.org/get-port/-/get-port-3.1.0.tgz", + "integrity": "sha1-7wGxioTKZIaXD/meVERhQac//T4=" + }, + "get-stdin": { + "version": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "getpass": { + "version": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dependencies": { + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=" + }, + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "har-schema": { + "version": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=" + }, + "hawk": { + "version": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=" + }, + "hoek": { + "version": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "home-path": { + "version": "https://registry.npmjs.org/home-path/-/home-path-1.0.5.tgz", + "integrity": "sha1-eIspgVsS1Tus9XVkhHbm+QQdEz8=" + }, + "hosted-git-info": { + "version": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz", + "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=" + }, + "http-signature": { + "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=" + }, + "indent-string": { + "version": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=" + }, + "inflight": { + "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" + }, + "inherits": { + "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "is-arrayish": { + "version": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-builtin-module": { + "version": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=" + }, + "is-finite": { + "version": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=" + }, + "is-fullwidth-code-point": { + "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=" + }, + "is-typedarray": { + "version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isstream": { + "version": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-stable-stringify": { + "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=" + }, + "json-stringify-safe": { + "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=" + }, + "jsonify": { + "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "dependencies": { + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "klaw": { + "version": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=" + }, + "load-json-file": { + "version": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=" + }, + "loud-rejection": { + "version": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=" + }, + "map-obj": { + "version": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "meow": { + "version": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=" + }, + "mime": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", + "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=" + }, + "mime-db": { + "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=" + }, + "mime-types": { + "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=" + }, + "minimatch": { + "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=" + }, + "minimist": { + "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "dependencies": { + "minimist": { + "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "normalize-package-data": { + "version": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz", + "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=" + }, + "nugget": { + "version": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", + "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=" + }, + "number-is-nan": { + "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-keys": { + "version": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, + "once": { + "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + }, + "parse-json": { + "version": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=" + }, + "path-exists": { + "version": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=" + }, + "path-is-absolute": { + "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-type": { + "version": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=" + }, + "pend": { + "version": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "pify": { + "version": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=" + }, + "pretty-bytes": { + "version": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", + "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=" + }, + "process-nextick-args": { + "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "progress-stream": { + "version": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz", + "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=" + }, + "punycode": { + "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "rc": { + "version": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=" + }, + "read-pkg": { + "version": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=" + }, + "read-pkg-up": { + "version": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=" + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=" + }, + "redent": { + "version": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=" + }, + "repeating": { + "version": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=" + }, + "request": { + "version": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=" + }, + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=" + }, + "safe-buffer": { + "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" + }, + "semver": { + "version": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "signal-exit": { + "version": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "single-line-log": { + "version": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", + "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=" + }, + "sntp": { + "version": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=" + }, + "spdx-correct": { + "version": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=" + }, + "spdx-expression-parse": { + "version": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=" + }, + "spdx-license-ids": { + "version": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" + }, + "speedometer": { + "version": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", + "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=" + }, + "sshpk": { + "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dependencies": { + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "string-width": { + "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=" + }, + "stringstream": { + "version": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=" + }, + "strip-bom": { + "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=" + }, + "strip-indent": { + "version": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=" + }, + "strip-json-comments": { + "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "sumchecker": { + "version": "https://registry.npmjs.org/sumchecker/-/sumchecker-1.3.1.tgz", + "integrity": "sha1-ebs7RFbdBPGOvbwNcDodHa7FEF0=" + }, + "throttleit": { + "version": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", + "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=" + }, + "through2": { + "version": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", + "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=" + }, + "tough-cookie": { + "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=" + }, + "trim-newlines": { + "version": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "tunnel-agent": { + "version": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=" + }, + "tweetnacl": { + "version": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "typedarray": { + "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "ultron": { + "version": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz", + "integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ=" + }, + "util-deprecate": { + "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" + }, + "validate-npm-package-license": { + "version": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=" + }, + "verror": { + "version": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=" + }, + "wrappy": { + "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "https://registry.npmjs.org/ws/-/ws-3.0.0.tgz", + "integrity": "sha1-mN2wAFbIOQy3Ued4h4hJf5kQO2w=", + "dependencies": { + "safe-buffer": { + "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" + } + } + }, + "xtend": { + "version": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=" + }, + "yauzl": { + "version": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=" + } + } +} diff --git a/router.js b/router.js new file mode 100644 index 0000000..2f0441a --- /dev/null +++ b/router.js @@ -0,0 +1,22 @@ +/** + * @file HTTP(S) Router that treats the first directory in a URL's path as + * a route to a host. + * @author Ken Grimes + * @version 0.0.1 + * @license AGPL-3.0 + * @copyright jk software 2017 + */ +const fs = require('fs') + +module.exports = (opts) => { + const startHttpd = (listener) => { + if (opts['no-tls']) + return require('http').createServer(listener) + if (fs.existsSync(opts['ca-cert'])) + + } + return { + httpd: startHttpd(), + wsd: startWsd() + } +} diff --git a/usage b/usage index d740aca..25b4715 100644 --- a/usage +++ b/usage @@ -10,17 +10,15 @@ CONFIG can override any of the previous file's settings. (/etc/strapp.conf:~/.strapp/strapp.conf:./strapp.conf) - config settings are overridden by command line opts - except where noted -j, --client-js=path Path to the client Strapp code (./client.js) -J, --host-js=path Path to the host Strapp code (./host.js) - -T, --no-tls=bool Don't use HTTPS and WSS protocols (false) - - makes 'ca-cert' and 'ca-key' unnecessary - -C, --ca-cert=path Accessible location of the CA Cert (../certs/cert.pem) -K, --ca-key=path Accessible location of the CA Key (../certs/key.pem) - -p, --port=number The local port to bind HTTPS listener to (2443) - -i, --index=path File serviced at the root domain (./index.html) + -p, --port=number The local port to bind HTTP/S listener to (2443) + -d, --file-dir=path Path to the directory that will serve files over HTTP + if a client's requested route doesn't exist and can't + be created ROUTING -b, --bind=[string:path[,string:path]]... @@ -49,6 +47,9 @@ ROUTING be established, so no remote hosts may exist COMPATIBILITY + --no-tls=bool Don't use HTTPS and WSS protocols (false) + - makes 'ca-cert' and 'ca-key' unnecessary + --legacy-socket=bool Use Socket.io compatibility layer to enable long-polling and AJAX fallbacks (false) - enables optional socket.io dependency