Как эмулировать кодировку URL Internet Explorer в консольном приложении - PullRequest
0 голосов
/ 08 мая 2018

Цель:

Создайте консольное приложение, которое отправит простой запрос get - абсолютно правильным образом.

Проблема:

У меня есть такой URL: http://myhost/somepage.do?Search01=コード番号=123456&Search02=改訂番号=2

Когда я копирую и вставляю это в IE 11 или ниже, страница, которую я хочу, возвращается правильно. Когда я копирую и вставляю это в IE Edge, chrome, firefox, он возвращает ошибку о том, что не распознает параметры должным образом.

Я исследовал это с помощью Wireshark и совершенно ясно вижу, что старый IE отправляет URL с какой-то другой кодировкой: enter image description here

Принимая во внимание, что Chrome делает более ожидаемую кодировку: enter image description here

Я не до конца понимаю, что здесь происходит, но кажется, что этот сервер, на который я отправляю сообщение, в некотором роде склонен к неправильному способу кодирования URL в IE - потому что он отвечает только на испорченный запросы.

Я проверил другие вещи, такие как useragent и т. Д. - это не имеет значения. На этом сервере запущена служба, которая очень старая (возможно, с использованием ASP).

Итак, моя цель - эмулировать эту испорченную кодировку в консольном приложении. Как мне это сделать?

1 Ответ

0 голосов
/ 09 мая 2018

Итак, с некоторой помощью из понимания того, что может происходить через: Этот вопрос о переполнении стека

Я понял, как кодируется мой URL.

Мой компьютер - японский, поэтому кодовая страница по умолчанию - 932. После долгого возни с образцом консольного приложения и просмотра пакетов в Wireshark я понял, что независимо от того, что я сделал, значения по умолчанию HttpClient и WebClient всегда будут корректно UrlEncode моего URL независимо от того, что кодировку я использовал. IE не так кодирует свои URL.

Я покопался глубже и обнаружил, что в источнике для HttpClientWebClient) он использует класс Uri, в котором есть конструктор с параметром: DontEscape который я подумал "Эврика!" но оказывается, что этот конструктор устарел, и нет способа не сделать так, чтобы URL автоматически экранировали себя при использовании HttpClient или WebClient.

Поэтому мне пришлось использовать TcpClient и вместо этого сделать свой собственный запрос. Который я украл отсюда:

    /// <summary>
    /// The initial request to search only works if the url is encoded using Shift-JIS, which means we cannot use any client library and must use a custom TCP message.
    /// </summary>
    /// <param name="serveripaddress"></param>
    /// <param name="restoftheurl"></param>
    /// <returns></returns>
    private async Task<string> HttpRequestAsync(string serveripaddress, string restoftheurl)
    {
        Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
        string result = string.Empty;

        using (var tcp = new TcpClient(serveripaddress, 80))
        using (var stream = tcp.GetStream())
        {
            tcp.SendTimeout = 500;
            tcp.ReceiveTimeout = 1000;
            Console.WriteLine("URL rest:" + restoftheurl);
            // Send request headers
            var builder = new StringBuilder();
            builder.AppendLine("GET " + restoftheurl + " HTTP/1.1");
            builder.AppendLine("Host: " + serveripaddress);
            //builder.AppendLine("Content-Length: " + data.Length);   // only for POST request
            builder.AppendLine("Accept: text/html, application/xhtml+xml, */*");
            builder.AppendLine("Accept-Language: ja-JP");
            builder.AppendLine("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko");
            builder.AppendLine("Accept-Encoding: gzip, deflate");
            builder.AppendLine("Connection: Close");
            builder.AppendLine();
            Console.WriteLine("Sending message:" + builder.ToString());
            var header = Encoding.GetEncoding(932).GetBytes(builder.ToString());
            await stream.WriteAsync(header, 0, header.Length);

            // Send payload data if you are POST request
            //await stream.WriteAsync(data, 0, data.Length);

            // receive data
            using (var memory = new MemoryStream())
            {
                await stream.CopyToAsync(memory);
                memory.Position = 0;
                var data = memory.ToArray();

                var index = BinaryMatch(data, Encoding.ASCII.GetBytes("\r\n\r\n")) + 4;
                var headers = Encoding.ASCII.GetString(data, 0, index);
                memory.Position = index;

                if (headers.IndexOf("Content-Encoding: gzip") > 0)
                {
                    using (GZipStream decompressionStream = new GZipStream(memory, CompressionMode.Decompress))
                    using (var decompressedMemory = new MemoryStream())
                    {
                        decompressionStream.CopyTo(decompressedMemory);
                        decompressedMemory.Position = 0;
                        result = Encoding.UTF8.GetString(decompressedMemory.ToArray());
                    }
                }
                else
                {
                    result = Encoding.UTF8.GetString(data, index, data.Length - index);
                    //result = Encoding.GetEncoding("gbk").GetString(data, index, data.Length - index);
                }
            }

            //Debug.WriteLine(result);
            return result;
        }
    }

    private int BinaryMatch(byte[] input, byte[] pattern)
    {
        int sLen = input.Length - pattern.Length + 1;
        for (int i = 0; i < sLen; ++i)
        {
            bool match = true;
            for (int j = 0; j < pattern.Length; ++j)
            {
                if (input[i + j] != pattern[j])
                {
                    match = false;
                    break;
                }
            }
            if (match)
            {
                return i;
            }
        }
        return -1;
    }

}

Ключевая часть этого кода:

var header = Encoding.GetEncoding(932).GetBytes(builder.ToString());

Это заставляет строку кодироваться в моей кодовой странице, что требовало регистрации поставщика кодовых страниц, поэтому вверху: Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

Использование простое:

await HttpRequestAsync("123.456.789.123", "/somepage.do?Search01=コード番号=123456&Search02=改訂番号=2");

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...