<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="">HTTP 与其他库的比较</string>
    <string name="">目前已有一些 C++ 库实现了部分 HTTP 协议。我们将分析这些库所采用的消息模型，并讨论它们相对于 Beast 的优缺点。</string>
    <string name="">作者评估外部库的总体策略如下：</string>
    <string name="">* 审查消息模型。它能否表示一个完整的请求或</string>
    <string name="">* 审查流抽象层。这是一种对象类型，例如</string>
    <string name="">* 检查缓冲区处理方式。该库是否负责管理缓冲区</string>
    <string name="">* 该库如何处理尾部（trailers）等边界情况，</string>
    <string name="">来自外部库的声明示例已做编辑：</string>
    <string name="">cpp-netlib</string>
    <string name="">"cpp-netlib 是一个网络编程库，原本计划纳入 Boost，但并未通过正式审查。在撰写本文时，它仍然使用 Boost 的名称、命名空间和目录结构，尽管该项目声明不再将纳入 Boost 作为目标。该库基于 Boost.Asio，自称是“一组与网络相关的例程/实现，旨在提供一个健壮的跨平台网络库”。它将“通用消息类型”列为其特性之一。在上述链接的分支中，它使用了以下声明：\n``` template  struct basic_message {"</string>
    <string name="82">}; ```</string>
    <string name="">这个容器是用于表示 HTTP 消息的基类模板。它使用“标签（tag）”类型风格来特化各种特征类，从而允许对消息的各个部分进行自定义。例如，用户可以通过特化 `headers_container&lt;T&gt;` 来确定使用何种容器类型来存储头部字段。我们注意到该容器的声明存在一些问题：</string>
    <string name="">* 头部和主体容器只能通过默认构造函数进行构造。</string>
    <string name="">* 不支持有状态分配器。</string>
    <string name="">* 无法推迟 `body_` 类型的提交。</string>
    <string name="">* 消息模型包含了“源”和“目标”。这是</string>
    <string name="">* 对源（source）使用 `string_type`（一个自定义点），</string>
    <string name="">* 该库使用 `string&lt;Tag&gt;` 的特化来改变类型</string>
    <string name="">* 特化的特征类会导致大量小型</string>
    <string name="">* `string&lt;Tag&gt;` 自定义点限制了用户自定义主体类型</string>
    <string name="">该库消息容器的设计非常繁琐，其自定义系统依赖于特征（trait）特化。由于这些自定义机制在容器声明中的使用方式极为受限，导致该设计过于复杂，却未能带来相应的收益。</string>
    <string name="">Boost.HTTP</string>
    <string name="">是一个源自 2014 年 Google Summer of Code 的库。它曾于 2015 年提交进行 Boost 正式审查，但遭到拒绝。该库基于 Boost.Asio 构建，其开发工作一直持续至今。在之前链接的分支中，它使用了如下的消息声明：``` template struct basic_message {</string>
    <string name="166">private:</string>
    <string name="170">};</string>
    <string name="">typedef basic_message&lt;boost::http::headers, std::vector&lt;std::uint8_t&gt;&gt; message;</string>
    <string name="">template&lt;class Headers, class Body&gt; struct is_message&lt;basic_message&lt;Headers, Body&gt;&gt;: public std::true_type {}; ```</string>
    <string name="">* 该容器无法对完整的消息进行建模。[\'start-line] 项</string>
    <string name="">* `headers_`、`body_` 和 `trailers_` 只能进行默认构造，</string>
    <string name="">* 无法将 [*Body] 类型的提交推迟到之后进行</string>
    <string name="">* 不支持有状态分配器。这是由前一个限制导致的</string>
    <string name="">* 尾部（trailers）存储在一个单独的对象中。除了组合爆炸的问题之外</string>
    <string name="">* 这些声明暗示 `std::vector` 是 [*Body] 的一个模型。</string>
    <string name="">* [*Body] 自定义点将用户自定义类型限制为</string>
    <string name="">* 这种表示法仅能解决一小部分用例。其自定义潜力和性能表现均十分有限。由于该模型将起始行（start line）字段排除在外，导致其使用难度增加。</string>
    <string name="">C++ REST SDK (cpprestsdk)</string>
    <string name="">是一个微软项目，其目标是“帮助 C++ 开发者连接服务并与之交互”。在本文所评测的库中，它的功能最为全面，包括通过其 websocket++ 依赖项来支持 WebSocket 服务。在构建基于 Windows 的应用程序时，它能够使用诸如 HTTP.SYS 等原生 API，同时也支持使用 Boost.Asio。其中，WebSocket 模块专门且排他性地使用了 Boost.Asio。</string>
    <string name="">由于 cpprestsdk 由大型公司（微软）开发，它包含了相当多的功能，因此也必然拥有更多的接口。我们将把用于建模消息的接口拆解为更易于管理的部分。以下是用于存储 HTTP 头字段的容器： ``` class http_headers { public:</string>
    <string name="254">private:</string>
    <string name="256">}; ```</string>
    <string name="">这个声明相当简陋。我们注意到大多数字段容器都存在一些典型问题：</string>
    <string name="">* 该容器只能进行默认构造。</string>
    <string name="">* 不支持分配器，无论是有状态分配器还是其他类型的分配器。</string>
    <string name="">* 完全没有任何自定义点。</string>
    <string name="">现在，我们来分析更大的消息容器的结构。该库使用了句柄/主体惯用法（handle/body idiom）。它提供了两个公开的消息容器接口，一个用于请求（`http_request`），另一个用于响应（`http_response`）。每个接口都维护着一个指向实现类的私有共享指针。公开的成员函数调用会被路由到内部的实现。以下是第一个实现类，它构成了请求和响应实现类的基类： ``` namespace details {</string>
    <string name="">class http_msg_base { public:</string>
    <string name="">protected:</string>
    <string name="306">}; ```</string>
    <string name="">要理解这些声明，需要先了解 cpprestsdk 使用了微软的 [@https://msdn.microsoft.com/en-us/library/dd504870.aspx [*Concurrency Runtime]] 所定义的异步模型。来自 [@https://msdn.microsoft.com/en-us/library/jj987780.aspx [*`pplx` namespace]] 的标识符定义了任务和事件等通用异步模式。`concurrency::streams::istream` 参数和 `m_data_available` 数据成员表明存在关注点分离缺失的问题。在消息声明中，不应将 HTTP 消息的表示形式与用于序列化或解析这些消息的异步模型混为一谈。</string>
    <string name="">以下声明构成了公开接口中的句柄所引用的完整实现类（该类位于后续）：``` /// HTTP 请求消息的内部表示。 class _http_request final : public http::details::http_msg_base, public std::enable_shared_from_this&lt;_http_request&gt; { public:</string>
    <string name="336">private:</string>
    <string name="344">};</string>
    <string name="">} // namespace details ```</string>
    <string name="">与之前一样，可以看出，HTTP 请求的实现类更关注于异步发送消息的机制，而不是按照 __rfc7230__ 中的描述对 HTTP 消息进行实际建模：</string>
    <string name="">* 接受 `std::unique_ptr&lt;http::details::_http_server_context` 的构造函数</string>
    <string name="">* “取消令牌”被存储在消息内部。这破坏了</string>
    <string name="">* `_reply_impl` 函数意味着消息的实现也</string>
    <string name="">最后，下面是代表 HTTP 请求的公共类：``` class http_request { public:</string>
    <string name="407">private:</string>
    <string name="412">}; ```</string>
    <string name="">从这个声明中可以清楚地看出，该库中消息模型的目标是由其用例（与 REST 服务器交互）驱动的，而不是为了对 HTTP 消息进行通用建模。我们注意到存在与其他声明类似的问题：</string>
    <string name="">* 完全没有编译期自定义点。唯一</string>
    <string name="">* 消息体的提取与异步模型混为一谈。</string>
    <string name="">* 无法为提取数据时所使用的容器定义分配器</string>
    <string name="">* 消息体只能提取一次，这限制了该容器的使用</string>
    <string name="">* 设置消息体需要使用向量或 `concurrency::streams::istream`。</string>
    <string name="">* HTTP 请求容器混淆了 HTTP 响应行为（见</string>
    <string name="">cpprestsdk 中 HTTP 消息模型的总体主题是“不支持用户可定义的自定义功能”。它没有分配器支持，也没有关注点分离。它的设计目的是执行特定的行为集。换句话说，它没有遵循开闭原则。</string>
    <string name="">Concurrency Runtime 中的任务操作方式与 `std::future` 类似，但进行了一些改进，例如 C++ 标准中尚未包含的 continuation（续体）。使用基于任务的异步接口而非完成处理程序（completion handlers）的代价已有详细记录：在组合任务操作的调用链上存在无法优化的同步点。参见：[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3747.pdf [*A Universal Model for Asynchronous Operations]] (Kohlhoff)。</string>
</resources>
