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>
49 boost::beast::http::request<boost::beast::http::string_body>;
51 boost::beast::http::response<boost::beast::http::string_body>;
61 boost::asio::ssl::context
sslCtx_{boost::asio::ssl::context::tlsv12};
72 [](
std::size_t, boost::asio::ssl::context_base::password_purpose) {
77 boost::asio::ssl::context::default_workarounds |
78 boost::asio::ssl::context::no_sslv2 |
79 boost::asio::ssl::context::single_dh_use);
82 boost::asio::buffer(
cert().data(),
cert().size()));
85 boost::asio::buffer(
key().data(),
key().size()),
86 boost::asio::ssl::context::file_format::pem);
88 sslCtx_.use_tmp_dh(boost::asio::buffer(
dh().data(),
dh().size()));
145 boost::asio::io_context& ioc,
150 bool immediateStart =
true,
153 ,
ep_{beast::IP::Address::from_string(
171 for (
auto const& val : validators)
173 data +=
"{\"validation_public_key\":\"" +
strHex(val.masterPublic) +
174 "\",\"manifest\":\"" + val.manifest +
"\"},";
179 auto const sig =
sign(keys.first, keys.second,
makeSlice(data));
182 l <<
"{\"blob\":\"" << blob <<
"\""
183 <<
",\"signature\":\"" <<
strHex(sig) <<
"\""
184 <<
",\"manifest\":\"" <<
manifest <<
"\""
185 <<
",\"refresh_interval\": " << interval
186 <<
",\"version\":" << version <<
'}';
206 boost::asio::ip::tcp::acceptor::reuse_address(
true), ec);
208 acceptor_.listen(boost::asio::socket_base::max_connections);
214 std::placeholders::_1));
309 -----BEGIN CERTIFICATE-----
310 MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEL
311 MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw
312 bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE5MDgwNzE3
313 MzM1OFoXDTQ2MTIyMzE3MzM1OFowazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
314 bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs
315 ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
316 AQEFAAOCAQ8AMIIBCgKCAQEA5Ky0UE9K+gFOznfwBvq2HfnQOOPGtVf4G9m63b5V
317 QNJYCSNiYxkGZW72ESM3XA8BledlkV9pwIm17+7ucB1Ed3efQjQDq2RSk5LDYDaa
318 r0Qzzy0EC3b9+AKA6mAoVY6s1Qws/YvM4esz0H+SVvtVcFqA46kRWhJN7M5ic1lu
319 d58fAq04BHqi5zOEOzfHJYPGUgQOxRTHluYkkkBrL2xioHHnOROshW+PIYFiAc/h
320 WPzuihPHnKaziPRw+O6O8ysnCxycQHgqtvx73T52eJdLxtr3ToRWaY/8VF/Cog5c
321 uvWEtg6EucGOszIH8O7eJWaJpVpAfZIX+c62MQWLpOLi/QIDAQABoyowKDAmBgNV
322 HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEF
323 BQADggEBAOhLAO/e0lGi9TZ2HiVi4sJ7KVQaBQHGhfsysILoQNHrNqDypPc/ZrSa
324 WQ2OqyUeltMnUdN5S1h3MKRZlbAeBQlwkPdjTzlzWkCMWB5BsfIGy5ovqmNQ7zPa
325 Khg5oxq3mU8ZLiJP4HngyU+hOOCt5tttex2S8ubjFT+3C3cydLKEOXCUPspaVkKn
326 Eq8WSBoYTvyUVmSi6+m6HGiowWsM5Qgj93IRW6JCbkgfPeKXC/5ykAPQcFHwNaKT
327 rpWokcavZyMbVjRsbzCQcc7n2j7tbLOu2svSLy6oXwG6n/bEagl5WpN2/TzQuwe7
328 f5ktutc4DDJSV7fuYYCuGumrHAjcELE=
329 -----END CERTIFICATE-----
338 -----BEGIN RSA PRIVATE KEY-----
339 MIIEpQIBAAKCAQEA5Ky0UE9K+gFOznfwBvq2HfnQOOPGtVf4G9m63b5VQNJYCSNi
340 YxkGZW72ESM3XA8BledlkV9pwIm17+7ucB1Ed3efQjQDq2RSk5LDYDaar0Qzzy0E
341 C3b9+AKA6mAoVY6s1Qws/YvM4esz0H+SVvtVcFqA46kRWhJN7M5ic1lud58fAq04
342 BHqi5zOEOzfHJYPGUgQOxRTHluYkkkBrL2xioHHnOROshW+PIYFiAc/hWPzuihPH
343 nKaziPRw+O6O8ysnCxycQHgqtvx73T52eJdLxtr3ToRWaY/8VF/Cog5cuvWEtg6E
344 ucGOszIH8O7eJWaJpVpAfZIX+c62MQWLpOLi/QIDAQABAoIBACf8mzs/4lh9Sg6I
345 ooxV4uqy+Fo6WlDzpQsZs7d6xOWk4ogWi+nQQnISSS0N/2w1o41W/UfCa3ejnRDr
346 sv4f4A0T+eFVvx6FWHs9urRkWAA16OldccufbyGjLm/NiMANRuOqUWO0woru2gyn
347 git7n6EZ8lfdBI+/i6jRHh4VkV+ROt5Zbp9zuJsj0yMqJH7J6Ebtl1jAF6PemLBL
348 yxdiYqR8LKTunTGGP/L+4K5a389oPDcJ1+YX805NEopmfrIhPr+BQYdz8905aVFk
349 FSS4TJy23EhFLzKf3+iSept6Giim+2yy2rv1RPCKgjOXbJ+4LD48xumDol6XWgYr
350 1CBzQIECgYEA/jBEGOjV02a9A3C5RJxZMawlGwGrvvALG2UrKvwQc595uxwrUw9S
351 Mn3ZQBEGnEWwEf44jSpWzp8TtejMxEvrU5243eWgwif1kqr1Mcj54DR7Qm15/hsj
352 M3nA2WscVG2OHBs4AwzMCHE2vfEAkbz71s6xonhg6zvsC26Zy3hYPqkCgYEA5k3k
353 OuCeG5FXW1/GzhvVFuhl6msNKzuUnLmJg6500XPny5Xo7W3RMvjtTM2XLt1USU6D
354 arMCCQ1A8ku1SoFdSw5RC6Fl8ZoUFBz9FPPwT6usQssGyFxiiqdHLvTlk12NNCk3
355 vJYsdQ+v/dKuZ8T4U3GTgQSwPTj6J0kJUf5y2jUCgYEA+hi/R8r/aArz+kiU4T78
356 O3Vm5NWWCD3ij8fQ23A7N6g3e7RRpF20wF02vmSCHowqmumI9swrsQyvthIiNxmD
357 pzfORvXCYIY0h2SR77QQt1qr1EYm+6/zyJgI+WL78s4APwNA7y9OKRhLhkN0DfDl
358 0Qp5mKPcqFbC/tSJmbsFCFECgYEAwlLC2rMgdV5jeWQNGWf+mv+ozu1ZBTuWn88l
359 qwiO5RSJZwysp3nb5MiJYh6vDAoQznIDDQrSEtUuEcOzypPxJh2EYO3kWMGLY5U6
360 Lm3OPUs7ZHhu1qytMRUISSS2eWucc4C72NJV3MhJ1T/pjQF0DuRsc5aDJoVm/bLw
361 vFCYlGkCgYEAgBDIIqdo1th1HE95SQfpP2wV/jA6CPamIciNwS3bpyhDBqs9oLUc
362 qzXidOpXAVYg1wl/BqpaCQcmmhCrnSLJYdOMpudVyLCCfYmBJ0bs2DCAe5ibGbL7
363 VruAOjS4yBepkXJU9xwKHxDmgTo/oQ5smq7SNOUWDSElVI/CyZ0x7qA=
364 -----END RSA PRIVATE KEY-----
373 -----BEGIN CERTIFICATE-----
374 MIIDQjCCAioCCQDxKQafEvp+VTANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJV
375 UzELMAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJy
376 aXBwbGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE5MDgw
377 NzE3MzM1OFoXDTQ2MTIyMzE3MzM1OFowYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
378 AkNBMRQwEgYDVQQHDAtMb3MgQW5nZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0
379 LXRlc3RzMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
380 ggEPADCCAQoCggEBAO9oqh72ttM7hjPnbMcJw0EuyULocEn2hlg4HE4YtzaxlRIz
381 dHm8nMkG/9yGmHBCuue/Gzssm/CzlduGezae01p8eaFUuEJsjxdrXe89Wk2QH+dm
382 Fn+SRbGcHaaTV/cyJrvusG7pOu95HL2eebuwiZ+tX5JP01R732iQt8Beeygh/W4P
383 n2f//fAxbdAIWzx2DH6cmSNe6lpoQe/MN15o8V3whutcC3fkis6wcA7BKZcdVdL2
384 daFWA6mt4SPWldOfWQVAIX4vRvheWPy34OLCgx+wZWg691Lwd1F+paarKombatUt
385 vKMTeolFYl3zkZZMYvR0Oyrt5NXUhRfmG7xR3bkCAwEAATANBgkqhkiG9w0BAQsF
386 AAOCAQEAggKO5WdtU67QPcAdo1Uar0SFouvVLwxJvoKlQ5rqF3idd0HnFVy7iojW
387 G2sZq7z8SNDMkUXZLbcbYNRyrZI0PdjfI0kyNpaa3pEcPcR8aOcTEOtW6V67FrPG
388 8aNYpr6a8PPq12aHzPSNjlUGot/qffGIQ0H2OqdWMOUXMMFnmH2KnnWi46Aq3gaF
389 uyHGrEczjJAK7NTzP8A7fbrmT00Sn6ft1FriQyhvDkUgPXBGWKpOFO84V27oo0ZL
390 xXQHDWcpX+8yNKynjafkXLx6qXwcySF2bKcTIRsxlN6WNRqZ+wqpNStkjuoFkYR/
391 IfW9PBfO/gCtNJQ+lqpoTd3kLBCAng==
392 -----END CERTIFICATE-----
401 -----BEGIN DH PARAMETERS-----
402 MIIBCAKCAQEAnJaaKu3U2a7ZVBvIC+NVNHXo9q6hNCazze+4pwXAKBVXH0ozInEw
403 WKozYxVJLW7dvDHdjdFOSuTLQDqaPW9zVMQKM0BKu81+JyfJi7C3HYKUw7ECVHp4
404 DLvhDe6N5eBj/t1FUwcfS2VNIx4QcJiw6FH3CwNNee1fIi5VTRJp2GLUuMCHkT/I
405 FTODJ+Anw12cJqLdgQfV74UV/Y7JCQl3/DOIy+2YkmX8vWVHX1h6EI5Gw4a3jgqF
406 gVyCOWoVCfgu37H5e7ERyoAxigiP8hMqoGpmJUYJghVKWoFgNUqXw+guVJ56eIuH
407 0wVs/LXflOZ42PJAiwv4LTNOtpG2pWGjOwIBAg==
408 -----END DH PARAMETERS-----
419 boost::asio::executor_work_guard<boost::asio::executor>
work;
438 self.do_peer(
id, std::move(
sock),
ssl);
457 std::placeholders::_1));
463 using namespace boost::beast;
468 boost::optional<ssl_stream<ip::tcp::socket&>> ssl_stream;
473 ssl_stream.emplace(sock,
sslCtx_);
475 ssl_stream->handshake(ssl::stream_base::server, ec);
487 http::read(*ssl_stream, sb, req, ec);
489 http::read(sock, sb, req, ec);
494 auto path = req.target().to_string();
495 res.insert(
"Server",
"TrustedPublisherServer");
496 res.version(req.version());
497 res.keep_alive(req.keep_alive());
500 if (boost::starts_with(path,
"/validators"))
502 res.result(http::status::ok);
503 res.insert(
"Content-Type",
"application/json");
504 if (path ==
"/validators/bad")
505 res.body() =
"{ 'bad': \"1']";
506 else if (path ==
"/validators/missing")
507 res.body() =
"{\"version\": 1}";
511 if (boost::starts_with(path,
"/validators/refresh"))
512 refresh = boost::lexical_cast<unsigned int>(
517 else if (boost::starts_with(path,
"/textfile"))
520 res.result(http::status::ok);
521 res.insert(
"Content-Type",
"text/example");
524 boost::starts_with(path,
"/textfile/huge")
527 res.content_length(cl);
528 if (req.method() == http::verb::get)
531 for (
auto i = 0; i < 1024; ++i)
532 body <<
static_cast<char>(rand_int<short>(32, 126)),
533 res.body() = body.
str();
536 else if (boost::starts_with(path,
"/sleep/"))
538 auto const sleep_sec =
539 boost::lexical_cast<unsigned int>(path.substr(7));
543 else if (boost::starts_with(path,
"/redirect"))
545 if (boost::ends_with(path,
"/301"))
546 res.result(http::status::moved_permanently);
547 else if (boost::ends_with(path,
"/302"))
548 res.result(http::status::found);
549 else if (boost::ends_with(path,
"/307"))
550 res.result(http::status::temporary_redirect);
551 else if (boost::ends_with(path,
"/308"))
552 res.result(http::status::permanent_redirect);
555 if (boost::starts_with(path,
"/redirect_to/"))
557 location << path.substr(13);
559 else if (!boost::starts_with(path,
"/redirect_nolo"))
562 << (ssl ?
"https://" :
"http://")
564 << (boost::starts_with(path,
"/redirect_forever/")
568 if (!location.
str().empty())
569 res.insert(
"Location", location.
str());
574 res.result(boost::beast::http::status::not_found);
575 res.insert(
"Content-Type",
"text/html");
576 res.body() =
"The file '" + path +
"' was not found";
580 res.prepare_payload();
585 res.result(boost::beast::http::status::internal_server_error);
586 res.version(req.version());
587 res.insert(
"Server",
"TrustedPublisherServer");
588 res.insert(
"Content-Type",
"text/html");
591 res.prepare_payload();
595 write(*ssl_stream, res, ec);
597 write(sock, res, ec);
599 if (ec || req.need_eof())
605 ssl_stream->shutdown(ec);