rendezvous.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // WebRTC rendezvous requires the exchange of SessionDescriptions between
  2. // peers in order to establish a PeerConnection.
  3. //
  4. // This file contains the one method currently available to Snowflake:
  5. //
  6. // - Domain-fronted HTTP signaling. The Broker automatically exchange offers
  7. // and answers between this client and some remote WebRTC proxy.
  8. package lib
  9. import (
  10. "bytes"
  11. "errors"
  12. "io"
  13. "io/ioutil"
  14. "log"
  15. "net/http"
  16. "net/url"
  17. "github.com/keroserene/go-webrtc"
  18. )
  19. const (
  20. BrokerError503 string = "No snowflake proxies currently available."
  21. BrokerError400 string = "You sent an invalid offer in the request."
  22. BrokerErrorUnexpected string = "Unexpected error, no answer."
  23. readLimit = 100000 //Maximum number of bytes to be read from an HTTP response
  24. )
  25. // Signalling Channel to the Broker.
  26. type BrokerChannel struct {
  27. // The Host header to put in the HTTP request (optional and may be
  28. // different from the host name in URL).
  29. Host string
  30. url *url.URL
  31. transport http.RoundTripper // Used to make all requests.
  32. }
  33. // We make a copy of DefaultTransport because we want the default Dial
  34. // and TLSHandshakeTimeout settings. But we want to disable the default
  35. // ProxyFromEnvironment setting.
  36. func CreateBrokerTransport() http.RoundTripper {
  37. transport := http.DefaultTransport.(*http.Transport)
  38. transport.Proxy = nil
  39. return transport
  40. }
  41. // Construct a new BrokerChannel, where:
  42. // |broker| is the full URL of the facilitating program which assigns proxies
  43. // to clients, and |front| is the option fronting domain.
  44. func NewBrokerChannel(broker string, front string, transport http.RoundTripper) *BrokerChannel {
  45. targetURL, err := url.Parse(broker)
  46. if nil != err {
  47. return nil
  48. }
  49. log.Println("Rendezvous using Broker at:", broker)
  50. bc := new(BrokerChannel)
  51. bc.url = targetURL
  52. if "" != front { // Optional front domain.
  53. log.Println("Domain fronting using:", front)
  54. bc.Host = bc.url.Host
  55. bc.url.Host = front
  56. }
  57. bc.transport = transport
  58. return bc
  59. }
  60. func limitedRead(r io.Reader, limit int64) ([]byte, error) {
  61. p, err := ioutil.ReadAll(&io.LimitedReader{R: r, N: limit + 1})
  62. if err != nil {
  63. return p, err
  64. } else if int64(len(p)) == limit+1 {
  65. return p[0:limit], io.ErrUnexpectedEOF
  66. }
  67. return p, err
  68. }
  69. // Roundtrip HTTP POST using WebRTC SessionDescriptions.
  70. //
  71. // Send an SDP offer to the broker, which assigns a proxy and responds
  72. // with an SDP answer from a designated remote WebRTC peer.
  73. func (bc *BrokerChannel) Negotiate(offer *webrtc.SessionDescription) (
  74. *webrtc.SessionDescription, error) {
  75. log.Println("Negotiating via BrokerChannel...\nTarget URL: ",
  76. bc.Host, "\nFront URL: ", bc.url.Host)
  77. data := bytes.NewReader([]byte(offer.Serialize()))
  78. // Suffix with broker's client registration handler.
  79. clientURL := bc.url.ResolveReference(&url.URL{Path: "client"})
  80. request, err := http.NewRequest("POST", clientURL.String(), data)
  81. if nil != err {
  82. return nil, err
  83. }
  84. if "" != bc.Host { // Set true host if necessary.
  85. request.Host = bc.Host
  86. }
  87. resp, err := bc.transport.RoundTrip(request)
  88. if nil != err {
  89. return nil, err
  90. }
  91. defer resp.Body.Close()
  92. log.Printf("BrokerChannel Response:\n%s\n\n", resp.Status)
  93. switch resp.StatusCode {
  94. case http.StatusOK:
  95. body, err := limitedRead(resp.Body, readLimit)
  96. if nil != err {
  97. return nil, err
  98. }
  99. answer := webrtc.DeserializeSessionDescription(string(body))
  100. return answer, nil
  101. case http.StatusServiceUnavailable:
  102. return nil, errors.New(BrokerError503)
  103. case http.StatusBadRequest:
  104. return nil, errors.New(BrokerError400)
  105. default:
  106. return nil, errors.New(BrokerErrorUnexpected)
  107. }
  108. }
  109. // Implements the |Tongue| interface to catch snowflakes, using BrokerChannel.
  110. type WebRTCDialer struct {
  111. *BrokerChannel
  112. webrtcConfig *webrtc.Configuration
  113. }
  114. func NewWebRTCDialer(
  115. broker *BrokerChannel, iceServers IceServerList) *WebRTCDialer {
  116. config := webrtc.NewConfiguration(iceServers...)
  117. if nil == config {
  118. log.Println("Unable to prepare WebRTC configuration.")
  119. return nil
  120. }
  121. return &WebRTCDialer{
  122. BrokerChannel: broker,
  123. webrtcConfig: config,
  124. }
  125. }
  126. // Initialize a WebRTC Connection by signaling through the broker.
  127. func (w WebRTCDialer) Catch() (Snowflake, error) {
  128. if nil == w.BrokerChannel {
  129. return nil, errors.New("Cannot Dial WebRTC without a BrokerChannel.")
  130. }
  131. // TODO: [#3] Fetch ICE server information from Broker.
  132. // TODO: [#18] Consider TURN servers here too.
  133. connection := NewWebRTCPeer(w.webrtcConfig, w.BrokerChannel)
  134. err := connection.Connect()
  135. return connection, err
  136. }