Commit ed6cc808 authored by Dominik Charousset's avatar Dominik Charousset

Add HTTP module test and fix SSL setup

parent 1f04207b
...@@ -26,6 +26,7 @@ option(CAF_ENABLE_CPACK "Enable packaging via CPack" OFF) ...@@ -26,6 +26,7 @@ option(CAF_ENABLE_CPACK "Enable packaging via CPack" OFF)
option(CAF_ENABLE_CURL_EXAMPLES "Build examples with libcurl" OFF) option(CAF_ENABLE_CURL_EXAMPLES "Build examples with libcurl" OFF)
option(CAF_ENABLE_PROTOBUF_EXAMPLES "Build examples with Google Protobuf" OFF) option(CAF_ENABLE_PROTOBUF_EXAMPLES "Build examples with Google Protobuf" OFF)
option(CAF_ENABLE_QT6_EXAMPLES "Build examples with the Qt6 framework" OFF) option(CAF_ENABLE_QT6_EXAMPLES "Build examples with the Qt6 framework" OFF)
option(CAF_ENABLE_ROBOT_TESTS "Add the Robot tests to CTest " OFF)
option(CAF_ENABLE_RUNTIME_CHECKS "Build CAF with extra runtime assertions" OFF) option(CAF_ENABLE_RUNTIME_CHECKS "Build CAF with extra runtime assertions" OFF)
# -- CAF options that are on by default ---------------------------------------- # -- CAF options that are on by default ----------------------------------------
...@@ -401,6 +402,12 @@ if(CAF_ENABLE_TOOLS) ...@@ -401,6 +402,12 @@ if(CAF_ENABLE_TOOLS)
add_subdirectory(tools) add_subdirectory(tools)
endif() endif()
# -- optionally add the Robot tests to CTest -----------------------------------
if(CAF_ENABLE_TESTING AND CAF_ENABLE_ROBOT_TESTS)
add_subdirectory(robot)
endif()
# -- add top-level compiler and linker flags that propagate to clients --------- # -- add top-level compiler and linker flags that propagate to clients ---------
# Disable warnings regarding C++ classes at ABI boundaries on MSVC. # Disable warnings regarding C++ classes at ABI boundaries on MSVC.
......
...@@ -48,11 +48,11 @@ struct kvs_actor_state { ...@@ -48,11 +48,11 @@ struct kvs_actor_state {
caf::behavior make_behavior() { caf::behavior make_behavior() {
return { return {
[this](caf::get_atom, const std::string& key) -> std::string { [this](caf::get_atom, const std::string& key) -> caf::result<std::string> {
if (auto i = data.find(key); i != data.end()) if (auto i = data.find(key); i != data.end())
return i->second; return i->second;
else else
return {}; return make_error(caf::sec::no_such_key, key + " not found");
}, },
[this](caf::put_atom, const std::string& key, std::string& value) { [this](caf::put_atom, const std::string& key, std::string& value) {
data.insert_or_assign(key, std::move(value)); data.insert_or_assign(key, std::move(value));
...@@ -122,7 +122,12 @@ int caf_main(caf::actor_system& sys, const config& cfg) { ...@@ -122,7 +122,12 @@ int caf_main(caf::actor_system& sys, const config& cfg) {
prom.respond(http::status::ok, "text/plain", value); prom.respond(http::status::ok, "text/plain", value);
}, },
[prom](const caf::error& what) mutable { [prom](const caf::error& what) mutable {
prom.respond(http::status::internal_server_error, what); if (what == caf::sec::no_such_key)
prom.respond(http::status::not_found, "text/plain",
"Key not found.");
else
prom.respond(http::status::internal_server_error,
what);
}); });
}) })
.route("/api/<arg>", http::method::post, .route("/api/<arg>", http::method::post,
...@@ -159,6 +164,10 @@ int caf_main(caf::actor_system& sys, const config& cfg) { ...@@ -159,6 +164,10 @@ int caf_main(caf::actor_system& sys, const config& cfg) {
prom.respond(http::status::internal_server_error, what); prom.respond(http::status::internal_server_error, what);
}); });
}) })
.route("/status", http::method::get,
[kvs](http::responder& res) {
res.respond(http::status::no_content);
})
// Launch the server. // Launch the server.
.start(); .start();
// Report any error to the user. // Report any error to the user.
......
...@@ -37,6 +37,7 @@ caf_add_component( ...@@ -37,6 +37,7 @@ caf_add_component(
src/net/dsl/config_base.cpp src/net/dsl/config_base.cpp
src/net/generic_lower_layer.cpp src/net/generic_lower_layer.cpp
src/net/generic_upper_layer.cpp src/net/generic_upper_layer.cpp
src/net/http/config.cpp
src/net/http/lower_layer.cpp src/net/http/lower_layer.cpp
src/net/http/method.cpp src/net/http/method.cpp
src/net/http/request.cpp src/net/http/request.cpp
......
...@@ -47,11 +47,12 @@ public: ...@@ -47,11 +47,12 @@ public:
static constexpr std::string_view name = "lazy"; static constexpr std::string_view name = "lazy";
lazy(std::string host, uint16_t port) lazy(std::shared_ptr<ssl::context> ctx, std::string host, uint16_t port)
: server(server_address{std::move(host), port}) { : has_ctx(std::move(ctx)), server(server_address{std::move(host), port}) {
} }
explicit lazy(const uri& addr) : server(addr) { lazy(std::shared_ptr<ssl::context> ctx, const uri& addr)
: has_ctx(std::move(ctx)), server(addr) {
// nop // nop
} }
...@@ -68,14 +69,17 @@ public: ...@@ -68,14 +69,17 @@ public:
size_t max_retry_count = 0; size_t max_retry_count = 0;
}; };
static constexpr auto lazy_v = client_config_tag<lazy>{}; using lazy_t = client_config_tag<lazy>;
static constexpr auto lazy_v = lazy_t{};
/// Configuration for a client that uses a user-provided socket. /// Configuration for a client that uses a user-provided socket.
class CAF_NET_EXPORT socket : public has_ctx { class CAF_NET_EXPORT socket : public has_ctx {
public: public:
static constexpr std::string_view name = "socket"; static constexpr std::string_view name = "socket";
explicit socket(stream_socket fd) : fd(fd) { socket(std::shared_ptr<ssl::context> ctx, stream_socket fd)
: has_ctx(std::move(ctx)), fd(fd) {
// nop // nop
} }
...@@ -112,7 +116,9 @@ public: ...@@ -112,7 +116,9 @@ public:
} }
}; };
static constexpr auto socket_v = client_config_tag<socket>{}; using socket_t = client_config_tag<socket>;
static constexpr auto socket_v = socket_t{};
/// Configuration for a client that uses an already established SSL /// Configuration for a client that uses an already established SSL
/// connection. /// connection.
...@@ -145,31 +151,17 @@ public: ...@@ -145,31 +151,17 @@ public:
ssl::connection state; ssl::connection state;
}; };
static constexpr auto conn_v = client_config_tag<conn>{}; using conn_t = client_config_tag<conn>;
static constexpr auto fail_v = client_config_tag<error>{}; static constexpr auto conn_v = conn_t{};
template <class Base> using fail_t = client_config_tag<error>;
class value : public config_impl<Base, lazy, socket, conn> {
public:
using super = config_impl<Base, lazy, socket, conn>;
using super::super; static constexpr auto fail_v = fail_t{};
template <class T, class From, class... Args> using value = config_impl<lazy, socket, conn>;
static auto make(client_config_tag<T>, From&& from, Args&&... args) {
static_assert(std::is_constructible_v<T, Args...>);
return make_counted<value>(std::forward<From>(from),
std::in_place_type<T>,
std::forward<Args>(args)...);
}
};
}; };
template <class Base> using client_config_value = client_config::value;
using client_config_value = client_config::value<Base>;
template <class Base>
using client_config_ptr = intrusive_ptr<client_config_value<Base>>;
} // namespace caf::net::dsl } // namespace caf::net::dsl
...@@ -17,10 +17,10 @@ ...@@ -17,10 +17,10 @@
namespace caf::net::dsl { namespace caf::net::dsl {
/// Base type for client factories for use with `can_connect`. /// Base type for client factories for use with `can_connect`.
template <class ConfigBase, class Derived> template <class Config, class Derived>
class client_factory_base { class client_factory_base {
public: public:
using config_type = client_config_value<ConfigBase>; using config_type = Config;
using trait_type = typename config_type::trait_type; using trait_type = typename config_type::trait_type;
......
...@@ -54,6 +54,8 @@ public: ...@@ -54,6 +54,8 @@ public:
+ std::string{name()}); + std::string{name()});
} }
/// Inspects the data of this configuration and returns a pointer to it as
/// `has_ctx` instance if possible, `nullptr` otherwise.
virtual has_ctx* as_has_ctx() noexcept = 0; virtual has_ctx* as_has_ctx() noexcept = 0;
bool failed() const noexcept { bool failed() const noexcept {
...@@ -81,31 +83,10 @@ public: ...@@ -81,31 +83,10 @@ public:
} }
}; };
/// Simple base class for a configuration with a trait member. template <class... Data>
template <class Trait> class config_impl : public config_base {
class config_with_trait : public config_base {
public: public:
using trait_type = Trait; using super = config_base;
explicit config_with_trait(multiplexer* mpx) : config_base(mpx) {
// nop
}
config_with_trait(const config_with_trait&) = default;
config_with_trait& operator=(const config_with_trait&) = default;
/// Configures various aspects of the protocol stack such as in- and output
/// types.
Trait trait;
};
template <class Base, class... Data>
class config_impl : public Base {
public:
using super = Base;
static_assert(std::is_base_of_v<config_base, super>);
template <class From, class... Args> template <class From, class... Args>
explicit config_impl(From&& from, Args&&... args) explicit config_impl(From&& from, Args&&... args)
......
...@@ -35,23 +35,16 @@ public: ...@@ -35,23 +35,16 @@ public:
static constexpr auto fail_v = generic_config_tag<error>{}; static constexpr auto fail_v = generic_config_tag<error>{};
template <class Base> class value : public config_impl<lazy> {
class value : public config_impl<Base, lazy> {
public: public:
using super = config_impl<Base, lazy>; using super = config_impl<lazy>;
using super::super; explicit value(multiplexer* mpx) : super(mpx, std::in_place_type<lazy>) {
// nop
static auto make(multiplexer* mpx) {
return make_counted<value>(mpx, std::in_place_type<lazy>);
} }
}; };
}; };
template <class Base> using generic_config_value = typename generic_config::value;
using generic_config_value = typename generic_config::value<Base>;
template <class Base>
using generic_config_ptr = intrusive_ptr<generic_config_value<Base>>;
} // namespace caf::net::dsl } // namespace caf::net::dsl
...@@ -18,6 +18,22 @@ namespace caf::net::dsl { ...@@ -18,6 +18,22 @@ namespace caf::net::dsl {
/// Configuration for a client that uses a user-provided socket. /// Configuration for a client that uses a user-provided socket.
class has_ctx { class has_ctx {
public: public:
using ctx_ptr = std::shared_ptr<ssl::context>;
has_ctx() = default;
has_ctx(ctx_ptr ptr) : ctx(std::move(ptr)) {
// nop
}
has_ctx(has_ctx&&) = default;
has_ctx(const has_ctx&) = default;
has_ctx& operator=(has_ctx&&) = default;
has_ctx& operator=(const has_ctx&) = default;
/// SSL context for secure servers. /// SSL context for secure servers.
std::shared_ptr<ssl::context> ctx; std::shared_ptr<ssl::context> ctx;
......
...@@ -33,9 +33,11 @@ public: ...@@ -33,9 +33,11 @@ public:
public: public:
static constexpr std::string_view name = "lazy"; static constexpr std::string_view name = "lazy";
lazy(uint16_t port, std::string bind_address) lazy(std::shared_ptr<ssl::context> ctx, uint16_t port,
: port(port), bind_address(std::move(bind_address)) { std::string bind_address)
// nop : has_ctx(std::move(ctx)),
port(port),
bind_address(std::move(bind_address)) {
} }
/// The port number to bind to. /// The port number to bind to.
...@@ -48,14 +50,17 @@ public: ...@@ -48,14 +50,17 @@ public:
bool reuse_addr = true; bool reuse_addr = true;
}; };
static constexpr auto lazy_v = server_config_tag<lazy>{}; using lazy_t = server_config_tag<lazy>;
static constexpr auto lazy_v = lazy_t{};
/// Configuration for a server that uses a user-provided socket. /// Configuration for a server that uses a user-provided socket.
class socket : public has_ctx { class socket : public has_ctx {
public: public:
static constexpr std::string_view name = "socket"; static constexpr std::string_view name = "socket";
explicit socket(tcp_accept_socket fd) : fd(fd) { socket(std::shared_ptr<ssl::context> ctx, tcp_accept_socket fd)
: has_ctx(std::move(ctx)), fd(fd) {
// nop // nop
} }
...@@ -76,24 +81,20 @@ public: ...@@ -76,24 +81,20 @@ public:
} }
}; };
static constexpr auto socket_v = server_config_tag<error>{}; using socket_t = server_config_tag<error>;
static constexpr auto socket_v = socket_t{};
using fail_t = server_config_tag<error>;
static constexpr auto fail_v = server_config_tag<error>{}; static constexpr auto fail_v = fail_t{};
template <class Base> class value : public config_impl<lazy, socket> {
class value : public config_impl<Base, lazy, socket> {
public: public:
using super = config_impl<Base, lazy, socket>; using super = config_impl<lazy, socket>;
using super::super; using super::super;
template <class T, class From, class... Args>
static auto make(server_config_tag<T>, const From& from, Args&&... args) {
static_assert(std::is_constructible_v<T, Args...>);
return make_counted<value>(from, std::in_place_type<T>,
std::forward<Args>(args)...);
}
/// Configures how many reads we allow on a socket before returning to the /// Configures how many reads we allow on a socket before returning to the
/// event loop. /// event loop.
size_t max_consecutive_reads size_t max_consecutive_reads
...@@ -104,10 +105,6 @@ public: ...@@ -104,10 +105,6 @@ public:
}; };
}; };
template <class Base> using server_config_value = server_config::value;
using server_config_value = server_config::value<Base>;
template <class Base>
using server_config_ptr = intrusive_ptr<server_config_value<Base>>;
} // namespace caf::net::dsl } // namespace caf::net::dsl
...@@ -16,10 +16,10 @@ ...@@ -16,10 +16,10 @@
namespace caf::net::dsl { namespace caf::net::dsl {
/// Base type for server factories for use with `can_accept`. /// Base type for server factories for use with `can_accept`.
template <class ConfigBase, class Derived> template <class Config, class Derived>
class server_factory_base { class server_factory_base {
public: public:
using config_type = server_config_value<ConfigBase>; using config_type = Config;
using config_pointer = intrusive_ptr<config_type>; using config_pointer = intrusive_ptr<config_type>;
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/actor_control_block.hpp"
#include "caf/detail/net_export.hpp"
#include "caf/net/dsl/generic_config.hpp"
#include "caf/net/dsl/server_config.hpp"
#include "caf/net/http/route.hpp"
#include "caf/net/tcp_accept_socket.hpp"
#include <string>
#include <vector>
namespace caf::net::http {
/// Configuration for the `with_t` DSL entry point. Refined into a server or
/// client configuration later on.
using base_config = dsl::generic_config_value;
/// Configuration for the server factory.
class CAF_NET_EXPORT server_config : public dsl::server_config_value {
public:
using super = dsl::server_config_value;
using super::super;
static intrusive_ptr<server_config>
make(dsl::server_config::lazy_t, const base_config& from, uint16_t port,
std::string bind_address);
static intrusive_ptr<server_config> make(dsl::server_config::socket_t,
const base_config& from,
tcp_accept_socket fd);
/// Stores the available routes on the HTTP server.
std::vector<route_ptr> routes;
/// Store actors that the server should monitor.
std::vector<strong_actor_ptr> monitored_actors;
};
} // namespace caf::net::http
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
#include "caf/detail/net_export.hpp" #include "caf/detail/net_export.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/net/checked_socket.hpp" #include "caf/net/checked_socket.hpp"
#include "caf/net/dsl/generic_config.hpp"
#include "caf/net/dsl/server_factory_base.hpp" #include "caf/net/dsl/server_factory_base.hpp"
#include "caf/net/http/config.hpp"
#include "caf/net/http/request.hpp" #include "caf/net/http/request.hpp"
#include "caf/net/http/router.hpp" #include "caf/net/http/router.hpp"
#include "caf/net/http/server.hpp" #include "caf/net/http/server.hpp"
...@@ -99,31 +101,11 @@ private: ...@@ -99,31 +101,11 @@ private:
namespace caf::net::http { namespace caf::net::http {
/// Configuration type for WebSocket clients with a handshake object. The
/// handshake object sets the default endpoint to '/' for convenience.
class server_factory_config : public dsl::config_base {
public:
using super = dsl::config_base;
explicit server_factory_config(multiplexer* mpx) : super(mpx) {
// nop
}
explicit server_factory_config(const super& other) : super(other) {
// nop
}
server_factory_config(const server_factory_config&) = default;
std::vector<route_ptr> routes;
std::vector<strong_actor_ptr> monitored_actors;
};
/// Factory type for the `with(...).accept(...).start(...)` DSL. /// Factory type for the `with(...).accept(...).start(...)` DSL.
class server_factory class server_factory
: public dsl::server_factory_base<server_factory_config, server_factory> { : public dsl::server_factory_base<server_config, server_factory> {
public: public:
using super = dsl::server_factory_base<server_factory_config, server_factory>; using super = dsl::server_factory_base<server_config, server_factory>;
using config_type = typename super::config_type; using config_type = typename super::config_type;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "caf/net/dsl/has_accept.hpp" #include "caf/net/dsl/has_accept.hpp"
#include "caf/net/dsl/has_connect.hpp" #include "caf/net/dsl/has_connect.hpp"
#include "caf/net/dsl/has_context.hpp" #include "caf/net/dsl/has_context.hpp"
#include "caf/net/http/config.hpp"
#include "caf/net/http/request.hpp" #include "caf/net/http/request.hpp"
#include "caf/net/http/router.hpp" #include "caf/net/http/router.hpp"
#include "caf/net/http/server_factory.hpp" #include "caf/net/http/server_factory.hpp"
...@@ -25,10 +26,8 @@ namespace caf::net::http { ...@@ -25,10 +26,8 @@ namespace caf::net::http {
class with_t : public extend<dsl::base, with_t>:: // class with_t : public extend<dsl::base, with_t>:: //
with<dsl::has_accept, dsl::has_context> { with<dsl::has_accept, dsl::has_context> {
public: public:
using config_type = dsl::generic_config_value<dsl::config_base>;
template <class... Ts> template <class... Ts>
explicit with_t(multiplexer* mpx) : config_(config_type::make(mpx)) { explicit with_t(multiplexer* mpx) : config_(make_counted<base_config>(mpx)) {
// nop // nop
} }
...@@ -41,7 +40,7 @@ public: ...@@ -41,7 +40,7 @@ public:
with_t& operator=(const with_t&) noexcept = default; with_t& operator=(const with_t&) noexcept = default;
/// @private /// @private
config_type& config() { base_config& config() {
return *config_; return *config_;
} }
...@@ -52,7 +51,7 @@ public: ...@@ -52,7 +51,7 @@ public:
} }
private: private:
intrusive_ptr<config_type> config_; intrusive_ptr<base_config> config_;
}; };
inline with_t with(actor_system& sys) { inline with_t with(actor_system& sys) {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "caf/disposable.hpp" #include "caf/disposable.hpp"
#include "caf/net/checked_socket.hpp" #include "caf/net/checked_socket.hpp"
#include "caf/net/dsl/client_factory_base.hpp" #include "caf/net/dsl/client_factory_base.hpp"
#include "caf/net/lp/config.hpp"
#include "caf/net/lp/framing.hpp" #include "caf/net/lp/framing.hpp"
#include "caf/net/ssl/connection.hpp" #include "caf/net/ssl/connection.hpp"
#include "caf/net/tcp_stream_socket.hpp" #include "caf/net/tcp_stream_socket.hpp"
...@@ -66,12 +67,11 @@ namespace caf::net::lp { ...@@ -66,12 +67,11 @@ namespace caf::net::lp {
/// Factory for the `with(...).connect(...).start(...)` DSL. /// Factory for the `with(...).connect(...).start(...)` DSL.
template <class Trait> template <class Trait>
class client_factory class client_factory : public dsl::client_factory_base<client_config<Trait>,
: public dsl::client_factory_base<dsl::config_with_trait<Trait>, client_factory<Trait>> {
client_factory<Trait>> {
public: public:
using super = dsl::client_factory_base<dsl::config_with_trait<Trait>, using super
client_factory<Trait>>; = dsl::client_factory_base<client_config<Trait>, client_factory<Trait>>;
using config_type = typename super::config_type; using config_type = typename super::config_type;
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/actor_control_block.hpp"
#include "caf/detail/net_export.hpp"
#include "caf/net/dsl/generic_config.hpp"
#include "caf/net/dsl/server_config.hpp"
#include "caf/net/stream_socket.hpp"
#include <string>
#include <vector>
namespace caf::net::lp {
/// Configuration for the `with_t` DSL entry point. Refined into a server or
/// client configuration later on.
template <class Trait>
class base_config : public dsl::generic_config_value {
public:
using trait_type = Trait;
using super = dsl::generic_config_value;
using super::super;
/// Configures the protocol layer.
Trait trait;
};
/// The configuration for a length-prefix framing server.
template <class Trait>
class server_config : public dsl::server_config_value {
public:
using trait_type = Trait;
using super = dsl::server_config_value;
using super::super;
static auto make(dsl::server_config::lazy_t, const base_config<Trait>& from,
uint16_t port, std::string bind_address) {
auto res = make_counted<server_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::server_config::lazy{src_data.ctx, port,
std::move(bind_address)};
res->trait = from.trait;
}
return res;
}
static auto make(dsl::server_config::socket_t, const base_config<Trait>& from,
tcp_accept_socket fd) {
auto res = make_counted<server_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
close(fd);
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::server_config::socket{src_data.ctx, fd};
res->trait = from.trait;
}
return res;
}
Trait trait;
};
/// The configuration for a length-prefix framing client.
template <class Trait>
class client_config : public dsl::client_config_value {
public:
using trait_type = Trait;
using super = dsl::client_config_value;
using super::super;
template <class... Ts>
static auto
make(dsl::client_config::lazy_t, const base_config<Trait>& from, Ts&&... args) {
auto res = make_counted<client_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::client_config::lazy{src_data.ctx,
std::forward<Ts>(args)...};
res->trait = from.trait;
}
return res;
}
static auto make(dsl::client_config::socket_t, const base_config<Trait>& from,
stream_socket fd) {
auto res = make_counted<client_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
close(fd);
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::client_config::socket{src_data.ctx, fd};
res->trait = from.trait;
}
return res;
}
static auto make(dsl::client_config::socket_t, const base_config<Trait>& from,
ssl::connection conn) {
auto res = make_counted<client_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::client_config::socket{src_data.ctx, std::move(conn)};
res->trait = from.trait;
}
return res;
}
static auto make(dsl::client_config::fail_t, const base_config<Trait>& from,
error err) {
auto res = make_counted<client_config>(from.mpx);
res->data = std::move(err);
return res;
}
Trait trait;
};
} // namespace caf::net::lp
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "caf/net/checked_socket.hpp" #include "caf/net/checked_socket.hpp"
#include "caf/net/dsl/server_factory_base.hpp" #include "caf/net/dsl/server_factory_base.hpp"
#include "caf/net/http/server.hpp" #include "caf/net/http/server.hpp"
#include "caf/net/lp/config.hpp"
#include "caf/net/lp/framing.hpp" #include "caf/net/lp/framing.hpp"
#include "caf/net/multiplexer.hpp" #include "caf/net/multiplexer.hpp"
#include "caf/net/octet_stream/transport.hpp" #include "caf/net/octet_stream/transport.hpp"
...@@ -118,12 +119,11 @@ namespace caf::net::lp { ...@@ -118,12 +119,11 @@ namespace caf::net::lp {
/// Factory type for the `with(...).accept(...).start(...)` DSL. /// Factory type for the `with(...).accept(...).start(...)` DSL.
template <class Trait> template <class Trait>
class server_factory class server_factory : public dsl::server_factory_base<server_config<Trait>,
: public dsl::server_factory_base<dsl::config_with_trait<Trait>, server_factory<Trait>> {
server_factory<Trait>> {
public: public:
using super = dsl::server_factory_base<dsl::config_with_trait<Trait>, using super
server_factory<Trait>>; = dsl::server_factory_base<server_config<Trait>, server_factory<Trait>>;
using config_type = typename super::config_type; using config_type = typename super::config_type;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "caf/net/dsl/has_connect.hpp" #include "caf/net/dsl/has_connect.hpp"
#include "caf/net/dsl/has_context.hpp" #include "caf/net/dsl/has_context.hpp"
#include "caf/net/lp/client_factory.hpp" #include "caf/net/lp/client_factory.hpp"
#include "caf/net/lp/config.hpp"
#include "caf/net/lp/server_factory.hpp" #include "caf/net/lp/server_factory.hpp"
#include "caf/net/multiplexer.hpp" #include "caf/net/multiplexer.hpp"
#include "caf/net/ssl/context.hpp" #include "caf/net/ssl/context.hpp"
...@@ -25,10 +26,10 @@ template <class Trait> ...@@ -25,10 +26,10 @@ template <class Trait>
class with_t : public extend<dsl::base, with_t<Trait>>::template // class with_t : public extend<dsl::base, with_t<Trait>>::template //
with<dsl::has_accept, dsl::has_connect, dsl::has_context> { with<dsl::has_accept, dsl::has_connect, dsl::has_context> {
public: public:
using config_type = dsl::generic_config_value<dsl::config_with_trait<Trait>>; using config_type = base_config<Trait>;
template <class... Ts> template <class... Ts>
explicit with_t(multiplexer* mpx) : config_(config_type::make(mpx)) { explicit with_t(multiplexer* mpx) : config_(make_counted<config_type>(mpx)) {
// nop // nop
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "caf/net/ssl/connection.hpp" #include "caf/net/ssl/connection.hpp"
#include "caf/net/tcp_stream_socket.hpp" #include "caf/net/tcp_stream_socket.hpp"
#include "caf/net/web_socket/client.hpp" #include "caf/net/web_socket/client.hpp"
#include "caf/net/web_socket/config.hpp"
#include "caf/net/web_socket/framing.hpp" #include "caf/net/web_socket/framing.hpp"
#include "caf/timespan.hpp" #include "caf/timespan.hpp"
...@@ -60,34 +61,13 @@ private: ...@@ -60,34 +61,13 @@ private:
namespace caf::net::web_socket { namespace caf::net::web_socket {
/// Configuration type for WebSocket clients with a handshake object. The
/// handshake object sets the default endpoint to '/' for convenience.
template <class Trait>
class client_factory_config : public dsl::config_with_trait<Trait> {
public:
using super = dsl::config_with_trait<Trait>;
explicit client_factory_config(multiplexer* mpx) : super(mpx) {
hs.endpoint("/");
}
explicit client_factory_config(const super& other) : super(other) {
hs.endpoint("/");
}
client_factory_config(const client_factory_config&) = default;
handshake hs;
};
/// Factory for the `with(...).connect(...).start(...)` DSL. /// Factory for the `with(...).connect(...).start(...)` DSL.
template <class Trait> template <class Trait>
class client_factory class client_factory : public dsl::client_factory_base<client_config<Trait>,
: public dsl::client_factory_base<client_factory_config<Trait>, client_factory<Trait>> {
client_factory<Trait>> {
public: public:
using super = dsl::client_factory_base<client_factory_config<Trait>, using super
client_factory<Trait>>; = dsl::client_factory_base<client_config<Trait>, client_factory<Trait>>;
using super::super; using super::super;
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/actor_control_block.hpp"
#include "caf/detail/net_export.hpp"
#include "caf/net/dsl/generic_config.hpp"
#include "caf/net/dsl/server_config.hpp"
#include "caf/net/tcp_accept_socket.hpp"
#include "caf/net/web_socket/handshake.hpp"
#include <string>
#include <vector>
namespace caf::net::web_socket {
/// Configuration for the `with_t` DSL entry point. Refined into a server or
/// client configuration later on.
template <class Trait>
class base_config : public dsl::generic_config_value {
public:
using trait_type = Trait;
using super = dsl::generic_config_value;
using super::super;
/// Configures the protocol layer.
Trait trait;
};
/// The configuration for a length-prefix framing server.
template <class Trait>
class server_config : public dsl::server_config_value {
public:
using trait_type = Trait;
using super = dsl::server_config_value;
using super::super;
static auto make(dsl::server_config::lazy_t, const base_config<Trait>& from,
uint16_t port, std::string bind_address) {
auto res = make_counted<server_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::server_config::lazy{src_data.ctx, port,
std::move(bind_address)};
res->trait = from.trait;
}
return res;
}
static auto make(dsl::server_config::socket_t, const base_config<Trait>& from,
tcp_accept_socket fd) {
auto res = make_counted<server_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
close(fd);
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::server_config::socket{src_data.ctx, fd};
res->trait = from.trait;
}
return res;
}
Trait trait;
};
/// The configuration for a length-prefix framing client.
template <class Trait>
class client_config : public dsl::client_config_value {
public:
using trait_type = Trait;
using super = dsl::client_config_value;
explicit client_config(multiplexer* mpx) : super(mpx) {
hs.endpoint("/");
}
client_config(const client_config&) = default;
template <class... Ts>
static auto
make(dsl::client_config::lazy_t, const base_config<Trait>& from, Ts&&... args) {
auto res = make_counted<client_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::client_config::lazy{src_data.ctx,
std::forward<Ts>(args)...};
res->trait = from.trait;
}
return res;
}
static auto make(dsl::client_config::socket_t, const base_config<Trait>& from,
tcp_accept_socket fd) {
auto res = make_counted<client_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
close(fd);
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::client_config::socket{src_data.ctx, fd};
res->trait = from.trait;
}
return res;
}
static auto make(dsl::client_config::fail_t, const base_config<Trait>& from,
error err) {
auto res = make_counted<client_config>(from.mpx);
res->data = std::move(err);
return res;
}
Trait trait;
handshake hs;
};
} // namespace caf::net::web_socket
...@@ -17,12 +17,11 @@ namespace caf::net::web_socket { ...@@ -17,12 +17,11 @@ namespace caf::net::web_socket {
/// DSL entry point for creating a server. /// DSL entry point for creating a server.
template <class Trait> template <class Trait>
class has_on_request class has_on_request : public dsl::server_factory_base<server_config<Trait>,
: public dsl::server_factory_base<dsl::config_with_trait<Trait>, has_on_request<Trait>> {
has_on_request<Trait>> {
public: public:
using super = dsl::server_factory_base<dsl::config_with_trait<Trait>, using super
has_on_request<Trait>>; = dsl::server_factory_base<server_config<Trait>, has_on_request<Trait>>;
using super::super; using super::super;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "caf/net/octet_stream/transport.hpp" #include "caf/net/octet_stream/transport.hpp"
#include "caf/net/ssl/transport.hpp" #include "caf/net/ssl/transport.hpp"
#include "caf/net/tcp_accept_socket.hpp" #include "caf/net/tcp_accept_socket.hpp"
#include "caf/net/web_socket/config.hpp"
#include "caf/net/web_socket/server.hpp" #include "caf/net/web_socket/server.hpp"
#include <cstdint> #include <cstdint>
...@@ -148,12 +149,11 @@ namespace caf::net::web_socket { ...@@ -148,12 +149,11 @@ namespace caf::net::web_socket {
/// Factory type for the `with(...).accept(...).start(...)` DSL. /// Factory type for the `with(...).accept(...).start(...)` DSL.
template <class Trait, class... Ts> template <class Trait, class... Ts>
class server_factory class server_factory : public dsl::server_factory_base<server_config<Trait>,
: public dsl::server_factory_base<dsl::config_with_trait<Trait>, server_factory<Trait>> {
server_factory<Trait>> {
public: public:
using super = dsl::server_factory_base<dsl::config_with_trait<Trait>, using super
server_factory<Trait>>; = dsl::server_factory_base<server_config<Trait>, server_factory<Trait>>;
using config_type = typename super::config_type; using config_type = typename super::config_type;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "caf/net/ssl/context.hpp" #include "caf/net/ssl/context.hpp"
#include "caf/net/tcp_accept_socket.hpp" #include "caf/net/tcp_accept_socket.hpp"
#include "caf/net/web_socket/client_factory.hpp" #include "caf/net/web_socket/client_factory.hpp"
#include "caf/net/web_socket/config.hpp"
#include "caf/net/web_socket/default_trait.hpp" #include "caf/net/web_socket/default_trait.hpp"
#include "caf/net/web_socket/has_on_request.hpp" #include "caf/net/web_socket/has_on_request.hpp"
...@@ -25,10 +26,10 @@ template <class Trait> ...@@ -25,10 +26,10 @@ template <class Trait>
class with_t : public extend<dsl::base, with_t<Trait>>::template // class with_t : public extend<dsl::base, with_t<Trait>>::template //
with<dsl::has_accept, dsl::has_uri_connect, dsl::has_context> { with<dsl::has_accept, dsl::has_uri_connect, dsl::has_context> {
public: public:
using config_type = dsl::generic_config_value<dsl::config_with_trait<Trait>>; using config_type = base_config<Trait>;
template <class... Ts> template <class... Ts>
explicit with_t(multiplexer* mpx) : config_(config_type::make(mpx)) { explicit with_t(multiplexer* mpx) : config_(make_counted<config_type>(mpx)) {
// nop // nop
} }
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/net/http/config.hpp"
namespace caf::net::http {
intrusive_ptr<server_config>
server_config::make(dsl::server_config::lazy_t, const base_config& from,
uint16_t port, std::string bind_address) {
auto res = make_counted<server_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::server_config::lazy{src_data.ctx, port,
std::move(bind_address)};
}
return res;
}
intrusive_ptr<server_config> server_config::make(dsl::server_config::socket_t,
const base_config& from,
tcp_accept_socket fd) {
auto res = make_counted<server_config>(from.mpx);
if (auto* err = std::get_if<error>(&from.data)) {
res->data = *err;
close(fd);
} else {
auto& src_data = std::get<dsl::generic_config::lazy>(from.data);
res->data = dsl::server_config::socket{src_data.ctx, fd};
}
return res;
}
} // namespace caf::net::http
...@@ -19,6 +19,7 @@ lower_layer::~lower_layer() { ...@@ -19,6 +19,7 @@ lower_layer::~lower_layer() {
bool lower_layer::send_response(status code) { bool lower_layer::send_response(status code) {
begin_header(code); begin_header(code);
add_header_field("Content-Length"sv, "0"sv);
return end_header() && send_payload(const_byte_span{}); return end_header() && send_payload(const_byte_span{});
} }
......
find_package(Python COMPONENTS Interpreter)
add_test(
NAME "robot-http-rest"
COMMAND
${Python_EXECUTABLE}
-m robot
--variable BINARY_PATH:$<TARGET_FILE:rest>
--variable SSL_PATH:${CMAKE_CURRENT_SOURCE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/http/rest.robot")
This directory contains module and system tests that complement the C++ unit
tests.
-----BEGIN CERTIFICATE-----
MIIDmzCCAoOgAwIBAgIJAPLZ3e3WR0LLMA0GCSqGSIb3DQEBCwUAMGQxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJDQTERMA8GA1UEBwwIQmVya2VsZXkxIzAhBgNVBAoM
GkFDTUUgU2lnbmluZyBBdXRob3JpdHkgSW5jMRAwDgYDVQQDDAdmb28uYmFyMB4X
DTE3MDQyMTIzMjM0OFoXDTQyMDQyMTIzMjM0OFowZDELMAkGA1UEBhMCVVMxCzAJ
BgNVBAgMAkNBMREwDwYDVQQHDAhCZXJrZWxleTEjMCEGA1UECgwaQUNNRSBTaWdu
aW5nIEF1dGhvcml0eSBJbmMxEDAOBgNVBAMMB2Zvby5iYXIwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC6ah79JvrN3LtcPzc9bX5THdzfidWncSmowotG
SZA3gcIhlsYD3P3RCaUR9g+f2Z/l0l7ciKgWetpNtN9hRBbg5/9tFzSpCb/Y0SSG
mwtHHovEqN2MWV+Od/MUcYSlL6MmPjSDc8Ls5NSniTr9OBE9J1jm72AsuzHasjPQ
D84TlWeTSs0HW3H5VxDb15xWYFnmgBo0JylDWj0+VWI+G41Xr7Ubu9699lWSFYF9
FCtdjzM5e1CGZOMvqUbUBus38BhUAdQ4fE7Dwnn8seKh+7HpJ70omIgqG87e4DBo
HbnMAkZaekk8+LBl0Hfu8c66Utw9mNoMIlFf/AMlJyLDIpNxAgMBAAGjUDBOMB0G
A1UdDgQWBBRc6Cbyshtny6jFWZtd/cEUUfMQ3DAfBgNVHSMEGDAWgBRc6Cbyshtn
y6jFWZtd/cEUUfMQ3DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCY
numHau9XYH5h4R2CoMdnKPMGk6V7UZZdbidLLcE4roQrYhnBdyhT69b/ySJK2Ee4
mt8T+E0wcg3k8Pr3aJEJA8eYYaJTqZvvv+TwuMBPjmE2rYSIpgMZv2tRD3XWMaQu
duLbwkclfejQHDD26xNXsxuU+WNB5kuvtNAg0oKFyFdNKElLQEcjyYzfxmCF4YX5
WmElijr1Tzuzd59rWPqC/tVIsh42vQ+P6g8Y1PDmo8eTUFveZ+wcr/eEPW6IOMrg
OW7tATcrgzNuXZ1umiuGgAPuIVqPfr9ssZHBqi9UOK9L/8MQrnOxecNUpPohcTFR
vq+Zqu15QV9T4BVWKHv0
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDOjCCAiICCQDz7oMOR7Wm7jANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJV
UzELMAkGA1UECAwCQ0ExETAPBgNVBAcMCEJlcmtlbGV5MSMwIQYDVQQKDBpBQ01F
IFNpZ25pbmcgQXV0aG9yaXR5IEluYzEQMA4GA1UEAwwHZm9vLmJhcjAgFw0xNzA0
MjEyMzI2MzhaGA80NzU1MDMxOTIzMjYzOFowWDELMAkGA1UEBhMCVVMxCzAJBgNV
BAgMAkNBMREwDwYDVQQHDAhCZXJrZWxleTEVMBMGA1UECgwMQUNNRSBTZXJ2aWNl
MRIwEAYDVQQDDAkxLmZvby5iYXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDHobccAQQqbZANdOdx852W/nUzGcwpurOi8zbh9yCxMwnFMogW9AsqKEnd
sypV6Ah/cIz45PAgCdEg+1pc2DG7+E0+QlV4ChNwCDuk+FSWB6pqMTCdZcLeIwlA
GPp6Ow9v40dW7IFpDetFKXEo6kqEzR5P58Q0a6KpCtpsSMqhk57Py83wB9gPA1vp
s77kN7D5CI3oay86TA5j5nfFMT1X/77Hs24csW6CLnW/OD4f1RK79UgPd/kpPKQ1
jNq+hsR7NZTcfrAF1hcfScxnKaznO7WopSt1k75NqLdnSN1GIci2GpiXYKtXZ9l5
TErv2Oucpw/u+a/wjKlXjrgLL9lfAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKuW
yKA2uuiNc9MKU+yVbNaP8kPaMb/wMvVaFG8FFFpCTZ0MFMLsqRpeqtj7gMK/gaJC
CQm4EyadjzfWFYDLkHzm6b7gI8digvvhjr/C2RJ5Qxr2P0iFP1buGq0CqnF20XgQ
Q+ecS43CZ77CfKfS6ZLPmAZMAwgFLImVyo5mkaTECo3+9oCnjDYBapvXLJqCJRhk
NosoTmGCV0HecWN4l38ojnXd44aSktQIND9iCLus3S6++nFnX5DHGZiv6/SnSO/6
+Op7nV0A6zKVcMOYQ0SGZPD8UQs5wDJgrR9LY29Ox5QBwu/5NqyvNSrMQaTop5vb
wkMInaq5lLxEYQDSLBc=
-----END CERTIFICATE-----
robotframework
robotframework-requests
*** Settings ***
Documentation A test suite for examples/http/rest.hpp.
Library Process
Library RequestsLibrary
Suite Setup Start Server
Suite Teardown Stop Server
*** Variables ***
${HTTP_URL} http://localhost:55501
${HTTPS_URL} https://localhost:55502
${BINARY_PATH} /path/to/the/server
${SSL_PATH} /path/to/the/pem/files
*** Test Cases ***
HTTP Test Add Key Value Pair
[Tags] POST
Add Key Value Pair ${HTTP_URL} foo bar
Get Key Value Pair ${HTTP_URL} foo bar
HTTP Test Update Key Value Pair
[Tags] POST
Add Key Value Pair ${HTTP_URL} foo bar
Update Key Value Pair ${HTTP_URL} foo baz
Get Key Value Pair ${HTTP_URL} foo baz
HTTP Test Delete Key Value Pair
[Tags] DELETE
Add Key Value Pair ${HTTP_URL} foo bar
Delete Key Value Pair ${HTTP_URL} foo
Key Should Not Exist ${HTTP_URL} foo
HTTPS Test Add Key Value Pair
[Tags] POST
Add Key Value Pair ${HTTPS_URL} foo bar
Get Key Value Pair ${HTTPS_URL} foo bar
HTTPS Test Update Key Value Pair
[Tags] POST
Add Key Value Pair ${HTTPS_URL} foo bar
Update Key Value Pair ${HTTPS_URL} foo baz
Get Key Value Pair ${HTTPS_URL} foo baz
HTTPS Test Delete Key Value Pair
[Tags] DELETE
Add Key Value Pair ${HTTPS_URL} foo bar
Delete Key Value Pair ${HTTPS_URL} foo
Key Should Not Exist ${HTTPS_URL} foo
*** Keywords ***
Start Server
${res1}= Start Process ${BINARY_PATH} -p 55501
Set Suite Variable ${http_server_process} ${res1}
${res2}= Start Process ${BINARY_PATH} -p 55502 -k ${SSL_PATH}/key.pem -c ${SSL_PATH}/cert.pem
Set Suite Variable ${https_server_process} ${res2}
Wait Until Keyword Succeeds 5s 125ms Check If HTTP Server Is Reachable
Wait Until Keyword Succeeds 5s 125ms Check If HTTPS Server Is Reachable
Stop Server
Terminate Process ${http_server_process}
Terminate Process ${https_server_process}
Check If HTTP Server Is Reachable
Log Try reaching ${HTTP_URL}/status.
${resp}= GET ${HTTP_URL}/status expected_status=204
Check If HTTPS Server Is Reachable
Log Try reaching ${HTTPS_URL}/status.
${resp}= GET ${HTTPS_URL}/status expected_status=204 verify=${False}
Add Key Value Pair
[Arguments] ${base_url} ${key} ${value}
${resp}= POST ${base_url}/api/${key} data=${value} expected_status=204 verify=${False}
Get Key Value Pair
[Arguments] ${base_url} ${key} ${expected_value}
${resp}= GET ${base_url}/api/${key} expected_status=200 verify=${False}
Should Be Equal As Strings ${resp.content} ${expected_value}
Update Key Value Pair
[Arguments] ${base_url} ${key} ${new_value}
${resp}= POST ${base_url}/api/${key} data=${new_value} expected_status=204 verify=${False}
Delete Key Value Pair
[Arguments] ${base_url} ${key}
${resp}= DELETE ${base_url}/api/${key} expected_status=204 verify=${False}
Key Should Not Exist
[Arguments] ${base_url} ${key}
${resp}= GET ${base_url}/api/${key} expected_status=404 verify=${False}
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAx6G3HAEEKm2QDXTncfOdlv51MxnMKbqzovM24fcgsTMJxTKI
FvQLKihJ3bMqVegIf3CM+OTwIAnRIPtaXNgxu/hNPkJVeAoTcAg7pPhUlgeqajEw
nWXC3iMJQBj6ejsPb+NHVuyBaQ3rRSlxKOpKhM0eT+fENGuiqQrabEjKoZOez8vN
8AfYDwNb6bO+5Dew+QiN6GsvOkwOY+Z3xTE9V/++x7NuHLFugi51vzg+H9USu/VI
D3f5KTykNYzavobEezWU3H6wBdYXH0nMZyms5zu1qKUrdZO+Tai3Z0jdRiHIthqY
l2CrV2fZeUxK79jrnKcP7vmv8IypV464Cy/ZXwIDAQABAoIBAC0Y7jmoTR2clJ9F
modWhnI215kMqd9/atdT5EEVx8/f/MQMj0vII8GJSm6H6/duLIVFksMjTM+gCBtQ
TPCOcmXJSQHYkGBGvm9fnMG+y7T81FWa+SWFeIkgFxXgzqzQLMOU72fGk9F8sHp2
Szb3/o+TmtZoQB2rdxqC9ibiJsxrG5IBVKkzlSPv3POkPXwSb1HcETqrTwefuioj
WMuMrqtm5Y3HddJ5l4JEF5VA3KrsfXWl3JLHH0UViemVahiNjXQAVTKAXIL1PHAV
J2MCEvlpA7sIgXREbmvPvZUTkt3pIqhVjZVJ7tHiSnSecqNTbuxcocnhKhZrHNtC
v2zYKHkCgYEA6cAIhz0qOGDycZ1lf9RSWw0RO1hO8frATMQNVoFVuJJCVL22u96u
0FvJ0JGyYbjthULnlOKyRe7DUL5HRLVS4D7vvKCrgwDmsJp1VFxMdASUdaBfq6aX
oKLUW4q7kC2lQcmK/PVRYwp2GQSx8bodWe+DtXUY/GcN03znY8mhSB0CgYEA2qJK
1GSZsm6kFbDek3BiMMHfO+X819owB2FmXiH+GQckyIZu9xA3HWrkOWTqwglEvzfO
qzFF96E9iEEtseAxhcM8gPvfFuXiUj9t2nH/7SzMnVGikhtYi0p6jrgHmscc4NBx
AOUA15kYEFOGqpZfl2uuKqgHidrHdGkJzzSUBqsCgYAVCjb6TVQejQNlnKBFOExN
a8iwScuZVlO21TLKJYwct/WGgSkQkgO0N37b6jFfQHEIvLPxn9IiH1KvUuFBWvzh
uGiF1wR5HzykitKizEgJbVwbllrmLXGagO2Sa9NkL+efG1AKYt53hrqIl/aYZoM7
1CZL0AV2uqPw9F4zijOdNQKBgH1WmvWGMsKjQTgaLI9z1ybCjjqlj70jHXOtt+Tx
Md2hRcobn5PN3PrlY68vlpHkhF/nG3jzB3x+GGt7ijm2IE3h7la3jl5vLb8fE9gu
kJykmSz7Nurx+GHqMbaN8/Ycfga4GIB9yGzRHIWHjOVQzb5eAfv8Vk4GeV/YM8Jx
Dwd/AoGAILn8pVC9dIFac2BDOFU5y9ZvMmZAvwRxh9vEWewNvkzg27vdYc+rCHNm
I7H0S/RqfqVeo0ApE5PQ8Sll6RvxN/mbSQo9YeCDGQ1r1rNe4Vs12GAYXAbE4ipf
BTdqMbieumB/zL97iK5baHUFEJ4VRtLQhh/SOXgew/BF8ccpilI=
-----END RSA PRIVATE KEY-----
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment