All files / src/adapters/ws client.ts

2.22% Statements 1/45
0% Branches 0/10
0% Functions 0/12
2.22% Lines 1/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126              1x                                                                                                                                                                                                                                            
/* eslint-disable security/detect-object-injection */
import Adapter from '../../lib/adapter.js'
import GleeQuoreMessage from '../../lib/message.js'
import ws from 'ws'
import GleeQuoreAuth from '../../lib/wsHttpAuth.js'
import { applyAddressParameters } from '../../lib/util.js'
import Debug from 'debug'
const debug = Debug("glee:ws:client")
interface Client {
  channel: string
  client: ws
  binding?: any
}
 
class WsClientAdapter extends Adapter {
  private clients: Array<Client> = []
 
  name(): string {
    return 'WS adapter'
  }
 
  async connect(): Promise<this> {
    return this._connect()
  }
 
  async send(message: GleeQuoreMessage) {
    return this._send(message)
  }
 
  private async _connect(): Promise<this> {
    const channelsOnThisServer = this.getWsChannels()
 
    debug("connecting to ", this.serverName)
    for (const channelName of channelsOnThisServer) {
      let headers = {}
      const authConfig = await this.app.clientAuthConfig(this.serverName)
      const gleeAuth = new GleeQuoreAuth(
        this.AsyncAPIServer,
        this.parsedAsyncAPI,
        this.serverName,
        authConfig
      )
 
      const protocol = this.AsyncAPIServer.protocol()
      const serverHost = this.AsyncAPIServer.host()
      const channel = this.parsedAsyncAPI.channels().get(channelName)
      const channelAddress = applyAddressParameters(channel)
      let url = new URL(`${protocol}://${serverHost}${channelAddress}`)
      Iif (authConfig) {
        const modedAuth = await gleeAuth.processClientAuth({ url, headers, query: {} })
        headers = modedAuth.headers
        url = modedAuth.url
      }
      this.clients.push({
        channel: channelName,
        client: new ws(url, { headers }),
        binding: this.parsedAsyncAPI.channels().get(channelName).bindings().get('ws'),
      })
    }
 
    for (const { client, channel } of this.clients) {
      client.on('open', () => {
        this.emit('connect', {
          name: this.name(),
          adapter: this,
          connection: client,
          channels: this.channelNames,
        })
      })
 
      client.on('message', (data) => {
        const msg = this._createMessage(channel, data)
        this.emit('message', msg, client)
      })
 
      client.on('error', (err: any) => {
        const errMessage = `Error: Authentication function not found at location auth/${this.serverName}. Expected function 'clientAuth'`
        this.emit('error', new Error(errMessage))
        console.error(err)
      })
    }
    return this
  }
 
  private getWsChannels() {
    const channels = []
    for (const channel of this.channelNames) {
      if (this.parsedAsyncAPI.channels().get(channel).servers().all().length !== 0) { // NOSONAR
        Iif (
          this.parsedAsyncAPI
            .channels().get(channel)
            .servers().get(this.serverName)
        ) {
          channels.push(channel)
        }
      } else {
        channels.push(channel)
      }
    }
 
    return channels
  }
 
  async _send(message: GleeQuoreMessage): Promise<void> {
    const client = this.clients.find(
      (cl) => cl.channel === message.channel
    )?.client
    if (client) {
      client.send(message.payload)
    } else {
      throw new Error(
        'There is no WebSocker connection to send the message yet.'
      )
    }
  }
 
  _createMessage(eventName: string, payload: any): GleeQuoreMessage {
    return new GleeQuoreMessage({
      payload: payload,
      channel: eventName,
    })
  }
}
 
export default WsClientAdapter