Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 17 additions & 10 deletions gui/src/components/LicensingGatherer/MHLM.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 = (
Expand All @@ -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') {
Expand Down Expand Up @@ -111,7 +118,7 @@ function MHLM() {
return () => {
window.removeEventListener("message", handler);
};
}, [dispatch, sourceId]);
}, [dispatch, sourceId, mhlmLoginHostname]);

useEffect(() => {
if (iFrameLoaded === true) {
Expand All @@ -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 (
<div id="MHLM">
Expand Down
14 changes: 14 additions & 0 deletions gui/src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -209,6 +222,7 @@ export const serverStatus = combineReducers({
licensingInfo,
matlabStatus,
matlabVersion,
wsEnv,
isFetching,
hasFetched,
isSubmitting,
Expand Down
1 change: 1 addition & 0 deletions gui/src/selectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions jupyter_matlab_proxy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}


Expand Down
3 changes: 3 additions & 0 deletions jupyter_matlab_proxy/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
}
)

Expand Down Expand Up @@ -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())
Expand Down
26 changes: 21 additions & 5 deletions jupyter_matlab_proxy/app_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -290,18 +291,31 @@ 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
# reverse proxy. Once this is addressed, remove this special case.
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. """
Expand Down Expand Up @@ -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"]
Expand Down
2 changes: 2 additions & 0 deletions jupyter_matlab_proxy/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
14 changes: 14 additions & 0 deletions jupyter_matlab_proxy/util/mw.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down