我正在使用Node.js代理服务器,它将来自网站的请求路由到本地主机端点。网站可以在本地或云示例上运行,因此代理需要支持HTTP和HTTPS请求并将它们路由到HTTP端点。
下面的代码几乎适用于所有用例,除了WebSocket请求来自HTTPS地址时。代码如下:
import * as express from "express";
import { Server, createServer } from "http";
import { createProxyServer } from "http-proxy";
import { settings } from "./settings"
export let port = 3004;
let server: Server;
export function startServer() {
const URL = settings.URL;
const app = express();
server = createServer(app);
const proxy = createProxyServer({
target: URL,
changeOrigin: !URL.includes("localhost"),
ws: true,
headers: {
// Attach request headers
},
});
server.on("upgrade", function (req, socket, head) {
proxy.ws(req, socket, head, {});
});
app.get("/public/*", function (req, res) {
proxy.web(req, res, {});
});
app.get("/api/*", function (req, res) {
proxy.web(req, res, {});
});
app.post("/api/query", function (req, res) {
proxy.web(req, res, {});
});
server.listen(0, () => {
port = server?.address()?.port;
console.log("Server started");
});
}
export function stopServer() {
if (server) {
server.close();
}
}
字符串
行changeOrigin: !URL.includes("localhost")
根据主机设置changeOrigin
。它对于localhost请求是不必要的,但对于HTTPS请求是必需的。
然而,这段代码对于WebSocket请求失败,并返回以下错误:
WebSocket connection to 'ws://localhost:61958/api/ws' failed:
//...
ERR read ECONNRESET: Error: read ECONNRESET
at TCP.onStreamRead (node:internal/stream_base_commons:217:20)
at TCP.callbackTrampoline (node:internal/async_hooks:130:17)
型
如果我没有通过以下方式为WebSocket端点设置changeOrigin
:
server.on("upgrade", function (req, socket, head) {
proxy.ws(req, socket, head, {changeOrigin: false});
});
型
我得到一个不同的错误:
ERR Client network socket disconnected before secure TLS connection was established:
Error: Client network socket disconnected before secure TLS connection was established
at connResetException (node:internal/errors:704:14)
at TLSSocket.onConnectEnd (node:_tls_wrap:1590:19)
at TLSSocket.emit (node:events:525:35)
at endReadableNT (node:internal/streams/readable:1358:12)
at process.processTicksAndRejections (node:internal/process/task_queues:83:21)
型
有什么解决办法吗?我觉得我忽略了一些简单的东西,但无法弄清楚。
2条答案
按热度按时间ukxgm1gy1#
Ruslan Zhomir的answer提到了一个解决方法:使用
http-party/node-http-proxy
中使用的proxyReq
事件修改WebSocket升级请求中的origin
头。这可能适用于目标端点使用
origin
头来验证WebSocket连接的场景,但这不是常见的要求。如果你只需要处理简单的、不安全的WebSocket连接,这可能就足够了。但是,如果您需要处理安全和不安全WebSocket连接的混合,或者如果您正在开发生产级应用程序,则需要不同的方法。
问题分析
现在,您正在尝试将安全的WebSocket升级请求从HTTPS客户端直接传递到HTTP目标,而没有正确管理和解密这些安全请求。您需要向代理服务器添加适当的SSL/TLS处理来解决这个问题。
在您的原始代码中,您正在管理
upgrade
事件并使用proxy.ws
方法,类似于node-http-proxy
自述文件。字符串
当客户端建立WebSocket连接时,它首先发送一个普通的HTTP请求,然后请求将连接升级到WebSocket。在您的情况下,如果此初始请求通过HTTPS发送,则将连接升级到WebSocket的请求 * 也 * 通过HTTPS发送。
但是,在上面的代码中,您试图将此升级请求直接传递给代理目标。如果目标是HTTP端点,它将无法处理来自HTTPS客户端的加密请求,导致您看到的
ECONNRESET
错误。当前的代理设置也无法正确处理HTTPS WebSocket连接:
型
在这里,您将
ws
选项设置为true
,表示代理应该能够处理WebSocket请求。但是,由于您没有提供任何SSL/TLS选项来处理HTTPS,因此代理无法正确建立 * 安全 * WebSocket连接。这就是第二个错误(关于在建立安全TLS连接之前断开连接)的来源:您尝试将WebSocket请求从HTTPS客户端代理到HTTP服务器。当WebSocket请求来自HTTPS客户端时,客户端期望建立安全的WebSocket连接(
wss://
)。为了正确地接受这个安全连接并将其降级为HTTP目标的非安全连接,您的代理服务器需要设置为处理HTTPS。否则,它将无法正确建立客户端所期望的安全WebSocket连接。您的代理服务器需要首先正确建立HTTPS WebSocket连接,然后将其降级为目标的非安全WebSocket连接。
可能的解决方案
由于您的目标始终是HTTP端点,并且您的服务器可以接收HTTP或HTTPS,因此您应该单独处理HTTPS连接,然后将其传递给代理。
这意味着除了HTTP服务器之外还要创建一个HTTPS服务器。您的HTTPS服务器应该具有所有必要的SSL/TLS配置,并且它可以简单地将请求转发到HTTP服务器。可以为此创建一个简单的自签名证书。
例如:
型
这段代码创建了一个HTTP服务器和一个HTTPS服务器,它们监听不同的端口(3004/8443)。HTTPS服务器正在使用
key.pem
和cert.pem
进行SSL/TLS配置。它处理HTTPS WebSocket升级请求,而HTTP服务器处理HTTP WebSocket升级请求。HTTPS服务器不会显式解码传入的安全WebSocket升级请求。相反,它侦听这些安全的WebSocket请求并代理它们,利用
node-http-proxy
library的能力来处理WebSocket连接。型
使用提供的SSL/TLS选项创建单独的HTTPS服务器。该服务器侦听“
upgrade
”事件,当客户端想要发起WebSocket连接时发生该事件。当接收到“
upgrade
“事件时,它调用proxy.ws()
,如node-http-proxy
/ UPGRADING中所示,并处理将WebSocket请求代理到创建代理时指定的目标的过程。在一些日志记录之后,似乎从未调用httpsServer的升级事件,只有来自httpServer的事件。
我在控制台中也看到了这个错误:
transport_websocket.js:32 WebSocket connection to 'ws://localhost:8443' failed
.因此,客户端试图建立一个到安全服务器(
localhost:8443
)的非安全WebSocket连接(ws://
)。这将不起作用,因为localhost:8443
服务器设置为仅接受安全WebSocket(wss://
)连接,因为存在SSL/TLS配置。您需要修改客户端以启动安全的WebSocket连接。如果客户端是Web浏览器,则在创建新的WebSocket连接时应使用
wss://localhost:8443
而不是ws://localhost:8443
。应根据连接是否需要安全使用正确的协议(
ws://
用于HTTP,wss://
用于HTTPS)。fafcakar2#
要正确处理HTTP和HTTPS请求的WebSocket代理,您需要修改Node.js代理服务器以支持WebSocket连接的SSL/TLS。您可以通过使用Node.js中的'http'和'https'模块来创建单独的HTTP和HTTPS服务器来实现这一点,每个服务器都有各自的配置。此外,您应该修改客户端WebSocket连接以使用正确的协议(对于HTTP为ws://,对于HTTPS为wss://)。
以下是Node.js代理服务器的更新代码:
字符串
代码中的更改:
1.代码现在使用'http'和'https'模块来创建单独的HTTP和HTTPS服务器。HTTPS服务器配置了SSL/TLS选项,允许它处理安全连接。
1.两个独立的代理示例
httpProxy
和httpsProxy
分别为HTTP和HTTPS服务器创建。httpsProxy
示例将secure: false
设置为允许代理非安全WebSocket服务器(HTTP)。1.在
startServer()
函数中,WebSocket升级事件现在由各自的服务器正确处理(httpServer
用于HTTP,httpsServer
用于HTTPS)。每个服务器使用其对应的代理示例来处理WebSocket连接。现在,在客户端,如果您使用的是Web浏览器,那么在连接到代理服务器时,您应该启动一个安全的WebSocket连接(wss://):
型
如果你需要通过不安全的WebSocket连接(ws://)连接到代理服务器,你可以这样做:
型
使用此更新的代码和正确的客户端WebSocket连接,您的Node.js代理服务器现在将能够处理安全(wss://)和不安全(ws://)WebSocket连接,并将它们正确转发到适当的目标(HTTP端点)。