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

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

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+款云产品!

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

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

最新文章

  1. ajaxpro返回值类型总结-DataTable(转)
  2. 3、软件评测师要阅读的书籍 - IT软件人员书籍系列文章
  3. canvas三角函数直线运动
  4. 深入浅出 - Android系统移植与平台开发(五)- 定制手机模拟器ROM
  5. JSON-SCHEMA
  6. PHP文本框的值随下拉框改变
  7. G面经prepare: Maximum Subsequence in Another String&#39;s Order
  8. liunx之zip格式的解压命令
  9. [置顶] MyEclipse下安装插件方法(properties文件编辑器Propedit为例)
  10. HTML用法小摘录
  11. 修改ORACLE的语言参数
  12. PLSQL Developer安装(Oracle11g+win7_64bit)
  13. jquery ajax请求成功,数据返回成功,seccess不执行的问题
  14. webStorm支持.wxml文件高亮显示
  15. Tacacs+认证详细调研
  16. 笔试题二(java面向对象、多线程、集合)
  17. spring boot2 基于百度云apiface实现人脸检测与认证1
  18. IOS UIApplication和AppDelegate 关系
  19. socketserver模块解析
  20. 海思平台交叉编译curl支持SSL功能

热门文章

  1. linux查找命令(find)
  2. 【ADO.NET】 使用通用数据库操作类Database (SQL Server)
  3. 【经验总结】OSG 安装配置
  4. iOS 应用程序内部国际化,不跟随系统语言
  5. ssm框架搭建(上)
  6. sql server 全部错误号详释
  7. 如何处理Docker的错误消息request canceled:Docker代理问题
  8. 【Conclusion】MySQL的安装和使用
  9. (转)编码剖析Spring管理Bean的原理
  10. (转载)RedHat Enterprise Linux 5 安装GCC