mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-14 05:11:19 -04:00
more jasmine specs for proxypair, ui, and snowflake coffee files
This commit is contained in:
parent
e0081ea954
commit
547cb9690a
9 changed files with 159 additions and 56 deletions
|
@ -13,7 +13,9 @@ FILES = [
|
||||||
]
|
]
|
||||||
FILES_SPEC = [
|
FILES_SPEC = [
|
||||||
'spec/util.spec.coffee'
|
'spec/util.spec.coffee'
|
||||||
|
'spec/ui.spec.coffee'
|
||||||
'spec/proxypair.spec.coffee'
|
'spec/proxypair.spec.coffee'
|
||||||
|
'spec/snowflake.spec.coffee'
|
||||||
]
|
]
|
||||||
FILES_ALL = FILES.concat FILES_SPEC
|
FILES_ALL = FILES.concat FILES_SPEC
|
||||||
OUTFILE = 'build/snowflake.coffee'
|
OUTFILE = 'build/snowflake.coffee'
|
||||||
|
|
|
@ -24,7 +24,6 @@ class Broker
|
||||||
constructor: (@url) ->
|
constructor: (@url) ->
|
||||||
@clients = 0
|
@clients = 0
|
||||||
@id = genSnowflakeID()
|
@id = genSnowflakeID()
|
||||||
dbg 'Contacting Broker at ' + @url + '\nSnowflake ID: ' + @id
|
|
||||||
# Ensure url has the right protocol + trailing slash.
|
# Ensure url has the right protocol + trailing slash.
|
||||||
@url = 'https://' + @url if 0 != @url.indexOf('https://', 0)
|
@url = 'https://' + @url if 0 != @url.indexOf('https://', 0)
|
||||||
@url += '/' if '/' != @url.substr -1
|
@url += '/' if '/' != @url.substr -1
|
||||||
|
|
|
@ -5,7 +5,6 @@ Represents a single:
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
|
||||||
class ProxyPair
|
class ProxyPair
|
||||||
|
|
||||||
MAX_BUFFER: 10 * 1024 * 1024
|
MAX_BUFFER: 10 * 1024 * 1024
|
||||||
|
@ -58,15 +57,15 @@ class ProxyPair
|
||||||
channel.onopen = =>
|
channel.onopen = =>
|
||||||
log 'WebRTC DataChannel opened!'
|
log 'WebRTC DataChannel opened!'
|
||||||
snowflake.state = MODE.WEBRTC_READY
|
snowflake.state = MODE.WEBRTC_READY
|
||||||
ui.setActive true
|
snowflake.ui.setActive true
|
||||||
# This is the point when the WebRTC datachannel is done, so the next step
|
# This is the point when the WebRTC datachannel is done, so the next step
|
||||||
# is to establish websocket to the server.
|
# is to establish websocket to the server.
|
||||||
@connectRelay()
|
@connectRelay()
|
||||||
channel.onclose = ->
|
channel.onclose = ->
|
||||||
log 'WebRTC DataChannel closed.'
|
log 'WebRTC DataChannel closed.'
|
||||||
ui.setStatus 'disconnected.'
|
snowflake.ui.setStatus 'disconnected.'
|
||||||
|
snowflake.ui.setActive false
|
||||||
snowflake.state = MODE.INIT
|
snowflake.state = MODE.INIT
|
||||||
ui.setActive false
|
|
||||||
# Change this for multiplexing.
|
# Change this for multiplexing.
|
||||||
snowflake.reset()
|
snowflake.reset()
|
||||||
channel.onerror = -> log 'Data channel error!'
|
channel.onerror = -> log 'Data channel error!'
|
||||||
|
@ -79,7 +78,7 @@ class ProxyPair
|
||||||
@relay.label = 'websocket-relay'
|
@relay.label = 'websocket-relay'
|
||||||
@relay.onopen = =>
|
@relay.onopen = =>
|
||||||
log @relay.label + ' connected!'
|
log @relay.label + ' connected!'
|
||||||
ui.setStatus 'connected'
|
snowflake.ui.setStatus 'connected'
|
||||||
@relay.onclose = @onClose
|
@relay.onclose = @onClose
|
||||||
@relay.onerror = @onError
|
@relay.onerror = @onError
|
||||||
@relay.onmessage = @onRelayToClientMessage
|
@relay.onmessage = @onRelayToClientMessage
|
||||||
|
|
|
@ -21,8 +21,6 @@ if 'undefined' != typeof window && window.location
|
||||||
COPY_PASTE_ENABLED = Params.getBool(query, 'manual', false)
|
COPY_PASTE_ENABLED = Params.getBool(query, 'manual', false)
|
||||||
else
|
else
|
||||||
window = {}
|
window = {}
|
||||||
# HEADLESS is true if we are running not in a browser with a DOM.
|
|
||||||
HEADLESS = 'undefined' == typeof(document)
|
|
||||||
|
|
||||||
# Bytes per second. Set to undefined to disable limit.
|
# Bytes per second. Set to undefined to disable limit.
|
||||||
DEFAULT_RATE_LIMIT = DEFAULT_RATE_LIMIT || undefined
|
DEFAULT_RATE_LIMIT = DEFAULT_RATE_LIMIT || undefined
|
||||||
|
@ -59,7 +57,7 @@ class Snowflake
|
||||||
state: MODE.INIT
|
state: MODE.INIT
|
||||||
retries: 0
|
retries: 0
|
||||||
|
|
||||||
constructor: (@broker) ->
|
constructor: (@broker, @ui) ->
|
||||||
rateLimitBytes = undefined
|
rateLimitBytes = undefined
|
||||||
if 'off' != query['ratelimit']
|
if 'off' != query['ratelimit']
|
||||||
rateLimitBytes = Params.getByteCount(query, 'ratelimit',
|
rateLimitBytes = Params.getByteCount(query, 'ratelimit',
|
||||||
|
@ -89,8 +87,8 @@ class Snowflake
|
||||||
return if COPY_PASTE_ENABLED
|
return if COPY_PASTE_ENABLED
|
||||||
timer = null
|
timer = null
|
||||||
# Temporary countdown.
|
# Temporary countdown.
|
||||||
countdown = (msg, sec) ->
|
countdown = (msg, sec) =>
|
||||||
ui.setStatus msg + ' (Retrying in ' + sec + ' seconds...)'
|
@ui.setStatus msg + ' (Retrying in ' + sec + ' seconds...)'
|
||||||
sec--
|
sec--
|
||||||
if sec >= 0
|
if sec >= 0
|
||||||
setTimeout((-> countdown(msg, sec)), 1000)
|
setTimeout((-> countdown(msg, sec)), 1000)
|
||||||
|
@ -101,8 +99,8 @@ class Snowflake
|
||||||
clearTimeout timer
|
clearTimeout timer
|
||||||
msg = 'polling for client... '
|
msg = 'polling for client... '
|
||||||
msg += '[retries: ' + @retries + ']' if @retries > 0
|
msg += '[retries: ' + @retries + ']' if @retries > 0
|
||||||
ui.setStatus msg
|
@ui.setStatus msg
|
||||||
recv = broker.getClientOffer()
|
recv = @broker.getClientOffer()
|
||||||
@retries++
|
@retries++
|
||||||
recv.then (desc) =>
|
recv.then (desc) =>
|
||||||
offer = JSON.parse desc
|
offer = JSON.parse desc
|
||||||
|
@ -132,14 +130,12 @@ class Snowflake
|
||||||
pair.onCleanup = (event) =>
|
pair.onCleanup = (event) =>
|
||||||
# Delete from the list of active proxy pairs.
|
# Delete from the list of active proxy pairs.
|
||||||
@proxyPairs.splice(@proxyPairs.indexOf(pair), 1)
|
@proxyPairs.splice(@proxyPairs.indexOf(pair), 1)
|
||||||
# @badge.endProxy() if @badge
|
|
||||||
try
|
try
|
||||||
pair.begin()
|
pair.begin()
|
||||||
catch err
|
catch err
|
||||||
log 'ERROR: ProxyPair exception while connecting.'
|
log 'ERROR: ProxyPair exception while connecting.'
|
||||||
log err
|
log err
|
||||||
return
|
return
|
||||||
# @badge.beginProxy if @badge
|
|
||||||
|
|
||||||
cease: ->
|
cease: ->
|
||||||
while @proxyPairs.length > 0
|
while @proxyPairs.length > 0
|
||||||
|
@ -148,12 +144,10 @@ class Snowflake
|
||||||
disable: ->
|
disable: ->
|
||||||
log 'Disabling Snowflake.'
|
log 'Disabling Snowflake.'
|
||||||
@cease()
|
@cease()
|
||||||
# @badge.disable() if @badge
|
|
||||||
|
|
||||||
die: ->
|
die: ->
|
||||||
log 'Snowflake died.'
|
log 'Snowflake died.'
|
||||||
@cease()
|
@cease()
|
||||||
# @badge.die() if @badge
|
|
||||||
|
|
||||||
# Close all existing ProxyPairs and begin finding new clients from scratch.
|
# Close all existing ProxyPairs and begin finding new clients from scratch.
|
||||||
reset: ->
|
reset: ->
|
||||||
|
@ -163,8 +157,6 @@ class Snowflake
|
||||||
@beginWebRTC()
|
@beginWebRTC()
|
||||||
|
|
||||||
snowflake = null
|
snowflake = null
|
||||||
broker = null
|
|
||||||
ui = null
|
|
||||||
|
|
||||||
# Signalling channel - just tells user to copy paste to the peer.
|
# Signalling channel - just tells user to copy paste to the peer.
|
||||||
# Eventually this should go over the broker.
|
# Eventually this should go over the broker.
|
||||||
|
@ -190,19 +182,19 @@ Signalling =
|
||||||
# Log to both console and UI if applicable.
|
# Log to both console and UI if applicable.
|
||||||
log = (msg) ->
|
log = (msg) ->
|
||||||
console.log 'Snowflake: ' + msg
|
console.log 'Snowflake: ' + msg
|
||||||
ui.log msg
|
snowflake.ui.log msg
|
||||||
|
|
||||||
dbg = (msg) -> log msg if true == ui.debug
|
dbg = (msg) -> log msg if true == snowflake.ui.debug
|
||||||
|
|
||||||
init = ->
|
init = ->
|
||||||
ui = new UI()
|
ui = new UI()
|
||||||
log '== snowflake proxy =='
|
|
||||||
log 'Copy-Paste mode detected.' if COPY_PASTE_ENABLED
|
|
||||||
brokerUrl = Params.getString(query, 'broker', DEFAULT_BROKER)
|
brokerUrl = Params.getString(query, 'broker', DEFAULT_BROKER)
|
||||||
broker = new Broker brokerUrl
|
broker = new Broker brokerUrl
|
||||||
snowflake = new Snowflake(broker)
|
snowflake = new Snowflake broker, ui
|
||||||
# window.snowflake = snowflake
|
|
||||||
# window.ui = ui
|
dbg 'Contacting Broker at ' + broker.url + '\nSnowflake ID: ' + broker.id
|
||||||
|
log '== snowflake proxy =='
|
||||||
|
log 'Copy-Paste mode detected.' if COPY_PASTE_ENABLED
|
||||||
|
|
||||||
relayAddr = Params.getAddress(query, 'relay', DEFAULT_RELAY)
|
relayAddr = Params.getAddress(query, 'relay', DEFAULT_RELAY)
|
||||||
snowflake.setRelayAddr relayAddr
|
snowflake.setRelayAddr relayAddr
|
||||||
|
|
|
@ -1,27 +1,13 @@
|
||||||
###
|
###
|
||||||
jasmine tests for Snowflake
|
jasmine tests for Snowflake proxypair
|
||||||
###
|
###
|
||||||
|
|
||||||
# Stubs to fake browser functionality.
|
|
||||||
class PeerConnection
|
|
||||||
class WebSocket
|
|
||||||
OPEN: 1
|
|
||||||
CLOSED: 0
|
|
||||||
ui =
|
|
||||||
log: ->
|
|
||||||
setActive: ->
|
|
||||||
log = ->
|
|
||||||
|
|
||||||
describe 'ProxyPair', ->
|
describe 'ProxyPair', ->
|
||||||
fakeRelay = Parse.address '0.0.0.0:12345'
|
fakeRelay = Parse.address '0.0.0.0:12345'
|
||||||
rateLimit = new DummyRateLimit()
|
rateLimit = new DummyRateLimit()
|
||||||
destination = []
|
destination = []
|
||||||
fakeClient = send: (d) -> destination.push d
|
fakeClient = send: (d) -> destination.push d
|
||||||
# Fake snowflake to interact with
|
|
||||||
snowflake = {
|
|
||||||
broker:
|
|
||||||
sendAnswer: ->
|
|
||||||
}
|
|
||||||
pp = new ProxyPair(fakeClient, fakeRelay, rateLimit)
|
pp = new ProxyPair(fakeClient, fakeRelay, rateLimit)
|
||||||
|
|
||||||
it 'begins webrtc connection', ->
|
it 'begins webrtc connection', ->
|
||||||
|
|
60
proxy/spec/snowflake.spec.coffee
Normal file
60
proxy/spec/snowflake.spec.coffee
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
###
|
||||||
|
jasmine tests for Snowflake
|
||||||
|
###
|
||||||
|
|
||||||
|
query = {}
|
||||||
|
# Fake browser functionality:
|
||||||
|
class PeerConnection
|
||||||
|
class RTCSessionDescription
|
||||||
|
type: 'offer'
|
||||||
|
class WebSocket
|
||||||
|
OPEN: 1
|
||||||
|
CLOSED: 0
|
||||||
|
log = ->
|
||||||
|
class FakeUI
|
||||||
|
log: ->
|
||||||
|
setActive: ->
|
||||||
|
setStatus: ->
|
||||||
|
fakeUI = new FakeUI()
|
||||||
|
class FakeBroker
|
||||||
|
getClientOffer: -> new Promise((F,R) -> {})
|
||||||
|
# Fake snowflake to interact with
|
||||||
|
snowflake =
|
||||||
|
ui: fakeUI
|
||||||
|
broker:
|
||||||
|
sendAnswer: ->
|
||||||
|
|
||||||
|
describe 'Snowflake', ->
|
||||||
|
|
||||||
|
it 'constructs correctly', ->
|
||||||
|
s = new Snowflake({ fake: 'broker' }, fakeUI)
|
||||||
|
query['ratelimit'] = 'off'
|
||||||
|
expect(s.rateLimit).not.toBeNull()
|
||||||
|
expect(s.broker).toEqual { fake: 'broker' }
|
||||||
|
expect(s.ui).not.toBeNull()
|
||||||
|
expect(s.retries).toBe 0
|
||||||
|
|
||||||
|
it 'sets relay address correctly', ->
|
||||||
|
s = new Snowflake(null, fakeUI)
|
||||||
|
s.setRelayAddr 'foo'
|
||||||
|
expect(s.relayAddr).toEqual 'foo'
|
||||||
|
|
||||||
|
it 'initalizes WebRTC connection', ->
|
||||||
|
s = new Snowflake(new FakeBroker(), fakeUI)
|
||||||
|
spyOn(s.broker, 'getClientOffer').and.callThrough()
|
||||||
|
s.beginWebRTC()
|
||||||
|
expect(s.retries).toBe 1
|
||||||
|
expect(s.broker.getClientOffer).toHaveBeenCalled()
|
||||||
|
|
||||||
|
it 'receives SDP offer', ->
|
||||||
|
s = new Snowflake(new FakeBroker(), fakeUI)
|
||||||
|
s.proxyPair = { receiveWebRTCOffer: -> }
|
||||||
|
spyOn(s.proxyPair, 'receiveWebRTCOffer').and.returnValue true
|
||||||
|
spyOn(s, 'sendAnswer')
|
||||||
|
s.receiveOffer 'foo'
|
||||||
|
expect(s.sendAnswer).toHaveBeenCalled()
|
||||||
|
|
||||||
|
it 'can make a proxypair', ->
|
||||||
|
s = new Snowflake(new FakeBroker(), fakeUI)
|
||||||
|
s.makeProxyPair()
|
||||||
|
expect(s.proxyPairs.length).toBe 2
|
75
proxy/spec/ui.spec.coffee
Normal file
75
proxy/spec/ui.spec.coffee
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
###
|
||||||
|
jasmine tests for Snowflake UI
|
||||||
|
###
|
||||||
|
|
||||||
|
document =
|
||||||
|
getElementById: (id) ->
|
||||||
|
|
||||||
|
describe 'UI', ->
|
||||||
|
|
||||||
|
it 'activates debug mode when badge does not exist', ->
|
||||||
|
spyOn(document, 'getElementById').and.callFake (id) ->
|
||||||
|
return null if 'badge' == id
|
||||||
|
return {
|
||||||
|
focus: ->
|
||||||
|
}
|
||||||
|
u = new UI()
|
||||||
|
expect(u.debug).toBe true
|
||||||
|
expect(document.getElementById.calls.count()).toEqual 5
|
||||||
|
expect(u.$status).not.toBeNull()
|
||||||
|
expect(u.$msglog).not.toBeNull()
|
||||||
|
expect(u.$send).not.toBeNull()
|
||||||
|
expect(u.$input).not.toBeNull()
|
||||||
|
|
||||||
|
it 'is not debug mode when badge exists', ->
|
||||||
|
spyOn(document, 'getElementById').and.callFake (id) ->
|
||||||
|
return {} if 'badge' == id
|
||||||
|
return null
|
||||||
|
u = new UI()
|
||||||
|
expect(u.debug).toBe false
|
||||||
|
expect(document.getElementById).toHaveBeenCalled()
|
||||||
|
expect(document.getElementById.calls.count()).toEqual 1
|
||||||
|
expect(u.$status).toBeNull()
|
||||||
|
expect(u.$msglog).toBeNull()
|
||||||
|
expect(u.$send).toBeNull()
|
||||||
|
expect(u.$input).toBeNull()
|
||||||
|
|
||||||
|
it 'sets status message only when in debug mode', ->
|
||||||
|
u = new UI()
|
||||||
|
u.$status = { innerHTML: '' }
|
||||||
|
u.debug = false
|
||||||
|
u.setStatus('test')
|
||||||
|
expect(u.$status.innerHTML).toEqual ''
|
||||||
|
u.debug = true
|
||||||
|
u.setStatus('test')
|
||||||
|
expect(u.$status.innerHTML).toEqual 'Status: test'
|
||||||
|
|
||||||
|
it 'sets message log css correctly for debug mode', ->
|
||||||
|
u = new UI()
|
||||||
|
u.debug = true
|
||||||
|
u.$msglog = {}
|
||||||
|
u.setActive true
|
||||||
|
expect(u.$msglog.className).toEqual 'active'
|
||||||
|
u.setActive false
|
||||||
|
expect(u.$msglog.className).toEqual ''
|
||||||
|
|
||||||
|
it 'sets badge css correctly for non-debug mode', ->
|
||||||
|
u = new UI()
|
||||||
|
u.debug = false
|
||||||
|
u.$badge = {}
|
||||||
|
u.setActive true
|
||||||
|
expect(u.$badge.className).toEqual 'active'
|
||||||
|
u.setActive false
|
||||||
|
expect(u.$badge.className).toEqual ''
|
||||||
|
|
||||||
|
it 'logs to the textarea correctly, only when debug mode', ->
|
||||||
|
u = new UI()
|
||||||
|
u.$msglog = { value: '', scrollTop: 0, scrollHeight: 1337 }
|
||||||
|
u.debug = false
|
||||||
|
u.log 'test'
|
||||||
|
expect(u.$msglog.value).toEqual ''
|
||||||
|
expect(u.$msglog.scrollTop).toEqual 0
|
||||||
|
u.debug = true
|
||||||
|
u.log 'test'
|
||||||
|
expect(u.$msglog.value).toEqual 'test\n'
|
||||||
|
expect(u.$msglog.scrollTop).toEqual 1337
|
|
@ -1,17 +1,7 @@
|
||||||
###
|
###
|
||||||
jasmine tests for Snowflake
|
jasmine tests for Snowflake utils
|
||||||
###
|
###
|
||||||
|
|
||||||
# Stubs to fake browser functionality.
|
|
||||||
class PeerConnection
|
|
||||||
class WebSocket
|
|
||||||
OPEN: 1
|
|
||||||
CLOSED: 0
|
|
||||||
ui =
|
|
||||||
log: ->
|
|
||||||
setActive: ->
|
|
||||||
log = ->
|
|
||||||
|
|
||||||
describe 'BuildUrl', ->
|
describe 'BuildUrl', ->
|
||||||
it 'should parse just protocol and host', ->
|
it 'should parse just protocol and host', ->
|
||||||
expect(buildUrl('http', 'example.com')).toBe 'http://example.com'
|
expect(buildUrl('http', 'example.com')).toBe 'http://example.com'
|
||||||
|
|
|
@ -6,10 +6,10 @@ class UI
|
||||||
debug = false # True when there's no badge
|
debug = false # True when there's no badge
|
||||||
|
|
||||||
# DOM elements references.
|
# DOM elements references.
|
||||||
$msglog = null
|
$msglog: null
|
||||||
$send = null
|
$send: null
|
||||||
$input = null
|
$input: null
|
||||||
$status = null
|
$status: null
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
@$badge = document.getElementById('badge')
|
@$badge = document.getElementById('badge')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue