Chinese (Simplified Han script) (Boost Beast Translation - en to zh_Hans translation catalogue (36 entries)

note: type: section title
en: HTTP Message Container __video__
zh_Hans: HTTP 消息容器 __video__

note: type: paragraph
en: The following video presentation was delivered at [@https://cppcon.org/ CppCon] in 2017. The presentation provides a simplified explanation of the design process for the HTTP message container used in Beast. The slides and code used are available in the [@https://github.com/vinniefalco/CppCon2017 GitHub repository].
zh_Hans: 以下视频演示是在 2017 年的 [@https://cppcon.org/ CppCon] 上发表的。该演示简要介绍了 Beast 中使用的 HTTP 消息容器的设计过程。演示所用的幻灯片和代码可在 [@https://github.com/vinniefalco/CppCon2017 GitHub 仓库] 中找到。

note: type: paragraph
en: In this section we describe the problem of modeling HTTP messages and explain how the library arrived at its solution, with a discussion of the benefits and drawbacks of the design choices. The goal for creating a message model is to create a container with value semantics, possibly movable and/or copyable, that contains all the information needed to serialize, or all of the information captured during parsing. More formally, given:
zh_Hans: 在本节中，我们将探讨 HTTP 消息建模所面临的挑战，并阐述该库是如何推导出最终解决方案的，同时也会深入剖析这些设计选择的利弊。构建消息模型的核心目标，是打造一个具备值语义（value semantics）的容器（支持移动和/或复制），使其能够囊括序列化所需的全部信息，或是完整保留解析过程中捕获的所有数据。从更严谨的层面来说，给定：

note: type: list
en: * `m` is an instance of an HTTP message container
zh_Hans: * `m` 是 HTTP 消息容器的一个实例

note: type: list
en: * `x` is a series of octets describing a valid HTTP message in
zh_Hans: * `x` 是一系列字节（octets），用于描述一个有效的 HTTP 消息

note: type: list
en: * `S(m)` is a serialization function which produces a series of octets
zh_Hans: * `S(m)` 是一个序列化函数，用于生成一系列字节（octets）

note: type: list
en: * `P(x)` is a parsing function which produces a message container from
zh_Hans: * `P(x)` 是一个解析函数，用于从……生成一个消息容器

note: type: paragraph
en: These relations are true:
zh_Hans: 以下关系成立：

note: type: paragraph
en: We would also like our message container to have customization points permitting the following: allocator awareness, user-defined containers to represent header fields, and user-defined types and algorithms to represent the body. And finally, because requests and responses have different fields in the ['start-line], we would like the containers for requests and responses to be represented by different types for function overloading.
zh_Hans: 此外，我们还希望消息容器提供自定义扩展点，以实现以下功能：支持分配器感知（allocator awareness）、允许使用用户自定义的容器来表示首部字段，以及支持使用用户自定义的类型和算法来表示消息体。最后，由于请求和响应的起始行（start-line）包含不同的字段，我们希望分别使用不同的类型来表示请求和响应的容器，从而支持函数重载。

note: type: paragraph
en: Here is our first attempt at declaring some message containers:
zh_Hans: 这是我们首次尝试声明一些消息容器：

note: type: paragraph
en: These containers are capable of representing everything in the model of HTTP requests and responses described in __rfc7230__. Request and response objects are different types. The user can choose the container used to represent the fields. And the user can choose the [*Body] type, which is a concept defining not only the type of `body` member but also the algorithms used to transfer information in and out of that member when performing serialization and parsing.
zh_Hans: 这些容器能够完整地表达 RFC 7230 中描述的 HTTP 请求和响应模型。请求对象和响应对象是两种不同的类型。用户可以选择用于表示首部字段的容器。此外，用户还可以选择 `Body` 类型，该类型不仅定义了 `body` 成员的类型，还规定了在序列化和解析过程中，信息进出该成员所使用的算法。

note: type: paragraph
en: However, a problem arises. How do we write a function which can accept an object that is either a request or a response? As written, the only obvious solution is to make the message a template type. Additional traits classes would then be needed to make sure that the passed object has a valid type which meets the requirements. These unnecessary complexities are bypassed by making each container a partial specialization: ``` /// An HTTP message template<bool isRequest, class Fields, class Body> struct message;
zh_Hans: 然而，问题随之而来。如何编写一个既能接受请求对象又能接受响应对象的函数呢？按照当前的写法，最显而易见的解决方案是将消息类型设为模板参数。这样一来，还需要额外的特征类（traits classes）来确保传入的对象具有符合要求的有效类型。为了避免这些不必要的复杂性，我们可以将每个容器设计为偏特化（partial specialization）：```/// An HTTP message template<bool isRequest, class Fields, class Body> struct message;

note: type: paragraph
en: /// An HTTP request template<class Fields, class Body> struct message<true, Fields, Body> {
zh_Hans: /// 一个 HTTP 请求模板<class Fields, class Body> struct message<true, Fields, Body> {

note: type: paragraph
en: /// An HTTP response template<bool isRequest, class Fields, class Body> struct message<false, Fields, Body> {
zh_Hans: /// 一个 HTTP 响应模板<bool isRequest, class Fields, class Body> struct message<false, Fields, Body> {

note: type: paragraph
en: Now we can declare a function which takes any message as a parameter: ``` template<bool isRequest, class Fields, class Body> void f(message<isRequest, Fields, Body>& msg); ```
zh_Hans: 现在，我们可以声明一个能够接受任意消息作为参数的函数了：``` template<bool isRequest, class Fields, class Body> void f(message<isRequest, Fields, Body>& msg); ```

note: type: paragraph
en: This function can manipulate the fields common to requests and responses. If it needs to access the other fields, it can use overloads with partial specialization, or in C++17 a `constexpr` expression: ``` template<bool isRequest, class Fields, class Body> void f(message<isRequest, Fields, Body>& msg) {
zh_Hans: 该函数可以处理请求和响应共有的字段。如果需要访问其他字段，则可以使用基于偏特化的重载，或者在 C++17 中使用 constexpr 表达式：``` template<bool isRequest, class Fields, class Body> void f(message<isRequest, Fields, Body>& msg) {

note: type: paragraph
en: Often, in non-trivial HTTP applications, we want to read the HTTP header and examine its contents before choosing a type for [*Body]. To accomplish this, there needs to be a way to model the header portion of a message. And we'd like to do this in a way that allows functions which take the header as a parameter, to also accept a type representing the whole message (the function will see just the header part). This suggests inheritance, by splitting a new base class off of the message: ``` /// An HTTP message header template<bool isRequest, class Fields> struct header; ```
zh_Hans: 通常，在非平凡的 HTTP 应用程序中，我们希望在为 [*Body] 选择类型之前，先读取 HTTP 头部并检查其内容。为了实现这一点，需要有一种方法来对消息的头部部分进行建模。并且，我们希望以这样的方式来实现：让那些将头部作为参数的函数，也能够接受代表整个消息的类型（函数只会看到头部部分）。这表明可以通过从 message 中分离出一个新的基类来使用继承：``` /// An HTTP message header template<bool isRequest, class Fields> struct header; ```

note: type: paragraph
en: Code which accesses the fields has to laboriously mention the `fields` member, so we'll not only make `header` a base class but we'll make a quality of life improvement and derive the header from the fields for notational convenience. In order to properly support all forms of construction of [*Fields] there will need to be a set of suitable constructor overloads (not shown): ``` /// An HTTP request header template<class Fields> struct header<true, Fields> : Fields {
zh_Hans: 访问字段的代码不得不反复显式引用 `fields` 成员，这显得十分繁琐。因此，我们不仅将 `header` 作为基类，还会为了书写便利进行一项优化：让 `header` 直接从 `fields` 派生。为了能够正确支持对 [*Fields] 的所有构造形式，还需要提供一组合适的构造函数重载（此处未展示）：``` /// An HTTP request header template<class Fields> struct header<true, Fields> : Fields {

[ctx: 194]
note: type: paragraph
en: /// An HTTP response header template<class Fields> struct header<false, Fields> : Fields {
zh_Hans: /// 一个 HTTP 响应头部 template struct header<false, Fields> : Fields {

note: type: paragraph
en: /// An HTTP message template<bool isRequest, class Fields, class Body> struct message : header<isRequest, Fields> {
zh_Hans: /// 一个 HTTP 消息 template<bool isRequest, class Fields, class Body> struct message : header<isRequest, Fields> {

note: type: paragraph
en: Note that the `message` class now has a constructor allowing messages to be constructed from a similarly typed `header`. This handles the case where the user already has the header and wants to make a commitment to the type for [*Body]. A function can be declared which accepts any header: ``` template<bool isRequest, class Fields> void f(header<isRequest, Fields>& msg); ```
zh_Hans: 请注意，`message` 类现在拥有一个构造函数，允许从类型相同的 `header` 构造消息。这解决了用户已经拥有头部，并希望为 [*Body] 确定具体类型的情况。我们可以声明一个能够接受任意头部的函数： ``` template<bool isRequest, class Fields> void f(header<isRequest, Fields>& msg); ```

note: type: paragraph
en: Until now we have not given significant consideration to the constructors of the `message` class. But to achieve all our goals we will need to make sure that there are enough constructor overloads to not only provide for the special copy and move members if the instantiated types support it, but also allow the fields container and body container to be constructed with arbitrary variadic lists of parameters. This allows the container to fully support allocators.
zh_Hans: 到目前为止，我们还没有对 `message` 类的构造函数给予足够的重视。但为了实现所有目标，我们必须确保提供足够多的构造函数重载。这不仅需要在实例化的类型支持时提供特殊的拷贝和移动成员，还需要允许使用任意的可变参数列表来构造字段容器和主体容器。这使得容器能够完全支持分配器。

note: type: paragraph
en: The solution used in the library is to treat the message like a `std::pair` for the purposes of construction, except that instead of `first` and `second` we have the `Fields` base class and `message::body` member. This means that single-argument constructors for those fields should be accessible as they are with `std::pair`, and that a mechanism identical to the pair's use of `std::piecewise_construct` should be provided. Those constructors are too complex to repeat here, but interested readers can view the declarations in the corresponding header file.
zh_Hans: 库中采用的解决方案是：在构造 `message` 时，将其视作 `std::pair` 来处理。不同的是，原本 `std::pair` 中的 `first` 和 `second` 在这里被替换成了 `Fields` 基类和 `message::body` 成员。这意味着，这些字段的单参数构造函数应当像 `std::pair` 一样易于调用，并且需要提供一套与 `std::pair` 的 `std::piecewise_construct` 完全相同的机制。由于这些构造函数的实现较为复杂，这里不再赘述，感兴趣的读者可以直接查阅相应头文件中的声明。

note: type: paragraph
en: There is now significant progress with our message container but a stumbling block remains. There is no way to control the allocator for the `std::string` members. We could add an allocator to the template parameter list of the header and message classes, use it for those strings. This is unsatisfying because of the combinatorial explosion of constructor variations needed to support the scheme. It also means that request messages could have [*four] different allocators: two for the fields and body, and two for the method and target strings. A better solution is needed.
zh_Hans: 尽管我们的消息容器已经取得了显著进展，但仍面临一个棘手的问题：目前无法控制 `std::string` 成员的分配器。虽然可以在 `header` 和 `message` 类的模板参数列表中添加一个分配器来管理这些字符串，但这并不是一个令人满意的方案。因为这需要引入数量庞大的构造函数重载来支持该机制，导致组合爆炸。此外，这也意味着请求消息可能会拥有多达四种不同的分配器：其中两个分别用于字段和主体，另外两个则用于方法（method）和目标（target）字符串。因此，我们需要寻找一个更好的解决方案。

note: type: paragraph
en: To get around this we make an interface modification and then add a requirement to the [*Fields] type. First, the interface change: ``` /// An HTTP request header template<class Fields> struct header<true, Fields> : Fields {
zh_Hans: 为了解决这个问题，我们首先对接口进行修改，然后对 `Fields` 类型增加一项要求。首先是接口层面的改动：``` /// 一个 HTTP 请求头部 template<class Fields> struct header<true, Fields> : Fields {

[ctx: 271]
note: type: paragraph
en: /// An HTTP response header template<class Fields> struct header<false, Fields> : Fields {
zh_Hans: /// 一个 HTTP 响应头部 template struct header<false, Fields> : Fields {

note: type: paragraph
en: The start-line data members are replaced by traditional accessors using non-owning references to string buffers. The method is stored using a simple integer instead of the entire string, for the case where the method is recognized from the set of known verb strings.
zh_Hans: 起始行（start-line）的数据成员被替换成传统的访问器（accessors），它们通过不持有所有权的引用来指向字符串缓冲区。此外，对于能从已知动词字符串集合中识别出的方法（method），系统直接使用一个简单的整数来存储，而不再保存完整的字符串。

note: type: paragraph
en: Now we add a requirement to the fields type: management of the corresponding string is delegated to the [*Fields] container, which can already be allocator aware and constructed with the necessary allocator parameter via the provided constructor overloads for `message`. The delegation implementation looks like this (only the response header specialization is shown): ``` /// An HTTP response header template<class Fields> struct header<false, Fields> : Fields {
zh_Hans: 对 `Fields` 类型增加一项要求：将对应字符串的管理委托给 `Fields` 容器。该容器本身已具备分配器感知能力，且可通过 `message` 提供的构造函数重载，使用必要的分配器参数进行构造。委托的实现方式如下（仅展示响应头部的特化版本）：``` /// 一个 HTTP 响应头部 template<class Fields> struct header<false, Fields> : Fields {

note: type: paragraph
en: Now that we've accomplished our initial goals and more, there are a few more quality of life improvements to make. Users will choose different types for `Body` far more often than they will for `Fields`. Thus, we swap the order of these types and provide a default. Then, we provide type aliases for requests and responses to soften the impact of using `bool` to choose the specialization:
zh_Hans: 初步目标已超额达成，接下来还需进行一些提升易用性的改进。由于用户为 `Body` 选择自定义类型的频率远高于为 `Fields` 选择，因此将这两者的模板参数顺序互换，并为 `Fields` 提供默认类型。随后，为请求和响应提供类型别名，以缓解直接使用 `bool` 来选择特化版本带来的不便：

note: type: paragraph
en: ``` /// An HTTP header template<bool isRequest, class Body, class Fields = fields> struct header;
zh_Hans: ``` /// 一个 HTTP 头部 template<bool isRequest, class Body, class Fields = fields> struct header;

note: type: paragraph
en: /// An HTTP message template<bool isRequest, class Body, class Fields = fields> struct message;
zh_Hans: /// 一个 HTTP 消息 template<bool isRequest, class Body, class Fields = fields> struct message;

note: type: paragraph
en: /// An HTTP request template<class Body, class Fields = fields> using request = message<true, Body, Fields>;
zh_Hans: /// 一个 HTTP 请求 template<class Body, class Fields = fields> using request = message<true, Body, Fields>;

note: type: paragraph
en: /// An HTTP response template<class Body, class Fields = fields> using response = message<false, Body, Fields>; ```
zh_Hans: /// 一个 HTTP 响应 template<class Body, class Fields = fields> using response = message<false, Body, Fields>; ```

note: type: paragraph
en: This allows concise specification for the common cases, while allowing for maximum customization for edge cases: ``` request<string_body> req;
zh_Hans: 这样既能以简洁的方式满足常见需求，又能为边缘情况提供最大程度的定制空间：``` request<string_body> req;

note: type: paragraph
en: This container is also capable of representing complete HTTP/2 messages. Not because it was explicitly designed for, but because the IETF wanted to preserve message compatibility with HTTP/1. Aside from version specific fields such as Connection, the contents of HTTP/1 and HTTP/2 messages are identical even though their serialized representation is considerably different. The message model presented in this library is ready for HTTP/2.
zh_Hans: 该容器同样能够表示完整的 HTTP/2 消息。这并非因为它是为此而专门设计的，而是因为 IETF 希望保持与 HTTP/1 的消息兼容性。除了像 `Connection` 这类特定于版本的字段之外，HTTP/1 和 HTTP/2 消息的内容是完全相同的，尽管它们的序列化表示形式存在显著差异。因此，本库中展示的消息模型已经为 HTTP/2 做好了准备。

note: type: paragraph
en: In conclusion, this representation for the message container is well thought out, provides comprehensive flexibility, and avoids the necessity of defining additional traits classes. User declarations of functions that accept headers or messages as parameters are easy to write in a variety of ways to accomplish different results, without forcing cumbersome SFINAE declarations everywhere.
zh_Hans: 综上所述，这种消息容器的表示方式经过深思熟虑，提供了全面的灵活性，并且免去了定义额外特征类（traits classes）的必要。用户声明接受头部或消息作为参数的函数时，可以通过多种方式轻松编写，以实现不同的目的，而无需在代码中到处使用繁琐的 SFINAE 声明。
