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

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

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

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

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

CEF与代理的更多相关文章

  1. 使用CEF(二)— 基于VS2019编写一个简单CEF样例

    使用CEF(二)- 基于VS2019编写一个简单CEF样例 在这一节中,本人将会在Windows下使用VS2019创建一个空白的C++Windows Desktop Application项目,逐步进 ...

  2. KVM halt-polling机制分析

    本文由作者朱益军授权网易云社区发布. 简介 在实际业务中,guest执行HLT指令是导致虚拟化overhead的一个重要原因.如[1]. KVM halt polling特性就是为了解决这一个问题被引 ...

  3. 使用C#在CEF中拦截并响应请求

    一.前言 忙里偷闲,研究了一下如何在CEF中拦截请求,并作出响应.这个功能对某些需要修改服务器响应的需求来说必不可少,可以直接读取本地文件作为响应内容. C#的CEF封装项目有很多,我使用的是Chro ...

  4. Cef 架构

    cef支持各种语言和多种操作系统.在设计的时候充分考虑了性能和易用性.cef核心功能提供了c和c++的接口.cef提供了和主程序之间的通信能力(利用 custom plugins, protocols ...

  5. 初识CEF

    介绍 CEF全称Chromium Embedded Framework,是一个基于Google Chromium 的开源项目.Google Chromium项目主要是为Google Chrome应用开 ...

  6. 刨根究底字符编码之十——Unicode字符集的字符编码方式CEF

    Unicode字符集的字符编码方式CEF 一.字符编码方式CEF的选择 1. 由于Unicode字符集非常大,有些字符的编号(码点值)需要两个或两个以上字节来表示,而要对这样的编号进行编码,也必须使用 ...

  7. CEF解决加载慢问题

    转载:http://blog.csdn.net/weolar/article/details/51994895 CEF加载慢的时候,加上以下代码,通过命令行的方式: CefRefPtr<CefC ...

  8. webrower + CEF

    理解WebKit和Chromium: Content API和CEF3 标签:               apiAPIAPibrowserchromeChromehtml5HTML5Html5web ...

  9. CEF General Usage(CEF3预览)

    CEF General Usage(CEF3预览) 介绍 CEF全称Chromium Embedded Framework,是一个基于Google Chromium 的开源项目.Google Chro ...

随机推荐

  1. 大数模板 (C ++)

    上次BC遇到一个大数题目,没有大数模板和不会使用JAVA的同学们GG了,赛后从队友哪里骗出大数模板.2333333,真的炒鸡nice(就是有点长),贴出来分享一下好辣. //可以处理字符串前导零 #i ...

  2. 【模板】c++动态数组vector

    相信大家都知道$C$++里有一个流弊的$STL$模板库.. 今天我们就要谈一谈这里面的一个容器:动态数组$vector$. $vector$实际上类似于$a[]$这个东西,也就是说它重载了$[]$运算 ...

  3. (转) ACM必备(学完一个就加亮一个)不多,就这些!

    时间复杂度(渐近时间复杂度的严格定义,NP问题,时间复杂度的分析方法,主定理)排序算法(平方排序算法的应用,Shell排序,快速排序,归并排序,时间复杂度下界,三种线性时间排  序,外部排序)数论(整 ...

  4. 转-eclipse管理多个workspace

    Eclipse作为Java开发中最常用的开发工具,大家都很熟悉了,但是,当你做过很多项目后你会发现你的eclipse的package explorer视图下显示的project超级多,这时你可能会关闭 ...

  5. 155 Min Stack 最小栈

    设计一个支持 push,pop,top 操作,并能在常量时间内检索最小元素的栈.    push(x) -- 将元素x推入栈中.    pop() -- 删除栈顶的元素.    top() -- 获取 ...

  6. MySQL与RAID

    RAID在mysq中适用场景 raid0:由于性能高和成本低,以及基本没有数据恢复的能力,而且它比单片磁盘损坏的概率要高.建议只在不担心数据丢失的情况下使用,如备库(slave)或者某些原因" ...

  7. [ AHOI 2013 ] 作业 & [ BZOJ 3809 ] Gty的二逼妹子序列

    \(\\\) Description 给出一个长为 \(n\) 的数列 \(A\) 和 \(k\),多次询问: 对于一个区间 \([L_i,R_i]\),问区间内有多少个数在 \([a_i,b_i]\ ...

  8. canvas画饼图

    <style> body {    background: black;    text-align: center; } #cans {    background: white; } ...

  9. 【Jenkins】Jenkins配置从节点,实现远程主机调用功能

    一.需求 使用Jenkins进行持续集成部署过程中,需要用到远端主机的处理功能.如部署到远程主机.文件备份等功能 二.思路 1.当远端主机为Linux系统时使用Publish Over SSH Plu ...

  10. 数据库连接池proxool的两种使用方式

    数据库连接池可以通过两种方式来应用,针对web应用和非web应用而来. 非web应用的数据库连接池配置 第一种方式:工厂类 非web应用可以使用工厂模式将数据库连接创建封装在一个工具类中,工具类中又使 ...