Hallo zusammen,
einer meiner Kunden hatte Probleme, mit unserer Anwendung auf eine REST-
API zuzugreifen. Nachdem ich zunächst behauptet hatte, dass da irgendwas bei ihm an Firewalls oder Proxys falsch konfiguriert ist (weil wir das Problem bisher auch nie hatten), muss ich mittlerweile zugeben, dass er wohl recht hat und das Problem tatsächlich bei uns liegt. Das Problem ist, dass der Kunde tatsächlich einen Proxy im Netzwerk aktiv hat,
Windows komplett auf "automatische
Erkennung" eingestellt ist, aber Delphi diese automatische
Erkennung nicht korrekt durchführt. Bevor ich jetzt einen Bugreport im QP aufmache, wollte ich hier noch einmal hören, ob ich vielleicht etwas übersehe, und auch, ob ihr vielleicht einen besseren Tipp für einen Workaround oder auch für die "richtige" Lösung habt.
Das Problem besteht nach einem ersten Drüberschauen zumindest in Delphi 10.3 bis zum aktuellen 11.1 und liegt in der System.Net.HttpClient.Win. Dort gibt es die Methode TWinHTTPRequest.SetWinProxySettings und diese hat wiederum die Unter-Funktion GetProxyInfo, welche nach meiner Analyse fehlerhaft ist:
Delphi-Quellcode:
function GetProxyInfo(
const AURL:
string;
var AProxy, AProxyBypass:
string): Boolean;
var
LAutoDetectProxy: Boolean;
LpProxyConfig: PWinHTTPCurrentUserIEProxyConfig;
LWinHttpProxyInfo: TWinHTTPProxyInfo;
LAutoProxyOptions: TWinHTTPAutoProxyOptions;
begin
Result := True;
AProxy := '
';
AProxyBypass := '
';
FillChar(LAutoProxyOptions, SizeOf(LAutoProxyOptions), 0);
LpProxyConfig := TWinHttpLib.GetProxyConfig;
if LpProxyConfig <>
nil then
begin
if LpProxyConfig^.fAutoDetect
then
begin
LAutoProxyOptions.dwFlags := WINHTTP_AUTOPROXY_AUTO_DETECT;
LAutoProxyOptions.dwAutoDetectFlags := WINHTTP_AUTO_DETECT_TYPE_DHCP
or WINHTTP_AUTO_DETECT_TYPE_DNS_A;
// *** Obwohl die AutoDetect in der Proxy-Konfig aktiv ist, bleibt LAutoDetectProxy auf False! ***
end;
if LpProxyConfig^.lpszAutoConfigURL <> '
'
then
begin
LAutoProxyOptions.dwFlags := WINHTTP_AUTOPROXY_CONFIG_URL;
LAutoProxyOptions.lpszAutoConfigUrl := LpProxyConfig^.lpszAutoConfigUrl;
LAutoDetectProxy := True;
end
else
begin
// *** Wenn keine "AutoConfig"-URL angegeben ist, landet der Code hier,
// *** auch wenn in der ProxyConfig "AutoDetect" angegeben ist. Dadurch
// *** wird dann versucht, einen direkt eingestellten Proxy zu nehmen,
// *** auch wenn keiner eingestellt ist. LAutoDetectProxy bleibt auf False.
AProxy := LpProxyConfig^.lpszProxy;
AProxyBypass := LpProxyConfig^.lpszProxyBypass;
LAutoDetectProxy := False;
end;
end
else
begin
// if the proxy configuration is not found then try to autodetect it (If the Internet Explorer settings are not configured for system accounts)
LAutoProxyOptions.dwFlags := WINHTTP_AUTOPROXY_AUTO_DETECT;
LAutoProxyOptions.dwAutoDetectFlags := WINHTTP_AUTO_DETECT_TYPE_DHCP
or WINHTTP_AUTO_DETECT_TYPE_DNS_A;
LAutoDetectProxy := True;
end;
if (AProxy = '
')
and LAutoDetectProxy
then
begin
// *** Eigentlich müssten wir hier landen, aber wegen der falschen Abfragen oben, landen wir
// *** nie hier. Folge: die entscheidende Funktion aus der WinHTTP-API, nämlich
// *** WinHttpGetProxyForUrl wird nie aufgerufen!
// From https://msdn.microsoft.com/en-us/library/aa383153%28VS.85%29.aspx
// Try with fAutoLogonIfChallenged parameter set to false, if ERROR_WINHTTP_LOGIN_FAILURE then try
// with fAutoLogonIfChallenged parameter set to true.
LAutoProxyOptions.fAutoLogonIfChallenged := False;
if WinHttpGetProxyForUrl(LClient.FWSession, LPCWSTR(AURL), LAutoProxyOptions, LWinHttpProxyInfo)
then
begin
AProxy := LWinHttpProxyInfo.lpszProxy;
AProxyBypass := LWinHttpProxyInfo.lpszProxyBypass;
end
else
begin
if GetLastError = ERROR_WINHTTP_LOGIN_FAILURE
then
begin
LAutoProxyOptions.fAutoLogonIfChallenged := True;
if WinHttpGetProxyForUrl(LClient.FWSession, LPCWSTR(AURL), LAutoProxyOptions, LWinHttpProxyInfo)
then
begin
AProxy := LWinHttpProxyInfo.lpszProxy;
AProxyBypass := LWinHttpProxyInfo.lpszProxyBypass;
end
else
Result := False;
end
else
Result := False;
end;
end;
if AProxy = '
'
then
Result := False;
end;
Meine Kommentare mit den *** sollten anzeigen, wo meiner Meinung nach der Fehler liegt und weshalb die automatische Proxy-Erkennung nicht funktioniert.
Eine einfache Lösung sieht so aus:
Delphi-Quellcode:
function GetProxyInfo(const AURL: string; var AProxy, AProxyBypass: string): Boolean;
var
LAutoDetectProxy: Boolean;
LpProxyConfig: PWinHTTPCurrentUserIEProxyConfig;
LWinHttpProxyInfo: TWinHTTPProxyInfo;
LAutoProxyOptions: TWinHTTPAutoProxyOptions;
begin
Result := True;
AProxy := '';
AProxyBypass := '';
FillChar(LAutoProxyOptions, SizeOf(LAutoProxyOptions), 0);
LpProxyConfig := TWinHttpLib.GetProxyConfig;
if LpProxyConfig <> nil then
begin
if LpProxyConfig^.fAutoDetect then
begin
LAutoProxyOptions.dwFlags := WINHTTP_AUTOPROXY_AUTO_DETECT;
LAutoProxyOptions.dwAutoDetectFlags := WINHTTP_AUTO_DETECT_TYPE_DHCP or WINHTTP_AUTO_DETECT_TYPE_DNS_A;
end;
if LpProxyConfig^.lpszAutoConfigURL <> '' then
begin
LAutoProxyOptions.dwFlags := WINHTTP_AUTOPROXY_CONFIG_URL;
LAutoProxyOptions.lpszAutoConfigUrl := LpProxyConfig^.lpszAutoConfigUrl;
LAutoDetectProxy := True;
end
else
begin
AProxy := LpProxyConfig^.lpszProxy;
AProxyBypass := LpProxyConfig^.lpszProxyBypass;
// *** hier die einfache Änderung: haben wir keinen manuellen Proxy bekommen,
// *** dann sorgen wir hier dafür, dass wir später in dem Block landen, in dem
// *** GetProxyForURL aufgerufen wird
LAutoDetectProxy := AProxy = '';
end;
end
else
[... weiter wie oben ...]
Mit dieser einfachen Änderung funktioniert es tatsächlich beim Kunden. "Richtiger" wäre wahrscheinlich etwas in diese Richtung:
Delphi-Quellcode:
function GetProxyInfo(const AURL: string; var AProxy, AProxyBypass: string): Boolean;
var
LAutoDetectProxy: Boolean;
LpProxyConfig: PWinHTTPCurrentUserIEProxyConfig;
LWinHttpProxyInfo: TWinHTTPProxyInfo;
LAutoProxyOptions: TWinHTTPAutoProxyOptions;
begin
Result := True;
AProxy := '';
AProxyBypass := '';
FillChar(LAutoProxyOptions, SizeOf(LAutoProxyOptions), 0);
LpProxyConfig := TWinHttpLib.GetProxyConfig;
if LpProxyConfig <> nil then
begin
// *** AutoDetect mal klar initialisieren
LAutoDetectProxy := False;
if LpProxyConfig^.fAutoDetect then
begin
LAutoProxyOptions.dwFlags := WINHTTP_AUTOPROXY_AUTO_DETECT;
LAutoProxyOptions.dwAutoDetectFlags := WINHTTP_AUTO_DETECT_TYPE_DHCP or WINHTTP_AUTO_DETECT_TYPE_DNS_A;
// *** AutoDetect hier auch tatsächlich aktivieren, wenn es in der Config so steht!
LAutoDetectProxy := True;
end;
if LpProxyConfig^.lpszAutoConfigURL <> '' then
begin
LAutoProxyOptions.dwFlags := WINHTTP_AUTOPROXY_CONFIG_URL;
LAutoProxyOptions.lpszAutoConfigUrl := LpProxyConfig^.lpszAutoConfigUrl;
LAutoDetectProxy := True;
end
// *** Proxy explizit setzen, wenn wirklich keine AutoDetect aktiviert
else if not LAutoDetectProxy then
begin
AProxy := LpProxyConfig^.lpszProxy;
AProxyBypass := LpProxyConfig^.lpszProxyBypass;
end;
end
else
[... weiter wie oben ...]
Soweit, das, was ich rausgefunden habe. Ist für mich immer recht schwer zu testen, weil ich im Büro bei mir ohne Proxy arbeite, aber das Ergebnis vom Kunden war mit der Änderung (und ein paar Debug-Ausgaben) schon sehr eindeutig. Oder übersehe ich etwas?
Als Workaround bis das von Emba mal gefixt wird, fällt mir nur ein, die System.Net.HttpClient.Win lokal ins Projekt einzubauen und die Stellen wie oben angegeben zu korrigieren. Hat hier jemand eine bessere Idee?