在代理上启用 ssl 后,服务器发送的事件停止工作

Server sent events stopped work after enabling ssl on proxy

我做了一个基于Tomcat和他前面的Nginx的web项目。
必须努力工作才能使其无错误地工作。
但是,当我将 ssl 添加到 nginx 时。已停止工作的服务器发送事件。
如果我直接访问后端服务器 - 它可以工作,所以问题出在 nginx 的某个地方。
有人有这样的问题吗?
这是配置的相关部分

我的 nginx.conf(我还没有使用启用站点,我的应用程序也配置在这里。基本设置在 conf 的末尾)。 /SecurConfig/api/tutorial/listen - 是事件来源

user www-data;
worker_processes 4;
pid /run/nginx.pid;

events {
worker_connections 768;
# multi_accept on;
}

http {

root /data/;

server{
listen 80;
#   server_name ajaxdemo.in.ua;
#   proxy_set_header Host ajaxdemo.in.ua;
location / {
rewrite ^(.*)$ https://ajaxdemo.in.ua permanent;
}
}

server {
#listen 80;
listen 443 default ssl;

#ssl on;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key; 



  proxy_set_header  Host               $host;
  proxy_set_header  X-Real-IP          $remote_addr;
  proxy_set_header  X-Forwarded-For    $proxy_add_x_forwarded_for;
  proxy_set_header            X-Forwarded-Proto $scheme;

location / {
    root /data/www;

    add_header 'Access-Control-Allow-Origin' *;
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'GET';

    if ($http_cookie ~* "jsessionid=([^;]+)(?:;|$)") {
        set $co "jsessionid=";
        }
    #proxy_set_header Cookie "$co";

    proxy_pass http://127.0.0.1:1666/SecurConfig/;
    #proxy_pass http://88.81.229.142:1666/SecurConfig/;
    add_before_body /header.html;
    add_after_body /footer.html;
}


location /SecurConfig/api/tutorial/listen {

    add_header 'Access-Control-Allow-Origin' *;
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'GET';

    ##Server sent events set
    proxy_set_header Connection '';
    proxy_http_version 1.1;
    chunked_transfer_encoding off;

    proxy_connect_timeout 300;
   proxy_send_timeout 300;
   proxy_read_timeout 300;

    proxy_buffering on;
    proxy_buffer_size 8k;
    #proxy_cache off;
    ##


    if ($http_cookie ~* "jsessionid=([^;]+)(?:;|$)") {
        set $co "jsessionid=";
        }
    #proxy_set_header Cookie "$co";
    proxy_pass http://127.0.0.1:1666/;
    #proxy_pass http://88.81.229.142:1666/;

}


location /SecurConfig/ {
    root /data/www;

    add_header 'Access-Control-Allow-Origin' *;
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'GET';

    if ($http_cookie ~* "jsessionid=([^;]+)(?:;|$)") {
        set $co "jsessionid=";
        }
    #proxy_set_header Cookie "$co";

    proxy_pass http://127.0.0.1:1666/;
    #proxy_pass http://88.81.229.142:1666/;
    add_before_body /header.html;
    add_after_body /footer.html;
}

location ~ \.css$ {
root /data/css/;
}

location /header.html {
root  /data/www;
}
location /footer.html {
root  /data/www;
}

location ~ \.(gif|jpg|png|jpeg)$ {
    root /data/images;
}

}


##
# Basic Settings
##

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 100m;

include /etc/nginx/mime.types;
default_type application/octet-stream;

##
# Logging Settings
##

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

##
# Gzip Settings
##

gzip on;
gzip_disable "msie6";


##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
#   include /etc/nginx/sites-enabled/*;
}

nginx的错误日志中没有错误条目。 但是在访问日志中也提到了对 /SecurConfig/api/tutorial/listen 的访问。代码 200 表示 "all is ok"

"GET /SecurConfig/api/tutorial/listen HTTP/1.1" 200 187 "https://ajaxdemo.in.ua/SecurConfig/api/tutorial/map/11111111" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"

Tomcat 日志像往常一样显示对 /SecurConfig/api/tutorial/listen 的访问(例如检查安全访问,接受它,然后重新发送给控制器)​​。
如果我 运行 我的页面处于 chrome 开发者模式,我会看到这个错误

GET https://ajaxdemo.in.ua/SecurConfig/api/tutorial/listen net::ERR_EMPTY_RESPONSE


UPD


好的。当我在互联网上搜索信息时,我离开了打开 SSE 的页面。 10 分钟后,我看到,我的数据如其所愿地出现了。我根据缓冲

评论了所有设置
    #proxy_buffering on;
    #proxy_buffer_size 8k;
    #proxy_cache off;

我还评论了参数

     #proxy_connect_timeout 300;
    #proxy_send_timeout 300;
    #proxy_read_timeout 300;

