Курилка

Автоматическая настройка прокси

Настройка параметров прокси в браузере может производиться автоматически при помощи PAC-файла (proxy auto configuration file) — программы на языке JavaScript, благодаря которой, браузер может узнать, каким образом соединяться с сайтами в Интернете с учётом доступности/блокировки серверов, даты, времени суток, IP-адреса компьютера в сети и др.

Спецификация сценария была разработана в Netscape для браузера Netscape Navigator 2.0 в 1996 году. Далее на странице находится её перевод.

Немного ссылок:

Для просмотра страницы с подсветкой синтаксиса разрешите, пожалуйста, исполнение JavaScript.

Формат файла автонастройки прокси в Navigator’е

Март 1996

(В конце документа приводятся несколько примеров и советов.)

Файл автонастройки прокси пишется на JavaScript. Скрипт должен определить одну функцию:

function FindProxyForURL(url, host)
{
    ...
}

которая будет вызываться Navigator-ом для каждого URL, который он собирается загрузить, следующим способом:

ret = FindProxyForURL(url, host);

где:

url — полный URL, к которому нужно получить доступ.
host — имя хоста, которое извлекается из URL. Это сделано только для удобства, это просто строка между «://» и первым символом «:» или «/» после неё. Номер порта в неё не включается, его можно извлечь при необходимости.
ret — (возвращаемое значение) строка конфигурации прокси. Формат строки описан ниже.

Сохранение файла автонастройки и настройка MIME-типа

  1. JavaScript-функцию необходимо сохранить в файле с расширением .pac, например:

    proxy.pac

    Примечание 1: Сохранять нужно саму JavaScript-функцию, а не встроенную в HTML.

    Примечание 2: Примеры в конце этого документа готовы к работе. Чтобы их использовать, в них не нужно вносить никаких синтаксических изменений (но конечно, нужно отредактировать скрипт так, чтобы в нём упоминались ваши домены и/или подсети).

  2. Далее необходимо сконфигурировать ваш сервер так, чтобы расширению .pac соответствовал следующий MIME-тип:

    application/x-ns-proxy-autoconfig

    Если используется сервер Netscape, нужно отредактировать файл mime.types в каталоге config. Если используются серверы Apache, CERN или NCSA, нужно использовать директиву AddType.

Формат возвращаемой строки

JavaScript-функция должна возвращать строку.

Если строка — null, прокси использоваться не будет.

Строка может содержать произвольное число блоков, разделённых точками с запятой:

DIRECT — соединение нужно устанавливать напрямую, без прокси.
PROXY host:port — использовать указанный прокси.
SOCKS host:port — использовать указанный SOCKS-сервер.

Если указано несколько настроек, будет использована самая левая, пока Navigator может установить соединение с прокси. В случае недоступности этого прокси будет использовано следующее значение и т.д.

Navigator будет автоматически пробовать соединиться с недоступным прокси спустя сначала 30 минут, потом через 1 час после предыдущей попытки (каждый раз добавляя дополнительные 30 минут).

