Skip to content
This repository was archived by the owner on Apr 6, 2019. It is now read-only.

Add implementation for the CLIENT KILL command #91

Merged
merged 6 commits into from
Aug 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ IF (BUILD_TESTS)
add_subdirectory(tests)
ExternalProject_Add("googletest"
GIT_REPOSITORY "https://github.com/google/googletest.git"
GIT_TAG "461713fec4603806d2049835c0790bf94d2db631"
CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${PROJECT_SOURCE_DIR}/deps")
# Reset variable to false to ensure tacopie does no build tests
set (BUILD_TESTS false)
Expand Down
3 changes: 3 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ target_link_libraries(cpp_redis_subscriber cpp_redis)

add_executable(cpp_redis_logger logger.cpp)
target_link_libraries(cpp_redis_logger cpp_redis)

add_executable(kill_client kill_client.cpp)
target_link_libraries(kill_client cpp_redis)
100 changes: 100 additions & 0 deletions examples/kill_client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2017 Simon Ninon <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <cpp_redis/cpp_redis>

#include <iostream>
#include <sstream>

#ifdef _WIN32
#include <Winsock2.h>
#endif /* _WIN32 */

int
main(void) {
#ifdef _WIN32
//! Windows netword DLL init
WORD version = MAKEWORD(2, 2);
WSADATA data;

if (WSAStartup(version, &data) != 0) {
std::cerr << "WSAStartup() failure" << std::endl;
return -1;
}
#endif /* _WIN32 */

cpp_redis::redis_client client;

client.connect("127.0.0.1", 6379, [](cpp_redis::redis_client&) {
std::cout << "client disconnected (disconnection handler)" << std::endl;
});

//! client kill ip:port
client.client_list([&client](cpp_redis::reply& reply) {
std::string addr;
std::stringstream ss(reply.as_string());

ss >> addr >> addr;

std::string host = std::string(addr.begin() + addr.find('=') + 1, addr.begin() + addr.find(':'));
int port = std::stoi(std::string(addr.begin() + addr.find(':') + 1, addr.end()));

client.client_kill(host, port, [](cpp_redis::reply& reply) {
std::cout << reply << std::endl; //! OK
});

client.commit();
});

client.sync_commit();
std::this_thread::sleep_for(std::chrono::seconds(1));

if (!client.is_connected()) {
client.connect("127.0.0.1", 6379, [](cpp_redis::redis_client&) {
std::cout << "client disconnected (disconnection handler)" << std::endl;
});
}

//! client kill filter
client.client_list([&client](cpp_redis::reply& reply) {
std::string id_str;
std::stringstream ss(reply.as_string());

ss >> id_str;

uint64_t id = std::stoi(std::string(id_str.begin() + id_str.find('=') + 1, id_str.end()));
client.client_kill(id, false, cpp_redis::redis_client::client_type::normal, [](cpp_redis::reply& reply) {
std::cout << reply << std::endl; //! 1
});

client.commit();
});

client.sync_commit();
std::this_thread::sleep_for(std::chrono::seconds(1));

#ifdef _WIN32
WSACleanup();
#endif /* _WIN32 */

return 0;
}
18 changes: 16 additions & 2 deletions includes/cpp_redis/future_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ class future_client {
future blpop(const std::vector<std::string>& keys, int timeout);
future brpop(const std::vector<std::string>& keys, int timeout);
future brpoplpush(const std::string& src, const std::string& dst, int timeout);
// future client_kill() [ip:port] [id client-id] [type normal|master|slave|pubsub] [addr ip:port] [skipme yes/no]
template <typename T, typename... Ts>
future client_kill(const T, const Ts...);
future client_list();
future client_getname();
future client_pause(int timeout);
Expand Down Expand Up @@ -305,4 +306,17 @@ class future_client {
redis_client m_client;
};

} //! cpp_redis

template <typename T, typename... Ts>
future_client::future
future_client::client_kill(const T arg, const Ts... args) {

//! gcc 4.8 doesn't handle variadic template capture arguments (appears in 4.9)
//! so std::bind should capture all arguments because of the compiler.
return exec_cmd(std::bind([this](T arg, Ts... args, const rcb_t& cb) -> rc& {
return m_client.client_kill(arg, args..., cb);
},
arg, args..., std::placeholders::_1));
}

} // namespace cpp_redis
69 changes: 69 additions & 0 deletions includes/cpp_redis/helpers/variadic_template.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2017 Simon Ninon <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

#include <type_traits>

