Line data Source code
1 : /**
2 : * @file RestEndpoint.cpp
3 : *
4 : * This is part of the DUNE DAQ Application Framework, copyright 2020.
5 : * Licensing/copyright details are in the COPYING file that you should have
6 : * received with this code.
7 : */
8 : #include "restcmd/RestEndpoint.hpp"
9 :
10 : #include "logging/Logging.hpp"
11 :
12 : #include <chrono>
13 : #include <future>
14 : #include <utility>
15 : #include <sstream>
16 :
17 : using namespace dunedaq::restcmd;
18 : using namespace Pistache;
19 :
20 0 : void RestEndpoint::init(size_t threads)
21 : {
22 0 : auto opts = Http::Endpoint::options()
23 0 : .threads(static_cast<int>(threads))
24 0 : .maxRequestSize(15728640) // 15MB
25 0 : .maxResponseSize(1048576) // 1MB
26 0 : .flags(Pistache::Tcp::Options::ReuseAddr)
27 0 : .flags(Pistache::Tcp::Options::ReusePort);
28 :
29 0 : http_endpoint_->init(opts);
30 0 : createRouting();
31 0 : http_client_options_ = Http::Client::options().threads(static_cast<int>(threads));
32 0 : http_client_->init(http_client_options_);
33 0 : }
34 :
35 0 : void RestEndpoint::start()
36 : {
37 0 : http_endpoint_->setHandler(router_.handler());
38 0 : http_endpoint_->serveThreaded();
39 0 : port_ = http_endpoint_->getPort();
40 0 : TLOG() << "REST server started on port " << port_;
41 0 : }
42 :
43 : // void RestEndpoint::serveTask()
44 : // {
45 : // }
46 :
47 0 : void RestEndpoint::shutdown()
48 : {
49 0 : http_endpoint_->shutdown();
50 : //server_thread_.join();
51 0 : http_client_->shutdown();
52 0 : }
53 :
54 0 : void RestEndpoint::createRouting()
55 : {
56 0 : using namespace Rest;
57 0 : Routes::Post(router_, "/command", Routes::bind(&RestEndpoint::handle_route_command, this));
58 0 : }
59 :
60 : inline void extendHeader(Http::Header::Collection& headers)
61 : {
62 : headers.add<Http::Header::AccessControlAllowOrigin>("*");
63 : headers.add<Http::Header::AccessControlAllowMethods>("POST,GET");
64 : headers.add<Http::Header::ContentType>(MIME(Text, Plain));
65 : }
66 :
67 : inline
68 : std::string
69 : getClientAddress(const Pistache::Rest::Request &request) {
70 : const auto xff = request.headers().tryGetRaw("X-Forwarded-For");
71 : if (!xff.isEmpty()) {
72 : //TODO: Strip of after first comma (to handle chained proxies).
73 : return xff.get().value();
74 : }
75 : return request.address().host();
76 : }
77 :
78 0 : void RestEndpoint::handle_route_command(const Rest::Request& request, Http::ResponseWriter response)
79 : {
80 0 : dunedaq::cmdlib::cmd::CommandReply meta;
81 0 : auto addr = request.address();
82 0 : auto headers = request.headers();
83 0 : auto ct = headers.get<Http::Header::ContentType>();
84 0 : if ( ct->mime() != accepted_mime_ ) {
85 0 : auto res = response.send(Http::Code::Not_Acceptable, "Not a JSON command!\n");
86 0 : } else {
87 0 : auto ansport = headers.getRaw("X-Answer-Port"); // RS: FIXME reply using headers
88 0 : auto anshost = headers.tryGetRaw("X-Answer-Host"); // RS: FIXME reply using headers
89 0 : meta.data["ans-port"] = ansport.value();
90 0 : meta.data["ans-host"] = ( !anshost.isEmpty() ? anshost.get().value() : addr.host() );
91 0 : command_callback_(nlohmann::json::parse(request.body()), meta); // RS: FIXME parse errors
92 0 : auto res = response.send(Http::Code::Accepted, "Command received\n");
93 0 : }
94 0 : }
95 :
96 0 : void RestEndpoint::handleResponseCommand(const cmdobj_t& cmd, dunedaq::cmdlib::cmd::CommandReply& meta)
97 : {
98 0 : dunedaq::cmdlib::cmd::Command command = cmd.get<dunedaq::cmdlib::cmd::Command>();
99 0 : std::ostringstream addrstr;
100 0 : addrstr << meta.data["ans-host"].get<std::string>() << ":" << meta.data["ans-port"].get<std::string>() << "/response";
101 0 : meta.data["cmdid"] = command.id;
102 0 : TLOG() << "Sending POST request to " << addrstr.str();
103 :
104 0 : nlohmann::json body_json;
105 0 : dunedaq::cmdlib::cmd::to_json(body_json, meta);
106 0 : auto response = http_client_->post(addrstr.str()).body(body_json.dump()).send();
107 0 : response.then(
108 0 : [&](Http::Response response) {
109 0 : TLOG() << "Response code = " << response.code();
110 0 : },
111 0 : [&](std::exception_ptr exc) {
112 : // handle response failure
113 0 : try{
114 0 : std::rethrow_exception(exc);
115 : }
116 0 : catch (const std::exception &e) {
117 0 : TLOG() << "Exception thrown by Http::Client::post() call: \"" << e.what() << "\"; errno = " << errno;
118 0 : }
119 0 : }
120 : );
121 0 : http_client_responses_.push_back(std::move(response));
122 0 : }
123 :
|