Kitware / paraviewweb

Web framework for building interactive visualization relying on VTK or ParaView to produce visualization data
http://kitware.github.io/paraviewweb/
BSD 3-Clause "New" or "Revised" License
163 stars 50 forks source link

vtkWSLinkClient + sessionmanagerurl #525

Closed skoudoro closed 3 years ago

skoudoro commented 3 years ago

Hello Paraviewweb Team,

I run locally a docker version of paraviewweb. The instance is running on 127.0.0.1:8081 and I can access all my apps easily and everything works as expected.

However, I have trouble when I try to create an external client that will connect to my local docker server, call the launcher, start an app, and communicate with it via the WebSocket.

My external client run at 127.0.0.1:5500 and you can see below the html and js files. The external client is calling correctly the docker server and the app starts. However, it falls into a strange loop: start the app sessions, kills the app session, starts the app session, kill the app session etc....

Dozen of sessions are created and it never stops. What do I do wrong?

Thank you for the feedback.

HTML FILE

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Webpack App</title>
  <meta name="viewport" content="width=device-width, initial-scale=1"></head>
  <body>
  <script src="ExternalWebClient.js"></script></body>
</html>

External CLIENT

import "./styles.css";

import vtkWSLinkClient from 'vtk.js/Sources/IO/Core/WSLinkClient';
import vtkRemoteView from 'vtk.js/Sources/Rendering/Misc/RemoteView';
import { connectImageStream } from 'vtk.js/Sources/Rendering/Misc/RemoteView';

import SmartConnect from 'wslink/src/SmartConnect';

vtkWSLinkClient.setSmartConnectClass(SmartConnect);

document.body.style.padding = '0';
document.body.style.margin = '0';

const divRenderer = document.createElement('div');
document.body.appendChild(divRenderer);

divRenderer.style.position = 'relative';
divRenderer.style.width = '100vw';
divRenderer.style.height = '100vh';
divRenderer.style.overflow = 'hidden';

// loading
const divLoading = document.createElement('div');
const txtLoading = document.createElement('h2');
txtLoading.innerHTML = "Loading...";
divLoading.classList.add("loader");
txtLoading.classList.add("loadertxt");
divRenderer.appendChild(divLoading);
divRenderer.appendChild(txtLoading);
divRenderer.classList.add("parent");

const view = vtkRemoteView.newInstance({
  rpcWheelEvent: 'viewport.mouse.zoom.wheel',
});
view.setContainer(divRenderer);
// Default of .5 causes 2x size labels on high-DPI screens.
// 1 good for demo, not for production.
if (location.hostname.split('.')[0] === 'localhost') {
    view.setInteractiveRatio(1);
} else {
    // Scaled image compared to the clients view resolution
    view.setInteractiveRatio(.75);
}
view.setInteractiveQuality(50); // jpeg quality

window.addEventListener('resize', view.resize);

const clientToConnect = vtkWSLinkClient.newInstance();

// Error
clientToConnect.onConnectionError((httpReq) => {
  const message =
    (httpReq && httpReq.response && httpReq.response.error) ||
    `Connection error`;
  console.error(message);
  console.log(httpReq);
  txtLoading.innerHTML = message;
});

// Close
clientToConnect.onConnectionClose((httpReq) => {
  const message =
    (httpReq && httpReq.response && httpReq.response.error) ||
    `Connection close`;
  console.error(message);
  console.log(httpReq);
  txtLoading.innerHTML = message;
});

const config = {
  sessionManagerURL: 'http://127.0.0.1:8081/paraview',
  application: 'spheres',
};

// Connect
clientToConnect
  .connect(config)
  .then((validClient) => {
    connectImageStream(validClient.getConnection().getSession());

    const session = validClient.getConnection().getSession();
    view.setSession(session);
    view.setViewId(-1);
    view.render();

    divRenderer.removeChild(divLoading);
    divRenderer.removeChild(txtLoading);
    divRenderer.classList.remove("parent");
  })
  .catch((error) => {
    console.error(error);
    txtLoading.innerHTML = message;
    divRenderer.appendChild(divLoading);
    divRenderer.appendChild(txtLoading);
    divRenderer.classList.add("parent");
  });
jourdain commented 3 years ago

I don't see anything strange. What do you mean by "kill the session" then "start session". It seems that the .connect is only happening once, and therefore only on post request should happen to the launcher.

jourdain commented 3 years ago

One thing though is that I would create the remoteView only after you have your connection available. But that might be fine the way it is.

skoudoro commented 3 years ago

Thank you for your feedback @jourdain.

After debugging step by step, it seems that the error is the following:

Access to XMLHttpRequest at 'http://127.0.0.1:8081/paraview' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

which makes sense since the client is on a different "domain" than the server. How can I fix this problem?

What do you mean by "kill the session" then "start session".

After failing with the error above, it automatically tries to reconnect by starting a new session which is strange to me. I need to create a gif.

jourdain commented 3 years ago

Extend the apache config in docker with the proper CORS policy. I'm sure Google will provide good guidance on that for Apache.

skoudoro commented 3 years ago

The problem was coming from VScode LiveServer. I was using VScode LiveServer to start my external client http://127.0.0.1:5500.

However, the LiveServer refresh the session regularly so it was killing the current session and restarting and a new one every second.

using npx serve resolved the problem. Also, I updated the CORS policy on the docker container as suggested by @jourdain.

Everything works like a charm now.

Thank you for your feedback/help @jourdain.