diff --git a/README.md b/README.md
index cad2bdba..b5a06ed9 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,12 @@ To install the `jupyter-matlab-proxy` package, follow these steps in your Jupyte
python -m pip install https://github.com/mathworks/jupyter-matlab-proxy/archive/0.1.0.tar.gz
```
-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:
+If you want to use this integration with JupyterLab®, ensure that you have JupyterLab installed on your machine by running the following command:
+```bash
+python -m pip install jupyterlab
+```
+
+You should then install `jupyterlab-server-proxy` JupyterLab extension. To install the extension, use the following command:
``` bash
jupyter labextension install @jupyterlab/server-proxy
diff --git a/gui/src/components/LicensingGatherer/MHLM.js b/gui/src/components/LicensingGatherer/MHLM.js
index d4fbdcc8..8d2a0852 100644
--- a/gui/src/components/LicensingGatherer/MHLM.js
+++ b/gui/src/components/LicensingGatherer/MHLM.js
@@ -1,9 +1,10 @@
// Copyright 2020 The MathWorks, Inc.
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useMemo } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {
- selectLicensingMhlmUsername
+ selectLicensingMhlmUsername,
+ selectWsEnv
} from '../../selectors';
import {
fetchSetLicensing
@@ -62,15 +63,21 @@ function initLogin(clientNonce, serverNonce, sourceId) {
loginFrame.postMessage(JSON.stringify(initPayload), "*");
}
-// TODO Receive from serverside
-// const mhlmLoginHostname = 'login-integ3';
-const mhlmLoginHostname = 'login';
-const getHostname = () => `https://${mhlmLoginHostname}.mathworks.com`;
-
function MHLM() {
const dispatch = useDispatch();
const [iFrameLoaded, setIFrameLoaded] = useState(false);
const username = useSelector(selectLicensingMhlmUsername);
+ const wsEnv = useSelector(selectWsEnv);
+ const mhlmLoginHostname = useMemo(
+ () => {
+ let subdomain = 'login';
+ if (wsEnv.includes('integ')) {
+ subdomain = `${subdomain}-${wsEnv}`;
+ }
+ return `https://${subdomain}.mathworks.com`;
+ },
+ [wsEnv]
+ );
// Create random sourceId string
const sourceId = (
@@ -83,7 +90,7 @@ function MHLM() {
const handler = event => {
// Only process events that are related to the iframe setup
- if (event.origin === getHostname()) {
+ if (event.origin === mhlmLoginHostname) {
const data = JSON.parse(event.data);
if (data.event === 'nonce') {
@@ -111,7 +118,7 @@ function MHLM() {
return () => {
window.removeEventListener("message", handler);
};
- }, [dispatch, sourceId]);
+ }, [dispatch, sourceId, mhlmLoginHostname]);
useEffect(() => {
if (iFrameLoaded === true) {
@@ -121,7 +128,7 @@ function MHLM() {
const handleIFrameLoaded = () => setIFrameLoaded(true);
- const embeddedLoginUrl = `${getHostname()}/embedded-login/v2/login.html`;
+ const embeddedLoginUrl = `${mhlmLoginHostname}/embedded-login/v2/login.html`;
return (
diff --git a/gui/src/reducers/index.js b/gui/src/reducers/index.js
index ef5c218d..8e55ad97 100644
--- a/gui/src/reducers/index.js
+++ b/gui/src/reducers/index.js
@@ -94,6 +94,19 @@ export function matlabVersion(state=null, action) {
}
}
+export function wsEnv(state=null, action) {
+ switch (action.type) {
+ case RECEIVE_SERVER_STATUS:
+ case RECEIVE_SET_LICENSING:
+ case RECEIVE_TERMINATE_INTEGRATION:
+ case RECEIVE_STOP_MATLAB:
+ case RECEIVE_START_MATLAB:
+ return action.status.wsEnv;
+ default:
+ return state;
+ }
+}
+
export function isFetching(state = false, action) {
switch (action.type) {
case REQUEST_SERVER_STATUS:
@@ -209,6 +222,7 @@ export const serverStatus = combineReducers({
licensingInfo,
matlabStatus,
matlabVersion,
+ wsEnv,
isFetching,
hasFetched,
isSubmitting,
diff --git a/gui/src/selectors/index.js b/gui/src/selectors/index.js
index bc1876fa..14eac064 100644
--- a/gui/src/selectors/index.js
+++ b/gui/src/selectors/index.js
@@ -6,6 +6,7 @@ export const selectTutorialHidden = state => state.tutorialHidden;
export const selectServerStatus = state => state.serverStatus;
export const selectMatlabStatus = state => state.serverStatus.matlabStatus;
export const selectMatlabVersion = state => state.serverStatus.matlabVersion;
+export const selectWsEnv = state => state.serverStatus.wsEnv;
export const selectSubmittingServerStatus = state => state.serverStatus.isSubmitting;
export const selectHasFetchedServerStatus = state => state.serverStatus.hasFetched;
export const selectLicensingInfo = state => state.serverStatus.licensingInfo;
diff --git a/jupyter_matlab_proxy/__init__.py b/jupyter_matlab_proxy/__init__.py
index c73aa2e3..aaac661c 100644
--- a/jupyter_matlab_proxy/__init__.py
+++ b/jupyter_matlab_proxy/__init__.py
@@ -8,6 +8,7 @@ def _get_env(port, base_url):
"APP_PORT": str(port),
"BASE_URL": f"{base_url}matlab",
"APP_HOST": "127.0.0.1",
+ "MHLM_CONTEXT" : "MATLAB_JUPYTER"
}
diff --git a/jupyter_matlab_proxy/app.py b/jupyter_matlab_proxy/app.py
index b9b6c98a..084a35f3 100644
--- a/jupyter_matlab_proxy/app.py
+++ b/jupyter_matlab_proxy/app.py
@@ -78,6 +78,7 @@ def create_status_response(app, loadUrl=None):
"licensing": marshal_licensing_info(state.licensing),
"loadUrl": loadUrl,
"error": marshal_error(state.error),
+ "wsEnv": state.settings["ws_env"],
}
)
@@ -390,7 +391,9 @@ def create_app():
def main():
logger.info("Starting MATLAB proxy-app")
+
app = create_app()
+
loop = asyncio.get_event_loop()
runner = web.AppRunner(app)
loop.run_until_complete(runner.setup())
diff --git a/jupyter_matlab_proxy/app_state.py b/jupyter_matlab_proxy/app_state.py
index bcdeaf5f..bcd7bf25 100644
--- a/jupyter_matlab_proxy/app_state.py
+++ b/jupyter_matlab_proxy/app_state.py
@@ -10,6 +10,7 @@
from pathlib import Path
import tempfile
import socket
+import errno
from collections import deque
from .util import mw
from .util.exceptions import (
@@ -290,7 +291,7 @@ def persist_licensing(self):
f.write(json.dumps(config))
def reserve_matlab_port(self):
- """ Reserve a free port for MATLAB Embedded Connector. """
+ """ Reserve a free port for MATLAB Embedded Connector in the allowed range. """
# FIXME Because of https://github.com/http-party/node-http-proxy/issues/1342 the
# node application in development mode always uses port 31515 to bypass the
@@ -298,10 +299,23 @@ def reserve_matlab_port(self):
if os.getenv("DEV") == "true":
self.matlab_port = 31515
else:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.bind(("", 0))
- self.matlab_port = s.getsockname()[1]
- s.close()
+
+ # TODO If MATLAB Connector is enhanced to allow any port, then the
+ # following can be used to get an unused port instead of the for loop and
+ # try-except.
+ # s.bind(("", 0))
+ # self.matlab_port = s.getsockname()[1]
+
+ for port in mw.range_matlab_connector_ports():
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.bind(("", port))
+ self.matlab_port = port
+ s.close()
+ break
+ except socket.error as e:
+ if e.errno != errno.EADDRINUSE:
+ raise e
async def start_matlab(self, restart=False):
""" Start MATLAB. """
@@ -381,6 +395,8 @@ async def start_matlab(self, restart=False):
matlab_env["MW_LOGIN_DISPLAY_NAME"] = self.licensing["display_name"]
matlab_env["MW_LOGIN_USER_ID"] = self.licensing["user_id"]
matlab_env["MW_LOGIN_PROFILE_ID"] = self.licensing["profile_id"]
+ if os.getenv("MHLM_CONTEXT") is None:
+ matlab_env["MHLM_CONTEXT"] = "MATLAB_JAVASCRIPT_DESKTOP"
elif self.licensing["type"] == "nlm":
matlab_env["MLM_LICENSE_FILE"] = self.licensing["conn_str"]
diff --git a/jupyter_matlab_proxy/settings.py b/jupyter_matlab_proxy/settings.py
index 0c4fbd8a..fab4f80e 100644
--- a/jupyter_matlab_proxy/settings.py
+++ b/jupyter_matlab_proxy/settings.py
@@ -64,6 +64,7 @@ def get(dev=False):
"matlab_config_file": Path(tempfile.gettempdir())
/ ".matlab"
/ "proxy_app_config.json",
+ "ws_env": ws_env,
"mwa_api_endpoint": f"https://login{ws_env_suffix}.mathworks.com/authenticationws/service/v4",
"mhlm_api_endpoint": f"https://licensing{ws_env_suffix}.mathworks.com/mls/service/v1/entitlement/list",
"mwa_login": f"https://login{ws_env_suffix}.mathworks.com",
@@ -104,6 +105,7 @@ def get(dev=False):
"matlab_display": ":1",
"nlm_conn_str": os.environ.get("MLM_LICENSE_FILE"),
"matlab_config_file": Path.home() / ".matlab" / "proxy_app_config.json",
+ "ws_env": ws_env,
"mwa_api_endpoint": f"https://login{ws_env_suffix}.mathworks.com/authenticationws/service/v4",
"mhlm_api_endpoint": f"https://licensing{ws_env_suffix}.mathworks.com/mls/service/v1/entitlement/list",
"mwa_login": f"https://login{ws_env_suffix}.mathworks.com",
diff --git a/jupyter_matlab_proxy/util/mw.py b/jupyter_matlab_proxy/util/mw.py
index 2783b058..3cebc763 100644
--- a/jupyter_matlab_proxy/util/mw.py
+++ b/jupyter_matlab_proxy/util/mw.py
@@ -100,6 +100,20 @@ async def fetch_access_token(mwa_api_endpoint, identity_token, source_id):
}
+def range_matlab_connector_ports():
+ """ Generator of acceptable ports for MATLAB Connector.
+
+ Allowed ports conform to the regex: [3,6]1[5-9][1-9][1-9]
+ """
+
+ for p1 in (3, 6):
+ p2 = 1
+ for p3 in range(5, 10):
+ for p4 in range(1, 10):
+ for p5 in range(1, 10):
+ yield int(f"{p1}{p2}{p3}{p4}{p5}")
+
+
def parse_nlm_error(logs, conn_str):
nlm_logs = []
start = False