Performanceoptimierung bei Proxy-Konfigurationsscripten (PAC)

Hallo,

Bei der Erstellung einer automatischen Proxy-Konfigurationsdatei (PAC) kommen gerne Fragen auf, wie man diese optimieren kann, so dass deren Abarbeitung möglichst zeitnah geschieht.

Die Funktionen, welche zur Auswertung der Adresse (URL und Hostname) in einer PAC verwendet werden können, sind im folgenden Artikel hinterlegt:

JavaScript or JScript Auto-Proxy Example Files
https://technet.microsoft.com/library/Dd361950

Wie im Artikel erwähnt führen hierbei die Funktionen isInNet, isResolvable und dnsResolve zu Abfragen ins DNS-Subsystem.

Daher ist der Ansatz eben diese Funktionen zu vermeiden - bzw. deren Aufruf zu minimieren sinnvoll um die Performance bei der Abarbeitung zu steigern.

1. Abfrage nach NetBIOS-Namen:
NetBIOS-Namen werden üblicherweise ausschließlich im Intranet verwendet und werden daher nicht über den Proxy ins Internet geroutet.
  if (isPlainHostName(host))
return "DIRECT";

2. Abfrage via DNS-Suffixen
Bei intern verwendeten DNS-Suffixen wird die Verwendung eines Proxies ebenfalls üblicherweise umgangen. Am einfachsten ist dies über die Funktion dnsDomainIs abgebildet:
  if (dnsDomainIs (host, ".dns.company.com"))
return "DIRECT";

Alternativ können hier auch einfache Stringvergleiche via ShExMatch durchgeführt werden - was zum gleichen Ergebnis führt. Hierbei wird dann der Stern als Wildcard hingefügt:
  if (shExpMatch(host, "*.dns.company.com"))
return "DIRECT";

3. Abfragen über IP-Ranges
Der Ansatz dieser Ausnahmeregel liegt darin, dass man - unabhängig vom Namen des Webservers - darauf setzt, dass die IP-Adresse im eigene Netz liegt und somit eben den Proxy umgehen soll.
Wurde im Internet Explorer die IP-Adresse direkt angegeben, so liegt diese bereits vor. Testen kann man dies mit der folgenden Script-Funktion:
  var isIpV4Addr = /^(\d+.){3}\d+$/;
ret = isIpV4Addr.test (host)

Die Routine selbst prüft ab, ob in der Variable host 3 Zahlen enthalten sind, welche jeweils mit einem Punkt enden, gefolgt von eine weiteren Zahl. Das Ergebnis in ret selbst ist dann true, wenn dies zutrifft, und false wenn dies eben nicht der Fall ist.
Somit wäre dann ein folgender Konstrukt geeignet die IP-Adresse des Hosts für weitere Abfragen in der Variable hostIP zu verwenden:
  var hostIP;
var isIpV4Addr = /^(\d+.){3}\d+$/;
if (isIpV4Addr.test (host))
hostIP=host;
else
hostIP=dnsResolve (host);

Für den möglichen Fall, dass der Benutzer einen nicht-existierenden Host eingegeben hat (bspw. wegen Tippfehler in der Adresszeile), oder die IP-Adresse vom Client nicht aufgelöst werden konnte, sollte dann das Ergebnis aus hostIP abgeprüft werden, ob dieses nicht 0 ist. Die weitere Fehlerbehandlung kann man dann dem Proxy-Server überlassen:
  if (hostIP==0)
return "PROXY myproxy:80";

Danach kommen die Checks gegen die jeweiligen Subnetze gegen über der bereits ermittelten, bzw. der übergebenen IP-Adresse.
Hierbei sollten dann auch eher die Funktion shExpMatch verwendet werden, anstatt die IP-Range mittels isInNet zu ermitteln. Folgende Funktionen liefern hierbei die identischen Resultate, wenn auch mit unterschiedlichem Zeitaufwand:
  if (isInNet (hostIP, "95.53.0.0", "255.255.0.0"))
return "DIRECT";
if (shExpMatch (hostIP, "95.53.*))
return "DIRECT";

4. Groß- und Kleinschreibung
Das Proxyscript verwendet als Sprache Javascript. Javascript selbst unterscheidet Groß-Kleinschreibung. Somit wird eine if-Abfrage mit Großbuchstaben zu einem Scriptfehler führen.
Weiterhin ist zu beachten, dass der Internet Explorer selbst eine Umwandlung der Variablen host und url von Großbuchstaben zu Kleinbuchstaben durchführt, so dass die Unterscheidungen jeweils immer in Kleinbuchstaben stattfinden.
WinHTTP führt diese Umwandlung nicht durch. Daher ist eine Umwandlung der zu testenden Variablen notwendig, so dass Programme, die WinHTTP als Schnittstelle verwenden auch die jeweiligen Regeln beachten. Durchgeführt wird dies mit der folgenden Funktion, welche vor der ersten Überprüfung eines Domain-Namens stattfinden sollte:
host = host.toLowerCase();

Zu beachten:
Wenn man in dem Script einen Fehler drin hat, wie beispielsweise eine fehlende Klammer in einer if-Anweisung , so wird das Script gar nicht ausgeführt.
In diesem Fall werden nicht einmal Debugging-Zeilen á la alert ("Hallo"); ausgeführt.
Um solche syntaktischen Fehler zu minimieren ist die Verwendung eines Script-Editors wie Visual Studio hier ratsam.
Damit Visual Studio dann das Script-Parsing verwendet, bietet es sich an, die zu bearbeitende PAC-Datei mit der Endung ".JS" hier zu verwenden.

Zum Testen, welchen Inhalt eine Variable hat, kann die Funktion alert () verwendet werden.

Somit würde das Beispiel-PAC dann in etwa wie folgt aussehen:
function FindProxyForURL(url,host)
{
// Alles was NetBIOS-Namen hat
if (isPlainHostName(host))
return "DIRECT";

  // Umwandlung auf Klienbuchstaben - falls noch nicht geschehen.
host = host.toLowerCase();

  // Interne DNS-Suffixe
if (shExpMatch(host, "*.corp.company.com") ||
shExpMatch(host, "*.dns.company.com"))
return "DIRECT";

  // Speicherung der IP-Adresse
var hostIP;
var isIpV4Addr = /^(\d+.){3}\d+$/;
if (isIpV4Addr.test (host))
hostIP=host;
else
hostIP=dnsResolve (host);

  // IP konnte zu dem Host nicht ermittelt werden -> ab zum Proxy
if (hostIP==0)
return "PROXY myproxy:80";

  // Diese 3 Scopes werden auschließlich intern verwendet
if (shExpMatch (hostIP, "95.53.*") ||
shExpMatch (hostIP, "192.168.*") ||
shExpMatch (hostIP, "127.0.0.1"))
return "DIRECT";

  // Alles andere geht über den Proxy
return "PROXY myproxy:80;";
}

Viele Grüße,
Heiko