copy-paste rendezvous works again, but with new interface allowing seamless recovery for the first time

This commit is contained in:
Serene Han 2016-06-12 12:43:24 -07:00
parent fedb124313
commit 02562ba750
3 changed files with 122 additions and 87 deletions

View file

@ -1,15 +1,26 @@
// WebRTC Rendezvous requires the exchange of SessionDescriptions between
// peers. This file contains the domain-fronted HTTP signaling mechanism
// between the client and a desired Broker.
// WebRTC rendezvous requires the exchange of SessionDescriptions between
// peers in order to establish a PeerConnection.
//
// This file contains the two methods currently available to Snowflake:
//
// - Domain-fronted HTTP signaling. The Broker automatically exchange offers
// and answers between this client and some remote WebRTC proxy.
// (This is the recommended default, enabled via the flags in "torrc".)
//
// - Manual copy-paste signaling. User must create a signaling pipe.
// (The flags in torrc-manual allow this)
package main
import (
"bufio"
"bytes"
"errors"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"syscall"
"github.com/keroserene/go-webrtc"
)
@ -100,3 +111,98 @@ func (bc *BrokerChannel) Negotiate(offer *webrtc.SessionDescription) (
return nil, errors.New(BrokerErrorUnexpected)
}
}
// Implements the |Tongue| interface to catch snowflakes, using BrokerChannel.
type WebRTCDialer struct {
*BrokerChannel
webrtcConfig *webrtc.Configuration
}
func NewWebRTCDialer(
broker *BrokerChannel, iceServers IceServerList) *WebRTCDialer {
config := webrtc.NewConfiguration(iceServers...)
return &WebRTCDialer{
BrokerChannel: broker,
webrtcConfig: config,
}
}
// 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.
connection := NewWebRTCConnection(w.webrtcConfig, w.BrokerChannel)
err := connection.Connect()
return connection, err
}
// CopyPasteDialer handles the interaction required to copy-paste the
// offers and answers.
// Implements |Tongue| interface to catch snowflakes manually.
// Supports recovery of connections.
type CopyPasteDialer struct {
webrtcConfig *webrtc.Configuration
signal *os.File
current *webRTCConn
}
func NewCopyPasteDialer(iceServers IceServerList) *CopyPasteDialer {
log.Println("No HTTP signaling detected. Using manual copy-paste signaling.")
log.Println("Waiting for a \"signal\" pipe...")
// This FIFO receives signaling messages.
err := syscall.Mkfifo("signal", 0600)
if err != nil {
if syscall.EEXIST != err.(syscall.Errno) {
log.Fatal(err)
}
}
signalFile, err := os.OpenFile("signal", os.O_RDONLY, 0600)
if nil != err {
log.Fatal(err)
return nil
}
config := webrtc.NewConfiguration(iceServers...)
dialer := &CopyPasteDialer{
webrtcConfig: config,
signal: signalFile,
}
go dialer.readSignals()
return dialer
}
// Initialize a WebRTC connection via manual copy-paste.
func (d *CopyPasteDialer) Catch() (*webRTCConn, error) {
if nil == d.signal {
return nil, errors.New("Cannot copy-paste dial without signal pipe.")
}
connection := NewWebRTCConnection(d.webrtcConfig, nil)
// Must keep track of pending new connection until copy-paste completes.
d.current = connection
// Outputs SDP offer to log, expecting user to copy-paste to the remote Peer.
// Blocks until user pastes back the answer.
err := connection.Connect()
d.current = nil
return connection, err
}
// Manual copy-paste signalling.
func (d *CopyPasteDialer) readSignals() {
defer d.signal.Close()
log.Printf("CopyPasteDialer: reading messages from signal pipe.")
s := bufio.NewScanner(d.signal)
for s.Scan() {
msg := s.Text()
sdp := webrtc.DeserializeSessionDescription(msg)
if sdp == nil {
log.Printf("CopyPasteDialer: ignoring invalid signal message %+q", msg)
continue
}
d.current.answerChannel <- sdp
}
if err := s.Err(); err != nil {
log.Printf("signal FIFO: %s", err)
}
}