Как правильно отправлять двоичные данные через HTTPS POST?

Я отправляю двоичные данные с клиента (Debian 6.0.3 ) на сервер (Windows Server 2003). Чтобы обойти большинство брандмауэров, я использую HTTPS POST . Клиент и сервер реализованы с использованием Boost.Asio и OpenSSL . Сначала я реализовал простейшую возможную версию, и она работала нормально.

Заголовок HTTP:

POST / HTTP/1.1
User-Agent: my custom client v.1

[binary data]

( [двоичные данные] не закодированы в base64, если это имеет значение)

Затем на другом клиентском компьютере это не удалось (подключено к тому же серверу). Поведение нестабильно. Соединение всегда устанавливается нормально (порт 443). В большинстве случаев я успешно передаю подтверждение SSL, но сервер не получает данных (почти нет данных, иногда фактически принимаются пакет или два). Иногда я получаю сообщение об ошибке подтверждения SSL «короткое чтение». Иногда я получаю неверные данные.

Клиент подключается к серверу, устанавливает связь, отправляет заголовок HTTP POST, а затем бесконечно отправляет двоичные данные, пока что-то не произойдет. Для теста я использую специально созданный сертификат SSL.

Код сервера:

namespace ssl = boost::asio::ssl;
ssl::context context(io_service, ssl::context::sslv23);
context.set_options(ssl::context::default_workarounds | ssl::context::no_sslv2);
context.use_certificate_chain_file("server.pem");
context.use_private_key_file("server.pem", boost::asio::ssl::context::pem);

ssl::stream<tcp::socket> socket(io_service, context);

// standard connection accepting

socket.async_handshake(ssl::stream_base::server, ...);
...
boost::asio::async_read_until(socket, POST_header, "\r\n\r\n", ...);
...

Код клиента:

ssl::context context(io_service, ssl::context::sslv23);
context.load_verify_file("server.crt");
socket.reset(new ssl::stream<tcp::socket>(io_service, context));
socket->set_verify_mode(ssl::verify_none);

// standard connection

socket.async_handshake(ssl::stream_base::client, ...);
...

(обработка ошибок опущена вместе с нерелевантным кодом)

Как видите, это простейшее возможное SSL-соединение. Что не так? Может быть причина в брандмауэре?

Я пробовал простой TCP без SSL через тот же порт 443, он отлично работает.

РЕДАКТИРОВАТЬ:

Пытался добавить «Content-Type: application / octet-stream», не помогло.

РЕДАКТИРОВАТЬ 2:

Обычно я получаю заголовок HTTP POST нормально. Затем я отправляю фрагменты данных как кусок размером (4 байта) (размер фрагмента в байтах) ... . Сервер получает размер блока нормально, но ничего. Клиент не уведомляет о проблемах сервера (нет ошибок) и продолжает отправлять данные. Иногда сервер может получить кусок или два, иногда он получает неверный размер фрагмента , но в большинстве случаев просто ничего.

РЕДАКТИРОВАТЬ 3:

Сравнил перехваченный трафик на клиенте и сервере, не обнаружил никаких различий.

Решение

Эта проблема меня ввела в заблуждение с самого начала. Сузил его до удивительных подробностей:

Отправка через сокет SSL не выполняется, если я использую мультибуферы Boost.Asio в Boost v.1.48 (самый последний на данный момент). Пример:

// data to send, protocol is [packet size: 4 bytes][packet: packet_size bytes]
std::vector<char> packet = ...;
uint32_t packet_size = packet.size();
// prepare buffers
boost::array<boost::asio::const_buffer, 2> bufs = {{boost::asio::buffer(&packet_size, sizeof(packet_size)), boost::asio::buffer(packet)}};
// send multi buffers by single call
boost::asio::async_write(socket, bufs, ...);

Отдельная отправка размера_пакета и пакета в этом примере решает проблему. Я далек от того, чтобы называть какое-либо подозрительное поведение ошибкой, особенно если оно связано с библиотеками Boost. Но это действительно похоже на ошибку. Пробовал на Boost v.1.47 - работает нормально. Пробовал с обычным сокетом TCP (не SSL) - работает нормально. То же самое и в Linux, и в Windows.

Я найду сообщения об этой проблеме в списке рассылки Asio и сообщу, если ничего не будет найдено.

13
задан Andriy Tylychko 15 February 2012 в 18:16
поделиться