zoukankan      html  css  js  c++  java
  • CEF与代理

    此文已由作者王荣涛授权网易云社区发布。

    欢迎访问网易云社区,了解更多网易技术产品运营经验。


    CEF(Chromium Embedded Framework)如今已经广泛被应用于客户端软件,网易内部就有网易云音乐有道云笔记等重要产品在深度使用。有时候我们需要让CEF在加载页面的时候走代理,比如科学上网,本文就以C++版的CEF为例,对CEF如何实现代理浏览作下简单介绍。


    Chromium的代理

    众所周知,CEF和Google Chrome都基于开源的Chromium浏览器项目。


    固定代理

    在Chromium中,你可以在启动时加入

    --proxy-server="myproxy:808"

    --proxy-server="http://myproxy:808"

    开关用以显式告诉它走myproxy这台主机上808端口的HTTP代理(如果装了SwitchyOmega之类的代理控制软件,这种方式将不一定会按预期工作)。除此之外,Chromium还支持SOCKS4

    --proxy-server="socks4://myproxy:1080"

    以及SOCKS5方式的代理:

    --proxy-server="socks://myproxy:1080"--proxy-server="socks5://myproxy:1080"

    以及按不同URL类型指定不同的代理方式:

    --proxy-server="https=http://myproxy:808;http=socks://myproxy:1080"

    除此之外,结合--proxy-server开关,还可以使用--proxy-bypass-list开关来讲某些指定站点或者IP排除在使用代理的名单之外,例如:

    --proxy-server="myproxy:808" --proxy-bypass-list="127.0.0.1;*.netease.com"


    PAC

    PAC(Proxy auto-config)文件本质上是一个包含FindProxyForURL函数的JS脚本,浏览器根据这个函数的返回结果来决定某个URL执行何种代理方式,一个例子如下:

    function FindProxyForURL(url, host) {    if (isInNet(host, "10.240.0.0", "255.255.0.0"))        return "DIRECT";    return "SOCKS myproxy:1080";
    }

    Chromium支持PAC文件,开关如下:

    --proxy-pac-url=URL

    其中URL是你存放PAC文件的地方。大家熟悉的GoAgent等翻墙利器就只带这个文件,有兴趣的不妨可以翻出来看看。


    其他开关

    强制不使用代理

    --no-proxy-server

    自动探测代理

    --proxy-auto-detect


    CEF全局代理

    CEF当然也支持Chromium方式的代理,我们只要选择但不限于以下二者之一:

    1、在CEF初始化的时候将以上开关作为参数传入CefInitialize、CefExecuteProcess等之中;

    2、实现自己的CefApp派生类,然后重载OnBeforeCommandLineProcessing方法:

    class MyCefApp final : public CefApp {public:    void OnBeforeCommandLineProcessing(        const CefString& process_type,
            CefRefPtr<CefCommandLine> command_line) {        if (process_type.empty()) {
                command_line->AppendSwitch("--proxy-server="myproxy:808"");
            }
        }private:
        IMPLEMENT_REFCOUNTING(MyCefApp)
    };

    但是以上方式都是一次性的,也就是你只有一次机会对命令行进行修改,之后所有请求都遵循这种代理方式,这种方式我们暂且称为全局代理。

    全局代理的方式有很多局限性,比如有时候我只想对某个页面的内容使用代理,这种就完全不适用了,因为第一我们需要重启CEF,第二一旦设置了代理将会影响所有页面。


    CEF任意请求代理

    幸运的是,最新版(准确说是2015年10月7日之后)的CEF加入另一种更加灵活的方式,即任意请求代理。

    这种方式的原理是在进行每次请求的时候CEF给应用一次机会让应用可以修改请求相关的参数。要实现这一点,我们需要自己的CefRequestHandler,然后重载OnBeforeBrowse和GetAuthCredentials(不一定需要)两个方法,定义如下:

    class MyRequestHandler final : public CefRequestHandler {public:    
        virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
                                    CefRefPtr<CefFrame> frame,
                                    CefRefPtr<CefRequest> request,                                bool is_redirect);    // 可选,根据是否需要认证
        virtual bool GetAuthCredentials(CefRefPtr<CefBrowser> browser,
                                        CefRefPtr<CefFrame> frame,                                    bool isProxy,                                    const CefString& host,                                    int port,                                    const CefString& realm,                                    const CefString& scheme,
                                        CefRefPtr<CefAuthCallback> callback);private:    
        IMPLEMENT_REFCOUNTING(MyRequestHandler);
    };

    具体实现示例:

    bool MyRequestHandler::OnBeforeBrowse(
        CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefRequest> request,
        bool is_redirect) {
        CefRefPtr<CefRequestContext> context =
            browser->GetHost()->GetRequestContext();
    
        CefString error;
        CefRefPtr<CefDictionaryValue> dict = CefDictionaryValue::Create();
        dict->SetString("mode", "fixed_servers");
        dict->SetString("server", "myproxy:808");
        CefRefPtr<CefValue> value = CefValue::Create();
        value->SetDictionary(dict);
    
        context->SetPreference("proxy", value, error);    return false;
    }

    其中mode可以取值以下任一:

    fixed_servers

    pac_script

    auto_detect

    system

    direct


    而当mode为fixed_servers时需要指定server参数,当mode为pac_script时需要指定pac_url参数。

    如果代理需要认证,那么需要同时实现:

    bool MyRequestHandler::GetAuthCredentials(
        CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,    bool isProxy,    const CefString& host,    int port,    const CefString& realm,    const CefString& scheme,
        CefRefPtr<CefAuthCallback> callback) {    if (isProxy) {
            callback->Continue("myuser", "mypass");        return true;
        }    return false;
    }

    题外话

    CefRequestHandler::OnBeforeBrowse中可以干的还有很多,比如控制拼写检查等,大家可以好好去发掘。


    网易云免费体验馆,0成本体验20+款云产品!

    更多网易技术、产品、运营经验分享请点击


    相关文章:
    【推荐】 微服务化的基石——持续集成
    【推荐】 一招搞定短信验证码服务不稳定

  • 相关阅读:
    Linux-netstat
    API接口防止参数篡改和重放攻击
    Java中遍历Map的几种方式
    Java泛型中的标记符含义
    Iterator 和 for...of 循环
    Promise 对象
    Reflect
    正则要求密码长度最少12位,包含至少1个特殊字符,2个数字,2个大写字母和一些小写字母。
    一个JS正则表达式,一个正实数,整数部分最多11位 小数部分最多 8位
    java阿里云短信发送配置
  • 原文地址:https://www.cnblogs.com/zyfd/p/9803177.html
Copyright © 2011-2022 走看看