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 Brotli decode
144 service is installed in the system
145 context. Not applied when the request
146 carries an explicit `Accept-Encoding`
147 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 zlib inflate service is installed in the
158 system context. Not applied when the
159 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 zlib inflate
170 service is installed in the system
171 context. Not applied when the request
172 carries an explicit `Accept-Encoding`
173 header.
174 */
175 bool gzip = true;
176
177 /** Maximum allowed size of a response body.
178
179 Reading a body which exceeds the limit
180 after decoding fails with
181 `http::error::body_too_large`. The
182 default is unlimited.
183 */
184 std::uint64_t response_body_limit =
185 (std::numeric_limits<std::uint64_t>::max)();
186
187 /** Size of the in-place response buffer.
188
189 Bodies up to this size fit in the
190 internal buffer of the parser and can be
191 read without additional allocations
192 using @ref response::try_as_view and
193 @ref response::as_view. Reading a
194 larger body in place fails with
195 `http::error::in_place_overflow`.
196 */
197 std::size_t response_inplace_buffer = 1024 * 1024;
198
199 /** Timeout for the entire operation.
200
201 When set, each request must complete
202 within this duration, from connection
203 establishment through receipt of the
204 response headers. The remaining time
205 then bounds a whole-body read with
206 @ref response::as or
207 @ref response::as_view (and their
208 `try_` forms), but not the streaming
209 sources @ref response::as_buffer_source
210 and @ref response::as_read_source. Can
211 be overridden per request with
212 @ref request_builder::timeout.
213 */
214 std::optional<clock::duration> timeout;
215
216 /** Timeout for establishing a connection.
217
218 Covers name resolution, the TCP
219 connection, proxy negotiation, and the
220 TLS handshake.
221 */
222 13x clock::duration connect_timeout = std::chrono::seconds(60);
223
224 /** Timeout for individual I/O operations.
225
226 When set, applies to every read and write
227 performed on a connection, bounding the
228 time the peer may remain unresponsive
229 regardless of the message size.
230 */
231 std::optional<clock::duration> io_timeout = std::nullopt;
232
233 /** Time an idle pooled connection remains usable.
234
235 Pooled connections which have been idle for
236 longer than this duration are discarded
237 instead of being reused.
238 */
239 13x clock::duration pool_idle_timeout = std::chrono::seconds(90);
240
241 /** Maximum number of idle pooled connections per origin.
242
243 When the limit is reached, additional
244 connections are closed instead of being
245 returned to the pool.
246 */
247 std::size_t pool_max_idle_per_host = 10;
248
249 /** Set the `TCP_NODELAY` option on sockets.
250
251 Disables Nagle's algorithm on newly
252 established connections.
253 */
254 bool tcp_nodelay = true;
255
256 /** The local endpoint to bind sockets to.
257 */
258 corosio::endpoint local_address;
259
260 /** The proxy used for establishing connections.
261
262 Supported proxy schemes are `http`,
263 `socks5`, and `socks5h`. Credentials in the
264 userinfo component of the URL are used for
265 proxy authentication.
266
267 @par Example
268 @code
269 cfg.proxy = urls::url("socks5h://user:pass@localhost:8080");
270 @endcode
271 */
272 std::optional<urls::url> proxy;
273
274 /** Override connection establishment.
275
276 When set, this function is invoked
277 instead of the built-in name resolution,
278 TCP connection, proxy negotiation, and
279 TLS handshake whenever the pool needs a
280 new connection.
281
282 Intended for testing and for advanced uses
283 such as connecting over a pre-established
284 tunnel or a Unix domain socket.
285
286 @par Example
287 @code
288 cfg.connect_handler =
289 [](urls::url_view) -> capy::io_task<capy::any_stream>
290 {
291 auto [a, b] = capy::test::make_stream_pair();
292 // drive b from the test; hand a to the client
293 co_return { {}, capy::any_stream(std::move(a)) };
294 };
295 @endcode
296 */
297 std::function<
298 capy::io_task<capy::any_stream>(urls::url_view url)>
299 connect_handler;
300 };
301
302 private:
303 config config_;
304 std::shared_ptr<detail::connection_pool> pool_;
305 http::fields headers_;
306 burl::cookie_jar cookie_jar_;
307
308 public:
309 /** Constructor.
310
311 Constructs a client with a default
312 configuration.
313
314 @param exec The executor used to perform
315 asynchronous operations.
316
317 @param tls_ctx The TLS context used for
318 `https` connections.
319 */
320 BOOST_BURL_DECL
321 client(capy::executor_ref exec, corosio::tls_context tls_ctx);
322
323 /** Constructor.
324
325 Constructs a client with the provided
326 configuration. Content codings whose decode
327 service is not installed in the system
328 context are disabled, regardless of the
329 configuration.
330
331 @param exec The executor used to perform
332 asynchronous operations.
333
334 @param tls_ctx The TLS context used for
335 `https` connections.
336
337 @param cfg The configuration settings.
338 */
339 BOOST_BURL_DECL
340 client(capy::executor_ref exec, corosio::tls_context tls_ctx, config cfg);
341
342 /** Copy constructor (deleted).
343 */
344 client(client const&) = delete;
345
346 /** Copy assignment (deleted).
347 */
348 client&
349 operator=(client const&) = delete;
350
351 /** Move constructor.
352
353 @param other The client to move from.
354 */
355 client(client&& other) = default;
356
357 /** Move assignment.
358
359 @param other The client to move from.
360
361 @return A reference to this client.
362 */
363 client&
364 operator=(client&& other) = default;
365
366 /** Return the default headers.
367
368 These headers are sent with every request.
369 Headers set on an individual request take
370 precedence over default headers with the
371 same name.
372
373 @par Example
374 @code
375 c.headers().set(http::field::user_agent, "BoostBurl/1.0");
376 @endcode
377 */
378 http::fields&
379 2x headers() noexcept
380 {
381 2x return headers_;
382 }
383
384 /** Return the default headers.
385
386 These headers are sent with every request.
387 Headers set on an individual request take
388 precedence over default headers with the
389 same name.
390 */
391 const http::fields&
392 headers() const noexcept
393 {
394 return headers_;
395 }
396
397 /** Return the cookie jar.
398
399 The jar stores cookies received in responses
400 and supplies them for subsequent requests
401 when @ref config::cookies is enabled.
402 */
403 burl::cookie_jar&
404 cookie_jar() noexcept
405 {
406 return cookie_jar_;
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 const burl::cookie_jar&
416 cookie_jar() const noexcept
417 {
418 return cookie_jar_;
419 }
420
421 /** Set default credentials for HTTP Basic authentication.
422
423 Sets the default `Authorization` header,
424 sent with every request, to the Basic scheme
425 with the provided credentials. Can be
426 overridden per request with
427 @ref request_builder::basic_auth.
428
429 Credentials are not sent when a redirect
430 leads to a different origin, unless
431 @ref config::unrestricted_auth is enabled.
432
433 @param user The username.
434
435 @param pass The password.
436 */
437 BOOST_BURL_DECL
438 void
439 basic_auth(std::string_view user, std::string_view pass);
440
441 /** Set a default token for HTTP Bearer authentication.
442
443 Sets the default `Authorization` header,
444 sent with every request, to the Bearer
445 scheme with the provided token. Can be
446 overridden per request with
447 @ref request_builder::bearer_auth.
448
449 The token is not sent when a redirect leads
450 to a different origin, unless
451 @ref config::unrestricted_auth is enabled.
452
453 @param token The bearer token.
454 */
455 BOOST_BURL_DECL
456 void
457 bearer_auth(std::string_view token);
458
459 /** Create a builder for a `GET` request.
460
461 @par Example
462 @code
463 auto r = co_await c.get("https://example.com")
464 .as<std::string>();
465 @endcode
466
467 @param url The URL of the request.
468
469 @return A builder for configuring and
470 sending the request.
471 */
472 BOOST_BURL_DECL
473 request_builder
474 get(urls::url_view url);
475
476 /** Create a builder for a `HEAD` request.
477
478 @param url The URL of the request.
479
480 @return A builder for configuring and
481 sending the request.
482 */
483 BOOST_BURL_DECL
484 request_builder
485 head(urls::url_view url);
486
487 /** Create a builder for a `POST` request.
488
489 @param url The URL of the request.
490
491 @return A builder for configuring and
492 sending the request.
493 */
494 BOOST_BURL_DECL
495 request_builder
496 post(urls::url_view url);
497
498 /** Create a builder for a `PUT` request.
499
500 @param url The URL of the request.
501
502 @return A builder for configuring and
503 sending the request.
504 */
505 BOOST_BURL_DECL
506 request_builder
507 put(urls::url_view url);
508
509 /** Create a builder for a `PATCH` request.
510
511 @param url The URL of the request.
512
513 @return A builder for configuring and
514 sending the request.
515 */
516 BOOST_BURL_DECL
517 request_builder
518 patch(urls::url_view url);
519
520 /** Create a builder for a `DELETE` request.
521
522 The trailing underscore in the function name
523 avoids the `delete` keyword.
524
525 @param url The URL of the request.
526
527 @return A builder for configuring and
528 sending the request.
529 */
530 BOOST_BURL_DECL
531 request_builder
532 delete_(urls::url_view url);
533
534 /** Create a builder for a request.
535
536 The verb functions are equivalent to calling
537 this function with the corresponding method.
538
539 @param method The method of the request.
540
541 @param url The URL of the request.
542
543 @return A builder for configuring and
544 sending the request.
545 */
546 BOOST_BURL_DECL
547 request_builder
548 request(http::method method, urls::url_view url);
549
550 /** Asynchronously execute a request.
551
552 Sends the request and reads the response
553 status line and headers; the body is left
554 unread and can be consumed through the
555 returned @ref response.
556
557 @par Example
558 @code
559 burl::request req = c.get("https://example.com").build();
560
561 auto [ec, r] = co_await c.execute(std::move(req));
562 @endcode
563
564 @param request The request to execute.
565
566 @return An awaitable yielding
567 `(error_code,response)`.
568
569 @see @ref request_builder::send.
570 */
571 BOOST_BURL_DECL
572 capy::io_task<response>
573 execute(burl::request request);
574
575 private:
576 BOOST_BURL_DECL
577 capy::io_task<response>
578 execute_impl(
579 burl::request request,
580 std::optional<config::clock::time_point> deadline);
581 };
582
583 } // namespace burl
584 } // namespace boost
585
586 #include <boost/burl/request_builder.hpp>
587
588 #endif
589