AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Indy 10 HTTP(S) Protokoll

Indy 10 HTTP(S) Protokoll

Ein Tutorial von geskill · begonnen am 29. Apr 2011 · letzter Beitrag vom 30. Jan 2020
Antwort Antwort
Seite 2 von 2     12
Benutzerbild von geskill
geskill
Registriert seit: 17. Feb 2007
.
Worum geht es hier?
Die Indy Komponenten bieten eine gute Möglichkeit um HTTP(S) Verbindungen zu ermöglichen. Wenn du also Informationen an eine Webseite senden oder empfangen willst bist du hier genau richtig.

Inhalt
  1. HTTP Komponente
    • Allgemeines
    • Daten Empfangen
    • Daten Senden
    • Request Einstellungen
    • Response Informationen
    • Weitere Einstellungen
  2. Cookies
  3. Gzip
  4. SSL
  5. HTTP Proxy / SOCKS 4, 5
  6. Konklusion
  7. Tools
    • WireShark
    • FireFox Web Developer

1. HTTP Komponente
Allgemeines
Die Komponente findet ihr in der Tool-Palette unter "Indy Clients". Dort klickt ihr auf TIdHTTP (Weltkugel) und zieht dies auf euer Formular. Delphi bindet nun automatisch eine Reihe von Units ein wichtig ist dabei IdHTTP, diese Datei enthält alle benötigten Methoden. Alternativ kann man die Klasse auch zur Laufzeit erstellen, ich bevorzuge diese Methode und gebe deshalb alle Beispiele so an.

Daten Empfangen
Die TIdHTTP-Klasse stellt eine Funktion GET zur Verfügung, damit lässt sich der komplette Quellcode von einer Webseite empfangen:
Delphi-Quellcode:
uses
  IdHTTP;
// ...

var
  ResponseStr: string;
begin
  with TIdHTTP.Create(nil) do
    try
      // Empfange den Quellcode
      ResponseStr := Get('http://www.delphipraxis.net/');

      // in den neueren Indy Releases (10.5.8+) erfolgt das Encoding automatisch,
      // vorher musste man sich behelfen (mit einem TMemoryStream oder TStringStream)
      // dazu: http://forums2.atozed.com/viewtopic.php?f=7&t=25756

      // Mache hier irgendwas mit dem Quellcode
      DoSomething(ResponseStr);
    finally
      Free;
    end;
Daten Senden
Zum Senden von Daten wie Login Informationen oder Dateien bietet die Klasse eine Funktion POST. Es gibt nun 3 verschiedene Möglichkeiten, wie man die Daten sendet. Entweder mit einer TStringList, wenn man keine Dateien senden muss, aber bestimmte Formularfelder ansprechen möchte. Wenn man eine API ansprechen möchte z.B. Google Blogger dann sendet man, ohne ein spezielles Formularfeld anzusprechen oder man nutzt die Klasse TIdMultiPartFormDataStream aus der Unit IdMultipartFormData. Oft ist es auch so, dass die Seite vorgibt, wie die Daten gesendet werden müssen. Im HTML Quelltext sollte man im form-Tag auf das Attribut "enctype" achten.
Hier stand mal das man keine TStringList benutzen sollte und stattdessen einen TStringStream und dann die Werte selber codieren sollte, dass ist bei alten Indy Versionen notwendig, in den aktuelleren kann man dies aber auch getrost Indy überlassen - so geht's:
Delphi-Quellcode:
uses
  IdGlobalProtocols, IdHTTP;
// ...

var
  Params: TStringList;
  Enc: TEncoding;
  ResponseStr: string;
