UNPKG

dreemgl

Version:

DreemGL is an open-source multi-screen prototyping framework for mediated environments, with a visual editor and shader styling for webGL and DALi runtimes written in JavaScript. As a toolkit for gpu-accelerated multiscreen development, DreemGL includes

384 lines (345 loc) 11.5 kB
/* DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others. Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.*/ define.class(function(require){ // Teem server var http = require('http') var os = require('os') var path = require('path') var fs = require('fs') var url = require('url') var zlib = require('zlib') var FileWatcher = require('./filewatcher') var ExternalApps = require('./externalapps') var NodeWebSocket = require('./nodewebsocket') var mimeFromFile = require('./mimefromfile') var CompositionServer = require('./compositionserver') var XMLConverter = require('./xmlconverter') var BusServer = require('$system/rpc/busserver') // Doccumentation this.atConstructor = function(args){ this.compositions = {} this.binrpc_incoming = {} this.binrpc_outgoing = {} this.args = args var port = this.args['-port'] || process.env.PORT || 2000 var iface = this.args['-iface'] || process.env.IP || '127.0.0.1' this.cache_gzip = define.makeCacheDir('gzip') this.server = http.createServer(this.request.bind(this)) this.server.listen(port, iface) this.server.on('upgrade', this.upgrade.bind(this)) this.addresses = [] if(iface == '0.0.0.0'){ var ifaces = os.networkInterfaces() var txt = '' Object.keys(ifaces).forEach(function(ifname){ var alias = 0; ifaces[ifname].forEach(function(iface){ if ('IPv4' !== iface.family) return var addr = 'http://' + iface.address + ':' + port + '/' if(!this.address) this.address = addr this.addresses.push('http://' + iface.address + ':' + port) if(iface.address == '127.0.0.1'){ this.addresses.push('http://localhost:'+port) } txt += ' ~~on ~c~'+addr }.bind(this)) }.bind(this)) console.color('Server running' + txt + '~~ Ready to go!\n') } else { this.address = 'http://' + iface + ':' + port + '/' this.addresses.push('http://' + iface + ':' + port) if(iface == '127.0.0.1'){ this.addresses.push('http://localhost:'+port) } console.color('Server running on ~c~' + this.address + "~~\n") } if(iface === '127.0.0.1'){ Object.defineProperty(define, "$localbound", { value: true, writable: false }); } else{ Object.defineProperty(define, "$localbound", { value: false, writable: false }); } // use the browser spawner var browser = this.args['-browser'] if(browser && (!this.args['-delay'] || this.args['-count'] ==0 )){ ExternalApps.browser(this.address + (browser===true?'':browser), this.args['-devtools']) } this.watcher = new FileWatcher() this.watcher.atChange = function(ifile){ // ok lets get the original path var file = ifile.replace(/\\/g,"/") for(var key in define.paths){ var match = define.expandVariables(define['$'+key]) if(file.indexOf(match) === 0){ var sliced = file.slice(match.length) if (sliced[0] !== '/') { sliced = '/' + sliced } file = '/'+key+sliced break } } //file = file.slice(define.expandVariables(define.$root).length).replace(/\\/g, "/") // ok lets rip off our this.broadcast({ type:'filechange', file: file }) }.bind(this) this.busserver = new BusServer() process.on('SIGHUP', function(){ if(this.args['-close']) this.broadcast({type:'close'}) if(this.args['-delay']) this.broadcast({type:'delay'}) }.bind(this)) if(this.args['-web']) this.getComposition(this.args['-web']) } this.COMP_DIR = 'compositions' this.broadcast = function(msg){ this.busserver.broadcast(msg) for(var k in this.compositions){ this.compositions[k].busserver.broadcast(msg) } } this.default_composition = null this.getComposition = function(file){ // lets find the composition either in define.COMPOSITIONS var options = {} options.whitelist = this.addresses if(!this.compositions[file]) this.compositions[file] = new CompositionServer(this.args, file, options) return this.compositions[file] } this.upgrade = function(req, sock, head){ if(!define.$unsafeorigin && this.addresses.indexOf(req.headers.origin) === -1){ console.log("WRONG ORIGIN SOCKET CONNECTION RECEIVED"+ req.headers.origin+ ' -> '+this.address) return false } // lets connect the sockets to the app var sock = new NodeWebSocket(req, sock, head) sock.url = req.url var mypath = req.url.slice(1) var busserver if(mypath) busserver = this.getComposition('$' + decodeURIComponent(mypath)).busserver else busserver = this.busserver busserver.addWebSocket(sock, req, this.binrpc_outgoing, this.binrpc_incoming) } // maps an input path into our files this.mapPath = function(input){ var reqparts = input.split(/\//) // lets do the filename lookup var mypath = define.paths[reqparts[1]] if(!mypath) return false // combine it var file = path.join(define.expandVariables(mypath), reqparts.slice(2).join('/')) return file } this.searchPath = function(basedir, match){ // lets recur our path var dir = fs.readdirSync(define.expandVariables(basedir)) for(var i = 0; i < dir.length; i++){ var subitem = basedir + '/' + dir[i] if(subitem.toLowerCase().indexOf(match) !== -1) return subitem var stat = fs.statSync(define.expandVariables(subitem)) if(stat.isDirectory()){ var file = this.searchPath(subitem, match) if(file) return file } } } this.request = function(req, res){ // lets delegate to var host = req.headers.host var requrl = req.url if(requrl.indexOf('/binrpc?') === 0){ var where = decodeURIComponent(requrl.slice(8)) if(req.method == 'POST'){ // alright lets store this somewhere var buf = new Uint8Array(req.headers['content-length']) var off = 0 req.on('data', function(data){ for(var i = 0; i < data.length; i ++, off++){ buf[off] = data[i] } }) req.on('end', function(){ this.binrpc_incoming[where] = buf res.writeHead(200) res.end() }.bind(this)) } else{ if(!this.binrpc_outgoing[where]){ res.writeHead(404) res.end() return } var out = this.binrpc_outgoing[where] if(out.remoteAddress !== req.connection.remoteAddress){ res.writeHead(404) res.end() return } var buf = new Buffer(out.data) res.writeHead(200, {'content-length':buf.length, 'content-type': 'application/octet-binary'}) res.write(buf) this.binrpc_outgoing[where] = undefined res.end() } return } // otherwise handle as static file if(requrl.indexOf('/proxy?') === 0){ // lets connect a http request and forward it back! var tgt_url = url.parse(decodeURIComponent(requrl.slice(7))) var tgtpath = tgt_url.path; if (tgt_url.search) { tgtpath = tgtpath +tgt_url.search } var proxy_req = http.request({ hostname: tgt_url.hostname, path: tgtpath, headers: { accept: req.headers.accept, 'user-agent': req.headers['user-agent'], 'accept-encoding': req.headers['accept-encoding'], 'accept-language': req.headers['accept-language'] } }, function(proxy_res){ res.writeHead(proxy_res.statusCode, proxy_res.headers) proxy_res.pipe(res) }) proxy_req.end() return } if(requrl =='/favicon.ico'){ res.writeHead(200) res.end() return } // block use of relative paths in the entire url. if(decodeURIComponent(requrl).indexOf('..') !== -1){ res.writeHead(404) res.end() return } var reqquery = requrl.split('?') // ok if we are a /single fetch var file = decodeURIComponent(this.mapPath(reqquery[0])) var urlext = define.fileExt(reqquery[0]) // write .dre to .dre.js files if (urlext === 'dre') { var dreurl = reqquery[0]; var filepath = define.expandVariables('$root' + dreurl) var dre = fs.readFileSync(filepath); var js = XMLConverter(dre) // TODO: warn for overwrites to changed file, e.g. check hash of file versus old version fs.writeFileSync(filepath + '.js', js); this.watcher.watch(file) } if(file === false){ // file is a search // what are we looking for // a directory named x or file or anything // we will just 302 to it for(var key in define.paths){ var mypath = define.paths[key] var fwdfile = this.searchPath(mypath, reqquery[0].slice(1)) if(fwdfile){ var urlpath = '/'+ key + '/' +define.fileBase(fwdfile.slice(mypath.length)) res.writeHead(307, {location:urlpath}) res.end() return } } res.writeHead(404) res.end("File not found: "+reqquery[0]) return } var fileext = define.fileExt(file) if(!fileext || fileext === 'dre'){ var composition = this.getComposition('$'+decodeURIComponent(reqquery[0].slice(1))) if(composition) return composition.request(req, res) } fs.stat(file, function(err, stat){ if(err || !stat.isFile()){ // see if we can find the index.js, otherwise skip it file = file.slice(0, file.lastIndexOf('.')) + '/index.js' fs.stat(file, function(err, stat){ if(err || !stat.isFile()){ res.writeHead(404) res.end() console.color('~br~Error~y~ ' + file + '~~ File not found, returning 404\n') return } processFile(stat) }) } else processFile(stat) }) var processFile = function(stat){ var header = { "Connection":"Close", "Cache-control":"max-age=0", "Content-Type": mimeFromFile(file), "etag": stat.mtime.getTime() + '_' + stat.size, "mtime": stat.mtime.getTime() } this.watcher.watch(file) if( req.headers['if-none-match'] == header.etag){ res.writeHead(304,header) res.end() return } // lets add a gzip cache var type = header["Content-Type"] if(type !== 'image/jpeg' && type !== 'image/png'){ // see if we accept gzip if(req.headers['accept-encoding'] && req.headers['accept-encoding'].toLowerCase().indexOf('gzip') !== -1){ //header["Content-Type"]+="; utf8" header["Content-encoding"] = "gzip" header["Transfer-Encoding"] = "gzip" var gzip_file = path.join(this.cache_gzip, requrl.replace(/\//g,'_')+header.etag) fs.stat(gzip_file, function(err, stat){ if(err){ // make it fs.readFile(file, function(err, data){ zlib.gzip(data, function(err, compr){ //header["Content-length"] = compr.length //console.log(compr.length) res.writeHead(200, header) res.write(compr) res.end() fs.writeFile(gzip_file, compr, function(err){ }) }) }) } else{ var stream = fs.createReadStream(gzip_file) res.writeHead(200, header) stream.pipe(res) } }) } else{ var stream = fs.createReadStream(file) res.writeHead(200, header) stream.pipe(res) } } else{ var stream = fs.createReadStream(file) res.writeHead(200, header) stream.pipe(res) } // ok so we get a filechange right? }.bind(this) } })