snowflake proxy asks user for relay address. also:

- tests for cookiestring and querystring
- fixed Cakefile with a concat so it works fine in both browser and local test
This commit is contained in:
Serene Han 2016-01-11 10:49:31 -08:00
parent 95952830ba
commit d735c0fbf9
3 changed files with 209 additions and 61 deletions

View file

@ -2,7 +2,11 @@ fs = require 'fs'
{exec} = require 'child_process'
task 'test', 'test snowflake.coffee', () ->
exec 'coffee snowflake_test.coffee -v', (err, stdout, stderr) ->
task 'test', 'snowflake unit tests', () ->
testFile = 'test/snowflake.bundle.coffee'
exec 'cat snowflake.coffee snowflake_test.coffee | cat > ' + testFile, (err, stdout, stderr) ->
throw err if err
console.log stdout + stderr
exec 'coffee ' + testFile + ' -v', (err, stdout, stderr) ->
throw err if err
console.log stdout + stderr

View file

@ -10,6 +10,16 @@ this must always act as the answerer.
TODO(keroserene): Complete the websocket + webrtc ProxyPair
###
if 'undefined' == typeof module || 'undefined' != typeof module.exports
console.log 'not in browser.'
else
window.PeerConnection = window.RTCPeerConnection ||
window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection
window.RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate;
window.RTCSessionDescription = window.RTCSessionDescription ||
window.mozRTCSessionDescription
Query =
###
Parse a URL query string or application/x-www-form-urlencoded body. The
@ -26,8 +36,7 @@ Query =
strings = []
strings = qs.split '&' if qs
return result if 0 == strings.length
for i in [1..strings.length]
string = strings[i]
for string in strings
j = string.indexOf '='
if j == -1
name = string
@ -37,7 +46,7 @@ Query =
value = string.substr(j + 1)
name = decodeURIComponent(name.replace(/\+/g, ' '))
value = decodeURIComponent(value.replace(/\+/g, ' '))
result[name] = value if !(name in result)
result[name] = value if name not of result
result
# params is a list of (key, value) 2-tuples.
@ -56,6 +65,22 @@ Params =
return false if "false" == val || "0" == val
return null
# Parse a cookie data string (usually document.cookie). The return type is an
# object mapping cookies names to values. Returns null on error.
# http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-8747038
parseCookie: (cookies) ->
result = {}
strings = []
strings = cookies.split ';' if cookies
for string in strings
j = string.indexOf '='
return null if -1 == j
name = decodeURIComponent string.substr(0, j).trim()
value = decodeURIComponent string.substr(j + 1).trim()
result[name] = value if !(name in result)
result
# repr = (x) ->
# return 'null' if null == x
# return 'undefined' if 'undefined' == typeof x
@ -72,9 +97,11 @@ Params =
safe_repr = (s) -> SAFE_LOGGING ? "[scrubbed]" : JSON.stringify(s)
# HEADLESS is true if we are running not in a browser with a DOM.
DEBUG = false
if window && window.location
query = Query.parse(window.location.search.substr(1))
HEADLESS = "undefined" == typeof(document)
DEBUG = Params.getBool(query, "debug", false)
HEADLESS = "undefined" == typeof(document)
# TODO: Different ICE servers.
config = {
@ -88,12 +115,6 @@ $chatlog = null
$send = null
$input = null
window.PeerConnection = window.RTCPeerConnection ||
window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection
window.RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate;
window.RTCSessionDescription = window.RTCSessionDescription ||
window.mozRTCSessionDescription
# TODO: Implement
class Badge
@ -112,6 +133,7 @@ class Snowflake
pc: null
rateLimit: 0
proxyPairs: []
relayAddr: null
badge: null
$badge: null
MAX_NUM_CLIENTS = 1
@ -126,8 +148,13 @@ class Snowflake
else
@badge = new Badge()
@$badgem = @badge.elem
if (@$badge)
@$badge.setAttribute("id", "snowflake-badge")
@$badge.setAttribute("id", "snowflake-badge") if (@$badge)
setRelayAddr: (relayAddr) ->
# TODO: User-supplied for now, but should fetch from facilitator later.
@relayAddr = relayAddr
log "Input offer from the snowflake client:"
@beginWebRTC()
# Initialize WebRTC PeerConnection
beginWebRTC: ->
@ -240,6 +267,9 @@ class Snowflake
@badge.die() if @badge
DEFAULT_PORTS =
http: 80
https: 443
# Build an escaped URL string from unescaped components. Only scheme and host
# are required. See RFC 3986, section 3.
buildUrl = (scheme, host, port, path, params) ->
@ -298,9 +328,6 @@ makeWebsocket = (addr) ->
# TODO: Implement
class ProxyPair
# TODO: Hardcoded for now, but should fetch from facilitator later.
relayAddr: null
constructor: (@clientAddr, @relayAddr, @rateLimit) ->
# Assumes WebRTC part is already connected.
@ -405,7 +432,7 @@ snowflake = null
welcome = ->
log "== snowflake browser proxy =="
log "Input offer from the snowflake client:"
log "Input desired relay address:"
# Log to the message window.
log = (msg) ->
@ -419,6 +446,9 @@ Interface =
acceptInput: ->
msg = $input.value
switch snowflake.state
when MODE.INIT
# Set target relay.
snowflake.setRelayAddr msg
when MODE.WEBRTC_CONNECTING
Signalling.receive msg
when MODE.WEBRTC_READY
@ -453,6 +483,7 @@ Signalling =
snowflake.receiveOffer recv if desc
init = ->
$chatlog = document.getElementById('chatlog')
$chatlog.value = ""
@ -465,7 +496,6 @@ init = ->
if 13 == e.keyCode # enter
$send.onclick()
snowflake = new Snowflake()
snowflake.beginWebRTC()
welcome()
window.onload = init
window.onload = init if window

View file

@ -1,80 +1,194 @@
s = require './snowflake'
# s = require './snowflake'
window = {}
VERBOSE = false
if process.argv.indexOf("-v") >= 0
if process.argv.indexOf('-v') >= 0
VERBOSE = true
numTests = 0
numFailed = 0
announce = (testName) ->
if VERBOSE
# if (!top)
# console.log();
console.log testName
# top = false
console.log '\n --- ' + testName + ' ---'
pass = (test) ->
numTests++;
if VERBOSE
console.log "PASS " + test
console.log 'PASS ' + test
fail = (test, expected, actual) ->
numTests++
numFailed++
console.log "FAIL " + test + " expected: " + expected + " actual: " + actual
console.log 'FAIL ' + test +
' expected: ' + JSON.stringify(expected) +
' actual: ' + JSON.stringify(actual)
testBuildUrl = ->
TESTS = [{
args: ["http", "example.com"]
expected: "http://example.com"
args: ['http', 'example.com']
expected: 'http://example.com'
},{
args: ["http", "example.com", 80]
expected: "http://example.com"
args: ['http', 'example.com', 80]
expected: 'http://example.com'
},{
args: ["http", "example.com", 81],
expected: "http://example.com:81"
args: ['http', 'example.com', 81],
expected: 'http://example.com:81'
},{
args: ["https", "example.com", 443]
expected: "https://example.com"
args: ['https', 'example.com', 443]
expected: 'https://example.com'
},{
args: ["https", "example.com", 444]
expected: "https://example.com:444"
args: ['https', 'example.com', 444]
expected: 'https://example.com:444'
},{
args: ["http", "example.com", 80, "/"]
expected: "http://example.com/"
args: ['http', 'example.com', 80, '/']
expected: 'http://example.com/'
},{
args: ["http", "example.com", 80, "/test?k=%#v"]
expected: "http://example.com/test%3Fk%3D%25%23v"
args: ['http', 'example.com', 80, '/test?k=%#v']
expected: 'http://example.com/test%3Fk%3D%25%23v'
},{
args: ["http", "example.com", 80, "/test", []]
expected: "http://example.com/test?"
args: ['http', 'example.com', 80, '/test', []]
expected: 'http://example.com/test?'
},{
args: ["http", "example.com", 80, "/test", [["k", "%#v"]]]
expected: "http://example.com/test?k=%25%23v"
args: ['http', 'example.com', 80, '/test', [['k', '%#v']]]
expected: 'http://example.com/test?k=%25%23v'
},{
args: ["http", "example.com", 80, "/test", [["a", "b"], ["c", "d"]]]
expected: "http://example.com/test?a=b&c=d"
args: ['http', 'example.com', 80, '/test', [['a', 'b'], ['c', 'd']]]
expected: 'http://example.com/test?a=b&c=d'
},{
args: ["http", "1.2.3.4"]
expected: "http://1.2.3.4"
args: ['http', '1.2.3.4']
expected: 'http://1.2.3.4'
},{
args: ["http", "1:2::3:4"]
expected: "http://[1:2::3:4]"
args: ['http', '1:2::3:4']
expected: 'http://[1:2::3:4]'
},{
args: ["http", "bog][us"]
expected: "http://bog%5D%5Bus"
args: ['http', 'bog][us']
expected: 'http://bog%5D%5Bus'
},{
args: ["http", "bog:u]s"]
expected: "http://bog%3Au%5Ds"
}
]
announce "-- testBuildUrl --"
args: ['http', 'bog:u]s']
expected: 'http://bog%3Au%5Ds'
}]
announce 'testBuildUrl'
for test in TESTS
actual = s.buildUrl.apply undefined, test.args
actual = buildUrl.apply undefined, test.args
if actual == test.expected
pass test.args
else
fail test.args, test.expected, actual
###
This test only checks that things work for strings formatted like
document.cookie. Browsers maintain several properties about this string, for
example cookie names are unique with no trailing whitespace. See
http://www.ietf.org/rfc/rfc2965.txt for the grammar.
###
testParseCookieString = ->
TESTS = [{
cs: ''
expected: { }
},{
cs: 'a=b'
expected: { a: 'b'}
},{
cs: 'a=b=c'
expected: { a: 'b=c' }
},{
cs: 'a=b; c=d'
expected: { a: 'b', c: 'd' }
},{
cs: 'a=b ; c=d'
expected: { a: 'b', c: 'd' }
},{
cs: 'a= b',
expected: {a: 'b' }
},{
cs: 'a='
expected: { a: '' }
}, {
cs: 'key',
expected: null
}, {
cs: 'key=%26%20'
expected: { key: '& ' }
}, {
cs: 'a=\'\''
expected: { a: '\'\'' }
}]
announce 'testParseCookieString'
for test in TESTS
actual = Params.parseCookie test.cs
if JSON.stringify(actual) == JSON.stringify(test.expected)
pass test.cs
else
fail test.cs, test.expected, actual
testParseQueryString = ->
TESTS = [{
qs: ''
expected: {}
},{
qs: 'a=b'
expected: { a: 'b' }
},{
qs: 'a=b=c'
expected: { a: 'b=c' }
},{
qs: 'a=b&c=d'
expected: { a: 'b', c: 'd' }
},{
qs: 'client=&relay=1.2.3.4%3A9001'
expected: { client: '', relay: '1.2.3.4:9001' }
},{
qs: 'a=b%26c=d'
expected: { a: 'b&c=d' }
},{
qs: 'a%3db=d'
expected: { 'a=b': 'd' }
},{
qs: 'a=b+c%20d'
expected: { 'a': 'b c d' }
},{
qs: 'a=b+c%2bd'
expected: { 'a': 'b c+d' }
},{
qs: 'a+b=c'
expected: { 'a b': 'c' }
},{
qs: 'a=b+c+d'
expected: { a: 'b c d' }
# First appearance wins.
},{
qs: 'a=b&c=d&a=e'
expected: { a: 'b', c: 'd' }
},{
qs: 'a'
expected: { a: '' }
},{
qs: '=b',
expected: { '': 'b' }
},{
qs: '&a=b'
expected: { '': '', a: 'b' }
},{
qs: 'a=b&'
expected: { a: 'b', '':'' }
},{
qs: 'a=b&&c=d'
expected: { a: 'b', '':'', c: 'd' }
}]
announce 'testParseQueryString'
for test in TESTS
actual = Query.parse test.qs
if JSON.stringify(actual) == JSON.stringify(test.expected)
pass test.qs
else
fail test.qs, test.expected, actual
testBuildUrl()
testParseCookieString()
testParseQueryString()