rippled
Loading...
Searching...
No Matches
TrustedPublisherServer.h
1#pragma once
2
3#include <test/jtx/envconfig.h>
4
5#include <xrpl/basics/base64.h>
6#include <xrpl/basics/random.h>
7#include <xrpl/basics/strHex.h>
8#include <xrpl/protocol/PublicKey.h>
9#include <xrpl/protocol/SecretKey.h>
10#include <xrpl/protocol/Sign.h>
11
12#include <boost/algorithm/string/predicate.hpp>
13#include <boost/asio.hpp>
14#include <boost/asio/ip/tcp.hpp>
15#include <boost/asio/ssl/stream.hpp>
16#include <boost/beast/core/flat_buffer.hpp>
17#include <boost/beast/http.hpp>
18#include <boost/beast/ssl.hpp>
19#include <boost/beast/version.hpp>
20#include <boost/lexical_cast.hpp>
21
22#include <memory>
23#include <thread>
24
25namespace xrpl {
26namespace test {
27
28class TrustedPublisherServer : public std::enable_shared_from_this<TrustedPublisherServer>
29{
30 using endpoint_type = boost::asio::ip::tcp::endpoint;
31 using address_type = boost::asio::ip::address;
32 using socket_type = boost::asio::ip::tcp::socket;
33
34 using req_type = boost::beast::http::request<boost::beast::http::string_body>;
35 using resp_type = boost::beast::http::response<boost::beast::http::string_body>;
36 using error_code = boost::system::error_code;
37
40 boost::asio::ip::tcp::acceptor acceptor_;
41 // Generates a version 1 validator list, using the int parameter as the
42 // actual version.
44 // Generates a version 2 validator list, using the int parameter as the
45 // actual version.
47
48 // The SSL context is required, and holds certificates
49 bool useSSL_;
50 boost::asio::ssl::context sslCtx_{boost::asio::ssl::context::tlsv12};
51
54
55 // Load a signed certificate into the ssl context, and configure
56 // the context for use with a server.
57 inline void
59 {
60 sslCtx_.set_password_callback(
61 [](std::size_t, boost::asio::ssl::context_base::password_purpose) { return "test"; });
62
63 sslCtx_.set_options(
64 boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 |
65 boost::asio::ssl::context::single_dh_use);
66
67 sslCtx_.use_certificate_chain(boost::asio::buffer(cert().data(), cert().size()));
68
69 sslCtx_.use_private_key(
70 boost::asio::buffer(key().data(), key().size()),
71 boost::asio::ssl::context::file_format::pem);
72
73 sslCtx_.use_tmp_dh(boost::asio::buffer(dh().data(), dh().size()));
74 }
75
76 struct BlobInfo
77 {
79 {
80 }
81
82 // base-64 encoded JSON containing the validator list.
84 // hex-encoded signature of the blob using the publisher's signing key
86 };
87
88public:
95
96 static std::string
98 PublicKey const& pk,
99 SecretKey const& sk,
100 PublicKey const& spk,
101 SecretKey const& ssk,
102 int seq)
103 {
105 st[sfSequence] = seq;
106 st[sfPublicKey] = pk;
107 st[sfSigningPubKey] = spk;
108
109 sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk);
110 sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature);
111
112 Serializer s;
113 st.add(s);
114
115 return base64_encode(std::string(static_cast<char const*>(s.data()), s.size()));
116 }
117
118 static Validator
120 {
121 auto const secret = randomSecretKey();
122 auto const masterPublic = derivePublicKey(KeyType::ed25519, secret);
123 auto const signingKeys = randomKeyPair(KeyType::secp256k1);
124 return {
125 masterPublic,
126 signingKeys.first,
127 makeManifestString(masterPublic, secret, signingKeys.first, signingKeys.second, 1)};
128 }
129
130 // TrustedPublisherServer must be accessed through a shared_ptr.
131 // This constructor is only public so std::make_shared has access.
132 // The function `make_TrustedPublisherServer` should be used to create
133 // instances.
134 // The `futures` member is expected to be structured as
135 // effective / expiration time point pairs for use in version 2 UNLs
137 boost::asio::io_context& ioc,
138 std::vector<Validator> const& validators,
139 NetClock::time_point validUntil,
141 bool useSSL = false,
142 int version = 1,
143 bool immediateStart = true,
144 int sequence = 1)
145 : sock_{ioc}
146 , ep_{boost::asio::ip::make_address(xrpl::test::getEnvLocalhostAddr()),
147 // 0 means let OS pick the port based on what's available
148 0}
149 , acceptor_{ioc}
150 , useSSL_{useSSL}
153 {
154 auto const keys = randomKeyPair(KeyType::secp256k1);
155 auto const manifest =
156 makeManifestString(publisherPublic_, publisherSecret_, keys.first, keys.second, 1);
157
158 std::vector<BlobInfo> blobInfo;
159 blobInfo.reserve(futures.size() + 1);
160 auto const [data, blob] = [&]() -> std::pair<std::string, std::string> {
161 // Builds the validator list, then encodes it into a blob.
162 std::string data = "{\"sequence\":" + std::to_string(sequence) +
163 ",\"expiration\":" + std::to_string(validUntil.time_since_epoch().count()) +
164 ",\"validators\":[";
165
166 for (auto const& val : validators)
167 {
168 data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) +
169 "\",\"manifest\":\"" + val.manifest + "\"},";
170 }
171 data.pop_back();
172 data += "]}";
173 std::string const blob = base64_encode(data);
174 return std::make_pair(data, blob);
175 }();
176 auto const sig = strHex(sign(keys.first, keys.second, makeSlice(data)));
177 blobInfo.emplace_back(blob, sig);
178 getList_ = [blob = blob, sig, manifest, version](int interval) {
179 // Build the contents of a version 1 format UNL file
181 l << "{\"blob\":\"" << blob << "\"" << ",\"signature\":\"" << sig << "\""
182 << ",\"manifest\":\"" << manifest << "\""
183 << ",\"refresh_interval\": " << interval << ",\"version\":" << version << '}';
184 return l.str();
185 };
186 for (auto const& future : futures)
187 {
188 std::string data = "{\"sequence\":" + std::to_string(++sequence) +
189 ",\"effective\":" + std::to_string(future.first.time_since_epoch().count()) +
190 ",\"expiration\":" + std::to_string(future.second.time_since_epoch().count()) +
191 ",\"validators\":[";
192
193 // Use the same set of validators for simplicity
194 for (auto const& val : validators)
195 {
196 data += "{\"validation_public_key\":\"" + strHex(val.masterPublic) +
197 "\",\"manifest\":\"" + val.manifest + "\"},";
198 }
199 data.pop_back();
200 data += "]}";
201 std::string const blob = base64_encode(data);
202 auto const sig = strHex(sign(keys.first, keys.second, makeSlice(data)));
203 blobInfo.emplace_back(blob, sig);
204 }
205 getList2_ = [blobInfo, manifest, version](int interval) {
206 // Build the contents of a version 2 format UNL file
207 // Use `version + 1` to get 2 for most tests, but have
208 // a "bad" version number for tests that provide an override.
210 for (auto const& info : blobInfo)
211 {
212 l << "{\"blob\":\"" << info.blob << "\"" << ",\"signature\":\"" << info.signature
213 << "\"},";
214 }
215 std::string blobs = l.str();
216 blobs.pop_back();
217 l.str(std::string());
218 l << "{\"blobs_v2\": [ " << blobs << "],\"manifest\":\"" << manifest << "\""
219 << ",\"refresh_interval\": " << interval << ",\"version\":" << (version + 1) << '}';
220 return l.str();
221 };
222
223 if (useSSL_)
224 {
225 // This holds the self-signed certificate used by the server
227 }
228 }
229
230 void
232 {
233 error_code ec;
234 acceptor_.open(ep_.protocol());
235 acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), ec);
236 acceptor_.bind(ep_);
237 acceptor_.listen(boost::asio::socket_base::max_listen_connections);
238 acceptor_.async_accept(
240 if (auto p = wp.lock())
241 {
242 p->on_accept(ec);
243 }
244 });
245 }
246
247 void
249 {
250 error_code ec;
251 acceptor_.close(ec);
252 // TODO: consider making this join
253 // any running do_peer threads
254 }
255
257 {
258 stop();
259 }
260
263 {
264 return acceptor_.local_endpoint();
265 }
266
267 PublicKey const&
269 {
270 return publisherPublic_;
271 }
272
273 /* CA/self-signed certs :
274 *
275 * The following three methods return certs/keys used by
276 * server and/or client to do the SSL handshake. These strings
277 * were generated using the script below. The server key and cert
278 * are used to configure the server (see load_server_certificate
279 * above). The ca.crt should be used to configure the client
280 * when ssl verification is enabled.
281 *
282 * note:
283 * cert() ==> server.crt
284 * key() ==> server.key
285 * ca_cert() ==> ca.crt
286 * dh() ==> dh.pem
287 ```
288 #!/usr/bin/env bash
289
290 mkdir -p /tmp/__certs__
291 pushd /tmp/__certs__
292 rm *.crt *.key *.pem
293
294 # generate CA
295 openssl genrsa -out ca.key 2048
296 openssl req -new -x509 -nodes -days 10000 -key ca.key -out ca.crt \
297 -subj "/C=US/ST=CA/L=Los
298 Angeles/O=rippled-unit-tests/CN=example.com" # generate private cert
299 openssl genrsa -out server.key 2048
300 # Generate certificate signing request
301 # since our unit tests can run in either ipv4 or ipv6 mode,
302 # we need to use extensions (subjectAltName) so that we can
303 # associate both ipv4 and ipv6 localhost addresses with this cert
304 cat >"extras.cnf" <<EOF
305 [req]
306 req_extensions = v3_req
307 distinguished_name = req_distinguished_name
308
309 [req_distinguished_name]
310
311 [v3_req]
312 subjectAltName = @alt_names
313
314 [alt_names]
315 DNS.1 = localhost
316 IP.1 = ::1
317 EOF
318 openssl req -new -key server.key -out server.csr \
319 -config extras.cnf \
320 -subj "/C=US/ST=California/L=San
321 Francisco/O=rippled-unit-tests/CN=127.0.0.1" \
322
323 # Create public certificate by signing with our CA
324 openssl x509 -req -days 10000 -in server.csr -CA ca.crt -CAkey ca.key
325 -out server.crt \ -extfile extras.cnf -set_serial 01 -extensions v3_req
326
327 # generate DH params for server
328 openssl dhparam -out dh.pem 2048
329 # verify certs
330 openssl verify -CAfile ca.crt server.crt
331 openssl x509 -in server.crt -text -noout
332 popd
333 ```
334 */
335 static std::string const&
337 {
338 static std::string const cert{R"cert(
339-----BEGIN CERTIFICATE-----
340MIIDczCCAlugAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEL
341MAkGA1UECAwCQ0ExFDASBgNVBAcMC0xvcyBBbmdlbGVzMRswGQYDVQQKDBJyaXBw
342bGVkLXVuaXQtdGVzdHMxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIyMDIwNTIz
343NDk0M1oXDTQ5MDYyMzIzNDk0M1owazELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
344bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoMEnJpcHBs
345ZWQtdW5pdC10ZXN0czESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
346AQEFAAOCAQ8AMIIBCgKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJ
347qTuBUcVcTRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs
348+T/qkwmc/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9f
349n9vrxnWX9LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3
350zVDe7+sxR//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0q
351S1Uj77mhwxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABoyowKDAmBgNV
352HREEHzAdgglsb2NhbGhvc3SHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEL
353BQADggEBAJkUFNS0CeEAKvo0ttzooXnCDH3esj2fwmLJQYLUGsAF8DFrFHTqZEcx
354hFRdr0ftEb/VKpV9dVF6xtSoMU56kHOnhbHEWADyqdKUkCDjrGBet5QdWmEwNV2L
355nYrwGQBAybMt/+1XMUV8HeLFJNHnyxfQYcW0fUsrmNGk8W0kzWuuq88qbhfXZAIx
356KiXrzYpLlM0RlpWXRfYQ6mTdSrRrLnEo5MklizVgNB8HYX78lxa06zP08oReQcfT
357GSGO8NEEq8BTVmp69zD1JyfvQcXzsi7WtkAX+/EOFZ7LesnZ6VsyjZ74wECCaQuD
358X1yu/XxHqchM+DOzzVw6wRKaM7Zsk80=
359-----END CERTIFICATE-----
360)cert"};
361 return cert;
362 }
363
364 static std::string const&
365 key()
367 static std::string const key{R"pkey(
368-----BEGIN RSA PRIVATE KEY-----
369MIIEpAIBAAKCAQEAueZ1hgRxwPgfeVx2AdngUYx7zYcaxcGYXyqi7izJqTuBUcVc
370TRC/9Ip67RAEhfcgGudRS/a4Sv1ljwiRknSCcD/ZjzOFDLgbqYGSZNEs+T/qkwmc
371/L+Pbzf85HM7RjeGOd6NDQy9+oOBbUtqpTxcSGa4ln+YBFUSeoS1Aa9fn9vrxnWX
3729LgTu5dSWzH5TqFIti+Zs/v0PFjEivBIAOHPslmnzg/wCr99I6z9CAR3zVDe7+sx
373R//ivpeVE7FWjgkGixnUpZAqn69zNkJjMLNXETgOYskZdMIgbVOMr+0qS1Uj77mh
374wxKfpnB6TqUVvWLBvmBDzPjf0m0NcCf9UAjqPwIDAQABAoIBAEC9MDpOu+quvg8+
375kt4MKSFdIhQuM7WguNaTe5AkSspDrcJzT7SK275mp259QIYCzMxxuA8TSZTb8A1C
376t6dgKbi7k6FaGMCYMRHzzK6NZfMbPi6cj245q9LYlZpdQswuM/FdPpPH1zUxrNYK
377CIaooZ6ZHzlSD/eaRMgkBQEkONHrZZtEinLIvKedwssPCaXkIISmt7MFQTDOlxkf
378K0Mt1mnRREPYbYSfPEEfIyy/KDIiB5AzgGt+uPOn8Oeb1pSqy69jpYcfhSj+bo4S
379UV6qTuTfBd4qkkNI6d/Z7DcDJFFlfloG/vVgGk/beWNnL2e39vzxiebB3w+MQn4F
380Wyx5mCECgYEA22z1/ihqt9LIAWtP42oSS3S/RxlFzpp5d7QfNqFnEoVgeRhQzleP
381pRJIzVXpMYBxexZYqZA/q8xBSggz+2gmRoYnW20VIzl14DsSH378ye3FRwJB0tLy
382dWU8DC7ZB5XQCTvI9UY3voJNToknODw7RCNO1h3V3T1y6JRLdcLskk8CgYEA2OLy
383aE5bvsUaLBSv7W9NFhSuZ0p9Y0pFmRgHI7g8i/AgRZ0BgiE8u8OZSHmPJPMaNs/h
384YIEIrlsgDci1PzwrUYseRp/aiVE1kyev09/ihqRXTPpLQu6h/d63KRe/06W3t5X3
385Dmfj49hH5zGPBI/0y1ECV/n0fwnRhxSv7fNr3RECgYBEuFpOUAAkNApZj29ErNqv
3868Q9ayAp5yx1RpQLFjEUIoub05e2gwgGF1DUiwc43p59iyjvYVwnp1x13fxwwl4yt
387N6Sp2H7vOja1lCp33MB0yVeohodw7InsxFjLA/0KiBvQWH32exhIPOzTNNcooIx7
388KYeuPUfWc0FCn/cGGZcXtwKBgQC1hp1k99CKBuY05suoanOWe5DNGud/ZvaBgD7Z
389gqYKadxY52QPyknOzZNJuZQ5VM8n+S2lW9osNFDLuKUaW/3Vrh6U9c4vCC1TEPB0
3904PnzvzDiWMsNJjWnCfU7C4meVyFBIt84y3NNjAQCWNRe+S3lzdOsVqRwf4NDD+l/
391uzEYQQKBgQCJczIlwobm1Y6O41hbGZhZL/CGMNS6Z0INi2yasV0WDqYlh7XayHMD
392cK55dMILcbHqeIBq/wR6sIhw6IJcaDBfFfrJiKKDilfij2lHxR2FQrEngtTCCRV+
393ZzARzaWhQPvbDqEtLJDWuXZNXfL8/PTIs5NmuKuQ8F4+gQJpkQgwaw==
394-----END RSA PRIVATE KEY-----
395)pkey"};
396 return key;
397 }
398
399 static std::string const&
400 ca_cert()
401 {
402 static std::string const cert{R"cert(
403-----BEGIN CERTIFICATE-----
404MIIDpzCCAo+gAwIBAgIUWc45WqaaNuaSLoFYTMC/Mjfqw/gwDQYJKoZIhvcNAQEL
405BQAwYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQHDAtMb3MgQW5n
406ZWxlczEbMBkGA1UECgwScmlwcGxlZC11bml0LXRlc3RzMRQwEgYDVQQDDAtleGFt
407cGxlLmNvbTAeFw0yMjAyMDUyMzQ5MDFaFw00OTA2MjMyMzQ5MDFaMGMxCzAJBgNV
408BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLTG9zIEFuZ2VsZXMxGzAZBgNV
409BAoMEnJpcHBsZWQtdW5pdC10ZXN0czEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEi
410MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0f2JBW2XNW2wT5/ajX2qxmUY+
411aNJGfpV6gZ5CmwdQpbHrPPvJoskxwsCyr3GifzT/GtCpmb1fiu59uUAPxQEYCxiq
412V+HchX4g4Vl27xKJ0P+usxuEED9v7TCteKum9u9eMZ8UDF0fspXcnWGs9fXlyoTj
413uTRP1SBQllk44DPc/KzlrtH+QNXmr9XQnP8XvwWCgJXMx87voxEGiFFOVhkSSAOv
414v+OUGgEuq0NPgwv2LHBlYHSdkoU9F5Z/TmkCAFMShbyoUjldIz2gcWXjN2tespGo
415D6qYvasvPIpmcholBBkc0z8QDt+RNq+Wzrults7epJXy/u+txGK9cHCNlLCpAgMB
416AAGjUzBRMB0GA1UdDgQWBBS1oydh+YyqDNOFKYOvOtVMWKqV4zAfBgNVHSMEGDAW
417gBS1oydh+YyqDNOFKYOvOtVMWKqV4zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
418DQEBCwUAA4IBAQCDPyGKQwQ8Lz0yEgvIl/Uo9BtwAzlvjrLM/39qhStLQqDGSs2Q
419xFIbtjzjuLf5vR3q6OJ62CCvzqXgHkJ+hzVN/tAvyliGTdjJrK+xv1M5a+XipO2f
420c9lb4gRbFL/DyoeoWgb1Rkv3gFf0FlCYH+ZUcYb9ZYCRlGtFgOcxJI2g+T7jSLFp
4218+hSzQ6W5Sp9L6b5iJyCww1vjBvBqzNyZMNeB4gXGtd6z9vMDSvKboTdGD7wcFB+
422mRMyNekaRw+Npy4Hjou5sx272cXHHmPCSF5TjwdaibSaGjx1k0Q50mOf7S9KG5b5
4237X1e3FekJlaD02EBEhtkXURIxogOQALdFncj
424-----END CERTIFICATE-----
425)cert"};
426 return cert;
427 }
428
429 static std::string const&
430 dh()
431 {
432 static std::string const dh{R"dh(
433-----BEGIN DH PARAMETERS-----
434MIIBCAKCAQEAp2I2fWEUZ3sCNfitSRC/MdAhJE/bS+NO0O2tWdIdlvmIFE6B5qhC
435sGW9ojrQT8DTxBvGAcbjr/jagmlE3BV4oSnxyhP37G2mDvMOJ29J3NvFD/ZFAW0d
436BvZJ1RNvMu29NmVCyt6/jgzcqrqnami9uD93aK+zaVrlPsPEYM8xB19HXwqsEYCL
437ux2B7sqXm9Ts74HPg/EV+pcVon9phxNWxxgHlOvFc2QjZ3hXH++kzmJ4vs7N/XDB
438xbEQ+TUZ5jbJGSeBqNFKFeuOUQGJ46Io0jBSYd4rSmKUXkvElQwR+n7KF3jy1uAt
439/8hzd8tHn9TyW7Q2/CPkOA6dCXzltpOSowIBAg==
440-----END DH PARAMETERS-----
441)dh"};
442 return dh;
443 }
444
445private:
446 struct lambda
447 {
448 int id;
449 TrustedPublisherServer& self;
451 boost::asio::executor_work_guard<boost::asio::executor> work;
452 bool ssl;
453
454 lambda(int id_, TrustedPublisherServer& self_, socket_type&& sock_, bool ssl_)
455 : id(id_), self(self_), sock(std::move(sock_)), work(sock_.get_executor()), ssl(ssl_)
456 {
457 }
458
459 void
460 operator()()
461 {
462 self.do_peer(id, std::move(sock), ssl);
463 }
464 };
465
466 void
468 {
469 if (ec || !acceptor_.is_open())
470 return;
471
472 static int id_ = 0;
473 std::thread{lambda{++id_, *this, std::move(sock_), useSSL_}}.detach();
474 acceptor_.async_accept(
476 if (auto p = wp.lock())
477 {
478 p->on_accept(ec);
479 }
480 });
481 }
482
483 void
484 do_peer(int id, socket_type&& s, bool ssl)
485 {
486 using namespace boost::beast;
487 using namespace boost::asio;
488 socket_type sock(std::move(s));
489 flat_buffer sb;
490 error_code ec;
492
493 if (ssl)
494 {
495 // Construct the stream around the socket
496 ssl_stream.emplace(sock, sslCtx_);
497 // Perform the SSL handshake
498 ssl_stream->handshake(ssl::stream_base::server, ec);
499 if (ec)
500 return;
501 }
502
503 for (;;)
504 {
505 resp_type res;
506 req_type req;
507 try
508 {
509 if (ssl)
510 http::read(*ssl_stream, sb, req, ec);
511 else
512 http::read(sock, sb, req, ec);
513
514 if (ec)
515 break;
516
517 std::string_view const path = req.target();
518 res.insert("Server", "TrustedPublisherServer");
519 res.version(req.version());
520 res.keep_alive(req.keep_alive());
521 bool prepare = true;
522
523 if (boost::starts_with(path, "/validators2"))
524 {
525 res.result(http::status::ok);
526 res.insert("Content-Type", "application/json");
527 if (path == "/validators2/bad")
528 res.body() = "{ 'bad': \"2']";
529 else if (path == "/validators2/missing")
530 res.body() = "{\"version\": 2}";
531 else
532 {
533 int refresh = 5;
534 constexpr char const* refreshPrefix = "/validators2/refresh/";
535 if (boost::starts_with(path, refreshPrefix))
536 refresh = boost::lexical_cast<unsigned int>(
537 path.substr(strlen(refreshPrefix)));
538 res.body() = getList2_(refresh);
539 }
540 }
541 else if (boost::starts_with(path, "/validators"))
542 {
543 res.result(http::status::ok);
544 res.insert("Content-Type", "application/json");
545 if (path == "/validators/bad")
546 res.body() = "{ 'bad': \"1']";
547 else if (path == "/validators/missing")
548 res.body() = "{\"version\": 1}";
549 else
550 {
551 int refresh = 5;
552 constexpr char const* refreshPrefix = "/validators/refresh/";
553 if (boost::starts_with(path, refreshPrefix))
554 refresh = boost::lexical_cast<unsigned int>(
555 path.substr(strlen(refreshPrefix)));
556 res.body() = getList_(refresh);
557 }
558 }
559 else if (boost::starts_with(path, "/textfile"))
560 {
561 prepare = false;
562 res.result(http::status::ok);
563 res.insert("Content-Type", "text/example");
564 // if huge was requested, lie about content length
565 std::uint64_t const cl = boost::starts_with(path, "/textfile/huge")
567 : 1024;
568 res.content_length(cl);
569 if (req.method() == http::verb::get)
570 {
572 for (auto i = 0; i < 1024; ++i)
573 body << static_cast<char>(rand_int<short>(32, 126)),
574 res.body() = body.str();
575 }
576 }
577 else if (boost::starts_with(path, "/sleep/"))
578 {
579 auto const sleep_sec = boost::lexical_cast<unsigned int>(path.substr(7));
581 }
582 else if (boost::starts_with(path, "/redirect"))
583 {
584 if (boost::ends_with(path, "/301"))
585 res.result(http::status::moved_permanently);
586 else if (boost::ends_with(path, "/302"))
587 res.result(http::status::found);
588 else if (boost::ends_with(path, "/307"))
589 res.result(http::status::temporary_redirect);
590 else if (boost::ends_with(path, "/308"))
591 res.result(http::status::permanent_redirect);
592
593 std::stringstream location;
594 if (boost::starts_with(path, "/redirect_to/"))
595 {
596 location << path.substr(13);
597 }
598 else if (!boost::starts_with(path, "/redirect_nolo"))
599 {
600 location << (ssl ? "https://" : "http://") << local_endpoint()
601 << (boost::starts_with(path, "/redirect_forever/")
602 ? path
603 : "/validators");
604 }
605 if (!location.str().empty())
606 res.insert("Location", location.str());
607 }
608 else
609 {
610 // unknown request
611 res.result(boost::beast::http::status::not_found);
612 res.insert("Content-Type", "text/html");
613 res.body() = "The file '" + std::string(path) +
614 "' was not "
615 "found";
616 }
617
618 if (prepare)
619 res.prepare_payload();
620 }
621 catch (std::exception const& e)
622 {
623 res = {};
624 res.result(boost::beast::http::status::internal_server_error);
625 res.version(req.version());
626 res.insert("Server", "TrustedPublisherServer");
627 res.insert("Content-Type", "text/html");
628 res.body() = std::string{"An internal error occurred"} + e.what();
629 res.prepare_payload();
630 }
631
632 if (ssl)
633 write(*ssl_stream, res, ec);
634 else
635 write(sock, res, ec);
636
637 if (ec || req.need_eof())
638 break;
639 }
640
641 // Perform the SSL shutdown
642 if (ssl)
643 ssl_stream->shutdown(ec);
644 }
645};
646
649 boost::asio::io_context& ioc,
651 NetClock::time_point validUntil,
653 bool useSSL = false,
654 int version = 1,
655 bool immediateStart = true,
656 int sequence = 1)
657{
659 ioc, validators, validUntil, futures, useSSL, version, sequence);
660 if (immediateStart)
661 r->start();
662 return r;
663}
664
665} // namespace test
666} // namespace xrpl
A public key.
Definition PublicKey.h:42
void add(Serializer &s) const override
Definition STObject.cpp:119
A secret key.
Definition SecretKey.h:18
std::size_t size() const noexcept
Definition Serializer.h:50
void const * data() const noexcept
Definition Serializer.h:56
static std::string makeManifestString(PublicKey const &pk, SecretKey const &sk, PublicKey const &spk, SecretKey const &ssk, int seq)
boost::beast::http::request< boost::beast::http::string_body > req_type
void do_peer(int id, socket_type &&s, bool ssl)
boost::asio::ip::tcp::acceptor acceptor_
std::function< std::string(int)> getList_
TrustedPublisherServer(boost::asio::io_context &ioc, std::vector< Validator > const &validators, NetClock::time_point validUntil, std::vector< std::pair< NetClock::time_point, NetClock::time_point > > const &futures, bool useSSL=false, int version=1, bool immediateStart=true, int sequence=1)
boost::asio::ip::tcp::endpoint endpoint_type
boost::asio::ip::tcp::socket socket_type
boost::beast::http::response< boost::beast::http::string_body > resp_type
std::function< std::string(int)> getList2_
Set the regular signature on a JTx.
Definition sig.h:15
T emplace_back(T... args)
T emplace(T... args)
T insert(T... args)
T is_same_v
T make_pair(T... args)
T max(T... args)
T move(T... args)
STL namespace.
void write(nudb::detail::ostream &os, std::size_t t)
Definition varint.h:115
auto const data
General field definitions, or fields used in multiple transaction namespaces.
void sign(Json::Value &jv, Account const &account, Json::Value &sigObject)
Sign automatically into a specific Json field of the jv object.
Definition utility.cpp:27
char const * getEnvLocalhostAddr()
Definition envconfig.h:16
std::shared_ptr< TrustedPublisherServer > make_TrustedPublisherServer(boost::asio::io_context &ioc, std::vector< TrustedPublisherServer::Validator > const &validators, NetClock::time_point validUntil, std::vector< std::pair< NetClock::time_point, NetClock::time_point > > const &futures, bool useSSL=false, int version=1, bool immediateStart=true, int sequence=1)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
KeyType
Definition KeyType.h:8
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
SField const sfGeneric
std::string base64_encode(std::uint8_t const *data, std::size_t len)
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
@ manifest
Manifest.
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:215
T pop_back(T... args)
T reserve(T... args)
T sleep_for(T... args)
T str(T... args)
T strlen(T... args)
lambda(int id_, TrustedPublisherServer &self_, socket_type &&sock_, bool ssl_)
boost::asio::executor_work_guard< boost::asio::executor > work
Set the sequence number on a JTx.
Definition seq.h:14
T substr(T... args)
T time_since_epoch(T... args)
T to_string(T... args)
T what(T... args)