【问题标题】:Custom JavaFX WebView Protocol Handler自定义 JavaFX WebView 协议处理程序
【发布时间】:2013-07-08 08:45:17
【问题描述】:

我正在尝试为使用 webview 访问单个网站的 JavaFX 应用程序编写自己的协议处理程序。到目前为止我做了什么

我的自定义 URLStreamHandlerFactory

public class MyURLStreamHandlerFactory implements URLStreamHandlerFactory {

    public URLStreamHandler createURLStreamHandler(String protocol) {
        System.out.println("Protocol: " + protocol);
        if (protocol.equalsIgnoreCase("http") || protocol.equalsIgnoreCase("https")) {
           return new MyURLStreamHandler();
        } else {
            return new URLStreamHandler() {
                @Override
               protected URLConnection openConnection(URL u) throws IOException {
                    return new URLConnection(u) {
                        @Override
                        public void connect() throws IOException {
                        }
                    };
                }
            };
        }
    }
 }

我的自定义 URLStreamHandler

public class MyURLStreamHandler extends java.net.URLStreamHandler{

    protected HttpURLConnection openConnection(URL u){
        MyURLConnection q = new MyURLConnection(u);
        return q;
    }    
}

我的自定义 HttpURLConnection

public class MyURLConnection extends HttpURLConnection {

    static int defaultPort = 443;
    InputStream in;
    OutputStream out;
    Socket s;

    publicMyURLConnection(URL url) {
        super(url);
        try {
            setRequestMethod("POST");
        } catch (ProtocolException ex) {
            ex.printStackTrace();
        }
    }

    public void setRequestProperty(String name, String value){
        super.setRequestProperty(name, value);
        System.out.println("Namee: " + name);
        System.out.println("Value: " + value);
    }

    public String getRequestProperty(String name){
        System.out.println("GET REQUEST: ");
        return super.getRequestProperty(name);
    }

    public OutputStream getOutputStream() throws IOException {
        OutputStream os = super.getOutputStream();
        System.out.println("Output: " + os);
        return os;
    }

    public InputStream getInputStream() throws IOException {
        InputStream is = super.getInputStream();
        System.out.println("INout stream: " + is);
       return is;
    }

    @Override
    public void connect() throws IOException {
    }

    @Override
    public void disconnect() {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public boolean usingProxy() {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

当我运行应用程序时,我收到以下错误,尽管它似乎设置了一些标题

Jul 08, 2013 11:09:04 AM com.sun.webpane.webkit.network.URLLoader doRun
WARNING: Unexpected error
java.net.UnknownServiceException: protocol doesn't support input
at java.net.URLConnection.getInputStream(URLConnection.java:839)
at qmed.QMedURLConnection.getInputStream(MyURLConnection.java:67)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:468)
at com.sun.webpane.webkit.network.URLLoader.receiveResponse(URLLoader.java:383)
at com.sun.webpane.webkit.network.URLLoader.doRun(URLLoader.java:142)
at com.sun.webpane.webkit.network.URLLoader.access$000(URLLoader.java:44)
at com.sun.webpane.webkit.network.URLLoader$1.run(URLLoader.java:106)
at com.sun.webpane.webkit.network.URLLoader$1.run(URLLoader.java:103)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.webpane.webkit.network.URLLoader.run(URLLoader.java:103)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)

我要做的就是获取给定请求的响应并读取其二进制数据。我希望协议的行为与默认协议完全相同,并且只检查给定响应的二进制数据。我究竟做错了什么?

应用程序正在做所有的 URLConnections 短。当协议为 http 或 https 时使用 HTTPURLConnection 作为我的自定义 URLConnection 类并在使用其他协议时启动默认 URLStreamHandler 是否正确,就像我在 MyURLStreamHandlerFactory 中所做的那样?我应该只扩展 MYURLConnection 中的默认 URLConnection 类来处理所有协议吗?

任何帮助将不胜感激,因为这是一个威胁项目的问题

谢谢

【问题讨论】:

    标签: webview javafx urlconnection


    【解决方案1】:

    您可能缺少的只是setDoInput(true) 或覆盖getDoInput() 并返回true(这就是我所做的)。

    如果这无助于检查我的工作解决方案:

    MyURLStreamHandlerFactory:

    import java.net.URLStreamHandler;
    import java.net.URLStreamHandlerFactory;
    
    public class MyURLStreamHandlerFactory implements URLStreamHandlerFactory
    {
    
        public URLStreamHandler createURLStreamHandler(String protocol)
        {
            if (protocol.equals("myapp"))
            {
                return new MyURLHandler();
            }
            return null;
        }
    
    }
    

    注册工厂:

    URL.setURLStreamHandlerFactory(new MyURLStreamHandlerFactory());
    

    MyURLHandler :

    import java.io.IOException;
    import java.net.URL;
    import java.net.URLConnection;
    import java.net.URLStreamHandler;
    
    public class MyURLHandler extends URLStreamHandler
    {
    
        @Override
        protected URLConnection openConnection(URL url) throws IOException
        {
            return new MyURLConnection(url);
        }
    
    }
    

    MyURLConnection:

    import java.io.*;
    import java.net.SocketTimeoutException;
    import java.net.URL;
    import java.net.URLConnection;
    
