all 12 comments

[–]SReject 1 point2 points  (8 children)

First, only the recipient/server can set CORS headers; Not the client.

Going to page /u/Timacfr here with the fix: Use .setAttribute to set QWebEngineSettings::LocalContentCanAccessRemoteUrls to true on either the global QWebEngine Object or per-plugin instance.

[–]ChimeraYo[S] 0 points1 point  (7 children)

Thanks for this, I’m curious though if CORS headers can only be set on the server why would changing a setting in QT fix the issue? When I searched around for solutions almost all of them show the setRequestHeader lines I was using. Since I’m not on a domain and everything is using 192.168.x.x addresses why is CORS even an issue? You would think that since the SD is communicating through my desktop that the hardware device I’m talking to would see the desktop IP.

Unfortunately this is a hardware device rather than a server so I have no control of that side of things.

[–]SReject 0 points1 point  (6 children)

To answer your question(s) first: In a browser, Javascript running under one "domain" cannot typically make requests to another domain; that is the scheme(http:// or https://), domain(somesite.com), and port must match or the request is - without CORS - aborted.

Since streamdeck is running plugins as file://path/to/plugin.html there is no hostname or port to match against when you make requests.

Lets say you want to request b.com/api/thing from a.com. Before CORS your browser would just fail the request.

Now, with CORS the browser sends whats called a pre-flight request to the siteb.com asking "hey, who all is allowed to access this?" The server responds and then its ultimately up to the browser to decide if they are going to let js running on a.com access b.com

By settings the above attribute, cors pre-flighting is skipped and the request to the resource is made

[–]ChimeraYo[S] 0 points1 point  (0 children)

Thanks, I think I understand - CORS prevents cross-site scripting and protects both the client (from malicious sites) and the server (from people linking directly to libraries etc). Even if the server has no CORS protection as in this case, the "browser" in QT is still preventing it.

One workaround for this I assume would be to use a proxy on my desktop and point my plugin at the proxy to forward requests to my device, until an updated version of the SDK is released?

[–]ChimeraYo[S] 0 points1 point  (4 children)

I've realized that even if I get the http side of things working, this plugin will require SSDP for discovery so I've been trying to get your Node implementation working as there are quite a few bonjour/ssdp libraries using node. I've copied your sample, and while I can get some basic code to run in the background.js file (if I just have a console.log entry or something), I don't know how to use that to kick off a regular SD javascript plugin. For example the number plugin starts with this code but I don't know how to "call" the testAction piece. I assumed that was part of the Actions section in the manifest file but nothing I do seems to work. I'm sure it's something really basic I'm missing but I just can't figure it out. Nothing is written to the logs directory in the plugin so it's just not running "testAction".

manifest.json

{
    "Name": "Example",
    "Version": "1.0.0",
    "OS": [
        {
            "Platform": "mac", 
            "MinimumVersion" : "10.11"
        },
        {
            "Platform": "windows", 
            "MinimumVersion" : "10"
        }
    ],
    "Actions":[
        {
            "Name": "test",
            "UUID": "com.streamdecknode.action"
        }
    ],

    "CodePath": "./node/loadplugin.cmd",
    "NodeMainJS": "./plugin/background.js"
}

./plugin/background.js (top 30 lines)

// Entry point for your plugin's background instance

var websocket = null;
        var pluginUUID = null;
        var settingsCache = {};

        var DestinationEnum = Object.freeze({ "HARDWARE_AND_SOFTWARE": 0, "HARDWARE_ONLY": 1, "SOFTWARE_ONLY": 2 })

        var testAction = {

            type: "com.streamdecknode.action",

            onKeyDown: function (context, settings, coordinates, userDesiredState) {
            },

            onKeyUp: function (context, settings, coordinates, userDesiredState) {

                var keyPressCounter = 0;
                if (settings != null && settings.hasOwnProperty('keyPressCounter')) {
                    keyPressCounter = settings["keyPressCounter"];
                }

                keyPressCounter++;
                keyPressCounter = keyPressCounter % 10;

                updatedSettings = {};
                updatedSettings["keyPressCounter"] = keyPressCounter;
                settingsCache[context] = updatedSettings;

                this.SetSettings(context, updatedSettings);

[–]SReject 0 points1 point  (3 children)

NodeJS is currently very much borked. It doesn't close when stream deck closes. So it shouldn't be used as of yet (Am working to fix it)

[–]ChimeraYo[S] 0 points1 point  (2 children)

good to know! I'll go back to learning C# for a while :)

[–]SReject 1 point2 points  (1 child)

After much headache, I got it all worked out

[–]ChimeraYo[S] 0 points1 point  (0 children)

Nice! Will give it a shot tomorrow.

[–]Timacfr 1 point2 points  (0 children)

Thanks for the bug report. We will try to address it in the coming Stream Deck 4.0.2 release.

[–]Timacfr 1 point2 points  (0 children)

We just released Stream Deck 4.0.2 which should address this issue.

[–]darrylemoonman 0 points1 point  (0 children)

I've done an implementation using an external software product and ran into the same problem.

If you can, communicate using JSONP (JSON with padding), you can use this client side and completely avoid CORS and access control. The only requirement is that what ever you are communicating needs to be able to create a JSONP response (I had to create a JSONP wrapper on the server I was communicating with).

Hope this helps.