📜  无法识别的内容编码类型. libcurl 理解 deflate、gzip 内容编码. (1)

📅  最后修改于: 2023-12-03 14:55:07.881000             🧑  作者: Mango

无法识别的内容编码类型

当使用 libcurl 发送 HTTP 请求时,如果请求的响应体是经过压缩编码的,例如使用 deflate 或 gzip 编码,而 libcurl 没有按照相应的编码方式解码该响应体,则会出现“无法识别的内容编码类型”错误。这是因为 libcurl 默认只支持这两种编码方式,而其他编码方式则需要程序员进行自己编码解码。

如何解决这个问题

要解决这个问题,有以下两种方法:

1.手动解码

可以通过手动解码操作解决上述错误。这可以使用 libcurl 的回调函数来实现。回调函数将在每次收到新数据时调用,因此需要在其中进行解码操作。以下是一个示例代码段,演示如何使用 inflate 函数解压缩内存中的数据:

#include <zlib.h>

size_t writer_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
    z_stream *zstream = (z_stream*) stream;
    zstream->next_in = ptr;
    zstream->avail_in = size * nmemb;

    do {
        char outbuf[32768];
        zstream->next_out = (Bytef *)outbuf;
        zstream->avail_out = sizeof(outbuf);

        int ret = inflate(zstream, Z_SYNC_FLUSH);
        if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
            return 0;
        }

        size_t write_size = sizeof(outbuf) - zstream->avail_out;
        fwrite(outbuf, 1, write_size, stdout);
    } while (zstream->avail_out == 0);

    return size * nmemb;
}

int main() {
    CURL *curl = curl_easy_init();
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/test.gz");
        curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer_callback);

        z_stream zstream;
        zstream.zalloc = Z_NULL;
        zstream.zfree = Z_NULL;
        zstream.opaque = Z_NULL;
        inflateInit2(&zstream, MAX_WBITS + 32);

        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &zstream);
        curl_easy_perform(curl);

        inflateEnd(&zstream);
        curl_easy_cleanup(curl);
    }
}

在上述代码中,我们首先初始化了 zlib 库的 z_stream 结构体,然后将其作为 CURLOPT_WRITEDATA 选项的参数来传递。回调函数 writer_callback 将在每次回调时传递该结构体,以便我们可以在其中进行解码操作。

2.使用支持编码方式的库

另一个解决此问题的方法是使用支持更多编码方式的库。例如,如果希望支持所有常见压缩编码,可以使用 cURLpp 库:

#include <iostream>
#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>

int main(int argc, char *argv[]) {
    std::ostringstream responseStream;
    try {
        curlpp::Cleanup myCleanup;
        curlpp::Easy myRequest;

        myRequest.setOpt<curlpp::options::Url>(argv[1]);
        myRequest.setOpt<curlpp::options::WriteStream>(&responseStream);
        myRequest.setOpt<curlpp::options::AcceptEncoding>("");

        myRequest.perform();
    }
    catch (curlpp::RuntimeError &exc) {
        std::cout << exc.what() << std::endl;
    }
    catch (curlpp::LogicError &exc) {
        std::cout << exc.what() << std::endl;
    }

    std::string response = responseStream.str();
    std::cout << response << std::endl;

    return 0;
}

在上述代码中,我们使用了 curlpp 库中的 setOpt 函数,来设置我们需要使用的选项。通过设置 AcceptEncoding 选项为空字符串,我们告诉 libcurl 忽略响应中 Content-Encoding 头信息,并尝试解压缩内容。

结论

当发送 HTTP 请求时,如果收到的响应体是经过压缩编码的,而 libcurl 没有按照相应的编码方式解码该响应体,则会出现“无法识别的内容编码类型”错误。为了解决此问题,我们可以手动解码或使用支持更多编码方式的库。