1
0
Fork 0
mirror of https://github.com/KingDuckZ/kamokan.git synced 2025-10-06 15:09:59 +00:00

Use the lua script to save new pasties.

I had to add a {store:} prefix, see this:
https://stackoverflow.com/questions/38720084/generate-new-key-inside-evalsha
This commit is contained in:
King_DuckZ 2017-06-23 21:49:28 +01:00
commit 6222f1e1b1
8 changed files with 78 additions and 42 deletions

View file

@ -8,7 +8,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(${PROJECT_NAME} STATIC
response.cpp
submit_paste_response.cpp
num_to_token.cpp
index_response.cpp
pastie_response.cpp
ini_file.cpp

View file

@ -1,64 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "kamokan".
*
* "kamokan" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "kamokan" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "kamokan". If not, see <http://www.gnu.org/licenses/>.
*/
#include "num_to_token.hpp"
#include <cassert>
namespace kamokan {
namespace {
//const int g_any_min = 0;
//const int g_any_max = g_any_min + 'z' - 'a' - 1;
//const int g_vowel_min = g_any_max + 1;
//const int g_vowel_max = g_vowel_min + 5 - 1;
//const int g_consonant_min = g_vowel_max + 1;
//const int g_consonant_max = g_consonant_min + ('z' - 'a') - (g_vowel_max - g_vowel_min) - 1;
//char code_to_char (int parCode) {
// const char vowels[] = {'a', 'i', 'u', 'e', 'o'};
// const char consonants[] = {
// 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p',
// 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'
// };
// static_assert(sizeof(vowels) == g_vowel_max - g_vowel_min + 1, "Wrong vowels count");
// static_assert(sizeof(consonants) == g_consonant_max - g_consonant_min + 1, "Wrong consonants count");
// if (parCode <= g_any_max)
// return static_cast<char>('a' + parCode - g_any_min);
// else if (parCode <= g_vowel_max)
// return vowels[parCode - g_vowel_min];
// else if (parCode <= g_consonant_max)
// return consonants[parCode - g_consonant_min];
// assert(false);
// return 'X';
//}
} //unnamed namespace
std::string num_to_token (int64_t parNum) {
assert(0 < parNum);
std::string retval;
do {
const auto remainder = parNum % ('z' - 'a' + 1);
retval.push_back(static_cast<char>('a' + remainder));
parNum /= ('z' - 'a' + 1);
} while (parNum);
return retval;
}
} //namespace kamokan

View file

@ -1,26 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "kamokan".
*
* "kamokan" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "kamokan" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "kamokan". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "duckhandy/compatibility.h"
#include <string>
#include <cstdint>
namespace kamokan {
std::string num_to_token (int64_t parNum) a_pure;
} //namespace kamokan

View file

@ -1,5 +1,7 @@
local token = KEYS[1]
local result = redis.call("HMGET", token, "pastie", "selfdes", "lang")
local token_prefix = ARGV[1]
local full_token = token_prefix .. token
local result = redis.call("HMGET", full_token, "pastie", "selfdes", "lang")
if false == result[1] then
return redis.error_reply("PastieNotFound")
end
@ -7,7 +9,7 @@ end
local selfdes = 0
local deleted = 0
if result[2] == 1 then
deleted = redis.call("DEL", token)
deleted = redis.call("DEL", full_token)
selfdes = 1
end

View file

@ -1,4 +1,4 @@
function num_to_value (num)
local function num_to_token (num)
local retval = ""
local running = true
@ -12,18 +12,34 @@ function num_to_value (num)
return retval
end
paste_counter = KEYS[1]
next_id = redis.call("INCR", paste_counter) - 1
token = num_to_token(next_id)
text = ARGV[1]
ttl = ARGV[2]
lang = ARGV[3]
selfdestruct = ARGV[4]
redis.call("HMSET", token,
"pastie", text,
"max_ttl", ttl,
"lang", lang,
"selfdes", selfdestruct
local paste_counter_token = KEYS[1]
local flooding_token = KEYS[2]
local flooding_result = redis.call("GET", flooding_token)
if flooding_result then
return redis.error_reply("UserFlooding")
end
local token_prefix = ARGV[1]
local text = ARGV[2]
local ttl = ARGV[3]
local lang = ARGV[4]
local selfdestruct = ARGV[5]
local flood_wait = ARGV[6]
local next_id = redis.call("INCR", paste_counter_token) - 1
local token = num_to_token(next_id)
local saved = redis.call("HMSET", token_prefix .. token,
"pastie", text,
"max_ttl", ttl,
"lang", lang,
"selfdes", selfdestruct
)
if saved then
redis.call("SET", flooding_token, "")
redis.call("EXPIRE", flooding_token, flood_wait)
else
return redis.error_reply("PastieNotSaved")
end
return token

View file

@ -18,12 +18,12 @@
#include "storage.hpp"
#include "settings_bag.hpp"
#include "incredis/incredis.hpp"
#include "num_to_token.hpp"
#include "duckhandy/stringize.h"
#include "spdlog.hpp"
#include "truncated_string.hpp"
#include "string_conv.hpp"
#include "lua_scripts_for_redis.hpp"
#include "redis_to_error_reason.hpp"
#include <cassert>
#include <ciso646>
#include <string>
@ -32,6 +32,8 @@
namespace kamokan {
namespace {
const char g_token_prefix[] = "kamokan:{store:}";
redis::IncRedis make_incredis (const SettingsBag& parSettings) {
using redis::IncRedis;
@ -130,54 +132,64 @@ namespace kamokan {
const std::string& parRemoteIP
) const {
using tawashi::ErrorReasons;
using redis::RedisInt;
using boost::string_view;
using dhandy::lexical_cast;
if (not is_connected())
return make_submission_result(ErrorReasons::RedisDisconnected);
assert(m_redis);
auto& redis = *m_redis;
if (redis.get(parRemoteIP)) {
//please wait and submit again
return make_submission_result(ErrorReasons::UserFlooding);
redis::Script retrieve = m_redis->command().make_script(string_view(g_save_script, g_save_script_size));
auto batch = m_redis->command().make_batch();
{
string_view paste_counter_token("{store:}paste_counter");
std::string prefix(g_token_prefix);
const auto expiry = lexical_cast<std::string>(parExpiry);
const auto self_des = string_view(parSelfDestruct ? "1" : "0");
const auto flood_wait = m_settings->as<string_view>("resubmit_wait");
retrieve.run(batch,
std::make_tuple(paste_counter_token, prefix + parRemoteIP),
std::make_tuple(prefix, parText, expiry, parLang, self_des, flood_wait)
);
}
auto raw_replies = batch.replies();
auto statuslog = spdlog::get("statuslog");
if (raw_replies.empty()) {
statuslog->error("Received empty reply from redis");
return make_submission_result(ErrorReasons::UnknownReason);
}
auto statuslog = spdlog::get("statuslog");
if (raw_replies.front().is_error()) {
statuslog->error("Received error reply from redis");
return make_submission_result(redis_to_error_reason(get_error_string(raw_replies.front())));
}
std::string token = get_string(raw_replies.front());
if (statuslog->should_log(spdlog::level::info)) {
statuslog->info(
"Submitting pastie of size {} to redis -> \"{}\"",
"Saved pastie of size {} to redis token \"{}\" -> \"{}\"",
parText.size(),
token,
tawashi::truncated_string(parText, 30)
);
}
const auto next_id = redis.incr("paste_counter");
std::string token = num_to_token(next_id);
assert(not token.empty());
if (redis.hmset(token,
"pastie", parText,
"max_ttl", dhandy::lexical_cast<std::string>(parExpiry),
"lang", parLang,
"selfdes", static_cast<RedisInt>(parSelfDestruct ? 1 : 0))
) {
redis.set(parRemoteIP, "");
redis.expire(parRemoteIP, m_settings->as<uint32_t>("resubmit_wait"));
if (redis.expire(token, parExpiry))
return make_submission_result(std::move(token));
}
return make_submission_result(ErrorReasons::PastieNotSaved);
return make_submission_result(std::move(token));
}
auto Storage::retrieve_pastie (const boost::string_view& parToken, uint32_t parMaxTokenLen) const -> RetrievedPastie {
using boost::string_view;
RetrievedPastie retval;
retval.valid_token = is_valid_token(parToken, parMaxTokenLen);
if (not retval.valid_token)
return retval;
redis::Script retrieve = m_redis->command().make_script(boost::string_view(g_load_script, g_load_script_size));
redis::Script retrieve = m_redis->command().make_script(string_view(g_load_script, g_load_script_size));
auto batch = m_redis->command().make_batch();
retrieve.run(batch, std::make_tuple(parToken), std::make_tuple());
retrieve.run(batch, std::make_tuple(parToken), std::make_tuple(string_view(g_token_prefix)));
auto raw_replies = batch.replies();
if (raw_replies.empty())
return retval;

View file

@ -50,6 +50,12 @@ namespace kamokan {
return dhandy::lexical_cast<uint32_t>(parStr);
}
template <>
[[gnu::pure,gnu::always_inline]] inline
long long string_conv (boost::string_view parStr) {
return dhandy::lexical_cast<long long>(parStr);
}
template <>
inline
std::string string_conv (boost::string_view parStr) {