Created
August 12, 2015 08:26
-
-
Save frequent/62c46d29b6a3efdf626a to your computer and use it in GitHub Desktop.
rtc-brainstorming
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title></title> | |
<!-- | |
<script type="text/javascript" src="sockjs-client.js"></script> | |
<script type="text/javascript" src="webtcp_client.js"></script> | |
--> | |
<script type="text/javascript" src="bluebird-custom.js"></script> | |
<script type="text/javascript"> | |
// NOTES: | |
// concept > http://stackoverflow.com/questions/16954585/is-it-possible-to-directly-connect-using-ice-and-then-do-direct-peer-to-peer-sig | |
// demo works by copy&paste > http://bit.ly/1F4xGjs | |
// false blog, but good comments > http://bit.ly/1qXL6Wq | |
// REST-polled relay "bulletin board" > like http://bit.ly/1xqlasp | |
// webtorrent has peer to peer node connection > | |
// peer to peer node connection find on > http://bit.ly/1uS0xEB | |
// try transmitting through postMessage instead of server | |
// angular example, mh... who can read that > http://bit.ly/1xQfsyR | |
// html5 rocks example > http://bit.ly/1F1cxoA | |
// peerConnection tutorial > http://bit.ly/1xV2m4t | |
// try <image>... http://css-tricks.com/svg-fallbacks/ | |
// aborting would work inside an iFrame, but loading img does not. | |
// same https://gist.github.com/coolaj86/1199048 | |
// read here: http://www.bitvise.com/how-the-net-works | |
// peer server : http://www.chriskranky.com/webrtc-server-inside-browser-next/ | |
// pass cookies to two domains | |
// http://stackoverflow.com/questions/3342140/cross-domain-cookies | |
// why not share cookie ascros same domain, both users will be on myfoo.com | |
// if they want to communicate, so I could share a cookie, no? | |
// like this: http://stackoverflow.com/questions/16186645/cross-domain-cookies-a-maybe-new-idea | |
// http://nfriedly.com/techblog/2010/08/how-facebook-sets-and-uses-cross-domain-cookies/ | |
// http://stackoverflow.com/questions/263010/whats-your-favorite-cross-domain-cookie-sharing-approach | |
// WEBTCP (still needs a server...) | |
// http://artemyankov.com/tcp-client-for-browsers/ | |
// access through image tag, only thing cross domain | |
// push cookie through img > http://stackoverflow.com/questions/3342140/cross-domain-cookies | |
// broadcast on UDP > http://stackoverflow.com/questions/19192205/sending-data-from-one-android-device-to-another | |
// ? http://stackoverflow.com/questions/13216785/how-to-send-a-udp-packet-with-web-rtc-javascript | |
// UDP but still uses websockets/localhost | |
// https://github.com/colinbdclark/osc.js-examples | |
// http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/ | |
//http://www.html5rocks.com/en/tutorials/webrtc/datachannels/ | |
// https://gist.github.com/dristic/6225723 | |
// cross domain webworker > http://colonelpanic.net/2014/08/using-pdf-js-web-worker-cross-domain-cors/ | |
// back to cross storage, say that client searches for all IPs and | |
// sets them up for all found IPs = open iframe, try to load and get | |
// hub finds himself and setsup hub with himself and ports, inside | |
// localstorage, you have my handshake data | |
// TRY | |
// fix 3rd party cookie for safari http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/ | |
// hasvisited hack? samy.pl/csshack/csshack.js | |
// same browser communication | |
// http://stackoverflow.com/questions/4079280/javascript-communication-between-browser-tabs-windows | |
// fake a server response and guess the right port? | |
// but what happens if there never was a request? | |
// can a browser trigger a request which timesout after 30secs and | |
// this is a window to push in a response? Since I cannot trigger | |
// ajax cross ip, I should try to load an image and possibly return it | |
// via png-base64 encoding | |
// still can't "return" or "PUSH" something!, maybe via POST? | |
// DOMContentLoaded | |
function contentLoaded(win, fn) { | |
var done, top, doc, root, add, rem, pre, init, poll; | |
done = false; | |
top = true; | |
doc = win.document; | |
root = doc.documentElement; | |
add = doc.addEventListener ? 'addEventListener' : 'attachEvent'; | |
rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent'; | |
pre = doc.addEventListener ? '' : 'on'; | |
init = function (e) { | |
if (e.type === 'readystatechange' && doc.readyState !== 'complete') { | |
return; | |
} | |
(e.type === 'load' ? win : doc)[rem](pre + e.type, init, false); | |
if (!done) { | |
done = true; | |
fn.call(win, e.type || e); | |
} | |
}; | |
poll = function () { | |
try { | |
root.doScroll('left'); | |
} catch (e) { | |
window.setTimeout(poll, 50); | |
return; | |
} | |
init('poll'); | |
}; | |
if (doc.readyState === 'complete') { | |
fn.call(win, 'lazy'); | |
} else { | |
if (doc.createEventObject && root.doScroll) { | |
try { | |
top = !win.frameElement; | |
} catch (ignore) {} | |
if (top) { | |
poll(); | |
} | |
} | |
doc[add](pre + 'DOMContentLoaded', init, false); | |
doc[add](pre + 'readystatechange', init, false); | |
win[add](pre + 'load', init, false); | |
} | |
} | |
// ============================== CROSS ============================= | |
function initializeChannels(my_connection_object) { | |
var i, len, ip_list, status, frame, frame_window, address_dict, | |
address, my_estimated_ip; | |
ip_list = my_connection_object.ip_list; | |
address_dict = my_connection_object.address_dict; | |
auth_list = []; | |
status = "IPs found: "; | |
for (i = 0, len = ip_list.length; i < len; i += 1) { | |
status += ip_list[i] + " | "; | |
} | |
document.getElementById("status").textContent = status; | |
console.log("POTENTIAL ADDRESS"); | |
console.log(address_dict); | |
console.log("POTENTIAL IPs"); | |
console.log(ip_list); | |
for (address in address_dict) { | |
if (ip_list.indexOf(address) > -1) { | |
my_estimated_ip = address; | |
break; | |
} | |
} | |
console.log("I hardcode me to: " + my_estimated_ip + " and assume laptop 2 is on: " + "192.168.1.2"); | |
} | |
// ============================== IP ================================ | |
// probe single IP hijacking the image src (no cross domain issues) | |
// NOTE: we resolve on all errors to pick out the 404s | |
// NOTE: a 404 is currently determined by a fast response, nothing else | |
// really works, img.alt on aborted requests may (comparing font-size | |
// set vs broken image displayed by browser), but also requires to | |
// abort long running requests and meddle with the DOM. | |
// NOTE: skip onload. Some requests return 200 but not in onload | |
function probeIP(my_ip) { | |
function resolver(resolve) { | |
var img; | |
// exit | |
function finishit(my_passed_ip) { | |
img.src = ''; | |
delete img; | |
if (my_passed_ip === undefined) { | |
resolve({ | |
"status": 408, | |
"message": "Forced timeout", | |
"probed": null | |
}); | |
} else { | |
resolve({ | |
"status": 404, | |
"message": "Found something", | |
"probed": my_passed_ip | |
}); | |
} | |
} | |
img = document.createElement('img'); | |
img.onerror = function () { | |
img.breaker.cancel(); | |
}; | |
// trigger the probing request | |
// TODO: why port? all random ports fail | |
// TODO: why cachebust? expect fail ~~(1024 + 1024 * Math.random()) | |
img.src = '//' + my_ip + '/I_DO_NOT_EXIST.jpg'; | |
// resolve after 2secs, otherwise timeouts run forever ~ 30sec | |
img.breaker = new Promise.delay(2000) | |
.cancellable() | |
.then(function () { | |
finishit(); | |
}) | |
.caught(function() { | |
finishit(my_ip); | |
}); | |
} | |
return new Promise(resolver); | |
}; | |
// probe a batch of IPs | |
function probeIPBatch(my_host) { | |
var i, fake_src, host, host_list, segment_list, queue; | |
host = my_host.replace(/(\d+\.\d+\.\d+)\.\d+/, '$1.'); | |
host_list = []; | |
segment_list = []; | |
// build array of ips to hit > should go to 255! | |
for (i = 0; i < 100; i += 1) { | |
host_list.push(probeIP(host + i)); | |
} | |
// maximum of 6 request per step. Since all are async, we | |
// continue when a step is through and don't loose time plus the | |
// browser does not choke with a queue of xxx requests | |
while (host_list.length > 0) { | |
segment_list.push(host_list.splice(0, 6)); | |
} | |
// queu-huh | |
queue = segment_list.reduce(function (previous, my_segment) { | |
return previous.then(function (previousValue) { | |
// custom method on each iteration | |
return Promise.all(my_segment) | |
.then(function (my_batch_list) { | |
previousValue.push(my_batch_list); | |
return previousValue; | |
}) | |
}) | |
}, Promise.resolve([])); | |
return queue | |
.then(function(my_response_list) { | |
var i, j, len, segment, count, response; | |
ip_list = []; | |
for (i = 0, len = my_response_list.length; i < len; i += 1) { | |
segment = my_response_list[i]; | |
for (j = 0, count = segment.length; j < count; j += 1) { | |
response = segment[j]; | |
if (response.status === 404) { | |
ip_list.push(response.probed.split('/I_DO_NOT_EXIST')[0]); | |
} | |
} | |
} | |
return ip_list; | |
}); | |
} | |
// pick candidates from response | |
function grepSDP(my_sdp) { | |
var address_dict, batch_list, i, len, candidate_list, queue; | |
function updateDisplay(my_new_address) { | |
var display_address; | |
function filterAddressDict(my_k) { | |
return address_dict[my_k]; | |
} | |
if (my_new_address in address_dict) { | |
return; | |
} else { | |
address_dict[my_new_address] = true; | |
} | |
display_address = Object.keys(address_dict).filter(filterAddressDict); | |
document.getElementById('list').textContent = | |
display_address.join(" or perhaps ") || "n/a"; | |
} | |
function digestLine(my_line, my_adress_list) { | |
var adress_list, address, type; | |
// http://tools.ietf.org/html/rfc4566#section-5.13 | |
if (~my_line.indexOf("a=candidate")) { | |
// http://tools.ietf.org/html/rfc5245#section-15.1 | |
part_list = my_line.split(' '); | |
address = part_list[4]; | |
type = part_list[7]; | |
if (type === 'host') { | |
updateDisplay(address); | |
my_adress_list.push(address); | |
} | |
// http://tools.ietf.org/html/rfc4566#section-5.7 | |
} else if (~my_line.indexOf("c=")) { | |
part_list = my_line.split(' '); | |
address = part_list[2]; | |
updateDisplay(address); | |
} | |
return my_adress_list; | |
} | |
address_dict = Object.create(null); | |
address_dict["0.0.0.0"] = false; | |
adress_list = []; | |
// c.f. http://tools.ietf.org/html/rfc4566#page-39 | |
candidate_list = my_sdp.split('\r\n'); | |
for (i = 0, len = candidate_list.length; i < len; i += 1) { | |
adress_list = digestLine(candidate_list[i], adress_list); | |
} | |
// loop over found candidates one-after-the-other, otherwise | |
// browser will choke with flurry of requests being made | |
// queu-huh | |
queue = adress_list.reduce(function (previous, my_address) { | |
return previous.then(function (previousValue) { | |
// custom method on each iteration | |
return probeIPBatch(my_address) | |
.then(function (my_batch_ip_list) { | |
previousValue.push(my_batch_ip_list); | |
return previousValue; | |
}); | |
}) | |
}, Promise.resolve([])); | |
return queue | |
.then(function (my_ip_list) { | |
var i, len, arr; | |
for (arr = [], i = 0, len = my_ip_list.length; i < len; i += 1) { | |
arr = arr.concat(my_ip_list[i]); | |
} | |
return {"address_dict": address_dict, "ip_list": arr}; | |
}); | |
} | |
// IP lookup initiliazer | |
function initializeIPLookup() { | |
var RTCPeerConnection, rtc, IceCandidate, SessionDescription; | |
// NOTE: window.RTCPeerConnection is "not a constructor" in FF22/23 | |
RTCPeerConnection = /*window.RTCPeerConnection ||*/ | |
window.webkitRTCPeerConnection || | |
window.mozRTCPeerConnection; | |
// not used | |
IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate; | |
SessionDescription = window.mozRTCSessionDescription || | |
window.RTCSessionDescription; | |
if (RTCPeerConnection === undefined) { | |
document.getElementById('list').nextSibling.textContent = | |
"In Chrome and Firefox your IP should display automatically"; | |
return; | |
} | |
// no ice, ice baby... | |
rtc = new RTCPeerConnection({iceServers:[]}); | |
// FF needs a channel/stream to proceed | |
if (window.mozRTCPeerConnection) { | |
rtc.createDataChannel('', {"reliable": false}); | |
}; | |
// convert candidate to SDP so it can run through our general parser | |
// see https://twitter.com/lancestout/status/525796175425720320 | |
// never triggers as we don't use ice, so candidate = null | |
rtc.onicecandidate = function (my_event) { | |
if (my_event.candidate) { | |
grepSDP("a=" + my_event.candidate.candidate); | |
} | |
} | |
// and go | |
return new Promise(function (resolve, reject) { | |
// create RTC offer successful | |
function rtcOfferSuccess(my_offer_description) { | |
rtc.setLocalDescription(my_offer_description); | |
resolve(grepSDP(my_offer_description.sdp)); | |
} | |
// create RTC offer failed | |
function rtcOfferFail(my_error) { | |
reject(my_error); | |
} | |
rtc.createOffer(rtcOfferSuccess, rtcOfferFail) | |
}); | |
} | |
// > Start here | |
contentLoaded(window, function () { | |
document.getElementById("status").textContent = "Probing possible IP addresses..." | |
initializeIPLookup() | |
.then(initializeChannels) | |
.caught(function (my_error) { | |
console.log("DANG"); | |
console.log(my_error); | |
}) | |
}); | |
</script> | |
</head> | |
<body> | |
<div id="list"></div> | |
<div id="status"></div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment