diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 0d2cb66..bfebc0a 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -1,22 +1,22 @@ * xref:index.adoc[Introduction] -* xref:quick-start.adoc[Quick Start] +* xref:quick-start.adoc[] * Guide -** xref:2.guide/2a.making-requests.adoc[Making Requests] -** xref:2.guide/2b.request-bodies.adoc[Request Bodies] -** xref:2.guide/2c.responses.adoc[Responses] -** xref:2.guide/2d.error-handling.adoc[Error Handling] -** xref:2.guide/2e.headers.adoc[Headers] -** xref:2.guide/2f.query-parameters.adoc[Query Parameters] -** xref:2.guide/2g.authentication.adoc[Authentication] -** xref:2.guide/2h.redirects.adoc[Redirects] -** xref:2.guide/2i.cookies.adoc[Cookies] -** xref:2.guide/2j.timeouts.adoc[Timeouts] -** xref:2.guide/2k.connection-pool.adoc[Connection Pool] -** xref:2.guide/2l.proxies.adoc[Proxies] -** xref:2.guide/2m.compression.adoc[Compression] -** xref:2.guide/2n.extending.adoc[Extending] +** xref:2.guide/2a.making-requests.adoc[] +** xref:2.guide/2b.request-bodies.adoc[] +** xref:2.guide/2c.responses.adoc[] +** xref:2.guide/2d.error-handling.adoc[] +** xref:2.guide/2e.headers.adoc[] +** xref:2.guide/2f.query-parameters.adoc[] +** xref:2.guide/2g.authentication.adoc[] +** xref:2.guide/2h.redirects.adoc[] +** xref:2.guide/2i.cookies.adoc[] +** xref:2.guide/2j.timeouts.adoc[] +** xref:2.guide/2k.connection-pool.adoc[] +** xref:2.guide/2l.proxies.adoc[] +** xref:2.guide/2m.compression.adoc[] +** xref:2.guide/2n.extending.adoc[] * Examples -** xref:examples/usage.adoc[Usage] -** xref:examples/nlohmann_json.adoc[nlohmann/json] -* xref:testing.adoc[Testing] +** xref:examples/usage.adoc[] +** xref:examples/nlohmann_json.adoc[] +* xref:testing.adoc[] * xref:reference:boost/burl.adoc[Reference] diff --git a/doc/modules/ROOT/pages/2.guide/2a.making-requests.adoc b/doc/modules/ROOT/pages/2.guide/2a.making-requests.adoc index c2700e6..29b8b29 100644 --- a/doc/modules/ROOT/pages/2.guide/2a.making-requests.adoc +++ b/doc/modules/ROOT/pages/2.guide/2a.making-requests.adoc @@ -85,7 +85,7 @@ feature. A chain is finished by one of three terminal calls. == Finishing: send -cpp:request_builder::send[send()] sends the request and reads the status line +cpp:request_builder::send[send] sends the request and reads the status line and headers, leaving the body unread. It yields `(error_code, response)`: [source,cpp] @@ -135,7 +135,7 @@ xref:2.guide/2d.error-handling.adoc[error handling] section. == Finishing: build and execute -cpp:request_builder::build[build()] does not send anything. It returns a +cpp:request_builder::build[build] does not send anything. It returns a cpp:request[] — a plain value holding the method, URL, headers, body, and per-request options — that can be stored, passed around, and executed later through cpp:client::execute[]: @@ -159,9 +159,7 @@ client; it can be executed by whichever client is convenient. == Next Steps -* xref:2.guide/2b.request-bodies.adoc[Request Bodies] — Attaching a payload -* xref:2.guide/2c.responses.adoc[Responses] — Reading the result -* xref:2.guide/2d.error-handling.adoc[Error Handling] — What the error code - means -* xref:2.guide/2e.headers.adoc[Headers and Query Parameters] — Shaping the - request +* xref:2.guide/2b.request-bodies.adoc[] — Attaching a payload +* xref:2.guide/2c.responses.adoc[] — Reading the result +* xref:2.guide/2d.error-handling.adoc[] — What the error code means +* xref:2.guide/2e.headers.adoc[] — Shaping the request diff --git a/doc/modules/ROOT/pages/2.guide/2b.request-bodies.adoc b/doc/modules/ROOT/pages/2.guide/2b.request-bodies.adoc index b390850..fcc17f4 100644 --- a/doc/modules/ROOT/pages/2.guide/2b.request-bodies.adoc +++ b/doc/modules/ROOT/pages/2.guide/2b.request-bodies.adoc @@ -9,138 +9,155 @@ = Request Bodies -A request body is attached with the builder's cpp:request_builder::body[body] -function. The type of the value decides three things: the `Content-Type` header, +A request body is attached with the cpp:request_builder::body[] function. +The type of the value decides three things: the `Content-Type` header, whether the body is sent with a known `Content-Length` or with chunked transfer -encoding, and how the bytes are produced on the wire. This section covers the -built-in body types, xref:2.guide/2n.extending.adoc[extending] shows how to add -your own type. +encoding, and how the bytes are produced on the wire. == How the Body Is Framed cpp:request_builder::body[body] accepts any value for which a conversion is -defined, by calling `tag_invoke` with a cpp:body_from_tag[]. The result is an -cpp:any_request_body[]. - -Two rules hold for every body type: +defined, by calling `tag_invoke` with a cpp:body_from_tag[]; the result is an +cpp:any_request_body[]. Two rules then hold for every body type: * The body's `Content-Type` is sent *unless* the request already sets that - header explicitly. Setting `http::field::content_type` on the request always + header explicitly — setting `http::field::content_type` on the request always wins. -* The framing — a `Content-Length` header, or chunked transfer encoding — is - *always* taken from the body and cannot be overridden. A body whose size is - known in advance is sent with a `Content-Length`; one whose size is not - is sent chunked. +* The framing — a `Content-Length`, or chunked transfer encoding — is *always* + taken from the body and cannot be overridden. A body whose size is known in + advance is sent with a `Content-Length`; one whose size is not is sent chunked. + +== Built-in Body Types -== Strings +=== Strings -A cpp:std::string[], a cpp:std::string_view[], or a string literal becomes a -body with `Content-Type: text/plain; charset=utf-8` and a `Content-Length`: +A cpp:std::string[], a cpp:std::string_view[], or a string literal is sent as +`text/plain; charset=utf-8` with a `Content-Length`: [source,cpp] ---- -auto r = co_await client.post("https://example.com/post") +auto [ec, r] = co_await client.post("https://example.com/post") .body("plain text payload") .send(); ---- -A cpp:std::string[] body owns its data. A cpp:std::string_view[] or -string-literal body refers to the characters in place without copying, so the -underlying buffer must outlive the request. To send text under a different media -type, set the header explicitly: +The default `Content-Type` can be overridden by setting the header explicitly: [source,cpp] ---- -auto r = co_await client.post("https://example.com/post") +auto [ec, r] = co_await client.post("https://example.com/post") .body("hi") .header(http::field::content_type, "application/xml") .send(); ---- -== JSON +=== JSON + +The Boost.JSON types are sent as `application/json`, serialized incrementally as +the request goes out. Their serialized size is not known beforehand, so this is +the one built-in body sent with chunked transfer encoding: -The Boost.JSON types — `json::value`, `json::object`, `json::array`, and -`json::string` — are serialized incrementally as the request is sent, under -`Content-Type: application/json`. [source,cpp] ---- json::object obj({ { "user", "John" }, { "lang", "En" } }); -auto r = co_await client.post("https://example.com/post") +auto [ec, r] = co_await client.post("https://example.com/post") .body(obj) - .as(); + .send(); ---- -A literal value can be constructed inline by naming the type explicitly: +Naming the type builds the value inline, with no named variable: [source,cpp] ---- -auto r = co_await client.post("https://example.com/post") +auto [ec, r] = co_await client.post("https://example.com/post") .body({ 1, 2, 3 }) - .as(); + .send(); ---- -== URL-Encoded Forms +=== URL-Encoded Forms cpp:urlencoded_form[] builds an `application/x-www-form-urlencoded` body from -name and value pairs. Names and values are percent-encoded, with spaces written -as `+`. cpp:urlencoded_form::append[append] chains: +name/value pairs, sent with a `Content-Length`. +cpp:urlencoded_form::append[append] chains, and the same name may be added more +than once to submit a multi-valued field: [source,cpp] ---- -auto r = co_await client.post("https://example.com/post") +auto [ec, r] = co_await client.post("https://example.com/post") .body(burl::urlencoded_form() .append("user", "John") - .append("lang", "En")) - .as(); + .append("color", "blue") + .append("color", "green")) + .send(); +// user=John&color=blue&color=green +---- + +Names and values are percent-encoded: only the RFC 3986 unreserved characters +pass through, spaces become `+`, and the `&` and `=` separators are escaped, so a +value can never break out of the field structure. A form can also be built from +an initializer list or any range of pairs: + +[source,cpp] +---- +std::map fields = { + { "lang", "En" }, + { "user", "John" } }; + +auto [ec, r] = co_await client.post("https://example.com/post") + .body(fields) + .send(); ---- -== Multipart Forms +=== Multipart Forms -cpp:multipart_form[] builds a `multipart/form-data` body, the format browsers -use for file uploads. It mixes text parts and file parts, separated by a -randomly generated boundary: +cpp:multipart_form[] builds a `multipart/form-data` body from a sequence of +parts separated by a generated boundary, as specified by +https://www.rfc-editor.org/rfc/rfc7578[RFC 7578]: [source,cpp] ---- -auto r = co_await client.post("https://example.com/upload") +auto [ec, r] = co_await client.post("https://example.com/upload") .body(burl::multipart_form() .text("priority", "high") .file("attachment", "./report.log")) - .as(); + .send(); ---- -A file part is streamed from disk while the request is sent; only the path is -held in the form. The filename and `Content-Type` reported in the part header -are deduced from the path — falling back to `application/octet-stream` — and -either can be given explicitly. When the contents are already in memory but -should still be presented to the server as a file, -cpp:multipart_form::bytes[bytes] writes a part with a filename from an in-memory -buffer. +cpp:multipart_form::text[text] adds a plain field, while +cpp:multipart_form::file[file] adds a file that is streamed from disk as the +request is sent; only the path is held, with its filename and `Content-Type` +deduced from it unless set explicitly. When the data is already in memory but +should still arrive as a file, cpp:multipart_form::bytes[bytes] writes a part +with a filename from an in-memory buffer. -== Files +=== Files -Naming cpp:std::filesystem::path[] as the body type uploads a file. Its contents -are streamed while the request is sent, so the whole file is never held in -memory: +Naming cpp:std::filesystem::path[] uploads a file, streamed from disk so the +whole file is never held in memory. The `Content-Type` is deduced from the +filename extension, defaulting to `application/octet-stream`, and the +`Content-Length` is the file's size: [source,cpp] ---- -auto r = co_await client.put("https://example.com/put") +auto [ec, r] = co_await client.put("https://example.com/put") .body("./report.log") .send(); ---- -The `Content-Type` is deduced from the filename extension, falling back to -`application/octet-stream`, and the `Content-Length` is the file's size at the -time of the call. Because the length is fixed up front, a file that *shrinks* -before the transfer completes fails the request with cpp:error::file_changed[], -rather than sending a truncated or mis-framed body. +The size is read when cpp:request_builder::body[] is called, so a path whose +size cannot be determined throws cpp:std::filesystem::filesystem_error[] from +that call. Because the length is then fixed, a file that shrinks before the +transfer completes fails the request with cpp:error::file_changed[]. + +== Your Own Types + +cpp:request_builder::body[] accepts any type for which a `tag_invoke` conversion +is defined. xref:2.guide/2n.extending.adoc[] shows how to write one for a type +of your own. == Next Steps -* xref:2.guide/2c.responses.adoc[Responses] — Reading the body that comes back -* xref:2.guide/2n.extending.adoc[Extending] — Teaching `body` about your own - types -* xref:2.guide/2e.headers.adoc[Headers and Query Parameters] — Overriding the - `Content-Type` +* xref:2.guide/2c.responses.adoc[] — Reading the body that comes back +* xref:2.guide/2d.error-handling.adoc[] — Handling failures +* xref:2.guide/2e.headers.adoc[] — Overriding the `Content-Type` diff --git a/doc/modules/ROOT/pages/2.guide/2c.responses.adoc b/doc/modules/ROOT/pages/2.guide/2c.responses.adoc index 3ffa31b..5baf635 100644 --- a/doc/modules/ROOT/pages/2.guide/2c.responses.adoc +++ b/doc/modules/ROOT/pages/2.guide/2c.responses.adoc @@ -30,7 +30,7 @@ r.raise_for_status(); // throws on 4xx and 5xx r.version(); // http::version r.headers(); // const http::fields_base& r.url(); // urls::url_view, the final URL after redirects -r.content_length(); // optional, empty for a chunked body +r.content_length(); // std::optional, empty for a chunked body ---- == Reading the Body @@ -158,11 +158,7 @@ case the connection is simply closed on destruction rather than pooled. == Next Steps -* xref:2.guide/2d.error-handling.adoc[Error Handling] — What the read can fail - with -* xref:2.guide/2m.compression.adoc[Compression] — Transparent decompression - of the body -* xref:2.guide/2k.connection-pool.adoc[Connection Pool] — Where the connection - goes next -* xref:2.guide/2n.extending.adoc[Extending] — Converting the body to your own - types +* xref:2.guide/2d.error-handling.adoc[] — What the read can fail with +* xref:2.guide/2m.compression.adoc[] — Transparent decompression of the body +* xref:2.guide/2k.connection-pool.adoc[] — Where the connection goes next +* xref:2.guide/2n.extending.adoc[] — Converting the body to your own types diff --git a/doc/modules/ROOT/pages/2.guide/2d.error-handling.adoc b/doc/modules/ROOT/pages/2.guide/2d.error-handling.adoc index 8ab5bbc..356d02f 100644 --- a/doc/modules/ROOT/pages/2.guide/2d.error-handling.adoc +++ b/doc/modules/ROOT/pages/2.guide/2d.error-handling.adoc @@ -114,9 +114,7 @@ the *earliest* failure in this order: == Next Steps -* xref:2.guide/2c.responses.adoc[Responses] — Reading a body once the status is - known -* xref:2.guide/2j.timeouts.adoc[Timeouts] — A common source of transport - failures -* xref:2.guide/2h.redirects.adoc[Redirects] — Where +* xref:2.guide/2c.responses.adoc[] — Reading a body once the status is known +* xref:2.guide/2j.timeouts.adoc[] — A common source of transport failures +* xref:2.guide/2h.redirects.adoc[] — Where cpp:error::too_many_redirects[too_many_redirects] comes from diff --git a/doc/modules/ROOT/pages/2.guide/2e.headers.adoc b/doc/modules/ROOT/pages/2.guide/2e.headers.adoc index 2d1d7fa..b4e4ed5 100644 --- a/doc/modules/ROOT/pages/2.guide/2e.headers.adoc +++ b/doc/modules/ROOT/pages/2.guide/2e.headers.adoc @@ -14,7 +14,7 @@ request, or on an individual request. == Default Headers -cpp:client::headers[client::headers()] is the set of headers sent with every +cpp:client::headers[] is the set of headers sent with every request. A natural place for things that do not change between requests, such as a `User-Agent` or an `Accept`: @@ -77,10 +77,10 @@ known | The request URL | `Authorization` -| xref:2.guide/2g.authentication.adoc[Authentication] +| xref:2.guide/2g.authentication.adoc[] | `Cookie` -| The xref:2.guide/2i.cookies.adoc[cookie jar] +| The cpp:client::cookie_jar[cookie jar] | `Accept-Encoding` | The xref:2.guide/2m.compression.adoc[compression] settings, unless you set it @@ -89,10 +89,7 @@ known == Next Steps -* xref:2.guide/2f.query-parameters.adoc[Query Parameters] — Adding parameters to - the URL -* xref:2.guide/2g.authentication.adoc[Authentication] — The managed - `Authorization` header -* xref:2.guide/2b.request-bodies.adoc[Request Bodies] — Where `Content-Type` - comes from -* xref:2.guide/2i.cookies.adoc[Cookies] — The managed `Cookie` header +* xref:2.guide/2f.query-parameters.adoc[] — Adding parameters to the URL +* xref:2.guide/2g.authentication.adoc[] — The managed `Authorization` header +* xref:2.guide/2b.request-bodies.adoc[] — Where `Content-Type` comes from +* xref:2.guide/2i.cookies.adoc[] — The managed `Cookie` header diff --git a/doc/modules/ROOT/pages/2.guide/2f.query-parameters.adoc b/doc/modules/ROOT/pages/2.guide/2f.query-parameters.adoc index 56f68e2..5b5e698 100644 --- a/doc/modules/ROOT/pages/2.guide/2f.query-parameters.adoc +++ b/doc/modules/ROOT/pages/2.guide/2f.query-parameters.adoc @@ -9,14 +9,14 @@ = Query Parameters -Query parameters are the key-and-value pairs that follow the `?` in a URL. Burl -lets you add them with the builder's cpp:request_builder::query[query] function, +Query parameters are the name/value pairs that follow the `?` in a URL. Burl +lets you add them with the cpp:request_builder::query[] function, and they can equally be written straight into the URL you pass to the verb function, the two approaches produce the same request. == The query Function -cpp:request_builder::query[query] appends a key-and-value pair to the URL's +cpp:request_builder::query[] appends a name/value pair to the URL's query string, percent-encoding both: [source,cpp] @@ -33,9 +33,8 @@ they appear in the query string. == Parameters in the URL -You do not have to use cpp:request_builder::query[query] at all: parameters -written directly into the URL you supply are treated identically. These two -requests are equivalent: +Parameters written directly into the URL you supply are treated identically. +These two requests are equivalent: [source,cpp] ---- @@ -52,7 +51,7 @@ auto b = co_await client.get("https://example.com/search?category=shoes&color=bl == Combining Both -cpp:request_builder::query[query] appends to whatever the URL already carries: +cpp:request_builder::query[] appends to whatever the URL already carries: [source,cpp] ---- @@ -64,8 +63,6 @@ auto r = co_await client.get("https://example.com/search?category=shoes") == Next Steps -* xref:2.guide/2e.headers.adoc[Headers] — Setting request and default headers -* xref:2.guide/2a.making-requests.adoc[Making Requests] — The verb functions - that accept a URL -* xref:2.guide/2g.authentication.adoc[Authentication] — The managed - `Authorization` header +* xref:2.guide/2e.headers.adoc[] — Setting request and default headers +* xref:2.guide/2a.making-requests.adoc[] — The verb functions that accept a URL +* xref:2.guide/2g.authentication.adoc[] — The managed `Authorization` header diff --git a/doc/modules/ROOT/pages/2.guide/2g.authentication.adoc b/doc/modules/ROOT/pages/2.guide/2g.authentication.adoc index 4e18534..5ebedff 100644 --- a/doc/modules/ROOT/pages/2.guide/2g.authentication.adoc +++ b/doc/modules/ROOT/pages/2.guide/2g.authentication.adoc @@ -64,12 +64,10 @@ regardless of this setting. == Custom Schemes For an authentication scheme Burl does not build in, you can set the -`Authorization` header yourself with the builder's -xref:2.guide/2e.headers.adoc[`header`] function. A header you set explicitly -is sent as-is. +`Authorization` header yourself with the cpp:request_builder::header[] +function. A header you set explicitly is sent as-is. == Next Steps -* xref:2.guide/2h.redirects.adoc[Redirects] — When credentials are stripped -* xref:2.guide/2e.headers.adoc[Headers and Query Parameters] — Setting a - scheme by hand +* xref:2.guide/2h.redirects.adoc[] — When credentials are stripped +* xref:2.guide/2e.headers.adoc[] — Setting a scheme by hand diff --git a/doc/modules/ROOT/pages/2.guide/2h.redirects.adoc b/doc/modules/ROOT/pages/2.guide/2h.redirects.adoc index cb93bc0..ee67ab5 100644 --- a/doc/modules/ROOT/pages/2.guide/2h.redirects.adoc +++ b/doc/modules/ROOT/pages/2.guide/2h.redirects.adoc @@ -44,7 +44,7 @@ std::cout << r.status_int() << '\n'; // e.g. 301 std::cout << r.headers().value_or(http::field::location, "") << '\n'; ---- -When redirects are followed, cpp:response::url[response::url()] reports the +When redirects are followed, cpp:response::url[] reports the final URL the response came from: [source,cpp] @@ -106,13 +106,12 @@ When a redirect crosses to a different origin, the `Authorization` and dropped, so credentials meant for one host are not handed to another. cpp:client::config[config::unrestricted_auth] keeps sending them; the xref:2.guide/2g.authentication.adoc[authentication] section covers this in full. -Same- origin redirects always carry credentials through. +Same-origin redirects always carry credentials through. == Next Steps -* xref:2.guide/2g.authentication.adoc[Authentication] — Cross-origin credential - stripping -* xref:2.guide/2i.cookies.adoc[Cookies] — How the jar follows redirects -* xref:2.guide/2d.error-handling.adoc[Error Handling] — +* xref:2.guide/2g.authentication.adoc[] — Cross-origin credential stripping +* xref:2.guide/2i.cookies.adoc[] — How the jar follows redirects +* xref:2.guide/2d.error-handling.adoc[] — cpp:error::too_many_redirects[too_many_redirects] and cpp:error::bad_redirect_response[bad_redirect_response] diff --git a/doc/modules/ROOT/pages/2.guide/2i.cookies.adoc b/doc/modules/ROOT/pages/2.guide/2i.cookies.adoc index 926aaf9..e449683 100644 --- a/doc/modules/ROOT/pages/2.guide/2i.cookies.adoc +++ b/doc/modules/ROOT/pages/2.guide/2i.cookies.adoc @@ -11,8 +11,8 @@ When cookie handling is enabled, the client stores cookies from `Set-Cookie` response headers in its cookie jar and sends matching cookies on later -requests, following the storage and matching rules of RFC 6265. The jar can be -inspected directly and persisted between sessions. +requests, following the storage and matching rules of RFC 6265bis. The jar can +be inspected directly and persisted between sessions. == Enabling Cookies @@ -33,7 +33,7 @@ auto r = co_await client.get("https://example.com/account").send(); == The Cookie Jar -cpp:client::cookie_jar[client::cookie_jar()] gives direct access to the jar. The +cpp:client::cookie_jar[] gives direct access to the jar. The cookies can be listed, cleared, or seeded by hand: [source,cpp] @@ -86,7 +86,5 @@ an error if a line is malformed. Empty and comment lines are skipped, except the == Next Steps -* xref:2.guide/2h.redirects.adoc[Redirects] — How cookies and credentials follow - hops -* xref:2.guide/2e.headers.adoc[Headers and Query Parameters] — Setting a - `Cookie` header by hand +* xref:2.guide/2h.redirects.adoc[] — How cookies and credentials follow hops +* xref:2.guide/2e.headers.adoc[] — Setting a `Cookie` header by hand diff --git a/doc/modules/ROOT/pages/2.guide/2j.timeouts.adoc b/doc/modules/ROOT/pages/2.guide/2j.timeouts.adoc index 1bbe645..0bd8e94 100644 --- a/doc/modules/ROOT/pages/2.guide/2j.timeouts.adoc +++ b/doc/modules/ROOT/pages/2.guide/2j.timeouts.adoc @@ -66,9 +66,6 @@ if(ec == capy::error::timeout) == Next Steps -* xref:2.guide/2d.error-handling.adoc[Error Handling] — Timeouts among transport - failures -* xref:2.guide/2k.connection-pool.adoc[Connection Pool] — The idle timeout, a - separate setting -* xref:2.guide/2c.responses.adoc[Responses] — Which body reads the timeout - covers +* xref:2.guide/2d.error-handling.adoc[] — Timeouts among transport failures +* xref:2.guide/2k.connection-pool.adoc[] — The idle timeout, a separate setting +* xref:2.guide/2c.responses.adoc[] — Which body reads the timeout covers diff --git a/doc/modules/ROOT/pages/2.guide/2k.connection-pool.adoc b/doc/modules/ROOT/pages/2.guide/2k.connection-pool.adoc index 5232ea1..d030cd8 100644 --- a/doc/modules/ROOT/pages/2.guide/2k.connection-pool.adoc +++ b/doc/modules/ROOT/pages/2.guide/2k.connection-pool.adoc @@ -61,9 +61,6 @@ consequences: == Next Steps -* xref:2.guide/2c.responses.adoc[Responses] — Reading the body to free the - connection -* xref:2.guide/2j.timeouts.adoc[Timeouts] — Connect and I/O timeouts on new - connections -* xref:2.guide/2l.proxies.adoc[Proxies] — Establishing connections through a - proxy +* xref:2.guide/2c.responses.adoc[] — Reading the body to free the connection +* xref:2.guide/2j.timeouts.adoc[] — Connect and I/O timeouts on new connections +* xref:2.guide/2l.proxies.adoc[] — Establishing connections through a proxy diff --git a/doc/modules/ROOT/pages/2.guide/2l.proxies.adoc b/doc/modules/ROOT/pages/2.guide/2l.proxies.adoc index 2af4f50..7a4db5e 100644 --- a/doc/modules/ROOT/pages/2.guide/2l.proxies.adoc +++ b/doc/modules/ROOT/pages/2.guide/2l.proxies.adoc @@ -37,7 +37,7 @@ The proxy URL's scheme selects the kind of proxy: | Scheme | Proxy | `http` -| An HTTP proxy. `https` targets are tunnelled with `CONNECT`. +| An HTTP proxy. `https` targets are tunneled with `CONNECT`. | `socks5` | A SOCKS5 proxy. The client resolves the target hostname itself. @@ -77,6 +77,5 @@ reaching the target: == Next Steps -* xref:2.guide/2k.connection-pool.adoc[Connection Pool] — Pooling of proxied - connections -* xref:2.guide/2d.error-handling.adoc[Error Handling] — The proxy error codes +* xref:2.guide/2k.connection-pool.adoc[] — Pooling of proxied connections +* xref:2.guide/2d.error-handling.adoc[] — The proxy error codes diff --git a/doc/modules/ROOT/pages/2.guide/2m.compression.adoc b/doc/modules/ROOT/pages/2.guide/2m.compression.adoc index 64677f7..ded0bff 100644 --- a/doc/modules/ROOT/pages/2.guide/2m.compression.adoc +++ b/doc/modules/ROOT/pages/2.guide/2m.compression.adoc @@ -72,7 +72,7 @@ setting, since the client cannot honor what it advertises. The decoding is transparent: the body you read through xref:2.guide/2c.responses.adoc[any of the body functions] is the decoded -content, and cpp:response::content_length[content_length()] and the +content, and cpp:response::content_length[] and the `Content-Length` header reflect the encoded size as sent on the wire. == Opting Out for a Request @@ -90,6 +90,6 @@ auto [ec, r] = co_await client.get("https://example.com/data") == Next Steps -* xref:2.guide/2c.responses.adoc[Responses] — Reading the decoded body -* xref:2.guide/2d.error-handling.adoc[Error Handling] — `body_too_large` among +* xref:2.guide/2c.responses.adoc[] — Reading the decoded body +* xref:2.guide/2d.error-handling.adoc[] — `http::error::body_too_large` among protocol failures diff --git a/doc/modules/ROOT/pages/2.guide/2n.extending.adoc b/doc/modules/ROOT/pages/2.guide/2n.extending.adoc index 043aae8..f3faf00 100644 --- a/doc/modules/ROOT/pages/2.guide/2n.extending.adoc +++ b/doc/modules/ROOT/pages/2.guide/2n.extending.adoc @@ -16,10 +16,10 @@ files are written exactly this way. == Sending a Type as a Request Body -To make a type usable with the builder's -xref:2.guide/2b.request-bodies.adoc[`body`] function, provide a `tag_invoke` -overload taking cpp:body_from_tag[]. It returns an cpp:any_request_body[], a -type-erased wrapper around an object satisfying the cpp:RequestBody[] concept: +To make a type usable with the cpp:request_builder::body[] function, provide a +`tag_invoke` overload taking cpp:body_from_tag[]. It returns an +cpp:any_request_body[], a type-erased wrapper around an object satisfying the +cpp:RequestBody[] concept: [source,cpp] ---- @@ -31,112 +31,51 @@ struct RequestBody }; ---- -Here is a complete body that serializes an `nlohmann::json` document. Because +Here is a complete body that serializes an +https://github.com/nlohmann/json[`nlohmann::json`^] document. Because the serialized text is materialized first, its size is known, so the body reports a `Content-Length`: [source,cpp] ---- -namespace nlohmann -{ -burl::any_request_body -tag_invoke(burl::body_from_tag, const nlohmann::json& value) -{ - class json_body - { - std::string text_; - - public: - explicit json_body(const nlohmann::json& value) - : text_(value.dump()) - { - } - - std::optional - content_type() const - { - return "application/json"; - } - - std::optional - content_length() const noexcept - { - return text_.size(); - } - - capy::io_task<> - write(capy::any_buffer_sink& sink) const - { - auto [ec, n] = co_await sink.write(capy::make_buffer(text_)); - co_return { ec }; - } - }; - return json_body{ value }; -} -} // namespace nlohmann +include::example$nlohmann_json.cpp[tag=body_from,indent=0] ---- +Because `content_length()` returns a size, the request goes out with a +`Content-Length` header; had it returned cpp:std::nullopt[], the body would be +sent with chunked transfer encoding instead. The `application/json` from +`content_type()` is used unless the request already sets that header explicitly. + The overload is found by argument-dependent lookup, so placing it in the type's -own namespace is enough. The type then works with `body` like any built-in: +own namespace is enough. The type then works with cpp:request_builder::body[] +like any built-in: [source,cpp] ---- -nlohmann::json doc({ { "user", "John" } }); - auto r = co_await client.post("https://example.com/post") - .body(doc) + .body({ { "user", "John" } }) .send(); ---- == Reading a Response Body into a Type -To make a type usable with xref:2.guide/2c.responses.adoc[cpp:response::as[]] -and cpp:request_builder::as[], provide a `tag_invoke` overload taking -cpp:body_to_tag[]. It takes the cpp:response[] and returns a -`capy::io_task` that reads and converts the body: +To read a response into a value with cpp:request_builder::as[as] or +cpp:request_builder::try_as[try_as], provide a `tag_invoke` overload +taking cpp:body_to_tag[] and the cpp:response[]. It returns a +`capy::io_task` that reads the body and converts it: [source,cpp] ---- -namespace nlohmann -{ -capy::io_task -tag_invoke(burl::body_to_tag, burl::response& resp) -{ - // Try the parser's in-place buffer first; it is allocation-free - // when the body fits. - auto [ec, sv] = co_await resp.try_as_view(); - - // Fall back to a heap string when the body is larger than the buffer. - std::string st; - if(ec == http::error::in_place_overflow) - { - auto [sec, body] = co_await resp.try_as(); - ec = sec; - st = std::move(body); - sv = st; - } - if(ec) - co_return { ec, {} }; - - // Surface a parse failure as an error rather than a discarded value. - auto doc = nlohmann::json::parse(sv, nullptr, false); - if(doc.is_discarded()) - co_return { make_error_code(std::errc::bad_message), {} }; - co_return { {}, std::move(doc) }; -} -} // namespace nlohmann +include::example$nlohmann_json.cpp[tag=body_to,indent=0] ---- -This conversion shows the recommended pattern: read with -xref:2.guide/2c.responses.adoc[cpp:response::try_as_view[try_as_view]] first, -which is allocation-free when the body fits the parser's in-place buffer, and on -`http::error::in_place_overflow` fall back to reading into a cpp:std::string[]. -Build the result on a value-side `tag_invoke` rather than reaching for raw I/O, -and you inherit the xref:2.guide/2j.timeouts.adoc[timeout] handling and the -buffer reuse the response already provides. +This follows the recommended pattern for reading a body. It first calls +cpp:response::try_as_view[try_as_view], which is allocation-free when the body +fits the parser's in-place buffer, and only on `http::error::in_place_overflow` +falls back to reading into a cpp:std::string[]. -With both overloads in place, the type is a first-class body in both -directions: +With the overload in place, the type is usable with +cpp:request_builder::as[as] and cpp:request_builder::try_as[try_as]: [source,cpp] ---- @@ -148,7 +87,7 @@ auto doc = co_await client.get("https://example.com/data") Both `body` and `as` forward any trailing arguments to the matching `tag_invoke`, positioned after its fixed parameters. An overload can therefore -take configuration that burl knows nothing about. +take configuration that Burl knows nothing about. The built-in file conversion uses this for its destination path. Its overload declares the extra parameter after the cpp:response[]: @@ -172,9 +111,7 @@ co_await client.get("https://example.com/file") == Next Steps -* xref:2.guide/2b.request-bodies.adoc[Request Bodies] — The built-in request - body types -* xref:2.guide/2c.responses.adoc[Responses] — The built-in response conversions - and cpp:response::try_as_view[try_as_view] -* xref:examples/nlohmann_json.adoc[nlohmann/json] — The full `nlohmann::json` - example +* xref:2.guide/2b.request-bodies.adoc[] — The built-in request body types +* xref:2.guide/2c.responses.adoc[] — The built-in response conversions and + cpp:response::try_as_view[try_as_view] +* xref:examples/nlohmann_json.adoc[] — The full `nlohmann::json` example diff --git a/doc/modules/ROOT/pages/examples/nlohmann_json.adoc b/doc/modules/ROOT/pages/examples/nlohmann_json.adoc index 5118cf6..933f6ee 100644 --- a/doc/modules/ROOT/pages/examples/nlohmann_json.adoc +++ b/doc/modules/ROOT/pages/examples/nlohmann_json.adoc @@ -10,54 +10,36 @@ = nlohmann/json A complete, runnable example of teaching Burl a body type it has never heard of, -extracted from `example/nlohmann_json.cpp`. -Burl ships conversions for strings, Boost.JSON, forms, and files, but the same -mechanism is open: a pair of `tag_invoke` overloads, found by argument-dependent -lookup, makes any type a first-class body in both directions. Here that type is -https://github.com/nlohmann/json[`nlohmann::json`]. - -See xref:2.guide/2n.extending.adoc[Extending] for the concepts this example puts -to work. +extracted from `example/nlohmann_json.cpp`. It puts +xref:2.guide/2n.extending.adoc[Extending] to work on +https://github.com/nlohmann/json[`nlohmann::json`^]: a pair of `tag_invoke` +overloads, found by argument-dependent lookup, that make the type a first-class +body in both directions. == Sending: body_from_tag -To send a value with cpp:request_builder::body[body], provide a `tag_invoke` -overload taking cpp:body_from_tag[]. It returns an cpp:any_request_body[], a -type-erased wrapper around any object satisfying the cpp:RequestBody[] concept. - -Here the document is serialized up front with `dump()`, so the body owns the -text and can report a known `Content-Length`. `write` then hands that buffer to -the sink in a single call: +The cpp:body_from_tag[] overload serializes the document up front with +`dump()`, so the body owns the text and reports a known `Content-Length`. [source,cpp] ---- include::example$nlohmann_json.cpp[tag=body_from,indent=0] ---- -Because `content_length()` returns a size, the request goes out with a -`Content-Length` header; had it returned `std::nullopt`, the body would be sent -with chunked transfer encoding instead. The `application/json` from -`content_type()` is used unless the request already sets that header explicitly. +See xref:2.guide/2n.extending.adoc[]. == Receiving: body_to_tag -To read a response into a value with cpp:request_builder::as[as] or -cpp:request_builder::try_as[try_as], provide a `tag_invoke` overload -taking cpp:body_to_tag[] and the cpp:response[]. It returns a -`capy::io_task` that reads the body and converts it: +The cpp:body_to_tag[] overload reads the body and parses it, following the +recommended cpp:response::try_as_view[try_as_view] pattern: in place when the +body fits the parser's buffer, falling back to a cpp:std::string[] on overflow. [source,cpp] ---- include::example$nlohmann_json.cpp[tag=body_to,indent=0] ---- -This follows the recommended pattern for reading a body. It first calls -cpp:response::try_as_view[try_as_view], which is allocation-free when the body -fits the parser's in-place buffer, and only on `http::error::in_place_overflow` -falls back to reading into a cpp:std::string[]. Building on the response's own -readers rather than raw I/O inherits its xref:2.guide/2j.timeouts.adoc[timeout] -handling and buffer reuse. A failed parse is surfaced as an `error_code` rather -than a silently discarded value. +See xref:2.guide/2n.extending.adoc[]. == Using It @@ -80,9 +62,9 @@ include::example$nlohmann_json.cpp[tag=round_trip_inline,indent=0] == Next Steps -* xref:2.guide/2n.extending.adoc[Extending] — The two `tag_invoke` overloads in - depth, including forwarding extra arguments -* xref:2.guide/2b.request-bodies.adoc[Request Bodies] — The built-in body types - and how framing is chosen -* xref:2.guide/2c.responses.adoc[Responses] — The built-in conversions and +* xref:2.guide/2n.extending.adoc[] — The two `tag_invoke` overloads in depth, + including forwarding extra arguments +* xref:2.guide/2b.request-bodies.adoc[] — The built-in body types and how + framing is chosen +* xref:2.guide/2c.responses.adoc[] — The built-in conversions and cpp:response::try_as_view[try_as_view] diff --git a/doc/modules/ROOT/pages/examples/usage.adoc b/doc/modules/ROOT/pages/examples/usage.adoc index 347a29b..bd3eabe 100644 --- a/doc/modules/ROOT/pages/examples/usage.adoc +++ b/doc/modules/ROOT/pages/examples/usage.adoc @@ -23,8 +23,8 @@ The type passed to cpp:request_builder::as[as] selects how the body is parsed include::example$usage.cpp[tag=basic_get,indent=0] ---- -See xref:2.guide/2a.making-requests.adoc[Making Requests] and -xref:2.guide/2c.responses.adoc[Responses]. +See xref:2.guide/2a.making-requests.adoc[] and +xref:2.guide/2c.responses.adoc[]. == Inspect Response Status and Headers @@ -36,7 +36,7 @@ body still unread. include::example$usage.cpp[tag=inspect_response,indent=0] ---- -See xref:2.guide/2c.responses.adoc[Responses]. +See xref:2.guide/2c.responses.adoc[]. == Handle Error Status Codes @@ -48,19 +48,19 @@ on 4xx/5xx. include::example$usage.cpp[tag=handle_error_status,indent=0] ---- -See xref:2.guide/2d.error-handling.adoc[Error Handling]. +See xref:2.guide/2d.error-handling.adoc[]. == Add Query Parameters -cpp:request_builder::query[query] appends percent-encoded key/value pairs to the -URL. +cpp:request_builder::query[query] appends percent-encoded name/value pairs to +the URL. [source,cpp] ---- include::example$usage.cpp[tag=add_query_params,indent=0] ---- -See xref:2.guide/2f.query-parameters.adoc[Query Parameters]. +See xref:2.guide/2f.query-parameters.adoc[]. == Set Request Headers @@ -71,7 +71,7 @@ Client headers apply to every request; request headers apply to one. include::example$usage.cpp[tag=set_headers,indent=0] ---- -See xref:2.guide/2e.headers.adoc[Headers]. +See xref:2.guide/2e.headers.adoc[]. == Authentication @@ -83,7 +83,7 @@ cpp:client::basic_auth[basic_auth]/cpp:client::bearer_auth[bearer_auth] set include::example$usage.cpp[tag=authenticate,indent=0] ---- -See xref:2.guide/2g.authentication.adoc[Authentication]. +See xref:2.guide/2g.authentication.adoc[]. == POST a String Body @@ -94,7 +94,7 @@ A string body defaults to `text/plain`; override with a `content_type` header. include::example$usage.cpp[tag=post_string,indent=0] ---- -See xref:2.guide/2b.request-bodies.adoc[Request Bodies]. +See xref:2.guide/2b.request-bodies.adoc[]. == POST a JSON Body @@ -105,7 +105,7 @@ A `json::value` is serialized and sent as `application/json`. include::example$usage.cpp[tag=post_json,indent=0] ---- -See xref:2.guide/2b.request-bodies.adoc[Request Bodies]. +See xref:2.guide/2b.request-bodies.adoc[]. == POST a URL-Encoded Form @@ -116,7 +116,7 @@ cpp:urlencoded_form[] builds an `application/x-www-form-urlencoded` body. include::example$usage.cpp[tag=post_urlencoded_form,indent=0] ---- -See xref:2.guide/2b.request-bodies.adoc[Request Bodies]. +See xref:2.guide/2b.request-bodies.adoc[]. == POST a Multipart Form @@ -128,7 +128,7 @@ cpp:multipart_form[] mixes file uploads and text fields as include::example$usage.cpp[tag=post_multipart_form,indent=0] ---- -See xref:2.guide/2b.request-bodies.adoc[Request Bodies]. +See xref:2.guide/2b.request-bodies.adoc[]. == Upload and Download a File @@ -140,8 +140,8 @@ cpp:request_builder::as[as] streams to one. include::example$usage.cpp[tag=upload_and_download_file,indent=0] ---- -See xref:2.guide/2b.request-bodies.adoc[Request Bodies] and -xref:2.guide/2c.responses.adoc[Responses]. +See xref:2.guide/2b.request-bodies.adoc[] and +xref:2.guide/2c.responses.adoc[]. == Stream a Response Body @@ -152,9 +152,9 @@ xref:2.guide/2c.responses.adoc[Responses]. include::example$usage.cpp[tag=stream_response,indent=0] ---- -See xref:2.guide/2c.responses.adoc[Responses]. +See xref:2.guide/2c.responses.adoc[]. -== Read a Response Body In Place +== Read a Response Body in Place When the body fits in cpp:client::config[response_inplace_buffer], cpp:response::as_view[as_view] reads it straight from the connection's internal @@ -165,7 +165,7 @@ buffer with no extra allocation. include::example$usage.cpp[tag=inplace_response_body,indent=0] ---- -See xref:2.guide/2c.responses.adoc[Responses]. +See xref:2.guide/2c.responses.adoc[]. == Set Timeouts @@ -176,7 +176,7 @@ Separate limits for connect, each I/O step, and the whole operation. include::example$usage.cpp[tag=set_timeouts,indent=0] ---- -See xref:2.guide/2j.timeouts.adoc[Timeouts]. +See xref:2.guide/2j.timeouts.adoc[]. == Follow Redirects @@ -188,7 +188,7 @@ the final URL. include::example$usage.cpp[tag=follow_redirects,indent=0] ---- -See xref:2.guide/2h.redirects.adoc[Redirects]. +See xref:2.guide/2h.redirects.adoc[]. == Enable Cookies @@ -200,9 +200,9 @@ per host. include::example$usage.cpp[tag=enable_cookies,indent=0] ---- -See xref:2.guide/2i.cookies.adoc[Cookies]. +See xref:2.guide/2i.cookies.adoc[]. -== Reuse Connections With the Pool +== Reuse Connections with the Pool Idle connections are kept alive so later requests skip the handshakes. @@ -211,7 +211,7 @@ Idle connections are kept alive so later requests skip the handshakes. include::example$usage.cpp[tag=connection_pool,indent=0] ---- -See xref:2.guide/2k.connection-pool.adoc[Connection Pool]. +See xref:2.guide/2k.connection-pool.adoc[]. == Use a Proxy @@ -222,7 +222,7 @@ Set cpp:client::config[proxy] to route requests through an HTTP or SOCKS5 proxy. include::example$usage.cpp[tag=use_proxy,indent=0] ---- -See xref:2.guide/2l.proxies.adoc[Proxies]. +See xref:2.guide/2l.proxies.adoc[]. == Build a Request and Execute It Later @@ -234,11 +234,10 @@ run later with cpp:client::execute[execute]. include::example$usage.cpp[tag=build_and_execute,indent=0] ---- -See xref:2.guide/2a.making-requests.adoc[Making Requests]. +See xref:2.guide/2a.making-requests.adoc[]. == Next Steps -* xref:quick-start.adoc[Quick Start] — The skeleton, built up one step at a time -* xref:2.guide/2a.making-requests.adoc[Making Requests] — Where the guide tour - begins -* xref:testing.adoc[Testing] — Exercising request code without a network +* xref:quick-start.adoc[] — The skeleton, built up one step at a time +* xref:2.guide/2a.making-requests.adoc[] — Where the guide tour begins +* xref:testing.adoc[] — Exercising request code without a network diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index 1fe08cb..cc25d88 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -25,12 +25,6 @@ be read all surface as a thrown cpp:std::system_error[]. The xref:2.guide/2d.error-handling.adoc[error handling] guide covers the non-throwing alternatives. -Burl sits at the top of the stack: it composes the sans-I/O HTTP/1.1 parser -and serializer from Boost.Http with the coroutine streams of Boost.Capy and -the networking of Boost.Corosio, and adds the conveniences an application -actually reaches for — body conversions, connection reuse, redirects, content -decoding, cookies, authentication, proxies, and timeouts. - == What This Library Does * *One-expression requests* — `client.get(url).as()` performs the request @@ -44,7 +38,7 @@ decoding, cookies, authentication, proxies, and timeouts. changes, `Referer` handling, and credential stripping on cross-origin hops * *Content decoding* — transparent `gzip`, `deflate`, and `br` when the corresponding decode service is installed -* *Cookies* — an RFC 6265 jar with optional public-suffix validation, and +* *Cookies* — an RFC 6265bis jar with optional public-suffix validation, and persistence in the Netscape cookie file format * *Authentication* — Basic and Bearer, set per client or per request * *Proxies* — `http`, `socks5`, and `socks5h`, with credentials @@ -78,18 +72,6 @@ Burl is a focused HTTP/1.1 client. It does not provide: compromises a hybrid model imposes and keeps the request path linear and readable. -== Target Audience - -Burl is for C++ developers writing applications that consume HTTP APIs and -fetch resources. This documentation assumes: - -* *C++20 coroutines* — `co_await`, `co_return`, and awaitables -* *Familiarity with Boost.Capy* — `task`, executors, and buffer sequences -* *Basic HTTP* — methods, status codes, and headers - -If coroutines or the underlying stack are unfamiliar, the Boost.Capy and -Boost.Corosio documentation cover them from the ground up. - == Requirements === Assumed Knowledge @@ -111,7 +93,7 @@ Boost.URL, Boost.JSON, and Boost.System. Content decoding requires Boost.Http built with zlib (`gzip`, `deflate`) and brotli (`br`). Public-suffix validation of cookies requires libpsl. -== Code Convention +== Code Conventions [NOTE] ==== @@ -146,12 +128,10 @@ The xref:quick-start.adoc[Quick Start] shows the surrounding program in full. == Next Steps -* xref:quick-start.adoc[Quick Start] — Build and run your first request -* xref:2.guide/2a.making-requests.adoc[Making Requests] — The client, verbs, - and the request builder -* xref:2.guide/2c.responses.adoc[Responses] — Status, headers, and reading the - body -* xref:2.guide/2d.error-handling.adoc[Error Handling] — The value-and-exception - error model -* xref:examples/usage.adoc[Examples] — A runnable tour of the API +* xref:quick-start.adoc[] — Build and run your first request +* xref:2.guide/2a.making-requests.adoc[] — The client, verbs, and the request + builder +* xref:2.guide/2c.responses.adoc[] — Status, headers, and reading the body +* xref:2.guide/2d.error-handling.adoc[] — The value-and-exception error model +* xref:examples/usage.adoc[] — A runnable tour of the API * xref:reference:boost/burl.adoc[Reference] — The generated API reference diff --git a/doc/modules/ROOT/pages/quick-start.adoc b/doc/modules/ROOT/pages/quick-start.adoc index 04b7bf6..2815502 100644 --- a/doc/modules/ROOT/pages/quick-start.adoc +++ b/doc/modules/ROOT/pages/quick-start.adoc @@ -53,9 +53,10 @@ The examples that follow assume this scaffolding and show only the body of == Parse the Body as JSON -Asking for a different type changes how the body comes back. With -Boost.JSON included, request a `json::value` and the body is parsed as it -arrives: +The program above asked for a `std::string` and got the raw body bytes. The +type argument selects the conversion: ask cpp:request_builder::as[as] for a +`json::value` and the same call parses the response as JSON, incrementally as +it arrives, and hands back a value you can navigate directly: [source,cpp] ---- @@ -70,8 +71,8 @@ types, and xref:2.guide/2n.extending.adoc[extending] shows how to add your own. == Query Parameters and Headers The builder shapes the request before it goes out. Chain -cpp:request_builder::query[query()] to add query-string parameters and -cpp:request_builder::header[header()] to set headers; each takes either an +cpp:request_builder::query[query] to add query-string parameters and +cpp:request_builder::header[header] to set headers; each takes either an `http::field` enumerator or a string name. Headers set on the client itself are sent with every request: @@ -164,9 +165,9 @@ built-in body type and how the `Content-Type` and framing are chosen. == Inspect the Status and Headers -cpp:request_builder::as[as()] is the shortcut for the common case. When you +cpp:request_builder::as[as] is the shortcut for the common case. When you need the status line and headers before deciding what to do with the body, call -cpp:request_builder::send[send()] instead. It yields `(error_code, response)` +cpp:request_builder::send[send] instead. It yields `(error_code, response)` with the body still unread: [source,cpp] @@ -182,9 +183,9 @@ std::cout << "body: " << co_await r.as() << '\n'; == Handle Errors Without Throwing -cpp:request_builder::as[as()] throws on any failure, including a 4xx or 5xx -status. To inspect an error instead, use the `try_` forms, which yield -`(error_code, T)`: +cpp:request_builder::as[as] throws on any failure, including a 4xx or 5xx +status. To inspect an error instead, use +cpp:request_builder::try_as[try_as], which yields `(error_code, T)`: [source,cpp] ---- @@ -200,9 +201,8 @@ full model, including the order in which different failures take precedence. == Next Steps -* xref:2.guide/2a.making-requests.adoc[Making Requests] — Verbs, the builder, - and deferred execution -* xref:2.guide/2b.request-bodies.adoc[Request Bodies] — Strings, JSON, forms, - and files -* xref:2.guide/2d.error-handling.adoc[Error Handling] — Errors as values -* xref:examples/usage.adoc[Examples] — A runnable tour of every feature +* xref:2.guide/2a.making-requests.adoc[] — Verbs, the builder, and deferred + execution +* xref:2.guide/2b.request-bodies.adoc[] — Strings, JSON, forms, and files +* xref:2.guide/2d.error-handling.adoc[] — Errors as values +* xref:examples/usage.adoc[] — A runnable tour of every feature diff --git a/doc/modules/ROOT/pages/testing.adoc b/doc/modules/ROOT/pages/testing.adoc index c4d9749..b548574 100644 --- a/doc/modules/ROOT/pages/testing.adoc +++ b/doc/modules/ROOT/pages/testing.adoc @@ -97,9 +97,8 @@ socket in production code. == Next Steps -* xref:2.guide/2c.responses.adoc[Responses] — The body functions that a - synthesized response feeds -* xref:2.guide/2d.error-handling.adoc[Error Handling] — The failures a fuse - helps you cover +* xref:2.guide/2c.responses.adoc[] — The body functions that a synthesized + response feeds +* xref:2.guide/2d.error-handling.adoc[] — The failures a fuse helps you cover * xref:reference:boost/burl.adoc[Reference] — cpp:test::response_factory[] in detail diff --git a/include/boost/burl/cookie.hpp b/include/boost/burl/cookie.hpp index 1344fce..ffb7fb8 100644 --- a/include/boost/burl/cookie.hpp +++ b/include/boost/burl/cookie.hpp @@ -26,7 +26,7 @@ namespace burl /** An HTTP cookie. Objects of this type represent a cookie as - defined in RFC 6265, with members corresponding + defined in RFC 6265bis, with members corresponding to the attributes of a `Set-Cookie` header. They are produced by @ref parse_cookie and stored in a @ref cookie_jar. diff --git a/include/boost/burl/cookie_jar.hpp b/include/boost/burl/cookie_jar.hpp index 134b5f2..5943f28 100644 --- a/include/boost/burl/cookie_jar.hpp +++ b/include/boost/burl/cookie_jar.hpp @@ -28,7 +28,7 @@ namespace burl This container stores cookies received in responses and produces the `Cookie` header value for requests, applying the storage and - matching rules of RFC 6265. A @ref client with + matching rules of RFC 6265bis. A @ref client with @ref client::config::cookies enabled maintains its cookie jar automatically. @@ -81,7 +81,7 @@ class cookie_jar /** Add a cookie received from a URL. This function performs the storage checks of - RFC 6265 against the URL the cookie was + RFC 6265bis against the URL the cookie was received from: @li When the cookie carries a `Domain` @@ -125,7 +125,7 @@ class cookie_jar by domain, path, and the `Secure` attribute, and returned as `name=value` pairs separated by `"; "`, ordered with longer paths first - (RFC 6265 5.4). Expired cookies encountered + (RFC 6265bis 5.4). Expired cookies encountered during matching are removed from the jar. @param url The URL of the request. diff --git a/include/boost/burl/request_builder.hpp b/include/boost/burl/request_builder.hpp index a22401b..2c63f52 100644 --- a/include/boost/burl/request_builder.hpp +++ b/include/boost/burl/request_builder.hpp @@ -84,7 +84,7 @@ class request_builder /** Append a parameter to the URL query. - The key and value are percent-encoded. + The name and value are percent-encoded. @par Example @code @@ -94,7 +94,7 @@ class request_builder .send(); @endcode - @param key The key of the parameter. + @param name The name of the parameter. @param value The value of the parameter. @@ -102,7 +102,7 @@ class request_builder */ BOOST_BURL_DECL request_builder&& - query(std::string_view key, std::string_view value) &&; + query(std::string_view name, std::string_view value) &&; /** Set a request header. diff --git a/src/request_builder.cpp b/src/request_builder.cpp index c1ab2e5..1ffcdc9 100644 --- a/src/request_builder.cpp +++ b/src/request_builder.cpp @@ -20,9 +20,9 @@ namespace burl { request_builder&& -request_builder::query(std::string_view key, std::string_view value) && +request_builder::query(std::string_view name, std::string_view value) && { - request_.url.params().append({ key, value }); + request_.url.params().append({ name, value }); return std::move(*this); }