Как исправить ошибку "Specified value has invalid Control characters" при скачивании файла через HttpClient?

Есть следующий код:

                static readonly HttpClient client = new HttpClient(handler);
        private async Task RequestAsync(Guid ActId, string FileId, string FileName, string FileFolderName, string Description, string UploaderName, string UploadDate)
        {
            client.DefaultRequestHeaders.Add("Accept-Language", "en-US,en;q=0.5");
            client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
            client.DefaultRequestHeaders.Add("Accept", "text/html, application/xhtml+xml, image/jxr, */*");
            try
            {
                var url = FileLinkIBMFileNet.Replace("{file_id}", FileId).Replace("{file_name}", FileName);
                var authToken = Encoding.ASCII.GetBytes($"{LoginIBMFilenet}:{PasswordIBMFileNet}");
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
                Convert.ToBase64String(authToken));
                byte[] tmpFileByte = await client.GetByteArrayAsync(url);
                InsertFile(ActId, FileName, FileFolderName, tmpFileByte, Description, UploaderName, UploadDate, FileId);

            }
            catch (Exception ex) {
                string message = string.Format("Error in RequestAsync\nMessage ---\n{0} \nSource ---\n{1} \nStackTrace ---\n{2} \nActId ---\n{3} \nFileId ---\n{4} \nFilename ---\n{5}", ex.Message, ex.Source, ex.StackTrace, ActId, FileId, FileName);
                _logger.Error(message, ex);
                throw ex;
            }
            }

Который через Fiddler при запросах всегда получает 200 ОК. введите сюда описание изображения Но в коде у большинство файлов при получении ответа происходит следующая ошибка в методе

await client.GetByteArrayAsync(url);

Ошибка:

Сервер нарушил протокол. Section=ResponseHeader Detail=За возвратом каретки должен следовать перевод строки
Specified value has invalid Control characters. (Parameter 'value') 
Source ---
System.Net.WebHeaderCollection 
StackTrace ---
   at System.Net.HttpValidationHelpers.CheckBadHeaderValueChars(String value)
   at System.Net.WebHeaderCollection.Set(String name, String value)
   at System.Net.HttpWebResponse.get_Headers()
   at ITRusPostISO.ITMetasonicMigrationFile.ITMetasonicMigrationFileService.RequestAsync(Guid ActId, String FileId, String FileName, String FileFolderName, String Description, String UploaderName, String UploadDate) 

Также прикрепляю скриншот Request/Response из Fiddler файла который нормально скачивается и которые падает в ошибку. введите сюда описание изображения

Response InternetExplorer при переходе по ссылке и скачивании файлов. введите сюда описание изображения

