在Apache基本身份验证之后,JavaScript EventSource无法打开连接

时间:2016-08-12 15:03:41

标签: javascript apache server-sent-events

我不负责Apache的配置,所以我不确定在有用的conf文本方面我能提供什么,但我相当肯定我已将问题缩小到登录。在没有任何登录的情况下,EventSource在XAMPP上本地完美运行,并且在生产服务器上进行身份验证后刷新页面,但是服务器上的第一次加载将无法打开连接。以前有人见过这个问题吗?在搜索过去几天之后,我无法在互联网上找到任何

编辑:一些代码

一些服务器端代码(主要不应该是相关的):

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$client_stream = new RedisStream();
$client_stream->poll(1); //The loop, with sleep time as a parameter

JavaScript:

var xhttpViewSet;
var xhttpSearch;
var view = 'tile';
var search = '';

var seed_url = '/core/seed_view.php';
var stream_url = '/core/stream.php';

var default_class = 'panel-default';
var success_class = 'panel-success';
var warning_class = 'panel-warning';
var danger_class = 'panel-danger';

function UpdateClient(c_name, c_obj) {
    if ((c_element = document.getElementById(c_name)) !== null) {
        c_element.classList.remove('text-muted');

        c_element.classList.remove(default_class);
        c_element.classList.remove(success_class);
        c_element.classList.remove(warning_class);
        c_element.classList.remove(danger_class);

        switch (c_obj['status']) {
            case 0:
                c_obj['status'] = 'OK';
                c_element.classList.add(success_class)
                break;
            case 1:
                c_obj['status'] = 'Warning';
                c_element.classList.add(warning_class)
                break;
            case 2:
                c_obj['status'] = 'Critical';
                c_element.classList.add(danger_class)
                break;
            default:
                c_obj['status'] = 'Unknown';
                c_element.classList.add(danger_class)
                break;
        }

        for (i in c_obj) {
            var var_nodes = c_element.getElementsByClassName(i);
            if (var_nodes.length > 0) {
                for (var j = var_nodes.length - 1; j >= 0; j--) {
                    var_nodes[j].innerHTML = c_obj[i];
                }
            }
        }
    }
}

function SetView() {
    var view_url = seed_url + '?search=' + search + '&view=' + view;

    xhttpViewSet.open('GET', view_url, true);
    xhttpViewSet.send();
}

var main = function() {
    container = document.getElementById('content');

    if (new XMLHttpRequest()) {
        xhttpViewSet = new XMLHttpRequest();
        xhttpSearch = new XMLHttpRequest();
    } else {
        xhttpViewSet = new ActiveXObject('Microsoft.XMLHTTP');
        xhttpSearch = new ActiveXObject('Microsoft.XMLHTTP');
    }

    var stream = new EventSource(stream_url);
    stream.onopen = function() {
        console.log('Connection opened.'); //This doesn't fire
    }

    stream.onmessage = function(e) {
        var c_obj = JSON.parse(e.data);
        UpdateClient(c_obj.name, c_obj.value);
    };

    xhttpViewSet.onreadystatechange = function() {
        if (xhttpViewSet.readyState == 4) {
            var resp = xhttpViewSet.responseText;
            if (xhttpViewSet.status == 200 && resp.length > 0) {
                container.innerHTML = resp;
                if (view == 'list') {
                    $('#computer-table').DataTable({
                      "lengthMenu": [[25, 50, 100], [25, 50, 100]]
                    });
                }
            } else {
                container.innerHTML = '<error>No computers matched your search or an error occured.</error>';
            }
        }
    }
    SetView(); //This successfully does all but make the EventSource connection, and only fails to do that on first load

    document.getElementById('list-view').addEventListener('click', function() {
        view = 'list';
        SetView();
    });

    document.getElementById('tile-view').addEventListener('click', function() {
        view = 'tile';
        SetView();
    });

    document.getElementById('search').addEventListener('keyup', function() {
        search = this.value.toUpperCase();
        SetView();
    });

    document.getElementById('clear-search').addEventListener('click', function() {
        document.getElementById('search').value = '';
        search = '';
        SetView();
    });
};

window.onload = main;

1 个答案:

答案 0 :(得分:1)

如果没有更多信息,确实有点难以确定,但根据您目前所说的内容,我认为它是以下之一:

HEAD / OPTIONS :某些浏览器会在发送GET或POST之前向服务器脚本发送HEAD或OPTIONS http调用。发送OPTIONS的目的是询问允许发送哪些标头。这可能是登录过程的一部分; 可能会解释重新加载时它的工作原理。有关详细信息,请参阅使用HTML5 SSE的数据推送应用程序的第9章(免责声明:我的书);基本上,在SSE脚本的顶部,您需要检查$_SERVER["REQUEST_METHOD"]的值,如果它是"OPTIONS",请拦截并说明您要接受的标题。我之前使用过这个:

header("Access-Control-Allow-Headers: Last-Event-ID,".
  " Origin, X-Requested-With, Content-Type, Accept,".
  " Authorization");`

CORS: HTML网页网址和SSE网页网址必须具有相同的来源。在数据推送应用程序的第9章(再次)中有详细的解释(特定于SSE),或者Wikipedia(不太具体)。如果这是问题所在,请考虑将header("Access-Control-Allow-Origin: *");添加到您的SSE脚本中。

withCredentials: SSE构造函数有第二个参数,你可以这样使用它:var stream = new EventSource(stream_url, { withCredentials: true });它说可以发送auth凭证。 (同样,本书的第9章更详细 - 抱歉重复的插件!)在服务器端还有第二步:在PHP SSE脚本的顶部,您需要添加以下内容。

header("Access-Control-Allow-Origin: ".@$_SERVER["HTTP_ORIGIN"]);
header("Access-Control-Allow-Credentials: true");

PHP会话锁定:这通常会导致相反的问题,即SSE脚本已锁定PHP会话,因此没有其他PHP脚本可以工作。有关如何处理它,请参阅https://stackoverflow.com/a/30878764/841830。 (无论如何,这是一个好主意,即使它不是你的问题。)