begin
  with TIdHTTP.Create(nil) do
    try
      // der ContentType beschreibt in welchem Format die Daten an
      // den Server gesendet werden
      Request.ContentType := 'application/x-www-form-urlencoded';

      Params := TStringList.Create;
      try
        with Params do
        begin
          Add('vb_login_username=' + AccountName);
          Add('vb_login_password=' + AccountPassword);
          Add('securitytoken=guest');
          Add('do=login');
          Add('vb_login_md5password=');
          Add('vb_login_md5password_utf=');
          Add('cookieuser=1');
          Add('s=');
        end;

        // Request.CharSet vorher setzten: z.B: ISO-8859-1
        Enc := CharsetToEncoding(Request.CharSet);
        try
          // Daten senden
          ResponseStr := Post('http://www.delphipraxis.net/login.php?do=login', Params, Enc);
        finally
          Enc.Free;
        end;

      finally
        Params.Free;
      end;
    finally
      Free;
    end;
Delphi-Quellcode:
uses
  IdGlobalProtocols, IdHTTP;
// ...

var
  Data: TStringStream;

  ResponseStr: string;
begin
  with TIdHTTP.Create(nil) do
    try
      Request.ContentType := 'application/atom+xml';
      with Request.CustomHeaders do
      begin
        Add('GData-Version: 2');
        Add('Authorization: GoogleLogin auth=' + {Hier käme ein Autorisierungswert rein});
      end;

      // Hier kann auch TStringStream.Create('', CharsetToEncoding(Request.CharSet)) genutzt werden
      Data := TStringStream.Create('', CP_UTF8);
      try
        Data.WriteString( newPageXMLDoc() ); // interne Funktion zum erstellen der API Requests

        Request.CharSet := 'UTF-8';
        
        ResponseStr := Post('http://www.blogger.com/feeds/' + BloggerSettings.id + '/posts/default', Data);
      finally
        Data.Free;
      end;

  // Wenn ResponseCode = 201 dann war das Eintragen erfolgreich.
Delphi-Quellcode:
uses
  IdHTTP, IdMultipartFormData, IdGlobalProtocols;
// ...

var
  Params: TIdMultiPartFormDataStream;
  ResponseStr: string;

  _filename: string;
begin
  with TIdHTTP.Create(nil) do
    try
      // der ContentType beschreibt in welchem Format die Daten an
      // den Server gesendet werden
      Request.ContentType := 'multipart/form-data';

      Params := TIdMultiPartFormDataStream.Create;
      try
        with Params do
        begin
          // Für kleinere Text oder Zahlenwerte, wo man sicher gehen kann, dass es
          // nicht zu Kodierungsproblemen kommen kann bietet sich diese Methode an
          AddFormField('einfachertext', 'text');

          // In der neueren Version ist diese Funktion zwar deprecated, aber um wirklich
          // sicher zu gehen, dass die Daten richtig Kodiert sind verwende ich diese Variante.
          // Der erste Parameter gibt den Elementnamen und der letze den Elementwert an.
          // für alte Indy's: AddObject('text_in_utf8', '', '', TStringStream.Create('text', TEncoding.UTF8));

          // Ab den aktuellen Funktioniert dies analog:
          AddFormField('text_in_utf8', 'text', 'UTF-8').ContentTransfer := 'binary';

          // zu ContentTransfer: http://stackoverflow.com/questions/7189794/indy-adds-at-every-72nd-char-with-multi-part-form-data-post

          // Das äquivalent zu oben nur für Webseiten die im ISO-8859-1 kodiert sind
          // für alte Indy's: AddObject('text_in_ascii', '', '', TStringStream.Create('text', TEncoding.ASCII));
          AddFormField('text_in_ascii', 'text', 'ISO-8859-1').ContentTransfer := 'binary';

          // Wie versprochen kann man hiermit auch Dateien senden, damit der MIME-
          // Type passt sollte man die GetMIMETypeFromFile() Funktion aus der Unit
          // IdGlobalProtocols verwenden.
          AddFile('file', _filename, GetMIMETypeFromFile(_filename));
        end;

        // Daten senden
        ResponseStr := Post('http://deineseite.com/', Params);

      finally
        Params.Free;
      end;
    finally
      Free;
    end;
