LINUX.ORG.RU

как корректно прочитать boost::asio::streambuf?

 , , streambuf


0

2

Приветствую.

Нужно скачать с http сервера файл и посчитать его md5, взял из документации пример, все в нем работает пока не вставляю в него свой кривой код вместо cout - в зависимости от размера файла получаю то корректные данные, то нет

#include <openssl/md5.h>
#include <fstream>
#include <iomanip>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

bool HTTPDownload(const std::string & host, const std::string & port, const std::string & file_src, const char * file_dst, const std::string & md5)
{
    std::ofstream outfile(file_dst, std::ios::binary);
    if (!outfile.is_open())
    {
        fprintf(stderr, "ERROR %s\n", file_dst);
        return false;
    }

    MD5_CTX md5Context;
    MD5_Init(&md5Context);
    try
    {
        boost::asio::io_service io_service;

        tcp::resolver resolver(io_service);
        tcp::resolver::query query(host, port);
        tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

        tcp::socket socket(io_service);
        boost::asio::connect(socket, endpoint_iterator);

        {
            boost::asio::streambuf request;
            std::ostream request_stream(&request);
            request_stream  << "GET " << file_src << " HTTP/1.0\r\n"
                            << "Host: " << host << "\r\n"
                            << "Accept: */*\r\n"
                            << "Connection: close\r\n\r\n";

            boost::asio::write(socket, request);
        }

        boost::asio::streambuf response;
        boost::asio::read_until(socket, response, "\r\n");

        std::istream response_stream(&response);
        std::string http_version;
        response_stream >> http_version;
        unsigned int status_code;
        response_stream >> status_code;
        std::string status_message;
        std::getline(response_stream, status_message);
        if (!response_stream || http_version.substr(0, 5) != "HTTP/")
        {
            fputs("ERROR HTTP response\n", stderr);
            return false;
        }
        if (status_code != 200)
        {
            fprintf(stderr, "ERROR %u\n", status_code);
            return false;
        }

        boost::asio::read_until(socket, response, "\r\n\r\n");

        std::string header;
        while (std::getline(response_stream, header) && header != "\r") fprintf(stdout, "%s\n", header.c_str());
        fputc('\n', stdout);

        boost::system::error_code error;
        while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error));;

        if (error != boost::asio::error::eof) return false;

        if (response.size() > 0)
        {
            auto response_data = boost::asio::buffer_cast<const char*>(response.data());
            MD5_Update(&md5Context, response_data, response.size());
            outfile << response_data;
        }
        else return false;
    }
    catch (std::exception& e)
    {
        fprintf(stderr, "ERROR [%s]: %s\n", file_dst, e.what());
        return false;
    }

    unsigned char md5hash[MD5_DIGEST_LENGTH];
    MD5_Final(md5hash, &md5Context);

    std::stringstream md5ss;
    md5ss << std::hex << std::setfill('0');
    for (const auto & byte: md5hash) md5ss << std::setw(2) << (int)byte;

    fprintf(stdout, "\n%s\n", md5ss.str().c_str());

    return true;
}

★★★

Последнее исправление: maxcom (всего исправлений: 3)
Ответ на: комментарий от cobold

может, но в заголовке все нормально, а файлы сохраняются то кривые то нет

Server: nginx/1.14.2
Date: Mon, 09 Dec 2024 06:19:03 GMT
Content-Type: application/octet-stream
Content-Length: 10496
Last-Modified: Wed, 27 Mar 2024 09:54:31 GMT
Connection: close
ETag: "6603ecd7-2900"
Accept-Ranges: bytes
wolverin ★★★
() автор топика
while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error));;

тут мне кажется можно сделать оптимальнее. Если не применяется chunked encoding, то можно прочитать сразу столько, сколько указано в Content-Length. Либо сразу вызывать MD5_Update в этом цикле.

Кстати MD5_{Init,Update,Final} помечены как устаревшие функции в openssl. Лучше заменить их на EVP_Digest{Init,Update,Final}

По теме - ты смотрел, хэш чего считаешь? Там сразу все должно стать видно. Может там надо чистить буфер после получения хедеров функцией response.consume() и т.п.

если проблема с записью в файл в конце, то там, подозреваю, надо вручную очищать буфер.

Lrrr ★★★★★
()
Последнее исправление: Lrrr (всего исправлений: 1)
Ответ на: комментарий от wolverin

Ну это было почти пальцем в небо, я много раз обжигался на использовании char *, многие операторы и методы думают что это строка, а значит будут работать до первого \0, и даже конвертация в uint8_t * нифига не помогает, хотя местами углы сглаживает

sparks ★★★★
()

boost::asio::streambuf response;

Скорее всего, надо где-то помнить индекс того места на котором закончено чтение.

Короче надо как то управлять response для дальнейшего его использования.

alnkapa
()
Последнее исправление: alnkapa (всего исправлений: 1)