-const electron = require('electron')
-const http = require('http')
-const path = require('path')
-const url = require('url')
+/**
+* @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 app = electron.app
-const BrowserWindow = electron.BrowserWindow
-const ipcMain = electron.ipcMain
+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')
-let mainWindow
+const router = {
+ skelPage: fs.readFileSync('./skel.html', { encoding: 'utf8' }).split('<!--STRAPP_SRC-->'),
+ 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]
-function beginRendererProcess() {
- mainWindow = new BrowserWindow({show: true, webPreferences: { webSecurity: false, experimentalFeatures: true}})
- mainWindow.loadURL(url.format({
- pathname: path.join(__dirname, 'renderer.html'),
- protocol: 'file:',
- slashes: true
- }))
- mainWindow.webContents.openDevTools()
-}
-function captureStream() {
- mainWindow.webContents.send('defaultStreamRequest', 'slr')
- let p = new Promise((resolve,reject) => {
- ipcMain.on('defaultStreamResponse', (event, arg) => {
- console.log('received defaultStreamResponse')
- console.log(arg)
- resolve(arg)
- })
- })
+ 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']
- return p
-}
-const routes = {
- '/': function root(response) {
- response.write(fs.readFileSync('admin.html'))
- response.end()
- },
- '/admin.js': function js(response) {
- response.write(fs.readFileSync('admin.js'))
- response.end()
- },
- '/admin.css': function css(response) {
- response.write(fs.readFileSync('admin.css'))
- response.end()
- },
- '/favicon.ico': function favicon() {
- response.end()
- },
- '/streamlist': function streamlist(response) {
- let mediaStream = captureStream().then((stream) => {
- console.log(stream)
- })
+ /* 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 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)
+ }
- //response.write(mediaStream)
- }
-}
+ }
+ 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'])
-app.on('ready', function() {
- beginRendererProcess()
+ /* 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')
+ }
+ })
+ })
- http.createServer(function(request, response) {
- const url = request.url
- console.log('routing to ' + url);
- routes[url](response)
+ 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'])
- }).listen(5140);
-});
+ //TODO: if ("electron" in process.versions) open a local renderwindow, and route to it