Request Einstellungen
Der HTTP Client sendet beim Empfangen oder Senden von Daten nebenher noch weitere Informationen. Eine haben wir eben schon kennengelernt, den ContentType. Die Definition von diesen Werten ist eigentlich optional, jedoch gibt es viele Server die Probleme mit den Standardangaben haben.
Delphi-Quellcode:
with TIdHTTP.Create(nil) do
  try
    // Die Accept Angabe definiert, welche Formen von Daten der Client akzeptiert
    Request.Accept := 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1';
    // Der AcceptCharSet Wert definiert, welche Zeichen-Formate der Client akzeptiert
    Request.AcceptCharSet := 'iso-8859-1, utf-8, utf-16, *;q=0.1';
    // Die AcceptEncoding Angabe definiert, welche Kompressionsformate der Client akzeptiert
    Request.AcceptEncoding := 'deflate, identity, *;q=0';
    Request.Connection := 'Keep-Alive';
    // Der Referer definiert, auf welcher Webseite wir zuvor waren. Gerade dieser Wert
    // wird gerne von Webseiten abgefragt um ungewünschte Bots zu blocken.
    Request.Referer := 'http://deineseite.com/';
    // Die Client Erkennung, um sich zu tarnen benutze ich gerne den Opera User-Agent
    Request.UserAgent := 'Opera/9.80 (Windows NT 6.1; U; de) Presto/2.5.22 Version/10.51';
//...
Response Informationen
Wenn man den Quelltext von einer Webseite empfängt werden noch weitere Protokoll Informationen gesendet.
Delphi-Quellcode:
//...

        ResponseStr := Get('http://www.delphipraxis.net/');

        // eine Mögliche Form der Weiterleitung an eine andere Adresse oder Unterseite
        // ist der Location-Header.
        ShowMessage(Response.Location);

        // Natürlich hat man auch auf die ganzen Statuscodes einen Zugriff (á la 404 Not Found) mit
        // Response.ResponseCode und Response.ResponseText

        // Alle Header Informationen kann man so Ausgeben:
        ShowMessage(Response.RawHeaders.Text);

//...
Weitere Einstellungen
Neben den ganzen Header Informationen kann man natürlich noch Timeouts und beispielsweise das Verhalten bei Weiterleitungen definierten.
Delphi-Quellcode:
with TIdHTTP.Create(nil) do
  try
    // Behandle Weiterleitungen (Standard = False)
    HandleRedirects := True;
    // Leite Maximal X mal weiter (Standard = 15)
    // Meistens reicht eine Weiterleitung, mehr ist oft nur Trafficwaste
    RedirectMaximum := 1;

    // Zeit in Millisekunden, wie lange gewartet wird, bis...
    // ...der Client zum Server verbunden ist
    ConnectTimeout := 5000; // 5 Sekunden
    // ...der Client alle geforderten Daten vom Server gelesen hat
    ReadTimeout := 15000; // 15 Sekunden
//...
2. Cookies
Hiermit handelt es sich nicht um das Problem, dass es keine Kekse gibt, sondern um kleine Dateien, die eine Webseite auf dem Client erstellt um Daten wie z.b. eine Sitzung's-ID zu speichern. Auch die HTTP Komponente unterstützt dies. Dazu reicht diese Einstellung:
Delphi-Quellcode:
uses
  IdHTTP, IdURI;
//...

with TIdHTTP.Create(nil) do
  try
    // der Cookie Manager wird nun automatisch angelegt
    AllowCookies := True;
    

    // Man kann natürlich Cookies auch modifizieren:

    //... irgendwelche Daten werden empfangen, dabei setzt der Server Cookies

    with CookieManager.CookieCollection do
      for I := 0 to Count - 1 do // alle Cookies durchsuchen
        if ('CookieName' = Cookies[I].CookieName) then
        begin
          MacheWasMitDemCookieWert(Cookies[I].Value);
          break;
        end;

    // Cookies werden so erstellt:
    // TIdURI Klasse aus: IdURI
    CookieManager.AddServerCookie('werbung_aus=1', TIdURI.Create('http://www.delphipraxis.net/'));

