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

microsoft / botbuilder-js / 4002045054

pending completion
4002045054

Pull #4413

github

GitHub
Merge 386e52a22 into fe8865250
Pull Request #4413: fix: streaming client should connect under Node.js

9705 of 12700 branches covered (76.42%)

Branch coverage included in aggregate %.

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

19993 of 22380 relevant lines covered (89.33%)

3267.02 hits per line

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

88.1
/libraries/botframework-streaming/src/webSocket/nodeWebSocket.ts
1
/**
2
 * @module botframework-streaming
3
 */
4
/**
5
 * Copyright (c) Microsoft Corporation. All rights reserved.
6
 * Licensed under the MIT License.
7
 */
8

9
import { IncomingMessage } from 'http';
10
import { URL } from 'url';
1✔
11
import * as WebSocket from 'ws';
1✔
12

13
import { INodeIncomingMessage, INodeBuffer, INodeSocket, ISocket } from '../interfaces';
14

15
/**
16
 * An implementation of [ISocket](xref:botframework-streaming.ISocket) to use with a [NodeWebSocketFactory](xref:botframework-streaming.NodeWebSocketFactory) to create a WebSocket server.
17
 */
18
export class NodeWebSocket implements ISocket {
1✔
19
    protected wsServer: WebSocket.Server;
20

21
    /**
22
     * Creates a new [NodeWebSocket](xref:botframework-streaming.NodeWebSocket) instance.
23
     *
24
     * @param wsSocket The `ws` WebSocket instance to build this connection on.
25
     */
26
    constructor(private wsSocket?: WebSocket) {}
24✔
27

28
    /**
29
     * Create and set a `ws` WebSocket with an HTTP Request, Socket and Buffer.
30
     *
31
     * @param req An HTTP Request matching the [INodeIncomingMessage](xref:botframework-streaming.INodeIncomingMessage) interface.
32
     * @param socket A Socket [INodeSocket](xref:botframework-streaming.INodeSocket) interface.
33
     * @param head A Buffer [INodeBuffer](xref:botframework-streaming.INodeBuffer) interface.
34
     * @returns A Promise that resolves after the WebSocket upgrade has been handled, otherwise rejects with a thrown error.
35
     */
36
    async create(req: INodeIncomingMessage, socket: INodeSocket, head: INodeBuffer): Promise<void> {
37
        this.wsServer = new WebSocket.Server({ noServer: true });
6✔
38
        return new Promise<void>((resolve, reject) => {
6✔
39
            try {
6✔
40
                this.wsServer.handleUpgrade(
6✔
41
                    req as IncomingMessage,
42
                    socket as INodeSocket,
43
                    head as INodeBuffer,
44
                    (websocket) => {
45
                        this.wsSocket = websocket;
6✔
46
                        resolve();
6✔
47
                    }
48
                );
49
            } catch (err) {
50
                reject(err);
×
51
            }
52
        });
53
    }
54

55
    /**
56
     * Indicates if the 'ws' WebSocket is currently connected and ready to send messages.
57
     *
58
     * @returns `true` if the underlying websocket is ready and availble to send messages, otherwise `false`.
59
     */
60
    get isConnected(): boolean {
61
        return this.wsSocket && this.wsSocket.readyState === WebSocket.OPEN;
7✔
62
    }
63

64
    /**
65
     * Writes a buffer to the socket and sends it.
66
     *
67
     * @param buffer The buffer of data to send across the connection.
68
     */
69
    write(buffer: INodeBuffer): void {
70
        this.wsSocket.send(buffer);
6✔
71
    }
72

73
    /**
74
     * Connects to the supporting socket using WebSocket protocol.
75
     *
76
     * @param serverAddressOrHostName The host name or URL the server is listening on.
77
     * @param port If `serverAddressOrHostName` is a host name, the port the server is listening on, defaults to 8082. Otherwise, this argument is ignored.
78
     * @returns A Promise that resolves when the websocket connection is closed, or rejects on an error.
79
     */
80
    async connect(serverAddressOrHostName: string, port = 8082): Promise<void> {
11✔
81
        return new Promise<void>((resolve, reject) => {
12✔
82
            let url: URL;
83

84
            try {
12✔
85
                url = new URL(serverAddressOrHostName);
12✔
86
                // eslint-disable-next-line no-empty
87
            } catch (_error) {}
88

89
            if (!url?.hostname) {
12✔
90
                url = new URL('ws://.');
4✔
91
                // `serverAddressOrHostName` is a required argument.
92
                // However, our existing tests assume `serverAddressOrHostName` is optional.
93
                // We are keeping the same API signatures, while internally coalesced to '127.0.0.1'.
94
                url.hostname = serverAddressOrHostName || '127.0.0.1';
4✔
95
                url.port = port + '';
4✔
96
            }
97

98
            const ws = (this.wsSocket = new WebSocket(url));
12✔
99

100
            ws.once('error', ({ message }) => reject(new Error(message)));
12✔
101
            ws.once('open', () => resolve());
12✔
102
        });
103
    }
104

105
    /**
106
     * Set the handler for `'message'` events received on the socket.
107
     *
108
     * @param handler The callback to handle the "message" event.
109
     */
110
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
    setOnMessageHandler(handler: (x: any) => void): void {
112
        this.wsSocket.on('message', handler);
15✔
113
    }
114

115
    /**
116
     * Close the socket.
117
     *
118
     * @remarks
119
     * Optionally pass in a status code and string explaining why the connection is closing.
120
     * @param code Optional status code to explain why the connection has closed.
121
     * @param data Optional additional data to explain why the connection has closed.
122
     */
123
    close(code?: number, data?: string): void {
124
        this.wsSocket.close(code, data);
10✔
125
    }
126

127
    /**
128
     * Set the callback to call when encountering socket closures.
129
     *
130
     * @param handler The callback to handle the "close" event.
131
     */
132
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
133
    setOnCloseHandler(handler: (x: any) => void): void {
134
        this.wsSocket.on('close', handler);
15✔
135
    }
136

137
    /**
138
     * Set the callback to call when encountering errors.
139
     *
140
     * @param handler The callback to handle the "error" event.
141
     */
142
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
143
    setOnErrorHandler(handler: (x: any) => void): void {
144
        this.wsSocket.on('error', (error): void => {
15✔
145
            if (error) {
×
146
                handler(error);
×
147
            }
148
        });
149
    }
150
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc