Search code examples
javascriptcorseventsourceeclipse-honoeclipse-ditto

Ditto HTTP API server sent events CORS error


I installed Hono+Ditto using helm-charts, as it is described in cloud2edge.

That means Hono+Ditto is running inside a minikube on my PC. I also created a connection, policy, and a device. So far everything works fine.

In the next step, I just wrote a simple "frond-end" to fetch the thing state from Ditto-HTTP-API. As long as I fetch the thing state manually by the mean of fetch-API everything is fine. But as soon as I try to use the SSE (Eventsource) I get the following CORS error:

index.html:1 Access to resource at 'http://192.168.99.100:32084/api/2/things/de.iot1:dev1' from
 origin 'http://localhost:63342' has been blocked by CORS policy: The value of the 
'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'
 when the request's credentials mode is 'include'.

I am just struggling with this error since yesterday and none of the answers regarding CORS-errors I found on the internet worked :(.

How can I communicate with Ditto from my PC using Eventsource without getting CORs-error?

Below is my simple front-end:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        {
            box-sizing: border-box;
        }

        .container {
            display: grid;
        }

        .row:after {
            content: "";
            display: table;
            clear: both;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="row">
        <label for="selector">Choose update strategy:
        <select name="method" id="selector">
            <option value="auto">Autorefresh</option>
            <option value="SSE">SSE</option>
        </select>
        </label>
    </div>

    <div class="row">
        <label for="dev"><h3>Device state:</h3></label>
    </div>
    <div class="row">
        <textarea id="dev" name="dev-data" rows="20" cols="50"></textarea>
    </div>
</div>

<script src="https://code.jquery.com/jquery-3.5.1.min.js"
        integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
        crossorigin="anonymous"></script>
<script>
    const baseUrl = "http://192.168.99.100:32084"; // Ditto IP:PORT
    const username = "ditto";
    const password = "ditto";
    const interval = 1000;
    const thingId = "de.iot1:dev1";
    const thingUrl = `${baseUrl}/api/2/things/${thingId}`;
    let intervalId;
    let eventSource = null

    function requestData(url) {
        var headers;
        headers = new Headers();
        headers.append('Content-Type', 'application/json');
        headers.append('Authorization', 'Basic ' + btoa(`${username}:${password}`));

        init = {
            method: 'GET',
            headers: headers,
        };

        var request = new Request(url);
        return fetch(request, init)
            .then(function (response) {
                if (response.ok) {
                    return response;
                }
                throw response;
            })
    }

    function updateDeviceState(data) {
        $('#dev').val(JSON.stringify(data, null, 2));
    }

    function onRefresh() {
        requestData(thingUrl)
            .then(response => {
                for (var pair of response.headers.entries()) {
                    console.log(pair[0] + ': ' + pair[1]);
                }
                return response.json()
            })
            .then(data => { updateDeviceState(data) });
    }

    function enableAutoRefresh(enabled=true) {
        if (enabled) {
            intervalId = setInterval(() => { onRefresh() }, interval);
        } else {
            clearInterval(intervalId);
        }
    }

    function enableEventSource(enabled=true) {
        if (enabled) {
            eventSource = new EventSource(thingUrl, {withCredentials: true})
            eventSource.addEventListener('message', (e) => { console.log(e) })
        } else if (eventSource != null) {
            eventSource.removeEventListener('message', this.eventListener)
            eventSource.close();
            eventSource = null;
        }
    }

    function applyUpdateStrategy() {
        let val = $('#selector').val();
        let autoRefreshEnabled = val.includes('auto');

        enableAutoRefresh(autoRefreshEnabled);
        enableEventSource(!autoRefreshEnabled);
    }

    $('#selector').on('change', () => { applyUpdateStrategy() })
    applyUpdateStrategy()
</script>
</body>
</html>

Thanks!


Solution

  • Thank you for reaching out. You found a bug which was already fixed the Ditto nginx configuration, however not yet applied to the "packages" project. I created a PR to fix this, so this should be fixed in the next Helm version of the Ditto chart: https://github.com/eclipse/packages/pull/193

    This question would have been better placed on GitHub as issue - but you could of course not have known that this was a bug before.