代理启用SSL后,服务器发送事件停止工作

10
我做了一个基于Tomcat和Nginx的Web项目。
我不得不努力使它在没有错误的情况下正常工作。
然而,当我在nginx上添加ssl时,服务器发送事件就停止工作了。
如果我直接访问后端服务器,它可以工作,所以问题可能出在nginx上。
有人遇到过这样的问题吗?
以下是相关配置的部分内容:
我的nginx.conf(我还没有使用sites-enabled,并将我的应用程序配置放在这里。基本设置在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$1 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=$1";
        }
    #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=$1";
        }
    #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=$1";
        }
    #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代码表示“一切正常”。

“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

更新


好的。当我在互联网上搜索信息时,我让我的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;

数据出现得更快了,但仍为一个整体(3-4个事件同时发生),在启用SSL之前,事件逐一显示。
我还需要你的帮助,并解释我哪里做错了。


更新


这是我的服务器配置,当我“关闭”SSL并且SSE工作时。
SSL关闭-SSE工作
80端口重定向到443 SSL-SSE不工作


是否需要任何身份验证或cookie?您确定在使用SSL时没有遇到任何CORS限制吗?(http和https被视为不同的来源)(还要确保您已在Chrome和Firefox中进行了测试,因为我记得在这个领域遇到过浏览器错误。) 我想知道的另一件事是为什么在配置文件的SSE部分打开了proxy_buffering。 如果这些猜测中有任何一项有帮助,请告诉我,我会发布一个适当的答案 :-) - Darren Cook
  1. 是的。在Tomcat端,会话安全检查需要使用Cookies,并且它可以成功地检查Cookie。
  2. "add_header 'Access-Control-Allow-Origin' *;" - 这应该可以保护我免受跨域来源的攻击。至于http/https - 我不知道如何检查是否存在问题。
  3. 是的,我在Chrome和Firefox上都进行了检查。
  4. proxy_buffering必须保护我免受“分片”消息的影响。如果缓冲区关闭,则Nginx可能会将长消息切成几部分。无论启用SSL与否,它之前都是有效的。
- Asprelis
3个回答

19

为了使SSE正常工作,您必须确保没有任何内容被缓存或者缓冲:无论是在您的脚本中(例如,在PHP中我们有@ob_flush();@flush()这样的习语),还是在您的Web服务器、中介代理或防火墙中。

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

proxy_buffering off;
proxy_buffer_size 0;
proxy_cache off;

我建议明确设置超时时间,而不是将其注释掉。"高"的定义取决于你的应用程序。例如,如果它每隔几秒钟发送一次数据,那么默认设置就可以了。但是,如果您正在使用SSE处理不规则数据,并且消息之间可能有半个小时的间隔,请确保超时时间超过半个小时。

更新: 显然(请参阅评论)添加response.addHeader("X-Accel-Buffering", "no");(即在服务器端进程中添加,而不是代理配置中)可以解决该问题。这是有道理的,因为它专门为SSE和类似的HTTP流添加了,可以参见nginx文档。这意味着上述的Nginx配置也应该可以工作(OP已经报告说无法)。但另一方面,在每个连接上使用一个标头来禁用缓冲似乎是更好的解决方案。


这似乎是合乎逻辑的。但是禁用缓冲区并没有帮助。至少不是以你建议的形式。仍需要进行实验。我猜,如果我设置低超时时间,我的数据在关闭监听源后会被清除。某个地方还有缓冲区... - Asprelis
2
好的,我解决了这个问题,通过手动添加头部 "response.addHeader("X-Accel-Buffering", "no");"。因此,即使有一些缓冲仍然存在,nginx也不会使用它来处理带有此标头的数据包。 @Darren Cook,你能更新你的答案并加入这些信息吗?这样我就可以接受它了。 - Asprelis
@Asprelis 我认为你可以自己回答吗?(或者需要最低声望?)我仍然很困惑,因为根据http://wiki.nginx.org/X-accel#X-Accel-Buffering,该命令只是在一个连接上关闭代理缓冲。它不是`proxy_buffering off;`的替代品吗?我想知道后者是否放错了位置在你的nginx配置文件中(或类似的东西)? - Darren Cook
你的帖子包含更多有用的信息。我希望其他人也能阅读它。所以请更新你的帖子。 - Asprelis
好的,谢谢。我会更新我的问题,以便向您展示有和没有SSL的配置。不幸的是,我的专业知识还不足以看出关键差异。 - Asprelis
显示剩余2条评论

2
更新:这是在 OP 收集更多信息之前我的答案(特别是如果他等待足够长的时间,数据就会到达,即它是一个缓冲问题:请参见我的其他答案)。我决定把它留在这里,因为它可能对其他人有用的故障排除思路。(但如果您不同意,请留下评论或标记删除。)
在计划“使用HTML5 SSE进行数据推送应用程序”时,使用代理服务器不幸成为我们划定界限的另一面;如果您已经阅读了第9章,您会知道一个看似简单的标准仍然可能变得非常复杂。因此,我非常想听听您是如何使其工作的,如果有的话。
首先想到的是您正在使用自签名的SSL证书。它们无法与Chrome一起使用SSE。Ajax也不行。(请参见错误报告,但它是在2011年开放的,所以不要抱太大希望。)但是您说它可以在Firefox中工作,因此这不太可能是原因。
下一个想法是你正在使用 Access-Control-Allow-Origin:*Access-Control-Allow-Credentials:true,因此我认为这意味着你需要进行 CORS(即你的 HTML 页面来源和 SSE 脚本来源在某种方式上不同),并且涉及到 cookies。你是否在 JavaScript 中将第二个参数设置为 { withCredentials: true },作为你的 EventSource 构造函数?即使如此,请注意,Access-Control-Allow-Credentials:true 不能与 Access-Control-Allow-Origin:* 一起使用。你不能指定 *,而必须明确说明允许哪个来源。
如果这是问题所在,你可以使用你的服务器端脚本根据客户端的来源动态生成头部信息。(书中展示了 PHP 代码来实现这一点。)如果 nginx 可以使用用户请求的环境变量,你也可以在那里实现。
然而,我的理解是这也会在 HTTP 上出现问题。我认为这不应该只是 HTTPS 的问题。(如果我错了,请让我知道。)
如果您的工作中使用的http SSE请求来自http://example.com,并且仍然来自http://example.com,即使现在SSE请求正在发送到https://example.com,那么一切都是有道理的- 您之前没有收到CORS错误,因为来源相同;现在您确实遇到了CORS问题,但未正确处理。
我的第三个猜测是,浏览器只会使用https请求发送预检OPTIONS请求。(预检请求从浏览器到浏览器的行为差异很大,但当前版本的Chrome和Firefox可能会以相同的方式行事。)当您收到OPTIONS请求时,需要返回Access-Control-Allow-Headers: Last-Event-ID, Origin, X-Requested-With, Content-Type, Accept, Authorization头信息。您还需要返回Access-Control-Allow-Origin:*头信息。
您可以通过数据包嗅探来确认或否认这第三个猜测,以查看正在发送和接收的内容。如果所有上述想法都没有结果,那么您应该进行数据包嗅探。

哇,我从来没有想过SSE是如此复杂。我真的使用自签名SSL证书。我会尝试调查你的猜测。 - Asprelis
哇。当我在互联网上搜索信息并保持我的应用程序页面打开时,经过一段长时间后,我收到了我的数据,完全符合预期。但是有很大的延迟。我还没有做出任何更改。 - Asprelis

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接