include/boost/burl/client.hpp

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