【问题标题】:Server sent events stopped work after enabling ssl on proxy在代理上启用 ssl 后服务器发送的事件停止工作
【发布时间】:2015-01-12 09:19:28
【问题描述】:

我在他面前做了一个基于 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$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;

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


更新


这是我服务器的配置,当我“关闭”ssl 并且 SSE 工作时。
ssl is off - sse works
80 port redirecting to 443 ssl - sse not works

【问题讨论】:

  • 是否需要任何身份验证或 cookie?您确定在使用 SSL 时没有遇到任何 CORS 限制吗? (http 和 https 算作不同的来源)(还要确保您已经在 Chrome 和 Firefox 中进行了测试,因为我记得在这方面遇到过浏览器错误。)我想知道的另一件事是为什么 proxy_buffering 在你的配置。如果这些猜测有帮助,请告诉我,我会发布正确的答案:-)
  • 1) 是的。在 tomcat 端进行会话安全检查需要 Cookie。它成功地检查了cookie。 2)“add_header‘访问控制允许来源’*;” - 这应该保护我免受跨域起源。至于 http/https - 我不知道如何检查是否有问题。 3)是的,我检查了 chrome 和 firefox。 4)proxy_buffering 必须保护我免受“粉碎”消息。如果缓冲如果“关闭”,那么 nginx 可以将长消息分开。无论如何,它在启用 ssl 之前工作。

标签: ssl nginx server-sent-events


【解决方案1】:

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

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

proxy_buffering off;
proxy_buffer_size 0;
proxy_cache off;

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

更新:显然(参见 cmets)添加 response.addHeader("X-Accel-Buffering", "no");(即添加到服务器端进程,而不是添加到代理配置)可以解决问题。这是有道理的,因为它是专门为 SSE 和类似的 HTTP 流添加的,请参阅nginx documentation。它确实暗示上述 Nginx 配置也应该起作用(OP 报告说它不起作用)。但是,另一方面,使用标头在每个连接的基础上禁用缓冲无论如何感觉都是更好的解决方案。

【讨论】:

  • 这似乎合乎逻辑。但是禁用缓冲区并没有帮助。至少以您建议的形式。还需要试验。我想,如果我设置低超时,我的数据只会在关闭监听源后刷新。某处缓冲区仍在运行...
  • 好的,我解决了问题,通过手动添加标题“response.addHeader("X-Accel-Buffering", "no");"。因此,即使有一些缓冲区仍然存在 - nginx 也不会使用它来处理带有此标头的数据包。 @Darren Cook 你能用这个信息更新你的答案吗?所以我可以接受。
  • @Asprelis 我想你可以自己回答吗? (或者这需要最低限度的代表吗?)我仍然感到困惑,因为根据wiki.nginx.org/X-accel#X-Accel-Buffering,该命令只是在一个连接上切换代理缓冲。它不只是proxy_buffering off; 的替代品吗?我想知道后者是否在您的 nginx 配置文件中的错误位置(或类似的东西)??
  • 您的帖子包含更多有用的信息。我希望其他人也阅读它。所以请更新你的帖子。
  • @Asprelis 好的,完成。顺便说一句,这个额外的标头是否肯定只需要 SSL,而在使用代理服务器但使用 http 时不需要? (如果是真的,那让我很困扰,因为这意味着我不明白的事情正在表面之下发生。)
【解决方案2】:

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


在规划“使用 HTML5 SSE 的数据推送应用程序”时,不幸的是,使用代理服务器是我们划定界限的另一面;如果你读过第 9 章,你就会知道一个看似简单的标准仍然会变得非常复杂。所以,我很想知道你是否以及如何让它发挥作用。

首先想到的是您使用的是自签名 SSL 证书。它们不能与 SSE 和 Chrome 一起使用。阿贾克斯也不会。 (见the bug report,但它是在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 问题,并且您没有正确处理它。)

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

您可以通过数据包嗅探来确认或反驳这第三个猜测,以查看来回发送的确切内容。如果以上所有想法都没有结果,那么无论如何你都应该这样做。

【讨论】:

  • 哇,我觉得 sse 太复杂了。我真的使用自签名的 ssl 证书。我会试着调查你的猜测。
  • 哇。当我在互联网上搜索信息并打开我的应用程序页面时 - 很长一段时间后,我收到了我的数据,应该是这样。但是延迟很大。我什么都没做。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-24
    • 1970-01-01
    相关资源
    最近更新 更多