19 #ifndef RIPPLE_TEST_TRUSTED_PUBLISHER_SERVER_H_INCLUDED
20 #define RIPPLE_TEST_TRUSTED_PUBLISHER_SERVER_H_INCLUDED
22 #include <ripple/basics/base64.h>
23 #include <ripple/basics/random.h>
24 #include <ripple/basics/strHex.h>
25 #include <ripple/protocol/PublicKey.h>
26 #include <ripple/protocol/SecretKey.h>
27 #include <ripple/protocol/Sign.h>
28 #include <boost/algorithm/string/predicate.hpp>
29 #include <boost/asio.hpp>
30 #include <boost/asio/ip/tcp.hpp>
31 #include <boost/asio/ssl/stream.hpp>
32 #include <boost/beast/http.hpp>
33 #include <boost/beast/ssl.hpp>
34 #include <boost/beast/version.hpp>
35 #include <boost/lexical_cast.hpp>
36 #include <test/jtx/envconfig.h>
52 boost::beast::http::request<boost::beast::http::string_body>;
54 boost::beast::http::response<boost::beast::http::string_body>;
64 boost::asio::ssl::context
sslCtx_{boost::asio::ssl::context::tlsv12};
75 [](
std::size_t, boost::asio::ssl::context_base::password_purpose) {
80 boost::asio::ssl::context::default_workarounds |
81 boost::asio::ssl::context::no_sslv2 |
82 boost::asio::ssl::context::single_dh_use);
85 boost::asio::buffer(
cert().data(),
cert().size()));
88 boost::asio::buffer(
key().data(),
key().size()),
89 boost::asio::ssl::context::file_format::pem);
91 sslCtx_.use_tmp_dh(boost::asio::buffer(
dh().data(),
dh().size()));
152 boost::asio::io_context& ioc,
157 bool immediateStart =
true,
160 ,
ep_{beast::IP::Address::from_string(
178 for (
auto const& val : validators)
180 data +=
"{\"validation_public_key\":\"" +
strHex(val.masterPublic) +
181 "\",\"manifest\":\"" + val.manifest +
"\"},";
186 auto const sig =
sign(keys.first, keys.second,
makeSlice(data));
189 l <<
"{\"blob\":\"" << blob <<
"\""
190 <<
",\"signature\":\"" <<
strHex(sig) <<
"\""
191 <<
",\"manifest\":\"" <<
manifest <<
"\""
192 <<
",\"refresh_interval\": " << interval
193 <<
",\"version\":" << version <<
'}';
210 boost::asio::ip::tcp::acceptor::reuse_address(
true), ec);
212 acceptor_.listen(boost::asio::socket_base::max_connections);
217 if (
auto p = wp.lock())
316 -----BEGIN CERTIFICATE-----
317 MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEL
318 MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw
319 bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE5MDgwNzE3
320 MzM1OFoXDTQ2MTIyMzE3MzM1OFowazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
321 bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs
322 ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
323 AQEFAAOCAQ8AMIIBCgKCAQEA5Ky0UE9K+gFOznfwBvq2HfnQOOPGtVf4G9m63b5V
324 QNJYCSNiYxkGZW72ESM3XA8BledlkV9pwIm17+7ucB1Ed3efQjQDq2RSk5LDYDaa
325 r0Qzzy0EC3b9+AKA6mAoVY6s1Qws/YvM4esz0H+SVvtVcFqA46kRWhJN7M5ic1lu
326 d58fAq04BHqi5zOEOzfHJYPGUgQOxRTHluYkkkBrL2xioHHnOROshW+PIYFiAc/h
327 WPzuihPHnKaziPRw+O6O8ysnCxycQHgqtvx73T52eJdLxtr3ToRWaY/8VF/Cog5c
328 uvWEtg6EucGOszIH8O7eJWaJpVpAfZIX+c62MQWLpOLi/QIDAQABoyowKDAmBgNV
329 HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEF
330 BQADggEBAOhLAO/e0lGi9TZ2HiVi4sJ7KVQaBQHGhfsysILoQNHrNqDypPc/ZrSa
331 WQ2OqyUeltMnUdN5S1h3MKRZlbAeBQlwkPdjTzlzWkCMWB5BsfIGy5ovqmNQ7zPa
332 Khg5oxq3mU8ZLiJP4HngyU+hOOCt5tttex2S8ubjFT+3C3cydLKEOXCUPspaVkKn
333 Eq8WSBoYTvyUVmSi6+m6HGiowWsM5Qgj93IRW6JCbkgfPeKXC/5ykAPQcFHwNaKT
334 rpWokcavZyMbVjRsbzCQcc7n2j7tbLOu2svSLy6oXwG6n/bEagl5WpN2/TzQuwe7
335 f5ktutc4DDJSV7fuYYCuGumrHAjcELE=
336 -----END CERTIFICATE-----
345 -----BEGIN RSA PRIVATE KEY-----
346 MIIEpQIBAAKCAQEA5Ky0UE9K+gFOznfwBvq2HfnQOOPGtVf4G9m63b5VQNJYCSNi
347 YxkGZW72ESM3XA8BledlkV9pwIm17+7ucB1Ed3efQjQDq2RSk5LDYDaar0Qzzy0E
348 C3b9+AKA6mAoVY6s1Qws/YvM4esz0H+SVvtVcFqA46kRWhJN7M5ic1lud58fAq04
349 BHqi5zOEOzfHJYPGUgQOxRTHluYkkkBrL2xioHHnOROshW+PIYFiAc/hWPzuihPH
350 nKaziPRw+O6O8ysnCxycQHgqtvx73T52eJdLxtr3ToRWaY/8VF/Cog5cuvWEtg6E
351 ucGOszIH8O7eJWaJpVpAfZIX+c62MQWLpOLi/QIDAQABAoIBACf8mzs/4lh9Sg6I
352 ooxV4uqy+Fo6WlDzpQsZs7d6xOWk4ogWi+nQQnISSS0N/2w1o41W/UfCa3ejnRDr
353 sv4f4A0T+eFVvx6FWHs9urRkWAA16OldccufbyGjLm/NiMANRuOqUWO0woru2gyn
354 git7n6EZ8lfdBI+/i6jRHh4VkV+ROt5Zbp9zuJsj0yMqJH7J6Ebtl1jAF6PemLBL
355 yxdiYqR8LKTunTGGP/L+4K5a389oPDcJ1+YX805NEopmfrIhPr+BQYdz8905aVFk
356 FSS4TJy23EhFLzKf3+iSept6Giim+2yy2rv1RPCKgjOXbJ+4LD48xumDol6XWgYr
357 1CBzQIECgYEA/jBEGOjV02a9A3C5RJxZMawlGwGrvvALG2UrKvwQc595uxwrUw9S
358 Mn3ZQBEGnEWwEf44jSpWzp8TtejMxEvrU5243eWgwif1kqr1Mcj54DR7Qm15/hsj
359 M3nA2WscVG2OHBs4AwzMCHE2vfEAkbz71s6xonhg6zvsC26Zy3hYPqkCgYEA5k3k
360 OuCeG5FXW1/GzhvVFuhl6msNKzuUnLmJg6500XPny5Xo7W3RMvjtTM2XLt1USU6D
361 arMCCQ1A8ku1SoFdSw5RC6Fl8ZoUFBz9FPPwT6usQssGyFxiiqdHLvTlk12NNCk3
362 vJYsdQ+v/dKuZ8T4U3GTgQSwPTj6J0kJUf5y2jUCgYEA+hi/R8r/aArz+kiU4T78
363 O3Vm5NWWCD3ij8fQ23A7N6g3e7RRpF20wF02vmSCHowqmumI9swrsQyvthIiNxmD
364 pzfORvXCYIY0h2SR77QQt1qr1EYm+6/zyJgI+WL78s4APwNA7y9OKRhLhkN0DfDl
365 0Qp5mKPcqFbC/tSJmbsFCFECgYEAwlLC2rMgdV5jeWQNGWf+mv+ozu1ZBTuWn88l
366 qwiO5RSJZwysp3nb5MiJYh6vDAoQznIDDQrSEtUuEcOzypPxJh2EYO3kWMGLY5U6
367 Lm3OPUs7ZHhu1qytMRUISSS2eWucc4C72NJV3MhJ1T/pjQF0DuRsc5aDJoVm/bLw
368 vFCYlGkCgYEAgBDIIqdo1th1HE95SQfpP2wV/jA6CPamIciNwS3bpyhDBqs9oLUc
369 qzXidOpXAVYg1wl/BqpaCQcmmhCrnSLJYdOMpudVyLCCfYmBJ0bs2DCAe5ibGbL7
370 VruAOjS4yBepkXJU9xwKHxDmgTo/oQ5smq7SNOUWDSElVI/CyZ0x7qA=
371 -----END RSA PRIVATE KEY-----
380 -----BEGIN CERTIFICATE-----
381 MIIDQjCCAioCCQDxKQafEvp+VTANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJV
382 UzELMAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJy
383 aXBwbGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE5MDgw
384 NzE3MzM1OFoXDTQ2MTIyMzE3MzM1OFowYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
385 AkNBMRQwEgYDVQQHDAtMb3MgQW5nZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0
386 LXRlc3RzMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
387 ggEPADCCAQoCggEBAO9oqh72ttM7hjPnbMcJw0EuyULocEn2hlg4HE4YtzaxlRIz
388 dHm8nMkG/9yGmHBCuue/Gzssm/CzlduGezae01p8eaFUuEJsjxdrXe89Wk2QH+dm
389 Fn+SRbGcHaaTV/cyJrvusG7pOu95HL2eebuwiZ+tX5JP01R732iQt8Beeygh/W4P
390 n2f//fAxbdAIWzx2DH6cmSNe6lpoQe/MN15o8V3whutcC3fkis6wcA7BKZcdVdL2
391 daFWA6mt4SPWldOfWQVAIX4vRvheWPy34OLCgx+wZWg691Lwd1F+paarKombatUt
392 vKMTeolFYl3zkZZMYvR0Oyrt5NXUhRfmG7xR3bkCAwEAATANBgkqhkiG9w0BAQsF
393 AAOCAQEAggKO5WdtU67QPcAdo1Uar0SFouvVLwxJvoKlQ5rqF3idd0HnFVy7iojW
394 G2sZq7z8SNDMkUXZLbcbYNRyrZI0PdjfI0kyNpaa3pEcPcR8aOcTEOtW6V67FrPG
395 8aNYpr6a8PPq12aHzPSNjlUGot/qffGIQ0H2OqdWMOUXMMFnmH2KnnWi46Aq3gaF
396 uyHGrEczjJAK7NTzP8A7fbrmT00Sn6ft1FriQyhvDkUgPXBGWKpOFO84V27oo0ZL
397 xXQHDWcpX+8yNKynjafkXLx6qXwcySF2bKcTIRsxlN6WNRqZ+wqpNStkjuoFkYR/
398 IfW9PBfO/gCtNJQ+lqpoTd3kLBCAng==
399 -----END CERTIFICATE-----
408 -----BEGIN DH PARAMETERS-----
409 MIIBCAKCAQEAnJaaKu3U2a7ZVBvIC+NVNHXo9q6hNCazze+4pwXAKBVXH0ozInEw
410 WKozYxVJLW7dvDHdjdFOSuTLQDqaPW9zVMQKM0BKu81+JyfJi7C3HYKUw7ECVHp4
411 DLvhDe6N5eBj/t1FUwcfS2VNIx4QcJiw6FH3CwNNee1fIi5VTRJp2GLUuMCHkT/I
412 FTODJ+Anw12cJqLdgQfV74UV/Y7JCQl3/DOIy+2YkmX8vWVHX1h6EI5Gw4a3jgqF
413 gVyCOWoVCfgu37H5e7ERyoAxigiP8hMqoGpmJUYJghVKWoFgNUqXw+guVJ56eIuH
414 0wVs/LXflOZ42PJAiwv4LTNOtpG2pWGjOwIBAg==
415 -----END DH PARAMETERS-----
426 boost::asio::executor_work_guard<boost::asio::executor>
work;
445 self.do_peer(
id, std::move(
sock),
ssl);
461 if (
auto p = wp.lock())
471 using namespace boost::beast;
476 boost::optional<ssl_stream<ip::tcp::socket&>> ssl_stream;
481 ssl_stream.emplace(sock,
sslCtx_);
483 ssl_stream->handshake(ssl::stream_base::server, ec);
495 http::read(*ssl_stream, sb, req, ec);
497 http::read(sock, sb, req, ec);
502 auto path = req.target().to_string();
503 res.insert(
"Server",
"TrustedPublisherServer");
504 res.version(req.version());
505 res.keep_alive(req.keep_alive());
508 if (boost::starts_with(path,
"/validators"))
510 res.result(http::status::ok);
511 res.insert(
"Content-Type",
"application/json");
512 if (path ==
"/validators/bad")
513 res.body() =
"{ 'bad': \"1']";
514 else if (path ==
"/validators/missing")
515 res.body() =
"{\"version\": 1}";
519 if (boost::starts_with(path,
"/validators/refresh"))
520 refresh = boost::lexical_cast<unsigned int>(
525 else if (boost::starts_with(path,
"/textfile"))
528 res.result(http::status::ok);
529 res.insert(
"Content-Type",
"text/example");
532 boost::starts_with(path,
"/textfile/huge")
535 res.content_length(cl);
536 if (req.method() == http::verb::get)
539 for (
auto i = 0; i < 1024; ++i)
540 body <<
static_cast<char>(rand_int<short>(32, 126)),
541 res.body() = body.
str();
544 else if (boost::starts_with(path,
"/sleep/"))
546 auto const sleep_sec =
547 boost::lexical_cast<unsigned int>(path.substr(7));
551 else if (boost::starts_with(path,
"/redirect"))
553 if (boost::ends_with(path,
"/301"))
554 res.result(http::status::moved_permanently);
555 else if (boost::ends_with(path,
"/302"))
556 res.result(http::status::found);
557 else if (boost::ends_with(path,
"/307"))
558 res.result(http::status::temporary_redirect);
559 else if (boost::ends_with(path,
"/308"))
560 res.result(http::status::permanent_redirect);
563 if (boost::starts_with(path,
"/redirect_to/"))
565 location << path.substr(13);
567 else if (!boost::starts_with(path,
"/redirect_nolo"))
570 << (ssl ?
"https://" :
"http://")
572 << (boost::starts_with(path,
"/redirect_forever/")
576 if (!location.
str().empty())
577 res.insert(
"Location", location.
str());
582 res.result(boost::beast::http::status::not_found);
583 res.insert(
"Content-Type",
"text/html");
584 res.body() =
"The file '" + path +
"' was not found";
588 res.prepare_payload();
593 res.result(boost::beast::http::status::internal_server_error);
594 res.version(req.version());
595 res.insert(
"Server",
"TrustedPublisherServer");
596 res.insert(
"Content-Type",
"text/html");
599 res.prepare_payload();
603 write(*ssl_stream, res, ec);
605 write(sock, res, ec);
607 if (ec || req.need_eof())
613 ssl_stream->shutdown(ec);
619 boost::asio::io_context& ioc,
624 bool immediateStart =
true,
627 auto const r = std::make_shared<TrustedPublisherServer>(
628 ioc, validators,
expiration, useSSL, version, sequence);