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,它们仅在传输层(两个跃点之间)才有意义。

    PB (P-B) 建立 TCP 连接 PB 发送 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 代理的主要内容,如果未能解决你的问题,请参考以下文章

用于 Rabbitmq Stomp 的 java websocket 客户端

015_NGINX作为WebSocket Proxy的设置

Android websock 应用

java http可以websocket不行

websock第一次连不上window

如何忽略所有与 mitmproxy 的 websocket 连接?