namespace cpp_redis {
namespace helpers {

template <typename T, typename... Args>
struct back {
using type = typename back<Args...>::type;
};

template <typename T>
struct back<T> {
using type = T;
};

template <typename T, typename... Ts>
struct front {
using type = T;
};

template <typename T1, typename T2, typename... Ts>
struct is_type_present {
static constexpr bool value = std::is_same<T1, T2>::value
? true
: is_type_present<T1, Ts...>::value;
};

template <typename T1, typename T2>
struct is_type_present<T1, T2> {
static constexpr bool value = std::is_same<T1, T2>::value;
};

template <typename T, typename... Args>
struct is_different_types {
static constexpr bool value = is_type_present<T, Args...>::value
? false
: is_different_types<Args...>::value;
};

template <typename T1>
struct is_different_types<T1> {
static constexpr bool value = true;
};
} //! helpers
} //! cpp_redis
120 changes: 120 additions & 0 deletions includes/cpp_redis/impl/redis_client.ipp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2017 Simon Ninon <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <functional>
#include <iostream>

namespace cpp_redis {

template <typename T>
typename std::enable_if<std::is_same<T, redis_client::client_type>::value>::type
redis_client::client_kill_unpack_arg(std::vector<std::string>& redis_cmd, reply_callback_t&, client_type type) {
redis_cmd.emplace_back("TYPE");
std::string type_string;

switch (type) {
case client_type::normal: type_string = "normal"; break;
case client_type::master: type_string = "master"; break;
case client_type::pubsub: type_string = "pubsub"; break;
case client_type::slave: type_string = "slave"; break;
}

redis_cmd.emplace_back(type_string);
}

template <typename T>
typename std::enable_if<std::is_same<T, bool>::value>::type
redis_client::client_kill_unpack_arg(std::vector<std::string>& redis_cmd, reply_callback_t&, bool skip) {
redis_cmd.emplace_back("SKIPME");
redis_cmd.emplace_back(skip ? "yes" : "no");
}

template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type
redis_client::client_kill_unpack_arg(std::vector<std::string>& redis_cmd, reply_callback_t&, uint64_t id) {
redis_cmd.emplace_back("ID");
redis_cmd.emplace_back(std::to_string(id));
}

template <typename T>
typename std::enable_if<std::is_class<T>::value>::type
redis_client::client_kill_unpack_arg(std::vector<std::string>&, reply_callback_t& reply_callback, const T& cb) {
reply_callback = cb;
}

template <typename T, typename... Ts>
void
redis_client::client_kill_impl(std::vector<std::string>& redis_cmd, reply_callback_t& reply, const T& arg, const Ts&... args) {
static_assert(!std::is_class<T>::value, "Reply callback should be in the end of the argument list");
client_kill_unpack_arg<T>(redis_cmd, reply, arg);
client_kill_impl(redis_cmd, reply, args...);
};

template <typename T>
void
redis_client::client_kill_impl(std::vector<std::string>& redis_cmd, reply_callback_t& reply, const T& arg) {
client_kill_unpack_arg<T>(redis_cmd, reply, arg);
};

template <typename T, typename... Ts>
inline redis_client&
redis_client::client_kill(const T& arg, const Ts&... args) {
static_assert(helpers::is_different_types<T, Ts...>::value, "Should only have one distinct value per filter type");
static_assert(!(std::is_class<T>::value && std::is_same<T, typename helpers::back<T, Ts...>::type>::value), "Should have at least one filter");

std::vector<std::string> redis_cmd({"CLIENT", "KILL"});
reply_callback_t reply_cb = nullptr;
client_kill_impl<T, Ts...>(redis_cmd, reply_cb, arg, args...);

return send(redis_cmd, reply_cb);
}

template <typename T, typename... Ts>
inline redis_client&
redis_client::client_kill(const std::string& host, int port, const T& arg, const Ts&... args) {
static_assert(helpers::is_different_types<T, Ts...>::value, "Should only have one distinct value per filter type");
std::vector<std::string> redis_cmd({"CLIENT", "KILL"});

//! If we have other type than lambda, then it's a filter
if (!std::is_class<T>::value) {
redis_cmd.emplace_back("ADDR");
}

redis_cmd.emplace_back(host + ":" + std::to_string(port));
reply_callback_t reply_cb = nullptr;
client_kill_impl<T, Ts...>(redis_cmd, reply_cb, arg, args...);

return send(redis_cmd, reply_cb);
}

inline redis_client&
redis_client::client_kill(const std::string& host, int port) {
return client_kill(host, port, reply_callback_t(nullptr));
}

template <typename... Ts>
inline redis_client&
redis_client::client_kill(const char* host, int port, const Ts&... args) {
return client_kill(std::string(host), port, args...);
}

} // namespace cpp_redis
2 changes: 1 addition & 1 deletion includes/cpp_redis/network/tcp_client_iface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@

#pragma once

#include <cstdint>
#include <functional>
#include <string>
#include <vector>
#include <cstdint>

namespace cpp_redis {

Expand Down
Loading