Skip to content

Instantly share code, notes, and snippets.

@frequent
Created August 12, 2015 08:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frequent/62c46d29b6a3efdf626a to your computer and use it in GitHub Desktop.
Save frequent/62c46d29b6a3efdf626a to your computer and use it in GitHub Desktop.
rtc-brainstorming
<!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