[tor-commits] [snowflake/master] Initialize snowflake instance with a config
arlo at torproject.org
arlo at torproject.org
Thu May 16 16:08:58 UTC 2019
commit 2d8a1690ba833829f086e0bfe9fc450513682d44
Author: Arlo Breault <arlolra at gmail.com>
Date: Wed May 8 16:13:22 2019 -0400
Initialize snowflake instance with a config
---
proxy/Cakefile | 2 ++
proxy/broker.coffee | 24 +++++++++--------
proxy/config.coffee | 26 ++++++++++++++++++
proxy/init.coffee | 57 ++++++++++------------------------------
proxy/proxypair.coffee | 10 +++----
proxy/snowflake.coffee | 25 +++++++++++-------
proxy/spec/broker.spec.coffee | 12 ++++-----
proxy/spec/init.spec.coffee | 28 ++++++++++++++++++++
proxy/spec/proxypair.spec.coffee | 5 ++--
proxy/spec/snowflake.spec.coffee | 43 ++++++++----------------------
proxy/util.coffee | 4 +--
11 files changed, 124 insertions(+), 112 deletions(-)
diff --git a/proxy/Cakefile b/proxy/Cakefile
index 8013dac..e0d610c 100644
--- a/proxy/Cakefile
+++ b/proxy/Cakefile
@@ -11,6 +11,7 @@ FILES = [
'broker.coffee'
'ui.coffee'
'snowflake.coffee'
+ 'config.coffee'
]
FILES_SPEC = [
'spec/util.spec.coffee'
@@ -19,6 +20,7 @@ FILES_SPEC = [
'spec/proxypair.spec.coffee'
'spec/snowflake.spec.coffee'
'spec/websocket.spec.coffee'
+ 'spec/init.spec.coffee'
]
FILES_ALL = FILES.concat FILES_SPEC
OUTFILE = 'snowflake.js'
diff --git a/proxy/broker.coffee b/proxy/broker.coffee
index 981a761..ca8a2e5 100644
--- a/proxy/broker.coffee
+++ b/proxy/broker.coffee
@@ -7,12 +7,14 @@ to get assigned to clients.
# Represents a broker running remotely.
class Broker
- @STATUS_OK = 200
- @STATUS_GONE = 410
- @STATUS_GATEWAY_TIMEOUT = 504
+ @STATUS:
+ OK: 200
+ GONE: 410
+ GATEWAY_TIMEOUT: 504
- @MESSAGE_TIMEOUT = 'Timed out waiting for a client offer.'
- @MESSAGE_UNEXPECTED = 'Unexpected status.'
+ @MESSAGE:
+ TIMEOUT: 'Timed out waiting for a client offer.'
+ UNEXPECTED: 'Unexpected status.'
clients: 0
@@ -38,15 +40,15 @@ class Broker
xhr.onreadystatechange = ->
return if xhr.DONE != xhr.readyState
switch xhr.status
- when Broker.STATUS_OK
+ when Broker.STATUS.OK
fulfill xhr.responseText # Should contain offer.
- when Broker.STATUS_GATEWAY_TIMEOUT
- reject Broker.MESSAGE_TIMEOUT
+ when Broker.STATUS.GATEWAY_TIMEOUT
+ reject Broker.MESSAGE.TIMEOUT
else
log 'Broker ERROR: Unexpected ' + xhr.status +
' - ' + xhr.statusText
snowflake.ui.setStatus ' failure. Please refresh.'
- reject Broker.MESSAGE_UNEXPECTED
+ reject Broker.MESSAGE.UNEXPECTED
@_xhr = xhr # Used by spec to fake async Broker interaction
@_postRequest id, xhr, 'proxy', id
@@ -59,10 +61,10 @@ class Broker
xhr.onreadystatechange = ->
return if xhr.DONE != xhr.readyState
switch xhr.status
- when Broker.STATUS_OK
+ when Broker.STATUS.OK
dbg 'Broker: Successfully replied with answer.'
dbg xhr.responseText
- when Broker.STATUS_GONE
+ when Broker.STATUS.GONE
dbg 'Broker: No longer valid to reply with answer.'
else
dbg 'Broker ERROR: Unexpected ' + xhr.status +
diff --git a/proxy/config.coffee b/proxy/config.coffee
new file mode 100644
index 0000000..810c79f
--- /dev/null
+++ b/proxy/config.coffee
@@ -0,0 +1,26 @@
+class Config
+ brokerUrl: 'snowflake-broker.bamsoftware.com'
+ relayAddr:
+ host: 'snowflake.bamsoftware.com'
+ port: '443'
+ # Original non-wss relay:
+ # host: '192.81.135.242'
+ # port: 9902
+
+ cookieName: "snowflake-allow"
+
+ # Bytes per second. Set to undefined to disable limit.
+ rateLimitBytes: undefined
+ minRateLimit: 10 * 1024
+ rateLimitHistory: 5.0
+ defaultBrokerPollInterval: 5.0 * 1000
+
+ maxNumClients: 1
+ connectionsPerClient: 1
+
+ # TODO: Different ICE servers.
+ pcConfig = {
+ iceServers: [
+ { urls: ['stun:stun.l.google.com:19302'] }
+ ]
+ }
diff --git a/proxy/init.coffee b/proxy/init.coffee
index 497a54f..48a2c39 100644
--- a/proxy/init.coffee
+++ b/proxy/init.coffee
@@ -1,37 +1,9 @@
-# General snowflake proxy constants.
-# For websocket-specific constants, see websocket.coffee.
-BROKER = 'snowflake-broker.bamsoftware.com'
-RELAY =
- host: 'snowflake.bamsoftware.com'
- port: '443'
- # Original non-wss relay:
- # host: '192.81.135.242'
- # port: 9902
-COOKIE_NAME = "snowflake-allow"
-# Bytes per second. Set to undefined to disable limit.
-DEFAULT_RATE_LIMIT = undefined
-MIN_RATE_LIMIT = 10 * 1024
-RATE_LIMIT_HISTORY = 5.0
-DEFAULT_BROKER_POLL_INTERVAL = 5.0 * 1000
-
-MAX_NUM_CLIENTS = 1
-CONNECTIONS_PER_CLIENT = 1
-
-# TODO: Different ICE servers.
-config = {
- iceServers: [
- { urls: ['stun:stun.l.google.com:19302'] }
- ]
-}
-
-CONFIRMATION_MESSAGE = 'You\'re currently serving a Tor user via Snowflake.'
+snowflake = null
query = Query.parse(location)
-DEBUG = Params.getBool(query, 'debug', false)
-
-snowflake = null
-silenceNotifications = false
+debug = Params.getBool(query, 'debug', false)
+silenceNotifications = Params.getBool(query, 'silent', false)
# Log to both console and UI if applicable.
# Requires that the snowflake and UI objects are hooked up in order to
@@ -40,13 +12,17 @@ log = (msg) ->
console.log 'Snowflake: ' + msg
snowflake?.ui.log msg
-dbg = (msg) -> log msg if DEBUG or (snowflake?.ui instanceof DebugUI)
-
+dbg = (msg) -> log msg if debug or (snowflake?.ui instanceof DebugUI)
###
Entry point.
###
init = () ->
+ config = new Config
+
+ if 'off' != query['ratelimit']
+ config.rateLimitBytes = Params.getByteCount(query, 'ratelimit', config.rateLimitBytes)
+
ui = null
if (document.getElementById('badge') != null)
ui = new BadgeUI()
@@ -57,29 +33,24 @@ init = () ->
else
ui = new UI()
- rateLimitBytes = undefined
- if 'off' != query['ratelimit']
- rateLimitBytes = Params.getByteCount(query, 'ratelimit', DEFAULT_RATE_LIMIT)
-
- silenceNotifications = Params.getBool(query, 'silent', false)
- broker = new Broker BROKER
- snowflake = new Snowflake broker, ui, rateLimitBytes
+ broker = new Broker config.brokerUrl
+ snowflake = new Snowflake config, ui, broker
log '== snowflake proxy =='
- if Util.snowflakeIsDisabled()
+ if Util.snowflakeIsDisabled(config.cookieName)
# Do not activate the proxy if any number of conditions are true.
log 'Currently not active.'
return
# Otherwise, begin setting up WebRTC and acting as a proxy.
dbg 'Contacting Broker at ' + broker.url
- snowflake.setRelayAddr RELAY
+ snowflake.setRelayAddr config.relayAddr
snowflake.beginWebRTC()
# Notification of closing tab with active proxy.
window.onbeforeunload = ->
if !silenceNotifications && Snowflake.MODE.WEBRTC_READY == snowflake.state
- return CONFIRMATION_MESSAGE
+ return Snowflake.MESSAGE.CONFIRMATION
null
window.onunload = ->
diff --git a/proxy/proxypair.coffee b/proxy/proxypair.coffee
index bd6850b..879c6b6 100644
--- a/proxy/proxypair.coffee
+++ b/proxy/proxypair.coffee
@@ -24,14 +24,14 @@ class ProxyPair
- @relayAddr is the destination relay
- @rateLimit specifies a rate limit on traffic
###
- constructor: (@relayAddr, @rateLimit) ->
+ constructor: (@relayAddr, @rateLimit, @pcConfig) ->
@id = Util.genSnowflakeID()
@c2rSchedule = []
@r2cSchedule = []
# Prepare a WebRTC PeerConnection and await for an SDP offer.
begin: ->
- @pc = new PeerConnection config, {
+ @pc = new PeerConnection @pcConfig, {
optional: [
{ DtlsSrtpKeyAgreement: true }
{ RtpDataChannels: false }
@@ -126,15 +126,13 @@ class ProxyPair
# WebRTC --> websocket
onClientToRelayMessage: (msg) =>
- if DEBUG
- log 'WebRTC --> websocket data: ' + msg.data.byteLength + ' bytes'
+ dbg 'WebRTC --> websocket data: ' + msg.data.byteLength + ' bytes'
@c2rSchedule.push msg.data
@flush()
# websocket --> WebRTC
onRelayToClientMessage: (event) =>
- if DEBUG
- log 'websocket --> WebRTC data: ' + event.data.byteLength + ' bytes'
+ dbg 'websocket --> WebRTC data: ' + event.data.byteLength + ' bytes'
@r2cSchedule.push event.data
@flush()
diff --git a/proxy/snowflake.coffee b/proxy/snowflake.coffee
index fa421ee..66885d9 100644
--- a/proxy/snowflake.coffee
+++ b/proxy/snowflake.coffee
@@ -16,21 +16,26 @@ class Snowflake
retries: 0
# Janky state machine
- @MODE =
+ @MODE:
INIT: 0
WEBRTC_CONNECTING: 1
WEBRTC_READY: 2
+ @MESSAGE:
+ CONFIRMATION: 'You\'re currently serving a Tor user via Snowflake.'
+
# Prepare the Snowflake with a Broker (to find clients) and optional UI.
- constructor: (@broker, @ui, rateLimitBytes) ->
+ constructor: (@config, @ui, @broker) ->
@state = Snowflake.MODE.INIT
@proxyPairs = []
- if undefined == rateLimitBytes
+ if undefined == @config.rateLimitBytes
@rateLimit = new DummyRateLimit()
else
- @rateLimit = new BucketRateLimit(rateLimitBytes * RATE_LIMIT_HISTORY,
- RATE_LIMIT_HISTORY)
+ @rateLimit = new BucketRateLimit(
+ @config.rateLimitBytes * @config.rateLimitHistory,
+ @config.rateLimitHistory
+ )
@retries = 0
# Set the target relay address spec, which is expected to be websocket.
@@ -45,7 +50,7 @@ class Snowflake
# process. |pollBroker| automatically arranges signalling.
beginWebRTC: ->
@state = Snowflake.MODE.WEBRTC_CONNECTING
- for i in [1..CONNECTIONS_PER_CLIENT]
+ for i in [1.. at config.connectionsPerClient]
@makeProxyPair @relayAddr
log 'ProxyPair Slots: ' + @proxyPairs.length
log 'Snowflake IDs: ' + (@proxyPairs.map (p) -> p.id).join ' | '
@@ -77,9 +82,9 @@ class Snowflake
recv = @broker.getClientOffer pair.id
recv.then (desc) =>
@receiveOffer pair, desc
- countdown('Serving 1 new client.', DEFAULT_BROKER_POLL_INTERVAL / 1000)
- , (err) ->
- countdown(err, DEFAULT_BROKER_POLL_INTERVAL / 1000)
+ countdown('Serving 1 new client.', @config.defaultBrokerPollInterval / 1000)
+ , (err) =>
+ countdown(err, @config.defaultBrokerPollInterval / 1000)
@retries++
findClients()
@@ -111,7 +116,7 @@ class Snowflake
.catch fail
makeProxyPair: (relay) ->
- pair = new ProxyPair relay, @rateLimit
+ pair = new ProxyPair relay, @rateLimit, @config.pcConfig
@proxyPairs.push pair
pair.onCleanup = (event) =>
# Delete from the list of active proxy pairs.
diff --git a/proxy/spec/broker.spec.coffee b/proxy/spec/broker.spec.coffee
index 3532fe1..2b1d2bd 100644
--- a/proxy/spec/broker.spec.coffee
+++ b/proxy/spec/broker.spec.coffee
@@ -25,7 +25,7 @@ describe 'Broker', ->
# fake successful request and response from broker.
spyOn(b, '_postRequest').and.callFake ->
b._xhr.readyState = b._xhr.DONE
- b._xhr.status = Broker.STATUS_OK
+ b._xhr.status = Broker.STATUS.OK
b._xhr.responseText = 'fake offer'
b._xhr.onreadystatechange()
poll = b.getClientOffer()
@@ -35,7 +35,7 @@ describe 'Broker', ->
expect(desc).toEqual 'fake offer'
done()
.catch ->
- fail 'should not reject on Broker.STATUS_OK'
+ fail 'should not reject on Broker.STATUS.OK'
done()
it 'rejects if the broker timed-out', (done) ->
@@ -43,16 +43,16 @@ describe 'Broker', ->
# fake timed-out request from broker
spyOn(b, '_postRequest').and.callFake ->
b._xhr.readyState = b._xhr.DONE
- b._xhr.status = Broker.STATUS_GATEWAY_TIMEOUT
+ b._xhr.status = Broker.STATUS.GATEWAY_TIMEOUT
b._xhr.onreadystatechange()
poll = b.getClientOffer()
expect(poll).not.toBeNull()
expect(b._postRequest).toHaveBeenCalled()
poll.then (desc) ->
- fail 'should not fulfill on GATEWAY_TIMEOUT'
+ fail 'should not fulfill on Broker.STATUS.GATEWAY_TIMEOUT'
done()
, (err) ->
- expect(err).toBe Broker.MESSAGE_TIMEOUT
+ expect(err).toBe Broker.MESSAGE.TIMEOUT
done()
it 'rejects on any other status', (done) ->
@@ -69,7 +69,7 @@ describe 'Broker', ->
fail 'should not fulfill on non-OK status'
done()
, (err) ->
- expect(err).toBe Broker.MESSAGE_UNEXPECTED
+ expect(err).toBe Broker.MESSAGE.UNEXPECTED
expect(b._xhr.status).toBe 1337
done()
diff --git a/proxy/spec/init.spec.coffee b/proxy/spec/init.spec.coffee
new file mode 100644
index 0000000..4134a22
--- /dev/null
+++ b/proxy/spec/init.spec.coffee
@@ -0,0 +1,28 @@
+
+# Fake snowflake to interact with
+snowflake =
+ ui: new UI
+ broker:
+ sendAnswer: ->
+ state: Snowflake.MODE.INIT
+
+describe 'Init', ->
+
+ it 'gives a dialog when closing, only while active', ->
+ silenceNotifications = false
+ snowflake.state = Snowflake.MODE.WEBRTC_READY
+ msg = window.onbeforeunload()
+ expect(snowflake.state).toBe Snowflake.MODE.WEBRTC_READY
+ expect(msg).toBe Snowflake.MESSAGE.CONFIRMATION
+
+ snowflake.state = Snowflake.MODE.INIT
+ msg = window.onbeforeunload()
+ expect(snowflake.state).toBe Snowflake.MODE.INIT
+ expect(msg).toBe null
+
+ it 'does not give a dialog when silent flag is on', ->
+ silenceNotifications = true
+ snowflake.state = Snowflake.MODE.WEBRTC_READY
+ msg = window.onbeforeunload()
+ expect(snowflake.state).toBe Snowflake.MODE.WEBRTC_READY
+ expect(msg).toBe null
diff --git a/proxy/spec/proxypair.spec.coffee b/proxy/spec/proxypair.spec.coffee
index 566c6f1..87aeb55 100644
--- a/proxy/spec/proxypair.spec.coffee
+++ b/proxy/spec/proxypair.spec.coffee
@@ -24,10 +24,11 @@ arrayMatching = (sample) -> {
describe 'ProxyPair', ->
fakeRelay = Parse.address '0.0.0.0:12345'
- rateLimit = new DummyRateLimit()
+ rateLimit = new DummyRateLimit
+ config = new Config
destination = []
# Using the mock PeerConnection definition from spec/snowflake.spec.coffee.
- pp = new ProxyPair(fakeRelay, rateLimit)
+ pp = new ProxyPair(fakeRelay, rateLimit, config.pcConfig)
beforeEach ->
pp.begin()
diff --git a/proxy/spec/snowflake.spec.coffee b/proxy/spec/snowflake.spec.coffee
index a619a06..2c87204 100644
--- a/proxy/spec/snowflake.spec.coffee
+++ b/proxy/spec/snowflake.spec.coffee
@@ -15,40 +15,38 @@ class WebSocket
constructor: ->
@bufferedAmount = 0
send: (data) ->
+
log = ->
-fakeUI = new UI()
+
+config = new Config
+ui = new UI
+
class FakeBroker
getClientOffer: -> new Promise((F,R) -> {})
-# Fake snowflake to interact with
-snowflake =
- ui: fakeUI
- broker:
- sendAnswer: ->
- state: Snowflake.MODE.INIT
describe 'Snowflake', ->
it 'constructs correctly', ->
- s = new Snowflake({ fake: 'broker' }, fakeUI)
+ s = new Snowflake(config, ui, { fake: 'broker' })
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 = new Snowflake(config, ui, null)
s.setRelayAddr 'foo'
expect(s.relayAddr).toEqual 'foo'
it 'initalizes WebRTC connection', ->
- s = new Snowflake(new FakeBroker(), fakeUI)
+ s = new Snowflake(config, ui, new FakeBroker())
spyOn(s.broker, 'getClientOffer').and.callThrough()
s.beginWebRTC()
expect(s.retries).toBe 1
expect(s.broker.getClientOffer).toHaveBeenCalled()
it 'receives SDP offer and sends answer', ->
- s = new Snowflake(new FakeBroker(), fakeUI)
+ s = new Snowflake(config, ui, new FakeBroker())
pair = { receiveWebRTCOffer: -> }
spyOn(pair, 'receiveWebRTCOffer').and.returnValue true
spyOn(s, 'sendAnswer')
@@ -56,7 +54,7 @@ describe 'Snowflake', ->
expect(s.sendAnswer).toHaveBeenCalled()
it 'does not send answer when receiving invalid offer', ->
- s = new Snowflake(new FakeBroker(), fakeUI)
+ s = new Snowflake(config, ui, new FakeBroker())
pair = { receiveWebRTCOffer: -> }
spyOn(pair, 'receiveWebRTCOffer').and.returnValue false
spyOn(s, 'sendAnswer')
@@ -64,25 +62,6 @@ describe 'Snowflake', ->
expect(s.sendAnswer).not.toHaveBeenCalled()
it 'can make a proxypair', ->
- s = new Snowflake(new FakeBroker(), fakeUI)
+ s = new Snowflake(config, ui, new FakeBroker())
s.makeProxyPair()
expect(s.proxyPairs.length).toBe 1
-
- it 'gives a dialog when closing, only while active', ->
- silenceNotifications = false
- snowflake.state = Snowflake.MODE.WEBRTC_READY
- msg = window.onbeforeunload()
- expect(snowflake.state).toBe Snowflake.MODE.WEBRTC_READY
- expect(msg).toBe CONFIRMATION_MESSAGE
-
- snowflake.state = Snowflake.MODE.INIT
- msg = window.onbeforeunload()
- expect(snowflake.state).toBe Snowflake.MODE.INIT
- expect(msg).toBe null
-
- it 'does not give a dialog when silent flag is on', ->
- silenceNotifications = true
- snowflake.state = Snowflake.MODE.WEBRTC_READY
- msg = window.onbeforeunload()
- expect(snowflake.state).toBe Snowflake.MODE.WEBRTC_READY
- expect(msg).toBe null
diff --git a/proxy/util.coffee b/proxy/util.coffee
index 15b4152..b8057fb 100644
--- a/proxy/util.coffee
+++ b/proxy/util.coffee
@@ -22,10 +22,10 @@ class Util
@genSnowflakeID: ->
Math.random().toString(36).substring(2)
- @snowflakeIsDisabled = ->
+ @snowflakeIsDisabled = (cookieName) ->
cookies = Parse.cookie document.cookie
# Do nothing if snowflake has not been opted in by user.
- if cookies[COOKIE_NAME] != '1'
+ if cookies[cookieName] != '1'
log 'Not opted-in. Please click the badge to change options.'
return true
# Also do nothing if running in Tor Browser.
More information about the tor-commits
mailing list