//...
Hinweis: Die Cookie Implementation ist erst seit 10.5.8 wirklich alterstauglich.

3. Gzip
Je umfangreicher die Webseite, desto mehr Quelltext muss geladen werden und umso länger dauert dies auch. Mit Gzip kann, wenn der Server das unterstützt der Datenaustausch komprimiert werden. Dazu muss zusätzlich die Unit IdCompressorZLib eingebunden werden.
Delphi-Quellcode:
uses
  IdHTTP, IdCompressorZLib;
// ...

with TIdHTTP.Create(nil) do
  try
    Compressor := TIdCompressorZLib.Create(nil); // Beim Freigeben der HTTP Komponente wird der Compressor mit freigegeben.

    // Nun dürfen wir im AcceptEncoding definieren, dass gzip Unterstützt wird
    Request.AcceptEncoding := 'deflate, gzip, identity, *;q=0';

    // Manche Server machen Probleme mit der Kompression, sodass es zu Fehlern kommen kann,
    // diese kann man leicht abfangen, die Dekompression klappte bei mir jedoch immer richtig.
    try
      // Bei Indy Versionen älter 10.5.8 muss man hierbei immer mit Streams arbeiten, weil sonst werden die Daten nicht dekomprimiert:
      // Post('http://deineseite.com/', Params, ReplyData); // ReplyData vom Typ TMemoryStream
      // ReplyData.Position := 0; nicht vergessen :)
      // ResponseStr := ReadStringAsCharset(ReplyData, Response.Charset);

      QuellcodeStringVariable := Post('http://deineseite.com/', Params);
    except
      on E: EDecompressionError do
        ; // Mache nichts (ich weiß das sieht nicht professionell aus)
      // Weitere Fehlerbehandlungen
    end;
//...
4. SSL
Das HTTPS Protokoll verschlüsselt den kompletten Datenverkehr. Vorab: Hierfür werden die passenden Versionen der libeay32.dll und ssleay32.dll Bibliothek benötigt. Die aktuelle Version findet man immer in diesem Thema. Die DLLs müssen ins root-Verzeichnis eurer Applikation. Zusätzlich müssen wir die Unit IdSSLOpenSSL einbinden.
Delphi-Quellcode:
uses
  IdHTTP, IdSSLOpenSSL;
// ...

with TIdHTTP.Create(nil) do
  try
    IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
//...
5. HTTP Proxy / SOCKS 4, 5
Was ich euch hier zeigen möchte ist eine Kombi-Lösung für die verschiedenen Proxy Varianten, d.h. man sollte sich eine eigene Proxy Klasse schreiben mit einem Proxy-Typ, der dann generell alles ermöglicht. Damit dies auch in Kombination mit SSL funktioniert habe ich auch dies eingebaut. Natürlich könnte man, wenn man wirklich nur SOCKS 5 benötigt das dementsprechend vereinfachen, aber wie gesagt, hier ein flexibles Beispiel:
Delphi-Quellcode:
uses
  IdHTTP, IdSSLOpenSSL, IdSocks;
// ...

var
  FIdSSLIOHandlerSocketOpenSSL: TIdSSLIOHandlerSocketOpenSSL;
  FIdSocksInfo: TIdSocksInfo;
