From 06502c50cdd935600e797146bed689c333373480 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 29 Sep 2025 03:45:47 -0700 Subject: [PATCH] doc work --- doc/modules/ROOT/images/ClassHierarchy.odg | Bin 25464 -> 20647 bytes doc/modules/ROOT/images/ClassHierarchy.svg | 2 +- doc/modules/ROOT/nav.adoc | 10 +- doc/modules/ROOT/pages/1.primer.adoc | 115 +++++++++++++++ doc/modules/ROOT/pages/2.messages.adoc | 134 ++++++++++++++++++ .../pages/design_requirements/parser.adoc | 45 ++++++ doc/modules/ROOT/pages/header_containers.adoc | 1 - doc/modules/ROOT/pages/index.adoc | 107 +++++--------- 8 files changed, 334 insertions(+), 80 deletions(-) create mode 100644 doc/modules/ROOT/pages/1.primer.adoc create mode 100644 doc/modules/ROOT/pages/2.messages.adoc delete mode 100644 doc/modules/ROOT/pages/header_containers.adoc diff --git a/doc/modules/ROOT/images/ClassHierarchy.odg b/doc/modules/ROOT/images/ClassHierarchy.odg index 1ee5a75eed333b06cffe2499f9d702ac368393c2..6f0432721a473a1c738e2c578d9a510f7e03212b 100644 GIT binary patch delta 19143 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 a2k8GRH` - + diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 95b69808..3aede057 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -1,20 +1,14 @@ +* xref:1.primer.adoc[] +* xref:2.messages.adoc[] * xref:sans_io_philosophy.adoc[] - * xref:http_protocol_basics.adoc[] - * xref:header_containers.adoc[] - * xref:message_bodies.adoc[] - * Serializing - * Parsing - * xref:Message.adoc[] - * Design Requirements ** xref:design_requirements/serializer.adoc[Serializer] ** xref:design_requirements/parser.adoc[Parser] - // * xref:reference:boost/http_proto.adoc[Reference] * xref:reference.adoc[Reference] diff --git a/doc/modules/ROOT/pages/1.primer.adoc b/doc/modules/ROOT/pages/1.primer.adoc new file mode 100644 index 00000000..949f878e --- /dev/null +++ b/doc/modules/ROOT/pages/1.primer.adoc @@ -0,0 +1,115 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/buffers +// + += HTTP Primer + +HTTP is a stream-oriented protocol between two connected programs: one acting +as the client, the other as the server. While the connection is open, the client +sends an HTTP request, which the server reads and answers with an HTTP response. +These _messages_ are paired in order; each request has exactly one +corresponding response. This exchange of structured messages continues until +either peer closes the connection, whether normally or due to an error. + +HTTP messages consist of three parts: the start line, the headers, and the +message body. The start line differs between requests and responses, while +the headers and body share the same structure. Headers are made up of zero +or more fields, each expressed as a name–value pair. Both the start line and +the header fields use a line-oriented text format, with each line terminated +by a CRLF sequence (carriage return followed by line feed, i.e. bytes +`0x0D 0x0A`). The message body is a sequence of bytes of defined length, +with content determined by the semantics of the start line and headers. + +This diagram shows an actual HTTP request and HTTP response + +[cols="1a,1a"] +|=== +|HTTP Request|HTTP Response + +| +[source] +---- +GET /index.html HTTP/1.1\r\n +User-Agent: Boost\r\n +\r\n +---- +| +[source] +---- +HTTP/1.1 200 OK\r\n +Server: Boost.Http.Proto\r\n +Content-Length: 13\r\n +\r\n +Hello, world! +---- + +|=== + +More formally, the ABNF for HTTP messages is defined as follows: + +[cols="1a,4a"] +|=== +|Name|ABNF + +|message +|[literal] +HTTP-message = request-line / status-line + *( header-field CRLF ) + CRLF + [ message-body ] + +|request-line +|[literal] +request-line = method SP request-target SP HTTP-version CRLF + +|status-line +|[literal] +status-line = HTTP-version SP status-code SP reason-phrase CRLF + +|=== + + +Most HTTP header field values are domain-specific or application-defined, while +certain fields commonly recur. The library understands these fields and takes +appropriate action to ensure RFC compliance: + +[cols="1a,4a"] +|=== +|Field|Description + +a| +https://tools.ietf.org/html/rfc7230#section-6.1[*Connection*] + +https://tools.ietf.org/html/rfc7230#appendix-A.1.2[*Proxy-Connection*] + +|This field lets the sender specify control options for the current connection. +Typical values include close, keep-alive, and upgrade. + +|https://tools.ietf.org/html/rfc7230#section-3.3.2[*Content-Length*] +|When present, this field tells the recipient the exact size of the message +body, measured in bytes, that follows the header. + +|https://tools.ietf.org/html/rfc7230#section-3.3.1[*Transfer-Encoding*] +|This optional field specifies the sequence of transfer codings that have been, +or will be, applied to the content payload to produce the message body. + +The library supports the +chunked, +gzip, +deflate, and +brotli +encoding schemes, +in any valid combination. Encodings can be automatically applied or removed +as needed by the caller. + +|https://tools.ietf.org/html/rfc7230#section-6.7[*Upgrade*] +|The Upgrade header field provides a mechanism to transition from HTTP/1.1 to +another protocol on the same connection. For example, it is the mechanism used +by WebSocket's initial HTTP handshake to establish a WebSocket connection. + +|=== + diff --git a/doc/modules/ROOT/pages/2.messages.adoc b/doc/modules/ROOT/pages/2.messages.adoc new file mode 100644 index 00000000..b003c7a3 --- /dev/null +++ b/doc/modules/ROOT/pages/2.messages.adoc @@ -0,0 +1,134 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http_proto +// + += Messages + +The library provides both modifiable containers and immutable views for +requests, responses, and standalone field sets such as trailers. These can +be used to store incoming messages or construct outgoing ones. Unlike other +libraries, such as its predecessor Boost.Beast, the message body is kept +separate. In other words, the containers and views offered by this library +do not include the body. + +NOTE: By omitting the body from its container and view types, the library avoids +the need for templates—unlike the message container in Boost.Beast. Experience +has shown that templated containers create poor ergonomics, a design flaw this +library corrects. + +The following table lists the types used to model containers and views: + +[cols="1a,4a"] +|=== +|Type|Description + +|cpp:fields[] +|A modifiable container of header fields. + +|cpp:fields_view[] +|A read-only view to a cpp:fields[] + +|cpp:message[] +|A modifiable container holding a start-line and header fields, with + accompanying metadata. + +|cpp:message_view[] +|A read-only view to a cpp:message[] + +|cpp:request[] +|A modifiable container holding a request-line and header fields, with + accompanying metadata. + +|cpp:request_view[] +|A read-only view to a cpp:request[] + +|cpp:response[] +|A modifiable container holding a status-line and header fields, with + accompanying metadata. + +|cpp:response_view[] +|A read-only view to a cpp:response[] + +|=== + +== Construction + +All containers maintain the following invariants: + +* The container’s contents are always stored in serialized form that is + syntactically valid. + +* Any modification that would produce a malformed field or start line + throws an exception, with strong exception safety guaranteed. + +To satisfy these invariants, default-constructed containers +initially consist of a start line: + +[source,cpp] +---- +request req; +response res; + +assert(req->buffer() == "GET / HTTP/1.1\r\n\r\n"); +assert(res->buffer() == "HTTP/1.1 200 OK\r\n\r\n"); +---- + +The `buffer` function runs in constant time, never throws exceptions, +and returns a cpp:boost::core::string_view[] representing the complete +serialized object. + +Each header field consists of a name and a value, both stored as strings, +with prescribed ABNF format: + +[source] +---- +field-name = token +field-value = *( field-content / obs-fold ) +field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +field-vchar = VCHAR / obs-text + +obs-fold = CRLF 1*( SP / HTAB ) + ; obsolete line folding +---- + +Operations that create or modify fields throw an exception if the name or +value violates the syntactic requirements of the protocol. + +Although fields may be identified by comparing their names, the library +provides the field enumeration, which defines a wide set of constants for +well-known field names. Internally, containers maintain a lookup table so +that specifying fields by enumeration replaces costly string comparisons +with efficient integer comparisons. + +The following example builds an +https://tools.ietf.org/html/rfc7231#section-4.3.1[HTTP GET] +request: + +[cols="1a,1a"] +|=== +|Code|Serialized Result + +| +[source,cpp] +---- +request req( method::get, "/index.htm", version::http_1_1 ); +req.append( field::accept, "text/html" ); +req.append( "User-Agent", "Boost" ); +---- +| +[literal] +GET /index.htm HTTP/1.1\r\n +Accept: text/html\r\n +User-Agent: Boost\r\n +\r\n + +|=== + +NOTE: Field-specific syntax (e.g., for date values) is not fully validated by +this library. It is the application’s responsibility to follow the relevant +specifications to ensure correct behavior. diff --git a/doc/modules/ROOT/pages/design_requirements/parser.adoc b/doc/modules/ROOT/pages/design_requirements/parser.adoc index 1b5379ce..204756eb 100644 --- a/doc/modules/ROOT/pages/design_requirements/parser.adoc +++ b/doc/modules/ROOT/pages/design_requirements/parser.adoc @@ -9,6 +9,51 @@ = Parser Design Requirements +== Design + +=== Comparison to Boost.Beast + +This library builds on the experiences learned from Boost.Beast's seven years +of success. Beast brings these unique design strengths: + +* Body type named requirements +* First-class message container +* Individual parser and serializer objects + +The message container suffers from these problems: + +* Templated on the body type. +* Templated on Allocator +* Node-based implementation +* Serialization is too costly + +Meanwhile parsers and serializes suffer from these problems: + +* Buffer-at-a-time operation is clumsy. +* Objects are not easily re-used +* Parser is a class template because of body types + +==== Message Container + +In HTTP.Proto the message container implementation always stores the complete +message or fields in its correctly serialized form. Insertions and modifications +are performed in linear time. When the container is reused, the amortized cost +of reallocation becomes zero. A small lookup table is stored past the end of +the serialized message, permitting iteration in constant time. + +==== Parser + +The HTTP.Proto parser is designed to persist for the lifetime of the connection +or application. It allocates a fixed size memory buffer upon construction and +uses this memory region to perform type-erasure and apply or remove content +encodings to the body. The parser is a regular class instead of a class +template, which greatly improves its ease of use over the Beast parser design. + +==== Serializer + +As with the parser, the serializer is designed to persist for the lifetime of +the connection or application and also allocates a fixed size buffer. + == Memory Allocation and Memory Utilization The `parser` must use a single block of memory allocated during construction and diff --git a/doc/modules/ROOT/pages/header_containers.adoc b/doc/modules/ROOT/pages/header_containers.adoc deleted file mode 100644 index 9c0fa11d..00000000 --- a/doc/modules/ROOT/pages/header_containers.adoc +++ /dev/null @@ -1 +0,0 @@ -= Header Containers diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index 0ac41ab6..975cfb76 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -9,33 +9,46 @@ = Boost.HTTP.Proto -Boost.HTTP.Proto is a portable C++ library which provides containers and -algorithms which implement the HTTP/1.1 protocol, widely used to deliver content -on the Internet. It adheres strictly to the HTTP/1.1 RFC specification -(henceforth referred to as https://datatracker.ietf.org/doc/html/rfc9110[rfc9110,window=blank_]). - -This library understands the grammars related to HTTP nessages and provides -functionality to validate, parse, examine, and modify messages. - -== Features +This is a portable C++ library offering containers and algorithms for implementing +the HTTP/1.1 protocol. The format is widely used to deliver content on the Internet, +and this implementation adheres strictly to the +https://datatracker.ietf.org/doc/html/rfc9110[HTTP/1.1 RFC specification], +henceform referred to as the RFC. The library is distinguished by these +provided features: + +* Sans-I/O approach +* Requires only C++11 +* Works without exceptions +* Fast compilation, few templates +* Advanced handling of memory (RAM) + +== Sans-I/O + +While this library implements the HTTP protocol, it does so without performing +any actual network activity as the logic is completely isolated from the +underlying I/O operations. The implementation manages state, ensures RFC +compliance, and provides the application-level interface for building and +inspecting HTTP messages and their payloads, and it is necessary to use or +write the interfacing network implementation on top of HTTP.Proto. + +The companion library Boost.HTTP.IO uses Boost.HTTP.Proto to implement network +I/O using Boost.Asio. The +https://sans-io.readthedocs.io/[sans-I/O] website goes into more depth regarding +this innovative approach to designing protocol libraries. == Requirements -The library requires a compiler supporting at least C++11. - -Standard types such as `error_code` or `string_view` use their Boost equivalents. +* Requires Boost and a compiler supporting at least C++11 +* Link to a static or dynamically linked version of this library +* Supports `-fno-exceptions`, detected automatically == Tested Compilers -Boost.HTTP.Proto has been tested with the following compilers: - -* clang: -* gcc: -* msvc: - -and these architectures: x86, x64 +Boost.HTTP.Proto is tested with the following compiler versions: -We do not test and support gcc 8.0.1. +* gcc: 5 to 14 (except 8.0.1) +* clang: 3.9, 4 to 18 +* msvc: 14.1 to 14.42 == Quality Assurance @@ -43,7 +56,6 @@ The development infrastructure for the library includes these per-commit analyse * Coverage reports * Compilation and tests on Drone.io and GitHub Actions -* Regular code audits for security == ABNF @@ -52,58 +64,13 @@ https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form[Backus-Naur Form,window=b (ABNF) notation of https://datatracker.ietf.org/doc/html/rfc5234[rfc5234,window=blank_] to specify particular grammars used by algorithms and containers. -While a complete understanding of the notation is not a requirement for using the -library, it may help for an understanding of how valid components of URLs are defined. -In particular, this is of interest to users who wish to compose parsing algorithms -using the combinators provided by the library. +While a complete understanding of the notation is not a requirement for using +the library, it may help for an understanding of how valid components of HTTP +messages are defined. In particular, this is of interest to users who wish to +compose parsing algorithms using the combinators provided by the library. == Acknowledgments This library wouldn't be where it is today without the help of https://github.com/pdimov[Peter Dimov,window=blank_] for design advice and general assistance. - -== Design - -=== Comparison to Boost.Beast - -This library builds on the experiences learned from Boost.Beast's seven years -of success. Beast brings these unique design strengths: - -* Body type named requirements -* First-class message container -* Individual parser and serializer objects - -The message container suffers from these problems: - -* Templated on the body type. -* Templated on Allocator -* Node-based implementation -* Serialization is too costly - -Meanwhile parsers and serializes suffer from these problems: - -* Buffer-at-a-time operation is clumsy. -* Objects are not easily re-used -* Parser is a class template because of body types - -==== Message Container - -In HTTP.Proto the message container implementation always stores the complete -message or fields in its correctly serialized form. Insertions and modifications -are performed in linear time. When the container is reused, the amortized cost -of reallocation becomes zero. A small lookup table is stored past the end of -the serialized message, permitting iteration in constant time. - -==== Parser - -The HTTP.Proto parser is designed to persist for the lifetime of the connection -or application. It allocates a fixed size memory buffer upon construction and -uses this memory region to perform type-erasure and apply or remove content -encodings to the body. The parser is a regular class instead of a class -template, which greatly improves its ease of use over the Beast parser design. - -==== Serializer - -As with the parser, the serializer is designed to persist for the lifetime of -the connection or application and also allocates a fixed size buffer.