diff --git a/examples/admin/app.js b/examples/admin/app.js index d8f42f14..1991e798 100644 --- a/examples/admin/app.js +++ b/examples/admin/app.js @@ -1,15 +1,19 @@ console.log("If modules not found, run `npm install` in example/admin folder!"); // git subtree push -P examples/admin heroku master var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || 8888; -var express = require('express'); +var express = require('connect'); var bodyParser = require('body-parser'); var app = express(); var Gun = require('gun'); var gun = Gun({ s3: (process.env.NODE_ENV === 'production')? null : require('../../test/shotgun') // replace this with your own keys! }); - -app.use(gun.server) - .use(express.static(__dirname)) +app.use(function(req, res, next){ + console.log("THIS HIT SEEEERVER", req.url); + next(); + }) + .use(gun.server) + .use(require('serve-static')(__dirname)) + //.use(express.static(__dirname)) app.listen(port); console.log('Express started on port ' + port + ' with /gun'); diff --git a/examples/admin/duel.html b/examples/admin/duel.html index 2923c672..9d09fafd 100644 --- a/examples/admin/duel.html +++ b/examples/admin/duel.html @@ -29,52 +29,183 @@ .none { display: none; } + .fight { + z-index: 9999; + background: brown; + color: white; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + .player input, .player button { + font-size: 18pt; + } -

Gun Duel!

- Old western cowboy style! Two players are needed, whoever can shoot the other first wins! - Fastest gun in the west, nut'n by nobody. -
- Player 1: - +

GUNSLINGER

+ Old West Duel! Two players are needed, whoever can tap the screen first to draw their pistol and shoot wins!
+ Fastest gun in the west, nut'n seconds, by nobody. + + Player 1:
-
- Player 2: - + + Player 2:
-
- +
+
+

GET READY!