Если все прокси недоступны, а опция DIRECT не указана, Navigator запросит пользователя игнорировать ли настройки прокси и попытатется использовать прямое соединение. Через 20 минут Navigator запросит о необходимости обновления настроек прокси (затем через 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, если первичный прокси не доступен.

Предопределённые функции и окружение для JavaScript-функции

isPlainHostName(host)

host — имя хоста в URL (без номера порта).

Истина, если имя хоста не содержит доменного имени (нет точек).

isPlainHostName("www") — истина.
isPlainHostName("www.netscape.com") — ложь.

dnsDomainIs(host, domain)

host — имя хоста из URL.
domain — доменное имени для сравнения с именем хоста.

Возвращает «истину», если имя домена соответствует имени хоста.

dnsDomainIs("www.netscape.com", ".netscape.com") — истина.
dnsDomainIs("www", ".netscape.com") — ложь.
dnsDomainIs("www.mcom.com", ".netscape.com") — ложь.

localHostOrDomainIs(host, hostdom)

host — имя хоста из URL.
hostdom — полностью указанное имя хоста для сравнения.

«Истина», если имя хоста в точности совпадает с указанным именем или в имени хоста отсутствует часть с доменным именем, а неуказанная часть соответствует.

localHostOrDomainIs("www.netscape.com", "www.netscape.com") — истина (точное соответствие).
localHostOrDomainIs("www", "www.netscape.com") — истина (имя хоста совпадает, а домен не указан).
localHostOrDomainIs("www.mcom.com", "www.netscape.com") — ложь (не совпадает имя домена).
localHostOrDomainIs("home.netscape.com", "www.netscape.com") — ложь (не совпадает имя хоста).

isResolvable(host)

host — имя хоста из URL.

Выполняет попытку узнать IP-адрес хоста. Возвращает «истину», если попытка была удачной.

isResolvable("www.netscape.com") — истина (если при получении адреса не было ошибки DNS из-за фаервола или по каким-либо другим причинам).
isResolvable("bogus.domain.foobar") — ложь.

isInNet(host, pattern, mask)

host — имя хоста или IP-адрес. Если указано имя хоста, функция сама выяснит его адрес.
pattern — шаблон IP-адреса в формате чисел разделённых точками.
mask — маска IP-адреса для указания части, с которой необходимо сравнить. 0 — игнорировать, 255 — сравнивать.

«Истина», если IP-адрес хоста соответствует шаблону. Шаблон и маска указываются также, как и в конфигурации SOCKS.

isInNet(host, "198.95.249.79", "255.255.255.255") — истина, если IP-адрес хоста точно равен 198.95.249.79.
isInNet(host, "198.95.0.0", "255.255.0.0") — истина, если IP-адрес совпадает в первых двух числах (198.95.*.*).

dnsResolve(host)

host — имя хоста для определения адреса.

Получает IP-адрес хоста и возвращает его в виде строки чисел разделённых точками.

dnsResolve("home.netscape.com") возвращает строку "198.95.249.79".

myIpAddress()

Возвращает IP-адрес хоста, на котором запущен Navigator, в виде строки чисел разделённых точками.

myIpAddress() вернёт строку "198.95.249.79", если Navigator запущен на машине с таким адресом.

dnsDomainLevels(host)

host — имя хоста из URL.

Возвращает уровень (целое число) домена (количество точек в имени).

dnsDomainLevels("www") возвращает 0.
dnsDomainLevels("www.netscape.com") возвращает 2.

shExpMatch(str, shexp)

str — произвольная строка для проверки соответствия (например URL или имя хоста).
shexp — шаблон для проверки.

Возвращает «истину», если str соответствует shexp. Здесь шаблон является выражением в стиле оболочки (как в bash или командной строке Windows), а не регулярным выражением.

shExpMatch("http://home.netscape...ari/index.html", "*/ari/*") — истина.
shExpMatch("http://home.netscape...lli/index.html", "*/ari/*") — ложь.

weekdayRange(wd1, wd2, gmt)

wd1 и wd2 — строки с названием дня недели, одна из: SUN MON TUE WED THU FRI SAT.
gmt — либо строка GMT, либо не указывается.

Обязателен только первый параметр. И второй, и третий могут быть опущены. Если указан только первый параметр, функция возвращает «истину» в день недели, который представлен первым параметром. Если в качестве второго параметра указана строка "GMT", время определяется по GMT, иначе используется локальное время.

Если указаны оба параметра, и wd1, и wd2, «истина» возвращается когда текущий день недели находится между этими днями включительно. Если указан параметр "GMT", используется время по Гринвичу, иначе — локальное.

weekdayRange("MON", "FRI") — истинно с понедельника по пятницу (время локальное).
weekdayRange("MON", "FRI", "GMT") — то же самое, но по Гринвичу (GMT).
weekdayRange("SAT") — истинно по субботам (время локальное).
weekdayRange("SAT", "GMT") — истинно по субботам (GMT).
weekdayRange("FRI", "MON") — истинно с пятницы по понедельник (внимание — порядок имеет значение!).

dateRange(day)
dateRange(day1, day2)
dateRange(mon)
dateRange(month1, month2)
dateRange(year)
dateRange(year1, year2)
dateRange(day1, month1, day2, month2)
dateRange(month1, year1, month2, year2)
dateRange(day1, month1, year1, day2, month2, year2)
dateRange(day1, month1, year1, day2, month2, year2, gmt)

day — день месяца от 1 до 31 (целое).
month — одна из строк сокращённого названия месяца: JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
year — полный номер года, например 1995 (а не просто 95). Целое.
gmt либо строка "GMT", что приводит к сравнению дат по Гринвичу, либо, если не указано, будет использоваться локальное время.

Несмотря на то, в предыдущих примерах это не показано, параметр "GMT" может быть использован в любом из 9 способов вызова, всегда в качестве последнего параметра.

Если указано только одно значение (из каждой категории — день, месяц, год), функция возвращает «истину» только в день, который указан. Если указаны оба значения, результат будет истинным между двумя датами включительно.

dateRange(1) — истинно в первый день любого месяца по локальному времени.
dateRange(1, "GMT") — истинно в первый день любого месяца, время зоны GMT.
dateRange(1, 15) — истинно в первой половине любого месяца.
dateRange(24, "DEC") — истинно 24 декабря любого года.
dateRange(24, "DEC", 1995) — истинно 24 декабря 1995 года.
dateRange("JAN", "MAR") — истинно в первом квартале года.
dateRange(1, "JUN", 15, "AUG") — истинно с 1 июня по 15 августа любого года, включая и 1 июня, и 15 августа.
dateRange(1, "JUN", 15, 1995, "AUG", 1995) — истинно с 1 июня 1995 года по 15 августа того же года.
dateRange("OCT", 1995, "MAR", 1996) — истинно с октября 1995 года по март 1996 (включая весь октябрь 1995 года и весь март 1996-го).
dateRange(1995) — истинно в течение всего 1995 года.
dateRange(1995, 1997) — истинно с начала 1995 года до конца 1997-го.

timeRange(hour)
timeRange(hour1, hour2)
timeRange(hour1, min1, hour2, min2)
timeRange(hour1, min1, sec1, hour2, min2, sec2)
timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)