begin
  with TIdHTTP.Create(nil) do
    try
      FIdSSLIOHandlerSocketOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
      with FIdSSLIOHandlerSocketOpenSSL.SSLOptions do
      begin
        Method := sslvTLSv1_2;
        SSLVersions := [sslvTLSv1_2];
      end;
      FIdSocksInfo := TIdSocksInfo.Create(nil);

      if { SOCKS wird benutzt ??? } then
        with FIdSocksInfo do
        begin
          Host := 'Server';
          Port := 8080; // Beispiel Port

          if { SOCKS Version = ??? } then
             Version := svSocks4
           else
             Version := svSocks5;

          case { RequireAuthentication ??? } of
            True:
              Authentication := saUsernamePassword;
            False:
              Authentication := saNoAuthentication;
          end;

          Username := 'AccountName';
          Password := 'AccountPassword';

          // Nun SOCKS aktivieren
          Enabled := True;
        end
        else // normale HTTP-Proxy wird benutzt
          with ProxyParams do
          begin
            ProxyServer := 'Server';
            ProxyPort := 80; // Beispiel Port
            BasicAuthentication := True; // RequireAuthentication ???
            ProxyUsername := 'AccountName';
            ProxyPassword := 'AccountPassword';
          end;

      // Erst dem SSL-Handler die Mögliche Proxy/SOCKS Konfiguration zuweisen
      // andersrum greift es nicht!
      FIdSSLIOHandlerSocketOpenSSL.TransparentProxy := FIdSocksInfo;

      // Jetzt der HTTP Komponente zuweisen
      IOHandler := FIdSSLIOHandlerSocketOpenSSL;
//...
6. Konklusion
Wie man sieht unterstützt die HTTP Komponente alle nötigen Funktionen eines Browsers. Ich höre/lese immer wieder, dass eine Webseite nicht auslesen werden kann, weil die Komponente kein JavaScript unterstützt. Diese Aussage kann ich zwar nicht widerlegen, jedoch ist mir noch keine Webseite unter den Nagel gekommen, wo die Komponente an ihre Grenzen gestoßen ist.

Wenn ihr jetzt also selber viele Teile hiervon benötigt, empfehle ich eine eigene Klasse zu schreiben, die von TIdHTTP erbt. Darin setzt ihr alle Voreinstellungen, damit man das nicht bei jeder Verwendung machen muss (okay eigentlich selbstverständlich nur ..., ja genau).

7. Tools
WireShark
Bei der Client/Server Kommunikation können sehr leicht Fehler auftreten, meistens weiß man aber nicht so genau, was überhaupt übertragen wurde, deshalb kann ich jetzt nun schon aus langjähriger Erfahrung dieses umfangreiche und kostenlose Programm empfehlen.
Filter:
Damit man nur den HTTP Datenverkehr angezeigt bekommt.
Code:
http && (http.response.code != 0) || (tcp.dstport == 80)
FireFox Web Developer
Ich bin wohl der letzte Mensch der freiwillig dauerhaft diesen Browser benutzen würde, jedoch gibt es dafür ein wunderbares Plugin, welches von einer beliebigen Webseite alle Formularfelder sauber in einer strukturierten Tabelle anzeigen kann. Sprich man muss sich nicht mehr durch den Quelltext-Dschungel kämpfen.

Da dies nun mein erstes Tutorial ist, hoffe ich, dass nicht allzu viel falsch gemacht wurde
.

Geändert von geskill (30. Okt 2015 um 01:14 Uhr) Grund: Updated to TLS 1.2 SSL.
 
Benutzerbild von Sherlock
Sherlock

 
Delphi 11 Alexandria
 
#11
  Alt 12. Sep 2018, 14:10
Randbemerkung: Um OpenSSL kann man aber nur einen Bogen machen, wenn man nicht plattformübergreifend entwickeln will.

Sherlock
Oliver
  Mit Zitat antworten Zitat
Hobbycoder
 
#12
  Alt 12. Sep 2018, 14:14
Ja. Um OpenSSL würde ich, wenn möglich, einen grossen Bogen machen. Die System.Net Funktionen laufen nativ auf Windows und brauchen daher kein OpenSSL.
Das ist schön zu wissen. Mich hat das immer gestört, fremde DLL's mit auszuliefern.
Wobei ich jetzt aber auch dazu sagen muss, dass ich mit Indy+OpenDLL nie wirklich Probleme hatte, außer denen die ich selbst reinprogrammiert habe
  Mit Zitat antworten Zitat
hoika

 
Delphi 10.4 Sydney
 
#13
  Alt 12. Sep 2018, 14:38
Hallo,
zu Redirects findest Du für Indy hier was