+ +
+ Last duel won by no one in 0 seconds against nobody. + diff --git a/gun.js b/gun.js index f9f70059..ec879798 100644 --- a/gun.js +++ b/gun.js @@ -95,8 +95,8 @@ } Gun.chain.path = function(path){ // The focal point follows the path var gun = this.chain(); - path = path.split('.'); - Gun.log("PATH stack trace", gun._.events.trace + 1, 'was it before loaded?', this._); + path = (path||'').split('.'); + Gun.log("PATH stack trace", path, gun._.events.trace + 1, 'was it before loaded?', this._); gun._.events.on(gun._.events.trace += 1).event(function trace(node){ Gun.log("stack at", gun._.events.at); if(!path.length){ // if the path resolves to another node, we finish here @@ -131,9 +131,9 @@ gun._.events.on(gun._.events.trace += 1).event(function(node){ console.log("BOOM got it", node); if(gun._.field){ - return cb((node||{})[gun._.field]); // copy data first? + return cb.call(gun, (node||{})[gun._.field]); // copy data first? } - cb(Gun.obj.copy(node)); // we do here. + cb.call(gun, Gun.obj.copy(node)); // we do here. }); if(gun._.loaded){ gun._.events.at -= 1; // IDK why we are doing this, just trying to get something to work. @@ -428,7 +428,7 @@ var serverState = Gun.time.is(); // add more checks? var state = HAM(serverState, deltaStates[field], states[field], deltaValue, current[field]); - // Gun.log("HAM:", field, deltaValue, deltaStates[field], current[field], 'the', state, (deltaStates[field] - serverState)); + //console.log("HAM:", field, deltaValue, deltaStates[field], current[field], 'the', state, (deltaStates[field] - serverState)); if(state.err){ Gun.log(".!HYPOTHETICAL AMNESIA MACHINE ERR!.", state.err); return; @@ -615,7 +615,7 @@ } }()); Gun.log = function(a, b, c, d, e, f){ //s, l){ - console.log(a, b, c, d, e, f); + //console.log(a, b, c, d, e, f); //console.log.apply(console, arguments); } own.sym = Gun.sym = { @@ -637,7 +637,7 @@ tab.server = tab.server || function(req, res, next){ } - window.tab = tab; //window.XMLHttpRequest = null; // for debugging purposes + window.tab = tab; // window.XMLHttpRequest = null; // for debugging purposes (function(){ tab.store = {}; var store = window.localStorage || {setItem: function(){}, removeItem: function(){}, getItem: function(){}}; @@ -659,7 +659,7 @@ } (function(){ - tab.subscribe.sub = (reply.headers || {})['gun-sub']; + tab.subscribe.sub = (reply.headers || {})['gun-sub'] || tab.subscribe.sub; //console.log("We are sub", tab.subscribe.sub); var data = reply.body; if(!data || !data._){ return } @@ -668,23 +668,44 @@ }, {headers: {'Gun-Sub': tab.subscribe.sub || ''}, header: {'Gun-Sub': 1}}); }); } + tab.url = function(nodes){ + return; + console.log("urlify delta", nodes); + var s = '' + , uri = encodeURIComponent; + Gun.obj.map(nodes, function(delta, id){ + var ham; + if(!delta || !delta._ || !(ham = delta._[Gun.sym.HAM])){ return } + s += uri('#') + '=' + uri(id) + '&'; + Gun.obj.map(delta, function(val, field){ + if(field === Gun.sym.meta){ return } + s += uri(field) + '=' + uri(Gun.text.ify(val)) + uri('>') + uri(ham[field]) + '&'; + }) + }); + console.log(s); + return s; + } tab.set = tab.set || function(nodes, cb){ cb = cb || function(){}; // TODO: batch and throttle later. - tab.store.set(respond.id = 'send/' + Gun.text.random(), nodes); - //console.log("localStorage the DELTA", nodes); + tab.store.set(cb.id = 'send/' + Gun.text.random(), nodes); + //tab.url(nodes); Gun.obj.map(gun.__.opt.peers, function(peer, url){ - tab.ajax(url, nodes, respond, {headers: {'Gun-Sub': tab.subscribe.sub || ''}}); - }); - function respond(err, reply, id){ - if(reply && reply.body){ - if(reply.body.defer){ - tab.set.defer[reply.body.defer] = respond; + tab.ajax(url, nodes, function respond(err, reply, id){ + var body = reply && reply.body; + respond.id = respond.id || cb.id; + Gun.obj.del(tab.set.defer, id); // handle err with a retry? Or make a system auto-do it? + if(!body){ return } + if(body.defer){ + console.log("deferring post", body.defer); + tab.set.defer[body.defer] = respond; } - if(reply.body.refed || reply.body.reply){ - //console.log("-------post-reply-all--------->", reply, err); - respond(null, {headers: reply.headers, body: reply}); - Gun.obj.map(reply.body.refed, function(r, id){ + if(body.reply){ + respond(null, {headers: reply.headers, body: body.reply }); + } + if(body.refed){ + console.log("-------post-reply-all--------->", reply, err); + Gun.obj.map(body.refed, function(r, id){ var cb; if(cb = tab.set.defer[id]){ cb(null, {headers: reply.headers, body: r}, id); @@ -693,11 +714,13 @@ // TODO: should be able to do some type of "checksum" that every request cleared, and if not, figure out what is wrong/wait for finish. return; } - console.log('callback complete, now respond', respond.id); + if(body.reply || body.defer || body.refed){ return } tab.store.del(respond.id); - } - Gun.obj.del(tab.set.defer, id); - } + }, {headers: {'Gun-Sub': tab.subscribe.sub || ''}}); + }); + Gun.obj.map(nodes, function(node, id){ + Gun.on(id).emit(node, true); // TODO: Temporary hack, I want to rebroadcast back to ourselves. IDK if this is always useful, and we shouldn't use global. + }); } tab.set.defer = {}; tab.subscribe = function(id){ // TODO: BUG!!! ERROR! Handle disconnection (onerror)!!!! @@ -711,10 +734,14 @@ 'Gun-Sub': tab.subscribe.sub || '' } }, query = tab.subscribe.sub? '' : tab.subscribe.query(tab.subscribe.to); + console.log("subscribing poll", tab.subscribe.sub); Gun.obj.map(gun.__.opt.peers, function(peer, url){ tab.ajax(url + query, null, function(err, reply){ - //console.log("poll", err, reply); - if(!reply || !reply.body){ return } // not interested in any null/0/''/undefined values + if(err || !reply || !reply.body || reply.body.err){ // not interested in any null/0/''/undefined values + console.log(err, reply); + return; + } + console.log("poll", reply); tab.subscribe.poll(); if(reply.headers){ tab.subscribe.sub = reply.headers['gun-sub'] || tab.subscribe.sub; diff --git a/package.json b/package.json index 60867f5f..4cd7ecd0 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name": "gun" -, "version": "0.0.6" +, "version": "0.0.6a" , "author": "Mark Nadal" , "description": "Graph engine." , "engines": { diff --git a/shots.js b/shots.js index 6777087f..5bef19bc 100644 --- a/shots.js +++ b/shots.js @@ -5,7 +5,8 @@ , url = require('url') , meta = {}; Gun.on('opt').event(function(gun, opt){ - gun.server = gun.server || function(req, res, next){ // this whole function needs refactoring and modularization + gun.server = gun.server || function(req, res, next){ // this whole function needs refactoring and modularization + //console.log("\n\n GUN SERVER!"); next = next || function(){}; if(!req || !res){ return next() } if(!req.url){ return next() } @@ -66,6 +67,7 @@ s3.prenode = s3.prenode || opt.s3.prenode || '_/nodes/'; gun.__.opt.batch = opt.batch || gun.__.opt.batch || 10; gun.__.opt.throttle = opt.throttle || gun.__.opt.throttle || 15; + gun.__.opt.disconnect = opt.disconnect || gun.__.opt.disconnect || 5; if(!gun.__.opt.keepMaxSockets){ require('https').globalAgent.maxSockets = require('http').globalAgent.maxSockets = Infinity } // WARNING: Document this! s3.load = s3.load || function(key, cb, opt){ @@ -160,11 +162,12 @@ gun.server.transport = (function(){ function tran(req, cb){ - //console.log("\n\n\n", req); + //console.log(req); req.sub = req.headers['gun-sub']; // grab the sub req.tab = tran.sub.s[req.sub] || {}; // check to see if there already is a tab associated with it, or create one req.tab.sub = req.sub = req.sub || Gun.text.random(); // Generate a session id if we don't already have one req.tran = tran.xhr(req, cb) || tran.jsonp(req, cb); // polyfill transport layer + clearTimeout(req.tab.timeout); // raw test for now, no auth: if(!req.tran){ return cb({headers: {"Content-Type": tran.json}, body: {err: "No transport layer!"}}) } if('post' === req.method || 'patch' === req.method){ return tran.post(req, req.tran) } // TODO: Handle JSONP emulated POST via GET @@ -204,7 +207,7 @@ if(context.err){ return cb({body: {err: context.err}}) } // WARNING! TODO: BUG! Do not send OK confirmation if amnesiaQuaratine is activated! Not until after it has actually been processed!!! if(Gun.fns.is(gun.__.opt.hooks.set)){ - gun.__.opt.hooks.set(context.nodes, function(err, data){ // now iterate through those nodes to S3 and get a callback once all are saved + gun.__.opt.hooks.set(context.nodes, function saved(err, data){ // now iterate through those nodes to S3 and get a callback once all are saved var body = {}; if(err){ body.err = err ; @@ -217,6 +220,7 @@ } var now = tran.post.s[req.sub]; // begin our stupid Chrome fix, we should abstract this out into defer (where it belogns) to keep things clean. if(!now){ return } // utoh we've lost our reply to the tab! + clearTimeout(now.timeout); now.body = now.body || {}; // make sure we have a body for our multi-response in a single response. if(req.wait){ // did this request get deferred? (now.body.refed = now.body.refed || {})[req.wait] = err? {err: err} : defer.map({}, context.nodes, 1); // then reply to it "here". @@ -224,7 +228,7 @@ now.body.reply = err? {err: err} : defer.map({}, context.nodes, 1); // else this is the original POST that had to be upgraded. } if(0 < (now.count = ((now.count || 0) - 1))){ // Don't reply till all deferred POSTs have successfully heard back from S3. (Sarcasm: Like counting guarantees that) - return; // TODO: BUG!!! Memory leak, we have no guarantee we'll ever get a reply! So time it out, maybe some multiple of the S3 throttle. + return now.timeout = setTimeout(saved, gun.__.opt.throttle * 2 * 1000); // reply not guaranteed, so time it out, in seconds. } if(Gun.fns.is(now)){ now({body: now.body}); // FINALLY reply for ALL the POSTs for that session that accumulated. @@ -278,6 +282,7 @@ //console.log("<-- ", req.sub, req.tran ," -->"); req.tab = tran.sub.s[req.sub]; if(!req.tab){ + console.log(req.url.query); cb({ headers: {'Gun-Sub': ''} ,body: {err: "Please re-initialize sub."} @@ -286,6 +291,7 @@ } //console.log("\n\n\n THE CURRENT STATUS IS");console.log(req.tab); if(req.tab.queue && req.tab.queue.length){ + tran.clean(req.tab); // We flush their data now, if they don't come back for more within timeout, we remove their session console.log("_____ NOW PUSHING YOUR DATA ______", req.sub); cb({ headers: {'Gun-Sub': req.sub} }); while(1 < req.tab.queue.length){ @@ -299,7 +305,18 @@ } } tran.sub.s = {}; - tran.sub.scribe = function(tab, id){ // TODO: BUG!!! Memory leaks, remember to destroy sessions via timeout. + tran.clean = function(tab, mult){ + if(!tab){ return } + mult = mult || 1; + clearTimeout(tab.timeout); + tab.timeout = setTimeout(function(){ + if(!tab){ return } + if(tab.reply){ tab.reply({body: {err: "Connection timed out"}}) } + console.log("!!!! DISCONNECTING CLIENT !!!!!", tab.sub); + Gun.obj.del(tran.sub.s, tab.sub) + }, gun.__.opt.disconnect * mult * 1000); // in seconds + } + tran.sub.scribe = function(tab, id){ tran.sub.s[tab.sub] = tab; tab.subs = tab.subs || {}; tab.subs[id] = tab.subs[id] || tran.push.on(id).event(function(req){ @@ -307,7 +324,8 @@ if(!tab){ return this.off() } // resolve any dangling callbacks req.sub = req.sub || req.headers['gun-sub']; if(req.sub === tab.sub){ return } // do not send back to the tab that sent it - console.log('FROM:', req.sub, "TO:", tab); + console.log('FROM:', req.sub, "TO:", tab.sub); + tran.clean(tab); if(tab.reply){ tab.reply({ headers: {'Gun-Sub': tab.sub} @@ -318,6 +336,7 @@ } (tab.queue = tab.queue || []).push(req.body); }); + tran.clean(tab, 2); } tran.xhr = function(req, cb){ // Streaming Long Polling return req.tran || (req.headers['x-requested-with'] === 'XMLHttpRequest'? transport : null);