• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 1019363462

28 Sep 2023 08:41AM UTC coverage: 78.477% (+0.9%) from 77.556%
1019363462

push

gitlab-ci

kacf
chore: Make out `optional` use compatible with C++17.

If the standard is pre-C++17, we use the `optional-lite` library, else
we use the one in `std`.

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>

17 of 17 new or added lines in 6 files covered. (100.0%)

5546 of 7067 relevant lines covered (78.48%)

11103.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.78
/common/http.hpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#ifndef MENDER_COMMON_HTTP_HPP
16
#define MENDER_COMMON_HTTP_HPP
17

18
#include <functional>
19
#include <string>
20
#include <memory>
21
#include <unordered_map>
22
#include <unordered_set>
23
#include <vector>
24

25
#ifdef MENDER_USE_BOOST_BEAST
26
#include <boost/asio.hpp>
27
#include <boost/beast.hpp>
28
#include <boost/asio/ssl.hpp>
29
#include <boost/asio/ssl/error.hpp>
30
#include <boost/asio/ssl/stream.hpp>
31
#endif // MENDER_USE_BOOST_BEAST
32

33
#include <config.h>
34

35
#include <common/error.hpp>
36
#include <common/events.hpp>
37
#include <common/expected.hpp>
38
#include <common/io.hpp>
39
#include <common/log.hpp>
40

41
namespace mender {
42
namespace http {
43

44
using namespace std;
45

46
#ifdef MENDER_USE_BOOST_BEAST
47
namespace asio = boost::asio;
48
namespace beast = boost::beast;
49
namespace http = beast::http;
50
namespace ssl = asio::ssl;
51
using tcp = asio::ip::tcp;
52
#endif // MENDER_USE_BOOST_BEAST
53

54
namespace error = mender::common::error;
55
namespace events = mender::common::events;
56
namespace expected = mender::common::expected;
57
namespace io = mender::common::io;
58
namespace log = mender::common::log;
59

60
class Client;
61

62
class HttpErrorCategoryClass : public std::error_category {
63
public:
64
        const char *name() const noexcept override;
65
        string message(int code) const override;
66
};
67
extern const HttpErrorCategoryClass HttpErrorCategory;
68

69
enum ErrorCode {
70
        NoError = 0,
71
        NoSuchHeaderError,
72
        InvalidUrlError,
73
        BodyMissingError,
74
        BodyIgnoredError,
75
        UnsupportedMethodError,
76
        StreamCancelledError,
77
        UnsupportedBodyType,
78
        MaxRetryError,
79
};
80

81
error::Error MakeError(ErrorCode code, const string &msg);
82

83
enum class Method {
84
        Invalid,
85
        GET,
86
        HEAD,
87
        POST,
88
        PUT,
89
        PATCH,
90
        CONNECT,
91
};
92

93
enum StatusCode {
94
        // Not a complete enum, we define only the ones we use.
95

96
        StatusSwitchingProtocols = 101,
97

98
        StatusOK = 200,
99
        StatusNoContent = 204,
100

101
        StatusBadRequest = 400,
102
        StatusUnauthorized = 401,
103
        StatusNotFound = 404,
104
        StatusConflict = 409,
105

106
        StatusInternalServerError = 500,
107
        StatusNotImplemented = 501,
108
};
109

110
string MethodToString(Method method);
111

112
struct BrokenDownUrl {
19✔
113
        string protocol;
114
        string host;
115
        int port {-1};
116
        string path;
117
};
118

119
error::Error BreakDownUrl(const string &url, BrokenDownUrl &address);
120

121
string URLEncode(const string &value);
122

123
string JoinOneUrl(const string &prefix, const string &url);
124

125
template <typename... Urls>
126
string JoinUrl(const string &prefix, const Urls &...urls) {
12✔
127
        string final_url {prefix};
128
        for (const auto &url : {urls...}) {
48✔
129
                final_url = JoinOneUrl(final_url, url);
24✔
130
        }
131
        return final_url;
12✔
132
}
133

134
class CaseInsensitiveHasher {
135
public:
136
        size_t operator()(const string &str) const;
137
};
138

139
class CaseInsensitiveComparator {
140
public:
141
        bool operator()(const string &str1, const string &str2) const;
142
};
143

144
class Transaction {
145
public:
146
        virtual ~Transaction() {
×
147
        }
×
148

149
        expected::ExpectedString GetHeader(const string &name) const;
150

151
        using HeaderMap =
152
                unordered_map<string, string, CaseInsensitiveHasher, CaseInsensitiveComparator>;
153

154
        const HeaderMap &GetHeaders() const {
155
                return headers_;
156
        }
157

158
protected:
159
        HeaderMap headers_;
160

161
        friend class Client;
162
};
163
using TransactionPtr = shared_ptr<Transaction>;
164

165
using BodyGenerator = function<io::ExpectedReaderPtr()>;
166
using AsyncBodyGenerator = function<io::ExpectedAsyncReaderPtr()>;
167

168
class Request : public Transaction {
169
public:
170
        Request() {
19✔
171
        }
172

173
        Method GetMethod() const;
174
        string GetPath() const;
175

176
protected:
177
        Method method_ {Method::Invalid};
178
        BrokenDownUrl address_;
179

180
        friend class Client;
181
        friend class Stream;
182
};
183
using RequestPtr = shared_ptr<Request>;
184
using ExpectedRequestPtr = expected::expected<RequestPtr, error::Error>;
185

186
class Response : public Transaction {
187
public:
188
        Response() {
189
        }
190

191
        unsigned GetStatusCode() const;
192
        string GetStatusMessage() const;
193

194
protected:
195
        unsigned status_code_ {StatusInternalServerError};
196
        string status_message_;
197

198
        friend class Client;
199
        friend class Stream;
200
};
201
using ResponsePtr = shared_ptr<Response>;
202
using ExpectedResponsePtr = expected::expected<ResponsePtr, error::Error>;
203

204
class OutgoingRequest;
205
using OutgoingRequestPtr = shared_ptr<OutgoingRequest>;
206
using ExpectedOutgoingRequestPtr = expected::expected<OutgoingRequestPtr, error::Error>;
207
class IncomingRequest;
208
using IncomingRequestPtr = shared_ptr<IncomingRequest>;
209
using ExpectedIncomingRequestPtr = expected::expected<IncomingRequestPtr, error::Error>;
210
class IncomingResponse;
211
using IncomingResponsePtr = shared_ptr<IncomingResponse>;
212
using ExpectedIncomingResponsePtr = expected::expected<IncomingResponsePtr, error::Error>;
213
class OutgoingResponse;
214
using OutgoingResponsePtr = shared_ptr<OutgoingResponse>;
215
using ExpectedOutgoingResponsePtr = expected::expected<OutgoingResponsePtr, error::Error>;
216

217
using RequestHandler = function<void(ExpectedIncomingRequestPtr)>;
218
using IdentifiedRequestHandler = function<void(IncomingRequestPtr, error::Error)>;
219
using ResponseHandler = function<void(ExpectedIncomingResponsePtr)>;
220

221
using ReplyFinishedHandler = function<void(error::Error)>;
222
using SwitchProtocolHandler = function<void(io::ExpectedAsyncReadWriterPtr)>;
223

224
// Usually you want to cancel the connection when there is an error during body writing, but there
225
// are some cases in tests where it's useful to keep the connection alive in order to let the
226
// original error make it to the body handler.
227
enum class BodyWriterErrorMode {
228
        Cancel,
229
        KeepAlive,
230
};
231

232
class OutgoingRequest : public Request {
233
public:
234
        OutgoingRequest() {
19✔
235
        }
236

237
        void SetMethod(Method method);
238
        error::Error SetAddress(const string &address);
239
        void SetHeader(const string &name, const string &value);
240

241
        // Set to a function which will generate the body. Make sure that the Content-Length set in
242
        // the headers matches the length of the body. Using a generator instead of a direct reader
243
        // is needed in case of redirects. Note that it is not possible to set both; setting one
244
        // unsets the other.
245
        void SetBodyGenerator(BodyGenerator body_gen);
246
        void SetAsyncBodyGenerator(AsyncBodyGenerator body_gen);
247

248
private:
249
        // Original address.
250
        string orig_address_;
251

252
        BodyGenerator body_gen_;
253
        io::ReaderPtr body_reader_;
254
        AsyncBodyGenerator async_body_gen_;
255
        io::AsyncReaderPtr async_body_reader_;
256

257
        friend class Client;
258
};
259

260
class Stream;
261

262
class IncomingRequest :
263
        public Request,
264
        virtual public io::Canceller,
265
        public enable_shared_from_this<IncomingRequest> {
266
public:
267
        ~IncomingRequest();
268

269
        // Set this after receiving the headers to automatically write the body. If there is no
270
        // body, nothing will be written. Mutually exclusive with `MakeBodyAsyncReader()`.
271
        void SetBodyWriter(
272
                io::WriterPtr body_writer, BodyWriterErrorMode mode = BodyWriterErrorMode::Cancel);
273

274
        // Use this to get an async reader for the body. If there is no body, it returns a
275
        // `BodyMissingError`; it's safe to continue afterwards, but without a reader. Mutually
276
        // exclusive with `SetBodyWriter()`.
277
        io::ExpectedAsyncReaderPtr MakeBodyAsyncReader();
278

279
        // Use this to get a response that can be used to reply to the request. Due to the
280
        // asynchronous nature, this can be done immediately or some time later.
281
        ExpectedOutgoingResponsePtr MakeResponse();
282

283
        void Cancel() override;
284

285
private:
286
        IncomingRequest(Stream &stream, shared_ptr<bool> cancelled) :
287
                stream_(stream),
288
                cancelled_(cancelled) {
289
        }
290

291
        Stream &stream_;
292
        shared_ptr<bool> cancelled_;
293

294
        friend class Server;
295
        friend class Stream;
296
};
297

298
class IncomingResponse :
299
        public Response,
300
        virtual public io::Canceller,
301
        public enable_shared_from_this<IncomingResponse> {
302
public:
303
        void Cancel() override;
304

305
        // Set this after receiving the headers to automatically write the body. If there is no
306
        // body, nothing will be written. Mutually exclusive with `MakeBodyAsyncReader()`.
307
        void SetBodyWriter(
308
                io::WriterPtr body_writer, BodyWriterErrorMode mode = BodyWriterErrorMode::Cancel);
309

310
        // Use this to get an async reader for the body. If there is no body, it returns a
311
        // `BodyMissingError`; it's safe to continue afterwards, but without a reader. Mutually
312
        // exclusive with `SetBodyWriter()`.
313
        io::ExpectedAsyncReaderPtr MakeBodyAsyncReader();
314

315
        // Gets the underlying socket after a 101 Switching Protocols response. This detaches the
316
        // socket from `Client`, and both can be used independently from then on.
317
        io::ExpectedAsyncReadWriterPtr SwitchProtocol();
318

319
private:
320
        IncomingResponse(Client &client, shared_ptr<bool> cancelled);
321

322
        Client &client_;
323
        shared_ptr<bool> cancelled_;
324

325
        friend class Client;
326
};
327

328
class OutgoingResponse :
329
        public Response,
330
        virtual public io::Canceller,
331
        public enable_shared_from_this<OutgoingResponse> {
332
public:
333
        ~OutgoingResponse();
334

335
        error::Error AsyncReply(ReplyFinishedHandler reply_finished_handler);
336
        void Cancel() override;
337

338
        void SetStatusCodeAndMessage(unsigned code, const string &message);
339
        void SetHeader(const string &name, const string &value);
340

341
        // Set to a Reader which contains the body. Make sure that the Content-Length set in the
342
        // headers matches the length of the body. Note that it is not possible to set both; setting
343
        // one unsets the other.
344
        void SetBodyReader(io::ReaderPtr body_reader);
345
        void SetAsyncBodyReader(io::AsyncReaderPtr body_reader);
346

347
        // An alternative to AsyncReply. `resp` should already contain the correct status and
348
        // headers to perform the switch, and the handler will be called after the HTTP headers have
349
        // been written.
350
        error::Error AsyncSwitchProtocol(SwitchProtocolHandler handler);
351

352
private:
353
        OutgoingResponse(Stream &stream, shared_ptr<bool> cancelled) :
354
                stream_ {stream},
355
                cancelled_ {cancelled} {
356
        }
357

358
        io::ReaderPtr body_reader_;
359
        io::AsyncReaderPtr async_body_reader_;
360

361
        Stream &stream_;
362
        shared_ptr<bool> cancelled_;
363

364
        friend class Server;
365
        friend class Stream;
366
        friend class IncomingRequest;
367
};
368

369
template <typename StreamType>
370
class BodyAsyncReader;
371

372
// Master object that connections are made from. Configure TLS options on this object before making
373
// connections.
374
struct ClientConfig {
375
        ClientConfig();
376
        ClientConfig(
377
                const string &server_cert_path,
378
                const string &client_cert_path = "",
379
                const string &client_cert_key_path = "");
380
        ~ClientConfig();
381

382
        string server_cert_path;
383
        string client_cert_path;
384
        string client_cert_key_path;
385
};
386

387
enum class TransactionStatus {
388
        None,
389
        HeaderHandlerCalled,
390
        ReaderCreated,
391
        BodyReadingInProgress,
392
        BodyReadingFinished,
393
        BodyHandlerCalled, // Only used by server.
394
        Replying,          // Only used by server.
395
        SwitchingProtocol,
396
        Done,
397
};
398
static inline bool AtLeast(TransactionStatus status, TransactionStatus expected_status) {
399
        return static_cast<int>(status) >= static_cast<int>(expected_status);
400
}
401

402
// Object which manages one connection, and its requests and responses (one at a time).
403
class Client : public events::EventLoopObject, virtual public io::Canceller {
404
public:
405
        Client(
406
                const ClientConfig &client,
407
                events::EventLoop &event_loop,
408
                const string &logger_name = "http_client");
409
        virtual ~Client();
410

411
        Client(Client &&) = default;
412

413
        // `header_handler` is called when header has arrived, `body_handler` is called when the
414
        // whole body has arrived.
415
        virtual error::Error AsyncCall(
416
                OutgoingRequestPtr req, ResponseHandler header_handler, ResponseHandler body_handler);
417
        void Cancel() override;
418

419
        // Use this to get an async reader for the body. If there is no body, it returns a
420
        // `BodyMissingError`; it's safe to continue afterwards, but without a reader.
421
        virtual io::ExpectedAsyncReaderPtr MakeBodyAsyncReader(IncomingResponsePtr req);
422

423
        // Gets the underlying socket after a 101 Switching Protocols response. This detaches the
424
        // socket from `Client`, and both can be used independently from then on.
425
        virtual io::ExpectedAsyncReadWriterPtr SwitchProtocol(IncomingResponsePtr req);
426

427
protected:
428
        events::EventLoop &event_loop_;
429
        string logger_name_;
430
        log::Logger logger_ {logger_name_};
431

432
private:
433
        bool is_https_ {false};
434

435
        // Used during connections. Must remain valid due to async nature.
436
        OutgoingRequestPtr request_;
437
        IncomingResponsePtr response_;
438
        ResponseHandler header_handler_;
439
        ResponseHandler body_handler_;
440

441
        vector<uint8_t>::iterator reader_buf_start_;
442
        vector<uint8_t>::iterator reader_buf_end_;
443
        io::AsyncIoHandler reader_handler_;
444

445
        // Each time we cancel something, we set this to true, and then make a new one. This ensures
446
        // that for everyone who has a copy, it will stay true even after a new request is made, or
447
        // after things have been destroyed.
448
        shared_ptr<bool> cancelled_;
449

450
#ifdef MENDER_USE_BOOST_BEAST
451

452
        ssl::context ssl_ctx_ {ssl::context::tls_client};
453

454
        boost::asio::ip::tcp::resolver resolver_;
455
        shared_ptr<ssl::stream<tcp::socket>> stream_;
456

457
        vector<uint8_t> body_buffer_;
458

459
        asio::ip::tcp::resolver::results_type resolver_results_;
460
        shared_ptr<http::request<http::buffer_body>> http_request_;
461
        shared_ptr<http::request_serializer<http::buffer_body>> http_request_serializer_;
462
        size_t request_body_length_;
463

464
        shared_ptr<beast::flat_buffer> response_buffer_;
465
        shared_ptr<http::response_parser<http::buffer_body>> http_response_parser_;
466
        size_t response_body_length_;
467
        size_t response_body_read_;
468
        TransactionStatus status_ {TransactionStatus::None};
469

470
        void DoCancel();
471

472
        void CallHandler(ResponseHandler handler);
473
        void CallErrorHandler(
474
                const error_code &ec, const OutgoingRequestPtr &req, ResponseHandler handler);
475
        void CallErrorHandler(
476
                const error::Error &err, const OutgoingRequestPtr &req, ResponseHandler handler);
477
        void ResolveHandler(const error_code &ec, const asio::ip::tcp::resolver::results_type &results);
478
        void ConnectHandler(const error_code &ec, const asio::ip::tcp::endpoint &endpoint);
479
        void HandshakeHandler(const error_code &ec, const asio::ip::tcp::endpoint &endpoint);
480
        void WriteHeaderHandler(const error_code &ec, size_t num_written);
481
        void WriteBodyHandler(const error_code &ec, size_t num_written);
482
        void PrepareAndWriteNewBodyBuffer();
483
        void WriteNewBodyBuffer(size_t size);
484
        void WriteBody();
485
        void ReadHeaderHandler(const error_code &ec, size_t num_read);
486
        void ReadHeader();
487
        void AsyncReadNextBodyPart(
488
                vector<uint8_t>::iterator start, vector<uint8_t>::iterator end, io::AsyncIoHandler handler);
489
        void ReadBodyHandler(error_code ec, size_t num_read);
490
#endif // MENDER_USE_BOOST_BEAST
491

492
        friend class IncomingResponse;
493
        friend class BodyAsyncReader<Client>;
494
};
495
using ClientPtr = shared_ptr<Client>;
496

497
// Master object that servers are made from. Configure TLS options on this object before listening.
498
struct ServerConfig {
499
        ServerConfig();
500
        ~ServerConfig();
501

502
        // TODO: Empty for now, but will contain TLS configuration options later.
503
};
504

505
class Server;
506

507
class Stream : public enable_shared_from_this<Stream> {
508
public:
509
        Stream(const Stream &) = delete;
510
        ~Stream();
511

512
        void Cancel();
513

514
private:
515
        Stream(Server &server);
516

517
private:
518
        Server &server_;
519
        friend class Server;
520

521
        log::Logger logger_;
522

523
        IncomingRequestPtr request_;
524

525
        // The reason we have two pointers is this: Between receiving a request, and producing a
526
        // reply, an arbitrary amount of time may pass, and it is the caller's responsibility to
527
        // first call MakeResponse(), and then at some point later, call AsyncReply(). However, if
528
        // the caller never does this, and destroys the response instead, we still have ownership to
529
        // the response here, which means it will never be destroyed, and we will leak memory. So we
530
        // use a weak_ptr to bridge the gap. As long as AsyncReply() has not been called yet, we use
531
        // a weak pointer so if the response goes out of scope, it will be properly destroyed. After
532
        // AsyncReply is called, we know that a handler will eventually be called, so we take
533
        // ownership of the response object from that point onwards.
534
        OutgoingResponsePtr response_;
535
        weak_ptr<OutgoingResponse> maybe_response_;
536

537
        friend class IncomingRequest;
538
        friend class OutgoingResponse;
539
        friend class BodyAsyncReader<Stream>;
540

541
        ReplyFinishedHandler reply_finished_handler_;
542
        SwitchProtocolHandler switch_protocol_handler_;
543

544
        vector<uint8_t>::iterator reader_buf_start_;
545
        vector<uint8_t>::iterator reader_buf_end_;
546
        io::AsyncIoHandler reader_handler_;
547

548
        // Each time we cancel something, we set this to true, and then make a new one. This ensures
549
        // that for everyone who has a copy, it will stay true even after a new request is made, or
550
        // after things have been destroyed.
551
        shared_ptr<bool> cancelled_;
552

553
#ifdef MENDER_USE_BOOST_BEAST
554
        asio::ip::tcp::socket socket_;
555

556
        shared_ptr<beast::flat_buffer> request_buffer_;
557
        http::request_parser<http::buffer_body> http_request_parser_;
558
        vector<uint8_t> body_buffer_;
559
        size_t request_body_length_;
560
        size_t request_body_read_;
561
        TransactionStatus status_ {TransactionStatus::None};
562

563
        shared_ptr<http::response<http::buffer_body>> http_response_;
564
        shared_ptr<http::response_serializer<http::buffer_body>> http_response_serializer_;
565

566
        void DoCancel();
567

568
        void CallErrorHandler(const error_code &ec, const RequestPtr &req, RequestHandler handler);
569
        void CallErrorHandler(const error::Error &err, const RequestPtr &req, RequestHandler handler);
570
        void CallErrorHandler(
571
                const error_code &ec, const IncomingRequestPtr &req, IdentifiedRequestHandler handler);
572
        void CallErrorHandler(
573
                const error::Error &err, const IncomingRequestPtr &req, IdentifiedRequestHandler handler);
574
        void CallErrorHandler(
575
                const error_code &ec, const RequestPtr &req, ReplyFinishedHandler handler);
576
        void CallErrorHandler(
577
                const error::Error &err, const RequestPtr &req, ReplyFinishedHandler handler);
578
        void CallErrorHandler(
579
                const error_code &ec, const RequestPtr &req, SwitchProtocolHandler handler);
580
        void CallErrorHandler(
581
                const error::Error &err, const RequestPtr &req, SwitchProtocolHandler handler);
582

583
        void AcceptHandler(const error_code &ec);
584
        void ReadHeader();
585
        void ReadHeaderHandler(const error_code &ec, size_t num_read);
586
        void AsyncReadNextBodyPart(
587
                vector<uint8_t>::iterator start, vector<uint8_t>::iterator end, io::AsyncIoHandler handler);
588
        void ReadBodyHandler(error_code ec, size_t num_read);
589
        void AsyncReply(ReplyFinishedHandler reply_finished_handler);
590
        void SetupResponse();
591
        void WriteHeaderHandler(const error_code &ec, size_t num_written);
592
        void PrepareAndWriteNewBodyBuffer();
593
        void WriteNewBodyBuffer(size_t size);
594
        void WriteBody();
595
        void WriteBodyHandler(const error_code &ec, size_t num_written);
596
        void CallBodyHandler();
597
        void FinishReply();
598
        error::Error AsyncSwitchProtocol(SwitchProtocolHandler handler);
599
        void SwitchingProtocolHandler(error_code ec, size_t num_written);
600
#endif // MENDER_USE_BOOST_BEAST
601
};
602

603
class Server : public events::EventLoopObject, virtual public io::Canceller {
604
public:
605
        Server(const ServerConfig &server, events::EventLoop &event_loop);
606
        ~Server();
607

608
        Server(Server &&) = default;
609

610
        error::Error AsyncServeUrl(
611
                const string &url, RequestHandler header_handler, RequestHandler body_handler);
612
        // Same as the above, except that the body handler has the `IncomingRequestPtr` included
613
        // even when there is an error, so that the request can be matched with the request which
614
        // was received in the header handler.
615
        error::Error AsyncServeUrl(
616
                const string &url, RequestHandler header_handler, IdentifiedRequestHandler body_handler);
617
        void Cancel() override;
618

619
        uint16_t GetPort() const;
620
        // Can differ from the passed in URL if a 0 (random) port number was used.
621
        string GetUrl() const;
622

623
        // Use this to get a response that can be used to reply to the request. Due to the
624
        // asynchronous nature, this can be done immediately or some time later.
625
        virtual ExpectedOutgoingResponsePtr MakeResponse(IncomingRequestPtr req);
626
        virtual error::Error AsyncReply(
627
                OutgoingResponsePtr resp, ReplyFinishedHandler reply_finished_handler);
628

629
        // Use this to get an async reader for the body. If there is no body, it returns a
630
        // `BodyMissingError`; it's safe to continue afterwards, but without a reader.
631
        virtual io::ExpectedAsyncReaderPtr MakeBodyAsyncReader(IncomingRequestPtr req);
632

633
        // An alternative to AsyncReply. `resp` should already contain the correct status and
634
        // headers to perform the switch, and the handler will be called after the HTTP headers have
635
        // been written.
636
        virtual error::Error AsyncSwitchProtocol(
637
                OutgoingResponsePtr resp, SwitchProtocolHandler handler);
638

639
private:
640
        events::EventLoop &event_loop_;
641

642
        BrokenDownUrl address_;
643

644
        RequestHandler header_handler_;
645
        IdentifiedRequestHandler body_handler_;
646

647
        friend class IncomingRequest;
648
        friend class Stream;
649
        friend class OutgoingResponse;
650

651
        using StreamPtr = shared_ptr<Stream>;
652

653
        friend class TestInspector;
654

655
#ifdef MENDER_USE_BOOST_BEAST
656
        asio::ip::tcp::acceptor acceptor_;
657

658
        unordered_set<StreamPtr> streams_;
659

660
        void DoCancel();
661

662
        void PrepareNewStream();
663
        void AsyncAccept(StreamPtr stream);
664
        void RemoveStream(const StreamPtr &stream);
665
#endif // MENDER_USE_BOOST_BEAST
666
};
667

668
class ExponentialBackoff {
669
public:
670
        ExponentialBackoff(chrono::milliseconds max_interval, int try_count = -1) :
671
                try_count_ {try_count} {
672
                SetMaxInterval(max_interval);
673
        }
674

675
        void Reset() {
676
                SetIteration(0);
677
        }
678

679
        int TryCount() {
680
                return try_count_;
681
        }
682
        void SetTryCount(int count) {
683
                try_count_ = count;
684
        }
685

686
        chrono::milliseconds SmallestInterval() {
687
                return smallest_interval_;
688
        }
689
        void SetSmallestInterval(chrono::milliseconds interval) {
690
                smallest_interval_ = interval;
691
                if (max_interval_ < smallest_interval_) {
692
                        max_interval_ = smallest_interval_;
693
                }
694
        }
695

696
        chrono::milliseconds MaxInterval() {
697
                return max_interval_;
698
        }
699
        void SetMaxInterval(chrono::milliseconds interval) {
700
                max_interval_ = interval;
701
                if (max_interval_ < smallest_interval_) {
702
                        max_interval_ = smallest_interval_;
703
                }
704
        }
705

706
        using ExpectedInterval = expected::expected<chrono::milliseconds, error::Error>;
707
        ExpectedInterval NextInterval();
708

709
        // Set which iteration we're at. Mainly for use in tests.
710
        void SetIteration(int iteration) {
711
                iteration_ = iteration;
712
        }
713

714
private:
715
        chrono::milliseconds smallest_interval_ {chrono::minutes(1)};
716
        chrono::milliseconds max_interval_;
717
        int try_count_;
718

719
        int iteration_ {0};
720
};
721

722
} // namespace http
723
} // namespace mender
724

725
#endif // MENDER_COMMON_HTTP_HPP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc