Skip to content

Commit 1fb585e

Browse files
Fix Web Debugging and Other Websocket Endpoints with Metro 0.67 (#1560)
* Fix Web Debugging and Other Websocket Endpoints with Metro 0.67 Metro 0.67 included facebook/metro@38a200e which changed the way external applications should interface with it to add new WebSocket endpoints. This materializes as failures when web debugging RN 0.68 apps. This change applies the reccomended fix, of passing a list of non-server endpoints to Metro when starting the server. I verified that web debugging works correctly after making the fix, for the same app where it previously did not work. `ws` is updated as part of the change, along with removing tpe-checker suppressions, of which one was resulting in a runtime error. * Remove session number * chore: stop using stubs for isDebuggerConnected and broadcast Co-authored-by: Michał Pierzchała <[email protected]>
1 parent 4253826 commit 1fb585e

File tree

8 files changed

+76
-121
lines changed

8 files changed

+76
-121
lines changed

packages/cli-plugin-metro/src/commands/start/runServer.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,12 @@ async function runServer(_argv: Array<string>, ctx: Config, args: Args) {
7070
);
7171
}
7272

73-
const {middleware, attachToServer} = createDevServerMiddleware({
73+
const {
74+
middleware,
75+
websocketEndpoints,
76+
messageSocketEndpoint,
77+
eventsSocketEndpoint,
78+
} = createDevServerMiddleware({
7479
host: args.host,
7580
port: metroConfig.server.port,
7681
watchFolders: metroConfig.watchFolders,
@@ -94,14 +99,13 @@ async function runServer(_argv: Array<string>, ctx: Config, args: Args) {
9499
secureCert: args.cert,
95100
secureKey: args.key,
96101
hmrEnabled: true,
102+
websocketEndpoints,
97103
});
98104

99-
const {messageSocket, eventsSocket} = attachToServer(serverInstance);
100-
101-
reportEvent = eventsSocket.reportEvent;
105+
reportEvent = eventsSocketEndpoint.reportEvent;
102106

103107
if (args.interactive) {
104-
enableWatchMode(messageSocket);
108+
enableWatchMode(messageSocketEndpoint);
105109
}
106110

107111
// In Node 8, the default keep-alive for an HTTP connection is 5 seconds. In

packages/cli-server-api/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
"nocache": "^2.1.0",
1616
"pretty-format": "^26.6.2",
1717
"serve-static": "^1.13.1",
18-
"ws": "^1.1.0"
18+
"ws": "^7.5.1"
1919
},
2020
"devDependencies": {
2121
"@types/compression": "^1.0.1",
2222
"@types/connect": "^3.4.33",
2323
"@types/errorhandler": "^0.0.32",
24-
"@types/ws": "^6.0.3"
24+
"@types/ws": "^7.4.7"
2525
},
2626
"files": [
2727
"build",
Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import http, {Server as HttpServer} from 'http';
2-
import {Server as HttpsServer} from 'https';
1+
import http from 'http';
32

43
import compression from 'compression';
54
import connect from 'connect';
@@ -17,9 +16,9 @@ import securityHeadersMiddleware from './securityHeadersMiddleware';
1716
import statusPageMiddleware from './statusPageMiddleware';
1817
import systraceProfileMiddleware from './systraceProfileMiddleware';
1918

20-
import debuggerProxyServer from './websocket/debuggerProxyServer';
21-
import eventsSocketServer from './websocket/eventsSocketServer';
22-
import messageSocketServer from './websocket/messageSocketServer';
19+
import createDebuggerProxyEndpoint from './websocket/createDebuggerProxyEndpoint';
20+
import createMessageSocketEndpoint from './websocket/createMessageSocketEndpoint';
21+
import createEventsSocketEndpoint from './websocket/createEventsSocketEndpoint';
2322

2423
export {devToolsMiddleware};
2524
export {indexPageMiddleware};
@@ -30,19 +29,20 @@ export {securityHeadersMiddleware};
3029
export {statusPageMiddleware};
3130
export {systraceProfileMiddleware};
3231

33-
export {debuggerProxyServer};
34-
export {eventsSocketServer};
35-
export {messageSocketServer};
36-
3732
type MiddlewareOptions = {
3833
host?: string;
3934
watchFolders: ReadonlyArray<string>;
4035
port: number;
4136
};
4237

4338
export function createDevServerMiddleware(options: MiddlewareOptions) {
44-
let isDebuggerConnected = () => false;
45-
let broadcast = (_event: any) => {};
39+
const debuggerProxyEndpoint = createDebuggerProxyEndpoint();
40+
const isDebuggerConnected = debuggerProxyEndpoint.isDebuggerConnected;
41+
42+
const messageSocketEndpoint = createMessageSocketEndpoint();
43+
const broadcast = messageSocketEndpoint.broadcast;
44+
45+
const eventsSocketEndpoint = createEventsSocketEndpoint(broadcast);
4646

4747
const middleware = connect()
4848
.use(securityHeadersMiddleware)
@@ -52,7 +52,7 @@ export function createDevServerMiddleware(options: MiddlewareOptions) {
5252
.use('/debugger-ui', debuggerUIMiddleware())
5353
.use(
5454
'/launch-js-devtools',
55-
devToolsMiddleware(options, () => isDebuggerConnected()),
55+
devToolsMiddleware(options, isDebuggerConnected),
5656
)
5757
.use('/open-stack-frame', openStackFrameInEditorMiddleware(options))
5858
.use('/open-url', openURLMiddleware)
@@ -71,28 +71,14 @@ export function createDevServerMiddleware(options: MiddlewareOptions) {
7171
});
7272

7373
return {
74-
attachToServer(server: HttpServer | HttpsServer) {
75-
const debuggerProxy = debuggerProxyServer.attachToServer(
76-
server,
77-
'/debugger-proxy',
78-
);
79-
const messageSocket = messageSocketServer.attachToServer(
80-
server,
81-
'/message',
82-
);
83-
broadcast = messageSocket.broadcast;
84-
isDebuggerConnected = debuggerProxy.isDebuggerConnected;
85-
const eventsSocket = eventsSocketServer.attachToServer(
86-
server,
87-
'/events',
88-
messageSocket,
89-
);
90-
return {
91-
debuggerProxy,
92-
eventsSocket,
93-
messageSocket,
94-
};
74+
websocketEndpoints: {
75+
'/debugger-proxy': debuggerProxyEndpoint.server,
76+
'/message': messageSocketEndpoint.server,
77+
'/events': eventsSocketEndpoint.server,
9578
},
79+
debuggerProxyEndpoint,
80+
messageSocketEndpoint,
81+
eventsSocketEndpoint,
9682
middleware,
9783
};
9884
}

packages/cli-server-api/src/websocket/debuggerProxyServer.ts renamed to packages/cli-server-api/src/websocket/createDebuggerProxyEndpoint.ts

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99

1010
import ws from 'ws';
1111
import {logger} from '@react-native-community/cli-tools';
12-
import {Server as HttpServer} from 'http';
13-
import {Server as HttpsServer} from 'https';
1412

15-
type Server = HttpServer | HttpsServer;
16-
function attachToServer(server: Server, path: string) {
13+
export default function createDebuggerProxyEndpoint(): {
14+
server: ws.Server;
15+
isDebuggerConnected: () => boolean;
16+
} {
1717
const WebSocketServer = ws.Server;
1818
const wss = new WebSocketServer({
19-
server,
20-
path,
19+
noServer: true,
2120
});
2221

2322
let debuggerSocket: ws | null;
@@ -48,37 +47,33 @@ function attachToServer(server: Server, path: string) {
4847
send(debuggerSocket, JSON.stringify({method: '$disconnected'}));
4948
};
5049

51-
wss.on('connection', (connection: ws) => {
52-
// @ts-ignore current definition of ws does not have upgradeReq type
53-
const {url} = connection.upgradeReq;
50+
wss.on('connection', (socket, request) => {
51+
const {url} = request;
5452

55-
if (url.indexOf('role=debugger') > -1) {
53+
if (url && url.indexOf('role=debugger') > -1) {
5654
if (debuggerSocket) {
57-
connection.close(1011, 'Another debugger is already connected');
55+
socket.close(1011, 'Another debugger is already connected');
5856
return;
5957
}
60-
debuggerSocket = connection;
58+
debuggerSocket = socket;
6159
if (debuggerSocket) {
6260
debuggerSocket.onerror = debuggerSocketCloseHandler;
6361
debuggerSocket.onclose = debuggerSocketCloseHandler;
6462
debuggerSocket.onmessage = ({data}) => send(clientSocket, data);
6563
}
66-
} else if (url.indexOf('role=client') > -1) {
64+
} else if (url && url.indexOf('role=client') > -1) {
6765
if (clientSocket) {
68-
// @ts-ignore not nullable with current type definition of ws
69-
clientSocket.onerror = null;
70-
// @ts-ignore not nullable with current type definition of ws
71-
clientSocket.onclose = null;
72-
// @ts-ignore not nullable with current type definition of ws
73-
clientSocket.onmessage = null;
66+
clientSocket.onerror = () => {};
67+
clientSocket.onclose = () => {};
68+
clientSocket.onmessage = () => {};
7469
clientSocket.close(1011, 'Another client connected');
7570
}
76-
clientSocket = connection;
71+
clientSocket = socket;
7772
clientSocket.onerror = clientSocketCloseHandler;
7873
clientSocket.onclose = clientSocketCloseHandler;
7974
clientSocket.onmessage = ({data}) => send(debuggerSocket, data);
8075
} else {
81-
connection.close(1011, 'Missing role param');
76+
socket.close(1011, 'Missing role param');
8277
}
8378
});
8479

@@ -89,7 +84,3 @@ function attachToServer(server: Server, path: string) {
8984
},
9085
};
9186
}
92-
93-
export default {
94-
attachToServer,
95-
};

packages/cli-server-api/src/websocket/eventsSocketServer.ts renamed to packages/cli-server-api/src/websocket/createEventsSocketEndpoint.ts

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import {Server as WebSocketServer} from 'ws';
22
import {logger} from '@react-native-community/cli-tools';
33
import prettyFormat from 'pretty-format';
4-
import {Server as HttpServer} from 'http';
5-
import {Server as HttpsServer} from 'https';
6-
import messageSocketModule from './messageSocketServer';
74

85
/**
96
* The eventsSocket websocket listens at the 'events/` for websocket
@@ -21,8 +18,6 @@ import messageSocketModule from './messageSocketServer';
2118
* Two useful commands are 'reload' and 'devmenu'.
2219
*/
2320

24-
type Server = HttpServer | HttpsServer;
25-
2621
type Command = {
2722
version: number;
2823
type: 'command';
@@ -105,23 +100,18 @@ function serializeMessage(message: any) {
105100
}
106101
}
107102

108-
type MessageSocket = ReturnType<typeof messageSocketModule.attachToServer>;
109-
110103
/**
111104
* Starts the eventsSocket at the given path
112105
*
113-
* @param server
114-
* @param path typically: 'events/'
115-
* @param messageSocket: webSocket to which all connected RN apps are listening
116106
*/
117-
function attachToServer(
118-
server: Server,
119-
path: string,
120-
messageSocket: MessageSocket,
121-
) {
107+
export default function createEventsSocketEndpoint(
108+
broadcast: (method: string, params?: Record<string, any>) => void,
109+
): {
110+
server: WebSocketServer;
111+
reportEvent: (event: any) => void;
112+
} {
122113
const wss = new WebSocketServer({
123-
server: server,
124-
path: path,
114+
noServer: true,
125115
verifyClient({origin}: {origin: string}) {
126116
// This exposes the full JS logs and enables issuing commands like reload
127117
// so let's make sure only locally running stuff can connect to it
@@ -186,7 +176,7 @@ function attachToServer(
186176
* messageSocket.broadcast (not to be confused with our own broadcast above)
187177
* forwards a command to all connected React Native applications.
188178
*/
189-
messageSocket.broadcast(message.command, message.params);
179+
broadcast(message.command, message.params);
190180
} catch (e) {
191181
logger.error('Failed to forward message to clients: ', e);
192182
}
@@ -197,12 +187,9 @@ function attachToServer(
197187
});
198188

199189
return {
190+
server: wss,
200191
reportEvent: (event: any) => {
201192
broadCastEvent(event);
202193
},
203194
};
204195
}
205-
206-
export default {
207-
attachToServer,
208-
};

packages/cli-server-api/src/websocket/messageSocketServer.ts renamed to packages/cli-server-api/src/websocket/createMessageSocketEndpoint.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import url from 'url';
99
import {Server as WebSocketServer} from 'ws';
1010
import {logger} from '@react-native-community/cli-tools';
11-
import {Server as HttpServer} from 'http';
12-
import {Server as HttpsServer} from 'https';
1311

1412
const PROTOCOL_VERSION = 2;
1513

@@ -70,11 +68,12 @@ function isResponse(message: Message) {
7068
);
7169
}
7270

73-
type Server = HttpServer | HttpsServer;
74-
function attachToServer(server: Server, path: string) {
71+
export default function createMessageSocketEndpoint(): {
72+
server: WebSocketServer;
73+
broadcast: (method: string, params?: Record<string, any>) => void;
74+
} {
7575
const wss = new WebSocketServer({
76-
server,
77-
path,
76+
noServer: true,
7877
});
7978
const clients = new Map();
8079
let nextClientId = 0;
@@ -211,8 +210,7 @@ function attachToServer(server: Server, path: string) {
211210

212211
clients.set(clientId, clientWs);
213212
const onCloseHandler = () => {
214-
// @ts-ignore
215-
clientWs.onmessage = null;
213+
clientWs.onmessage = () => {};
216214
clients.delete(clientId);
217215
};
218216
clientWs.onclose = onCloseHandler;
@@ -245,10 +243,9 @@ function attachToServer(server: Server, path: string) {
245243
});
246244

247245
return {
246+
server: wss,
248247
broadcast: (method: string, params?: Record<string, any>) => {
249248
handleSendBroadcast(null, {method, params});
250249
},
251250
};
252251
}
253-
254-
export default {attachToServer, parseMessage};

packages/debugger-ui/src/ui/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const Page = (window.Page = {
4949
const statusNode = document.getElementById('status');
5050
switch (status.type) {
5151
case 'connected':
52-
statusNode.innerHTML = 'Debugger session #' + status.id + ' active.';
52+
statusNode.innerHTML = 'Debugger session active.';
5353
break;
5454
case 'error':
5555
statusNode.innerHTML =

0 commit comments

Comments
 (0)