hour — час с 0 по 23 (0 — полночь, 23 — 11 пополудни).
min — минуты от 0 до 59.
sec — секунды от 0 до 59.
gmt — либо строка "GMT" для временной зоны GMT (по Гринвичу). Если не указано, будет использоваться локальное время. И опять, хотя это и не отражено в примерах, этот параметр может использоваться в любом случае, всегда последним.

Истинно в течение (или в диапазоне) указанного времени.

timeRange(12) — истинно с полудня до 1 часа пополудни.
timeRange(12, 13) — то же самое.
timeRange(12, "GMT") — истинно с полудня по 1 час дня по Гринвичу (GMT).
timeRange(9, 17) — истинно с 9 часов утра по 5 часов вечера.
timeRange(8, 30, 17, 00) — истинно с 8:30 по 17:00.
timeRange(0, 0, 0, 0, 0, 30) — истинно с полуночи в течение 30 секунд.

Примеры

Пример 1: Использовать прокси для всех адресов за исключением локальных

Это будет работать в домене Netscape. Соединение с неполностью указанными хостами или хостами находящимися в локальном домене будет осуществляться напрямую. Всё остальное будет направлено через w3proxy:8080. Если прокси не будет отвечать, соединение будет сделано напрямую.

function FindProxyForURL(url, host)
{
    if (isPlainHostName(host) || dnsDomainIs(host, ".netscape.com"))
        return "DIRECT";
    else
        return "PROXY w3proxy.netscape.com:8080; DIRECT";
}

Примечание: Это простейший и самый эффективный файл автонастройки для случая, когда есть только один прокси-сервер.

Пример 1б: То же, что и выше, но использовать прокси для локальных серверов, которые находятся за фаерволом

Если есть хосты (такие как основной веб-сервер), которые находятся в локальном домене, но за фаерволом, и доступны только через прокси-сервер, то такое исключение может быть обработано с помощью функции localHostOrDomainIs():

