Advertisement
Guest User

Untitled

a guest
Jan 21st, 2019
74
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. export type SocketOpenFunction     = (event: Event)         => void
  2. export type SocketMessageFunction  = (event: MessageEvent)  => void
  3. export type SocketErrorFunction    = (event: RTCErrorEvent) => void
  4. export type SocketCloseFunction    = (event: Event)         => void
  5.  
  6. const into = (func: Function) => func()
  7.  
  8. export class SocketNotOpenError extends Error {
  9.   constructor() {
  10.     super(`Cannot send to a socket that's not 'open'. Wait for 'open' event.`)
  11.  }
  12. }
  13.  
  14. export class SocketTimeoutError extends Error {
  15.  constructor() {
  16.    super('Socket failed to connect due to timeout.')
  17.  }
  18. }
  19.  
  20. export class HandshakeError extends Error {
  21.  constructor() {
  22.    super('The remote host rejected this socket.')
  23.  }
  24. }
  25.  
  26. export class Socket extends Events {
  27.  private channel!: RTCDataChannel
  28.  public  local!:   string
  29.  public  remote!:  string
  30.  
  31.  // note: we may want to consider buffering messages when there
  32.  // are no subscribers, of perhaps subscribe on the underlying
  33.  // channel on first call to either once or on.
  34.  public once(event: 'open',    func: SocketOpenFunction):    void
  35.  public once(event: 'message', func: SocketMessageFunction): void
  36.  public once(event: 'error',   func: SocketErrorFunction):   void
  37.  public once(event: 'close',   func: SocketCloseFunction):   void
  38.  public once(event: string,    func: EventHandler) {
  39.    super.once(event, func)
  40.  }
  41.  
  42.  public on(event: 'open',     func: SocketOpenFunction): void
  43.  public on(event: 'message',  func: SocketMessageFunction): void
  44.  public on(event: 'error',    func: SocketErrorFunction): void
  45.  public on(event: 'close',    func: SocketCloseFunction): void
  46.  public on(event: string, func: EventHandler) {
  47.    super.on(event, func)
  48.  }
  49.  
  50.  /** Sends a message to this socket. */
  51.  public send(message: string | Blob | ArrayBuffer | ArrayBufferView) {
  52.    if(!this.channel) {
  53.      throw new SocketNotOpenError()
  54.    }
  55.    this.channel!.send(message as any)
  56.  }
  57.  
  58.  /** Closes this socket. */
  59.  public close() {
  60.    this.channel.close()
  61.  }
  62.  
  63.  /** Creates a socket from a datachannel sent from the smoke driver. */
  64.  public static fromChannel(channel: RTCDataChannel, local: string, remote: string): Socket {
  65.    const socket   = new Socket()
  66.    socket.remote  = remote
  67.    socket.local   = local
  68.    socket.channel = channel
  69.    socket.channel.addEventListener('open',    event => socket.emit('open',    event))
  70.    socket.channel.addEventListener('message', event => socket.emit('message', event))
  71.    socket.channel.addEventListener('error',   event => socket.emit('error',   event))
  72.    socket.channel.addEventListener('close',   event => socket.emit('close',   event))
  73.    return socket
  74.  }
  75.  
  76.  /**
  77.   * Creates an outbound socket to the remote endpoint. This function will return a socket
  78.   * immediately to the caller, but will asynchronously connect and handshake with the server.
  79.   * The caller is expected to listen on this sockets events prior to interacting with
  80.   * the socket.
  81.   */
  82.  public static createSocket(driver: Driver, remote: string, port: string): Socket {
  83.    const socket  = new Socket()
  84.    into(async () => {
  85.  
  86.      // Resolve local and remote endpoints for this socket and rewrite local
  87.      // and remote endpoints to 'loopback' if on localhost. The rewrite to loopback
  88.      // is required for locating the appropriate localhost peer 'loopback:1'
  89.      const local    = await driver.address()
  90.      socket.remote  = (remote === 'localhost' || remote === local) ? 'loopback:1' : remote
  91.      socket.local   = local
  92.      const peer     = await driver.getPeer(socket.remote)
  93.      socket.channel = peer.connection.createDataChannel(port)
  94.  
  95.      // We need to handle timeouts to the remote endpoint. Timeouts may occur
  96.      // due to the remote host being unavailable, or due to network problems.
  97.      // We set a reasonable connection timeout of 8 seconds.
  98.      let waiting = true
  99.      setTimeout(() => {
  100.        if(waiting) {
  101.          waiting = false
  102.          socket.emit('error', new SocketTimeoutError())
  103.          socket.emit('close')
  104.          socket.channel.close()
  105.        }
  106.      }, 8000)
  107.  
  108.      // Wait for the channel to open then wait on the first message over
  109.      // the channel to be the handshake 'sync' message from the server.
  110.      // A server that fails to respond with a 'sync' is interpretted as
  111.      // a rejection from that server. On sync, we respond to the server
  112.      // with a 'sync' indicating that the client is responding.
  113.      socket.channel.addEventListener('open', event => {
  114.        if(waiting) {
  115.          waiting = false
  116.          const onHandshake = (message: MessageEvent) => {
  117.            socket.channel.removeEventListener('message', onHandshake)
  118.            if(message.data !== 'sync') {
  119.              socket.emit('error', new HandshakeError())
  120.              socket.emit('close')
  121.              socket.channel.close()
  122.              return
  123.            }
  124.            socket.channel.send('sync')
  125.            socket.emit('open', event)
  126.            socket.channel.addEventListener('message', event => socket.emit('message', event))
  127.            socket.channel.addEventListener('error',   event => socket.emit('error',   event))
  128.            socket.channel.addEventListener('close',   event => socket.emit('close',   event))
  129.          }
  130.          socket.channel.addEventListener('message', onHandshake)
  131.        }
  132.      })
  133.    })
  134.    return socket
  135.  }
  136. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement