Boost Beast:当内容长度不容易获得时,如何使用自定义正文生成非分块响应?
Boost Beast: how to produce non-chunked response with a custom body when a content length is not readily available?
我正在尝试实现一个自定义主体类型,该主体类型将包含已解析的 JSON 树对象。这个任务看起来很自然,但我找不到使用 Beast 生成携带 JSON 的非分块 HTTP 消息的方法。我有一个通过包装 libmicrohttpd 和 libcurl 实现的 REST client/server,但我更愿意改用 Boost Beast。
据我了解,问题是正文类型的 size(value_type const&)
方法接收到对要序列化的正文值的引用(在我的例子中是 JSON 树对象),但是如果不实际将其字符串化,则无法确定字符串化 JSON 的确切长度。但是,如果我删除 size()
方法,Beast 认为我要求的是分块传输编码。当然,分块编码本身并没有什么问题,但对我来说,这可能意味着修复一些自动化和监控脚本,更不用说集成测试了。
我想做的是将一个JSON对象分配给我正在准备的消息,然后Beast去询问作者,而不是body::size()
用于有效负载大小。这对我来说似乎合乎逻辑,因为实际的 HTTP 消息正文(序列化 JSON)与内存中的活动 JSON 对象完全不同,它是 body::writer 谁产生用于传输的正文字节。我错了吗?
无论如何,你认为有好的方法来解决这个问题吗?
预先感谢您的时间和精力!
此致,
弗拉德
感谢您的美言,我很高兴看到 Body 自定义系统受到关注!你在问题中写的大部分内容都是正确的。如果您希望 Beast 在您调用 message::prepare_payload
时设置 Content-Length 字段,那么您必须在 Body::size
中提供正确的实现。 BodyWriter 直到序列化时才创建。这个问题最自然的解决方案是避免提供 Body::size
并让 Beast 在序列化您的 body 类型时使用分块的 Transfer-Encoding。我还应该注意,当您序列化 JSON 时,最好是在 writer 中一次做一点而不是将整个事物转换为字符串。这就是让编写器 object 保持中间状态并允许增量序列化的目的。否则,您必须为整个序列化表示分配内存,效率较低。
更新:
body::size()
和 message::prepare_payload
并非旨在按照您希望的方式使用。如果我明白你想做什么,那么这个函数应该处理它:
/** Prepare a message with a JSON payload.
This function accepts ownership of a message with a JSON body
and converts the JSON to string, returning a new message with
a string body. The Content-Length field is set on the new
message. All other fields are transferred over unmodified.
*/
template<
bool isRequest,
class Allocator>
message<
isRequest,
string_body,
basic_fields<Allocator>>
prepare(
message<
isRequest,
json_body,
basic_fields<Allocator>>&& m)
{
message<
isRequest,
string_body,
basic_fields<Allocator>> result{
std::move(m.base()),
json_to_string(m.body())};
result.prepare_payload();
return result;
}
我正在尝试实现一个自定义主体类型,该主体类型将包含已解析的 JSON 树对象。这个任务看起来很自然,但我找不到使用 Beast 生成携带 JSON 的非分块 HTTP 消息的方法。我有一个通过包装 libmicrohttpd 和 libcurl 实现的 REST client/server,但我更愿意改用 Boost Beast。
据我了解,问题是正文类型的 size(value_type const&)
方法接收到对要序列化的正文值的引用(在我的例子中是 JSON 树对象),但是如果不实际将其字符串化,则无法确定字符串化 JSON 的确切长度。但是,如果我删除 size()
方法,Beast 认为我要求的是分块传输编码。当然,分块编码本身并没有什么问题,但对我来说,这可能意味着修复一些自动化和监控脚本,更不用说集成测试了。
我想做的是将一个JSON对象分配给我正在准备的消息,然后Beast去询问作者,而不是body::size()
用于有效负载大小。这对我来说似乎合乎逻辑,因为实际的 HTTP 消息正文(序列化 JSON)与内存中的活动 JSON 对象完全不同,它是 body::writer 谁产生用于传输的正文字节。我错了吗?
无论如何,你认为有好的方法来解决这个问题吗?
预先感谢您的时间和精力!
此致, 弗拉德
感谢您的美言,我很高兴看到 Body 自定义系统受到关注!你在问题中写的大部分内容都是正确的。如果您希望 Beast 在您调用 message::prepare_payload
时设置 Content-Length 字段,那么您必须在 Body::size
中提供正确的实现。 BodyWriter 直到序列化时才创建。这个问题最自然的解决方案是避免提供 Body::size
并让 Beast 在序列化您的 body 类型时使用分块的 Transfer-Encoding。我还应该注意,当您序列化 JSON 时,最好是在 writer 中一次做一点而不是将整个事物转换为字符串。这就是让编写器 object 保持中间状态并允许增量序列化的目的。否则,您必须为整个序列化表示分配内存,效率较低。
更新:
body::size()
和 message::prepare_payload
并非旨在按照您希望的方式使用。如果我明白你想做什么,那么这个函数应该处理它:
/** Prepare a message with a JSON payload.
This function accepts ownership of a message with a JSON body
and converts the JSON to string, returning a new message with
a string body. The Content-Length field is set on the new
message. All other fields are transferred over unmodified.
*/
template<
bool isRequest,
class Allocator>
message<
isRequest,
string_body,
basic_fields<Allocator>>
prepare(
message<
isRequest,
json_body,
basic_fields<Allocator>>&& m)
{
message<
isRequest,
string_body,
basic_fields<Allocator>> result{
std::move(m.base()),
json_to_string(m.body())};
result.prepare_payload();
return result;
}