代理自動配置 (PAC) 檔案
代理自動配置 (PAC) 檔案是一個 JavaScript 函式,用於確定 Web 瀏覽器請求(HTTP、HTTPS 和 FTP)是直接傳送到目的地,還是轉發到 Web 代理伺服器。PAC 檔案中包含的 JavaScript 函式定義了該函式
語法
function FindProxyForURL(url, host) {
// …
}
引數
url-
正在訪問的 URL。
https://URL 的路徑和查詢元件被剝離。在 Chrome(版本 52 到 73)中,您可以透過在策略中將PacHttpsUrlStrippingEnabled設定為false或使用--unsafe-pac-url命令列標誌啟動來停用此功能(在 Chrome 74 中,只有該標誌有效;從 75 開始,無法停用路徑剝離;截至 Chrome 81,路徑剝離不適用於 HTTP URL,但有興趣更改此行為以匹配 HTTPS);在 Firefox 中,首選項為network.proxy.autoconfig_url.include_path。 主機-
從 URL 中提取的主機名。這僅為了方便;它與
://和其後的第一個:或/之間的字串相同。埠號不包含在此引數中。必要時可以從 URL 中提取。
描述
返回一個描述配置的字串。此字串的格式在下面的返回值格式中定義。
返回值格式
- JavaScript 函式返回一個字串
- 如果字串為 null,則不應使用代理
- 字串可以包含任意數量的以下構建塊,以分號分隔
DIRECT-
連線應直接進行,不使用任何代理
PROXY 主機:埠-
應使用指定的代理
SOCKS 主機:埠-
應使用指定的 SOCKS 伺服器
Firefox 的最新版本也支援
HTTP 主機:埠-
應使用指定的代理
HTTPS 主機:埠-
應使用指定的 HTTPS 代理
SOCKS4 主機:埠,SOCKS5 主機:埠-
應使用指定的 SOCKS 伺服器(具有指定的 SOCKS 版本)
如果存在多個以分號分隔的設定,則使用最左側的設定,直到 Firefox 無法與代理建立連線。在這種情況下,將使用下一個值,依此類推。
瀏覽器將在 30 分鐘後自動重試以前無響應的代理。後續嘗試將從一小時開始,每次嘗試之間的時間間隔始終增加 30 分鐘。
如果所有代理都已關閉,並且未指定 DIRECT 選項,瀏覽器將詢問是否應暫時忽略代理並嘗試直接連線。20 分鐘後,瀏覽器將詢問是否應重試代理,並在另外 40 分鐘後再次詢問。查詢將繼續,每次查詢之間的時間間隔始終增加 20 分鐘。
示例
PROXY w3proxy.netscape.com:8080; PROXY mozilla.netscape.com:8081-
主代理是 w3proxy:8080;如果它出現故障,則開始使用 mozilla:8081,直到主代理再次恢復。
PROXY w3proxy.netscape.com:8080; PROXY mozilla.netscape.com:8081; DIRECT-
與上述相同,但如果兩個代理都出現故障,則自動開始直接連線。(在上面的第一個示例中,Netscape 會詢問使用者確認是否進行直接連線;在這種情況下,不需要使用者干預。)
PROXY w3proxy.netscape.com:8080; SOCKS socks:1080-
如果主代理出現故障,則使用 SOCKS。
自動配置檔案應儲存為 .pac 檔名副檔名的檔案:proxy.pac。
MIME 型別應設定為 application/x-ns-proxy-autoconfig。
接下來,您應該配置伺服器以將 .pac 檔名副檔名對映到 MIME 型別。
備註
- JavaScript 函式應始終單獨儲存到一個檔案中,而不應嵌入到 HTML 檔案或任何其他檔案中。
- 本文件末尾的示例是完整的。無需額外的語法即可將其儲存到檔案中並使用。(當然,必須編輯 JavaScript 以反映您站點的域名和/或子網。)
預定義函式和環境
這些函式可用於構建 PAC 檔案
-
基於主機名的條件
-
相關實用函式
-
基於 URL/主機名的條件
-
基於時間的條件
-
日誌實用程式
-
已經定義了一個關聯陣列(物件),因為當時 JavaScript 程式碼無法自行定義它
ProxyConfig.bindings已棄用
注意: pactester(pacparser 包的一部分)用於測試以下語法示例。
- PAC 檔名為
proxy.pac - 命令列:
pactester -p ~/pacparser-master/tests/proxy.pac -u https://www.mozilla.org(傳遞host引數www.mozilla.org和url引數https://www.mozilla.org)
isPlainHostName()
語法
isPlainHostName(host)
引數
- 主機
-
URL 中的主機名(不包括埠號)。
描述
當且僅當主機名中沒有域名(沒有點)時為 True。
示例
isPlainHostName("www.mozilla.org"); // false
isPlainHostName("www"); // true
dnsDomainIs()
語法
dnsDomainIs(host, domain)
引數
描述
當且僅當主機名的域匹配時返回 true。
示例
dnsDomainIs("www.mozilla.org", ".mozilla.org") // true
dnsDomainIs("www", ".mozilla.org") // false
localHostOrDomainIs()
語法
localHostOrDomainIs(host, hostDom)
引數
描述
如果主機名與指定主機名完全匹配,或者主機名中沒有域名部分,但未限定主機名匹配,則為 true。
示例
localHostOrDomainIs("www.mozilla.org", "www.mozilla.org") // true (exact match)
localHostOrDomainIs("www", "www.mozilla.org") // true (hostname match, domain not specified)
localHostOrDomainIs("www.google.com", "www.mozilla.org") // false (domain name mismatch)
localHostOrDomainIs("home.mozilla.org", "www.mozilla.org") // false (hostname mismatch)
isResolvable()
語法
isResolvable(host)
引數
- 主機
-
是 URL 中的主機名。
嘗試解析主機名。如果成功則返回 true。
示例
isResolvable("www.mozilla.org") // true
isInNet()
語法
isInNet(host, pattern, mask)
引數
- 主機
-
DNS 主機名或 IP 地址。如果傳遞主機名,此函式將將其解析為 IP 地址。
- pattern
-
點分隔格式的 IP 地址模式。
- mask
-
IP 地址模式的掩碼,指示 IP 地址的哪些部分應匹配。0 表示忽略,255 表示匹配。
當且僅當主機的 IP 地址與指定的 IP 地址模式匹配時為 True。
模式和掩碼規範與 SOCKS 配置相同。
示例
function FindProxyForURL(url, host) {
alert(isInNet(host, "192.0.2.172", "255.255.255.255"));
// "PAC-alert: true"
}
dnsResolve()
dnsResolve(host)
引數
- 主機
-
要解析的主機名。
將給定的 DNS 主機名解析為 IP 地址,並以點分隔格式作為字串返回。
示例
dnsResolve("www.mozilla.org"); // returns the string "104.16.41.2"
convert_addr()
語法
convert_addr(ipaddr)
引數
- ipaddr
-
任何點分地址,例如 IP 地址或掩碼。
將四個點分隔的位元組連線成一個 4 位元組字並將其轉換為十進位制。
示例
convert_addr("192.0.2.172"); // returns the decimal number 1745889538
myIpAddress()
語法
myIpAddress()
引數
(無)
返回值
返回 Firefox 執行機器的伺服器 IP 地址,格式為點分隔的整數字符串。為了更具幫助性,它將嘗試幾種替代方案,然後才回退到迴環地址(例如 127.0.0.1)。
示例
myIpAddress()
dnsDomainLevels()
語法
dnsDomainLevels(host)
引數
- 主機
-
是 URL 中的主機名。
返回主機名中 DNS 域級別(點數)的數量(整數)。
示例
dnsDomainLevels("www") // 0
dnsDomainLevels("mozilla.org") // 1
dnsDomainLevels("www.mozilla.org"); // 2
shExpMatch()
語法
shExpMatch(str, shExp)
引數
如果字串匹配指定的 shell 萬用字元表示式,則返回 true。
對特定萬用字元表示式語法的支援因瀏覽器而異:*(匹配任意數量的字元)和 ?(匹配一個字元)始終支援,而 [characters] 和 [^characters] 則由某些實現(包括 Firefox)額外支援。
注意:如果客戶端支援,JavaScript 正則表示式通常提供更強大、更一致的方式來匹配 URL(和其他字串)。
示例
shExpMatch("http://home.netscape.com/people/ari/index.html", "*/ari/*"); // returns true
shExpMatch("http://home.netscape.com/people/montulli/index.html", "*/ari/*"); // returns false
weekdayRange()
語法
weekdayRange(wd1, wd2)
weekdayRange(wd1, wd2, gmt)
注意:(在 Firefox 49 之前)如果您希望函式將這些引數評估為範圍,則 wd1 必須小於 wd2。請參閱下面的警告。
引數
只有第一個引數是強制性的。第二個、第三個或兩者都可以省略。
如果只有一個引數存在,函式在引數表示的星期幾返回 true 值。如果將字串“GMT”指定為第二個引數,則時間被視為 GMT。否則,它們被假定為本地時區。
如果同時定義了 wd1 和 wd2,則噹噹前星期幾在這兩個有序星期幾之間時,條件為 true。邊界是包含的,但邊界是有序的。如果指定了“GMT”引數,則時間被視為 GMT。否則,使用本地時區。
警告: 日期的順序很重要。在 Firefox 49 之前,weekdayRange("SUN", "SAT") 總是評估為 true。現在,weekdayRange("WED", "SUN") 僅噹噹前日期是星期三或星期日時才評估為 true。
示例
weekdayRange("MON", "FRI") // returns true Monday through Friday (local timezone)
weekdayRange("MON", "FRI", "GMT") // returns true Monday through Friday (GMT timezone)
weekdayRange("SAT") // returns true on Saturdays local time
weekdayRange("SAT", "GMT") // returns true on Saturdays GMT time
weekdayRange("FRI", "MON") // returns true Friday and Monday only (note, the order does matter!)
dateRange()
語法
dateRange(dayOrMonthOrYear)
dateRange(dayOrMonthOrYear, gmt) // ambiguity is resolved by assuming year is greater than 31
dateRange(day1, day2)
dateRange(day1, day2, gmt)
dateRange(month1, month2)
dateRange(month1, month2, gmt)
dateRange(year1, year2)
dateRange(year1, year2, gmt)
dateRange(day1, month1, day2, month2)
dateRange(day1, month1, day2, month2, gmt)
dateRange(month1, year1, month2, year2)
dateRange(month1, year1, month2, year2, gmt)
dateRange(day1, month1, year1, day2, month2, year2)
dateRange(day1, month1, year1, day2, month2, year2, gmt)
注意:(在 Firefox 49 之前)如果您希望函式將這些引數評估為範圍,則 day1 必須小於 day2,month1 必須小於 month2,year1 必須小於 year2。請參閱下面的警告。
引數
- 日
-
是介於 1 和 31 之間的有序日期(整數)。
- 月份
-
是有序月份字串之一:
"JAN"、"FEB"、"MAR"、"APR"、"MAY"、"JUN"、"JUL"、"AUG"、"SEP"、"OCT"、"NOV"、"DEC"。 - 年
-
是完整的有序年份整數。例如,2016(不是 16)。
- gmt
-
是字串“GMT”(使時間比較發生在 GMT 時區)或省略。如果未指定,則時間被視為本地時區。
如果只指定了一個值(來自每個類別:日、月、年),則函式僅在與該規範匹配的日期返回 true 值。如果指定了兩個值,則結果在這些時間之間為 true,包括邊界,但邊界是有序的。
警告: 日、月、年的順序很重要;在 Firefox 49 之前,dateRange("JAN", "DEC") 總是評估為 true。現在,dateRange("DEC", "JAN") 僅噹噹前月份是十二月或一月時才評估為 true。
示例
dateRange(1) // returns true on the first day of each month, local timezone
dateRange(1, "GMT") // returns true on the first day of each month, GMT timezone
dateRange(1, 15) // returns true on the first half of each month
dateRange(24, "DEC");// returns true on 24th of December each year
dateRange("JAN", "MAR"); // returns true on the first quarter of the year
dateRange(1, "JUN", 15, "AUG");
// returns true from June 1st until August 15th, each year
// (including June 1st and August 15th)
dateRange(1, "JUN", 1995, 15, "AUG", 1995);
// returns true from June 1st, 1995, until August 15th, same year
dateRange("OCT", 1995, "MAR", 1996);
// returns true from October 1995 until March 1996
// (including the entire month of October 1995 and March 1996)
dateRange(1995);
// returns true during the entire year of 1995
dateRange(1995, 1997);
// returns true from beginning of year 1995 until the end of year 1997
timeRange()
語法
// The full range of expansions is analogous to dateRange.
timeRange(hour1, min1, sec1, hour2, min2, sec2)
timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)
注意:(在 Firefox 49 之前)如果您希望函式將這些引數評估為範圍,則類別 hour1、min1、sec1 必須小於類別 hour2、min2、sec2。請參閱下面的警告。
引數
- 小時
-
是 0 到 23 之間的小時。(0 是午夜,23 是晚上 11 點。)
- min
-
分鐘從 0 到 59。
- 秒
-
秒從 0 到 59。
- gmt
-
對於 GMT 時區,是字串“GMT”,否則未指定,表示本地時區。
如果只指定了一個值(來自每個類別:小時、分鐘、秒),則函式僅在與該規範匹配的時間返回 true 值。如果指定了兩個值,則結果在這些時間之間為 true,包括邊界,但邊界是有序的。
警告: 小時、分鐘、秒的順序很重要;在 Firefox 49 之前,timeRange(0, 23) 總是評估為 true。現在,timeRange(23, 0) 僅噹噹前小時是 23:00 或午夜時才評估為 true。
示例
timeRange(12); // returns true from noon to 1pm
timeRange(12, 13) // returns true from noon to 1pm
timeRange(12, "GMT") // returns true from noon to 1pm, in the GMT timezone
timeRange(9, 17) // returns true from 9am to 5pm
timeRange(8, 30, 17, 0) // returns true from 8:30am to 5:00pm
timeRange(0, 0, 0, 0, 0, 30) // returns true between midnight and 30 seconds past midnight
alert()
語法
alert(message)
引數
- message
-
要記錄的字串
將訊息記錄到瀏覽器控制檯。
示例
alert(`${host} = ${dnsResolve(host)}`) // logs the host name and its IP address
alert("Error: shouldn't reach this clause.") // log a message
示例 1
除本地主機外,所有請求都使用代理
注意:由於以下所有示例都非常具體,因此尚未經過測試。
所有未完全限定的主機,或本地域中的主機,都將直接連線。所有其他請求都將透過 w3proxy.mozilla.org:8080。如果代理關閉,連線將自動變為直接連線
function FindProxyForURL(url, host) {
if (isPlainHostName(host) || dnsDomainIs(host, ".mozilla.org")) {
return "DIRECT";
}
return "PROXY w3proxy.mozilla.org:8080; DIRECT";
}
注意:對於只有一個代理的情況,這是最簡單、最有效的自動配置檔案。
示例 2
同上,但對於防火牆外的本地伺服器使用代理
如果存在屬於本地域但位於防火牆外部且只能透過代理伺服器訪問的主機(例如主 Web 伺服器),則可以使用 localHostOrDomainIs() 函式處理這些例外情況
function FindProxyForURL(url, host) {
if (
(isPlainHostName(host) || dnsDomainIs(host, ".mozilla.org")) &&
!localHostOrDomainIs(host, "www.mozilla.org") &&
!localHostOrDomainIs(host, "merchant.mozilla.org")
) {
return "DIRECT";
}
return "PROXY w3proxy.mozilla.org:8080; DIRECT";
}
上述示例將對除 mozilla.org 域中的本地主機之外的所有請求使用代理,但 www.mozilla.org 和 merchant.mozilla.org 主機將透過代理訪問。
注意:上述例外情況的順序是為了提高效率:localHostOrDomainIs() 函式僅針對本地域中的 URL 執行,而不是針對每個 URL 執行。請注意 or 表示式周圍的括號,位於 and 表示式之前,以實現上述高效行為。
示例 3
僅當無法解析主機時才使用代理
此示例適用於內部 DNS 伺服器僅能解析內部主機名,並且目標是僅對無法解析的主機使用代理的環境
function FindProxyForURL(url, host) {
if (isResolvable(host)) {
return "DIRECT";
}
return "PROXY proxy.mydomain.com:8080";
}
上述要求每次都查詢 DNS;可以將其與其他規則智慧地組合,以便僅當其他規則未產生結果時才查詢 DNS
function FindProxyForURL(url, host) {
if (
isPlainHostName(host) ||
dnsDomainIs(host, ".mydomain.com") ||
isResolvable(host)
) {
return "DIRECT";
}
return "PROXY proxy.mydomain.com:8080";
}
示例 4
基於子網的決策
在此示例中,給定子網中的所有主機都直接連線,其他主機透過代理連線
function FindProxyForURL(url, host) {
if (isInNet(host, "192.0.2.172", "255.255.0.0")) {
return "DIRECT";
}
return "PROXY proxy.mydomain.com:8080";
}
同樣,可以透過在開頭新增冗餘規則來最大程度地減少上述 DNS 伺服器的使用
function FindProxyForURL(url, host) {
if (
isPlainHostName(host) ||
dnsDomainIs(host, ".mydomain.com") ||
isInNet(host, "192.0.2.0", "255.255.0.0")
) {
return "DIRECT";
}
return "PROXY proxy.mydomain.com:8080";
}
示例 5
基於 URL 模式的負載均衡/路由
此示例更為複雜。有四個(4)代理伺服器;其中一個作為所有其他伺服器的熱備用,因此如果其餘三個中的任何一個出現故障,第四個將接管。此外,其餘三個代理伺服器根據 URL 模式分擔負載,這使得它們的快取更有效(三個伺服器上任何文件只有一個副本 - 而不是每個伺服器上一個副本)。負載分佈如下
| Proxy | 用途 |
|---|---|
| #1 | .com 域 |
| #2 | .edu 域 |
| #3 | 所有其他域 |
| #4 | 熱備用 |
所有本地訪問都希望直接進行。所有代理伺服器都在埠 8080 上執行(它們不需要,您可以更改埠,但請記住同時修改兩側的配置)。請注意 JavaScript 中字串如何使用 + 運算子進行連線。
function FindProxyForURL(url, host) {
if (isPlainHostName(host) || dnsDomainIs(host, ".mydomain.com")) {
return "DIRECT";
} else if (shExpMatch(host, "*.com")) {
return "PROXY proxy1.mydomain.com:8080; PROXY proxy4.mydomain.com:8080";
} else if (shExpMatch(host, "*.edu")) {
return "PROXY proxy2.mydomain.com:8080; PROXY proxy4.mydomain.com:8080";
}
return "PROXY proxy3.mydomain.com:8080; PROXY proxy4.mydomain.com:8080";
}
示例 6
為特定協議設定代理
大多數標準 JavaScript 功能都可用於 FindProxyForURL() 函式。例如,要根據協議設定不同的代理,可以使用 startsWith() 函式
function FindProxyForURL(url, host) {
if (url.startsWith("http:")) {
return "PROXY http-proxy.mydomain.com:8080";
} else if (url.startsWith("ftp:")) {
return "PROXY ftp-proxy.mydomain.com:8080";
} else if (url.startsWith("gopher:")) {
return "PROXY gopher-proxy.mydomain.com:8080";
} else if (url.startsWith("https:") || url.startsWith("snews:")) {
return "PROXY security-proxy.mydomain.com:8080";
}
return "DIRECT";
}
注意:可以使用前面描述的 shExpMatch() 函式實現相同的效果。
例如
if (shExpMatch(url, "http:*")) {
return "PROXY http-proxy.mydomain.com:8080";
}
注意:自動配置檔案可以透過 CGI 指令碼輸出。這很有用,例如,當根據客戶端 IP 地址(CGI 中的 REMOTE_ADDR 環境變數)使自動配置檔案行為不同時。
應仔細考慮 isInNet()、isResolvable() 和 dnsResolve() 函式的使用,因為它們需要查詢 DNS 伺服器。所有其他與自動配置相關的函式都是簡單的字串匹配函式,不需要使用 DNS 伺服器。如果使用代理,代理將執行其 DNS 查詢,這將使對 DNS 伺服器的影響加倍。大多數情況下,這些函式對於實現所需結果來說不是必需的。
歷史和實現
代理自動配置於 1990 年代後期隨 JavaScript 一同引入 Netscape Navigator 2.0。Netscape 的開源最終導致了 Firefox 本身。
因此,PAC 及其 JavaScript 庫最“原始”的實現是早期 Firefox 版本中找到的 nsProxyAutoConfig.js。這些實用程式在許多其他開源系統中都可以找到,包括 Chromium。Firefox 後來將該檔案整合到 ProxyAutoConfig.cpp 中,作為 C++ 字串字面量。要將其提取到自己的檔案中,只需將該塊複製到 JavaScript 中,並使用 console.log 指令將其打印出來。
Microsoft 通常有自己的實現。過去 他們的庫存在一些問題,但大多數問題現已解決。他們定義了 一些新的以 "Ex" 結尾的函式,用於處理 IPv6 地址。該功能受 Chromium 支援,但 Firefox 尚未支援(bugzilla #558253)。