AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke Proxy-Erkennung für REST-Client/HTTP-Client nicht korrekt

Proxy-Erkennung für REST-Client/HTTP-Client nicht korrekt

Ein Thema von Bbommel · begonnen am 16. Mai 2022 · letzter Beitrag vom 16. Mai 2022
Antwort Antwort
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
648 Beiträge
 
Delphi 12 Athens
 
#1

Proxy-Erkennung für REST-Client/HTTP-Client nicht korrekt

  Alt 16. Mai 2022, 10:43
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?
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.058 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: Proxy-Erkennung für REST-Client/HTTP-Client nicht korrekt

  Alt 16. Mai 2022, 11:38
Ich habe das Problem nur mit einem Auge überflogen, aber TWinHttpCurrentUserIEProxyConfig/WINHTTP_CURRENT_USER_IE_PROXY_CONFIG wird von der Windows-Funktion WinHttpGetIEProxyConfigForCurrentUser im Aufruf von TWinHttpLib.GetProxyConfig richtig mit den erwarteten Werten gefüllt oder ist das auch schon Quatsch?
  Mit Zitat antworten Zitat
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
648 Beiträge
 
Delphi 12 Athens
 
#3

AW: Proxy-Erkennung für REST-Client/HTTP-Client nicht korrekt

  Alt 16. Mai 2022, 11:44
Das sieht für mich korrekt aus. LpProxyConfig^.fAutoDetect ist dann auf true und lpszProxy ist leer, wenn ich alles auf Autoconfig stehen habe. So hätte ich das auch erwartet - nur, dass, was dann draus gemacht wird, ist fehlerhaft.

Ich hatte auch mal die Gegenprobe gemacht und manuell einen Proxy eingetragen. Der wird von TWinHttpLib.GetProxyConfig über WinHttpGetIEProxyConfigForCurrentUser korrekt ausgelesen und steht dann in lpszProxy und fAutoDetect ist dann auch false.

An der Stelle scheint mir das Problem also nicht zu liegen.
  Mit Zitat antworten Zitat
mytbo

Registriert seit: 8. Jan 2007
455 Beiträge
 
#4

AW: Proxy-Erkennung für REST-Client/HTTP-Client nicht korrekt

  Alt 16. Mai 2022, 14:05
...Dort gibt es die Methode TWinHTTPRequest.SetWinProxySettings und diese hat wiederum die Unter-Funktion GetProxyInfo, welche nach meiner Analyse fehlerhaft ist:
Du könntest zum Vergleich deine Bugfix-Lösung mit der mORMot Funktion WinHttpGetProxyInfo() in Unit mormot.lib.winhttp vergleichen.

Bis bald...
Thomas
  Mit Zitat antworten Zitat
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
648 Beiträge
 
Delphi 12 Athens
 
#5

AW: Proxy-Erkennung für REST-Client/HTTP-Client nicht korrekt

  Alt 16. Mai 2022, 15:56
Danke noch für die Rückmeldung, Thomas! Das geht ja sehr in die Richtung wie auch in meinem zweiten Vorschlag.

Hier der QP-Eintrag RSP-38236.
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:32 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz