尝试实现 socket.io 通信时收到 GET 400。 Node.js、ELB、Route 53、CloudFront 和 s3

Receiving GET 400 when trying to implement socket.io communication. Node.js, ELB, Route 53, CloudFront, and s3

注意** 我能够通过仅使用 route-53 和弹性 beanstalk 环境将云端从 server.com 域的图片中移除,从而使事情变得可操作。仍然很高兴知道为什么 cloudfront 会阻止它,但不是开发的直接问题 **

我正在使用 cloudfront 和路由 53 为一个 node.js 静态 socket.io 客户端提供一个 s3 存储桶。我试图让这个客户端与一个 node.js 网络服务器使用弹性豆茎。网络服务器使用 route 53 域和 cloudfront 与亚马逊证书管理器生成的 ssl 连接。

使用 http 客户端并直接连接到 beantalk 环境,我看到了所需的功能。但是,当我尝试使用收到的客户端和服务器移动到 SSL/https 时:

(log from /var/log/nginx/error.log on elastic beanstalk instance)

"GET /socket.io/ HTTP/1.1" 400 51 "-" "Amazon CloudFront"

这是来自静态 s3 https 域的 运行 客户端代码:

import ioClient from "socket.io-client";

const ENDPOINT = "https://server.com";
export const socket = ioClient(ENDPOINT);

这里是服务器端。 process.env.port 设置为 8080,我可以通过 elastic beanstalk 日志验证应用程序正在侦听 8080。

const express = require("express");
const http = require("http");
const socket_io = require("socket.io");

const index = require("./routes/index");
const app = express();
app.use(index);

const server = http.createServer(app);
const io = socket_io(server);

const port = process.env.port || 4001;
server.listen(port, () => console.log(`http server listing on port ${port}`));

在 ALB 内部,我在端口 443 上设置了一个 https 侦听器,并使用 Amazon Certificate Manager (ACM) ssl 认证。侦听器有一个将 443 https 映射到 http 上的 8080 的进程,我认为 ngnix 应该从那里充当我的 socket.io 8080

侦听器的反向代理

监听器和处理器

在我的节点项目文件夹的根目录中,我有一个 .ebextensions 目录,里面有一个名为 01_files.config 的文件,其中包含以下内容:

files:
    "/etc/nginx/conf.d/websocketupgrade.conf" :
        mode: "000755"
        owner: root
        group: root
        content: |
             proxy_pass http://localhost:8080;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection "upgrade";
             proxy_http_version 1.1;
             proxy_set_header Host $host;

从 post 中的评论:socket.io handshake return error "Transport unknown" 我发现了以下 socket.io 错误代码

engine.io message type:

open =0
close =1
ping =2
pong =3
message =4
upgrade =5
noop =6
socket.io message type:

connect = 0
disconnect = 1
event = 2
ack = 3
error = 4
binary_event = 5
binary_ack = 6

所以,400 51 可能意味着“错误请求,升级断开连接。”

这是响应正文: {"code":0,"message":"Transport unknown"}

这是浏览器中客户端应用程序的错误:

polling-xhr.js:268 GET https://server.com/socket.io/?EIO=3&transport=polling&t=NIgQkyr 400

这看起来像是 xhr 传输层上的失败轮询请求

从 AWS 支持收到此解决方案:

I have discussed your case with a CloudFront (CF) expert and I have some information for you.

After going through the CF setup with my colleague, we noticed that CF is not forwarding the HOST header to Elastic Beanstalk (EB). This means if the connection between CF and EB is established over HTTPS, it will fail. To fix this, we had to take a look at the CF policies. We noticed that you are making use of Cache Policies. In this policy there is a setting for Headers. Here I would recommend that you specify the HOST setting. This way, it secures the HTTPS connection against the Origins certificated.

If that does not work, there is something else I could suggest. The error you were seeing (GET /socket.io/ HTTP/1.1" 400 51 "-" "Amazon CloudFront" "(redacted IP, redacted IP") should have returned an "X-Amz-Cf-Id" ID along with a value (something like "X-Amz-Cf-Id: jkahDAKJSHDAjhAKSJDHasd=="). Since it did not, I suspect something may have gone wrong with the websocket request parameters. For this I would recommend taking a look at our documentation and ensure that the correct request parameters required for CloudFront are being used. You can find a link to that documentation here: [1].

If all else fails, I would recommend making a request to your CF and record the key-value pair that is returned in the response header from CF. It should look something like "X-Amz-Cf-Id: jkahDAKJSHDAjhAKSJDHasd==" (the same ID and value I mentioned earlier). This value allows us as support engineers to pull the internal logs for CF using our tooling. This value will need to be recorded at the time of the request. Once you have this key-value pair, you can then open a support ticket with the CloudFront team, provide them with the error details as well as the key-value pair, and a CF engineer would be more than happy to assist you. Additionally, you could even reference this case (case ID = 7399701121) on the new case so that the engineer assisting you can have some more context.

To sum up, my first recommendation is that you forward the host header. Second is to use the request parameters required for CloudFront. And last, if you encounter another error after that, record the value from the HTTP response given by CloudFront for the error. Since I am not a CloudFront expert, I would suggest opening a new CloudFront case and providing that key-value pair in the message.

I hope you find this information useful. If you have any more questions or concerns, please feel free to reach out.