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 MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEL
391 MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw
392 bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIyMDIwNTIz
393 NDk0M1oXDTQ5MDYyMzIzNDk0M1owazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
394 bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs
395 ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
396 AQEFAAOCAQ8AMIIBCgKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJ
397 qTuBUcVcTRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs
398 +T/qkwmc/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9f
399 n9vrxnWX9LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3
400 zVDe7+sxR//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0q
401 S1Uj77mhwxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABoyowKDAmBgNV
402 HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEL
403 BQADggEBAJkUFNS0CeEAKvo0ttzooXnCDH3esj2fwmLJQYLUGsAF8DFrFHTqZEcx
404 hFRdr0ftEb/VKpV9dVF6xtSoMU56kHOnhbHEWADyqdKUkCDjrGBet5QdWmEwNV2L
405 nYrwGQBAybMt/+1XMUV8HeLFJNHnyxfQYcW0fUsrmNGk8W0kzWuuq88qbhfXZAIx
406 KiXrzYpLlM0RlpWXRfYQ6mTdSrRrLnEo5MklizVgNB8HYX78lxa06zP08oReQcfT
407 GSGO8NEEq8BTVmp69zD1JyfvQcXzsi7WtkAX+/EOFZ7LesnZ6VsyjZ74wECCaQuD
408 X1yu/XxHqchM+DOzzVw6wRKaM7Zsk80=
409 -----END CERTIFICATE-----
418 -----BEGIN RSA PRIVATE KEY-----
419 MIIEpAIBAAKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJqTuBUcVc
420 TRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs+T/qkwmc
421 /L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9fn9vrxnWX
422 9LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3zVDe7+sx
423 R//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0qS1Uj77mh
424 wxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABAoIBAEC9MDpOu+quvg8+
425 kt4MKSFdIhQuM7WguNaTe5AkSspDrcJzT7SK275mp259QIYCzMxxuA8TSZTb8A1C
426 t6dgKbi7k6FaGMCYMRHzzK6NZfMbPi6cj245q9LYlZpdQswuM/FdPpPH1zUxrNYK
427 CIaooZ6ZHzlSD/eaRMgkBQEkONHrZZtEinLIvKedwssPCaXkIISmt7MFQTDOlxkf
428 K0Mt1mnRREPYbYSfPEEfIyy/KDIiB5AzgGt+uPOn8Oeb1pSqy69jpYcfhSj+bo4S
429 UV6qTuTfBd4qkkNI6d/Z7DcDJFFlfloG/vVgGk/beWNnL2e39vzxiebB3w+MQn4F
430 Wyx5mCECgYEA22z1/ihqt9LIAWtP42oSS3S/RxlFzpp5d7QfNqFnEoVgeRhQzleP
431 pRJIzVXpMYBxexZYqZA/q8xBSggz+2gmRoYnW20VIzl14DsSH378ye3FRwJB0tLy
432 dWU8DC7ZB5XQCTvI9UY3voJNToknODw7RCNO1h3V3T1y6JRLdcLskk8CgYEA2OLy
433 aE5bvsUaLBSv7W9NFhSuZ0p9Y0pFmRgHI7g8i/AgRZ0BgiE8u8OZSHmPJPMaNs/h
434 YIEIrlsgDci1PzwrUYseRp/aiVE1kyev09/ihqRXTPpLQu6h/d63KRe/06W3t5X3
435 Dmfj49hH5zGPBI/0y1ECV/n0fwnRhxSv7fNr3RECgYBEuFpOUAAkNApZj29ErNqv
436 8Q9ayAp5yx1RpQLFjEUIoub05e2gwgGF1DUiwc43p59iyjvYVwnp1x13fxwwl4yt
437 N6Sp2H7vOja1lCp33MB0yVeohodw7InsxFjLA/0KiBvQWH32exhIPOzTNNcooIx7
438 KYeuPUfWc0FCn/cGGZcXtwKBgQC1hp1k99CKBuY05suoanOWe5DNGud/ZvaBgD7Z
439 gqYKadxY52QPyknOzZNJuZQ5VM8n+S2lW9osNFDLuKUaW/3Vrh6U9c4vCC1TEPB0
440 4PnzvzDiWMsNJjWnCfU7C4meVyFBIt84y3NNjAQCWNRe+S3lzdOsVqRwf4NDD+l/
441 uzEYQQKBgQCJczIlwobm1Y6O41hbGZhZL/CGMNS6Z0INi2yasV0WDqYlh7XayHMD
442 cK55dMILcbHqeIBq/wR6sIhw6IJcaDBfFfrJiKKDilfij2lHxR2FQrEngtTCCRV+
443 ZzARzaWhQPvbDqEtLJDWuXZNXfL8/PTIs5NmuKuQ8F4+gQJpkQgwaw==
444 -----END RSA PRIVATE KEY-----
453 -----BEGIN CERTIFICATE-----
454 MIIDpzCCAo+gAwIBAgIUWc45WqaaNuaSLoFYTMC/Mjfqw/gwDQYJKoZIhvcNAQEL
455 BQAwYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQHDAtMb3MgQW5n
456 ZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0LXRlc3RzMRQwEgYDVQQDDAtleGFt
457 cGxlLmNvbTAeFw0yMjAyMDUyMzQ5MDFaFw00OTA2MjMyMzQ5MDFaMGMxCzAJBgNV
458 BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLTG9zIEFuZ2VsZXMxGzAZBgNV
459 BAoMEnJpcHBsZWQtdW5pdC10ZXN0czEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEi
460 MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0f2JBW2XNW2wT5/ajX2qxmUY+
461 aNJGfpV6gZ5CmwdQpbHrPPvJoskxwsCyr3GifzT/GtCpmb1fiu59uUAPxQEYCxiq
462 V+HchX4g4Vl27xKJ0P+usxuEED9v7TCteKum9u9eMZ8UDF0fspXcnWGs9fXlyoTj
463 uTRP1SBQllk44DPc/KzlrtH+QNXmr9XQnP8XvwWCgJXMx87voxEGiFFOVhkSSAOv
464 v+OUGgEuq0NPgwv2LHBlYHSdkoU9F5Z/TmkCAFMShbyoUjldIz2gcWXjN2tespGo
465 D6qYvasvPIpmcholBBkc0z8QDt+RNq+Wzrults7epJXy/u+txGK9cHCNlLCpAgMB
466 AAGjUzBRMB0GA1UdDgQWBBS1oydh+YyqDNOFKYOvOtVMWKqV4zAfBgNVHSMEGDAW
467 gBS1oydh+YyqDNOFKYOvOtVMWKqV4zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
468 DQEBCwUAA4IBAQCDPyGKQwQ8Lz0yEgvIl/Uo9BtwAzlvjrLM/39qhStLQqDGSs2Q
469 xFIbtjzjuLf5vR3q6OJ62CCvzqXgHkJ+hzVN/tAvyliGTdjJrK+xv1M5a+XipO2f
470 c9lb4gRbFL/DyoeoWgb1Rkv3gFf0FlCYH+ZUcYb9ZYCRlGtFgOcxJI2g+T7jSLFp
471 8+hSzQ6W5Sp9L6b5iJyCww1vjBvBqzNyZMNeB4gXGtd6z9vMDSvKboTdGD7wcFB+
472 mRMyNekaRw+Npy4Hjou5sx272cXHHmPCSF5TjwdaibSaGjx1k0Q50mOf7S9KG5b5
473 7X1e3FekJlaD02EBEhtkXURIxogOQALdFncj
474 -----END CERTIFICATE-----
483 -----BEGIN DH PARAMETERS-----
484 MIIBCAKCAQEAp2I2fWEUZ3sCNfitSRC/MdAhJE/bS+NO0O2tWdIdlvmIFE6B5qhC
485 sGW9ojrQT8DTxBvGAcbjr/jagmlE3BV4oSnxyhP37G2mDvMOJ29J3NvFD/ZFAW0d
486 BvZJ1RNvMu29NmVCyt6/jgzcqrqnami9uD93aK+zaVrlPsPEYM8xB19HXwqsEYCL
487 ux2B7sqXm9Ts74HPg/EV+pcVon9phxNWxxgHlOvFc2QjZ3hXH++kzmJ4vs7N/XDB
488 xbEQ+TUZ5jbJGSeBqNFKFeuOUQGJ46Io0jBSYd4rSmKUXkvElQwR+n7KF3jy1uAt
489 /8hzd8tHn9TyW7Q2/CPkOA6dCXzltpOSowIBAg==
490 -----END DH PARAMETERS-----
501 boost::asio::executor_work_guard<boost::asio::executor>
work;
520 self.do_peer(
id, std::move(
sock),
ssl);
536 if (
auto p = wp.lock())
546 using namespace boost::beast;
558 ssl_stream->handshake(ssl::stream_base::server, ec);
570 http::read(*ssl_stream, sb, req, ec);
572 http::read(sock, sb, req, ec);
577 auto path = req.target().to_string();
578 res.insert(
"Server",
"TrustedPublisherServer");
579 res.version(req.version());
580 res.keep_alive(req.keep_alive());
583 if (boost::starts_with(path,
"/validators2"))
585 res.result(http::status::ok);
586 res.insert(
"Content-Type",
"application/json");
587 if (path ==
"/validators2/bad")
588 res.body() =
"{ 'bad': \"2']";
589 else if (path ==
"/validators2/missing")
590 res.body() =
"{\"version\": 2}";
594 constexpr
char const* refreshPrefix =
595 "/validators2/refresh/";
596 if (boost::starts_with(path, refreshPrefix))
597 refresh = boost::lexical_cast<unsigned int>(
598 path.substr(
strlen(refreshPrefix)));
602 else if (boost::starts_with(path,
"/validators"))
604 res.result(http::status::ok);
605 res.insert(
"Content-Type",
"application/json");
606 if (path ==
"/validators/bad")
607 res.body() =
"{ 'bad': \"1']";
608 else if (path ==
"/validators/missing")
609 res.body() =
"{\"version\": 1}";
613 constexpr
char const* refreshPrefix =
614 "/validators/refresh/";
615 if (boost::starts_with(path, refreshPrefix))
616 refresh = boost::lexical_cast<unsigned int>(
617 path.substr(
strlen(refreshPrefix)));
621 else if (boost::starts_with(path,
"/textfile"))
624 res.result(http::status::ok);
625 res.insert(
"Content-Type",
"text/example");
628 boost::starts_with(path,
"/textfile/huge")
631 res.content_length(cl);
632 if (req.method() == http::verb::get)
635 for (
auto i = 0; i < 1024; ++i)
636 body <<
static_cast<char>(rand_int<short>(32, 126)),
637 res.body() = body.
str();
640 else if (boost::starts_with(path,
"/sleep/"))
642 auto const sleep_sec =
643 boost::lexical_cast<unsigned int>(path.substr(7));
647 else if (boost::starts_with(path,
"/redirect"))
649 if (boost::ends_with(path,
"/301"))
650 res.result(http::status::moved_permanently);
651 else if (boost::ends_with(path,
"/302"))
652 res.result(http::status::found);
653 else if (boost::ends_with(path,
"/307"))
654 res.result(http::status::temporary_redirect);
655 else if (boost::ends_with(path,
"/308"))
656 res.result(http::status::permanent_redirect);
659 if (boost::starts_with(path,
"/redirect_to/"))
661 location << path.substr(13);
663 else if (!boost::starts_with(path,
"/redirect_nolo"))
666 << (ssl ?
"https://" :
"http://")
668 << (boost::starts_with(path,
"/redirect_forever/")
672 if (!location.
str().empty())
673 res.insert(
"Location", location.
str());
678 res.result(boost::beast::http::status::not_found);
679 res.insert(
"Content-Type",
"text/html");
680 res.body() =
"The file '" + path +
"' was not found";
684 res.prepare_payload();
689 res.result(boost::beast::http::status::internal_server_error);
690 res.version(req.version());
691 res.insert(
"Server",
"TrustedPublisherServer");
692 res.insert(
"Content-Type",
"text/html");
695 res.prepare_payload();
699 write(*ssl_stream, res, ec);
701 write(sock, res, ec);
703 if (ec || req.need_eof())
709 ssl_stream->shutdown(ec);
715 boost::asio::io_context& ioc,
722 bool immediateStart =
true,
725 auto const r = std::make_shared<TrustedPublisherServer>(
726 ioc, validators, validUntil, futures, useSSL, version, sequence);