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>;
69 boost::asio::ssl::context
sslCtx_{boost::asio::ssl::context::tlsv12};
80 [](
std::size_t, boost::asio::ssl::context_base::password_purpose) {
85 boost::asio::ssl::context::default_workarounds |
86 boost::asio::ssl::context::no_sslv2 |
87 boost::asio::ssl::context::single_dh_use);
90 boost::asio::buffer(
cert().data(),
cert().size()));
93 boost::asio::buffer(
key().data(),
key().size()),
94 boost::asio::ssl::context::file_format::pem);
96 sslCtx_.use_tmp_dh(boost::asio::buffer(
dh().data(),
dh().size()));
171 boost::asio::io_context& ioc,
179 bool immediateStart =
true,
182 ,
ep_{beast::IP::Address::from_string(
196 blobInfo.
reserve(futures.size() + 1);
204 for (
auto const& val : validators)
206 data +=
"{\"validation_public_key\":\"" +
207 strHex(val.masterPublic) +
"\",\"manifest\":\"" +
208 val.manifest +
"\"},";
220 l <<
"{\"blob\":\"" << blob <<
"\""
221 <<
",\"signature\":\"" << sig <<
"\""
222 <<
",\"manifest\":\"" <<
manifest <<
"\""
223 <<
",\"refresh_interval\": " << interval
224 <<
",\"version\":" << version <<
'}';
227 for (
auto const& future : futures)
237 for (
auto const& val : validators)
239 data +=
"{\"validation_public_key\":\"" +
240 strHex(val.masterPublic) +
"\",\"manifest\":\"" +
241 val.manifest +
"\"},";
255 for (
auto const& info : blobInfo)
257 l <<
"{\"blob\":\"" << info.blob <<
"\""
258 <<
",\"signature\":\"" << info.signature <<
"\"},";
263 l <<
"{\"blobs_v2\": [ " << blobs <<
"],\"manifest\":\"" <<
manifest
265 <<
",\"refresh_interval\": " << interval
266 <<
",\"version\":" << (version + 1) <<
'}';
283 boost::asio::ip::tcp::acceptor::reuse_address(
true), ec);
285 acceptor_.listen(boost::asio::socket_base::max_connections);
290 if (
auto p = wp.lock())
389 -----BEGIN CERTIFICATE-----
390 MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEL
391 MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw
392 bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE5MDgwNzE3
393 MzM1OFoXDTQ2MTIyMzE3MzM1OFowazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
394 bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs
395 ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
396 AQEFAAOCAQ8AMIIBCgKCAQEA5Ky0UE9K+gFOznfwBvq2HfnQOOPGtVf4G9m63b5V
397 QNJYCSNiYxkGZW72ESM3XA8BledlkV9pwIm17+7ucB1Ed3efQjQDq2RSk5LDYDaa
398 r0Qzzy0EC3b9+AKA6mAoVY6s1Qws/YvM4esz0H+SVvtVcFqA46kRWhJN7M5ic1lu
399 d58fAq04BHqi5zOEOzfHJYPGUgQOxRTHluYkkkBrL2xioHHnOROshW+PIYFiAc/h
400 WPzuihPHnKaziPRw+O6O8ysnCxycQHgqtvx73T52eJdLxtr3ToRWaY/8VF/Cog5c
401 uvWEtg6EucGOszIH8O7eJWaJpVpAfZIX+c62MQWLpOLi/QIDAQABoyowKDAmBgNV
402 HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEF
403 BQADggEBAOhLAO/e0lGi9TZ2HiVi4sJ7KVQaBQHGhfsysILoQNHrNqDypPc/ZrSa
404 WQ2OqyUeltMnUdN5S1h3MKRZlbAeBQlwkPdjTzlzWkCMWB5BsfIGy5ovqmNQ7zPa
405 Khg5oxq3mU8ZLiJP4HngyU+hOOCt5tttex2S8ubjFT+3C3cydLKEOXCUPspaVkKn
406 Eq8WSBoYTvyUVmSi6+m6HGiowWsM5Qgj93IRW6JCbkgfPeKXC/5ykAPQcFHwNaKT
407 rpWokcavZyMbVjRsbzCQcc7n2j7tbLOu2svSLy6oXwG6n/bEagl5WpN2/TzQuwe7
408 f5ktutc4DDJSV7fuYYCuGumrHAjcELE=
409 -----END CERTIFICATE-----
418 -----BEGIN RSA PRIVATE KEY-----
419 MIIEpQIBAAKCAQEA5Ky0UE9K+gFOznfwBvq2HfnQOOPGtVf4G9m63b5VQNJYCSNi
420 YxkGZW72ESM3XA8BledlkV9pwIm17+7ucB1Ed3efQjQDq2RSk5LDYDaar0Qzzy0E
421 C3b9+AKA6mAoVY6s1Qws/YvM4esz0H+SVvtVcFqA46kRWhJN7M5ic1lud58fAq04
422 BHqi5zOEOzfHJYPGUgQOxRTHluYkkkBrL2xioHHnOROshW+PIYFiAc/hWPzuihPH
423 nKaziPRw+O6O8ysnCxycQHgqtvx73T52eJdLxtr3ToRWaY/8VF/Cog5cuvWEtg6E
424 ucGOszIH8O7eJWaJpVpAfZIX+c62MQWLpOLi/QIDAQABAoIBACf8mzs/4lh9Sg6I
425 ooxV4uqy+Fo6WlDzpQsZs7d6xOWk4ogWi+nQQnISSS0N/2w1o41W/UfCa3ejnRDr
426 sv4f4A0T+eFVvx6FWHs9urRkWAA16OldccufbyGjLm/NiMANRuOqUWO0woru2gyn
427 git7n6EZ8lfdBI+/i6jRHh4VkV+ROt5Zbp9zuJsj0yMqJH7J6Ebtl1jAF6PemLBL
428 yxdiYqR8LKTunTGGP/L+4K5a389oPDcJ1+YX805NEopmfrIhPr+BQYdz8905aVFk
429 FSS4TJy23EhFLzKf3+iSept6Giim+2yy2rv1RPCKgjOXbJ+4LD48xumDol6XWgYr
430 1CBzQIECgYEA/jBEGOjV02a9A3C5RJxZMawlGwGrvvALG2UrKvwQc595uxwrUw9S
431 Mn3ZQBEGnEWwEf44jSpWzp8TtejMxEvrU5243eWgwif1kqr1Mcj54DR7Qm15/hsj
432 M3nA2WscVG2OHBs4AwzMCHE2vfEAkbz71s6xonhg6zvsC26Zy3hYPqkCgYEA5k3k
433 OuCeG5FXW1/GzhvVFuhl6msNKzuUnLmJg6500XPny5Xo7W3RMvjtTM2XLt1USU6D
434 arMCCQ1A8ku1SoFdSw5RC6Fl8ZoUFBz9FPPwT6usQssGyFxiiqdHLvTlk12NNCk3
435 vJYsdQ+v/dKuZ8T4U3GTgQSwPTj6J0kJUf5y2jUCgYEA+hi/R8r/aArz+kiU4T78
436 O3Vm5NWWCD3ij8fQ23A7N6g3e7RRpF20wF02vmSCHowqmumI9swrsQyvthIiNxmD
437 pzfORvXCYIY0h2SR77QQt1qr1EYm+6/zyJgI+WL78s4APwNA7y9OKRhLhkN0DfDl
438 0Qp5mKPcqFbC/tSJmbsFCFECgYEAwlLC2rMgdV5jeWQNGWf+mv+ozu1ZBTuWn88l
439 qwiO5RSJZwysp3nb5MiJYh6vDAoQznIDDQrSEtUuEcOzypPxJh2EYO3kWMGLY5U6
440 Lm3OPUs7ZHhu1qytMRUISSS2eWucc4C72NJV3MhJ1T/pjQF0DuRsc5aDJoVm/bLw
441 vFCYlGkCgYEAgBDIIqdo1th1HE95SQfpP2wV/jA6CPamIciNwS3bpyhDBqs9oLUc
442 qzXidOpXAVYg1wl/BqpaCQcmmhCrnSLJYdOMpudVyLCCfYmBJ0bs2DCAe5ibGbL7
443 VruAOjS4yBepkXJU9xwKHxDmgTo/oQ5smq7SNOUWDSElVI/CyZ0x7qA=
444 -----END RSA PRIVATE KEY-----
453 -----BEGIN CERTIFICATE-----
454 MIIDQjCCAioCCQDxKQafEvp+VTANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJV
455 UzELMAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJy
456 aXBwbGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE5MDgw
457 NzE3MzM1OFoXDTQ2MTIyMzE3MzM1OFowYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
458 AkNBMRQwEgYDVQQHDAtMb3MgQW5nZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0
459 LXRlc3RzMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
460 ggEPADCCAQoCggEBAO9oqh72ttM7hjPnbMcJw0EuyULocEn2hlg4HE4YtzaxlRIz
461 dHm8nMkG/9yGmHBCuue/Gzssm/CzlduGezae01p8eaFUuEJsjxdrXe89Wk2QH+dm
462 Fn+SRbGcHaaTV/cyJrvusG7pOu95HL2eebuwiZ+tX5JP01R732iQt8Beeygh/W4P
463 n2f//fAxbdAIWzx2DH6cmSNe6lpoQe/MN15o8V3whutcC3fkis6wcA7BKZcdVdL2
464 daFWA6mt4SPWldOfWQVAIX4vRvheWPy34OLCgx+wZWg691Lwd1F+paarKombatUt
465 vKMTeolFYl3zkZZMYvR0Oyrt5NXUhRfmG7xR3bkCAwEAATANBgkqhkiG9w0BAQsF
466 AAOCAQEAggKO5WdtU67QPcAdo1Uar0SFouvVLwxJvoKlQ5rqF3idd0HnFVy7iojW
467 G2sZq7z8SNDMkUXZLbcbYNRyrZI0PdjfI0kyNpaa3pEcPcR8aOcTEOtW6V67FrPG
468 8aNYpr6a8PPq12aHzPSNjlUGot/qffGIQ0H2OqdWMOUXMMFnmH2KnnWi46Aq3gaF
469 uyHGrEczjJAK7NTzP8A7fbrmT00Sn6ft1FriQyhvDkUgPXBGWKpOFO84V27oo0ZL
470 xXQHDWcpX+8yNKynjafkXLx6qXwcySF2bKcTIRsxlN6WNRqZ+wqpNStkjuoFkYR/
471 IfW9PBfO/gCtNJQ+lqpoTd3kLBCAng==
472 -----END CERTIFICATE-----
481 -----BEGIN DH PARAMETERS-----
482 MIIBCAKCAQEAnJaaKu3U2a7ZVBvIC+NVNHXo9q6hNCazze+4pwXAKBVXH0ozInEw
483 WKozYxVJLW7dvDHdjdFOSuTLQDqaPW9zVMQKM0BKu81+JyfJi7C3HYKUw7ECVHp4
484 DLvhDe6N5eBj/t1FUwcfS2VNIx4QcJiw6FH3CwNNee1fIi5VTRJp2GLUuMCHkT/I
485 FTODJ+Anw12cJqLdgQfV74UV/Y7JCQl3/DOIy+2YkmX8vWVHX1h6EI5Gw4a3jgqF
486 gVyCOWoVCfgu37H5e7ERyoAxigiP8hMqoGpmJUYJghVKWoFgNUqXw+guVJ56eIuH
487 0wVs/LXflOZ42PJAiwv4LTNOtpG2pWGjOwIBAg==
488 -----END DH PARAMETERS-----
499 boost::asio::executor_work_guard<boost::asio::executor>
work;
518 self.do_peer(
id, std::move(
sock),
ssl);
534 if (
auto p = wp.lock())
544 using namespace boost::beast;
556 ssl_stream->handshake(ssl::stream_base::server, ec);
568 http::read(*ssl_stream, sb, req, ec);
570 http::read(sock, sb, req, ec);
575 auto path = req.target().to_string();
576 res.insert(
"Server",
"TrustedPublisherServer");
577 res.version(req.version());
578 res.keep_alive(req.keep_alive());
581 if (boost::starts_with(path,
"/validators2"))
583 res.result(http::status::ok);
584 res.insert(
"Content-Type",
"application/json");
585 if (path ==
"/validators2/bad")
586 res.body() =
"{ 'bad': \"2']";
587 else if (path ==
"/validators2/missing")
588 res.body() =
"{\"version\": 2}";
592 constexpr
char const* refreshPrefix =
593 "/validators2/refresh/";
594 if (boost::starts_with(path, refreshPrefix))
595 refresh = boost::lexical_cast<unsigned int>(
596 path.substr(
strlen(refreshPrefix)));
600 else if (boost::starts_with(path,
"/validators"))
602 res.result(http::status::ok);
603 res.insert(
"Content-Type",
"application/json");
604 if (path ==
"/validators/bad")
605 res.body() =
"{ 'bad': \"1']";
606 else if (path ==
"/validators/missing")
607 res.body() =
"{\"version\": 1}";
611 constexpr
char const* refreshPrefix =
612 "/validators/refresh/";
613 if (boost::starts_with(path, refreshPrefix))
614 refresh = boost::lexical_cast<unsigned int>(
615 path.substr(
strlen(refreshPrefix)));
619 else if (boost::starts_with(path,
"/textfile"))
622 res.result(http::status::ok);
623 res.insert(
"Content-Type",
"text/example");
626 boost::starts_with(path,
"/textfile/huge")
629 res.content_length(cl);
630 if (req.method() == http::verb::get)
633 for (
auto i = 0; i < 1024; ++i)
634 body <<
static_cast<char>(rand_int<short>(32, 126)),
635 res.body() = body.
str();
638 else if (boost::starts_with(path,
"/sleep/"))
640 auto const sleep_sec =
641 boost::lexical_cast<unsigned int>(path.substr(7));
645 else if (boost::starts_with(path,
"/redirect"))
647 if (boost::ends_with(path,
"/301"))
648 res.result(http::status::moved_permanently);
649 else if (boost::ends_with(path,
"/302"))
650 res.result(http::status::found);
651 else if (boost::ends_with(path,
"/307"))
652 res.result(http::status::temporary_redirect);
653 else if (boost::ends_with(path,
"/308"))
654 res.result(http::status::permanent_redirect);
657 if (boost::starts_with(path,
"/redirect_to/"))
659 location << path.substr(13);
661 else if (!boost::starts_with(path,
"/redirect_nolo"))
664 << (ssl ?
"https://" :
"http://")
666 << (boost::starts_with(path,
"/redirect_forever/")
670 if (!location.
str().empty())
671 res.insert(
"Location", location.
str());
676 res.result(boost::beast::http::status::not_found);
677 res.insert(
"Content-Type",
"text/html");
678 res.body() =
"The file '" + path +
"' was not found";
682 res.prepare_payload();
687 res.result(boost::beast::http::status::internal_server_error);
688 res.version(req.version());
689 res.insert(
"Server",
"TrustedPublisherServer");
690 res.insert(
"Content-Type",
"text/html");
693 res.prepare_payload();
697 write(*ssl_stream, res, ec);
699 write(sock, res, ec);
701 if (ec || req.need_eof())
707 ssl_stream->shutdown(ec);
713 boost::asio::io_context& ioc,
720 bool immediateStart =
true,
723 auto const r = std::make_shared<TrustedPublisherServer>(
724 ioc, validators, validUntil, futures, useSSL, version, sequence);