` Вот старый код на HttpWebRequest

private async Task RequestAsync(Guid ActId, string FileId, string FileName, string FileFolderName, string Description, string UploaderName, string UploadDate)
        {
            try{
                string downloadLink = FileLinkIBMFileNet.Replace("{file_id}", FileId).Replace("{file_name}", FileName);
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(downloadLink);
                request.Credentials = new NetworkCredential(LoginIBMFilenet, PasswordIBMFileNet);
                request.Timeout = 180000;
                request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
                HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();

                WebHeaderCollection headers = response.Headers;
                int streamSize = int.Parse(response.Headers["Content-Disposition"].Split(';')[2].Replace(" size=", ""));
                byte[] tmpFileByte = new byte[streamSize];
                using (Stream stream = response.GetResponseStream())
                {
                    using (BinaryReader reader = new BinaryReader(stream))
                    {
                        reader.Read(tmpFileByte, 0, streamSize);
                    }
                }
                InsertFile(ActId, FileName, FileFolderName, tmpFileByte, Description, UploaderName, UploadDate, FileId);
                response.Close();
            }
            catch(Exception ex) {
                string message = string.Format("Error in RequestAsync\nMessage ---\n{0} \nSource ---\n{1} \nStackTrace ---\n{2} \nActId ---\n{3} \nFileId ---\n{4} \nFilename ---\n{5}", ex.Message, ex.Source, ex.StackTrace, ActId, FileId, FileName);
                _logger.Error(message, ex);
                throw ex;
            }
        }

Решение использовать tcpClient так как сервер шлет всегда битый response


Ответы (1 шт):

Автор решения: aepot

Я читал бы ответ от сервера примерно вот так:

private readonly char[] _colon = new[] { ':' };

public async Task RequestAsync(Guid ActId, string FileId, string FileName, string FileFolderName, string Description, string UploaderName, string UploadDate)
{
    try
    {
        byte[] body;
        Uri url = new Uri(FileLinkIBMFileNet.Replace("{file_id}", FileId).Replace("{file_name}", FileName));
        using (var tcp = new TcpClient(url.Host, 9080))
        using (var tcpStream = tcp.GetStream())
        {
            tcp.SendTimeout = 1000;
            tcp.ReceiveTimeout = 1800;
            StringBuilder builder = new StringBuilder();
            builder.Append("GET ").Append(url.AbsoluteUri).AppendLine(" HTTP/1.1");
            builder.Append("Host: ").AppendLine(url.Authority);
            builder.AppendLine("Connection: Keep-Alive");
            builder.AppendLine("Accept-Encoding: gzip");
            builder.Append("Authorization: Basic ").AppendLine(Convert.ToBase64String(Encoding.ASCII.GetBytes($"{LoginIBMFilenet}:{PasswordIBMFileNet}")));
            builder.AppendLine();
            byte[] header = Encoding.ASCII.GetBytes(builder.ToString());
            await tcpStream.WriteAsync(header, 0, header.Length).ConfigureAwait(false);

            Dictionary<string, string> headers = new Dictionary<string, string>();
            string line;
            while ((line = tcpStream.ReadLine())?.Length > 0)
            {
                string[] tokens = line.Split(_colon, 2);
                if (tokens.Length > 1)
                    headers.Add(tokens[0], tokens[1].Trim());
                else
                    headers.Add("Response", tokens[0]);
            }

            using (MemoryStream memory = new MemoryStream())
            {
                bool FirstChunk = true;
                if (headers.TryGetValue("Transfer-Encoding", out string chk) && chk.Contains("chunked"))
                {
                    int chunkSize = 0;
                    while (true)
                    {
                        if (!FirstChunk)
                            tcpStream.ReadLine();
                        else
                            FirstChunk = false;
                        chunkSize = int.Parse(tcpStream.ReadLine(), NumberStyles.HexNumber);
                        if (chunkSize == 0) break;
                        await tcpStream.CopyBytesAsync(memory, chunkSize).ConfigureAwait(false);
                    }
                }
                else
                    await tcpStream.CopyToAsync(memory).ConfigureAwait(false);

                memory.Position = 0;

                if (headers.TryGetValue("Content-Encoding", out string enc) && enc.Contains("gzip"))
                    using (Stream gzipStream = new GZipStream(memory, CompressionMode.Decompress, true))
                    using (MemoryStream ms = new MemoryStream())
                    {
                        await gzipStream.CopyToAsync(ms).ConfigureAwait(false);
                        body = ms.ToArray();
                    }
                else
                    body = memory.ToArray();
            }
        }
        InsertFile(ActId, FileName, FileFolderName, body, Description, UploaderName, UploadDate, FileId);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

И вот такие экстеншн методы для NetworkStream понадобятся.

public static class NetworkStreamExtensions
{
    public static string ReadLine(this NetworkStream stream)
    {
        List<byte> bytes = new List<byte>();
        int b;
        while ((b = stream.ReadByte()) != -1 && b != '\n')
            bytes.Add((byte)b);
        if (bytes.Count > 0 && bytes.Last() == '\r')
            bytes.RemoveAt(bytes.Count - 1);
        return b == -1 && bytes.Count == 0 ? null : Encoding.ASCII.GetString(bytes.ToArray());
    }

    public static async Task CopyBytesAsync(this NetworkStream stream, Stream outputStream, int bytesToCopy)
    {
        int toCopy = bytesToCopy;
        const int bufferSize = 32768;
        byte[] buffer = new byte[bufferSize];
        while (true)
        {
            int toRead = toCopy > bufferSize ? bufferSize : toCopy;
            if (toRead == 0) break;
            int bytesRead = await stream.ReadAsync(buffer, 0, toRead).ConfigureAwait(false);
            if (bytesRead == 0) break;
            await outputStream.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false);
            toCopy -= bytesRead;
        }
    }
}
→ Ссылка