Einzelnen Beitrag anzeigen

ele

Registriert seit: 18. Feb 2009
129 Beiträge
 
Delphi 2010 Professional
 
#1

Indy TIdCustomHttpServer utf-8 Bug - Workaround gesucht

  Alt 20. Okt 2010, 16:37
Delphi-Version: 5
Hallo Leute,

Ich habe da ein kleines Problem, das mir Kopfschmerzen bereitet. Folgendes Szenario:

Ich habe einen Delphi-Web-Server der auf den Indy-Komponenten aufbaut. Wenn der Browser einen Get oder Post-Request absetzt, welcher in den Parametern Unicode-Zeichen (Umlaute, Akzente etc.) enthält, so werden diese vom Browser mit UrlEncode codiert. Also z.B ein Parameter namens MyParam mit dem Wert 'Über':

MyParam=%C3%9Cber

Das Problem liegt daran, dass Indy (D2010) zuerst ein Utf8Decode vornimmt und erst danach das UrlDecode macht, was leider ein Problem ist, denn die Zeichen #$C3 und #$9C werden als einzelne Zeichen gesetzt. Konkret wird dann daraus:

MyParam=Ã'#$009C'ber

oder Hexadezimal:

$4D $00 $79 $00 $50 $00 $61 $00 $72 $00 $61 $00 $6D $00 $3D $00 $C3 $00 $9C $00 $62 $00 $65 $00 $72 $00

Was ich aber gerne hätte ist:

MyParam=Ü'ber

oder Hexadezimal:

$4D $00 $79 $00 $50 $00 $61 $00 $72 $00 $61 $00 $6D $00 $3D $00 $DC $00 $62 $00 $65 $00 $72 $00

Der fehler liegt klar bei Indy, weil zuerst das Utf8decode vorgenommen wird und danach das UrlDecode gemacht wird anstatt umgekehrt. Ich weiss nicht ob der Fehler in einer aktuelleren Version behoben worden ist, aber erste Versuche mit einer aktuellen Version sind an anderen Problemen gescheitert (veränderte API und ein noch ungeklärtes Problem mit der Cookie-Version).

Deshalb suche ich einen Workaround. Und zwar habe ich mir gedacht ich könnte den fehlerhaften String wieder zurückkonvertieren, dass er so aussieht wie der Browser ihn gesendet hat und danach die Korrekte decodierung vornehmen. Aber die Theorie scheitert an der Praxis, es will mir einfach nicht gelingen den ursprünglichen String wiederherzustellen:

Delphi-Quellcode:
uses
  IdURI;

function URLEncode(Text: String): String;
var
  i: Integer;

begin
  Result := '';
  i := 1;

  while i <= Length(Text) do
  begin
    if CharInSet(Text[i], ['A'..'Z', 'a'..'z', '0'..'9', '-', '_', '.', '~' , '/']) and not CharInSet(Text[i], ['!', '*', '''', '(', ')', ';', ':', '@', '&', '=', '+', '$', ',', '?', '#', '%', '[', ']']) then
    begin
      Result := Result + Text[i];
      Inc(i);
    end
    else
    begin
      Result := Result + '%' + IntToHex(Ord(Text[i]), 2);
      inc(i);
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Input: String;
  WrongString: String;
  CorrectedString: String;
  RightString: String;
  RBS: RawByteString;
  s: string;
  Output: String;

begin
  Input := '%C3%9Cber'; // Der Ausgangsstting

  WrongString := TIdURI.URLDecode(UTF8ToString(Input)); // Das ist das was Indy liefert
  RightString := 'Über'; // So müsste das Ergebnis aussehen


  { URLEncode rückgängig machen:
    TidURI.URLEncode mag es nicht wenn der übergebene String keine URL
    ist - bzw. das Protokoll fehlt. Deswegen wird hier meine eigene Version von URLDecode() verwendet.
  }

  s := URLEncode(WrongString);

  // Utf8Decode rückgängig machen
  CorrectedString := UTF8Encode(s);

  // und richtig decodieren
  s := TIdURI.URLDecode(CorrectedString);
  RBS := s; // !!! Hauptproblem ist hier

  { s  : $C3 $00 $9C $00 $62 $00 $65 $00 $72 $00
    RBS: $C3 $3F $62 $65 $72
    gewünscht wäre: $C3 $9C $62 $65 $72
  }


  Output := UTF8ToString(RBS);

  ShowMessage('Expected String: ' + RightString + #13 + 'Computed String:' + Output);
end;
Das Hauptproblem scheint die konversion von UnicodeString zu RawByteString. Delphi nimmt dort irgendwie eine konversion vor, die gar nicht erwünscht ist.

Ich bin für jede Hilfe dankbar.
  Mit Zitat antworten Zitat