19 #ifndef RIPPLE_TEST_TRUSTED_PUBLISHER_SERVER_H_INCLUDED
20 #define RIPPLE_TEST_TRUSTED_PUBLISHER_SERVER_H_INCLUDED
22 #include <ripple/protocol/PublicKey.h>
23 #include <ripple/protocol/SecretKey.h>
24 #include <ripple/protocol/Sign.h>
25 #include <ripple/basics/base64.h>
26 #include <ripple/basics/random.h>
27 #include <ripple/basics/strHex.h>
28 #include <test/jtx/envconfig.h>
29 #include <boost/algorithm/string/predicate.hpp>
30 #include <boost/asio.hpp>
31 #include <boost/asio/ip/tcp.hpp>
32 #include <boost/asio/ssl/stream.hpp>
33 #include <boost/beast/http.hpp>
34 #include <boost/beast/ssl.hpp>
35 #include <boost/beast/version.hpp>
36 #include <boost/lexical_cast.hpp>
48 using req_type = boost::beast::http::request<boost::beast::http::string_body>;
49 using resp_type = boost::beast::http::response<boost::beast::http::string_body>;
59 boost::asio::ssl::context
sslCtx_{boost::asio::ssl::context::tlsv12};
72 boost::asio::ssl::context_base::password_purpose)
78 boost::asio::ssl::context::default_workarounds |
79 boost::asio::ssl::context::no_sslv2 |
80 boost::asio::ssl::context::single_dh_use);
83 boost::asio::buffer(
cert().data(),
cert().size()));
86 boost::asio::buffer(
key().data(),
key().size()),
87 boost::asio::ssl::context::file_format::pem);
90 boost::asio::buffer(
dh().data(),
dh().size()));
123 static_cast<char const*
> (s.
data()), s.
size()));
131 auto const masterPublic =
135 masterPublic, secret, signingKeys.first, signingKeys.second, 1) };
139 boost::asio::io_context& ioc,
144 bool immediateStart =
true,
147 ,
ep_{beast::IP::Address::from_string(
169 for (
auto const& val : validators)
171 data +=
"{\"validation_public_key\":\"" +
strHex(val.masterPublic) +
172 "\",\"manifest\":\"" + val.manifest +
"\"},";
177 auto const sig =
sign(keys.first, keys.second,
makeSlice(data));
180 l <<
"{\"blob\":\"" << blob <<
"\"" <<
181 ",\"signature\":\"" <<
strHex(sig) <<
"\"" <<
182 ",\"manifest\":\"" <<
manifest <<
"\"" <<
183 ",\"refresh_interval\": " << interval <<
184 ",\"version\":" << version <<
'}';
203 boost::asio::ip::tcp::acceptor::reuse_address(
true), ec);
205 acceptor_.listen(boost::asio::socket_base::max_connections);
303 -----BEGIN CERTIFICATE-----
304 MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEL
305 MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw
306 bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE5MDgwNzE3
307 MzM1OFoXDTQ2MTIyMzE3MzM1OFowazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
308 bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs
309 ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
310 AQEFAAOCAQ8AMIIBCgKCAQEA5Ky0UE9K+gFOznfwBvq2HfnQOOPGtVf4G9m63b5V
311 QNJYCSNiYxkGZW72ESM3XA8BledlkV9pwIm17+7ucB1Ed3efQjQDq2RSk5LDYDaa
312 r0Qzzy0EC3b9+AKA6mAoVY6s1Qws/YvM4esz0H+SVvtVcFqA46kRWhJN7M5ic1lu
313 d58fAq04BHqi5zOEOzfHJYPGUgQOxRTHluYkkkBrL2xioHHnOROshW+PIYFiAc/h
314 WPzuihPHnKaziPRw+O6O8ysnCxycQHgqtvx73T52eJdLxtr3ToRWaY/8VF/Cog5c
315 uvWEtg6EucGOszIH8O7eJWaJpVpAfZIX+c62MQWLpOLi/QIDAQABoyowKDAmBgNV
316 HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEF
317 BQADggEBAOhLAO/e0lGi9TZ2HiVi4sJ7KVQaBQHGhfsysILoQNHrNqDypPc/ZrSa
318 WQ2OqyUeltMnUdN5S1h3MKRZlbAeBQlwkPdjTzlzWkCMWB5BsfIGy5ovqmNQ7zPa
319 Khg5oxq3mU8ZLiJP4HngyU+hOOCt5tttex2S8ubjFT+3C3cydLKEOXCUPspaVkKn
320 Eq8WSBoYTvyUVmSi6+m6HGiowWsM5Qgj93IRW6JCbkgfPeKXC/5ykAPQcFHwNaKT
321 rpWokcavZyMbVjRsbzCQcc7n2j7tbLOu2svSLy6oXwG6n/bEagl5WpN2/TzQuwe7
322 f5ktutc4DDJSV7fuYYCuGumrHAjcELE=
323 -----END CERTIFICATE-----
333 -----BEGIN RSA PRIVATE KEY-----
334 MIIEpQIBAAKCAQEA5Ky0UE9K+gFOznfwBvq2HfnQOOPGtVf4G9m63b5VQNJYCSNi
335 YxkGZW72ESM3XA8BledlkV9pwIm17+7ucB1Ed3efQjQDq2RSk5LDYDaar0Qzzy0E
336 C3b9+AKA6mAoVY6s1Qws/YvM4esz0H+SVvtVcFqA46kRWhJN7M5ic1lud58fAq04
337 BHqi5zOEOzfHJYPGUgQOxRTHluYkkkBrL2xioHHnOROshW+PIYFiAc/hWPzuihPH
338 nKaziPRw+O6O8ysnCxycQHgqtvx73T52eJdLxtr3ToRWaY/8VF/Cog5cuvWEtg6E
339 ucGOszIH8O7eJWaJpVpAfZIX+c62MQWLpOLi/QIDAQABAoIBACf8mzs/4lh9Sg6I
340 ooxV4uqy+Fo6WlDzpQsZs7d6xOWk4ogWi+nQQnISSS0N/2w1o41W/UfCa3ejnRDr
341 sv4f4A0T+eFVvx6FWHs9urRkWAA16OldccufbyGjLm/NiMANRuOqUWO0woru2gyn
342 git7n6EZ8lfdBI+/i6jRHh4VkV+ROt5Zbp9zuJsj0yMqJH7J6Ebtl1jAF6PemLBL
343 yxdiYqR8LKTunTGGP/L+4K5a389oPDcJ1+YX805NEopmfrIhPr+BQYdz8905aVFk
344 FSS4TJy23EhFLzKf3+iSept6Giim+2yy2rv1RPCKgjOXbJ+4LD48xumDol6XWgYr
345 1CBzQIECgYEA/jBEGOjV02a9A3C5RJxZMawlGwGrvvALG2UrKvwQc595uxwrUw9S
346 Mn3ZQBEGnEWwEf44jSpWzp8TtejMxEvrU5243eWgwif1kqr1Mcj54DR7Qm15/hsj
347 M3nA2WscVG2OHBs4AwzMCHE2vfEAkbz71s6xonhg6zvsC26Zy3hYPqkCgYEA5k3k
348 OuCeG5FXW1/GzhvVFuhl6msNKzuUnLmJg6500XPny5Xo7W3RMvjtTM2XLt1USU6D
349 arMCCQ1A8ku1SoFdSw5RC6Fl8ZoUFBz9FPPwT6usQssGyFxiiqdHLvTlk12NNCk3
350 vJYsdQ+v/dKuZ8T4U3GTgQSwPTj6J0kJUf5y2jUCgYEA+hi/R8r/aArz+kiU4T78
351 O3Vm5NWWCD3ij8fQ23A7N6g3e7RRpF20wF02vmSCHowqmumI9swrsQyvthIiNxmD
352 pzfORvXCYIY0h2SR77QQt1qr1EYm+6/zyJgI+WL78s4APwNA7y9OKRhLhkN0DfDl
353 0Qp5mKPcqFbC/tSJmbsFCFECgYEAwlLC2rMgdV5jeWQNGWf+mv+ozu1ZBTuWn88l
354 qwiO5RSJZwysp3nb5MiJYh6vDAoQznIDDQrSEtUuEcOzypPxJh2EYO3kWMGLY5U6
355 Lm3OPUs7ZHhu1qytMRUISSS2eWucc4C72NJV3MhJ1T/pjQF0DuRsc5aDJoVm/bLw
356 vFCYlGkCgYEAgBDIIqdo1th1HE95SQfpP2wV/jA6CPamIciNwS3bpyhDBqs9oLUc
357 qzXidOpXAVYg1wl/BqpaCQcmmhCrnSLJYdOMpudVyLCCfYmBJ0bs2DCAe5ibGbL7
358 VruAOjS4yBepkXJU9xwKHxDmgTo/oQ5smq7SNOUWDSElVI/CyZ0x7qA=
359 -----END RSA PRIVATE KEY-----
369 -----BEGIN CERTIFICATE-----
370 MIIDQjCCAioCCQDxKQafEvp+VTANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJV
371 UzELMAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJy
372 aXBwbGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE5MDgw
373 NzE3MzM1OFoXDTQ2MTIyMzE3MzM1OFowYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
374 AkNBMRQwEgYDVQQHDAtMb3MgQW5nZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0
375 LXRlc3RzMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
376 ggEPADCCAQoCggEBAO9oqh72ttM7hjPnbMcJw0EuyULocEn2hlg4HE4YtzaxlRIz
377 dHm8nMkG/9yGmHBCuue/Gzssm/CzlduGezae01p8eaFUuEJsjxdrXe89Wk2QH+dm
378 Fn+SRbGcHaaTV/cyJrvusG7pOu95HL2eebuwiZ+tX5JP01R732iQt8Beeygh/W4P
379 n2f//fAxbdAIWzx2DH6cmSNe6lpoQe/MN15o8V3whutcC3fkis6wcA7BKZcdVdL2
380 daFWA6mt4SPWldOfWQVAIX4vRvheWPy34OLCgx+wZWg691Lwd1F+paarKombatUt
381 vKMTeolFYl3zkZZMYvR0Oyrt5NXUhRfmG7xR3bkCAwEAATANBgkqhkiG9w0BAQsF
382 AAOCAQEAggKO5WdtU67QPcAdo1Uar0SFouvVLwxJvoKlQ5rqF3idd0HnFVy7iojW
383 G2sZq7z8SNDMkUXZLbcbYNRyrZI0PdjfI0kyNpaa3pEcPcR8aOcTEOtW6V67FrPG
384 8aNYpr6a8PPq12aHzPSNjlUGot/qffGIQ0H2OqdWMOUXMMFnmH2KnnWi46Aq3gaF
385 uyHGrEczjJAK7NTzP8A7fbrmT00Sn6ft1FriQyhvDkUgPXBGWKpOFO84V27oo0ZL
386 xXQHDWcpX+8yNKynjafkXLx6qXwcySF2bKcTIRsxlN6WNRqZ+wqpNStkjuoFkYR/
387 IfW9PBfO/gCtNJQ+lqpoTd3kLBCAng==
388 -----END CERTIFICATE-----
398 -----BEGIN DH PARAMETERS-----
399 MIIBCAKCAQEAnJaaKu3U2a7ZVBvIC+NVNHXo9q6hNCazze+4pwXAKBVXH0ozInEw
400 WKozYxVJLW7dvDHdjdFOSuTLQDqaPW9zVMQKM0BKu81+JyfJi7C3HYKUw7ECVHp4
401 DLvhDe6N5eBj/t1FUwcfS2VNIx4QcJiw6FH3CwNNee1fIi5VTRJp2GLUuMCHkT/I
402 FTODJ+Anw12cJqLdgQfV74UV/Y7JCQl3/DOIy+2YkmX8vWVHX1h6EI5Gw4a3jgqF
403 gVyCOWoVCfgu37H5e7ERyoAxigiP8hMqoGpmJUYJghVKWoFgNUqXw+guVJ56eIuH
404 0wVs/LXflOZ42PJAiwv4LTNOtpG2pWGjOwIBAg==
405 -----END DH PARAMETERS-----
417 boost::asio::executor_work_guard<boost::asio::executor>
work;
436 self.do_peer(
id, std::move(
sock),
ssl);
459 using namespace boost::beast;
464 boost::optional<ssl_stream<ip::tcp::socket&>> ssl_stream;
469 ssl_stream.emplace(sock,
sslCtx_);
471 ssl_stream->handshake(ssl::stream_base::server, ec);
483 http::read(*ssl_stream, sb, req, ec);
485 http::read(sock, sb, req, ec);
490 auto path = req.target().to_string();
491 res.insert(
"Server",
"TrustedPublisherServer");
492 res.version(req.version());
493 res.keep_alive(req.keep_alive());
496 if (boost::starts_with(path,
"/validators"))
498 res.result(http::status::ok);
499 res.insert(
"Content-Type",
"application/json");
500 if (path ==
"/validators/bad")
501 res.body() =
"{ 'bad': \"1']" ;
502 else if (path ==
"/validators/missing")
503 res.body() =
"{\"version\": 1}";
507 if (boost::starts_with(path,
"/validators/refresh"))
509 boost::lexical_cast<unsigned int>(
514 else if (boost::starts_with(path,
"/textfile"))
517 res.result(http::status::ok);
518 res.insert(
"Content-Type",
"text/example");
521 boost::starts_with(path,
"/textfile/huge") ?
524 res.content_length(cl);
525 if (req.method() == http::verb::get)
528 for (
auto i=0; i<1024; ++i)
529 body <<
static_cast<char>(rand_int<short>(32, 126)),
530 res.body() = body.
str();
533 else if (boost::starts_with(path,
"/sleep/"))
535 auto const sleep_sec =
536 boost::lexical_cast<unsigned int>(path.substr(7));
540 else if (boost::starts_with(path,
"/redirect"))
542 if (boost::ends_with(path,
"/301"))
543 res.result(http::status::moved_permanently);
544 else if (boost::ends_with(path,
"/302"))
545 res.result(http::status::found);
546 else if (boost::ends_with(path,
"/307"))
547 res.result(http::status::temporary_redirect);
548 else if (boost::ends_with(path,
"/308"))
549 res.result(http::status::permanent_redirect);
552 if (boost::starts_with(path,
"/redirect_to/"))
554 location << path.substr(13);
556 else if (! boost::starts_with(path,
"/redirect_nolo"))
558 location << (ssl ?
"https://" :
"http://") <<
560 (boost::starts_with(path,
"/redirect_forever/") ?
561 path :
"/validators");
563 if (! location.
str().empty())
564 res.insert(
"Location", location.
str());
569 res.result(boost::beast::http::status::not_found);
570 res.insert(
"Content-Type",
"text/html");
571 res.body() =
"The file '" + path +
"' was not found";
575 res.prepare_payload();
580 res.result(boost::beast::http::status::internal_server_error);
581 res.version(req.version());
582 res.insert(
"Server",
"TrustedPublisherServer");
583 res.insert(
"Content-Type",
"text/html");
585 res.prepare_payload();
589 write(*ssl_stream, res, ec);
591 write(sock, res, ec);
593 if (ec || req.need_eof())
599 ssl_stream->shutdown(ec);