• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cameri / nostream / 24229855811

10 Apr 2026 06:31AM UTC coverage: 48.639% (-0.2%) from 48.803%
24229855811

Pull #416

github

web-flow
Merge a0029bd7b into b17223506
Pull Request #416: Fix: nip01 replaceable tiebreaker

452 of 1029 branches covered (43.93%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

6 existing lines in 2 files now uncovered.

1317 of 2608 relevant lines covered (50.5%)

9.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.16
/src/adapters/web-socket-server-adapter.ts
1
import { IncomingMessage, Server } from 'http'
2
import WebSocket, { OPEN, WebSocketServer } from 'ws'
2✔
3
import { propEq } from 'ramda'
2✔
4

5
import { IWebSocketAdapter, IWebSocketServerAdapter } from '../@types/adapters'
6
import { WebSocketAdapterEvent, WebSocketServerAdapterEvent } from '../constants/adapter'
2✔
7
import { createLogger } from '../factories/logger-factory'
2✔
8
import { Event } from '../@types/event'
9
import { Factory } from '../@types/base'
10
import { getRemoteAddress } from '../utils/http'
2✔
11
import { isRateLimited } from '../handlers/request-handlers/rate-limiter-middleware'
2✔
12
import { Settings } from '../@types/settings'
13
import { WebServerAdapter } from './web-server-adapter'
2✔
14

15
const debug = createLogger('web-socket-server-adapter')
2✔
16

17
const WSS_CLIENT_HEALTH_PROBE_INTERVAL = 120000
2✔
18

19
export class WebSocketServerAdapter extends WebServerAdapter implements IWebSocketServerAdapter {
2✔
20
  private webSocketsAdapters: WeakMap<WebSocket, IWebSocketAdapter>
21

22
  private heartbeatInterval: NodeJS.Timeout
23

24
  public constructor(
25
    webServer: Server,
26
    private readonly webSocketServer: WebSocketServer,
2✔
27
    private readonly createWebSocketAdapter: Factory<
2✔
28
      IWebSocketAdapter,
29
      [WebSocket, IncomingMessage, IWebSocketServerAdapter]
30
    >,
31
    private readonly settings: () => Settings,
2✔
32
  ) {
33
    debug('created')
2✔
34
    super(webServer)
2✔
35

36
    this.webSocketsAdapters = new WeakMap()
2✔
37

38
    this
2✔
39
      .on(WebSocketServerAdapterEvent.Broadcast, this.onBroadcast.bind(this))
40

41
    this.webSocketServer
2✔
42
      .on(WebSocketServerAdapterEvent.Connection, this.onConnection.bind(this))
43
      .on('error', (error) => {
44
        debug('error: %o', error)
×
45
      })
46
    this.heartbeatInterval = setInterval(this.onHeartbeat.bind(this), WSS_CLIENT_HEALTH_PROBE_INTERVAL)
2✔
47
  }
48

49
  public close(callback?: () => void): void {
50
    super.close(() => {
2✔
51
      debug('closing')
2✔
52
      clearInterval(this.heartbeatInterval)
2✔
53
      this.webSocketServer.clients.forEach((webSocket: WebSocket) => {
2✔
UNCOV
54
        const webSocketAdapter = this.webSocketsAdapters.get(webSocket)
×
UNCOV
55
        if (webSocketAdapter) {
×
UNCOV
56
          debug('terminating client %s: %s', webSocketAdapter.getClientId(), webSocketAdapter.getClientAddress())
×
57
        }
UNCOV
58
        webSocket.terminate()
×
59
      })
60
      debug('closing web socket server')
2✔
61
      this.webSocketServer.close(() => {
2✔
62
        this.webSocketServer.removeAllListeners()
2✔
63
        if (typeof callback !== 'undefined') {
2✔
64
          callback()
1✔
65
        }
66
        debug('closed')
2✔
67
      })
68
    })
69
    this.removeAllListeners()
2✔
70
  }
71

72
  private onBroadcast(event: Event) {
73
    this.webSocketServer.clients.forEach((webSocket: WebSocket) => {
52✔
74
      if (!propEq('readyState', OPEN)(webSocket)) {
80!
75
        return
×
76
      }
77
      const webSocketAdapter = this.webSocketsAdapters.get(webSocket) as IWebSocketAdapter
80✔
78
      if (!webSocketAdapter) {
80!
79
        return
×
80
      }
81
      webSocketAdapter.emit(WebSocketAdapterEvent.Event, event)
80✔
82
    })
83
  }
84

85
  public getConnectedClients(): number {
86
    return Array.from(this.webSocketServer.clients).filter(propEq('readyState', OPEN)).length
×
87
  }
88

89
  private async onConnection(client: WebSocket, req: IncomingMessage) {
90
    const currentSettings = this.settings()
46✔
91
    const remoteAddress = getRemoteAddress(req, currentSettings)
46✔
92

93
    debug('client %s connected: %o', remoteAddress, req.headers)
46✔
94

95
    if (await isRateLimited(remoteAddress, currentSettings)) {
46!
96
      debug('client %s terminated: rate-limited', remoteAddress)
×
97
      client.terminate()
×
98
      return
×
99
    }
100

101
    this.webSocketsAdapters.set(client, this.createWebSocketAdapter([client, req, this]))
46✔
102
  }
103

104
  private onHeartbeat() {
105
    this.webSocketServer.clients.forEach((webSocket) => {
×
106
      const webSocketAdapter = this.webSocketsAdapters.get(webSocket) as IWebSocketAdapter
×
107
      if (webSocketAdapter) {
×
108
        webSocketAdapter.emit(WebSocketAdapterEvent.Heartbeat)
×
109
      }
110
    })
111
  }
112
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc