include/boost/burl/client.hpp

100.0% Lines (4/4) 100.0% List of functions (1/2) -% Branches (0/0)
client.hpp
f(x) Functions (2)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Mohammad Nejati
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/burl
8 //
9
10 #ifndef BOOST_BURL_CLIENT_HPP
11 #define BOOST_BURL_CLIENT_HPP
12
13 #include <boost/burl/cookie_jar.hpp>
14 #include <boost/burl/detail/config.hpp>
15 #include <boost/burl/detail/connection_pool.hpp>
16 #include <boost/burl/request.hpp>
17 #include <boost/burl/response.hpp>
18
19 #include <boost/capy/ex/executor_ref.hpp>
20 #include <boost/capy/io/any_stream.hpp>
21 #include <boost/capy/io_task.hpp>
22 #include <boost/corosio/endpoint.hpp>
23 #include <boost/corosio/tls_context.hpp>
24 #include <boost/http/field.hpp>
25 #include <boost/http/fields.hpp>
26 #include <boost/url/url.hpp>
27 #include <boost/url/url_view.hpp>
28
29 #include <chrono>
30 #include <cstddef>
31 #include <cstdint>
32 #include <functional>
33 #include <limits>
34 #include <memory>
35 #include <optional>
36 #include <string_view>
37
38 namespace boost
39 {
40 namespace burl
41 {
42
43 class request_builder;
44
45 /** An HTTP client.
46
47 This is the main interface for performing HTTP
48 requests. A client owns the configuration, a
49 connection pool, a set of default headers, and a
50 cookie jar, which are shared by all requests
51 performed through it. Connections to the same
52 origin are reused across requests when possible.
53
54 @par Example
55 @code
56 burl::client c(co_await capy::this_coro::executor, tls_ctx);
57
58 auto r = co_await c.get("https://example.com")
59 .as<std::string>();
60 @endcode
61
62 @see
63 @ref request_builder,
64 @ref response.
65 */
66 class client
67 {
68 public:
69 /** Configuration settings for a client.
70 */
71 struct config
72 {
73 using clock = std::chrono::steady_clock;
74
75 /** Enable automatic cookie handling.
76
77 When enabled, cookies received in
78 `Set-Cookie` headers are stored in the
79 cookie jar, and matching cookies are
80 sent in the `Cookie` header of
81 subsequent requests.
82
83 @see @ref client::cookie_jar.
84 */
85 bool cookies = false;
86
87 /** The HTTP version used for requests.
88 */
89 http::version version = http::version::http_1_1;
90
91 /** Follow redirect responses automatically.
92
93 When enabled, responses with status
94 codes 301, 302, 303, 307, and 308 are
95 followed transparently, up to
96 @ref maxredirs times.
97 */
98 bool followlocation = true;
99
100 /** Maximum number of redirects to follow.
101
102 Exceeding the limit fails the request
103 with @ref error::too_many_redirects.
104 */
105 std::uint32_t maxredirs = 10;
106
107 /** Keep the request method on 301 responses.
108 */
109 bool post301 = false;
110
111 /** Keep the request method on 302 responses.
112 */
113 bool post302 = false;
114
115 /** Keep the request method on 303 responses.
116 */
117 bool post303 = false;
118
119 /** Send credentials on cross-origin redirects.
120
121 By default, the `Authorization` and
122 `Proxy-Authorization` headers, along
123 with any `Cookie` header set explicitly
124 on the request, are dropped when a
125 redirect leads to a different origin
126 than the original request. Enable to
127 keep sending them.
128 */
129 bool unrestricted_auth = false;
130
131 /** Set the `Referer` header when following redirects.
132
133 The header is set to the URL being left,
134 with any userinfo component removed.
135 */
136 bool autoreferer = true;
137
138 /** Advertise and decode the Brotli content coding.
139
140 When enabled, `br` is included in the
141 `Accept-Encoding` header and response
142 bodies are decoded transparently.
143 Effective only when the library was built
144 with Brotli support
145 (`BOOST_BURL_HAS_BROTLI`). Not applied
146 when the request carries an explicit
147 `Accept-Encoding` header.
148 */
149 bool brotli = true;
150
151 /** Advertise and decode the deflate content coding.
152
153 When enabled, `deflate` is included in
154 the `Accept-Encoding` header and
155 response bodies are decoded
156 transparently. Effective only when the
157 library was built with zlib support
158 (`BOOST_BURL_HAS_ZLIB`). Not applied when
159 the request carries an explicit
160 `Accept-Encoding` header.
161 */
162 bool deflate = true;
163
164 /** Advertise and decode the gzip content coding.
165
166 When enabled, `gzip` is included in the
167 `Accept-Encoding` header and response
168 bodies are decoded transparently.
169 Effective only when the library was built
170 with zlib support
171 (`BOOST_BURL_HAS_ZLIB`). Not applied when
172 the request carries an explicit
173 `Accept-Encoding` header.
174 */
175 bool gzip = true;
176
177 /** Advertise and decode the zstd content coding.
178
179 When enabled, `zstd` is included in the
180 `Accept-Encoding` header and response
181 bodies are decoded transparently.
182 Effective only when the library was built
183 with zstd support
184 (`BOOST_BURL_HAS_ZSTD`). Not applied when
185 the request carries an explicit
186 `Accept-Encoding` header.
187 */
188 bool zstd = true;
189
190 /** Maximum allowed size of a response body.
191
192 Reading a body which exceeds the limit
193 after decoding fails with
194 `http::error::body_too_large`. The
195 default is unlimited.
196 */
197 std::uint64_t response_body_limit =
198 (std::numeric_limits<std::uint64_t>::max)();
199
200 /** Size of the in-place response buffer.
201
202 Bodies up to this size fit in the
203 internal buffer of the parser and can be
204 read without additional allocations
205 using @ref response::try_as_view and
206 @ref response::as_view. Reading a
207 larger body in place fails with
208 `http::error::in_place_overflow`.
209 */
210 std::size_t response_inplace_buffer = 1024 * 1024;
211
212 /** Timeout for the entire operation.
213
214 When set, each request must complete
215 within this duration, from connection
216 establishment through receipt of the
217 response headers. The remaining time
218 then bounds a whole-body read with
219 @ref response::as or
220 @ref response::as_view (and their
221 `try_` forms), but not the streaming
222 sources @ref response::as_buffer_source
223 and @ref response::as_read_source. Can
224 be overridden per request with
225 @ref request_builder::timeout.
226 */
227 std::optional<clock::duration> timeout;
228
229 /** Timeout for establishing a connection.
230
231 Covers name resolution, the TCP
232 connection, proxy negotiation, and the
233 TLS handshake.
234 */
235 13x clock::duration connect_timeout = std::chrono::seconds(60);
236
237 /** Timeout for individual I/O operations.
238
239 When set, applies to every read and write
240 performed on a connection, bounding the
241 time the peer may remain unresponsive
242 regardless of the message size.
243 */
244 std::optional<clock::duration> io_timeout = std::nullopt;
245
246 /** Time an idle pooled connection remains usable.
247
248 Pooled connections which have been idle for
249 longer than this duration are discarded
250 instead of being reused.
251 */
252 13x clock::duration pool_idle_timeout = std::chrono::seconds(90);
253
254 /** Maximum number of idle pooled connections per origin.
255
256 When the limit is reached, additional
257 connections are closed instead of being
258 returned to the pool.
259 */
260 std::size_t pool_max_idle_per_host = 10;
261
262 /** Set the `TCP_NODELAY` option on sockets.
263
264 Disables Nagle's algorithm on newly
265 established connections.
266 */
267 bool tcp_nodelay = true;
268
269 /** The local endpoint to bind sockets to.
270 */
271 corosio::endpoint local_address;
272
273 /** The proxy used for establishing connections.
274
275 Supported proxy schemes are `http`,
276 `socks5`, and `socks5h`. Credentials in the
277 userinfo component of the URL are used for
278 proxy authentication.
279
280 @par Example
281 @code
282 cfg.proxy = urls::url("socks5h://user:pass@localhost:8080");
283 @endcode
284 */
285 std::optional<urls::url> proxy;
286
287 /** Override connection establishment.
288
289 When set, this function is invoked
290 instead of the built-in name resolution,
291 TCP connection, proxy negotiation, and
292 TLS handshake whenever the pool needs a
293 new connection.
294
295 Intended for testing and for advanced uses
296 such as connecting over a pre-established
297 tunnel or a Unix domain socket.
298
299 @par Example
300 @code
301 cfg.connect_handler =
302 [](urls::url_view) -> capy::io_task<capy::any_stream>
303 {
304 auto [a, b] = capy::test::make_stream_pair();
305 // drive b from the test; hand a to the client
306 co_return { {}, capy::any_stream(std::move(a)) };
307 };
308 @endcode
309 */
310 std::function<
311 capy::io_task<capy::any_stream>(urls::url_view url)>
312 connect_handler;
313 };
314
315 private:
316 config config_;
317 std::shared_ptr<detail::connection_pool> pool_;
318 http::fields headers_;
319 burl::cookie_jar cookie_jar_;
320
321 public:
322 /** Constructor.
323
324 Constructs a client with a default
325 configuration.
326
327 @param exec The executor used to perform
328 asynchronous operations.
329
330 @param tls_ctx The TLS context used for
331 `https` connections.
332 */
333 BOOST_BURL_DECL
334 client(capy::executor_ref exec, corosio::tls_context tls_ctx);
335
336 /** Constructor.
337
338 Constructs a client with the provided
339 configuration. Content codings whose decode
340 service is not installed in the system
341 context are disabled, regardless of the
342 configuration.
343
344 @param exec The executor used to perform
345 asynchronous operations.
346
347 @param tls_ctx The TLS context used for
348 `https` connections.
349
350 @param cfg The configuration settings.
351 */
352 BOOST_BURL_DECL
353 client(capy::executor_ref exec, corosio::tls_context tls_ctx, config cfg);
354
355 /** Copy constructor (deleted).
356 */
357 client(client const&) = delete;
358
359 /** Copy assignment (deleted).
360 */
361 client&
362 operator=(client const&) = delete;
363
364 /** Move constructor.
365
366 @param other The client to move from.
367 */
368 client(client&& other) = default;
369
370 /** Move assignment.
371
372 @param other The client to move from.
373
374 @return A reference to this client.
375 */
376 client&
377 operator=(client&& other) = default;
378
379 /** Return the default headers.
380
381 These headers are sent with every request.
382 Headers set on an individual request take
383 precedence over default headers with the
384 same name.
385
386 @par Example
387 @code
388 c.headers().set(http::field::user_agent, "BoostBurl/1.0");
389 @endcode
390 */
391 http::fields&
392 2x headers() noexcept
393 {
394 2x return headers_;
395 }
396
397 /** Return the default headers.
398
399 These headers are sent with every request.
400 Headers set on an individual request take
401 precedence over default headers with the
402 same name.
403 */
404 const http::fields&
405 headers() const noexcept
406 {
407 return headers_;
408 }
409
410 /** Return the cookie jar.
411
412 The jar stores cookies received in responses
413 and supplies them for subsequent requests
414 when @ref config::cookies is enabled.
415 */
416 burl::cookie_jar&
417 cookie_jar() noexcept
418 {
419 return cookie_jar_;
420 }
421
422 /** Return the cookie jar.
423
424 The jar stores cookies received in responses
425 and supplies them for subsequent requests
426 when @ref config::cookies is enabled.
427 */
428 const burl::cookie_jar&
429 cookie_jar() const noexcept
430 {
431 return cookie_jar_;
432 }
433
434 /** Set default credentials for HTTP Basic authentication.
435
436 Sets the default `Authorization` header,
437 sent with every request, to the Basic scheme
438 with the provided credentials. Can be
439 overridden per request with
440 @ref request_builder::basic_auth.
441
442 Credentials are not sent when a redirect
443 leads to a different origin, unless
444 @ref config::unrestricted_auth is enabled.
445
446 @param user The username.
447
448 @param pass The password.
449 */
450 BOOST_BURL_DECL
451 void
452 basic_auth(std::string_view user, std::string_view pass);
453
454 /** Set a default token for HTTP Bearer authentication.
455
456 Sets the default `Authorization` header,
457 sent with every request, to the Bearer
458 scheme with the provided token. Can be
459 overridden per request with
460 @ref request_builder::bearer_auth.
461
462 The token is not sent when a redirect leads
463 to a different origin, unless
464 @ref config::unrestricted_auth is enabled.
465
466 @param token The bearer token.
467 */
468 BOOST_BURL_DECL
469 void
470 bearer_auth(std::string_view token);
471
472 /** Create a builder for a `GET` request.
473
474 @par Example
475 @code
476 auto r = co_await c.get("https://example.com")
477 .as<std::string>();
478 @endcode
479
480 @param url The URL of the request.
481
482 @return A builder for configuring and
483 sending the request.
484 */
485 BOOST_BURL_DECL
486 request_builder
487 get(urls::url_view url);
488
489 /** Create a builder for a `HEAD` request.
490
491 @param url The URL of the request.
492
493 @return A builder for configuring and
494 sending the request.
495 */
496 BOOST_BURL_DECL
497 request_builder
498 head(urls::url_view url);
499
500 /** Create a builder for a `POST` request.
501
502 @param url The URL of the request.
503
504 @return A builder for configuring and
505 sending the request.
506 */
507 BOOST_BURL_DECL
508 request_builder
509 post(urls::url_view url);
510
511 /** Create a builder for a `PUT` request.
512
513 @param url The URL of the request.
514
515 @return A builder for configuring and
516 sending the request.
517 */
518 BOOST_BURL_DECL
519 request_builder
520 put(urls::url_view url);
521
522 /** Create a builder for a `PATCH` request.
523
524 @param url The URL of the request.
525
526 @return A builder for configuring and
527 sending the request.
528 */
529 BOOST_BURL_DECL
530 request_builder
531 patch(urls::url_view url);
532
533 /** Create a builder for a `DELETE` request.
534
535 The trailing underscore in the function name
536 avoids the `delete` keyword.
537
538 @param url The URL of the request.
539
540 @return A builder for configuring and
541 sending the request.
542 */
543 BOOST_BURL_DECL
544 request_builder
545 delete_(urls::url_view url);
546
547 /** Create a builder for a request.
548
549 The verb functions are equivalent to calling
550 this function with the corresponding method.
551
552 @param method The method of the request.
553
554 @param url The URL of the request.
555
556 @return A builder for configuring and
557 sending the request.
558 */
559 BOOST_BURL_DECL
560 request_builder
561 request(http::method method, urls::url_view url);
562
563 /** Asynchronously execute a request.
564
565 Sends the request and reads the response
566 status line and headers; the body is left
567 unread and can be consumed through the
568 returned @ref response.
569
570 @par Example
571 @code
572 burl::request req = c.get("https://example.com").build();
573
574 auto [ec, r] = co_await c.execute(std::move(req));
575 @endcode
576
577 @param request The request to execute.
578
579 @return An awaitable yielding
580 `(error_code,response)`.
581
582 @see @ref request_builder::send.
583 */
584 BOOST_BURL_DECL
585 capy::io_task<response>
586 execute(burl::request request);
587
588 private:
589 BOOST_BURL_DECL
590 capy::io_task<response>
591 execute_impl(
592 burl::request request,
593 std::optional<config::clock::time_point> deadline);
594 };
595
596 } // namespace burl
597 } // namespace boost
598
599 #include <boost/burl/request_builder.hpp>
600
601 #endif
602