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

mendersoftware / mender / 1726099042

20 Mar 2025 09:41AM UTC coverage: 75.916% (-0.003%) from 75.919%
1726099042

push

gitlab-ci

web-flow
Merge pull request #1778 from jo-lund/cert-failure

fix: Return an error when certificate fails to load

2 of 4 new or added lines in 1 file covered. (50.0%)

7373 of 9712 relevant lines covered (75.92%)

11151.79 hits per line

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

87.12
/src/common/http/platform/beast/http.cpp
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
#include <common/http.hpp>
16

17
#include <algorithm>
18

19
#include <boost/asio.hpp>
20
#include <boost/asio/ip/tcp.hpp>
21
#include <boost/asio/ssl/host_name_verification.hpp>
22
#include <boost/asio/ssl/verify_mode.hpp>
23

24
#include <common/common.hpp>
25
#include <common/crypto.hpp>
26

27
namespace mender {
28
namespace common {
29
namespace http {
30

31
namespace common = mender::common;
32
namespace crypto = mender::common::crypto;
33

34
// At the time of writing, Beast only supports HTTP/1.1, and is unlikely to support HTTP/2
35
// according to this discussion: https://github.com/boostorg/beast/issues/1302.
36
const unsigned int BeastHttpVersion = 11;
37

38
namespace asio = boost::asio;
39
namespace http = boost::beast::http;
40

41
const int HTTP_BEAST_BUFFER_SIZE = MENDER_BUFSIZE;
42

43
static http::verb MethodToBeastVerb(Method method) {
246✔
44
        switch (method) {
246✔
45
        case Method::GET:
46
                return http::verb::get;
47
        case Method::HEAD:
48
                return http::verb::head;
49
        case Method::POST:
50
                return http::verb::post;
51
        case Method::PUT:
52
                return http::verb::put;
53
        case Method::PATCH:
54
                return http::verb::patch;
55
        case Method::CONNECT:
56
                return http::verb::connect;
57
        case Method::Invalid:
58
                // Fallthrough to end (no-op).
59
                break;
60
        }
61
        // Don't use "default" case. This should generate a warning if we ever add any methods. But
62
        // still assert here for safety.
63
        assert(false);
64
        return http::verb::get;
65
}
66

67
static expected::expected<Method, error::Error> BeastVerbToMethod(
225✔
68
        http::verb verb, const string &verb_string) {
69
        switch (verb) {
225✔
70
        case http::verb::get:
189✔
71
                return Method::GET;
72
        case http::verb::head:
×
73
                return Method::HEAD;
74
        case http::verb::post:
13✔
75
                return Method::POST;
76
        case http::verb::put:
23✔
77
                return Method::PUT;
78
        case http::verb::patch:
×
79
                return Method::PATCH;
80
        case http::verb::connect:
×
81
                return Method::CONNECT;
82
        default:
×
83
                return expected::unexpected(MakeError(UnsupportedMethodError, verb_string));
×
84
        }
85
}
86

87
template <typename StreamType>
88
class BodyAsyncReader : virtual public io::AsyncReader {
89
public:
90
        BodyAsyncReader(StreamType &stream, shared_ptr<bool> cancelled) :
153✔
91
                stream_ {stream},
92
                cancelled_ {cancelled} {
306✔
93
        }
153✔
94
        ~BodyAsyncReader() {
39✔
95
                Cancel();
39✔
96
        }
78✔
97

98
        error::Error AsyncRead(
2,187✔
99
                vector<uint8_t>::iterator start,
100
                vector<uint8_t>::iterator end,
101
                io::AsyncIoHandler handler) override {
102
                if (eof_) {
2,187✔
103
                        handler(0);
×
104
                        return error::NoError;
×
105
                }
106

107
                if (*cancelled_) {
2,187✔
108
                        return error::MakeError(
×
109
                                error::ProgrammingError,
110
                                "BodyAsyncReader::AsyncRead called after stream is destroyed");
×
111
                }
112
                stream_.AsyncReadNextBodyPart(start, end, [this, handler](io::ExpectedSize size) {
12,805✔
113
                        if (size && size.value() == 0) {
6,310✔
114
                                eof_ = true;
125✔
115
                        }
116
                        handler(size);
12,620✔
117
                });
118
                return error::NoError;
2,187✔
119
        }
120

121
        void Cancel() override {
39✔
122
                if (!*cancelled_) {
39✔
123
                        stream_.Cancel();
4✔
124
                }
125
        }
39✔
126

127
private:
128
        StreamType &stream_;
129
        shared_ptr<bool> cancelled_;
130
        bool eof_ {false};
131

132
        friend class Client;
133
        friend class Server;
134
};
135

136
template <typename StreamType>
137
class RawSocket : virtual public io::AsyncReadWriter {
138
public:
139
        RawSocket(shared_ptr<StreamType> stream, shared_ptr<beast::flat_buffer> buffered) :
×
140
                destroying_ {make_shared<bool>(false)},
×
141
                stream_ {stream},
142
                buffered_ {buffered} {
×
143
                // If there are no buffered bytes, then we don't need it.
144
                if (buffered_ && buffered_->size() == 0) {
×
145
                        buffered_.reset();
×
146
                }
147
        }
×
148

149
        ~RawSocket() {
15✔
150
                *destroying_ = true;
15✔
151
                Cancel();
15✔
152
        }
30✔
153

154
        error::Error AsyncRead(
320✔
155
                vector<uint8_t>::iterator start,
156
                vector<uint8_t>::iterator end,
157
                io::AsyncIoHandler handler) override {
158
                // If we have prebuffered bytes, which can happen if the HTTP parser read the
159
                // header and parts of the body in one block, return those first.
160
                if (buffered_) {
320✔
161
                        return DrainPrebufferedData(start, end, handler);
8✔
162
                }
163

164
                read_buffer_ = asio::buffer(&*start, end - start);
316✔
165
                auto &destroying = destroying_;
166
                stream_->async_read_some(
632✔
167
                        read_buffer_,
316✔
168
                        [destroying, handler](const boost::system::error_code &ec, size_t num_read) {
313✔
169
                                if (*destroying) {
313✔
170
                                        return;
171
                                }
172

173
                                if (ec == asio::error::operation_aborted) {
313✔
174
                                        handler(expected::unexpected(error::Error(
12✔
175
                                                make_error_condition(errc::operation_canceled),
6✔
176
                                                "Could not read from socket")));
177
                                } else if (ec) {
310✔
178
                                        handler(expected::unexpected(
12✔
179
                                                error::Error(ec.default_error_condition(), "Could not read from socket")));
12✔
180
                                } else {
181
                                        handler(num_read);
608✔
182
                                }
183
                        });
184
                return error::NoError;
316✔
185
        }
186

187
        error::Error AsyncWrite(
309✔
188
                vector<uint8_t>::const_iterator start,
189
                vector<uint8_t>::const_iterator end,
190
                io::AsyncIoHandler handler) override {
191
                write_buffer_ = asio::buffer(&*start, end - start);
309✔
192
                auto &destroying = destroying_;
193
                stream_->async_write_some(
618✔
194
                        write_buffer_,
309✔
195
                        [destroying, handler](const boost::system::error_code &ec, size_t num_written) {
306✔
196
                                if (*destroying) {
306✔
197
                                        return;
198
                                }
199

200
                                if (ec == asio::error::operation_aborted) {
306✔
201
                                        handler(expected::unexpected(error::Error(
×
202
                                                make_error_condition(errc::operation_canceled),
×
203
                                                "Could not write to socket")));
204
                                } else if (ec) {
306✔
205
                                        handler(expected::unexpected(
×
206
                                                error::Error(ec.default_error_condition(), "Could not write to socket")));
×
207
                                } else {
208
                                        handler(num_written);
612✔
209
                                }
210
                        });
211
                return error::NoError;
309✔
212
        }
213

214
        void Cancel() override {
28✔
215
                if (stream_->lowest_layer().is_open()) {
28✔
216
                        stream_->lowest_layer().cancel();
15✔
217
                        stream_->lowest_layer().close();
15✔
218
                }
219
        }
28✔
220

221
private:
222
        error::Error DrainPrebufferedData(
4✔
223
                vector<uint8_t>::iterator start,
224
                vector<uint8_t>::iterator end,
225
                io::AsyncIoHandler handler) {
226
                size_t to_copy = min(static_cast<size_t>(end - start), buffered_->size());
4✔
227

228
                // These two lines are equivalent to:
229
                //   copy_n(static_cast<const uint8_t *>(buffered_->cdata().data()), to_copy, start);
230
                // but compatible with Boost 1.67.
231
                const beast::flat_buffer &cbuffered = *buffered_;
232
                copy_n(static_cast<const uint8_t *>(cbuffered.data().data()), to_copy, start);
4✔
233
                buffered_->consume(to_copy);
4✔
234
                if (buffered_->size() == 0) {
4✔
235
                        // We don't need it anymore.
236
                        buffered_.reset();
4✔
237
                }
238
                handler(to_copy);
4✔
239
                return error::NoError;
4✔
240
        }
241

242
        shared_ptr<bool> destroying_;
243
        shared_ptr<StreamType> stream_;
244
        shared_ptr<beast::flat_buffer> buffered_;
245
        asio::mutable_buffer read_buffer_;
246
        asio::const_buffer write_buffer_;
247
};
248

249
template <typename PARSER>
250
int64_t GetContentLength(const PARSER &parser) {
398✔
251
        auto content_length = parser.content_length();
398✔
252
        if (content_length) {
398✔
253
                return content_length.value();
351✔
254
        } else {
255
                return 0;
256
        }
257
}
258

259
expected::ExpectedBool HasBody(
447✔
260
        const expected::ExpectedString &content_length,
261
        const expected::ExpectedString &transfer_encoding) {
262
        if (transfer_encoding) {
447✔
263
                if (transfer_encoding.value() != "chunked") {
2✔
264
                        return expected::unexpected(error::Error(
×
265
                                make_error_condition(errc::not_supported),
×
266
                                "Unsupported Transfer-Encoding: " + transfer_encoding.value()));
×
267
                }
268
                return true;
269
        }
270

271
        if (content_length) {
445✔
272
                auto length = common::StringToLongLong(content_length.value());
219✔
273
                if (!length || length.value() < 0) {
219✔
274
                        return expected::unexpected(error::Error(
×
275
                                length.error().code,
276
                                "Content-Length contains invalid number: " + content_length.value()));
×
277
                }
278
                return length.value() > 0;
219✔
279
        }
280

281
        return false;
282
}
283

284
Client::Client(
353✔
285
        const ClientConfig &client, events::EventLoop &event_loop, const string &logger_name) :
286
        event_loop_ {event_loop},
287
        logger_name_ {logger_name},
288
        client_config_ {client},
289
        http_proxy_ {client.http_proxy},
353✔
290
        https_proxy_ {client.https_proxy},
353✔
291
        no_proxy_ {client.no_proxy},
353✔
292
        cancelled_ {make_shared<bool>(true)},
×
293
        resolver_(GetAsioIoContext(event_loop)),
294
        body_buffer_(HTTP_BEAST_BUFFER_SIZE) {
1,412✔
295
}
353✔
296

297
Client::~Client() {
2,118✔
298
        if (!*cancelled_) {
353✔
299
                logger_.Warning("Client destroyed while request is still active!");
30✔
300
        }
301
        DoCancel();
353✔
302
}
353✔
303

304
error::Error Client::Initialize() {
284✔
305
        if (initialized_) {
284✔
306
                return error::NoError;
71✔
307
        }
308

309
        for (auto i = 0; i < MENDER_BOOST_BEAST_SSL_CTX_COUNT; i++) {
631✔
310
                ssl_ctx_[i].set_verify_mode(
842✔
311
                        client_config_.skip_verify ? ssl::verify_none : ssl::verify_peer);
312

313
                beast::error_code ec {};
422✔
314
                if (client_config_.client_cert_path != "" and client_config_.client_cert_key_path != "") {
422✔
315
                        ssl_ctx_[i].set_options(boost::asio::ssl::context::default_workarounds);
4✔
316
                        ssl_ctx_[i].use_certificate_file(
317
                                client_config_.client_cert_path, boost::asio::ssl::context_base::pem, ec);
4✔
318
                        if (ec) {
4✔
319
                                return error::Error(
320
                                        ec.default_error_condition(), "Could not load client certificate");
2✔
321
                        }
322
                        auto exp_key = crypto::PrivateKey::Load(
323
                                {client_config_.client_cert_key_path, "", client_config_.ssl_engine});
6✔
324
                        if (!exp_key) {
3✔
325
                                return exp_key.error().WithContext(
326
                                        "Error loading private key from " + client_config_.client_cert_key_path);
2✔
327
                        }
328

329
                        const int ret =
330
                                SSL_CTX_use_PrivateKey(ssl_ctx_[i].native_handle(), exp_key.value()->Get());
2✔
331
                        if (ret != 1) {
2✔
332
                                return MakeError(
333
                                        HTTPInitError,
334
                                        "Failed to add the PrivateKey: " + client_config_.client_cert_key_path
×
335
                                                + " to the SSL CTX");
×
336
                        }
337
                } else if (
338
                        client_config_.client_cert_path != "" or client_config_.client_cert_key_path != "") {
418✔
339
                        return error::Error(
340
                                make_error_condition(errc::invalid_argument),
4✔
341
                                "Cannot set only one of client certificate, and client certificate private key");
4✔
342
                }
343

344
                bool cert_loaded = true;
345
                ssl_ctx_[i].set_default_verify_paths(ec); // Load the default CAs
418✔
346
                if (ec) {
418✔
347
                        auto err = error::Error(
348
                                ec.default_error_condition(), "Failed to load the SSL default directory");
×
349
                        if (client_config_.server_cert_path == "") {
×
350
                                // We aren't going to have any valid certificates then.
351
                                return err;
×
352
                        } else {
353
                                // We have a dedicated certificate, so this is not fatal.
354
                                log::Info(err.String());
×
355
                                cert_loaded = false;
356
                        }
357
                }
358
                if (client_config_.server_cert_path != "") {
418✔
359
                        ssl_ctx_[i].load_verify_file(client_config_.server_cert_path, ec);
52✔
360
                        if (ec) {
52✔
361
                                log::Warning("Failed to load the server certificate! Falling back to the CA store");
4✔
362
                                if (!cert_loaded) {
2✔
363
                                        return error::Error(
NEW
364
                                                ec.default_error_condition(),
×
NEW
365
                                                "Failed to load SSL default directory and server certificate");
×
366
                                }
367
                        }
368
                }
369
        }
370

371
        initialized_ = true;
209✔
372

373
        return error::NoError;
209✔
374
}
375

376
// Create the HOST header according to:
377
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23
378
// In short: Add the port-number if it is non-standard HTTP
379
static string CreateHOSTAddress(OutgoingRequestPtr req) {
272✔
380
        if (req->GetPort() == 80 || req->GetPort() == 443) {
272✔
381
                return req->GetHost();
4✔
382
        }
383
        return req->GetHost() + ":" + to_string(req->GetPort());
536✔
384
}
385

386
error::Error Client::AsyncCall(
284✔
387
        OutgoingRequestPtr req, ResponseHandler header_handler, ResponseHandler body_handler) {
388
        auto err = Initialize();
284✔
389
        if (err != error::NoError) {
284✔
390
                return err;
4✔
391
        }
392

393
        if (!*cancelled_ && status_ != TransactionStatus::Done) {
280✔
394
                return error::Error(
395
                        make_error_condition(errc::operation_in_progress), "HTTP call already ongoing");
×
396
        }
397

398
        if (req->address_.protocol == "" || req->address_.host == "" || req->address_.port < 0) {
280✔
399
                return error::MakeError(error::ProgrammingError, "Request is not ready");
4✔
400
        }
401

402
        if (!header_handler || !body_handler) {
278✔
403
                return error::MakeError(
404
                        error::ProgrammingError, "header_handler and body_handler can not be nullptr");
2✔
405
        }
406

407
        if (req->address_.protocol != "http" && req->address_.protocol != "https") {
277✔
408
                return error::Error(
409
                        make_error_condition(errc::protocol_not_supported), req->address_.protocol);
2✔
410
        }
411

412
        logger_ = log::Logger(logger_name_).WithFields(log::LogField("url", req->orig_address_));
276✔
413

414
        request_ = req;
415

416
        err = HandleProxySetup();
276✔
417
        if (err != error::NoError) {
276✔
418
                return err;
4✔
419
        }
420

421
        // NOTE: The AWS loadbalancer requires that the HOST header always be set, in order for the
422
        // request to route to our k8s cluster. Set this in all cases.
423
        const string header_url = CreateHOSTAddress(req);
544✔
424
        req->SetHeader("HOST", header_url);
544✔
425

426
        log::Trace("Setting HOST address: " + header_url);
272✔
427

428
        header_handler_ = header_handler;
272✔
429
        body_handler_ = body_handler;
272✔
430
        status_ = TransactionStatus::None;
272✔
431

432
        cancelled_ = make_shared<bool>(false);
272✔
433

434
        auto &cancelled = cancelled_;
435

436
        resolver_.async_resolve(
544✔
437
                request_->address_.host,
438
                to_string(request_->address_.port),
544✔
439
                [this, cancelled](
540✔
440
                        const error_code &ec, const asio::ip::tcp::resolver::results_type &results) {
267✔
441
                        if (!*cancelled) {
268✔
442
                                ResolveHandler(ec, results);
267✔
443
                        }
444
                });
268✔
445

446
        return error::NoError;
272✔
447
}
448

449
static inline error::Error AddProxyAuthHeader(OutgoingRequest &req, BrokenDownUrl &proxy_address) {
22✔
450
        if (proxy_address.username == "") {
22✔
451
                // nothing to do
452
                return error::NoError;
19✔
453
        }
454
        auto ex_dec_username = URLDecode(proxy_address.username);
3✔
455
        auto ex_dec_password = URLDecode(proxy_address.password);
3✔
456
        if (!ex_dec_username) {
3✔
457
                return ex_dec_username.error();
×
458
        }
459
        if (!ex_dec_password) {
3✔
460
                return ex_dec_password.error();
×
461
        }
462
        auto creds = ex_dec_username.value() + ":" + ex_dec_password.value();
3✔
463
        auto ex_encoded_creds = crypto::EncodeBase64(common::ByteVectorFromString(creds));
6✔
464
        if (!ex_encoded_creds) {
3✔
465
                return ex_encoded_creds.error();
×
466
        }
467
        req.SetHeader("Proxy-Authorization", "Basic " + ex_encoded_creds.value());
6✔
468
        log::Warning(
3✔
469
                "Avoid using basic authentication if possible, and make sure if it's used, it's through HTTPS");
6✔
470

471
        return error::NoError;
3✔
472
}
473

474
error::Error Client::HandleProxySetup() {
276✔
475
        secondary_req_.reset();
276✔
476

477
        if (request_->address_.protocol == "http") {
276✔
478
                socket_mode_ = SocketMode::Plain;
250✔
479

480
                if (http_proxy_ != "" && !HostNameMatchesNoProxy(request_->address_.host, no_proxy_)) {
250✔
481
                        // Make a modified proxy request.
482
                        BrokenDownUrl proxy_address;
20✔
483
                        auto err = BreakDownUrl(http_proxy_, proxy_address, true);
11✔
484
                        if (err != error::NoError) {
11✔
485
                                return err.WithContext("HTTP proxy URL is invalid");
2✔
486
                        }
487
                        if (proxy_address.path != "" && proxy_address.path != "/") {
10✔
488
                                return MakeError(
489
                                        InvalidUrlError, "A URL with a path is not legal for a proxy address");
2✔
490
                        }
491

492
                        request_->address_.path = request_->address_.protocol + "://" + request_->address_.host
18✔
493
                                                                          + ":" + to_string(request_->address_.port)
27✔
494
                                                                          + request_->address_.path;
27✔
495
                        request_->address_.host = proxy_address.host;
9✔
496
                        request_->address_.port = proxy_address.port;
9✔
497
                        request_->address_.protocol = proxy_address.protocol;
9✔
498

499
                        err = AddProxyAuthHeader(*request_, proxy_address);
9✔
500
                        if (err != error::NoError) {
9✔
501
                                return err;
×
502
                        }
503

504
                        if (proxy_address.protocol == "https") {
9✔
505
                                socket_mode_ = SocketMode::Tls;
5✔
506
                        } else if (proxy_address.protocol == "http") {
4✔
507
                                socket_mode_ = SocketMode::Plain;
4✔
508
                        } else {
509
                                // Should never get here.
510
                                assert(false);
511
                        }
512
                }
513
        } else if (request_->address_.protocol == "https") {
26✔
514
                socket_mode_ = SocketMode::Tls;
26✔
515

516
                if (https_proxy_ != "" && !HostNameMatchesNoProxy(request_->address_.host, no_proxy_)) {
26✔
517
                        // Save the original request for later, so that we can make a new request
518
                        // over the channel established by CONNECT.
519
                        secondary_req_ = std::move(request_);
520

521
                        request_ = make_shared<OutgoingRequest>();
30✔
522
                        request_->SetMethod(Method::CONNECT);
15✔
523
                        BrokenDownUrl proxy_address;
28✔
524
                        auto err = BreakDownUrl(https_proxy_, proxy_address, true);
15✔
525
                        if (err != error::NoError) {
15✔
526
                                return err.WithContext("HTTPS proxy URL is invalid");
2✔
527
                        }
528
                        if (proxy_address.path != "" && proxy_address.path != "/") {
14✔
529
                                return MakeError(
530
                                        InvalidUrlError, "A URL with a path is not legal for a proxy address");
2✔
531
                        }
532

533
                        request_->address_.path =
534
                                secondary_req_->address_.host + ":" + to_string(secondary_req_->address_.port);
26✔
535
                        request_->address_.host = proxy_address.host;
13✔
536
                        request_->address_.port = proxy_address.port;
13✔
537
                        request_->address_.protocol = proxy_address.protocol;
13✔
538

539
                        err = AddProxyAuthHeader(*request_, proxy_address);
13✔
540
                        if (err != error::NoError) {
13✔
541
                                return err;
×
542
                        }
543

544
                        if (proxy_address.protocol == "https") {
13✔
545
                                socket_mode_ = SocketMode::Tls;
7✔
546
                        } else if (proxy_address.protocol == "http") {
6✔
547
                                socket_mode_ = SocketMode::Plain;
6✔
548
                        } else {
549
                                // Should never get here.
550
                                assert(false);
551
                        }
552
                }
553
        } else {
554
                // Should never get here
555
                assert(false);
556
        }
557

558
        return error::NoError;
272✔
559
}
560

561
io::ExpectedAsyncReaderPtr Client::MakeBodyAsyncReader(IncomingResponsePtr resp) {
172✔
562
        if (status_ != TransactionStatus::HeaderHandlerCalled) {
172✔
563
                return expected::unexpected(error::Error(
2✔
564
                        make_error_condition(errc::operation_in_progress),
4✔
565
                        "MakeBodyAsyncReader called while reading is in progress"));
6✔
566
        }
567

568
        if (GetContentLength(*response_data_.http_response_parser_) == 0
170✔
569
                && !response_data_.http_response_parser_->chunked()) {
170✔
570
                return expected::unexpected(
17✔
571
                        MakeError(BodyMissingError, "Response does not contain a body"));
51✔
572
        }
573

574
        status_ = TransactionStatus::ReaderCreated;
153✔
575
        return make_shared<BodyAsyncReader<Client>>(resp->client_.GetHttpClient(), resp->cancelled_);
306✔
576
}
577

578
io::ExpectedAsyncReadWriterPtr Client::SwitchProtocol(IncomingResponsePtr req) {
7✔
579
        if (*cancelled_) {
7✔
580
                return expected::unexpected(error::Error(
×
581
                        make_error_condition(errc::not_connected),
×
582
                        "Cannot switch protocols if endpoint is not connected"));
×
583
        }
584

585
        // Rest of the connection is done directly on the socket, we are done here.
586
        status_ = TransactionStatus::Done;
7✔
587
        *cancelled_ = true;
7✔
588
        cancelled_ = make_shared<bool>(false);
14✔
589

590
        auto stream = stream_;
591
        // This no longer belongs to us.
592
        stream_.reset();
7✔
593

594
        switch (socket_mode_) {
7✔
595
        case SocketMode::TlsTls:
×
596
                return make_shared<RawSocket<ssl::stream<ssl::stream<tcp::socket>>>>(
×
597
                        stream, response_data_.response_buffer_);
×
598
        case SocketMode::Tls:
×
599
                return make_shared<RawSocket<ssl::stream<tcp::socket>>>(
×
600
                        make_shared<ssl::stream<tcp::socket>>(std::move(stream->next_layer())),
×
601
                        response_data_.response_buffer_);
×
602
        case SocketMode::Plain:
7✔
603
                return make_shared<RawSocket<tcp::socket>>(
7✔
604
                        make_shared<tcp::socket>(std::move(stream->next_layer().next_layer())),
14✔
605
                        response_data_.response_buffer_);
7✔
606
        }
607

608
        AssertOrReturnUnexpected(false);
×
609
}
610

611
void Client::CallHandler(ResponseHandler handler) {
356✔
612
        // This function exists to make sure we have a copy of the handler we're calling (in the
613
        // argument list). This is important in case the handler owns the client instance through a
614
        // capture, and it replaces the handler with a different one (using `AsyncCall`). If it
615
        // does, then it destroys the final copy of the handler, and therefore also the client,
616
        // which is why we need to make a copy here, before calling it.
617
        handler(response_);
356✔
618
}
356✔
619

620
void Client::CallErrorHandler(
83✔
621
        const error_code &ec, const OutgoingRequestPtr &req, ResponseHandler handler) {
622
        CallErrorHandler(error::Error(ec.default_error_condition(), ""), req, handler);
249✔
623
}
83✔
624

625
void Client::CallErrorHandler(
125✔
626
        const error::Error &err, const OutgoingRequestPtr &req, ResponseHandler handler) {
627
        status_ = TransactionStatus::Done;
125✔
628
        DoCancel();
125✔
629
        handler(expected::unexpected(
250✔
630
                err.WithContext(MethodToString(req->method_) + " " + req->orig_address_)));
500✔
631
}
125✔
632

633
void Client::ResolveHandler(
267✔
634
        const error_code &ec, const asio::ip::tcp::resolver::results_type &results) {
635
        if (ec) {
267✔
636
                CallErrorHandler(ec, request_, header_handler_);
×
637
                return;
×
638
        }
639

640
        if (logger_.Level() >= log::LogLevel::Debug) {
267✔
641
                string ips = "[";
245✔
642
                string sep;
643
                for (auto r : results) {
1,028✔
644
                        ips += sep;
269✔
645
                        ips += r.endpoint().address().to_string();
269✔
646
                        sep = ", ";
269✔
647
                }
648
                ips += "]";
245✔
649
                logger_.Debug("Hostname " + request_->address_.host + " resolved to " + ips);
490✔
650
        }
651

652
        resolver_results_ = results;
653

654
        stream_ = make_shared<ssl::stream<ssl::stream<tcp::socket>>>(
267✔
655
                ssl::stream<tcp::socket>(GetAsioIoContext(event_loop_), ssl_ctx_[0]), ssl_ctx_[1]);
534✔
656

657
        if (!response_data_.response_buffer_) {
267✔
658
                // We can reuse this if preexisting.
659
                response_data_.response_buffer_ = make_shared<beast::flat_buffer>();
396✔
660

661
                // This is equivalent to:
662
                //   response_data_.response_buffer_.reserve(body_buffer_.size());
663
                // but compatible with Boost 1.67.
664
                response_data_.response_buffer_->prepare(
665
                        body_buffer_.size() - response_data_.response_buffer_->size());
198✔
666
        }
667

668
        auto &cancelled = cancelled_;
669

670
        asio::async_connect(
267✔
671
                stream_->lowest_layer(),
672
                resolver_results_,
267✔
673
                [this, cancelled](const error_code &ec, const asio::ip::tcp::endpoint &endpoint) {
534✔
674
                        if (!*cancelled) {
267✔
675
                                switch (socket_mode_) {
267✔
676
                                case SocketMode::TlsTls:
×
677
                                        // Should never happen because we always need to handshake
678
                                        // the innermost Tls first, then the outermost, but the
679
                                        // latter doesn't happen here.
680
                                        assert(false);
681
                                        CallErrorHandler(
×
682
                                                error::MakeError(
×
683
                                                        error::ProgrammingError, "TlsTls mode is invalid in ResolveHandler"),
×
684
                                                request_,
×
685
                                                header_handler_);
×
686
                                case SocketMode::Tls:
21✔
687
                                        return HandshakeHandler(stream_->next_layer(), ec, endpoint);
21✔
688
                                case SocketMode::Plain:
246✔
689
                                        return ConnectHandler(ec, endpoint);
246✔
690
                                }
691
                        }
692
                });
693
}
694

695
template <typename StreamType>
696
void Client::HandshakeHandler(
25✔
697
        StreamType &stream, const error_code &ec, const asio::ip::tcp::endpoint &endpoint) {
698
        if (ec) {
25✔
699
                CallErrorHandler(ec, request_, header_handler_);
2✔
700
                return;
2✔
701
        }
702

703
        // Enable TCP keepalive
704
        boost::asio::socket_base::keep_alive option(true);
705
        stream_->lowest_layer().set_option(option);
23✔
706

707
        // We can't avoid a C style cast on this next line. The usual method by which system headers
708
        // are excluded from warnings doesn't work, because `SSL_set_tlsext_host_name` is a macro,
709
        // containing a cast, which expands here, not in the original file. So just disable the
710
        // warning here.
711
#ifdef __clang__
712
#pragma clang diagnostic push
713
#pragma clang diagnostic ignored "-Wold-style-cast"
714
#else
715
#pragma GCC diagnostic push
716
#pragma GCC diagnostic ignored "-Wold-style-cast"
717
#endif
718
        // Set SNI Hostname (many hosts need this to handshake successfully)
719
        if (!SSL_set_tlsext_host_name(stream.native_handle(), request_->address_.host.c_str())) {
23✔
720
#ifdef __clang__
721
#pragma clang diagnostic pop
722
#else
723
#pragma GCC diagnostic pop
724
#endif
725
                beast::error_code ec2 {
×
726
                        static_cast<int>(::ERR_get_error()), asio::error::get_ssl_category()};
×
727
                logger_.Error("Failed to set SNI host name: " + ec2.message());
×
728
        }
729

730
        // Enable host name verification (not done automatically and we don't have
731
        // enough access to the TLS internals to use X509_VERIFY_PARAM_set1_host(),
732
        // hence the callback that boost provides).
733
        boost::system::error_code b_ec;
23✔
734
        stream.set_verify_callback(ssl::host_name_verification(request_->address_.host), b_ec);
46✔
735
        if (b_ec) {
23✔
736
                logger_.Error("Failed to enable host name verification: " + b_ec.message());
×
737
                CallErrorHandler(b_ec, request_, header_handler_);
×
738
                return;
×
739
        }
740

741
        auto &cancelled = cancelled_;
742

743
        stream.async_handshake(
46✔
744
                ssl::stream_base::client, [this, cancelled, endpoint](const error_code &ec) {
23✔
745
                        if (*cancelled) {
26✔
746
                                return;
747
                        }
748
                        if (ec) {
26✔
749
                                logger_.Error("https: Failed to perform the SSL handshake: " + ec.message());
20✔
750
                                CallErrorHandler(ec, request_, header_handler_);
10✔
751
                                return;
10✔
752
                        }
753
                        logger_.Debug("https: Successful SSL handshake");
32✔
754
                        ConnectHandler(ec, endpoint);
16✔
755
                });
756
}
757

758

759
void Client::ConnectHandler(const error_code &ec, const asio::ip::tcp::endpoint &endpoint) {
262✔
760
        if (ec) {
262✔
761
                CallErrorHandler(ec, request_, header_handler_);
16✔
762
                return;
16✔
763
        }
764

765
        // Enable TCP keepalive
766
        boost::asio::socket_base::keep_alive option(true);
767
        stream_->lowest_layer().set_option(option);
246✔
768

769
        logger_.Debug("Connected to " + endpoint.address().to_string());
492✔
770

771
        request_data_.http_request_ = make_shared<http::request<http::buffer_body>>(
246✔
772
                MethodToBeastVerb(request_->method_), request_->address_.path, BeastHttpVersion);
492✔
773

774
        for (const auto &header : request_->headers_) {
646✔
775
                request_data_.http_request_->set(header.first, header.second);
400✔
776
        }
777

778
        request_data_.http_request_serializer_ =
779
                make_shared<http::request_serializer<http::buffer_body>>(*request_data_.http_request_);
246✔
780

781
        response_data_.http_response_parser_ = make_shared<http::response_parser<http::buffer_body>>();
492✔
782

783
        // Don't enforce limits. Since we stream everything, limits don't generally apply, and
784
        // if they do, they should be handled higher up in the application logic.
785
        //
786
        // Note: There is a bug in Beast here (tested on 1.74): One is supposed to be able to
787
        // pass an uninitialized `optional` to mean unlimited, but they do not check for
788
        // `has_value()` in their code, causing their subsequent comparison operation to
789
        // misbehave. So pass highest possible value instead.
790
        response_data_.http_response_parser_->body_limit(numeric_limits<uint64_t>::max());
791

792
        auto &cancelled = cancelled_;
793
        auto &request_data = request_data_;
246✔
794

795
        auto handler = [this, cancelled, request_data](const error_code &ec, size_t num_written) {
246✔
796
                if (!*cancelled) {
246✔
797
                        WriteHeaderHandler(ec, num_written);
246✔
798
                }
799
        };
492✔
800

801
        switch (socket_mode_) {
246✔
802
        case SocketMode::TlsTls:
2✔
803
                http::async_write_header(*stream_, *request_data_.http_request_serializer_, handler);
2✔
804
                break;
805
        case SocketMode::Tls:
14✔
806
                http::async_write_header(
14✔
807
                        stream_->next_layer(), *request_data_.http_request_serializer_, handler);
808
                break;
809
        case SocketMode::Plain:
230✔
810
                http::async_write_header(
230✔
811
                        stream_->next_layer().next_layer(), *request_data_.http_request_serializer_, handler);
812
                break;
813
        }
814
}
815

816
void Client::WriteHeaderHandler(const error_code &ec, size_t num_written) {
246✔
817
        if (num_written > 0) {
246✔
818
                logger_.Trace("Wrote " + to_string(num_written) + " bytes of header data to stream.");
492✔
819
        }
820

821
        if (ec) {
246✔
822
                CallErrorHandler(ec, request_, header_handler_);
×
823
                return;
206✔
824
        }
825

826
        auto exp_has_body =
827
                HasBody(request_->GetHeader("Content-Length"), request_->GetHeader("Transfer-Encoding"));
492✔
828
        if (!exp_has_body) {
246✔
829
                CallErrorHandler(exp_has_body.error(), request_, header_handler_);
×
830
                return;
×
831
        }
832
        if (!exp_has_body.value()) {
246✔
833
                ReadHeader();
205✔
834
                return;
835
        }
836

837
        if (!request_->body_gen_ && !request_->async_body_gen_) {
41✔
838
                auto err = MakeError(BodyMissingError, "No body generator");
2✔
839
                CallErrorHandler(err, request_, header_handler_);
2✔
840
                return;
841
        }
842

843
        assert(!(request_->body_gen_ && request_->async_body_gen_));
844

845
        if (request_->body_gen_) {
40✔
846
                auto body_reader = request_->body_gen_();
34✔
847
                if (!body_reader) {
34✔
848
                        CallErrorHandler(body_reader.error(), request_, header_handler_);
×
849
                        return;
850
                }
851
                request_->body_reader_ = body_reader.value();
34✔
852
        } else {
853
                auto body_reader = request_->async_body_gen_();
6✔
854
                if (!body_reader) {
6✔
855
                        CallErrorHandler(body_reader.error(), request_, header_handler_);
×
856
                        return;
857
                }
858
                request_->async_body_reader_ = body_reader.value();
6✔
859
        }
860

861
        PrepareAndWriteNewBodyBuffer();
40✔
862
}
863

864
void Client::WriteBodyHandler(const error_code &ec, size_t num_written) {
2,278✔
865
        if (num_written > 0) {
2,278✔
866
                logger_.Trace("Wrote " + to_string(num_written) + " bytes of body data to stream.");
2,242✔
867
        }
868

869
        if (ec == http::make_error_code(http::error::need_buffer)) {
2,278✔
870
                // Write next block of the body.
871
                PrepareAndWriteNewBodyBuffer();
1,120✔
872
        } else if (ec) {
1,158✔
873
                CallErrorHandler(ec, request_, header_handler_);
8✔
874
        } else if (num_written > 0) {
1,154✔
875
                // We are still writing the body.
876
                WriteBody();
1,121✔
877
        } else {
878
                // We are ready to receive the response.
879
                ReadHeader();
33✔
880
        }
881
}
2,278✔
882

883
void Client::PrepareAndWriteNewBodyBuffer() {
1,160✔
884
        // request_->body_reader_ XOR request_->async_body_reader_
885
        assert(
886
                (request_->body_reader_ || request_->async_body_reader_)
887
                && !(request_->body_reader_ && request_->async_body_reader_));
888

889
        auto cancelled = cancelled_;
890
        auto read_handler = [this, cancelled](io::ExpectedSize read) {
3,592✔
891
                if (!*cancelled) {
1,160✔
892
                        if (!read) {
1,159✔
893
                                CallErrorHandler(read.error(), request_, header_handler_);
2✔
894
                                return;
2✔
895
                        }
896
                        WriteNewBodyBuffer(read.value());
1,157✔
897
                }
898
        };
1,160✔
899

900

901
        if (request_->body_reader_) {
1,160✔
902
                read_handler(request_->body_reader_->Read(body_buffer_.begin(), body_buffer_.end()));
1,470✔
903
        } else {
904
                auto err = request_->async_body_reader_->AsyncRead(
905
                        body_buffer_.begin(), body_buffer_.end(), read_handler);
850✔
906
                if (err != error::NoError) {
425✔
907
                        CallErrorHandler(err, request_, header_handler_);
×
908
                }
909
        }
910
}
1,160✔
911

912
void Client::WriteNewBodyBuffer(size_t size) {
1,157✔
913
        request_data_.http_request_->body().data = body_buffer_.data();
1,157✔
914
        request_data_.http_request_->body().size = size;
1,157✔
915

916
        if (size > 0) {
1,157✔
917
                request_data_.http_request_->body().more = true;
1,124✔
918
        } else {
919
                // Release ownership of Body reader.
920
                request_->body_reader_.reset();
33✔
921
                request_->async_body_reader_.reset();
33✔
922
                request_data_.http_request_->body().more = false;
33✔
923
        }
924

925
        WriteBody();
1,157✔
926
}
1,157✔
927

928
void Client::WriteBody() {
2,278✔
929
        auto &cancelled = cancelled_;
930
        auto &request_data = request_data_;
2,278✔
931

932
        auto handler = [this, cancelled, request_data](const error_code &ec, size_t num_written) {
2,278✔
933
                if (!*cancelled) {
2,278✔
934
                        WriteBodyHandler(ec, num_written);
2,278✔
935
                }
936
        };
4,556✔
937

938
        switch (socket_mode_) {
2,278✔
939
        case SocketMode::TlsTls:
×
940
                http::async_write_some(*stream_, *request_data_.http_request_serializer_, handler);
941
                break;
942
        case SocketMode::Tls:
×
943
                http::async_write_some(
944
                        stream_->next_layer(), *request_data_.http_request_serializer_, handler);
945
                break;
946
        case SocketMode::Plain:
2,278✔
947
                http::async_write_some(
948
                        stream_->next_layer().next_layer(), *request_data_.http_request_serializer_, handler);
949
                break;
950
        }
951
}
2,278✔
952

953
void Client::ReadHeader() {
238✔
954
        auto &cancelled = cancelled_;
955
        auto &response_data = response_data_;
238✔
956

957
        auto handler = [this, cancelled, response_data](const error_code &ec, size_t num_read) {
235✔
958
                if (!*cancelled) {
235✔
959
                        ReadHeaderHandler(ec, num_read);
235✔
960
                }
961
        };
476✔
962

963
        switch (socket_mode_) {
238✔
964
        case SocketMode::TlsTls:
2✔
965
                http::async_read_some(
2✔
966
                        *stream_,
967
                        *response_data_.response_buffer_,
968
                        *response_data_.http_response_parser_,
969
                        handler);
970
                break;
971
        case SocketMode::Tls:
14✔
972
                http::async_read_some(
14✔
973
                        stream_->next_layer(),
974
                        *response_data_.response_buffer_,
975
                        *response_data_.http_response_parser_,
976
                        handler);
977
                break;
978
        case SocketMode::Plain:
222✔
979
                http::async_read_some(
222✔
980
                        stream_->next_layer().next_layer(),
981
                        *response_data_.response_buffer_,
982
                        *response_data_.http_response_parser_,
983
                        handler);
984
                break;
985
        }
986
}
238✔
987

988
void Client::ReadHeaderHandler(const error_code &ec, size_t num_read) {
235✔
989
        if (num_read > 0) {
235✔
990
                logger_.Trace("Read " + to_string(num_read) + " bytes of header data from stream.");
460✔
991
        }
992

993
        if (ec) {
235✔
994
                CallErrorHandler(ec, request_, header_handler_);
5✔
995
                return;
65✔
996
        }
997

998
        if (!response_data_.http_response_parser_->is_header_done()) {
230✔
999
                ReadHeader();
×
1000
                return;
×
1001
        }
1002

1003
        if (secondary_req_) {
230✔
1004
                HandleSecondaryRequest();
9✔
1005
                return;
9✔
1006
        }
1007

1008
        response_.reset(new IncomingResponse(*this, cancelled_));
442✔
1009
        response_->status_code_ = response_data_.http_response_parser_->get().result_int();
221✔
1010
        response_->status_message_ = string {response_data_.http_response_parser_->get().reason()};
221✔
1011

1012
        logger_.Debug(
442✔
1013
                "Received response: " + to_string(response_->status_code_) + " "
442✔
1014
                + response_->status_message_);
663✔
1015

1016
        string debug_str;
1017
        for (auto header = response_data_.http_response_parser_->get().cbegin();
255✔
1018
                 header != response_data_.http_response_parser_->get().cend();
476✔
1019
                 header++) {
1020
                response_->headers_[string {header->name_string()}] = string {header->value()};
765✔
1021
                if (logger_.Level() >= log::LogLevel::Debug) {
255✔
1022
                        debug_str += string {header->name_string()};
240✔
1023
                        debug_str += ": ";
240✔
1024
                        debug_str += string {header->value()};
240✔
1025
                        debug_str += "\n";
240✔
1026
                }
1027
        }
1028

1029
        logger_.Debug("Received headers:\n" + debug_str);
442✔
1030
        debug_str.clear();
1031

1032
        if (GetContentLength(*response_data_.http_response_parser_) == 0
221✔
1033
                && !response_data_.http_response_parser_->chunked()) {
221✔
1034
                auto cancelled = cancelled_;
1035
                status_ = TransactionStatus::HeaderHandlerCalled;
48✔
1036
                CallHandler(header_handler_);
96✔
1037
                if (!*cancelled) {
48✔
1038
                        status_ = TransactionStatus::Done;
43✔
1039
                        if (response_->status_code_ != StatusCode::StatusSwitchingProtocols) {
43✔
1040
                                // Make an exception for 101 Switching Protocols response, where the TCP connection
1041
                                // is meant to be reused.
1042
                                DoCancel();
39✔
1043
                        }
1044
                        CallHandler(body_handler_);
86✔
1045
                }
1046
                return;
1047
        }
1048

1049
        auto cancelled = cancelled_;
1050
        status_ = TransactionStatus::HeaderHandlerCalled;
173✔
1051
        CallHandler(header_handler_);
346✔
1052
        if (*cancelled) {
173✔
1053
                return;
1054
        }
1055

1056
        // We know that a body reader is required here, because of the check for body above.
1057
        if (status_ == TransactionStatus::HeaderHandlerCalled) {
170✔
1058
                CallErrorHandler(MakeError(BodyIgnoredError, ""), request_, body_handler_);
36✔
1059
        }
1060
}
1061

1062
void Client::HandleSecondaryRequest() {
9✔
1063
        logger_.Debug(
18✔
1064
                "Received proxy response: "
1065
                + to_string(response_data_.http_response_parser_->get().result_int()) + " "
18✔
1066
                + string {response_data_.http_response_parser_->get().reason()});
36✔
1067

1068
        request_ = std::move(secondary_req_);
1069

1070
        if (response_data_.http_response_parser_->get().result_int() != StatusOK) {
9✔
1071
                auto err = MakeError(
1072
                        ProxyError,
1073
                        "Proxy returned unexpected response: "
1074
                                + to_string(response_data_.http_response_parser_->get().result_int()) + " "
4✔
1075
                                + string {response_data_.http_response_parser_->get().reason()});
6✔
1076
                CallErrorHandler(err, request_, header_handler_);
4✔
1077
                return;
1078
        }
1079

1080
        if (GetContentLength(*response_data_.http_response_parser_) != 0
7✔
1081
                || response_data_.http_response_parser_->chunked()) {
7✔
1082
                auto err = MakeError(ProxyError, "Body not allowed in proxy response");
×
1083
                CallErrorHandler(err, request_, header_handler_);
×
1084
                return;
1085
        }
1086

1087
        // We are connected. Now repeat the request cycle with the original request. Pretend
1088
        // we were just connected.
1089

1090
        assert(request_->GetProtocol() == "https");
1091

1092
        // Make sure that no data is "lost" inside the buffering mechanism, since when switching to
1093
        // a different layer, this will get out of sync.
1094
        assert(response_data_.response_buffer_->size() == 0);
1095

1096
        switch (socket_mode_) {
7✔
1097
        case SocketMode::TlsTls:
×
1098
                // Should never get here, because this is the only place where TlsTls mode
1099
                // is supposed to be turned on.
1100
                assert(false);
1101
                CallErrorHandler(
×
1102
                        error::MakeError(
×
1103
                                error::ProgrammingError,
1104
                                "Any other mode than Tls is not valid when handling secondary request"),
×
1105
                        request_,
×
1106
                        header_handler_);
×
1107
                break;
×
1108
        case SocketMode::Tls:
3✔
1109
                // Upgrade to TLS inside TLS.
1110
                socket_mode_ = SocketMode::TlsTls;
3✔
1111
                HandshakeHandler(*stream_, error_code {}, stream_->lowest_layer().remote_endpoint());
3✔
1112
                break;
3✔
1113
        case SocketMode::Plain:
4✔
1114
                // Upgrade to TLS.
1115
                socket_mode_ = SocketMode::Tls;
4✔
1116
                HandshakeHandler(
4✔
1117
                        stream_->next_layer(), error_code {}, stream_->lowest_layer().remote_endpoint());
4✔
1118
                break;
4✔
1119
        }
1120
}
1121

1122
void Client::AsyncReadNextBodyPart(
4,201✔
1123
        vector<uint8_t>::iterator start, vector<uint8_t>::iterator end, io::AsyncIoHandler handler) {
1124
        assert(AtLeast(status_, TransactionStatus::ReaderCreated));
1125

1126
        if (status_ == TransactionStatus::ReaderCreated) {
4,201✔
1127
                status_ = TransactionStatus::BodyReadingInProgress;
151✔
1128
        }
1129

1130
        if (AtLeast(status_, TransactionStatus::BodyReadingFinished)) {
4,201✔
1131
                auto cancelled = cancelled_;
1132
                handler(0);
184✔
1133
                if (!*cancelled && status_ == TransactionStatus::BodyReadingFinished) {
92✔
1134
                        status_ = TransactionStatus::Done;
92✔
1135
                        DoCancel();
92✔
1136
                        CallHandler(body_handler_);
184✔
1137
                }
1138
                return;
1139
        }
1140

1141
        reader_buf_start_ = start;
4,109✔
1142
        reader_buf_end_ = end;
4,109✔
1143
        reader_handler_ = handler;
4,109✔
1144
        size_t read_size = end - start;
4,109✔
1145
        size_t smallest = min(body_buffer_.size(), read_size);
6,222✔
1146

1147
        response_data_.http_response_parser_->get().body().data = body_buffer_.data();
4,109✔
1148
        response_data_.http_response_parser_->get().body().size = smallest;
4,109✔
1149
        response_data_.last_buffer_size_ = smallest;
4,109✔
1150

1151
        auto &cancelled = cancelled_;
1152
        auto &response_data = response_data_;
4,109✔
1153

1154
        auto async_handler = [this, cancelled, response_data](const error_code &ec, size_t num_read) {
4,108✔
1155
                if (!*cancelled) {
4,108✔
1156
                        ReadBodyHandler(ec, num_read);
4,108✔
1157
                }
1158
        };
8,218✔
1159

1160
        switch (socket_mode_) {
4,109✔
1161
        case SocketMode::TlsTls:
2✔
1162
                http::async_read_some(
2✔
1163
                        *stream_,
1164
                        *response_data_.response_buffer_,
1165
                        *response_data_.http_response_parser_,
1166
                        async_handler);
1167
                break;
1168
        case SocketMode::Tls:
4✔
1169
                http::async_read_some(
4✔
1170
                        stream_->next_layer(),
1171
                        *response_data_.response_buffer_,
1172
                        *response_data_.http_response_parser_,
1173
                        async_handler);
1174
                break;
1175
        case SocketMode::Plain:
4,103✔
1176
                http::async_read_some(
4,103✔
1177
                        stream_->next_layer().next_layer(),
1178
                        *response_data_.response_buffer_,
1179
                        *response_data_.http_response_parser_,
1180
                        async_handler);
1181
                break;
1182
        }
1183
}
1184

1185
void Client::ReadBodyHandler(error_code ec, size_t num_read) {
4,108✔
1186
        if (num_read > 0) {
4,108✔
1187
                logger_.Trace("Read " + to_string(num_read) + " bytes of body data from stream.");
8,116✔
1188
        }
1189

1190
        if (ec == http::make_error_code(http::error::need_buffer)) {
4,108✔
1191
                // This can be ignored. We always reset the buffer between reads anyway.
1192
                ec = error_code();
1,958✔
1193
        }
1194

1195
        assert(reader_handler_);
1196

1197
        if (response_data_.http_response_parser_->is_done()) {
4,108✔
1198
                status_ = TransactionStatus::BodyReadingFinished;
98✔
1199
        }
1200

1201
        auto cancelled = cancelled_;
1202

1203
        if (ec) {
4,108✔
1204
                auto err = error::Error(ec.default_error_condition(), "Could not read body");
100✔
1205
                reader_handler_(expected::unexpected(err));
150✔
1206
                if (!*cancelled) {
50✔
1207
                        CallErrorHandler(ec, request_, body_handler_);
92✔
1208
                }
1209
                return;
1210
        }
1211

1212
        // The num_read from above includes out of band payload data, such as chunk headers, which
1213
        // we are not interested in. So we need to calculate the payload size from the remaining
1214
        // buffer space.
1215
        size_t payload_read =
1216
                response_data_.last_buffer_size_ - response_data_.http_response_parser_->get().body().size;
4,058✔
1217

1218
        size_t buf_size = reader_buf_end_ - reader_buf_start_;
4,058✔
1219
        size_t smallest = min(payload_read, buf_size);
4,058✔
1220

1221
        if (smallest == 0) {
4,058✔
1222
                // We read nothing, which can happen if all we read was a chunk header. We cannot
1223
                // return 0 to the handler however, because in `io::Reader` context this means
1224
                // EOF. So just repeat the request instead, until we get actual payload data.
1225
                AsyncReadNextBodyPart(reader_buf_start_, reader_buf_end_, reader_handler_);
154✔
1226
        } else {
1227
                copy_n(body_buffer_.begin(), smallest, reader_buf_start_);
3,981✔
1228
                reader_handler_(smallest);
7,962✔
1229
        }
1230
}
1231

1232
void Client::Cancel() {
203✔
1233
        auto cancelled = cancelled_;
1234

1235
        if (!*cancelled) {
203✔
1236
                auto err =
1237
                        error::Error(make_error_condition(errc::operation_canceled), "HTTP request cancelled");
40✔
1238
                switch (status_) {
20✔
1239
                case TransactionStatus::None:
3✔
1240
                        CallErrorHandler(err, request_, header_handler_);
3✔
1241
                        break;
3✔
1242
                case TransactionStatus::HeaderHandlerCalled:
16✔
1243
                case TransactionStatus::ReaderCreated:
1244
                case TransactionStatus::BodyReadingInProgress:
1245
                case TransactionStatus::BodyReadingFinished:
1246
                        CallErrorHandler(err, request_, body_handler_);
16✔
1247
                        break;
16✔
1248
                case TransactionStatus::Replying:
1249
                case TransactionStatus::SwitchingProtocol:
1250
                        // Not used by client.
1251
                        assert(false);
1252
                        break;
1253
                case TransactionStatus::BodyHandlerCalled:
1254
                case TransactionStatus::Done:
1255
                        break;
1256
                }
1257
        }
1258

1259
        if (!*cancelled) {
203✔
1260
                DoCancel();
1✔
1261
        }
1262
}
203✔
1263

1264
void Client::DoCancel() {
610✔
1265
        resolver_.cancel();
610✔
1266
        if (stream_) {
610✔
1267
                stream_->lowest_layer().cancel();
260✔
1268
                stream_->lowest_layer().close();
260✔
1269
                stream_.reset();
260✔
1270
        }
1271

1272
        // Reset logger to no connection.
1273
        logger_ = log::Logger(logger_name_);
610✔
1274

1275
        // Set cancel state and then make a new one. Those who are interested should have their own
1276
        // pointer to the old one.
1277
        *cancelled_ = true;
610✔
1278
        cancelled_ = make_shared<bool>(true);
610✔
1279
}
610✔
1280

1281
Stream::Stream(Server &server) :
441✔
1282
        server_ {server},
1283
        logger_ {"http"},
1284
        cancelled_(make_shared<bool>(true)),
441✔
1285
        socket_(server_.GetAsioIoContext(server_.event_loop_)),
441✔
1286
        body_buffer_(HTTP_BEAST_BUFFER_SIZE) {
1,323✔
1287
        request_data_.request_buffer_ = make_shared<beast::flat_buffer>();
882✔
1288

1289
        // This is equivalent to:
1290
        //   request_data_.request_buffer_.reserve(body_buffer_.size());
1291
        // but compatible with Boost 1.67.
1292
        request_data_.request_buffer_->prepare(
1293
                body_buffer_.size() - request_data_.request_buffer_->size());
441✔
1294

1295
        request_data_.http_request_parser_ = make_shared<http::request_parser<http::buffer_body>>();
882✔
1296

1297
        // Don't enforce limits. Since we stream everything, limits don't generally apply, and if
1298
        // they do, they should be handled higher up in the application logic.
1299
        //
1300
        // Note: There is a bug in Beast here (tested on 1.74): One is supposed to be able to pass
1301
        // an uninitialized `optional` to mean unlimited, but they do not check for `has_value()` in
1302
        // their code, causing their subsequent comparison operation to misbehave. So pass highest
1303
        // possible value instead.
1304
        request_data_.http_request_parser_->body_limit(numeric_limits<uint64_t>::max());
1305
}
441✔
1306

1307
Stream::~Stream() {
1,323✔
1308
        DoCancel();
441✔
1309
}
441✔
1310

1311
void Stream::Cancel() {
7✔
1312
        auto cancelled = cancelled_;
1313

1314
        if (!*cancelled) {
7✔
1315
                auto err =
1316
                        error::Error(make_error_condition(errc::operation_canceled), "HTTP response cancelled");
14✔
1317
                switch (status_) {
7✔
1318
                case TransactionStatus::None:
×
1319
                        CallErrorHandler(err, request_, server_.header_handler_);
×
1320
                        break;
×
1321
                case TransactionStatus::HeaderHandlerCalled:
5✔
1322
                case TransactionStatus::ReaderCreated:
1323
                case TransactionStatus::BodyReadingInProgress:
1324
                case TransactionStatus::BodyReadingFinished:
1325
                        CallErrorHandler(err, request_, server_.body_handler_);
5✔
1326
                        break;
5✔
1327
                case TransactionStatus::BodyHandlerCalled:
×
1328
                        // In between body handler and reply finished. No one to handle the status
1329
                        // here.
1330
                        server_.RemoveStream(shared_from_this());
×
1331
                        break;
×
1332
                case TransactionStatus::Replying:
1✔
1333
                        CallErrorHandler(err, request_, reply_finished_handler_);
3✔
1334
                        break;
1✔
1335
                case TransactionStatus::SwitchingProtocol:
1✔
1336
                        CallErrorHandler(err, request_, switch_protocol_handler_);
3✔
1337
                        break;
1✔
1338
                case TransactionStatus::Done:
1339
                        break;
1340
                }
1341
        }
1342

1343
        if (!*cancelled) {
7✔
1344
                DoCancel();
×
1345
        }
1346
}
7✔
1347

1348
void Stream::DoCancel() {
795✔
1349
        if (socket_.is_open()) {
795✔
1350
                socket_.cancel();
217✔
1351
                socket_.close();
217✔
1352
        }
1353

1354
        // Set cancel state and then make a new one. Those who are interested should have their own
1355
        // pointer to the old one.
1356
        *cancelled_ = true;
795✔
1357
        cancelled_ = make_shared<bool>(true);
795✔
1358
}
795✔
1359

1360
void Stream::CallErrorHandler(const error_code &ec, const RequestPtr &req, RequestHandler handler) {
×
1361
        CallErrorHandler(error::Error(ec.default_error_condition(), ""), req, handler);
×
1362
}
×
1363

1364
void Stream::CallErrorHandler(
×
1365
        const error::Error &err, const RequestPtr &req, RequestHandler handler) {
1366
        status_ = TransactionStatus::Done;
×
1367
        DoCancel();
×
1368
        handler(expected::unexpected(err.WithContext(
×
1369
                req->address_.host + ": " + MethodToString(req->method_) + " " + request_->GetPath())));
×
1370

1371
        server_.RemoveStream(shared_from_this());
×
1372
}
×
1373

1374
void Stream::CallErrorHandler(
2✔
1375
        const error_code &ec, const IncomingRequestPtr &req, IdentifiedRequestHandler handler) {
1376
        CallErrorHandler(error::Error(ec.default_error_condition(), ""), req, handler);
6✔
1377
}
2✔
1378

1379
void Stream::CallErrorHandler(
8✔
1380
        const error::Error &err, const IncomingRequestPtr &req, IdentifiedRequestHandler handler) {
1381
        status_ = TransactionStatus::Done;
8✔
1382
        DoCancel();
8✔
1383
        handler(
8✔
1384
                req,
1385
                err.WithContext(
8✔
1386
                        req->address_.host + ": " + MethodToString(req->method_) + " " + request_->GetPath()));
24✔
1387

1388
        server_.RemoveStream(shared_from_this());
8✔
1389
}
8✔
1390

1391
void Stream::CallErrorHandler(
4✔
1392
        const error_code &ec, const RequestPtr &req, ReplyFinishedHandler handler) {
1393
        CallErrorHandler(error::Error(ec.default_error_condition(), ""), req, handler);
12✔
1394
}
4✔
1395

1396
void Stream::CallErrorHandler(
7✔
1397
        const error::Error &err, const RequestPtr &req, ReplyFinishedHandler handler) {
1398
        status_ = TransactionStatus::Done;
7✔
1399
        DoCancel();
7✔
1400
        handler(err.WithContext(
14✔
1401
                req->address_.host + ": " + MethodToString(req->method_) + " " + request_->GetPath()));
14✔
1402

1403
        server_.RemoveStream(shared_from_this());
7✔
1404
}
7✔
1405

1406
void Stream::CallErrorHandler(
×
1407
        const error_code &ec, const RequestPtr &req, SwitchProtocolHandler handler) {
1408
        CallErrorHandler(error::Error(ec.default_error_condition(), ""), req, handler);
×
1409
}
×
1410

1411
void Stream::CallErrorHandler(
1✔
1412
        const error::Error &err, const RequestPtr &req, SwitchProtocolHandler handler) {
1413
        status_ = TransactionStatus::Done;
1✔
1414
        DoCancel();
1✔
1415
        handler(expected::unexpected(err.WithContext(
2✔
1416
                req->address_.host + ": " + MethodToString(req->method_) + " " + request_->GetPath())));
4✔
1417

1418
        server_.RemoveStream(shared_from_this());
1✔
1419
}
1✔
1420

1421
void Stream::AcceptHandler(const error_code &ec) {
225✔
1422
        if (ec) {
225✔
1423
                log::Error("Error while accepting HTTP connection: " + ec.message());
×
1424
                return;
×
1425
        }
1426

1427
        auto ip = socket_.remote_endpoint().address().to_string();
450✔
1428

1429
        // Use IP as context for logging.
1430
        logger_ = log::Logger("http_server").WithFields(log::LogField("ip", ip));
225✔
1431

1432
        logger_.Debug("Accepted connection.");
450✔
1433

1434
        request_.reset(new IncomingRequest(*this, cancelled_));
450✔
1435

1436
        request_->address_.host = ip;
225✔
1437

1438
        *cancelled_ = false;
225✔
1439

1440
        ReadHeader();
225✔
1441
}
1442

1443
void Stream::ReadHeader() {
225✔
1444
        auto &cancelled = cancelled_;
1445
        auto &request_data = request_data_;
225✔
1446

1447
        http::async_read_some(
450✔
1448
                socket_,
225✔
1449
                *request_data_.request_buffer_,
1450
                *request_data_.http_request_parser_,
1451
                [this, cancelled, request_data](const error_code &ec, size_t num_read) {
225✔
1452
                        if (!*cancelled) {
225✔
1453
                                ReadHeaderHandler(ec, num_read);
225✔
1454
                        }
1455
                });
225✔
1456
}
225✔
1457

1458
void Stream::ReadHeaderHandler(const error_code &ec, size_t num_read) {
225✔
1459
        if (num_read > 0) {
225✔
1460
                logger_.Trace("Read " + to_string(num_read) + " bytes of header data from stream.");
450✔
1461
        }
1462

1463
        if (ec) {
225✔
1464
                CallErrorHandler(ec, request_, server_.header_handler_);
×
1465
                return;
185✔
1466
        }
1467

1468
        if (!request_data_.http_request_parser_->is_header_done()) {
225✔
1469
                ReadHeader();
×
1470
                return;
×
1471
        }
1472

1473
        auto method_result = BeastVerbToMethod(
1474
                request_data_.http_request_parser_->get().base().method(),
1475
                string {request_data_.http_request_parser_->get().base().method_string()});
450✔
1476
        if (!method_result) {
225✔
1477
                CallErrorHandler(method_result.error(), request_, server_.header_handler_);
×
1478
                return;
×
1479
        }
1480
        request_->method_ = method_result.value();
225✔
1481
        request_->address_.path = string(request_data_.http_request_parser_->get().base().target());
225✔
1482

1483
        logger_ = logger_.WithFields(log::LogField("path", request_->address_.path));
225✔
1484

1485
        string debug_str;
1486
        for (auto header = request_data_.http_request_parser_->get().cbegin();
391✔
1487
                 header != request_data_.http_request_parser_->get().cend();
616✔
1488
                 header++) {
1489
                request_->headers_[string {header->name_string()}] = string {header->value()};
1,173✔
1490
                if (logger_.Level() >= log::LogLevel::Debug) {
391✔
1491
                        debug_str += string {header->name_string()};
326✔
1492
                        debug_str += ": ";
326✔
1493
                        debug_str += string {header->value()};
326✔
1494
                        debug_str += "\n";
326✔
1495
                }
1496
        }
1497

1498
        logger_.Debug("Received headers:\n" + debug_str);
450✔
1499
        debug_str.clear();
1500

1501
        if (GetContentLength(*request_data_.http_request_parser_) == 0
225✔
1502
                && !request_data_.http_request_parser_->chunked()) {
225✔
1503
                auto cancelled = cancelled_;
1504
                status_ = TransactionStatus::HeaderHandlerCalled;
184✔
1505
                server_.header_handler_(request_);
368✔
1506
                if (!*cancelled) {
184✔
1507
                        status_ = TransactionStatus::BodyHandlerCalled;
184✔
1508
                        CallBodyHandler();
184✔
1509
                }
1510
                return;
1511
        }
1512

1513
        assert(!request_data_.http_request_parser_->is_done());
1514

1515
        auto cancelled = cancelled_;
1516
        status_ = TransactionStatus::HeaderHandlerCalled;
41✔
1517
        server_.header_handler_(request_);
82✔
1518
        if (*cancelled) {
41✔
1519
                return;
1520
        }
1521

1522
        // We know that a body reader is required here, because of the check for body above.
1523
        if (status_ == TransactionStatus::HeaderHandlerCalled) {
40✔
1524
                CallErrorHandler(MakeError(BodyIgnoredError, ""), request_, server_.body_handler_);
2✔
1525
        }
1526
}
1527

1528
void Stream::AsyncReadNextBodyPart(
2,264✔
1529
        vector<uint8_t>::iterator start, vector<uint8_t>::iterator end, io::AsyncIoHandler handler) {
1530
        assert(AtLeast(status_, TransactionStatus::ReaderCreated));
1531

1532
        if (status_ == TransactionStatus::ReaderCreated) {
2,264✔
1533
                status_ = TransactionStatus::BodyReadingInProgress;
39✔
1534
        }
1535

1536
        if (status_ != TransactionStatus::BodyReadingInProgress) {
2,264✔
1537
                auto cancelled = cancelled_;
1538
                handler(0);
66✔
1539
                if (!*cancelled && status_ == TransactionStatus::BodyReadingFinished) {
33✔
1540
                        status_ = TransactionStatus::BodyHandlerCalled;
33✔
1541
                        CallBodyHandler();
33✔
1542
                }
1543
                return;
1544
        }
1545

1546
        reader_buf_start_ = start;
2,231✔
1547
        reader_buf_end_ = end;
2,231✔
1548
        reader_handler_ = handler;
2,231✔
1549
        size_t read_size = end - start;
2,231✔
1550
        size_t smallest = min(body_buffer_.size(), read_size);
3,287✔
1551

1552
        request_data_.http_request_parser_->get().body().data = body_buffer_.data();
2,231✔
1553
        request_data_.http_request_parser_->get().body().size = smallest;
2,231✔
1554
        request_data_.last_buffer_size_ = smallest;
2,231✔
1555

1556
        auto &cancelled = cancelled_;
1557
        auto &request_data = request_data_;
2,231✔
1558

1559
        http::async_read_some(
4,462✔
1560
                socket_,
2,231✔
1561
                *request_data_.request_buffer_,
1562
                *request_data_.http_request_parser_,
1563
                [this, cancelled, request_data](const error_code &ec, size_t num_read) {
2,231✔
1564
                        if (!*cancelled) {
2,231✔
1565
                                ReadBodyHandler(ec, num_read);
2,231✔
1566
                        }
1567
                });
2,231✔
1568
}
1569

1570
void Stream::ReadBodyHandler(error_code ec, size_t num_read) {
2,231✔
1571
        if (num_read > 0) {
2,231✔
1572
                logger_.Trace("Read " + to_string(num_read) + " bytes of body data from stream.");
4,454✔
1573
        }
1574

1575
        if (ec == http::make_error_code(http::error::need_buffer)) {
2,231✔
1576
                // This can be ignored. We always reset the buffer between reads anyway.
1577
                ec = error_code();
979✔
1578
        }
1579

1580
        assert(reader_handler_);
1581

1582
        if (request_data_.http_request_parser_->is_done()) {
2,231✔
1583
                status_ = TransactionStatus::BodyReadingFinished;
33✔
1584
        }
1585

1586
        auto cancelled = cancelled_;
1587

1588
        if (ec) {
2,231✔
1589
                auto err = error::Error(ec.default_error_condition(), "Could not read body");
8✔
1590
                reader_handler_(expected::unexpected(err));
12✔
1591
                if (!*cancelled) {
4✔
1592
                        CallErrorHandler(ec, request_, server_.body_handler_);
4✔
1593
                }
1594
                return;
1595
        }
1596

1597
        // The num_read from above includes out of band payload data, such as chunk headers, which
1598
        // we are not interested in. So we need to calculate the payload size from the remaining
1599
        // buffer space.
1600
        size_t payload_read =
1601
                request_data_.last_buffer_size_ - request_data_.http_request_parser_->get().body().size;
2,227✔
1602

1603
        size_t buf_size = reader_buf_end_ - reader_buf_start_;
2,227✔
1604
        size_t smallest = min(payload_read, buf_size);
2,227✔
1605

1606
        if (smallest == 0) {
2,227✔
1607
                // We read nothing, which can happen if all we read was a chunk header. We cannot
1608
                // return 0 to the handler however, because in `io::Reader` context this means
1609
                // EOF. So just repeat the request instead, until we get actual payload data.
1610
                AsyncReadNextBodyPart(reader_buf_start_, reader_buf_end_, reader_handler_);
154✔
1611
        } else {
1612
                copy_n(body_buffer_.begin(), smallest, reader_buf_start_);
2,150✔
1613
                reader_handler_(smallest);
4,300✔
1614
        }
1615
}
1616

1617
void Stream::AsyncReply(ReplyFinishedHandler reply_finished_handler) {
202✔
1618
        SetupResponse();
202✔
1619

1620
        reply_finished_handler_ = reply_finished_handler;
202✔
1621

1622
        auto &cancelled = cancelled_;
1623
        auto &response_data = response_data_;
202✔
1624

1625
        http::async_write_header(
404✔
1626
                socket_,
202✔
1627
                *response_data_.http_response_serializer_,
1628
                [this, cancelled, response_data](const error_code &ec, size_t num_written) {
202✔
1629
                        if (!*cancelled) {
202✔
1630
                                WriteHeaderHandler(ec, num_written);
201✔
1631
                        }
1632
                });
202✔
1633
}
202✔
1634

1635
void Stream::SetupResponse() {
211✔
1636
        auto response = maybe_response_.lock();
211✔
1637
        // Only called from existing responses, so this should always be true.
1638
        assert(response);
1639

1640
        assert(status_ == TransactionStatus::BodyHandlerCalled);
1641
        status_ = TransactionStatus::Replying;
211✔
1642

1643
        // From here on we take shared ownership.
1644
        response_ = response;
1645

1646
        response_data_.http_response_ = make_shared<http::response<http::buffer_body>>();
422✔
1647

1648
        for (const auto &header : response->headers_) {
438✔
1649
                response_data_.http_response_->base().set(header.first, header.second);
227✔
1650
        }
1651

1652
        response_data_.http_response_->result(response->GetStatusCode());
211✔
1653
        response_data_.http_response_->reason(response->GetStatusMessage());
422✔
1654

1655
        response_data_.http_response_serializer_ =
1656
                make_shared<http::response_serializer<http::buffer_body>>(*response_data_.http_response_);
422✔
1657
}
211✔
1658

1659
void Stream::WriteHeaderHandler(const error_code &ec, size_t num_written) {
201✔
1660
        if (num_written > 0) {
201✔
1661
                logger_.Trace("Wrote " + to_string(num_written) + " bytes of header data to stream.");
402✔
1662
        }
1663

1664
        if (ec) {
201✔
1665
                CallErrorHandler(ec, request_, reply_finished_handler_);
×
1666
                return;
37✔
1667
        }
1668

1669
        auto exp_has_body =
1670
                HasBody(response_->GetHeader("Content-Length"), response_->GetHeader("Transfer-Encoding"));
402✔
1671
        if (!exp_has_body) {
201✔
1672
                CallErrorHandler(exp_has_body.error(), request_, reply_finished_handler_);
×
1673
                return;
×
1674
        }
1675
        if (!exp_has_body.value()) {
201✔
1676
                FinishReply();
36✔
1677
                return;
1678
        }
1679

1680
        if (!response_->body_reader_ && !response_->async_body_reader_) {
165✔
1681
                auto err = MakeError(BodyMissingError, "No body reader");
2✔
1682
                CallErrorHandler(err, request_, reply_finished_handler_);
3✔
1683
                return;
1684
        }
1685

1686
        PrepareAndWriteNewBodyBuffer();
164✔
1687
}
1688

1689
void Stream::PrepareAndWriteNewBodyBuffer() {
2,073✔
1690
        // response_->body_reader_ XOR response_->async_body_reader_
1691
        assert(
1692
                (response_->body_reader_ || response_->async_body_reader_)
1693
                && !(response_->body_reader_ && response_->async_body_reader_));
1694

1695
        auto read_handler = [this](io::ExpectedSize read) {
2,074✔
1696
                if (!read) {
2,073✔
1697
                        CallErrorHandler(read.error(), request_, reply_finished_handler_);
3✔
1698
                        return;
1✔
1699
                }
1700
                WriteNewBodyBuffer(read.value());
2,072✔
1701
        };
2,073✔
1702

1703
        if (response_->body_reader_) {
2,073✔
1704
                read_handler(response_->body_reader_->Read(body_buffer_.begin(), body_buffer_.end()));
3,598✔
1705
        } else {
1706
                auto err = response_->async_body_reader_->AsyncRead(
1707
                        body_buffer_.begin(), body_buffer_.end(), read_handler);
274✔
1708
                if (err != error::NoError) {
274✔
1709
                        CallErrorHandler(err, request_, reply_finished_handler_);
×
1710
                }
1711
        }
1712
}
2,073✔
1713

1714
void Stream::WriteNewBodyBuffer(size_t size) {
2,072✔
1715
        response_data_.http_response_->body().data = body_buffer_.data();
2,072✔
1716
        response_data_.http_response_->body().size = size;
2,072✔
1717

1718
        if (size > 0) {
2,072✔
1719
                response_data_.http_response_->body().more = true;
1,945✔
1720
        } else {
1721
                response_data_.http_response_->body().more = false;
127✔
1722
        }
1723

1724
        WriteBody();
2,072✔
1725
}
2,072✔
1726

1727
void Stream::WriteBody() {
3,997✔
1728
        auto &cancelled = cancelled_;
1729
        auto &response_data = response_data_;
3,997✔
1730

1731
        http::async_write_some(
7,994✔
1732
                socket_,
3,997✔
1733
                *response_data_.http_response_serializer_,
1734
                [this, cancelled, response_data](const error_code &ec, size_t num_written) {
3,955✔
1735
                        if (!*cancelled) {
3,955✔
1736
                                WriteBodyHandler(ec, num_written);
3,955✔
1737
                        }
1738
                });
3,955✔
1739
}
3,997✔
1740

1741
void Stream::WriteBodyHandler(const error_code &ec, size_t num_written) {
3,955✔
1742
        if (num_written > 0) {
3,955✔
1743
                logger_.Trace("Wrote " + to_string(num_written) + " bytes of body data to stream.");
3,850✔
1744
        }
1745

1746
        if (ec == http::make_error_code(http::error::need_buffer)) {
3,955✔
1747
                // Write next body block.
1748
                PrepareAndWriteNewBodyBuffer();
1,909✔
1749
        } else if (ec) {
2,046✔
1750
                CallErrorHandler(ec, request_, reply_finished_handler_);
12✔
1751
        } else if (num_written > 0) {
2,042✔
1752
                // We are still writing the body.
1753
                WriteBody();
1,925✔
1754
        } else {
1755
                // We are finished.
1756
                FinishReply();
117✔
1757
        }
1758
}
3,955✔
1759

1760
void Stream::FinishReply() {
153✔
1761
        // We are done.
1762
        status_ = TransactionStatus::Done;
153✔
1763
        DoCancel();
153✔
1764
        // Release ownership of Body reader.
1765
        response_->body_reader_.reset();
153✔
1766
        response_->async_body_reader_.reset();
153✔
1767
        reply_finished_handler_(error::NoError);
153✔
1768
        server_.RemoveStream(shared_from_this());
153✔
1769
}
153✔
1770

1771
error::Error Stream::AsyncSwitchProtocol(SwitchProtocolHandler handler) {
9✔
1772
        SetupResponse();
9✔
1773

1774
        switch_protocol_handler_ = handler;
9✔
1775
        status_ = TransactionStatus::SwitchingProtocol;
9✔
1776

1777
        auto &cancelled = cancelled_;
1778
        auto &response_data = response_data_;
9✔
1779

1780
        http::async_write_header(
18✔
1781
                socket_,
9✔
1782
                *response_data_.http_response_serializer_,
1783
                [this, cancelled, response_data](const error_code &ec, size_t num_written) {
9✔
1784
                        if (!*cancelled) {
9✔
1785
                                SwitchingProtocolHandler(ec, num_written);
8✔
1786
                        }
1787
                });
9✔
1788

1789
        return error::NoError;
9✔
1790
}
1791

1792
void Stream::SwitchingProtocolHandler(error_code ec, size_t num_written) {
8✔
1793
        if (num_written > 0) {
8✔
1794
                logger_.Trace("Wrote " + to_string(num_written) + " bytes of header data to stream.");
16✔
1795
        }
1796

1797
        if (ec) {
8✔
1798
                CallErrorHandler(ec, request_, switch_protocol_handler_);
×
1799
                return;
×
1800
        }
1801

1802
        auto socket = make_shared<RawSocket<tcp::socket>>(
1803
                make_shared<tcp::socket>(std::move(socket_)), request_data_.request_buffer_);
8✔
1804

1805
        auto switch_protocol_handler = switch_protocol_handler_;
8✔
1806

1807
        // Rest of the connection is done directly on the socket, set cancelled_ but don't close it.
1808
        *cancelled_ = true;
8✔
1809
        cancelled_ = make_shared<bool>(true);
8✔
1810
        server_.RemoveStream(shared_from_this());
16✔
1811

1812
        switch_protocol_handler(socket);
16✔
1813
}
1814

1815
void Stream::CallBodyHandler() {
217✔
1816
        // Get a pointer to ourselves. This is just in case the body handler make a response, which
1817
        // it immediately destroys, which would destroy this stream as well. At the end of this
1818
        // function, it's ok to destroy it.
1819
        auto stream_ref = shared_from_this();
1820

1821
        server_.body_handler_(request_, error::NoError);
651✔
1822

1823
        // MakeResponse() should have been called inside body handler. It can use this to generate a
1824
        // response, either immediately, or later. Therefore it should still exist, otherwise the
1825
        // request has not been handled correctly.
1826
        auto response = maybe_response_.lock();
217✔
1827
        if (!response) {
217✔
1828
                logger_.Error("Handler produced no response. Closing stream prematurely.");
6✔
1829
                *cancelled_ = true;
3✔
1830
                cancelled_ = make_shared<bool>(true);
3✔
1831
                server_.RemoveStream(shared_from_this());
9✔
1832
        }
1833
}
217✔
1834

1835
Server::Server(const ServerConfig &server, events::EventLoop &event_loop) :
237✔
1836
        event_loop_ {event_loop},
1837
        acceptor_(GetAsioIoContext(event_loop_)) {
428✔
1838
}
237✔
1839

1840
Server::~Server() {
474✔
1841
        Cancel();
237✔
1842
}
237✔
1843

1844
error::Error Server::AsyncServeUrl(
202✔
1845
        const string &url, RequestHandler header_handler, RequestHandler body_handler) {
1846
        return AsyncServeUrl(
1847
                url, header_handler, [body_handler](IncomingRequestPtr req, error::Error err) {
822✔
1848
                        if (err != error::NoError) {
212✔
1849
                                body_handler(expected::unexpected(err));
12✔
1850
                        } else {
1851
                                body_handler(req);
412✔
1852
                        }
1853
                });
616✔
1854
}
1855

1856
error::Error Server::AsyncServeUrl(
217✔
1857
        const string &url, RequestHandler header_handler, IdentifiedRequestHandler body_handler) {
1858
        auto err = BreakDownUrl(url, address_);
217✔
1859
        if (error::NoError != err) {
217✔
1860
                return MakeError(InvalidUrlError, "Could not parse URL " + url + ": " + err.String());
×
1861
        }
1862

1863
        if (address_.protocol != "http") {
217✔
1864
                return error::Error(make_error_condition(errc::protocol_not_supported), address_.protocol);
×
1865
        }
1866

1867
        if (address_.path.size() > 0 && address_.path != "/") {
217✔
1868
                return MakeError(InvalidUrlError, "URLs with paths are not supported when listening.");
2✔
1869
        }
1870

1871
        boost::system::error_code ec;
216✔
1872
        auto address = asio::ip::make_address(address_.host, ec);
216✔
1873
        if (ec) {
216✔
1874
                return error::Error(
1875
                        ec.default_error_condition(),
×
1876
                        "Could not construct endpoint from address " + address_.host);
×
1877
        }
1878

1879
        asio::ip::tcp::endpoint endpoint(address, address_.port);
216✔
1880

1881
        ec.clear();
1882
        acceptor_.open(endpoint.protocol(), ec);
216✔
1883
        if (ec) {
216✔
1884
                return error::Error(ec.default_error_condition(), "Could not open acceptor");
×
1885
        }
1886

1887
        // Allow address reuse, otherwise we can't re-bind later.
1888
        ec.clear();
1889
        acceptor_.set_option(asio::socket_base::reuse_address(true), ec);
216✔
1890
        if (ec) {
216✔
1891
                return error::Error(ec.default_error_condition(), "Could not set socket options");
×
1892
        }
1893

1894
        ec.clear();
1895
        acceptor_.bind(endpoint, ec);
216✔
1896
        if (ec) {
216✔
1897
                return error::Error(ec.default_error_condition(), "Could not bind socket");
×
1898
        }
1899

1900
        ec.clear();
1901
        acceptor_.listen(asio::socket_base::max_listen_connections, ec);
216✔
1902
        if (ec) {
216✔
1903
                return error::Error(ec.default_error_condition(), "Could not start listening");
×
1904
        }
1905

1906
        header_handler_ = header_handler;
216✔
1907
        body_handler_ = body_handler;
216✔
1908

1909
        PrepareNewStream();
216✔
1910

1911
        return error::NoError;
216✔
1912
}
1913

1914
void Server::Cancel() {
257✔
1915
        if (acceptor_.is_open()) {
257✔
1916
                acceptor_.cancel();
216✔
1917
                acceptor_.close();
216✔
1918
        }
1919
        streams_.clear();
1920
}
257✔
1921

1922
uint16_t Server::GetPort() const {
17✔
1923
        return acceptor_.local_endpoint().port();
17✔
1924
}
1925

1926
string Server::GetUrl() const {
16✔
1927
        return "http://127.0.0.1:" + to_string(GetPort());
32✔
1928
}
1929

1930
ExpectedOutgoingResponsePtr Server::MakeResponse(IncomingRequestPtr req) {
216✔
1931
        if (*req->cancelled_) {
216✔
1932
                return expected::unexpected(MakeError(StreamCancelledError, "Cannot make response"));
×
1933
        }
1934
        OutgoingResponsePtr response {new OutgoingResponse(req->stream_, req->cancelled_)};
432✔
1935
        req->stream_.maybe_response_ = response;
216✔
1936
        return response;
216✔
1937
}
1938

1939
error::Error Server::AsyncReply(
202✔
1940
        OutgoingResponsePtr resp, ReplyFinishedHandler reply_finished_handler) {
1941
        if (*resp->cancelled_) {
202✔
1942
                return MakeError(StreamCancelledError, "Cannot send response");
×
1943
        }
1944

1945
        resp->stream_.AsyncReply(reply_finished_handler);
202✔
1946
        return error::NoError;
202✔
1947
}
1948

1949
io::ExpectedAsyncReaderPtr Server::MakeBodyAsyncReader(IncomingRequestPtr req) {
58✔
1950
        if (*req->cancelled_) {
58✔
1951
                return expected::unexpected(MakeError(StreamCancelledError, "Cannot make body reader"));
×
1952
        }
1953

1954
        auto &stream = req->stream_;
58✔
1955
        if (stream.status_ != TransactionStatus::HeaderHandlerCalled) {
58✔
1956
                return expected::unexpected(error::Error(
1✔
1957
                        make_error_condition(errc::operation_in_progress),
2✔
1958
                        "MakeBodyAsyncReader called while reading is in progress"));
3✔
1959
        }
1960

1961
        if (GetContentLength(*stream.request_data_.http_request_parser_) == 0
57✔
1962
                && !stream.request_data_.http_request_parser_->chunked()) {
57✔
1963
                return expected::unexpected(MakeError(BodyMissingError, "Request does not contain a body"));
54✔
1964
        }
1965

1966
        stream.status_ = TransactionStatus::ReaderCreated;
39✔
1967
        return make_shared<BodyAsyncReader<Stream>>(stream, req->cancelled_);
78✔
1968
}
1969

1970
error::Error Server::AsyncSwitchProtocol(OutgoingResponsePtr resp, SwitchProtocolHandler handler) {
9✔
1971
        return resp->stream_.AsyncSwitchProtocol(handler);
18✔
1972
}
1973

1974
void Server::PrepareNewStream() {
441✔
1975
        StreamPtr new_stream {new Stream(*this)};
441✔
1976
        streams_.insert(new_stream);
1977
        AsyncAccept(new_stream);
882✔
1978
}
441✔
1979

1980
void Server::AsyncAccept(StreamPtr stream) {
441✔
1981
        acceptor_.async_accept(stream->socket_, [this, stream](const error_code &ec) {
669✔
1982
                if (ec) {
228✔
1983
                        if (ec != errc::operation_canceled) {
3✔
1984
                                log::Error("Could not accept connection: " + ec.message());
×
1985
                        }
1986
                        return;
3✔
1987
                }
1988

1989
                stream->AcceptHandler(ec);
225✔
1990

1991
                this->PrepareNewStream();
225✔
1992
        });
1993
}
441✔
1994

1995
void Server::RemoveStream(StreamPtr stream) {
185✔
1996
        streams_.erase(stream);
185✔
1997

1998
        stream->DoCancel();
185✔
1999
}
185✔
2000

2001
} // namespace http
2002
} // namespace common
2003
} // namespace mender
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc