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

microsoft / botbuilder-js / 4039942987

pending completion
4039942987

Pull #4416

github

GitHub
Merge d01915475 into 736979c95
Pull Request #4416: fix: teams meeting notification interface structure changes

9702 of 12698 branches covered (76.41%)

Branch coverage included in aggregate %.

10 of 10 new or added lines in 2 files covered. (100.0%)

19998 of 22387 relevant lines covered (89.33%)

3606.46 hits per line

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

86.27
/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, request } from 'http';
1✔
10
import { URL } from 'url';
1✔
11
import * as crypto from 'crypto';
1✔
12
import * as WebSocket from 'ws';
1✔
13

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

16
const NONCE_LENGTH = 16;
1✔
17

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

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

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

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

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

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

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

91
        if (url?.hostname) {
10✔
92
            return new Promise<void>((resolve, reject) => {
8✔
93
                const ws = (this.wsSocket = new WebSocket(url));
8✔
94

95
                ws.once('error', ({ message }) => reject(new Error(message)));
8✔
96
                ws.once('open', () => resolve());
8✔
97
            });
98
        }
99

100
        // [hawo]: The following logics are kept here for backward compatibility.
101
        //
102
        //         However, there are no tests to prove the following code works.
103
        //         We tried our best to write a test and figure out how the code would work.
104
        //
105
        //         However, there are obvious mistakes in the code that made it very unlikely to work:
106
        //         - `options.headers.upgrade` must set to `'websocket'`
107
        //         - Second argument of `WebSocket.server.completeUpgrade` should be `{}`, instead of `undefined`
108
        //
109
        //         More readings at https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#client_handshake_request.
110
        this.wsServer = new WebSocket.Server({ noServer: true });
2✔
111
        // Key generation per https://tools.ietf.org/html/rfc6455#section-1.3 (pg. 7)
112
        const wskey = crypto.randomBytes(NONCE_LENGTH).toString('base64');
2✔
113
        const options = {
2✔
114
            port: port,
115
            hostname: serverAddressOrHostName,
116
            headers: {
117
                connection: 'upgrade',
118
                'Sec-WebSocket-Key': wskey,
119
                'Sec-WebSocket-Version': '13',
120
            },
121
        };
122

123
        const req = request(options);
2✔
124
        req.end();
2✔
125
        req.on('upgrade', (res, socket, head): void => {
2✔
126
            // @types/ws does not contain the signature for completeUpgrade
127
            // https://github.com/websockets/ws/blob/0a612364e69fc07624b8010c6873f7766743a8e3/lib/websocket-server.js#L269
128
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
            (this.wsServer as any).completeUpgrade(wskey, undefined, res, socket, head, (websocket): void => {
×
130
                this.wsSocket = websocket;
×
131
            });
132
        });
133

134
        return new Promise<void>((resolve, reject): void => {
2✔
135
            req.on('close', resolve);
2✔
136
            req.on('error', reject);
2✔
137
        });
138
    }
139

140
    /**
141
     * Set the handler for `'message'` events received on the socket.
142
     *
143
     * @param handler The callback to handle the "message" event.
144
     */
145
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
146
    setOnMessageHandler(handler: (x: any) => void): void {
147
        this.wsSocket.on('message', handler);
13✔
148
    }
149

150
    /**
151
     * Close the socket.
152
     *
153
     * @remarks
154
     * Optionally pass in a status code and string explaining why the connection is closing.
155
     * @param code Optional status code to explain why the connection has closed.
156
     * @param data Optional additional data to explain why the connection has closed.
157
     */
158
    close(code?: number, data?: string): void {
159
        this.wsSocket.close(code, data);
10✔
160
    }
161

162
    /**
163
     * Set the callback to call when encountering socket closures.
164
     *
165
     * @param handler The callback to handle the "close" event.
166
     */
167
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
    setOnCloseHandler(handler: (x: any) => void): void {
169
        this.wsSocket.on('close', handler);
13✔
170
    }
171

172
    /**
173
     * Set the callback to call when encountering errors.
174
     *
175
     * @param handler The callback to handle the "error" event.
176
     */
177
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
178
    setOnErrorHandler(handler: (x: any) => void): void {
179
        this.wsSocket.on('error', (error): void => {
13✔
180
            if (error) {
×
181
                handler(error);
×
182
            }
183
        });
184
    }
185
}
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