UNPKG

rtcmulticonnection

Version:

RTCMultiConnection is a WebRTC JavaScript wrapper library runs top over RTCPeerConnection API to support all possible peer-to-peer features.

470 lines (406 loc) 16.5 kB
// Muaz Khan - www.MuazKhan.com // MIT License - www.WebRTC-Experiment.com/licence // Documentation - github.com/muaz-khan/RTCMultiConnection var fs = require('fs'); var path = require('path'); var resolveURL = require('./node_scripts/resolveURL.js'); var BASH_COLORS_HELPER = require('./node_scripts/BASH_COLORS_HELPER.js'); var config = require('./node_scripts/get-values-from-config-json.js'); require('./node_scripts/get-bash-parameters.js')(config, BASH_COLORS_HELPER); var isAdminAuthorized = require('./node_scripts/verify-admin.js'); // pushLogs is used to write error logs into logs.json var pushLogs = function(name, error) { console.log(name, error); }; try { pushLogs = require('./node_scripts/pushLogs.js'); } catch (e) { console.log('Unable to read pushLogs.js', e); } var server = require(config.isUseHTTPs ? 'https' : 'http'); var url = require('url'); function serverHandler(request, response) { try { var uri, filename; try { uri = url.parse(request.url).pathname; filename = path.join(process.cwd(), uri); } catch(e) { pushLogs('url.parse', e); } if (request.method !== 'GET' || uri.indexOf('..') !== -1) { try { response.writeHead(401, { 'Content-Type': 'text/plain' }); response.write('401 Unauthorized: ' + path.join('/', uri) + '\n'); response.end(); return; } catch(e) { pushLogs('!GET or ..', e); } } var matched = false; filename && ['/demos/', '/dev/', '/dist/', '/socket.io/', '/admin/'].forEach(function(item) { if (filename.indexOf(resolveURL(item)) !== -1) { matched = true; } }); if(filename.indexOf(resolveURL('/logs.json')) !== -1 && isAdminAuthorized(request, config)) { try { // uncache to fetch recent (up-to-dated) var uncache = require('./node_scripts/uncache.js'); uncache('../logs.json'); var logs = require('./logs.json'); response.writeHead(200, { 'Content-Type': 'text/plain' }); response.write(JSON.stringify(logs)); response.end(); return; } catch(e) { pushLogs('/logs.json', e); } } // handling /admin/ page if (filename && (filename.indexOf('/admin/') !== -1 || filename.indexOf('\\admin\\') !== -1)) { if (!isAdminAuthorized(request, config)) { try { response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Node"' }); response.write('401 Unauthorized\n'); response.end(); return; } catch(e) { pushLogs('/admin/ auth issues', e); } } // these three values are used inside Signaling-Server.js app.request = request; app.isAdminAuthorized = isAdminAuthorized; app.config = config; fs.readFile('admin/index.html', 'binary', function(err, file) { try { if (err) { response.writeHead(500, { 'Content-Type': 'text/plain' }); response.write('404 Not Found: admin/index.html\n'); response.end(); return; } response.writeHead(200, { 'Content-Type': 'text/html' }); response.write(file, 'binary'); response.end(); } catch(e) { pushLogs('admin/index.html', e); } }); return; } if (filename && filename.search(/.js|.json/g) !== -1 && !matched) { try { response.writeHead(404, { 'Content-Type': 'text/plain' }); response.write('404 Not Found: ' + path.join('/', uri) + '\n'); response.end(); return; } catch(e) { pushLogs('404 Not Found', e); } } ['Video-Broadcasting', 'Screen-Sharing', 'Switch-Cameras'].forEach(function(fname) { try { if (filename && filename.indexOf(fname + '.html') !== -1) { filename = filename.replace(fname + '.html', fname.toLowerCase() + '.html'); } } catch(e) { pushLogs('forEach', e); } }); var stats; try { stats = fs.lstatSync(filename); if (filename && filename.search(/demos/g) === -1 && stats.isDirectory() && config.defaultDemo === '/demos/index.html') { if (response.redirect) { response.redirect('/demos/'); } else { response.writeHead(301, { 'Location': '/demos/' }); } response.end(); return; } } catch (e) { response.writeHead(404, { 'Content-Type': 'text/plain' }); response.write('404 Not Found: ' + path.join('/', uri) + '\n'); response.end(); return; } try { if (fs.statSync(filename).isDirectory()) { response.writeHead(404, { 'Content-Type': 'text/html' }); if (filename.indexOf(resolveURL('/demos/MultiRTC/')) !== -1) { filename = filename.replace(resolveURL('/demos/MultiRTC/'), ''); filename += resolveURL('/demos/MultiRTC/index.html'); } else if (filename.indexOf(resolveURL('/demos')) !== -1) { filename = filename.replace(resolveURL('/demos/'), ''); filename = filename.replace(resolveURL('/demos'), ''); filename += resolveURL('/demos/index.html'); } else { filename += resolveURL(config.defaultDemo); } } } catch(e) { pushLogs('statSync.isDirectory', e); } var contentType = 'text/plain'; if (filename.toLowerCase().indexOf('.html') !== -1) { contentType = 'text/html'; } if (filename.toLowerCase().indexOf('.css') !== -1) { contentType = 'text/css'; } if (filename.toLowerCase().indexOf('.png') !== -1) { contentType = 'image/png'; } fs.readFile(filename, 'binary', function(err, file) { if (err) { response.writeHead(500, { 'Content-Type': 'text/plain' }); response.write('404 Not Found: ' + path.join('/', uri) + '\n'); response.end(); return; } try { file = file.replace('connection.socketURL = \'/\';', 'connection.socketURL = \'' + config.socketURL + '\';'); } catch (e) {} response.writeHead(200, { 'Content-Type': contentType }); response.write(file, 'binary'); response.end(); }); } catch (e) { pushLogs('Unexpected', e); response.writeHead(404, { 'Content-Type': 'text/plain' }); response.write('404 Not Found: Unexpected error.\n'); response.end(); } } var app; try { if (config.isUseHTTPs) { // See how to use a valid certificate: // https://github.com/muaz-khan/WebRTC-Experiment/issues/62 var options = { key: null, cert: null, ca: null }; if (!fs.existsSync(config.sslKey)) { console.log(BASH_COLORS_HELPER.getRedFG(), 'sslKey:\t ' + config.sslKey + ' does not exist.'); } if (!fs.existsSync(config.sslCert)) { console.log(BASH_COLORS_HELPER.getRedFG(), 'sslCert:\t ' + config.sslCert + ' does not exist.'); } options.key = fs.readFileSync(config.sslKey); options.cert = fs.readFileSync(config.sslCert); if (config.sslCabundle) { if (!fs.existsSync(config.sslCabundle)) { console.log(BASH_COLORS_HELPER.getRedFG(), 'sslCabundle:\t ' + config.sslCabundle + ' does not exist.'); } options.ca = fs.readFileSync(config.sslCabundle); } app = server.createServer(options, serverHandler); } else { app = server.createServer(serverHandler); } } catch(e) { pushLogs('createServer', e); } function runServer() { try { app.on('error', function(e) { pushLogs('app.onerror', e); if (e.code != 'EADDRINUSE') return; try { function cmd_exec(cmd, args, cb_stdout, cb_end) { try { var spawn = require('child_process').spawn; var child = spawn(cmd, args); var me = this; me.exit = 0; me.stdout = ""; child.stdout.on('data', function(data) { try { cb_stdout(me, data); } catch(e) { pushLogs('stdout.data', e); } }); child.stdout.on('end', function() { try { cb_end(me); } catch(e) { pushLogs('stdout.end', e); } }); } catch(e) { pushLogs('cmd_exec', e); } } function log_console() { try { console.log(foo.stdout); var pidToBeKilled = foo.stdout.split('\nnode ')[1].split(' ')[0]; console.log('------------------------------'); console.log('Please execute below command:'); console.log('\x1b[31m%s\x1b[0m ', 'kill ' + pidToBeKilled); console.log('Then try to run "server.js" again.'); console.log('------------------------------'); } catch (e) { pushLogs('log_console', e); } } if (e.address === '0.0.0.0') { e.address = 'localhost'; } var socketURL = (config.isUseHTTPs ? 'https' : 'http') + '://' + e.address + ':' + e.port + '/'; console.log('------------------------------'); console.log('\x1b[31m%s\x1b[0m ', 'Unable to listen on port: ' + e.port); console.log('\x1b[31m%s\x1b[0m ', socketURL + ' is already in use. Please kill below processes using "kill PID".'); console.log('------------------------------'); foo = new cmd_exec('lsof', ['-n', '-i4TCP:9001'], function(me, data) { try { me.stdout += data.toString(); } catch(e) { pushLogs('lsof', e); } }, function(me) { try { me.exit = 1; } catch(e) { pushLogs('lsof.exit', e); } } ); setTimeout(log_console, 250); } catch(e) { pushLogs('app.onerror.EADDRINUSE', e); } }); app = app.listen(config.port, process.env.IP || '0.0.0.0', function(error) { try { var addr = app.address(); if (addr.address === '0.0.0.0') { addr.address = 'localhost'; } var domainURL = (config.isUseHTTPs ? 'https' : 'http') + '://' + addr.address + ':' + addr.port + '/'; console.log('\n'); console.log('Socket.io is listening at:'); console.log(BASH_COLORS_HELPER.getGreenFG(), '\t' + domainURL); if (!config.isUseHTTPs) { console.log('You can use --ssl to enable HTTPs:'); console.log(BASH_COLORS_HELPER.getYellowFG(), '\t' + 'node server --ssl'); } console.log('Your web-browser (HTML file) MUST set this line:'); console.log(BASH_COLORS_HELPER.getGreenFG(), '\tconnection.socketURL = "' + domainURL + '";'); if (addr.address != 'localhost' && !config.isUseHTTPs) { console.log(BASH_COLORS_HELPER.getRedBG(), 'Warning:'); console.log(BASH_COLORS_HELPER.getRedBG(), 'Please run on HTTPs to make sure audio,video and screen demos can work on Google Chrome as well.'); } console.log('For more help: ', BASH_COLORS_HELPER.getYellowFG('node server.js --help')); console.log('\n'); } catch(e) { pushLogs('app.listen.callback', e); } }); } catch(e) { pushLogs('app.listen', e); } try { require('./node_scripts/Signaling-Server.js')(app, function(socket) { try { var params = socket.handshake.query; // "socket" object is totally in your own hands! // do whatever you want! // in your HTML page, you can access socket as following: // connection.socketCustomEvent = 'custom-message'; // var socket = connection.getSocket(); // socket.emit(connection.socketCustomEvent, { test: true }); if (!params.socketCustomEvent) { params.socketCustomEvent = 'custom-message'; } socket.on(params.socketCustomEvent, function(message) { try { socket.broadcast.emit(params.socketCustomEvent, message); } catch (e) { pushLogs('socket.broadcast.socketCustomEvent'); } }); } catch (e) { pushLogs('Signaling-Server.callback', e); } }); } catch(e) { pushLogs('require.Signaling-Server', e); } } if (config.autoRebootServerOnFailure) { try { // auto restart app on failure var cluster = require('cluster'); if (cluster.isMaster) { cluster.fork(); cluster.on('exit', function(worker, code, signal) { try { cluster.fork(); } catch(e) { pushLogs('cluster.exit.fork', e); } }); } if (cluster.isWorker) { runServer(); } } catch(e) { pushLogs('cluster.require.fork', e); } } else { runServer(); }