跳到主要内容

浏览器代理

· 阅读需 8 分钟

1、背景

以前我认为,只要开启代理,本地所有请求都会先经过代理再访问网络资源。直到这两天,用 Node.js 访问 Google 出现报错。

代码长这样:

const chalk = require('chalk');
const axios = require('axios');

const url = 'https://www.google.com';

axios
.get(url)
.then((a) => {
console.log(chalk.green(`Request ${url} finish, status: ${a.status}.`));
})
.catch((e) => {
console.log(chalk.red(`Request ${url} error!\nMessage: ${e.message}.`));
});

执行结果如下:

执行结果

此时我的 Chrome 却能正常访问 Google

正常访问Google

查阅资料后才发现其中的原因:浏览器会自动读取系统代理配置并使用,通常我们写的代码会直接访问网络资源而不会使用代理!

2、系统代理

浏览器会自动读取系统代理配置并使用

3、透明代理

TUN,拦截本地所有请求,不支持系统代理的软件也能被自动代理

查阅资料时还发现:代理自动配置竟然已经有30年的历史!

4、自动代理的由来

  • 20世纪90年代,代理设置还是一种重复性的体力劳动,饱受折磨的IT公司和程序员们决定改变这种现状
  • 1996年,网景为自家浏览器 Netscape Navigator 2.0 设计了一套代理自动配置格式(Proxy Auto Config,简称 PAC
  • 1999年,Microsoft、Sun(研发 Java 的公司,现被 Oracle 收购)、Inktomi、RealNetworks等公司共同起草了网络代理自动发现协议(Web Proxy Auto-Discovery Protocol,简称 WPAD
  • 1999年,WPADMicrosoft Internet Explorer 5.0 中被首次引入
  • WPAD 的互联网草案已于1999年12月到期,但时至今日主流浏览器仍然在使用 WPAD

5、自动代理的分类

现代浏览器实现了多种级别的代理设置,下面是三种常用的设置:

  • 手动代理配置/全局模式:为所有的请求规定一个主机名和端口作为代理,另外可以配置例外的域名列表(如 localhost 等),访问这个列表中的域名时不使用代理服务器
  • 代理自动配置(PAC):规定一个指向 PAC 文件的 URL,这个文件中包括一个选择代理服务器的 JavaScript 程序。这个方法适合复杂设置,可以针对不同网址配置不同的代理服务器,也可以指定某些网站使用或不使用代理服务器
  • 网络代理自发现协议(WPAD):浏览器通过 DHCPDNS 来搜索 PAC 文件

6、自动代理的工作流程

  • 手动代理配置

    • 用户手工配置系统代理的主机名和端口
    • 软件根据系统代理配置,将 HTTP 请求转发到对应的主机端口
  • PAC

    • 用户手工配置 PACURL
    • 系统按 PACURL 下载该配置文件
    • 软件根据 PAC 文件配置对所有 HTTP 请求进行分发
  • WPAD

    • 如果系统设置中 WPAD 被启用,则通过 DHCPDNS 查找 PAC 配置文件
    • 系统找到 PACURL 之后下载该配置文件
    • 软件根据 PAC 文件配置对所有 HTTP 请求进行分发

7、实际示例

代理配置

上图是我电脑的代理配置情况,可以看到左侧的 protocol 选框包含了上述三种类型代理设置,其中:

  • Automatic Proxy Configuration 对应 PAC
  • Auto Proxy Discovery 对应 WPAD
  • 其他都可以认为是 手动代理配置

我们更常用到的是手动代理配置,常见的代理软件(抓包软件)启动之后都会自动启用并配置 Web ProxySecure Web Proxy 以及 SOCKS Proxy,上图中 Web Proxy 的服务器和端口是 127.0.0.1:7890

接着打开浏览器,浏览器会先读取系统代理配置并在请求时使用该配置。然后访问 https://www.google.com/,发起的请求会被转发到 127.0.0.1:7890,代理软件会对请求做一些处理然后再转发到公网,当请求获得响应结果时再把该结果交回给浏览器。这样就使用代理完成了一次网络请求。

当关闭代理软件时,代理软件会禁用或还原系统代理设置。如果代理软件异常关闭,会导致系统代理仍然处于启用状态,这时候就可能会导致浏览器无法正常访问网络。

手动代理配置一般只影响个人主机,而 PACWPAD 则可以用于范围更广的局域网代理或开放代理。

说明:

  • 1、抓包软件和代理软件的原理类似,都是利用浏览器自动使用系统代理的特性进行请求拦截
  • 2、代理软件和抓包软件同时开启时,先启动的软件可能无法正常工作,因为后启动的软件会覆盖之前的代理配置,可以设置代理转发解决
  • 3、浏览器可以禁用代理服务,使用以下命令行 /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --no-proxy-server 启动浏览器之后,即使开启代理也无法访问 Google

8、升级代码:自动使用系统代理

咱们可以对文章开头的那段 Node.js 代码做个升级,简单实现一个自动使用代理的程序。具体代码如下:

const chalk = require('chalk');
const axios = require('axios');
const { execSync } = require('child_process');

function getProxyInfo() {
const out = execSync('networksetup -getwebproxy Wi-Fi', { encoding: 'utf8' });

console.log('====System Proxy Info====');
console.log(out);

const lines = out.split('\n');
const values = lines.map((l) => l.replace(/\s/g, '').toLowerCase().split(':'));
const proxyInfo = values.reduce((a, [key, val]) => {
a[key] = val;
return a;
}, {});

return proxyInfo.enabled === 'yes' ? { host: proxyInfo.server, port: +proxyInfo.port } : null;
}

const proxy = getProxyInfo();
const url = 'https://www.google.com';

axios
.get(url, { proxy })
.then((a) => {
console.log(chalk.green(`Request ${url} finish, status: ${a.status}.`));
})
.catch((e) => {
console.log(chalk.red(`Request ${url} error!\nMessage: ${e.message}.`));
});

这段代码的主要修改点是新增了一个 getProxyInfo 函数,该函数自动读取系统的代理配置,在请求网络资源时使用该配置。

执行结果如下:

执行结果

9、参考