OHIF / Viewers

OHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages
https://docs.ohif.org/
MIT License
3.1k stars 3.28k forks source link

How can I turn off CORS and go directly from the OHIF container to a dicom-web destination in Docker ohif/viewer? #1315

Closed cutecycle closed 4 years ago

cutecycle commented 4 years ago

Description

I have an older meteor-based configuration of the OHIF viewer that made requests to an Orthanc dicom-web endpoint over the loopback interface.

I'm trying to recreate this in docker-compose, but I'm seeing errors in-console from the client trying to make requests to the orthanc dicom-web endpoint and failing because of either CORS policy or a non-existent hostname.

image

Is going directly to DICOMweb from the ohif viewer server still supported? Should I edit the nginx configuration in the container for CORS and just do dicomweb on the client side?

dannyrb commented 4 years ago

👋 Hello @cutecycle. I'm not sure I 100% follow. There are two ways to mitigate CORS issues:

  1. Make sure the server that exposes the DICOMWeb endpoints has the appropriate CORS Headers
  1. Host the client at the same domain/port as the DICOMWeb endpoints (no "cross origin" request, so everything just works)

You can see a few examples of our own usage of these. If anything seems incorrect, let me know. They may need cleaned up:

  1. The default server the OHIF Viewer is configured to hit for test/development sets very permissive CORS headers
  2. When using Orthanc as a PACS, it's not supposed to be exposed to open web traffic. One option is to use nginx to handle web requests, which provides a convenient place to set CORS headers. We have an nginx configuration file lying around that should do that
  3. In our nginx + Image Archive recipe, we show how to configure and deploy the web viewer and orthanc. We host them at the same domain, so we don't need to set CORS headers. The nginx config for that recipe is hiding here
cutecycle commented 4 years ago

Hello, and Thank you.

Apologies for the misconception; I was going against the Data Source documentation.

I've set the containers to host networking mode, and set all internal communication between OHIF and orthanc to 127.0.0.1:

  servers: {
    dicomWeb: [
      {
        name: "orthanc",
        wadoUriRoot: "http://127.0.0.1:8042/wado",
        qidoRoot: "http://127.0.0.1:8042/dicom-web",
        wadoRoot: "http://127.0.0.1:8042/dicom-web",
        qidoSupportsIncludeField: false,
        imageRendering: "wadors",
        thumbnailRendering: "wadors",
        enableStudyLazyLoad: true,
      },
    ],
  },
version: '3.6'
services:
  mongo:
    image: "mongo:latest"
    network_mode: host
    restart: always
  orthanc:
    image: jodogne/orthanc-plugins
    restart: always
    command: ["--trace","/etc/orthanc/orthanc.json" ]
    network_mode: host
    volumes:
       - ./configs/ohif-config/orthanc.json:/etc/orthanc/orthanc.json
  viewer:
    image: ohif/viewer:v2.2.0.5809
    restart: always
    network_mode: host
    ports:
      - "80:80"
      - "3000:3000"
    environment:
      - MONGO_URL=mongodb://127.0.0.1:27017/ohif
      - PUBLIC_URL=http://34.220.35.158/
    extra_hosts:
      - "pacsip:127.0.0.1"
      - "pacsIP:127.0.0.1"
    volumes:
      - ./configs/ohif-config/ohif.json:/app/app.json
      - ./configs/ohif-config/ohif.js:/usr/share/nginx/html/app-config.js

When you say "client on the same origin," do you mean the browser as well? I thought if I had both OHIF and orthanc on 127.0.0.1, the ohif/viewers container itself would GET 127.0.0.1:8042/dicom-web, which should be allowed.

But it looks like what's happening in newer versions is now the browser on an end-user machine is making an AJAX request to the configured DICOMweb endpoint URL, rather than the Viewers server.

Meteor/~0.4.0: OHIF backend makes the request on loopback

