@samitouri / QOS-React-2 / commits / 1cbaf48889

Add SSL support to React devtools standalone (#19191)

Co-authored-by: Brian Vaughn <brian.david.vaughn@gmail.com>

Ittai Baratz committed Jul 6, 2020 at 07:09 UTC 1cbaf48889c6362232d907e57ce6c132744445d0
4 files changed +65 -8
packages/react-devtools-core/README.md
+19
@@ -21,6 +21,7 @@ Be sure to run this function *before* importing e.g. `react`, `react-dom`, `reac
21 The `config` object may contain:
22 * `host: string` (defaults to "localhost") - Websocket will connect to this host.
23 * `port: number` (defaults to `8097`) - Websocket will connect to this port.
24 +* `useHttps: boolean` (defaults to `false`) - Websocked should use a secure protocol (wss).
25 * `websocket: Websocket` - Custom websocket to use. Overrides `host` and `port` settings if provided.
26 * `resolveRNStyle: (style: number) => ?Object` - Used by the React Native style plug-in.
27 * `isAppActive: () => boolean` - If provided, DevTools will poll this method and wait until it returns true before connecting to React.
@@ -38,6 +39,24 @@ require("react-devtools-core/standalone")
39 .startServer(port);
40 ```
41
42 +Renders DevTools interface into a DOM node over SSL using a custom host name (Default is localhost).
43 +
44 +```js
45 +const host = 'dev.server.com';
46 +const options = {
47 + key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
48 + cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
49 +};
50 +
51 +
52 +require("react-devtools-core/standalone")
53 + .setContentDOMNode(document.getElementById("container"))
54 + .setStatusListener(status => {
55 + // This callback is optional...
56 + })
57 + .startServer(port, host, options);
58 +```
59 +
60 Reference the `react-devtools` package for a complete integration example.
61
62 ## Development
packages/react-devtools-core/src/backend.js
+4 -1
@@ -24,6 +24,7 @@ type ConnectOptions = {
24 host?: string,
25 nativeStyleEditorValidAttributes?: $ReadOnlyArray<string>,
26 port?: number,
27 + useHttps?: boolean,
28 resolveRNStyle?: ResolveNativeStyle,
29 isAppActive?: () => boolean,
30 websocket?: ?WebSocket,
@@ -55,12 +56,14 @@ export function connectToDevTools(options: ?ConnectOptions) {
56 const {
57 host = 'localhost',
58 nativeStyleEditorValidAttributes,
59 + useHttps = false,
60 port = 8097,
61 websocket,
62 resolveRNStyle = null,
63 isAppActive = () => true,
64 } = options || {};
65
66 + const protocol = useHttps ? 'wss' : 'ws';
67 let retryTimeoutID: TimeoutID | null = null;
68
69 function scheduleRetry() {
@@ -80,7 +83,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
83 let bridge: BackendBridge | null = null;
84
85 const messageListeners = [];
83 - const uri = 'ws://' + host + ':' + port;
86 + const uri = protocol + '://' + host + ':' + port;
87
88 // If existing websocket is passed, use it.
89 // This is necessary to support our custom integrations.
packages/react-devtools-core/src/standalone.js
+17 -3
@@ -242,8 +242,20 @@ function connectToSocket(socket: WebSocket) {
242 };
243 }
244
245 -function startServer(port?: number = 8097) {
246 - const httpServer = require('http').createServer();
245 +type ServerOptions = {
246 + key?: string,
247 + cert?: string,
248 +};
249 +
250 +function startServer(
251 + port?: number = 8097,
252 + host?: string = 'localhost',
253 + httpsOptions?: ServerOptions,
254 +) {
255 + const useHttps = !!httpsOptions;
256 + const httpServer = useHttps
257 + ? require('https').createServer(httpsOptions)
258 + : require('http').createServer();
259 const server = new Server({server: httpServer});
260 let connected: WebSocket | null = null;
261 server.on('connection', (socket: WebSocket) => {
@@ -298,7 +310,9 @@ function startServer(port?: number = 8097) {
310 '\n;' +
311 backendFile.toString() +
312 '\n;' +
301 - 'ReactDevToolsBackend.connectToDevTools();',
313 + `ReactDevToolsBackend.connectToDevTools({port: ${port}, host: '${host}', useHttps: ${
314 + useHttps ? 'true' : 'false'
315 + }});`,
316 );
317 });
318
packages/react-devtools/app.html
+25 -4
@@ -145,12 +145,33 @@
145 </div>
146 </div>
147 <script>
148 + const fs = require('fs');
149 + let options;
150 + let useHttps = false;
151 +
152 + try {
153 + if (process.env.KEY && process.env.CERT) {
154 + options.key = fs.readFileSync(process.env.KEY);
155 + options.cert = fs.readFileSync(process.env.CERT);
156 + useHttps = true;
157 + }
158 + } catch (err) {
159 + console.error('Failed to process SSL options - ', err);
160 + options = undefined;
161 + }
162 +
163 const {clipboard} = require("electron");
149 - const port = process.env.PORT || 8097;
164 + const host = process.env.HOST || 'localhost';
165 + const protocol = useHttps ? 'https' : 'http';
166 + const port = Number(process.env.PORT || 8097);
167 const localIp = require("ip").address();
168 + const defaultPort = (port === 443 && useHttps) || (port === 80 && !useHttps);
169 + const server = defaultPort ? `${protocol}://${host}` : `${protocol}://${host}:${port}`;
170 + const serverIp = defaultPort ? `${protocol}://${localIp}` : `${protocol}://${localIp}:${port}`;
171 const $ = document.querySelector.bind(document);
172 const $promptDiv = $("#box-content-prompt");
173 const $confirmationDiv = $("#box-content-confirmation");
174 +
175 let timeoutID;
176
177 function selectAllAndCopy(event) {
@@ -184,12 +205,12 @@
205 });
206
207 const $localhost = $("#localhost");
187 - $localhost.innerText = `<script src="http://localhost:${port}"></` + 'script>';
208 + $localhost.innerText = `<script src="${server}"></` + 'script>';
209 $localhost.addEventListener('click', selectAllAndCopy);
210 $localhost.addEventListener('focus', selectAllAndCopy);
211
212 const $byIp = $("#byip");
192 - $byIp.innerText = `<script src="http://${localIp}:${port}"></` + 'script>';
213 + $byIp.innerText = `<script src="${serverIp}"></` + 'script>';
214 $byIp.addEventListener('click', selectAllAndCopy);
215 $byIp.addEventListener('focus', selectAllAndCopy);
216
@@ -211,7 +232,7 @@
232 element.innerText = status;
233 }
234 })
214 - .startServer(port);
235 + .startServer(port, host, options);
236 </script>
237 </body>
238 </html>