所以这个参数返回了默认值(大约 20 秒) 我的所有数据都在 ~20 秒后出现。 所以我设置了

  proxy_connect_timeout 2;
    proxy_send_timeout 2;
    proxy_read_timeout 2;

而且数据出现得更快。但是its.s还是一件一件的(一次3-4个事件),在启用ssl事件之前一个一个显示。
仍然需要您的帮助和解释,我哪里错了。


UPD


这是我服务器的配置,当我 "turn off" ssl 和 SSE 工作时。
ssl is off - sse works
80 port redirecting to 443 ssl - sse not works

更新:这是我在 OP 收集更多信息之前的回答(特别是如果他等待足够长的时间数据到达,即这是一个缓冲问题:请参阅我的其他回答)。我决定把它留在这里,因为它可能对其他人有用的故障排除想法。 (但如果您不同意,请发表评论或标记删除。)


规划 "Data Push Apps with HTML5 SSE" 时,不幸的是,使用代理服务器是我们划清界限的另一面;如果你读过第 9 章,你就会知道一个看似简单的标准可能会变得非常复杂。所以,我很想知道你是否以及如何使它起作用。

首先想到的是您正在使用 self-signed SSL 证书。他们不会与 Chrome 一起使用 SSE。 Ajax 也不会。 (参见the bug report,但它是在 2011 年开放的,所以请不要屏住呼吸。)但是你说它适用于 Firefox,所以这不太可能。

下一个想法是您正在使用 Access-Control-Allow-Origin:*Access-Control-Allow-Credentials:true,所以我认为这意味着您需要使用 CORS(即您的 html 页面来源和 SSE 脚本origin 在某种程度上是不同的),并且涉及到 cookie。您是否将 { withCredentials: true } 设置为 JavaScript 中 EventSource 构造函数的第二个参数?即使如此,请注意 Access-Control-Allow-Credentials:true 不适用于 Access-Control-Allow-Origin:*。您不能指定 *,而必须明确说明允许哪个来源。

如果这是问题所在,您可以使用 server-side 脚本根据客户端的来源动态生成 header。 (本书在 PHP 中显示了执行此操作的代码。)如果 nginx 可以根据用户的请求使用环境变量,那么您也应该可以在那里执行此操作。

但是,我的理解是这也会因 http 而出错。我认为这不应该只是一个 https 问题。 (如果我错了,请告诉我。)

(哦,如果你的工作 http SSE 请求来自 http://example.com, and are still coming from http://example.com, even though the SSE request is now going to https://example.com,那么一切都有意义 - 你之前没有遇到 CORS 失败,因为来源相同;现在你确实遇到了 CORS 问题,而且你没有正确处理它。)

我的第三个猜测是,浏览器正在发送预检 OPTIONS 请求,但仅使用 https 请求进行。 (预检请求从浏览器到浏览器疯狂,但当前版本的 Chrome 和 Firefox 的行为方式相同并非不可能。)当您收到 OPTIONS 请求时,您需要发回 Access-Control-Allow-Headers: Last-Event-ID, Origin, X-Requested-With, Content-Type, Accept, Authorization。您还需要发回 Access-Control-Allow-Origin:* header.

您可以通过数据包嗅探来确认或反驳第三个猜测,以查看来回发送的到底是什么。如果以上所有想法都没有结果,您还是应该这样做。

要使 SSE 正常工作,您必须确保没有任何内容被缓存或缓冲:不在您的脚本中(例如,在 PHP 中我们有 @ob_flush();@flush() 习语),不在您的网络服务器中,而不是在任何中间代理或防火墙上。

你说你注释掉了所有与缓冲有关的 nginx 命令,但注释掉意味着它将使用默认值。例如。 proxy_buffering 的默认值为 on。我建议明确指定它们以确保关闭所有缓冲和缓存。

proxy_buffering off;
proxy_buffer_size 0;
proxy_cache off;

我也会考虑明确地将超时设置得较高,而不是将它们注释掉。 "high" 的定义取决于您的应用程序。例如。如果它总是每隔几秒发送一次数据,默认值就可以了。但是,如果您使用 SSE 处理不规则数据,并且有时消息之间可能有半小时,请确保超时超过半小时。

更新: 显然(见评论)添加 response.addHeader("X-Accel-Buffering", "no");(即添加到 server-side 进程,而不是代理配置)解决了问题。这是有道理的,因为它是专门为 SSE 和类似的 HTTP 流添加的,请参阅 nginx documentation。它确实意味着上面的 Nginx 配置也应该有效(OP 报告说它不起作用)。但是,另一方面,使用 header 在 per-connection 的基础上禁用缓冲无论如何感觉都是更好的解决方案。

简而言之,nginx 会检测与后端应用程序的连接是否有效,并且在使用 SSL 的情况下检测可能无法正常工作,如此处所述

http://mailman.nginx.org/pipermail/nginx/2013-March/038120.html