ESC[34mI20190826-18:46:46.732(0)? ESC[39m  dicomWeb: { endpoints: [ [Object] ] },
ESC[34mI20190826-18:46:46.732(0)? ESC[39m  dimse: { host: '127.0.0.1', hostAE: 'IMAGEMOVER_SCP', port: 11112 },
ESC[34mI20190826-18:46:46.732(0)? ESC[39m  public: {} }
ESC[34mI20190826-19:00:22.756(0)? ESC[39mhttp://127.0.0.1:8042/dicom-web/studies?limit=20&includefield=00081030%2C00080060
ESC[34mI20190826-19:00:22.764(0)? ESC[39mhttp://127.0.0.1:8042/dicom-web/studies?limit=20&includefield=00081030%2C00080060: 8ms
ESC[34mI20190826-19:35:59.354(0)? ESC[39mhttp://127.0.0.1:8042/dicom-web/studies?limit=20&includefield=00081030%2C00080060

React/v2+: OHIF frontend makes the request on XHR

image

Would you say it's best for me to add an nginx container that proxies to the orthanc container and overwrites the existing CORS headers, so that an end-user machine can hit that endpoint from the XHR?

dannyrb commented 4 years ago

I was going against the Data Source documentation

The Data Source documentation is specific to local development using WebPack's built in "dev server". The development configuration supports two environment variables that allow WebPack to proxy request to the PACS to avoid CORS issues.

For production usage, or any usage where the OHIF Viewer isn't served by WebPack, we need to switch up our strategy.

When you say "client on the same origin," do you mean the browser as well? I thought if I had both OHIF and orthanc on 127.0.0.1, the ohif/viewers container itself would GET 127.0.0.1:8042/dicom-web, which should be allowed.

The difference in port is what trips CORS. CORS cares about: domain, protocol, port

React/v2+: OHIF frontend makes the request on XHR

v2+ no longer has a "back end", hence the change.

Would you say it's best for me to add an nginx container that proxies to the orthanc container and overwrites the existing CORS headers, so that an end-user machine can hit that endpoint from the XHR?

That would probably be the best route. For local development, you can cheat and use WebPack dev server's proxying. For production/hosted, you'll likely need to proxy your PACS and set appropriate headers.

cutecycle commented 4 years ago

Thanks so much, I'm up and running a test system!

Should I close or do maintainers close?

dannyrb commented 4 years ago

🎉 Great to hear, @cutecycle! I'll close, but you're more than welcome to for future issues. Please don't hesitate to reach out if you have additional questions, suggestions, PRs, etc.

ranasrule commented 1 year ago

Thanks so much, I'm up and running a test system!

Should I close or do maintainers close?

@cutecycle would you mind sharing your nginx config with me?

@dannyrb is there an nginx config example for OHIF v3 and Orthanc ?

ranasrule commented 1 year ago

I have been pulling my hair out over the last three days trying to figure this out without any luck.....

Describe the Bug

OHIF V3 with Orthanc MPR functionality works fine on localhost but Error: Uncaught (in promise) Error: SharedArrayBuffer is NOT supported in your browser see https://developer.chrome.com/blog/enabling-shared-array-buffer/ error occurs when accessing over that internet

Steps to Reproduce:

  1. open any study on localhost and switch to MPR view. It will work find
  2. open any study over the internet and switch to MPR view. It will generate Error: Uncaught (in promise) Error: SharedArrayBuffer is NOT supported in your browser see https://developer.chrome.com/blog/enabling-shared-array-buffer/ error in browser console and MPR will not generate.

my nginx config file is below:

worker_processes 1;

events { worker_connections 1024; }

http {

    upstream orthanc-server {
        server 127.0.0.1:8099;
    }

    server {
        listen [::]:8099 default_server;
        listen 8099;

        # CORS Magic
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow_Credentials' 'true';
        add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
        add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';

        location / {

            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow_Credentials' 'true';
                add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
                add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain charset=UTF-8';
                add_header 'Content-Length' 0;
                return 204;
            }
            try_files $uri $uri/ /index.html;
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Origin' '*';
            add_header Cross-Origin-Opener-Policy same-origin;
            add_header Cross-Origin-Embedder-Policy require-corp;
            add_header Cross-Origin-Resource-Policy same-origin;
                  }

        location /orthanc/{

            proxy_pass http://localhost:8042;
            proxy_set_header Authorization "Basic YWxuYXNhcnBhY3MbyybyybNhcnBhY3M1Nzc=";
            proxy_set_header Host $Host:8099/orthanc/;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $server_name;

            # CORS Magic
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow_Credentials' 'true';
            add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
            add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
            rewrite /orthanc(.*) $1 break;
                           }              
    }
}

My app-config.js is below

window.config = {
    routerBasename: "/",
    extensions: [],
    modes: [],
    customizationService: {},
    showStudyList: !0,
    maxNumberOfWebWorkers: 3,
    omitQuotationForMultipartRequest: !0,
    showWarningMessageForCrossOrigin: !0,
    showCPUFallbackMessage: !0,
    showLoadingIndicator: !0,
    maxNumRequests: {
        interaction: 100,
        thumbnail: 75,
        prefetch: 10
    },
    dataSources: [{
        friendlyName: "dcmjs DICOMWeb Server",
        namespace: "@ohif/extension-default.dataSourcesModule.dicomweb",
        sourceName: "dicomweb",
        configuration: {
            name: "aws",
            wadoUriRoot: '/orthanc/wado',
            qidoRoot: '/orthanc/dicom-web',
            wadoRoot: '/orthanc/dicom-web',
            qidoSupportsIncludeField: !1,
            supportsReject: !1,
            imageRendering: "wadors",
            thumbnailRendering: "wadors",
            enableStudyLazyLoad: !1,
            supportsFuzzyMatching: !1,
            supportsWildcard: !0,
            staticWado: !0,
            singlepart: "bulkdata,video,pdf"
        }
    }, {
        friendlyName: "dicom json",
        namespace: "@ohif/extension-default.dataSourcesModule.dicomjson",
        sourceName: "dicomjson",
        configuration: {
            name: "json"
        }
    }, {
        friendlyName: "dicom local",
        namespace: "@ohif/extension-default.dataSourcesModule.dicomlocal",
        sourceName: "dicomlocal",
        configuration: {}
    }],
    httpErrorHandler: e => {
        console.warn(e.status), console.warn("test, navigate to https://ohif.org/")
    },
    defaultDataSourceName: "dicomweb",

};

The current behavior

OHIF V3 with Orthanc MPR functionality works fine on localhost but Error: Uncaught (in promise) Error: SharedArrayBuffer is NOT supported in your browser see https://developer.chrome.com/blog/enabling-shared-array-buffer/ error occurs when accessing over that internet

The expected behavior

It should work over the internet as well as localhost

I tried using the example nginx config file here as a guide >>> https://github.com/OHIF/Viewers/blob/master/.docker/Nginx-Orthanc/config/nginx.conf but no luck.

cutecycle commented 1 year ago

Thanks so much, I'm up and running a test system! Should I close or do maintainers close?

@cutecycle would you mind sharing your nginx config with me?

@dannyrb is there an nginx config example for OHIF v3 and Orthanc ?

So sorry, I've been out of the OHIF world for a long time :(

vasiliykim98 commented 11 months ago

I have been pulling my hair out over the last three days trying to figure this out without any luck.....

Describe the Bug

OHIF V3 with Orthanc MPR functionality works fine on localhost but Error: Uncaught (in promise) Error: SharedArrayBuffer is NOT supported in your browser see https://developer.chrome.com/blog/enabling-shared-array-buffer/ error occurs when accessing over that internet

Steps to Reproduce:

  1. open any study on localhost and switch to MPR view. It will work find
  2. open any study over the internet and switch to MPR view. It will generate Error: Uncaught (in promise) Error: SharedArrayBuffer is NOT supported in your browser see https://developer.chrome.com/blog/enabling-shared-array-buffer/ error in browser console and MPR will not generate.

my nginx config file is below:

worker_processes 1;

events { worker_connections 1024; }

http {

    upstream orthanc-server {
        server 127.0.0.1:8099;
    }

    server {
        listen [::]:8099 default_server;
        listen 8099;

        # CORS Magic
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow_Credentials' 'true';
        add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
        add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';

        location / {

          if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow_Credentials' 'true';
                add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
                add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain charset=UTF-8';
                add_header 'Content-Length' 0;
                return 204;
            }
          try_files $uri $uri/ /index.html;
          add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Origin' '*';
          add_header Cross-Origin-Opener-Policy same-origin;
          add_header Cross-Origin-Embedder-Policy require-corp;
          add_header Cross-Origin-Resource-Policy same-origin;
                }

      location /orthanc/{

            proxy_pass http://localhost:8042;
            proxy_set_header Authorization "Basic YWxuYXNhcnBhY3MbyybyybNhcnBhY3M1Nzc=";
            proxy_set_header Host $Host:8099/orthanc/;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $server_name;

            # CORS Magic
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow_Credentials' 'true';
            add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
            add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
            rewrite /orthanc(.*) $1 break;
                           }            
    }
}

My app-config.js is below

window.config = {
    routerBasename: "/",
    extensions: [],
    modes: [],
    customizationService: {},
    showStudyList: !0,
    maxNumberOfWebWorkers: 3,
    omitQuotationForMultipartRequest: !0,
    showWarningMessageForCrossOrigin: !0,
    showCPUFallbackMessage: !0,
    showLoadingIndicator: !0,
    maxNumRequests: {
        interaction: 100,
        thumbnail: 75,
        prefetch: 10
    },
    dataSources: [{
        friendlyName: "dcmjs DICOMWeb Server",
        namespace: "@ohif/extension-default.dataSourcesModule.dicomweb",
        sourceName: "dicomweb",
        configuration: {
            name: "aws",
            wadoUriRoot: '/orthanc/wado',
          qidoRoot: '/orthanc/dicom-web',
          wadoRoot: '/orthanc/dicom-web',
            qidoSupportsIncludeField: !1,
            supportsReject: !1,
            imageRendering: "wadors",
            thumbnailRendering: "wadors",
            enableStudyLazyLoad: !1,
            supportsFuzzyMatching: !1,
            supportsWildcard: !0,
            staticWado: !0,
            singlepart: "bulkdata,video,pdf"
        }
    }, {
        friendlyName: "dicom json",
        namespace: "@ohif/extension-default.dataSourcesModule.dicomjson",
        sourceName: "dicomjson",
        configuration: {
            name: "json"
        }
    }, {
        friendlyName: "dicom local",
        namespace: "@ohif/extension-default.dataSourcesModule.dicomlocal",
        sourceName: "dicomlocal",
        configuration: {}
    }],
    httpErrorHandler: e => {
        console.warn(e.status), console.warn("test, navigate to https://ohif.org/")
    },
    defaultDataSourceName: "dicomweb",

};

The current behavior

OHIF V3 with Orthanc MPR functionality works fine on localhost but Error: Uncaught (in promise) Error: SharedArrayBuffer is NOT supported in your browser see https://developer.chrome.com/blog/enabling-shared-array-buffer/ error occurs when accessing over that internet

The expected behavior

It should work over the internet as well as localhost

I tried using the example nginx config file here as a guide >>> https://github.com/OHIF/Viewers/blob/master/.docker/Nginx-Orthanc/config/nginx.conf but no luck.

Have you solved this problem?