Jetty WebSocket 代理
Posted
技术标签:
【中文标题】Jetty WebSocket 代理【英文标题】:Jetty WebSocket proxying 【发布时间】:2014-03-07 23:07:59 【问题描述】:只是想知道是否有人尝试过使用嵌入式 Jetty 进行 WebSocket 代理(用于透明代理)?
在使用 Jetty 9.1.2.v20140210 大约一天半之后,我只能说它无法代理当前形式的 WebSockets,并且添加这样的支持是一项不平凡的任务(至少 afact) .
基本上,Jetty ProxyServlet 会去掉“升级”和“连接”标头字段,无论它是否来自 WebSocket 握手请求。添加这些字段很容易,如下所示。但是,当代理服务器返回带有 HTTP 代码 101(切换协议)的响应时,代理服务器上不会进行任何协议升级。因此,当第一个 WebSocket 数据包到达时,HttpParser 会阻塞并将其视为错误的 HTTP 请求。
如果有人已经有解决方案或熟悉 Jetty 建议尝试什么,我们将不胜感激。
下面是我实验中去掉不重要部分的代码:
public class ProxyServer
public static void main(String[] args) throws Exception
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8888);
server.addConnector(connector);
// Setup proxy handler to handle CONNECT methods
ConnectHandler proxy = new ConnectHandler();
server.setHandler(proxy);
// Setup proxy servlet
ServletContextHandler context = new ServletContextHandler(proxy, "/", ServletContextHandler.SESSIONS);
ServletHolder proxyServlet = new ServletHolder(MyProxyServlet.class);
context.addServlet(proxyServlet, "/*");
server.start();
@SuppressWarnings("serial")
public class MyProxyServlet extends ProxyServlet
@Override
protected void customizeProxyRequest(Request proxyRequest, HttpServletRequest request)
// Pass through the upgrade and connection header fields for websocket handshake request.
String upgradeValue = request.getHeader("Upgrade");
if (upgradeValue != null && upgradeValue.compareToIgnoreCase("websocket") == 0)
setHeader(proxyRequest, "Upgrade", upgradeValue);
setHeader(proxyRequest, "Connection", request.getHeader("Connection"));
@Override
protected void onResponseHeaders(HttpServletRequest request, HttpServletResponse response, Response proxyResponse)
super.onResponseHeaders(request, response, proxyResponse);
// Restore the upgrade and connection header fields for websocket handshake request.
HttpFields fields = proxyResponse.getHeaders();
for (HttpField field : fields)
if (field.getName().compareToIgnoreCase("Upgrade") == 0)
String upgradeValue = field.getValue();
if (upgradeValue != null && upgradeValue.compareToIgnoreCase("websocket") == 0)
response.setHeader(field.getName(), upgradeValue);
for (HttpField searchField : fields)
if (searchField.getName().compareToIgnoreCase("Connection") == 0)
response.setHeader(searchField.getName(), searchField.getValue());
【问题讨论】:
【参考方案1】:让我们想象一下您正在尝试构建的代理方案,我们有客户端 A、服务器 B 和代理 P。现在让我们来看看连接工作流程:
-
A 与代理 P (A-P) 建立 TCP 连接
A 通过 WebSocket 握手发送 CONNECT addr(B) 请求
这里有第一个问题,HTTP RFCWS 握手中使用的标头不是端到端标头,因为对于 HTTP,它们仅在传输层(两个跃点之间)才有意义。
-
P 与 B (P-B) 建立 TCP 连接
P 向 B 发送 WS 握手 HTTP 请求
B 响应 HTTP->WS 升级(通过发送 101)
这是另一个问题,在发送 HTTP 101 之后,服务器 B 和客户端 A 现在只能通过 TCP 进行通信,但 jetty servlet 不支持普通 TCP 数据包传播.换句话说,jetty 代理 servlet 一直等到客户端 A 开始传输 HTTP 请求,而这在 A 收到 HTTP 101 后将永远不会发生。
您需要使用 WS 服务器和 WS 客户端自行实现。
【讨论】:
我已经尝试使用嵌入式 tomcat 并且它正在工作。我在我的项目中使用了它。以上是关于Jetty WebSocket 代理的主要内容,如果未能解决你的问题,请参考以下文章