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