Quantcast
Channel: Active questions tagged https - Stack Overflow
Viewing all articles
Browse latest Browse all 1535

Checksum does not match on client side when downloading large files over HTTPS

$
0
0

I already have a Server/Client application, clients connect to the server via a token, server verifies the token and only then server proceeds to send files to the client(s).

However, when I try to send and receive files, the checksum is different on client's end, BUT only for larger files. For smaller 39 byte text files is the checksum exactly the same, but as soon as I try to transfer something larger, the checksum differs.

I really need to use HTTP protocol & OpenSSL.Here are my functions:

std::size_t SendFiles(std::shared_ptr<Connection> connection,                      const std::vector<std::filesystem::path>& files){    // Basic validation    if (!connection || connection->GetSocket() < 0)        throw std::runtime_error("Invalid connection.");    // Prepare boundary and initial response    const std::string boundary = "----FILE_BOUNDARY";    std::string header ="HTTP/1.1 200 OK\r\n""Content-Type: multipart/mixed; boundary=" + boundary +"\r\n" +"expected_files=" + std::to_string(files.size()) +"\r\n""Connection: close\r\n\r\n";    if (SSL_write(connection->GetSSL(), header.data(), static_cast<int>(header.size())) <= 0)        throw std::runtime_error("Failed to send initial response headers.");    std::size_t sent_files = 0;    for (auto &fp : files)    {        // Retrieve content from preloaded storage        auto it = preloadedFiles.find(fp);        if (it == preloadedFiles.end())            continue; // Skip if missing        const std::string_view content = it->second;        const std::string fname = fp.filename().string();        // Send MIME part headers        std::string part ="--" + boundary +"\r\n""Content-Type: application/octet-stream\r\n""Content-Length: " + std::to_string(content.size()) +"\r\n""Content-Disposition: attachment; filename=\"" + fname +"\"\r\n""Content-Transfer-Encoding: binary\r\n\r\n";        if (SSL_write(connection->GetSSL(), part.data(), static_cast<int>(part.size())) <= 0)            break;        // Send file in chunks        std::string_view chunk = content;        while (!chunk.empty())        {            const int to_send = std::min<int>(static_cast<int>(chunk.size()), 16384);            const int written = SSL_write(connection->GetSSL(), chunk.data(), to_send);            if (written <= 0)            {                const int err = SSL_get_error(connection->GetSSL(), written);                if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)                    continue;                return sent_files;            }            chunk.remove_prefix(written);        }        // End of this file part        static const std::string crlf = "\r\n";        if (SSL_write(connection->GetSSL(), crlf.data(), static_cast<int>(crlf.size())) <= 0)            break;        sent_files++;    }    // Final boundary    const std::string closing = "--" + boundary +"--\r\n";    SSL_write(connection->GetSSL(), closing.data(), static_cast<int>(closing.size()));    return sent_files;}std::size_t ReceiveFiles(std::shared_ptr<Connection> connection,                         const std::filesystem::path &destination_dir){    if (!connection || connection->GetSocket() < 0)        throw std::runtime_error("Invalid connection.");    std::string buffer;    buffer.reserve(65536);    const std::size_t chunkSize = 16384;    char temp[chunkSize];    bool headersParsed = false;    std::string boundary;    int expectedFiles = 0, filesReceived = 0;    bool readingFile = false;    std::ofstream outFile;    uint64_t contentLeft = 0;    // Continuously read from SSL    while (true)    {        // If we've met the file count, stop        if (filesReceived == expectedFiles && expectedFiles > 0)            break;        const int r = SSL_read(connection->GetSSL(), temp, chunkSize);        if (r <= 0)        {            const int err = SSL_get_error(connection->GetSSL(), r);            if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)                continue;            if (err == SSL_ERROR_ZERO_RETURN)                break;            return filesReceived;        }        buffer.append(temp, r);        // Parse headers first        if (!headersParsed)        {            auto pos = buffer.find("\r\n\r\n");            if (pos == std::string::npos)                continue;            const std::string head = buffer.substr(0, pos);            buffer.erase(0, pos + 4);            headersParsed = true;            // Extract boundary            const std::string btag = "boundary=";            if (auto bpos = head.find(btag); bpos != std::string::npos)            {                auto start = bpos + btag.size();                auto end = head.find("\r\n", start);                boundary = "--" + head.substr(start, end - start);            }            // Extract expected files            const std::string etag = "expected_files=";            if (auto epos = head.find(etag); epos != std::string::npos)            {                auto start = epos + etag.size();                auto end = head.find("\r\n", start);                expectedFiles = std::stoi(head.substr(start, end - start));            }        }        // Process each file part        while (true)        {            // If not currently reading a file, find the next boundary            if (!readingFile)            {                auto bpos = buffer.find(boundary);                if (bpos == std::string::npos)                    break;                buffer.erase(0, bpos + boundary.size());                // After boundary, we expect headers                auto headerEnd = buffer.find("\r\n\r\n");                if (headerEnd == std::string::npos)                    break;                // Extract file headers                std::string fileHead = buffer.substr(0, headerEnd);                buffer.erase(0, headerEnd + 4);                readingFile = true;                // Filename                std::string fname;                if (auto fpos = fileHead.find("filename=\""); fpos != std::string::npos)                {                    auto start = fpos + 10;                    auto end = fileHead.find("\"", start);                    fname = fileHead.substr(start, end - start);                }                outFile.open(destination_dir / fname, std::ios::binary);                if (!outFile.is_open())                    return filesReceived;                // Content length                contentLeft = 0;                if (auto cpos = fileHead.find("Content-Length:"); cpos != std::string::npos)                {                    auto start = fileHead.find_first_not_of(" \t", cpos + 15);                    auto end = fileHead.find("\r\n", start);                    contentLeft = std::stoull(fileHead.substr(start, end - start));                }            }            // Write file content            if (readingFile)            {                const size_t canWrite = std::min<uint64_t>(buffer.size(), contentLeft);                if (canWrite == 0)                    break;                outFile.write(buffer.data(), canWrite);                buffer.erase(0, canWrite);                contentLeft -= canWrite;                // If done, close file                if (contentLeft == 0)                {                    outFile.close();                    filesReceived++;                    readingFile = false;                }            }            else            {                break;            }        }    }    return filesReceived;}

As I've already mentioned, it works with small files (the checksum matches). It's really strange to me. Anyone has any idea? What am I doing wrong?


Viewing all articles
Browse latest Browse all 1535

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>