function FindProxyForURL(url, host)
{
    if (
        (isPlainHostName(host) || dnsDomainIs(host, ".netscape.com"))
        && !localHostOrDomainIs(host, "www.netscape.com")
        && !localHostOrDoaminIs(host, "merchant.netscape.com")
    )
        return "DIRECT";
    else
        return "PROXY w3proxy.netscape.com:8080; DIRECT";
}

Вышеприведённый код будет использовать прокси для всего, кроме хостов в домене netscape.com, и хостов www.netscape.com и merchant.netscape.com.

Обратите внимание на порядок перечисления хостов, который влияет на эффективность: localHostOrDomainIs() будет вызвана только для URL в локальном домене, а не для всех. Обратите внимание на скобки в выражении, которые добавлены для достижения вышеупомянутой эффективности.

Пример 2: Использовать прокси только для хостов с неизвестным адресом

Этот пример будет работать только в окружении, где внутренний DNS настроен так, что он может распознавать только внутренние имена хостов, и цель такой настройки — использовать прокси только для неопознанных хостов:

function FindProxyForURL(url, host)
{
    if (isResolvable(host))
        return "DIRECT";
    else
        return "PROXY proxy.mydomain.com:8080";
}

Этот код требует обращения к DNS при каждом вызове; его можно сгруппировать с другими правилами, чтобы обращение к DNS требовалось только тогда, когда другие правила не дадут результата:

function FindProxyForURL(url, host)
{
    if (isPlainHostName(host) || dnsDomainIs(host, ".mydomain.com") || isResolvable(host))
        return "DIRECT";
    else
        return "PROXY proxy.mydomain.com:8080";
}

Пример 3: Принятие решения на основе информации о подсети

В этом примере соединение с хостами в данной подсети производятся напрямую, а все остальные — через прокси.

function FindProxyForURL(url, host)
{
    if (isInNet(host, "198.95.0.0", "255.255.0.0"))
        return "DIRECT";
    else
        return "PROXY proxy.mydomain.com:8080";
}

И снова, использование обращения к DNS может быть минимизировано путём добавления дополнительных проверок перед вызовом isInNet():

function FindProxyForURL(url, host)
{
    if (isPlainHostName(host) || dnsDomainIs(host, ".mydomain.com")
            || isInNet(host, "198.95.0.0", "255.255.0.0"))
        return "DIRECT";
    else
        return "PROXY proxy.mydomain.com:8080";
}

Пример 4: Распределение нагрузки и перенаправление по шаблону URL

Этот пример — более сложный. Имеется четыре (4) прокси-сервера, один из которых, резервный (hot stand-by), подменяет остальных в случае их отказа.

Кроме того, три первых прокси-сервера обслуживают свои URL — у каждого своя зона, что добавляет эффективности кэшированию (у любого загруженного документа — только одна копия на одном из трёх серверов, а не на каждом из них). Нагрузка распределяется примерно так:

Прокси Назначение
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";
    else
        return "PROXY proxy3.mydomain.com:8080; " + "PROXY proxy4.mydomain.com:8080";
}

Пример 5: Настройка прокси для конкретного протокола

В функции FindProxyForURL() можно использовать большинство стандартных функций JavaScript. Например, чтобы для разных протоколов использовать разные прокси, можно использовать функцию substring():

function FindProxyForURL(url, host)
{
    if (url.substring(0, 5) == "http:") {
        return "PROXY http-proxy.mydomain.com:8080";
    }
    else if (url.substring(0, 4) == "ftp:") {
        return "PROXY ftp-proxy.mydomain.com:8080";
    }
    else if (url.substring(0, 7) == "gopher:") {
        return "PROXY gopher-proxy.mydomain.com:8080";
    }
    else if (url.substring(0, 6) == "https:" || url.substring(0, 6) == "snews:") {
        return "PROXY security-proxy.mydomain.com:8080";
    }
    else {
        return "DIRECT";
    }
}

Обратите внимание, что того же можно добиться, используя описанную ранее функцию shExpMatch(); например:

...
if (shExpMatch(url, "http:*")) {
    return "PROXY http-proxy.mydomain.com:8080;
}
...

Советы