Lendo a resposta “fragmentada” com HttpWebResponse
Pergunta
Estou tendo problemas para ler uma resposta "fragmentada" ao usar um StreamReader para ler o fluxo retornado por GetResponseStream() de um HttpWebResponse:
// response is an HttpWebResponse
StreamReader reader = new StreamReader(response.GetResponseStream());
string output = reader.ReadToEnd(); // throws exception...
Quando o reader.ReadToEnd()
método é chamado, estou recebendo o seguinte System.IO.IOException: Não é possível ler dados da conexão de transporte:A conexão foi fechada.
O código acima funciona bem quando o servidor retorna uma resposta "não fragmentada".
A única maneira de fazê-lo funcionar é usar HTTP/1.0 para a solicitação inicial (em vez de HTTP/1.1, o padrão), mas isso parece uma solução alternativa esfarrapada.
Alguma ideia?
@Mandril
Sua solução funciona muito bem.Ele ainda lança o mesmo IOExecception no último Read().Mas depois de inspecionar o conteúdo do StringBuilder parece que todos os dados foram recebidos.Então, talvez eu só precise agrupar Read() em um try-catch e engolir o "erro".
Solução
Ainda não tentei fazer isso com uma resposta "fragmentada", mas algo assim funcionaria?
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);
Outras dicas
Estou trabalhando em um problema semelhante.O .net HttpWebRequest e o HttpWebRequest lidam com cookies e redirecionamentos automaticamente, mas não lidam automaticamente com conteúdo fragmentado no corpo da resposta.
Talvez isso ocorra porque o conteúdo fragmentado pode conter mais do que dados simples (ou seja:nomes de blocos, cabeçalhos finais).
Simplesmente ler o stream e ignorar a exceção EOF não funcionará, pois o stream contém mais do que o conteúdo desejado.O fluxo conterá pedaços e cada pedaço começa declarando seu tamanho.Se o fluxo for simplesmente lido do início ao fim, os dados finais conterão os metadados do bloco (e no caso de conteúdo compactado em gzip, a verificação CRC falhará ao descompactar).
Para resolver o problema é necessário analisar manualmente o fluxo, removendo o tamanho do pedaço de cada pedaço (bem como os delimitadores CR LF), detectando o pedaço final e mantendo apenas os dados do pedaço.Provavelmente existe uma biblioteca em algum lugar que faz isso, ainda não a encontrei.
Recursos úteis:
http://en.wikipedia.org/wiki/Chunked_transfer_encoding http://tools.ietf.org/html/rfc2616#section-3.6.1
Craig, sem ver o fluxo que você está lendo é um pouco difícil de depurar, mas TALVEZ você possa alterar a configuração da variável de contagem para isto:
count = resStream.Read(buf, 0, buf.Length-1);
É um pouco complicado, mas se a última leitura estiver matando você e não estiver retornando nenhum dado, teoricamente isso evitará o problema.Ainda me pergunto por que o stream está fazendo isso.
Eu tive o mesmo problema (e foi assim que acabei aqui :-).Eventualmente, descobri que o fluxo em pedaços não era válido - o pedaço final de comprimento zero estava faltando.Eu criei o código a seguir, que lida com fluxos em partes válidos e inválidos.
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();
}