Commit 8073c0da authored by captainwong's avatar captainwong

update simple libevent client/server

parent 1a66d846
...@@ -80,7 +80,7 @@ static std::pair<bool, std::string> get_domain_ip_impl(const std::string& domain ...@@ -80,7 +80,7 @@ static std::pair<bool, std::string> get_domain_ip_impl(const std::string& domain
} // end of namespace detail } // end of namespace detail
static bool get_domain_ip(const std::string& domain, int ping_times, std::string& result, waiting_cb cb) { static bool get_domain_ip(const std::string& domain, int ping_times, std::string& result, waiting_cb cb = nullptr) {
auto f = std::async(std::launch::async, detail::get_domain_ip_impl, domain, ping_times); auto f = std::async(std::launch::async, detail::get_domain_ip_impl, domain, ping_times);
while (true) { while (true) {
auto status = f.wait_for(std::chrono::milliseconds(1)); auto status = f.wait_for(std::chrono::milliseconds(1));
......
#include "Client.h" #include "simple_libevent_client.h"
#ifdef _WIN32 #ifdef _WIN32
#include <WinSock2.h> #include <WinSock2.h>
...@@ -19,7 +19,11 @@ ...@@ -19,7 +19,11 @@
#include <event2/thread.h> #include <event2/thread.h>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#ifdef SIMPLELIBEVENTCLIENTLIB
#include "../log2.h" #include "../log2.h"
#else
#include <jlib/log2.h>
#endif
#ifndef JLIB_LOG2_ENABLED #ifndef JLIB_LOG2_ENABLED
#pragma error "jlib::log2 not found!" #pragma error "jlib::log2 not found!"
...@@ -27,8 +31,6 @@ ...@@ -27,8 +31,6 @@
namespace jlib { namespace jlib {
namespace net { namespace net {
namespace client {
struct OneTimeIniter { struct OneTimeIniter {
OneTimeIniter() { OneTimeIniter() {
...@@ -48,7 +50,7 @@ struct OneTimeIniter { ...@@ -48,7 +50,7 @@ struct OneTimeIniter {
} }
}; };
struct Client::Impl struct simple_libevent_client::Impl
{ {
Impl(void* user_data) Impl(void* user_data)
: user_data(user_data) : user_data(user_data)
...@@ -66,7 +68,7 @@ struct Client::Impl ...@@ -66,7 +68,7 @@ struct Client::Impl
static void writecb(struct bufferevent*, void* user_data) static void writecb(struct bufferevent*, void* user_data)
{ {
Client* client = (Client*)user_data; simple_libevent_client* client = (simple_libevent_client*)user_data;
client->lastTimeSendData = std::chrono::steady_clock::now(); client->lastTimeSendData = std::chrono::steady_clock::now();
} }
...@@ -75,7 +77,7 @@ struct Client::Impl ...@@ -75,7 +77,7 @@ struct Client::Impl
char buff[4096]; char buff[4096];
auto input = bufferevent_get_input(bev); auto input = bufferevent_get_input(bev);
JLOG_DBUG("readcb, readable len {}", evbuffer_get_length(input)); JLOG_DBUG("readcb, readable len {}", evbuffer_get_length(input));
Client* client = (Client*)user_data; simple_libevent_client* client = (simple_libevent_client*)user_data;
if (client->userData_ && client->onMsg_) { if (client->userData_ && client->onMsg_) {
while (1) { while (1) {
int len = evbuffer_copyout(input, buff, std::min(sizeof(buff), evbuffer_get_length(input))); int len = evbuffer_copyout(input, buff, std::min(sizeof(buff), evbuffer_get_length(input)));
...@@ -108,7 +110,7 @@ struct Client::Impl ...@@ -108,7 +110,7 @@ struct Client::Impl
static void eventcb(struct bufferevent* bev, short events, void* user_data) static void eventcb(struct bufferevent* bev, short events, void* user_data)
{ {
Client* client = (Client*)user_data; simple_libevent_client* client = (simple_libevent_client*)user_data;
JLOG_DBUG("eventcb events={}", eventToString(events)); JLOG_DBUG("eventcb events={}", eventToString(events));
std::string msg; std::string msg;
...@@ -156,7 +158,7 @@ struct Client::Impl ...@@ -156,7 +158,7 @@ struct Client::Impl
static void timercb(evutil_socket_t, short, void* user_data) static void timercb(evutil_socket_t, short, void* user_data)
{ {
Client* client = (Client*)user_data; simple_libevent_client* client = (simple_libevent_client*)user_data;
if (client->strictTimer_) { if (client->strictTimer_) {
if (client->userData_ && client->onTimer_) { if (client->userData_ && client->onTimer_) {
client->onTimer_(client->userData_); client->onTimer_(client->userData_);
...@@ -179,7 +181,7 @@ struct Client::Impl ...@@ -179,7 +181,7 @@ struct Client::Impl
static void reconn_timercb(evutil_socket_t, short, void* user_data) static void reconn_timercb(evutil_socket_t, short, void* user_data)
{ {
Client* client = (Client*)user_data; simple_libevent_client* client = (simple_libevent_client*)user_data;
do { do {
std::string msg = "Reconnecting " + client->impl_->ip + ":" + std::to_string(client->impl_->port); std::string msg = "Reconnecting " + client->impl_->ip + ":" + std::to_string(client->impl_->port);
...@@ -216,7 +218,7 @@ struct Client::Impl ...@@ -216,7 +218,7 @@ struct Client::Impl
} }
}; };
bool Client::start(const std::string& ip, uint16_t port, std::string& msg) bool simple_libevent_client::start(const std::string& ip, uint16_t port, std::string& msg)
{ {
do { do {
stop(); stop();
...@@ -267,7 +269,7 @@ bool Client::start(const std::string& ip, uint16_t port, std::string& msg) ...@@ -267,7 +269,7 @@ bool Client::start(const std::string& ip, uint16_t port, std::string& msg)
return false; return false;
} }
void Client::stop() void simple_libevent_client::stop()
{ {
std::lock_guard<std::mutex> lg(mutex_); std::lock_guard<std::mutex> lg(mutex_);
if (!impl_) { return; } if (!impl_) { return; }
...@@ -309,7 +311,7 @@ void Client::stop() ...@@ -309,7 +311,7 @@ void Client::stop()
autoReconnect_ = old; autoReconnect_ = old;
} }
void Client::send(const char* data, size_t len) void simple_libevent_client::send(const char* data, size_t len)
{ {
std::lock_guard<std::mutex> lg(mutex_); std::lock_guard<std::mutex> lg(mutex_);
if (!impl_ || !impl_->base || !impl_->bev) { return; } if (!impl_ || !impl_->base || !impl_->bev) { return; }
...@@ -321,4 +323,3 @@ void Client::send(const char* data, size_t len) ...@@ -321,4 +323,3 @@ void Client::send(const char* data, size_t len)
} }
} }
}
...@@ -19,22 +19,23 @@ ...@@ -19,22 +19,23 @@
namespace jlib { namespace jlib {
namespace net { namespace net {
namespace client {
typedef void(*OnConnectinoCallback)(bool up, const std::string& msg, void* user_data); class simple_libevent_client
{
public:
typedef void(*OnConnectinoCallback)(bool up, const std::string& msg, void* user_data);
// return > 0 for ate // return > 0 for ate
// return 0 for stop // return 0 for stop
typedef size_t(*OnMessageCallback)(const char* data, size_t len, void* user_data); typedef size_t(*OnMessageCallback)(const char* data, size_t len, void* user_data);
typedef void(*OnTimerCallback)(void* user_data);
typedef void(*OnTimerCallback)(void* user_data);
class Client
{
public: public:
Client() {} simple_libevent_client() {}
virtual ~Client() { stop(); } virtual ~simple_libevent_client() { stop(); }
void setUserData(void* d) { userData_ = d; } void setUserData(void* d) { userData_ = d; }
void setOnConnectionCallback(OnConnectinoCallback cb) { onConn_ = cb; } void setOnConnectionCallback(OnConnectinoCallback cb) { onConn_ = cb; }
...@@ -71,4 +72,3 @@ protected: ...@@ -71,4 +72,3 @@ protected:
} }
} }
}
#include "Server.h" #include "simple_libevent_server.h"
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -14,12 +14,18 @@ ...@@ -14,12 +14,18 @@
#include <algorithm> #include <algorithm>
#include <signal.h> #include <signal.h>
#include <inttypes.h> #include <inttypes.h>
#ifdef SIMPLELIBEVENTSERVERLIB
#include "../log2.h" #include "../log2.h"
#else
#include <jlib/log2.h>
#endif
#ifndef JLIB_LOG2_ENABLED
#pragma error "jlib::log2 not found!"
#endif
namespace jlib { namespace jlib {
namespace net { namespace net {
namespace server {
struct OneTimeIniter { struct OneTimeIniter {
...@@ -63,25 +69,25 @@ struct BaseClientPrivateData { ...@@ -63,25 +69,25 @@ struct BaseClientPrivateData {
}; };
BaseClient::BaseClient(int fd, void* bev) simple_libevent_server::BaseClient::BaseClient(int fd, void* bev)
: fd(fd) : fd(fd)
, privateData(new BaseClientPrivateData()) , privateData(new BaseClientPrivateData())
{ {
((BaseClientPrivateData*)privateData)->bev = bev; ((BaseClientPrivateData*)privateData)->bev = bev;
} }
BaseClient::~BaseClient() simple_libevent_server::BaseClient::~BaseClient()
{ {
delete (BaseClientPrivateData*)privateData; delete (BaseClientPrivateData*)privateData;
} }
BaseClient* BaseClient::createDefaultClient(int fd, void* bev) simple_libevent_server::BaseClient* simple_libevent_server::BaseClient::createDefaultClient(int fd, void* bev)
{ {
BaseClient* client = new BaseClient(fd, bev); BaseClient* client = new BaseClient(fd, bev);
return client; return client;
} }
void BaseClient::send(const void* data, size_t len) void simple_libevent_server::BaseClient::send(const void* data, size_t len)
{ {
if (!((BaseClientPrivateData*)privateData)->bev) { if (!((BaseClientPrivateData*)privateData)->bev) {
JLOG_CRTC("BaseClient::send bev is nullptr, #{}", fd); JLOG_CRTC("BaseClient::send bev is nullptr, #{}", fd);
...@@ -99,7 +105,7 @@ void BaseClient::send(const void* data, size_t len) ...@@ -99,7 +105,7 @@ void BaseClient::send(const void* data, size_t len)
evbuffer_unlock(output); evbuffer_unlock(output);
} }
void BaseClient::shutdown(int what) void simple_libevent_server::BaseClient::shutdown(int what)
{ {
if (fd != 0) { if (fd != 0) {
::shutdown(fd, what); ::shutdown(fd, what);
...@@ -107,12 +113,12 @@ void BaseClient::shutdown(int what) ...@@ -107,12 +113,12 @@ void BaseClient::shutdown(int what)
} }
} }
void BaseClient::updateLastTimeComm() void simple_libevent_server::BaseClient::updateLastTimeComm()
{ {
((BaseClientPrivateData*)privateData)->lastTimeComm = std::chrono::steady_clock::now(); ((BaseClientPrivateData*)privateData)->lastTimeComm = std::chrono::steady_clock::now();
} }
struct Server::PrivateImpl struct simple_libevent_server::PrivateImpl
{ {
struct WorkerThreadContext { struct WorkerThreadContext {
std::string name = {}; std::string name = {};
...@@ -143,7 +149,7 @@ struct Server::PrivateImpl ...@@ -143,7 +149,7 @@ struct Server::PrivateImpl
{ {
char buff[4096]; char buff[4096];
auto input = bufferevent_get_input(bev); auto input = bufferevent_get_input(bev);
Server* server = (Server*)user_data; simple_libevent_server* server = (simple_libevent_server*)user_data;
if (server->userData_ && server->onMsg_) { if (server->userData_ && server->onMsg_) {
int fd = (int)bufferevent_getfd(bev); int fd = (int)bufferevent_getfd(bev);
BaseClient* client = nullptr; BaseClient* client = nullptr;
...@@ -176,7 +182,7 @@ struct Server::PrivateImpl ...@@ -176,7 +182,7 @@ struct Server::PrivateImpl
static void eventcb(struct bufferevent* bev, short events, void* user_data) static void eventcb(struct bufferevent* bev, short events, void* user_data)
{ {
Server* server = (Server*)user_data; simple_libevent_server* server = (simple_libevent_server*)user_data;
//printf("eventcb events=%d %s\n", events, eventToString(events).data()); //printf("eventcb events=%d %s\n", events, eventToString(events).data());
std::string msg; std::string msg;
...@@ -237,7 +243,7 @@ struct Server::PrivateImpl ...@@ -237,7 +243,7 @@ struct Server::PrivateImpl
static void timercb(evutil_socket_t fd, short, void* user_data) static void timercb(evutil_socket_t fd, short, void* user_data)
{ {
auto server = (Server*)user_data; auto server = (simple_libevent_server*)user_data;
std::lock_guard<std::mutex> lg(server->mutex); std::lock_guard<std::mutex> lg(server->mutex);
auto iter = server->clients.find((int)fd); auto iter = server->clients.find((int)fd);
if (iter != server->clients.end()) { if (iter != server->clients.end()) {
...@@ -263,7 +269,7 @@ struct Server::PrivateImpl ...@@ -263,7 +269,7 @@ struct Server::PrivateImpl
auto sin = (sockaddr_in*)addr; auto sin = (sockaddr_in*)addr;
inet_ntop(AF_INET, &sin->sin_addr, str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &sin->sin_addr, str, INET_ADDRSTRLEN);
Server* server = (Server*)user_data; simple_libevent_server* server = (simple_libevent_server*)user_data;
auto ctx = server->impl->workerThreadContexts[server->impl->curWorkerId]; auto ctx = server->impl->workerThreadContexts[server->impl->curWorkerId];
auto bev = bufferevent_socket_new(ctx->base, fd, BEV_OPT_CLOSE_ON_FREE); auto bev = bufferevent_socket_new(ctx->base, fd, BEV_OPT_CLOSE_ON_FREE);
...@@ -298,17 +304,17 @@ struct Server::PrivateImpl ...@@ -298,17 +304,17 @@ struct Server::PrivateImpl
}; };
Server::Server() simple_libevent_server::simple_libevent_server()
{ {
static OneTimeIniter initLibEvent; static OneTimeIniter initLibEvent;
} }
Server::~Server() simple_libevent_server::~simple_libevent_server()
{ {
stop(); stop();
} }
bool Server::start(uint16_t port, std::string& msg) bool simple_libevent_server::start(uint16_t port, std::string& msg)
{ {
do { do {
stop(); stop();
...@@ -365,7 +371,7 @@ bool Server::start(uint16_t port, std::string& msg) ...@@ -365,7 +371,7 @@ bool Server::start(uint16_t port, std::string& msg)
return false; return false;
} }
void Server::stop() void simple_libevent_server::stop()
{ {
std::lock_guard<std::mutex> lg(mutex); std::lock_guard<std::mutex> lg(mutex);
if (!impl) { return; } if (!impl) { return; }
...@@ -408,4 +414,3 @@ void Server::stop() ...@@ -408,4 +414,3 @@ void Server::stop()
} }
} }
}
...@@ -28,10 +28,11 @@ ...@@ -28,10 +28,11 @@
namespace jlib { namespace jlib {
namespace net { namespace net {
namespace server {
class simple_libevent_server
struct BaseClient { {
public:
struct BaseClient {
explicit BaseClient(int fd, void* bev); explicit BaseClient(int fd, void* bev);
virtual ~BaseClient(); virtual ~BaseClient();
...@@ -45,22 +46,21 @@ struct BaseClient { ...@@ -45,22 +46,21 @@ struct BaseClient {
std::string ip = {}; std::string ip = {};
uint16_t port = 0; uint16_t port = 0;
void* privateData = nullptr; void* privateData = nullptr;
}; };
typedef BaseClient*(*NewClientCallback)(int fd, void* bev); typedef BaseClient* (*NewClientCallback)(int fd, void* bev);
typedef void(*OnConnectinoCallback)(bool up, const std::string& msg, BaseClient* client, void* user_data); typedef void(*OnConnectinoCallback)(bool up, const std::string& msg, BaseClient* client, void* user_data);
// return > 0 for ate // return > 0 for ate
// return 0 for stop // return 0 for stop
typedef size_t(*OnMessageCallback)(const char* data, size_t len, BaseClient* client, void* user_data); typedef size_t(*OnMessageCallback)(const char* data, size_t len, BaseClient* client, void* user_data);
class Server
{
public: public:
explicit Server(); explicit simple_libevent_server();
virtual ~Server(); virtual ~simple_libevent_server();
// these functions wont take effect after start() is called // these functions wont take effect after start() is called
void setName(const std::string& name) { name_ = name; } void setName(const std::string& name) { name_ = name; }
...@@ -99,4 +99,3 @@ protected: ...@@ -99,4 +99,3 @@ protected:
} }
} }
}
...@@ -19,10 +19,10 @@ ...@@ -19,10 +19,10 @@
</ProjectConfiguration> </ProjectConfiguration>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\jlib\net\Client.cpp" /> <ClCompile Include="..\..\jlib\net\simple_libevent_client.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\jlib\net\Client.h" /> <ClInclude Include="..\..\jlib\net\simple_libevent_client.h" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion> <VCProjectVersion>16.0</VCProjectVersion>
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;SIMPLELIBEVENTCLIENTLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader> <PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;SIMPLELIBEVENTCLIENTLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader> <PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
......
...@@ -15,13 +15,13 @@ ...@@ -15,13 +15,13 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\jlib\net\Client.cpp"> <ClInclude Include="..\..\jlib\net\simple_libevent_client.h">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\jlib\net\Client.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\jlib\net\simple_libevent_client.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project> </Project>
\ No newline at end of file
...@@ -19,10 +19,10 @@ ...@@ -19,10 +19,10 @@
</ProjectConfiguration> </ProjectConfiguration>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\jlib\net\Server.cpp" /> <ClCompile Include="..\..\jlib\net\simple_libevent_server.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\jlib\net\Server.h" /> <ClInclude Include="..\..\jlib\net\simple_libevent_server.h" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion> <VCProjectVersion>16.0</VCProjectVersion>
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;SIMPLELIBEVENTSERVERLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader> <PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;SIMPLELIBEVENTSERVERLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader> <PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
......
...@@ -15,13 +15,13 @@ ...@@ -15,13 +15,13 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\jlib\net\Server.cpp"> <ClInclude Include="..\..\jlib\net\simple_libevent_server.h">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\jlib\net\Server.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\jlib\net\simple_libevent_server.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project> </Project>
\ No newline at end of file
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