[tor-commits] [snowflake/master] move webRTCConn into separate file
serene at torproject.org
serene at torproject.org
Fri Mar 4 21:22:52 UTC 2016
commit f86f5b5b07d8b9409a58d85f3169e85446fd17e8
Author: Serene Han <keroserene+git at gmail.com>
Date: Sun Feb 21 22:03:17 2016 -0800
move webRTCConn into separate file
---
client/snowflake.go | 247 --------------------------------------------------
client/webrtc.go | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 255 insertions(+), 247 deletions(-)
diff --git a/client/snowflake.go b/client/snowflake.go
index 4e31f15..cc7f547 100644
--- a/client/snowflake.go
+++ b/client/snowflake.go
@@ -3,10 +3,8 @@ package main
import (
"bufio"
- "bytes"
"errors"
"flag"
- "fmt"
"io"
"log"
"net"
@@ -14,7 +12,6 @@ import (
"os/signal"
"sync"
"syscall"
- "time"
"git.torproject.org/pluggable-transports/goptlib.git"
"github.com/keroserene/go-webrtc"
@@ -57,250 +54,6 @@ type SnowflakeChannel interface {
Close() error
}
-// Implements net.Conn interface
-type webRTCConn struct {
- config *webrtc.Configuration
- pc *webrtc.PeerConnection
- snowflake SnowflakeChannel // Interface holding the WebRTC DataChannel.
- broker *BrokerChannel
- offerChannel chan *webrtc.SessionDescription
- answerChannel chan *webrtc.SessionDescription
- errorChannel chan error
- writeChannel chan []byte
- recvPipe *io.PipeReader
- writePipe *io.PipeWriter
- buffer bytes.Buffer
- reset chan struct{}
- *BytesInfo
-}
-
-var webrtcRemote *webRTCConn
-
-func (c *webRTCConn) Read(b []byte) (int, error) {
- return c.recvPipe.Read(b)
-}
-
-func (c *webRTCConn) Write(b []byte) (int, error) {
- c.SendData(b)
- return len(b), nil
-}
-
-func (c *webRTCConn) Close() error {
- // Data channel closed implicitly?
- return c.pc.Close()
-}
-
-func (c *webRTCConn) LocalAddr() net.Addr {
- return nil
-}
-
-func (c *webRTCConn) RemoteAddr() net.Addr {
- return nil
-}
-
-func (c *webRTCConn) SetDeadline(t time.Time) error {
- return fmt.Errorf("SetDeadline not implemented")
-}
-
-func (c *webRTCConn) SetReadDeadline(t time.Time) error {
- return fmt.Errorf("SetReadDeadline not implemented")
-}
-
-func (c *webRTCConn) SetWriteDeadline(t time.Time) error {
- return fmt.Errorf("SetWriteDeadline not implemented")
-}
-
-func (c *webRTCConn) PreparePeerConnection() {
- if nil != c.pc {
- c.pc.Close()
- c.pc = nil
- }
- pc, err := webrtc.NewPeerConnection(c.config)
- if err != nil {
- log.Printf("NewPeerConnection: %s", err)
- c.errorChannel <- err
- }
- // Prepare PeerConnection callbacks.
- pc.OnNegotiationNeeded = func() {
- log.Println("WebRTC: OnNegotiationNeeded")
- go func() {
- offer, err := pc.CreateOffer()
- // TODO: Potentially timeout and retry if ICE isn't working.
- if err != nil {
- c.errorChannel <- err
- return
- }
- err = pc.SetLocalDescription(offer)
- if err != nil {
- c.errorChannel <- err
- return
- }
- }()
- }
- // Allow candidates to accumulate until OnIceComplete.
- pc.OnIceCandidate = func(candidate webrtc.IceCandidate) {
- log.Printf(candidate.Candidate)
- }
- // TODO: This may soon be deprecated, consider OnIceGatheringStateChange.
- pc.OnIceComplete = func() {
- log.Printf("WebRTC: OnIceComplete")
- c.offerChannel <- pc.LocalDescription()
- }
- // This callback is not expected, as the Client initiates the creation
- // of the data channel, not the remote peer.
- pc.OnDataChannel = func(channel *webrtc.DataChannel) {
- log.Println("OnDataChannel")
- panic("Unexpected OnDataChannel!")
- }
- c.pc = pc
- log.Println("WebRTC: PeerConnection created.")
-}
-
-// Create a WebRTC DataChannel locally.
-func (c *webRTCConn) EstablishDataChannel() error {
- dc, err := c.pc.CreateDataChannel("snowflake", webrtc.Init{})
- // Triggers "OnNegotiationNeeded" on the PeerConnection, which will prepare
- // an SDP offer while other goroutines operating on this struct handle the
- // signaling. Eventually fires "OnOpen".
- if err != nil {
- log.Printf("CreateDataChannel: %s", err)
- return err
- }
- dc.OnOpen = func() {
- log.Println("WebRTC: DataChannel.OnOpen")
- if nil != c.snowflake {
- panic("PeerConnection snowflake already exists.")
- }
- // Flush the buffer, then enable datachannel.
- dc.Send(c.buffer.Bytes())
- log.Println("Flushed", c.buffer.Len(), "bytes")
- c.buffer.Reset()
- c.snowflake = dc
- }
- dc.OnClose = func() {
- // Disable the DataChannel as a write destination.
- // Future writes will go to the buffer until a new DataChannel is available.
- log.Println("WebRTC: DataChannel.OnClose")
- // Only reset if this OnClose was triggered remotely.
- if nil != c.snowflake {
- c.snowflake = nil
- c.Reset()
- }
- }
- dc.OnMessage = func(msg []byte) {
- c.BytesInfo.AddInbound(len(msg))
- n, err := c.writePipe.Write(msg)
- if err != nil {
- // TODO: Maybe shouldn't actually close.
- log.Println("Error writing to SOCKS pipe")
- c.writePipe.CloseWithError(err)
- }
- if n != len(msg) {
- panic("short write")
- }
- }
- log.Println("WebRTC: DataChannel created.")
- return nil
-}
-
-// Block until an offer is available, then send it to either
-// the Broker or signal pipe.
-func (c *webRTCConn) SendOffer() error {
- select {
- case offer := <-c.offerChannel:
- if "" == brokerURL {
- log.Printf("Please Copy & Paste the following to the peer:")
- log.Printf("----------------")
- fmt.Fprintln(logFile, "\n"+offer.Serialize()+"\n")
- log.Printf("----------------")
- return nil
- }
- // Otherwise, use Broker.
- go func() {
- log.Println("Sending offer via BrokerChannel...\nTarget URL: ", brokerURL,
- "\nFront URL: ", frontDomain)
- answer, err := c.broker.Negotiate(c.pc.LocalDescription())
- if nil != err || nil == answer {
- log.Printf("BrokerChannel error: %s", err)
- answer = nil
- }
- c.answerChannel <- answer
- }()
- case err := <-c.errorChannel:
- c.pc.Close()
- return err
- }
- return nil
-}
-
-func (c *webRTCConn) ReceiveAnswer() {
- go func() {
- answer, ok := <-c.answerChannel
- if !ok || nil == answer {
- log.Printf("Failed to retrieve answer. Retrying in %d seconds", ReconnectTimeout)
- <-time.After(time.Second * ReconnectTimeout)
- c.Reset()
- return
- }
- log.Printf("Received Answer:\n\n%s\n", answer.Sdp)
- err := c.pc.SetRemoteDescription(answer)
- if nil != err {
- c.errorChannel <- err
- }
- }()
-}
-
-func (c *webRTCConn) SendData(data []byte) {
- c.BytesInfo.AddOutbound(len(data))
- // Buffer the data in case datachannel isn't available yet.
- if nil == c.snowflake {
- log.Printf("Buffered %d bytes --> WebRTC", len(data))
- c.buffer.Write(data)
- return
- }
- c.writeChannel <- data
-}
-
-// Expected in own goroutine.
-func (c *webRTCConn) SendLoop() {
- log.Println("send loop")
- for data := range c.writeChannel {
- // Flush buffer if necessary.
- for c.buffer.Len() > 0 {
- c.snowflake.Send(c.buffer.Bytes())
- log.Println("Flushed", c.buffer.Len(), "bytes")
- c.buffer.Reset()
- }
- c.snowflake.Send(data)
- }
-}
-
-// WebRTC re-establishment loop. Expected in own goroutine.
-func (c *webRTCConn) ConnectLoop() {
- for {
- log.Println("Establishing WebRTC connection...")
- // TODO: When go-webrtc is more stable, it's possible that a new
- // PeerConnection won't need to be re-prepared each time.
- c.PreparePeerConnection()
- err := c.EstablishDataChannel()
- if err == nil {
- c.SendOffer()
- c.ReceiveAnswer()
- <-c.reset
- log.Println(" --- snowflake connection reset ---")
- } else {
- log.Println("WebRTC: Could not establish DataChannel.")
- }
- }
-}
-
-func (c *webRTCConn) Reset() {
- go func() {
- c.reset <- struct{}{} // Attempt to negotiate a new datachannel..
- log.Println("WebRTC resetting...")
- }()
-}
-
// Initialize a WebRTC Connection.
func dialWebRTC(config *webrtc.Configuration, broker *BrokerChannel) (
*webRTCConn, error) {
diff --git a/client/webrtc.go b/client/webrtc.go
new file mode 100644
index 0000000..b1e1628
--- /dev/null
+++ b/client/webrtc.go
@@ -0,0 +1,255 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/keroserene/go-webrtc"
+ "io"
+ "log"
+ "net"
+ "time"
+)
+
+// Implements net.Conn interface
+type webRTCConn struct {
+ config *webrtc.Configuration
+ pc *webrtc.PeerConnection
+ snowflake SnowflakeChannel // Interface holding the WebRTC DataChannel.
+ broker *BrokerChannel
+ offerChannel chan *webrtc.SessionDescription
+ answerChannel chan *webrtc.SessionDescription
+ errorChannel chan error
+ writeChannel chan []byte
+ recvPipe *io.PipeReader
+ writePipe *io.PipeWriter
+ buffer bytes.Buffer
+ reset chan struct{}
+ *BytesInfo
+}
+
+var webrtcRemote *webRTCConn
+
+func (c *webRTCConn) Read(b []byte) (int, error) {
+ return c.recvPipe.Read(b)
+}
+
+func (c *webRTCConn) Write(b []byte) (int, error) {
+ c.SendData(b)
+ return len(b), nil
+}
+
+func (c *webRTCConn) Close() error {
+ // Data channel closed implicitly?
+ return c.pc.Close()
+}
+
+func (c *webRTCConn) LocalAddr() net.Addr {
+ return nil
+}
+
+func (c *webRTCConn) RemoteAddr() net.Addr {
+ return nil
+}
+
+func (c *webRTCConn) SetDeadline(t time.Time) error {
+ return fmt.Errorf("SetDeadline not implemented")
+}
+
+func (c *webRTCConn) SetReadDeadline(t time.Time) error {
+ return fmt.Errorf("SetReadDeadline not implemented")
+}
+
+func (c *webRTCConn) SetWriteDeadline(t time.Time) error {
+ return fmt.Errorf("SetWriteDeadline not implemented")
+}
+
+func (c *webRTCConn) PreparePeerConnection() {
+ if nil != c.pc {
+ c.pc.Close()
+ c.pc = nil
+ }
+ pc, err := webrtc.NewPeerConnection(c.config)
+ if err != nil {
+ log.Printf("NewPeerConnection: %s", err)
+ c.errorChannel <- err
+ }
+ // Prepare PeerConnection callbacks.
+ pc.OnNegotiationNeeded = func() {
+ log.Println("WebRTC: OnNegotiationNeeded")
+ go func() {
+ offer, err := pc.CreateOffer()
+ // TODO: Potentially timeout and retry if ICE isn't working.
+ if err != nil {
+ c.errorChannel <- err
+ return
+ }
+ err = pc.SetLocalDescription(offer)
+ if err != nil {
+ c.errorChannel <- err
+ return
+ }
+ }()
+ }
+ // Allow candidates to accumulate until OnIceComplete.
+ pc.OnIceCandidate = func(candidate webrtc.IceCandidate) {
+ log.Printf(candidate.Candidate)
+ }
+ // TODO: This may soon be deprecated, consider OnIceGatheringStateChange.
+ pc.OnIceComplete = func() {
+ log.Printf("WebRTC: OnIceComplete")
+ c.offerChannel <- pc.LocalDescription()
+ }
+ // This callback is not expected, as the Client initiates the creation
+ // of the data channel, not the remote peer.
+ pc.OnDataChannel = func(channel *webrtc.DataChannel) {
+ log.Println("OnDataChannel")
+ panic("Unexpected OnDataChannel!")
+ }
+ c.pc = pc
+ log.Println("WebRTC: PeerConnection created.")
+}
+
+// Create a WebRTC DataChannel locally.
+func (c *webRTCConn) EstablishDataChannel() error {
+ dc, err := c.pc.CreateDataChannel("snowflake", webrtc.Init{})
+ // Triggers "OnNegotiationNeeded" on the PeerConnection, which will prepare
+ // an SDP offer while other goroutines operating on this struct handle the
+ // signaling. Eventually fires "OnOpen".
+ if err != nil {
+ log.Printf("CreateDataChannel: %s", err)
+ return err
+ }
+ dc.OnOpen = func() {
+ log.Println("WebRTC: DataChannel.OnOpen")
+ if nil != c.snowflake {
+ panic("PeerConnection snowflake already exists.")
+ }
+ // Flush the buffer, then enable datachannel.
+ dc.Send(c.buffer.Bytes())
+ log.Println("Flushed", c.buffer.Len(), "bytes")
+ c.buffer.Reset()
+ c.snowflake = dc
+ }
+ dc.OnClose = func() {
+ // Disable the DataChannel as a write destination.
+ // Future writes will go to the buffer until a new DataChannel is available.
+ log.Println("WebRTC: DataChannel.OnClose")
+ // Only reset if this OnClose was triggered remotely.
+ if nil != c.snowflake {
+ c.snowflake = nil
+ c.Reset()
+ }
+ }
+ dc.OnMessage = func(msg []byte) {
+ c.BytesInfo.AddInbound(len(msg))
+ n, err := c.writePipe.Write(msg)
+ if err != nil {
+ // TODO: Maybe shouldn't actually close.
+ log.Println("Error writing to SOCKS pipe")
+ c.writePipe.CloseWithError(err)
+ }
+ if n != len(msg) {
+ panic("short write")
+ }
+ }
+ log.Println("WebRTC: DataChannel created.")
+ return nil
+}
+
+// Block until an offer is available, then send it to either
+// the Broker or signal pipe.
+func (c *webRTCConn) SendOffer() error {
+ select {
+ case offer := <-c.offerChannel:
+ if "" == brokerURL {
+ log.Printf("Please Copy & Paste the following to the peer:")
+ log.Printf("----------------")
+ fmt.Fprintln(logFile, "\n"+offer.Serialize()+"\n")
+ log.Printf("----------------")
+ return nil
+ }
+ // Otherwise, use Broker.
+ go func() {
+ log.Println("Sending offer via BrokerChannel...\nTarget URL: ", brokerURL,
+ "\nFront URL: ", frontDomain)
+ answer, err := c.broker.Negotiate(c.pc.LocalDescription())
+ if nil != err || nil == answer {
+ log.Printf("BrokerChannel error: %s", err)
+ answer = nil
+ }
+ c.answerChannel <- answer
+ }()
+ case err := <-c.errorChannel:
+ c.pc.Close()
+ return err
+ }
+ return nil
+}
+
+func (c *webRTCConn) ReceiveAnswer() {
+ go func() {
+ answer, ok := <-c.answerChannel
+ if !ok || nil == answer {
+ log.Printf("Failed to retrieve answer. Retrying in %d seconds", ReconnectTimeout)
+ <-time.After(time.Second * ReconnectTimeout)
+ c.Reset()
+ return
+ }
+ log.Printf("Received Answer:\n\n%s\n", answer.Sdp)
+ err := c.pc.SetRemoteDescription(answer)
+ if nil != err {
+ c.errorChannel <- err
+ }
+ }()
+}
+
+func (c *webRTCConn) SendData(data []byte) {
+ c.BytesInfo.AddOutbound(len(data))
+ // Buffer the data in case datachannel isn't available yet.
+ if nil == c.snowflake {
+ log.Printf("Buffered %d bytes --> WebRTC", len(data))
+ c.buffer.Write(data)
+ return
+ }
+ c.writeChannel <- data
+}
+
+// Expected in own goroutine.
+func (c *webRTCConn) SendLoop() {
+ log.Println("send loop")
+ for data := range c.writeChannel {
+ // Flush buffer if necessary.
+ for c.buffer.Len() > 0 {
+ c.snowflake.Send(c.buffer.Bytes())
+ log.Println("Flushed", c.buffer.Len(), "bytes")
+ c.buffer.Reset()
+ }
+ c.snowflake.Send(data)
+ }
+}
+
+// WebRTC re-establishment loop. Expected in own goroutine.
+func (c *webRTCConn) ConnectLoop() {
+ for {
+ log.Println("Establishing WebRTC connection...")
+ // TODO: When go-webrtc is more stable, it's possible that a new
+ // PeerConnection won't need to be re-prepared each time.
+ c.PreparePeerConnection()
+ err := c.EstablishDataChannel()
+ if err == nil {
+ c.SendOffer()
+ c.ReceiveAnswer()
+ <-c.reset
+ log.Println(" --- snowflake connection reset ---")
+ } else {
+ log.Println("WebRTC: Could not establish DataChannel.")
+ }
+ }
+}
+
+func (c *webRTCConn) Reset() {
+ go func() {
+ c.reset <- struct{}{} // Attempt to negotiate a new datachannel..
+ log.Println("WebRTC resetting...")
+ }()
+}
More information about the tor-commits
mailing list