14 Stimmen

Lesen der "chunked" Antwort mit HttpWebResponse

Ich habe Probleme beim Lesen einer "Chunked"-Antwort, wenn Sie einen StreamReader verwenden, um den von GetResponseStream() einer HttpWebResponse zurückgegebenen Stream zu lesen:

// response is an HttpWebResponse
StreamReader reader = new StreamReader(response.GetResponseStream());
string output = reader.ReadToEnd(); // throws exception...

Wenn die reader.ReadToEnd() Methode aufgerufen wird, erhalte ich die folgende System.IO.IOException: Es ist nicht möglich, Daten aus der Transportverbindung zu lesen: Die Verbindung wurde geschlossen.

Der obige Code funktioniert einwandfrei, wenn der Server eine "non-chunked" Antwort zurückgibt.

Der einzige Weg, den ich gefunden habe, um es zum Funktionieren zu bringen, ist die Verwendung von HTTP/1.0 für die erste Anfrage (anstelle von HTTP/1.1, dem Standard), aber das scheint ein lahmer Workaround zu sein.

Irgendwelche Ideen?


@Chuck

Ihre Lösung funktioniert ziemlich gut. Es wird immer noch die gleiche IOException beim letzten Read() geworfen. Aber nachdem ich den Inhalt des StringBuilders untersucht habe, sieht es so aus, als ob alle Daten empfangen wurden. Vielleicht muss ich also nur die Read() in eine try-catch verpacken und den "Fehler" schlucken.

0 Stimmen

Um Chunked Response zu lesen, müssen Sie Folgendes tun de.wikipedia.org/wiki/Chunked_transfer_encoding

0 Stimmen

Ich erlebe dieses Verhalten mit .NET 4.6 bei der Verbindung mit der PowerDNS 3.4.5 HTTP REST API. Die Workarounds helfen nicht. Wenn ich die Ausnahme verschlucke, verliere ich einen Teil der Antwort.

2voto

Ich habe es noch nicht mit einer "chunked" Antwort versucht, aber würde so etwas funktionieren?

StringBuilder sb = new StringBuilder();
Byte[] buf = new byte[8192];
Stream resStream = response.GetResponseStream();
string tmpString = null;
int count = 0;
do
{
     count = resStream.Read(buf, 0, buf.Length);
     if(count != 0)
     {
          tmpString = Encoding.ASCII.GetString(buf, 0, count);
          sb.Append(tmpString);
     }
}while (count > 0);

5 Stimmen

Dies ist gefährlich für Multibyte-Kodierungen (d.h. nicht ASCII), weil es keine Garantie dafür gibt, dass die Lesungen an Zeichengrenzen ausgerichtet werden.

2 Stimmen

@Chuck Sie können nicht einfach ASCII verwenden, Sie müssen herausfinden, welche Kodierung tatsächlich verwendet wird, z. B. mit Hilfe von Content-Type, und dies dann für "GetString" verwenden.

1voto

user2186152 Punkte 11

Ich arbeite gerade an einem ähnlichen Problem. Die .net HttpWebRequest und HttpWebRequest behandeln Cookies und Weiterleitungen automatisch, aber sie behandeln nicht chunked Inhalt auf die Antwortkörper automatisch.

Das liegt vielleicht daran, dass gechunkter Inhalt mehr als nur einfache Daten enthalten kann (z. B.: Chunk-Namen, Kopfzeilen am Ende).

Einfaches Lesen des Streams und Ignorieren der EOF-Ausnahme wird nicht funktionieren, da der Stream mehr als den gewünschten Inhalt enthält. Der Stream enthält Chunks und jeder Chunk beginnt mit der Angabe seiner Größe. Wird der Stream einfach vom Anfang bis zum Ende gelesen, enthalten die endgültigen Daten die Metadaten des Chunks (und falls es sich um gziped Inhalte handelt, wird die CRC-Prüfung beim Dekomprimieren fehlschlagen).

Um das Problem zu lösen, muss der Datenstrom manuell geparst werden, wobei die Chunk-Größe aus jedem Chunk (sowie die CR-LF-Begrenzer) entfernt, der letzte Chunk erkannt und nur die Chunk-Daten behalten werden. Wahrscheinlich gibt es irgendwo da draußen eine Bibliothek, die dies tut, ich habe sie noch nicht gefunden.

Nützliche Ressourcen :

http://en.wikipedia.org/wiki/Chunked_transfer_encoding https://www.rfc-editor.org/rfc/rfc2616#section-3.6.1

0voto

Steven Craft Punkte 1215

Nach dem Ausprobieren eine Menge von Schnipseln aus StackOverflow und Google, schließlich fand ich dies am besten funktionieren (vorausgesetzt, Sie wissen, dass die Daten eine UTF8-Zeichenfolge, wenn nicht, können Sie nur das Byte-Array und entsprechend verarbeiten):

byte[] data;
var responseStream = response.GetResponseStream();
var reader = new StreamReader(responseStream, Encoding.UTF8);
data = Encoding.UTF8.GetBytes(reader.ReadToEnd());
return Encoding.Default.GetString(data.ToArray());

Ich habe festgestellt, dass andere Varianten zwar meistens funktionieren, aber gelegentlich die Daten abschneiden. Ich habe dieses Snippet von:

https://social.msdn.microsoft.com/Forums/en-US/4f28d99d-9794-434b-8b78-7f9245c099c4/problems-with-httpwebrequest-and-transferencoding-chunked?forum=ncl

0voto

Vanden Punkte 1

Das ist lustig. Als ich mit dem Request-Header spielte und "Accept-Encoding: gzip,deflate" entfernte, antwortete der Server in meinem Anwendungsfall in einfacher ASCII-Form und nicht mehr mit gechunkten, kodierten Snippets. Vielleicht sollten Sie es einmal ausprobieren und "Accept-Encoding: gzip,deflate" weglassen. Die Idee kam mir beim Lesen des oben erwähnten Wikis zum Thema Komprimierung.

-1voto

Liam Corner Punkte 337

Ich hatte das gleiche Problem (und so bin ich hier gelandet :-). Schließlich fand ich heraus, dass der Chunked Stream ungültig war - der letzte Chunk mit der Länge Null fehlte. Ich kam mit dem folgenden Code, der sowohl gültige als auch ungültige Chunked Streams behandelt.

using (StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
    StringBuilder sb = new StringBuilder();

    try
    {
        while (!sr.EndOfStream)
        {
            sb.Append((char)sr.Read());
        }
    }
    catch (System.IO.IOException)
    { }

    string content = sb.ToString();
}

4 Stimmen

Casting Bytes zu char ist gefährlich, weil es völlig ignoriert Multibyte-Kodierungen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X