https://stackoverflow.com/questions/...rl-redirection
Heiko
  Mit Zitat antworten Zitat
CCRDude

 
FreePascal / Lazarus
 
#14
  Alt 12. Sep 2018, 18:39
Ja. Um OpenSSL würde ich, wenn möglich, einen grossen Bogen machen. Die System.Net Funktionen laufen nativ auf Windows und brauchen daher kein OpenSSL.
Nativ kann aber auch von Nachteil sein. Gerade erst erlebt, auf dem Server wollten wir veralterte TLS-Versionen abschalten - ging aber noch nicht, da die Software nativ zugreift und damit auf altem Windows noch ein altes TLS verwendet.
  Mit Zitat antworten Zitat
Benutzerbild von Minz3
Minz3

 
Delphi 10.1 Berlin Enterprise
 
#15
  Alt 27. Jan 2020, 13:38
Hey ho,

danke für das Tutorial, es hat mir gut geholfen!

An einer Stelle stolpere ich allerdings etwas und zwar bei der POST Methode.

Ich hab den Code aus dem Tutorial übernommen und etwas angepasst:
Delphi-Quellcode:
procedure TProgWebAppFrm.HTTPPost(Username : String; Password : String; Database : String);
var
  Params: TStringList;
  Enc: TEncoding;
  ResponseStr: string;

begin
  with TIdHTTP.Create(nil) do
  try
    Request.ContentType := 'application/x-www-form-urlencoded';

    Params := TStringList.Create;
    try
      with Params do
      begin
        Add('username=' + Username);
        Add('password=' + Password);
        Add('database=' + Database);
      end;

      // Request.CharSet vorher setzten: z.B: ISO-8859-1
      Enc := CharsetToEncoding(Request.CharSet);
      try
        // Daten senden
        ResponseStr := Post('https://nb1819157:5001/login?', Params, Enc);
      finally
        Enc.Free;
      end;

    finally
      Params.Free;
    end;
  finally
    Free;
  end;
end;
An folgender Stelle:
Enc := CharsetToEncoding(Request.CharSet); Kommt nun der Fehler, dass die Typen TEncoding und IIdTextEncoding nicht miteinander kompatibel sind. Das verstehe ich soweit auch, aber wie wurde das dann im Tutorial gemacht? Es schien ja so mal funktioniert zu haben. Wurden mit der Zeit Libraries angepasst so das die Inkompabilität jetzt zustande kommt?
  Mit Zitat antworten Zitat
hoika

 
Delphi 10.4 Sydney
 
#16
  Alt 29. Jan 2020, 14:34
Hallo,
du hast den Kommentar überlesen?

// Request.CharSet vorher setzten: z.B: ISO-8859-1
Heiko
  Mit Zitat antworten Zitat
Benutzerbild von Minz3
Minz3

 
Delphi 10.1 Berlin Enterprise
 
#17
  Alt 29. Jan 2020, 15:32
Hallo,
du hast den Kommentar überlesen?

// Request.CharSet vorher setzten: z.B: ISO-8859-1
Scheinbar hab ich das einfach hingenommen.
Wird der ganz einfach mit
Request.CharSet := 'UTF-8'; gesetzt oder muss ich da noch was beachten?

*Edit:
Auch wenn das Charset gesetzt wäre, also nur mal angenommen, so wäre es vom Typ immer noch IIdTextEncoding und damit noch inkompatibel zu TEncoding. Eine Idee, wie ich das beheben kann?

Geändert von Minz3 (29. Jan 2020 um 15:39 Uhr)
  Mit Zitat antworten Zitat
hoika

 
Delphi 10.4 Sydney
 
#18
  Alt 30. Jan 2020, 01:06
Hallo,
da bin ich raus
Müsste mal auf Arbeit nachsehen, was ich da so gesetzt hatte.
Heiko
  Mit Zitat antworten Zitat
Themen-Optionen Tutorial durchsuchen
Tutorial 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 09:29 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