关键看响应头是否有Transfer-Encoding: chunked字段,大小写不敏感但需完整匹配;若同时存在Content-Length则必须忽略后者;cpp-httplib中res->body存的是原始chunked数据,需手动解析,状态机应含READING_SIZE、READING_DATA、READING_CRLF三态。

怎么判断响应用了Chunked编码
关键看响应头里有没有 Transfer-Encoding: chunked。注意大小写不敏感,但字段名必须完整匹配,不能只搜 chunked 字符串——因为有些服务会在 Content-Type 或自定义头里误带这个词。
如果同时存在 Content-Length 和 Transfer-Encoding: chunked,按 RFC 7230 规定,Content-Length 必须被忽略。实际解析时,只要检测到后者,就直接放弃用长度预分配缓冲区的思路。
- 用
res->has_header("Transfer-Encoding")先确认字段存在(cpp-httplib) - 再用
res->get_header_value("Transfer-Encoding")取值,检查是否包含"chunked"(注意可能为"chunked, gzip") - 别信
res->body—— cpp-httplib 默认不会自动解 chunk,它把原始 chunked 数据原样塞进body字段,直接读会看到十六进制长度+\r\n混在数据里
手动解析Chunked数据的状态机怎么写
不能靠 std::string::find("\r\n") 简单切分——网络数据是流式的,一次 recv 可能只收到半个块头,或跨块的碎片。必须维护状态和偏移量。
核心状态就三个:READING_SIZE、READING_DATA、READING_CRLF。不需要复杂 FSM 库,用一个 enum class + switch 就够。
立即学习“C++免费学习笔记(深入)”;
-
READING_SIZE:逐字节收集十六进制数字,遇到\r\n停下,调用std::stoul(size_str, nullptr, 16)转整数;若转出 0,进入结束流程 -
READING_DATA:按上一步得到的长度,从缓冲区拷贝对应字节数;若缓冲区不够,先等下次数据到来 -
READING_CRLF:跳过紧随数据后的\r\n,然后回到READING_SIZE - 每次处理完一块,把数据追加到目标缓冲区(如
std::vector<uint8_t></uint8_t>),别用std::string存二进制内容——中间可能有\0
cpp-httplib里怎么拿到原始chunked数据
默认情况下,httplib::Client::Get() 返回的 Response 对象,其 body 是未经解码的 chunked 原始字节流。你得自己解,库不代劳。
如果想绕过手动解析,可以换底层:用 httplib::Client::send 发送原始请求,传入自定义 ResponseHandler 回调,在回调里逐段接收数据(类似 libcurl 的 WRITEFUNCTION),自己拼接并实时解 chunk。
- 别依赖
res->body.size()判断数据完整性——它返回的是带 chunk 头的总长度,不是真实 payload 长度 - 如果响应头还有
Content-Encoding: gzip,解 chunk 后还得再解压缩,顺序不能反 - 测试时用
curl -H "Accept-Encoding: identity" http://example.com强制关闭压缩,排除干扰
容易被忽略的边界情况
协议允许块大小后跟扩展参数,比如 5;ext=abc\r\nhello\r\n。标准解析器必须跳过分号及之后内容,只取前面的十六进制部分。
最后一块是 0\r\n,后面可能紧跟 trailers(额外头字段),格式为 Header: value\r\n,以单独 \r\n 结束。大多数客户端可忽略 trailers,但如果你要校验 Content-MD5 这类字段,就得继续解析。
- 十六进制字符串为空(只有
\r\n)是非法的,应报错退出,不能当 0 处理 - 块大小超过
size_t范围(如ffffffffffffffff)需截断或拒绝,防止malloc溢出 - 数据未收全就遇到 socket 关闭,状态机必须能识别
FIN并报incomplete chunk错误,而不是静默返回部分数据


















