Skip to content

Commit eb27f1f

Browse files
prabhakk-mwdpwrussellPrabhakar Kumar
authored
Bugfixes and updates (#4)
* Restricted MATLAB Connector port range to allowed range * Fix embedded login for MathWorks development environments * Add command to install JupyterLab * Set default value of MHLM_CONTEXT to MATLAB_JAVASCRIPT_DESKTOP and MATLAB_JUPYTER when accessed through Juptyer interface Co-authored-by: dpwrussell <drussell@mathworks.com> Co-authored-by: Prabhakar Kumar <prabhakar.kumar@mathworks.com>
1 parent 92f5717 commit eb27f1f

File tree

9 files changed

+79
-16
lines changed

9 files changed

+79
-16
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,12 @@ To install the `jupyter-matlab-proxy` package, follow these steps in your Jupyte
6464
python -m pip install https://github.com/mathworks/jupyter-matlab-proxy/archive/0.1.0.tar.gz
6565
```
6666

67-
If you want to use this integration with JupyterLab®, you must also install `jupyterlab-server-proxy` JupyterLab extension. To install the extension, use the following command:
67+
If you want to use this integration with JupyterLab®, ensure that you have JupyterLab installed on your machine by running the following command:
68+
```bash
69+
python -m pip install jupyterlab
70+
```
71+
72+
You should then install `jupyterlab-server-proxy` JupyterLab extension. To install the extension, use the following command:
6873

6974
``` bash
7075
jupyter labextension install @jupyterlab/server-proxy

gui/src/components/LicensingGatherer/MHLM.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// Copyright 2020 The MathWorks, Inc.
22

3-
import React, { useState, useEffect } from 'react';
3+
import React, { useState, useEffect, useMemo } from 'react';
44
import { useSelector, useDispatch } from 'react-redux';
55
import {
6-
selectLicensingMhlmUsername
6+
selectLicensingMhlmUsername,
7+
selectWsEnv
78
} from '../../selectors';
89
import {
910
fetchSetLicensing
@@ -62,15 +63,21 @@ function initLogin(clientNonce, serverNonce, sourceId) {
6263
loginFrame.postMessage(JSON.stringify(initPayload), "*");
6364
}
6465

65-
// TODO Receive from serverside
66-
// const mhlmLoginHostname = 'login-integ3';
67-
const mhlmLoginHostname = 'login';
68-
const getHostname = () => `https://${mhlmLoginHostname}.mathworks.com`;
69-
7066
function MHLM() {
7167
const dispatch = useDispatch();
7268
const [iFrameLoaded, setIFrameLoaded] = useState(false);
7369
const username = useSelector(selectLicensingMhlmUsername);
70+
const wsEnv = useSelector(selectWsEnv);
71+
const mhlmLoginHostname = useMemo(
72+
() => {
73+
let subdomain = 'login';
74+
if (wsEnv.includes('integ')) {
75+
subdomain = `${subdomain}-${wsEnv}`;
76+
}
77+
return `https://${subdomain}.mathworks.com`;
78+
},
79+
[wsEnv]
80+
);
7481

7582
// Create random sourceId string
7683
const sourceId = (
@@ -83,7 +90,7 @@ function MHLM() {
8390
const handler = event => {
8491

8592
// Only process events that are related to the iframe setup
86-
if (event.origin === getHostname()) {
93+
if (event.origin === mhlmLoginHostname) {
8794
const data = JSON.parse(event.data);
8895

8996
if (data.event === 'nonce') {
@@ -111,7 +118,7 @@ function MHLM() {
111118
return () => {
112119
window.removeEventListener("message", handler);
113120
};
114-
}, [dispatch, sourceId]);
121+
}, [dispatch, sourceId, mhlmLoginHostname]);
115122

116123
useEffect(() => {
117124
if (iFrameLoaded === true) {
@@ -121,7 +128,7 @@ function MHLM() {
121128

122129
const handleIFrameLoaded = () => setIFrameLoaded(true);
123130

124-
const embeddedLoginUrl = `${getHostname()}/embedded-login/v2/login.html`;
131+
const embeddedLoginUrl = `${mhlmLoginHostname}/embedded-login/v2/login.html`;
125132

126133
return (
127134
<div id="MHLM">

gui/src/reducers/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,19 @@ export function matlabVersion(state=null, action) {
9494
}
9595
}
9696

97+
export function wsEnv(state=null, action) {
98+
switch (action.type) {
99+
case RECEIVE_SERVER_STATUS:
100+
case RECEIVE_SET_LICENSING:
101+
case RECEIVE_TERMINATE_INTEGRATION:
102+
case RECEIVE_STOP_MATLAB:
103+
case RECEIVE_START_MATLAB:
104+
return action.status.wsEnv;
105+
default:
106+
return state;
107+
}
108+
}
109+
97110
export function isFetching(state = false, action) {
98111
switch (action.type) {
99112
case REQUEST_SERVER_STATUS:
@@ -209,6 +222,7 @@ export const serverStatus = combineReducers({
209222
licensingInfo,
210223
matlabStatus,
211224
matlabVersion,
225+
wsEnv,
212226
isFetching,
213227
hasFetched,
214228
isSubmitting,

gui/src/selectors/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const selectTutorialHidden = state => state.tutorialHidden;
66
export const selectServerStatus = state => state.serverStatus;
77
export const selectMatlabStatus = state => state.serverStatus.matlabStatus;
88
export const selectMatlabVersion = state => state.serverStatus.matlabVersion;
9+
export const selectWsEnv = state => state.serverStatus.wsEnv;
910
export const selectSubmittingServerStatus = state => state.serverStatus.isSubmitting;
1011
export const selectHasFetchedServerStatus = state => state.serverStatus.hasFetched;
1112
export const selectLicensingInfo = state => state.serverStatus.licensingInfo;

jupyter_matlab_proxy/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def _get_env(port, base_url):
88
"APP_PORT": str(port),
99
"BASE_URL": f"{base_url}matlab",
1010
"APP_HOST": "127.0.0.1",
11+
"MHLM_CONTEXT" : "MATLAB_JUPYTER"
1112
}
1213

1314

jupyter_matlab_proxy/app.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def create_status_response(app, loadUrl=None):
7878
"licensing": marshal_licensing_info(state.licensing),
7979
"loadUrl": loadUrl,
8080
"error": marshal_error(state.error),
81+
"wsEnv": state.settings["ws_env"],
8182
}
8283
)
8384

@@ -390,7 +391,9 @@ def create_app():
390391

391392
def main():
392393
logger.info("Starting MATLAB proxy-app")
394+
393395
app = create_app()
396+
394397
loop = asyncio.get_event_loop()
395398
runner = web.AppRunner(app)
396399
loop.run_until_complete(runner.setup())

jupyter_matlab_proxy/app_state.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pathlib import Path
1111
import tempfile
1212
import socket
13+
import errno
1314
from collections import deque
1415
from .util import mw
1516
from .util.exceptions import (
@@ -290,18 +291,31 @@ def persist_licensing(self):
290291
f.write(json.dumps(config))
291292

292293
def reserve_matlab_port(self):
293-
""" Reserve a free port for MATLAB Embedded Connector. """
294+
""" Reserve a free port for MATLAB Embedded Connector in the allowed range. """
294295

295296
# FIXME Because of https://github.com/http-party/node-http-proxy/issues/1342 the
296297
# node application in development mode always uses port 31515 to bypass the
297298
# reverse proxy. Once this is addressed, remove this special case.
298299
if os.getenv("DEV") == "true":
299300
self.matlab_port = 31515
300301
else:
301-
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
302-
s.bind(("", 0))
303-
self.matlab_port = s.getsockname()[1]
304-
s.close()
302+
303+
# TODO If MATLAB Connector is enhanced to allow any port, then the
304+
# following can be used to get an unused port instead of the for loop and
305+
# try-except.
306+
# s.bind(("", 0))
307+
# self.matlab_port = s.getsockname()[1]
308+
309+
for port in mw.range_matlab_connector_ports():
310+
try:
311+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
312+
s.bind(("", port))
313+
self.matlab_port = port
314+
s.close()
315+
break
316+
except socket.error as e:
317+
if e.errno != errno.EADDRINUSE:
318+
raise e
305319

306320
async def start_matlab(self, restart=False):
307321
""" Start MATLAB. """
@@ -381,6 +395,8 @@ async def start_matlab(self, restart=False):
381395
matlab_env["MW_LOGIN_DISPLAY_NAME"] = self.licensing["display_name"]
382396
matlab_env["MW_LOGIN_USER_ID"] = self.licensing["user_id"]
383397
matlab_env["MW_LOGIN_PROFILE_ID"] = self.licensing["profile_id"]
398+
if os.getenv("MHLM_CONTEXT") is None:
399+
matlab_env["MHLM_CONTEXT"] = "MATLAB_JAVASCRIPT_DESKTOP"
384400

385401
elif self.licensing["type"] == "nlm":
386402
matlab_env["MLM_LICENSE_FILE"] = self.licensing["conn_str"]

jupyter_matlab_proxy/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def get(dev=False):
6464
"matlab_config_file": Path(tempfile.gettempdir())
6565
/ ".matlab"
6666
/ "proxy_app_config.json",
67+
"ws_env": ws_env,
6768
"mwa_api_endpoint": f"https://login{ws_env_suffix}.mathworks.com/authenticationws/service/v4",
6869
"mhlm_api_endpoint": f"https://licensing{ws_env_suffix}.mathworks.com/mls/service/v1/entitlement/list",
6970
"mwa_login": f"https://login{ws_env_suffix}.mathworks.com",
@@ -104,6 +105,7 @@ def get(dev=False):
104105
"matlab_display": ":1",
105106
"nlm_conn_str": os.environ.get("MLM_LICENSE_FILE"),
106107
"matlab_config_file": Path.home() / ".matlab" / "proxy_app_config.json",
108+
"ws_env": ws_env,
107109
"mwa_api_endpoint": f"https://login{ws_env_suffix}.mathworks.com/authenticationws/service/v4",
108110
"mhlm_api_endpoint": f"https://licensing{ws_env_suffix}.mathworks.com/mls/service/v1/entitlement/list",
109111
"mwa_login": f"https://login{ws_env_suffix}.mathworks.com",

jupyter_matlab_proxy/util/mw.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ async def fetch_access_token(mwa_api_endpoint, identity_token, source_id):
100100
}
101101

102102

103+
def range_matlab_connector_ports():
104+
""" Generator of acceptable ports for MATLAB Connector.
105+
106+
Allowed ports conform to the regex: [3,6]1[5-9][1-9][1-9]
107+
"""
108+
109+
for p1 in (3, 6):
110+
p2 = 1
111+
for p3 in range(5, 10):
112+
for p4 in range(1, 10):
113+
for p5 in range(1, 10):
114+
yield int(f"{p1}{p2}{p3}{p4}{p5}")
115+
116+
103117
def parse_nlm_error(logs, conn_str):
104118
nlm_logs = []
105119
start = False

0 commit comments

Comments
 (0)