commit 556596cc5aa14eecb687bbfb44188d1e733b6855
Author: Serene Han <[email protected]>
Date:   Tue May 24 15:18:54 2016 -0700

    interfaces.go, SnowflakeCollector, better composition
---
 client/client_test.go | 124 +++++++++++++++++++++++++-------------
 client/interfaces.go  |  30 ++++++++++
 client/snowflake.go   | 161 +++++++++++++++++++++++---------------------------
 client/webrtc.go      |  18 ++++++
 4 files changed, 205 insertions(+), 128 deletions(-)

diff --git a/client/client_test.go b/client/client_test.go
index 1ccf206..0bd3844 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -3,12 +3,15 @@ package main
 import (
        "bytes"
        "fmt"
-       "github.com/keroserene/go-webrtc"
-       . "github.com/smartystreets/goconvey/convey"
        "io/ioutil"
+       "net"
        "net/http"
        "strings"
        "testing"
+
+       // "git.torproject.org/pluggable-transports/goptlib.git"
+       "github.com/keroserene/go-webrtc"
+       . "github.com/smartystreets/goconvey/convey"
 )
 
 type MockDataChannel struct {
@@ -56,56 +59,93 @@ func (w FakeDialer) Catch() (*webRTCConn, error) {
        return &webRTCConn{}, nil
 }
 
+type FakeSocksConn struct {
+       net.Conn
+       rejected bool
+}
+
+func (f FakeSocksConn) Reject() error {
+       f.rejected = true
+       return nil
+}
+func (f FakeSocksConn) Grant(addr *net.TCPAddr) error {
+       return nil
+}
+
+type FakeSnowflakeJar struct {
+       toRelease *webRTCConn
+}
+
+func (f FakeSnowflakeJar) Release() *webRTCConn {
+       return nil
+}
+
+func (f FakeSnowflakeJar) Collect() (*webRTCConn, error) {
+       return nil, nil
+}
+
 func TestSnowflakeClient(t *testing.T) {
-       Convey("Snowflake", t, func() {
 
-               Convey("Peers", func() {
+       Convey("WebRTC ConnectLoop", t, func() {
 
-                       Convey("WebRTC ConnectLoop continues until capacity of 
1.\n", func() {
-                               peers := NewPeers(1)
-                               peers.Tongue = FakeDialer{}
+               Convey("WebRTC ConnectLoop continues until capacity of 1.\n", 
func() {
+                       snowflakes := NewSnowflakeJar(1)
+                       snowflakes.Tongue = FakeDialer{}
 
-                               go ConnectLoop(peers)
-                               <-peers.maxedChan
+                       go ConnectLoop(snowflakes)
+                       <-snowflakes.maxedChan
 
-                               So(peers.Count(), ShouldEqual, 1)
-                               r := <-peers.snowflakeChan
-                               So(r, ShouldNotBeNil)
-                               So(peers.Count(), ShouldEqual, 0)
-                       })
+                       So(snowflakes.Count(), ShouldEqual, 1)
+                       r := <-snowflakes.snowflakeChan
+                       So(r, ShouldNotBeNil)
+                       So(snowflakes.Count(), ShouldEqual, 0)
+               })
 
-                       Convey("WebRTC ConnectLoop continues until capacity of 
3.\n", func() {
-                               peers := NewPeers(3)
-                               peers.Tongue = FakeDialer{}
-
-                               go ConnectLoop(peers)
-                               <-peers.maxedChan
-                               So(peers.Count(), ShouldEqual, 3)
-                               <-peers.snowflakeChan
-                               <-peers.snowflakeChan
-                               <-peers.snowflakeChan
-                               So(peers.Count(), ShouldEqual, 0)
-                       })
+               Convey("WebRTC ConnectLoop continues until capacity of 3.\n", 
func() {
+                       snowflakes := NewSnowflakeJar(3)
+                       snowflakes.Tongue = FakeDialer{}
+
+                       go ConnectLoop(snowflakes)
+                       <-snowflakes.maxedChan
+                       So(snowflakes.Count(), ShouldEqual, 3)
+                       <-snowflakes.snowflakeChan
+                       <-snowflakes.snowflakeChan
+                       <-snowflakes.snowflakeChan
+                       So(snowflakes.Count(), ShouldEqual, 0)
+               })
 
-                       Convey("WebRTC ConnectLoop continues filling when 
Snowflakes disconnect.\n", func() {
-                               peers := NewPeers(3)
-                               peers.Tongue = FakeDialer{}
+               Convey("WebRTC ConnectLoop continues filling when Snowflakes 
disconnect.\n", func() {
+                       snowflakes := NewSnowflakeJar(3)
+                       snowflakes.Tongue = FakeDialer{}
 
-                               go ConnectLoop(peers)
-                               <-peers.maxedChan
-                               So(peers.Count(), ShouldEqual, 3)
+                       go ConnectLoop(snowflakes)
+                       <-snowflakes.maxedChan
+                       So(snowflakes.Count(), ShouldEqual, 3)
 
-                               r := <-peers.snowflakeChan
-                               So(peers.Count(), ShouldEqual, 2)
-                               r.Close()
-                               <-peers.maxedChan
-                               So(peers.Count(), ShouldEqual, 3)
+                       r := <-snowflakes.snowflakeChan
+                       So(snowflakes.Count(), ShouldEqual, 2)
+                       r.Close()
+                       <-snowflakes.maxedChan
+                       So(snowflakes.Count(), ShouldEqual, 3)
+
+                       <-snowflakes.snowflakeChan
+                       <-snowflakes.snowflakeChan
+                       <-snowflakes.snowflakeChan
+                       So(snowflakes.Count(), ShouldEqual, 0)
+               })
+       })
+
+       Convey("Snowflake", t, func() {
+
+               SkipConvey("Handler Grants correctly", func() {
+                       socks := &FakeSocksConn{}
+                       snowflakes := &FakeSnowflakeJar{}
+
+                       So(socks.rejected, ShouldEqual, false)
+                       snowflakes.toRelease = nil
+                       handler(socks, snowflakes)
+                       So(socks.rejected, ShouldEqual, true)
 
-                               <-peers.snowflakeChan
-                               <-peers.snowflakeChan
-                               <-peers.snowflakeChan
-                               So(peers.Count(), ShouldEqual, 0)
-                       })
                })
 
                Convey("WebRTC Connection", func() {
diff --git a/client/interfaces.go b/client/interfaces.go
new file mode 100644
index 0000000..80a2ba3
--- /dev/null
+++ b/client/interfaces.go
@@ -0,0 +1,30 @@
+package main
+
+import (
+       "net"
+)
+
+// Interface for collecting and releasing snowflakes.
+type SnowflakeCollector interface {
+       Collect() (*webRTCConn, error)
+       Release() *webRTCConn
+}
+
+// Interface for catching those wild Snowflakes.
+type Tongue interface {
+       Catch() (*webRTCConn, error)
+}
+
+// Interface which primarily adapts to goptlib's SocksConn struct.
+type SocksConnector interface {
+       Grant(*net.TCPAddr) error
+       Reject() error
+       net.Conn
+}
+
+// Interface for the Snowflake's transport.
+// (Specifically, webrtc.DataChannel)
+type SnowflakeChannel interface {
+       Send([]byte)
+       Close() error
+}
diff --git a/client/snowflake.go b/client/snowflake.go
index 464420c..cd7f151 100644
--- a/client/snowflake.go
+++ b/client/snowflake.go
@@ -1,4 +1,5 @@
 // Client transport plugin for the Snowflake pluggable transport.
+// In the Client context, "Snowflake" refers to a remote browser proxy.
 package main
 
 import (
@@ -49,22 +50,12 @@ func copyLoop(a, b net.Conn) {
        log.Println("copy loop ended")
 }
 
-// Interface for catching Snowflakes.
-type Tongue interface {
-       Catch() (*webRTCConn, error)
-}
-
-// Interface for the Snowflake transport. (usually a webrtc.DataChannel)
-type SnowflakeChannel interface {
-       Send([]byte)
-       Close() error
-}
-
-// Collect and track available remote WebRTC Peers, to switch between if the
-// current one disconnects.
-// Right now, it is only possible to use one remote in a circuit. This can be
-// updated once multiplexed transport on a single circuit is available.
-type Peers struct {
+// Collect and track available snowflakes. (Implements SnowflakeCollector)
+// Right now, it is only possible to use one active remote in a circuit.
+// This can be updated once multiplexed transport on a single circuit is 
available.
+// Keeping multiple WebRTC connections available allows for quicker recovery 
when
+// the current snowflake disconnects.
+type SnowflakeJar struct {
        Tongue
        BytesLogger
 
@@ -74,30 +65,42 @@ type Peers struct {
        maxedChan     chan struct{}
 }
 
-func NewPeers(max int) *Peers {
-       p := &Peers{capacity: max}
+func NewSnowflakeJar(max int) *SnowflakeJar {
+       p := &SnowflakeJar{capacity: max}
        p.snowflakeChan = make(chan *webRTCConn, max)
        p.maxedChan = make(chan struct{}, 1)
        return p
 }
 
-// Find, connect, and add a new peer to the internal collection.
-func (p *Peers) FindSnowflake() (*webRTCConn, error) {
-       if p.Count() >= p.capacity {
-               s := fmt.Sprintf("At capacity [%d/%d]", p.Count(), p.capacity)
-               p.maxedChan <- struct{}{}
+// Establish connection to some remote WebRTC peer, and keep it available for
+// later.
+func (jar *SnowflakeJar) Collect() (*webRTCConn, error) {
+       if jar.Count() >= jar.capacity {
+               s := fmt.Sprintf("At capacity [%d/%d]", jar.Count(), 
jar.capacity)
+               jar.maxedChan <- struct{}{}
                return nil, errors.New(s)
        }
-       connection, err := p.Catch()
-       connection.BytesLogger = p.BytesLogger
+       snowflake, err := jar.Catch()
        if err != nil {
                return nil, err
        }
-       return connection, nil
+       jar.snowflakeChan <- snowflake
+       return snowflake, nil
+}
+
+// Prepare and present an available remote WebRTC peer for active use.
+func (jar *SnowflakeJar) Release() *webRTCConn {
+       snowflake, ok := <-jar.snowflakeChan
+       if !ok {
+               return nil
+       }
+       jar.current = snowflake
+       snowflake.BytesLogger = jar.BytesLogger
+       return snowflake
 }
 
 // TODO: Needs fixing.
-func (p *Peers) Count() int {
+func (p *SnowflakeJar) Count() int {
        count := 0
        if p.current != nil {
                count = 1
@@ -106,7 +109,7 @@ func (p *Peers) Count() int {
 }
 
 // Close all remote peers.
-func (p *Peers) End() {
+func (p *SnowflakeJar) End() {
        log.Printf("WebRTC: interruped")
        if nil != p.current {
                p.current.Close()
@@ -118,87 +121,71 @@ func (p *Peers) End() {
 
 // Maintain |SnowflakeCapacity| number of available WebRTC connections, to
 // transfer to the Tor SOCKS handler when needed.
-func ConnectLoop(peers *Peers) {
+func ConnectLoop(snowflakes SnowflakeCollector) {
        for {
-               s, err := peers.FindSnowflake()
+               s, err := snowflakes.Collect()
                if nil == s || nil != err {
                        log.Println("WebRTC Error:", err,
                                " Retrying in", ReconnectTimeout, "seconds...")
+                       // Failed collections get a timeout.
                        <-time.After(time.Second * ReconnectTimeout)
                        continue
                }
-               peers.snowflakeChan <- s
+               // Successful collection gets rate limited to once per second.
                <-time.After(time.Second)
        }
 }
 
-// Implements |Tongue|
-type WebRTCDialer struct {
-       *BrokerChannel
-}
-
-// Initialize a WebRTC Connection by signaling through the broker.
-func (w WebRTCDialer) Catch() (*webRTCConn, error) {
-       if nil == w.BrokerChannel {
-               return nil, errors.New("Cannot Dial WebRTC without a 
BrokerChannel.")
+// Accept local SOCKS connections and pass them to the handler.
+func acceptLoop(ln *pt.SocksListener, snowflakes SnowflakeCollector) error {
+       defer ln.Close()
+       for {
+               log.Println("SOCKS listening...", ln)
+               conn, err := ln.AcceptSocks()
+               log.Println("accepting", conn, err)
+               if err != nil {
+                       if e, ok := err.(net.Error); ok && e.Temporary() {
+                               continue
+                       }
+                       return err
+               }
+               err = handler(conn, snowflakes)
+               if err != nil {
+                       log.Printf("handler error: %s", err)
+               }
        }
-       // TODO: [#3] Fetch ICE server information from Broker.
-       // TODO: [#18] Consider TURN servers here too.
-       config := webrtc.NewConfiguration(iceServers...)
-       connection := NewWebRTCConnection(config, w.BrokerChannel)
-       err := connection.Connect()
-       return connection, err
 }
 
-// Establish a WebRTC channel for SOCKS connections.
-func handler(conn *pt.SocksConn, peers *Peers) error {
+// Given an accepted SOCKS connection, establish a WebRTC connection to the
+// remote peer and exchange traffic.
+func handler(socks SocksConnector, snowflakes SnowflakeCollector) error {
        handlerChan <- 1
        defer func() {
                handlerChan <- -1
        }()
        // Wait for an available WebRTC remote...
-       remote, ok := <-peers.snowflakeChan
-       peers.current = remote
-       if remote == nil || !ok {
-               conn.Reject()
+       snowflake := snowflakes.Release()
+       if nil == snowflake {
+               socks.Reject()
                return errors.New("handler: Received invalid Snowflake")
        }
-       defer conn.Close()
+       defer socks.Close()
        log.Println("handler: Snowflake assigned.")
-
-       err := conn.Grant(&net.TCPAddr{IP: net.IPv4zero, Port: 0})
+       err := socks.Grant(&net.TCPAddr{IP: net.IPv4zero, Port: 0})
        if err != nil {
                return err
        }
 
-       go copyLoop(conn, remote)
+       // Begin exchanging data.
+       go copyLoop(socks, snowflake)
+
        // When WebRTC resets, close the SOCKS connection, which induces new 
handler.
-       <-remote.reset
+       // TODO: Double check this / fix it.
+       <-snowflake.reset
        log.Println("---- Closed ---")
        return nil
 }
 
-func acceptLoop(ln *pt.SocksListener, peers *Peers) error {
-       defer ln.Close()
-       for {
-               log.Println("SOCKS listening...", ln)
-               conn, err := ln.AcceptSocks()
-               log.Println("accepting", conn, err)
-               if err != nil {
-                       if e, ok := err.(net.Error); ok && e.Temporary() {
-                               continue
-                       }
-                       return err
-               }
-               go func() {
-                       err := handler(conn, peers)
-                       if err != nil {
-                               log.Printf("handler error: %s", err)
-                       }
-               }()
-       }
-}
-
 // TODO: Fix since multiplexing changes access to remotes.
 func readSignalingMessages(f *os.File) {
        log.Printf("readSignalingMessages")
@@ -258,19 +245,21 @@ func main() {
                go readSignalingMessages(signalFile)
        }
 
-       // Prepare WebRTC Peers and the Broker, then accumulate connections.
+       // Prepare WebRTC SnowflakeCollector and the Broker, then accumulate 
connections.
        // TODO: Expose remote peer capacity as a flag?
-       remotes := NewPeers(SnowflakeCapacity)
+       snowflakes := NewSnowflakeJar(SnowflakeCapacity)
+
        broker := NewBrokerChannel(brokerURL, frontDomain, 
CreateBrokerTransport())
+       snowflakes.Tongue = WebRTCDialer{broker}
 
-       remotes.BytesLogger = &BytesSyncLogger{
+       // Use a real logger for traffic.
+       snowflakes.BytesLogger = &BytesSyncLogger{
                inboundChan: make(chan int, 5), outboundChan: make(chan int, 5),
                inbound: 0, outbound: 0, inEvents: 0, outEvents: 0,
        }
-       go remotes.BytesLogger.Log()
 
-       remotes.Tongue = WebRTCDialer{broker}
-       go ConnectLoop(remotes)
+       go ConnectLoop(snowflakes)
+       go snowflakes.BytesLogger.Log()
 
        ptInfo, err = pt.ClientSetup(nil)
        if err != nil {
@@ -292,7 +281,7 @@ func main() {
                                pt.CmethodError(methodName, err.Error())
                                break
                        }
-                       go acceptLoop(ln, remotes)
+                       go acceptLoop(ln, snowflakes)
                        pt.Cmethod(methodName, ln.Version(), ln.Addr())
                        listeners = append(listeners, ln)
                default:
@@ -319,7 +308,7 @@ func main() {
                ln.Close()
        }
 
-       remotes.End()
+       snowflakes.End()
 
        // wait for second signal or no more handlers
        sig = nil
diff --git a/client/webrtc.go b/client/webrtc.go
index a47ac19..41642be 100644
--- a/client/webrtc.go
+++ b/client/webrtc.go
@@ -11,6 +11,24 @@ import (
        "time"
 )
 
+// Implements the |Tongue| interface to catch snowflakes, using a 
BrokerChannel.
+type WebRTCDialer struct {
+       *BrokerChannel
+}
+
+// Initialize a WebRTC Connection by signaling through the broker.
+func (w WebRTCDialer) Catch() (*webRTCConn, error) {
+       if nil == w.BrokerChannel {
+               return nil, errors.New("Cannot Dial WebRTC without a 
BrokerChannel.")
+       }
+       // TODO: [#3] Fetch ICE server information from Broker.
+       // TODO: [#18] Consider TURN servers here too.
+       config := webrtc.NewConfiguration(iceServers...)
+       connection := NewWebRTCConnection(config, w.BrokerChannel)
+       err := connection.Connect()
+       return connection, err
+}
+
 // Remote WebRTC peer.  Implements the |net.Conn| interface.
 type webRTCConn struct {
        config    *webrtc.Configuration



_______________________________________________
tor-commits mailing list
[email protected]
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits

Reply via email to