为什么Tomcat websockets不支持JVM范围的HTTP代理设置?

时间:2016-04-29 14:53:40

标签: java tomcat websocket http-proxy

(这是Java 8和Tomcat 8)

我有一个简单的websocket客户端测试应用程序,我试图针对squid代理进行测试。我正在使用http[s].proxy[Host|Port]系统属性在JVM范围内设置代理信息。

当我使用Tyrus实现(tyrus-standalone-client-1.12.jar)时,代理系统属性受到尊重(当然,我遇到wss URL不起作用的问题,但这是一个不同的问题)。

但是,当我使用Tomcat实现(tomcat-websocket.jar)时,系统代理设置会根据经验被忽略(我正在使用网络嗅探器,我看到数据包不会进入代理,而使用Tyrus我确实看到了发往代理的数据包。)

我查看了org.apache.tomcat.websocket.WsWebSocketContainer.connectToServer()的来源,看起来好像是在拿起代理信息并使用它:

    // Check to see if a proxy is configured. Javadoc indicates return value
    // will never be null
    List<Proxy> proxies = ProxySelector.getDefault().select(proxyPath);
    Proxy selectedProxy = null;
    for (Proxy proxy : proxies) {
        if (proxy.type().equals(Proxy.Type.HTTP)) {
            sa = proxy.address();
            if (sa instanceof InetSocketAddress) {
                InetSocketAddress inet = (InetSocketAddress) sa;
                if (inet.isUnresolved()) {
                    sa = new InetSocketAddress(inet.getHostName(), inet.getPort());
                }
            }
            selectedProxy = proxy;
            break;
        }
    }

我将查找代理的代码片段放入我的测试程序中以查看它发现的内容,正如您将看到的,它会获取代理信息。所以我不知道为什么Tomcat websockets不尊重这些属性。

我的测试应用:

import javax.websocket.*;

import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

/**
 * This class is adapted from http://stackoverflow.com/a/26454417/411393 which
 * is an answer to http://stackoverflow.com/questions/26452903/javax-websocket-client-simple-example/
 */
public class WebsocketTestApp
{
    public static void main(String[] args) {
        try {
            // Determine where to connect
            final URI servicePath;
            if ((args.length != 0) && "secure".equals(args[0])) {
                // servicePath = new URI("wss://real.okcoin.cn:10440/websocket/okcoinapi");
                servicePath = new URI("wss://echo.websocket.org/");
            }
            else {
                servicePath = new URI("ws://echo.websocket.org/");
            }

            // See what Java thinks the proxy for the service is.
            URI proxyPath = buildProxyPath(servicePath);
            System.out.println("service path: " + servicePath);
            System.out.println("proxy path:   " + proxyPath);

            // This line is copied from the source to org.apache.tomcat.websocket.WsWebSocketContainer (approx line 254)
            List<Proxy> proxies = ProxySelector.getDefault().select(proxyPath);
            for (Proxy proxy : proxies) {
                System.out.println(proxy.toString());
            }

            // open websocket
            final WebsocketClientEndpoint clientEndPoint = new WebsocketClientEndpoint(servicePath);

            // add listener
            clientEndPoint.addMessageHandler(System.out::println);

            // send message to websocket
            clientEndPoint.sendMessage("{'event':'addChannel','channel':'ok_btccny_ticker'}");

            // wait 2 seconds for messages from websocket
            Thread.sleep(2000);
        }
        catch (InterruptedException ex) {
            System.err.println("InterruptedException exception: " + ex.getMessage());
        }
        catch (URISyntaxException ex) {
            System.err.println("URISyntaxException exception: " + ex.getMessage());
        }
    }

    // This is essentially copied from the source to org.apache.tomcat.websocket.WsWebSocketContainer (approx lines 230-241)
    private static URI buildProxyPath(URI path) {
        URI proxyPath;

        // Validate scheme (and build proxyPath)
        String scheme = path.getScheme();
        if ("ws".equalsIgnoreCase(scheme)) {
            proxyPath = URI.create("http" + path.toString().substring(2));
        }
        else if ("wss".equalsIgnoreCase(scheme)) {
            proxyPath = URI.create("https" + path.toString().substring(3));
        }
        else {
            throw new IllegalArgumentException("wsWebSocketContainer.pathWrongScheme: " + scheme);
        }

        return proxyPath;
    }

    @ClientEndpoint
    public static class WebsocketClientEndpoint
    {

        Session userSession = null;
        private MessageHandler messageHandler;

        public WebsocketClientEndpoint(URI endpointURI) {
            try {
                WebSocketContainer container = ContainerProvider.getWebSocketContainer();
                //((ClientManager)container).getProperties().put(ClientProperties.PROXY_URI, "http://172.16.99.15:3128");
                container.connectToServer(this, endpointURI);
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        /**
         * Callback hook for Connection open events.
         *
         * @param userSession the userSession which is opened.
         */
        @OnOpen
        public void onOpen(Session userSession) {
            System.out.println("opening websocket");
            this.userSession = userSession;
        }

        /**
         * Callback hook for Connection close events.
         *
         * @param userSession the userSession which is getting closed.
         * @param reason      the reason for connection close
         */
        @OnClose
        public void onClose(Session userSession, CloseReason reason) {
            System.out.println("closing websocket");
            this.userSession = null;
        }

        /**
         * Callback hook for Message Events. This method will be invoked when a client send a message.
         *
         * @param message The text message
         */
        @OnMessage
        public void onMessage(String message) {
            if (this.messageHandler != null) {
                this.messageHandler.handleMessage(message);
            }
        }

        /**
         * register message handler
         *
         * @param msgHandler
         */
        public void addMessageHandler(MessageHandler msgHandler) {
            this.messageHandler = msgHandler;
        }

        /**
         * Send a message.
         *
         * @param message
         */
        public void sendMessage(String message) {
            this.userSession.getAsyncRemote().sendText(message);
        }

        /**
         * Message handler.
         */
        @FunctionalInterface
        public static interface MessageHandler
        {
            public void handleMessage(String message);
        }
    }
}

当我运行它时,输出是:

service path: ws://echo.websocket.org/
proxy path:   http://echo.websocket.org/
HTTP @ 172.16.99.15:3128
opening websocket
{'event':'addChannel','channel':'ok_btccny_ticker'}

因此它使用WsWebSocketContainer.connectToServer()正在使用的相同查找代码打印出正确的代理信息,但代理信息不受尊重,而是直接连接。

这是Tomcat实现中的一些奇怪的错误吗?已知Tomcat实现是否忽略代理属性设置?

更新

我还将代码放入快速&amp;运行Tomcat中的脏servlet也会发生同样的事情。虽然Tomcat总体上似乎遵守设置(在Tomcat中调用java.net.URL.openStream()来表示设置),但websocket的内容再次拒绝遵守这些设置。所以无论是什么造成这种情况,代码都是独立运行而不是在Tomcat容器中运行。

1 个答案:

答案 0 :(得分:0)

原因是我是个白痴。

我正在运行Tomcat 8.0.23(这是我的外部要求)。

但是,我忘了并将源代码下载到8.0。 33

我已经回去并将源代码下载到8.0.23了,而我在我的问题中讨论的代理查找代码似乎并不存在。