在代理上启用 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
我做了一个基于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