    /**
     * Register a protocol handler for URLs like this: <code>myapp:///pics/sland.gif</code><br>
     */
    public class MyURLConnection extends URLConnection
    {
    
        private byte[] data;
    
        @Override
        public void connect() throws IOException
        {
            if (connected)
            {
                return;
            }
            loadImage();
            connected = true;
        }
    
        public String getHeaderField(String name)
        {
            if ("Content-Type".equalsIgnoreCase(name))
            {
                return getContentType();
            }
            else if ("Content-Length".equalsIgnoreCase(name))
            {
                return "" + getContentLength();
            }
            return null;
        }
    
        public String getContentType()
        {
            String fileName = getURL().getFile();
            String ext = fileName.substring(fileName.lastIndexOf('.'));
            return "image/" + ext; // TODO: switch based on file-type
        }
    
        public int getContentLength()
        {
            return data.length;
        }
    
        public long getContentLengthLong()
        {
            return data.length;
        }
    
        public boolean getDoInput()
        {
            return true;
        }
    
        public InputStream getInputStream() throws IOException
        {
            connect();
            return new ByteArrayInputStream(data);
        }
    
        private void loadImage() throws IOException
        {
            if (data != null)
            {
                return;
            }
            try
            {
                int timeout = this.getConnectTimeout();
                long start = System.currentTimeMillis();
                URL url = getURL();
    
                String imgPath = url.toExternalForm();
                imgPath = imgPath.startsWith("myapp://") ? imgPath.substring("myapp://".length()) : imgPath.substring("myapp:".length()); // attention: triple '/' is reduced to a single '/'
    
                // this is my own asynchronous image implementation
                // instead of this part (including the following loop) you could do your own (synchronous) loading logic
                MyImage img = MyApp.getImage(imgPath);
                do
                {
                    if (img.isFailed())
                    {
                        throw new IOException("Could not load image: " + getURL());
                    }
                    else if (!img.hasData())
                    {
                        long now = System.currentTimeMillis();
                        if (now - start > timeout)
                        {
                            throw new SocketTimeoutException();
                        }
                        Thread.sleep(100);
                    }
                } while (!img.hasData());
                data = img.getData();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    
        public OutputStream getOutputStream() throws IOException
        {
            // this might be unnecessary - the whole method can probably be omitted for our purposes
            return new ByteArrayOutputStream();
        }
    
        public java.security.Permission getPermission() throws IOException
        {
            return null; // we need no permissions to access this URL
        }
    
    }
    

    MyURLConnection 的某些部分可能不需要它工作,但像这样它对我有用。

    在 JavaFX WebView 中的使用:

    <img src="myapp:///pics/image.png"/>
    

    关于权限的说明:

    我使用带有 AllPermissions 的小程序对上面的代码进行了测试。

    沙盒-Applet 中这不起作用,因为缺少 setFactory 权限。

    【讨论】:

    • 很好的答案。到目前为止,我发现它最接近我的需求。但我想要的是捕获所有协议。 HTTP,HTTPS,file:// 就像我们可以在开发者控制台中看到所有请求一样。
    • 你想用这个实现什么?也许网络嗅探器/分析器工具可以满足您的需求。
    • 另一件可能有帮助的事情是使用 FirebugLite:&lt;script type='text/javascript' src='http://www.getfirebug.com/firebug-lite.js#startOpened'&gt;&lt;/script&gt; - 将此代码嵌入到您的 WebView 页面中,您可以在 WebView 中调试页面。不幸的是,FBLite 没有网络标签。
    • 要捕获所有协议,您只需调整 createURLStreamHandler 方法以始终返回您的处理程序。但问题是你用它做什么?是否仍要执行请求?还是只是捕捉它?我猜你还想执行它。因此,问题可能是弄清楚如何为您拦截的每个协议执行此操作。也许有一种方法可以获取每个原始默认工厂并将其委托。并简单地包装原始的 URLConnection 来截取数据。
    • 我只想捕获所有请求。不想使用 selenium 等其他插件或手动执行操作。在 android, awesomium 中拦截所有请求非常容易,但我需要在 java 中。
    【解决方案2】:

    这与提出的问题没有直接关系,但可能会使问题本身过时。

    借助 Java SE 6 Update 10,Java 小程序支持访问任何域和端口上的资源,该域和端口已正确设置使用 crossdomain.xml

    因此,注册您自己的协议的原因可能会过时,因为您可以访问所需的所有资源。

    另一个想法是:如果您正在尝试创建一种网络嗅探器,为什么不直接使用为此类任务设计的网络嗅探器/分析器程序?

    【讨论】:

      【解决方案3】:

      通过在 Java 控制面板中激活日志记录和跟踪,您的 Java 控制台将打印所有尝试和执行的网络调用,包括来自 WebView 的那些。

      您可以查看所有 HTTP 和 HTTPS 调用及其返回码 + cookie 数据。 您可能还会看到其他协议连接,但可能看不到通过它们发送的任何数据。

      这适用于浏览器中的小程序。 如果您在不同的上下文中需要它,也许有一种方法可以通过传递命令行参数来激活相同的选项。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-28
        • 2011-10-28
        • 1970-01-01
        • 2011-01-30
        • 2011-09-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多