Skip to content

Commit

Permalink
Ourls-Resty, ported from takashiki/Ourls
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaooloong committed Sep 5, 2016
0 parents commit c274c98
Show file tree
Hide file tree
Showing 15 changed files with 1,008 additions and 0 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Ourls-Resty

[Ourls][1] 是由 [takashiki][2] 实现的一个基于发号和 hashid 的短网址服务。
受这个项目的启发,将此工程移植,使用 [OpenResty][3] 实现。

### 待移植的功能:

- url 规格化
- 前置代理支持

### 待增加的特性:

- Cache 支持

### 安装方法:

- 安装 openresty rpm 包(或手动编译,建议使用 --prefix=/usr/local/openresty)
- 将原 openresty/nginx/conf 目录备份
- 将本工程解压到 openresty/nginx/conf 目录
- 进入 openresty/nginx/conf/ourl 目录,复制 config.sample.lua 为 config.lua
- 修改 config.lua 中的数据库等配置
- 恢复 urls.sql 至 mysql/mariadb 数据库
- 进入 openresty/nginx/conf/vhosts 目录,修改 ourl.conf 中的 server_name
- 进入 openresty/nginx/conf/lib/hashids 目录,执行 make 命令
- 启动 openresty

### 使用到的其他项目

- [leihog/hashids.lua][4]
- [APItools/router.lua][5]

[1]: https://github.com/takashiki/Ourls
[2]: https://github.com/takashiki
[3]: http://openresty.org/
[4]: https://github.com/leihog/hashids.lua
[5]: https://github.com/APItools/router.lua
10 changes: 10 additions & 0 deletions lib/hashids/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
UNAME := $(shell uname -s)

all:
ifeq ($(UNAME),Darwin)
@echo building for OS X
gcc -bundle -undefined dynamic_lookup -I/usr/local/openresty/luajit/include/luajit-2.1 -o clib.so clib.c
else
@echo building for Linux
gcc -Wall -shared -fPIC -I/usr/local/openresty/luajit/include/luajit-2.1 -o clib.so clib.c
endif
48 changes: 48 additions & 0 deletions lib/hashids/clib.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "lua.h"
#include "lauxlib.h"
#include "string.h"
#include "stdlib.h"

static int str_switch_pos(lua_State *L) {
size_t slen;
const char *str = lua_tolstring(L, 1, &slen);
int pos1 = lua_tonumber(L, 2);
int pos2 = lua_tonumber(L, 3);

char *nstr = strdup(str);
nstr[pos1] = str[pos2];
nstr[pos2] = str[pos1];

lua_pushlstring(L, nstr, slen);
free(nstr);
return 1;
}

static const luaL_reg hashidslib[] = {
{"str_switch_pos", str_switch_pos},
{NULL, NULL}
};

#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM == 501
/*
* http://lua-users.org/wiki/CompatibilityWithLuaFive
*/
static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */
int i;
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
lua_pushstring(L, l->name);
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
lua_settable(L, -(nup + 3));
}
lua_pop(L, nup); /* remove upvalues */
}
#endif

LUALIB_API int luaopen_hashids_clib(lua_State *L) {
lua_newtable(L);
luaL_setfuncs(L, hashidslib, 0);
return 1;
}
237 changes: 237 additions & 0 deletions lib/hashids/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
local ceil = math.ceil
local floor = math.floor
local pow = math.pow
local substr = string.sub
local upcase = string.upper
local format = string.format
local strcat = table.concat
local push = table.insert
local str_switch_pos

local ok, lib = pcall(require, "lib.hashids.clib");
if ok then
str_switch_pos = lib.str_switch_pos;
else
str_switch_pos = function(str, pos1, pos2)
pos1 = pos1 + 1; pos2 = pos2 + 1;
local a,b = str:sub(pos1,pos1), str:sub(pos2, pos2);
if pos1 > pos2 then return str:gsub(a, b, 1):gsub(b, a, 1); end
return str:gsub(b, a, 1):gsub(a, b, 1);
end
end


hash_mt = {};
hash_mt.__index = hash_mt;

local function gcap(str, pos)
pos = pos + 1;
return str:sub(pos, pos);
end

-- TODO using string concatenation with .. might not be the fastest in a loop
local function hash(number, alphabet)
local hash, alen = "", alphabet:len();
repeat
hash = gcap(alphabet, (number % alen)) .. hash;
number = floor(number / alen);
until number == 0

return hash;
end

local function unhash(input, alphabet)
local number, ilen, alen = 0, input:len(), alphabet:len();

for i=0, ilen do
local cpos = (alphabet:find(gcap(input, i), 1, true) - 1);
number = number + cpos * pow(alen, (ilen - i - 1))
end

return number;
end

local function consistent_shuffle(alphabet, salt)
local slen = salt:len();
if slen == 0 then return alphabet end

local v, p = 0, 0;
for i = (alphabet:len() - 1), 1, -1 do
v = (v % slen);
local ord = gcap(salt, v):byte();
p = p + ord;
local j = (ord + v + p) % i;

alphabet = str_switch_pos(alphabet, j, i);
v = v + 1;
end

return alphabet;
end

function hash_mt:encode(...)
local numbers = {select(1,...)};
local numbers_size, hash_int = #numbers, 0;

for i, number in ipairs(numbers) do
assert(type(number) == 'number', "all paramters must be numbers");
hash_int = hash_int + (number % ((i - 1) + 100));
end

local alpha = self.alphabet;
local alpha_len = alpha:len();

local lottery = gcap(alpha, hash_int % alpha_len);
local ret = lottery;
local last = nil;

for i, number in ipairs(numbers) do
-- for i=1, #numbers do
-- local number = numbers[i];
alpha = consistent_shuffle(alpha, substr(strcat({lottery, self.salt, alpha}), 1, alpha_len));
last = hash(number, alpha);
ret = ret .. last;

if i < numbers_size then
number = number % (last:byte() + (i - 1));
ret = ret .. gcap(self.seps, (number % self.seps:len()));
end
end

local guards_len = self.guards:len();
if ret:len() < self.min_hash_length then
local guard_index = (hash_int + gcap(ret, 0):byte()) % guards_len;
ret = gcap(self.guards, guard_index) .. ret;

if ret:len() < self.min_hash_length then
guard_index = (hash_int + gcap(ret, 2):byte()) % guards_len;
ret = ret .. gcap(self.guards, guard_index);
end
end

local half_len, excess = floor(alpha_len * 0.5), 0; -- alpha_len / 2
while ret:len() < self.min_hash_length do
alpha = consistent_shuffle(alpha, alpha);
ret = alpha:sub(half_len + 1) .. ret .. alpha:sub(1, half_len);

excess = (ret:len() - self.min_hash_length);
if excess > 0 then
excess = (excess * 0.5);
ret = ret:sub(excess + 1, (excess + self.min_hash_length));
end
end

return ret;
end

function hash_mt:encode_hex(str)
local pos, max, numbers = 0, #str, {}
while true do
local part = substr(str, pos + 1, pos + 12)
if part == "" then break end
pos = pos + #part
push(numbers, tonumber("1" .. part, 16))
end

return self:encode(unpack(numbers))
end

function hash_mt:decode(hash)
-- TODO validate input

local parts, index = {}, 1;
for part in hash:gmatch("[^".. self.guards .."]+") do
parts[index] = part;
index = index + 1;
end

local num_parts, t, lottery = #parts;
if num_parts == 3 or num_parts == 2 then
t = parts[2];
else
t = parts[1];
end

lottery = gcap(t, 0); -- put the first char in lottery
t = t:sub(2); -- then put the rest in t

parts, index = {}, 1;
for part in t:gmatch("[^".. self.seps .."]+") do
parts[index] = part;
index = index + 1;
end

local ret, alpha = {}, self.alphabet;
for i=1, #parts do
alpha = consistent_shuffle(alpha, substr(strcat({lottery, self.salt, alpha}), 1, self.alphabet_length));
ret[i] = unhash(parts[i], alpha);
end

return ret;
end

function hash_mt:decode_hex(hash)
local result, numbers = {}, self:decode(hash)
for _, number in ipairs(numbers) do
push(result, substr(format("%x", number), 2))
end

return upcase(strcat(result))
end

return {
VERSION = "1.0.0",
new = function(salt, min_hash_length, alphabet)
salt = salt or "";
min_hash_length = min_hash_length or 0;
alphabet = alphabet or "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
-- TODO make sure alphabet doesn't contain duplicates.

local tmp_seps, tmp_alpha, c = "", "";
local seps = "cfhistuCFHISTU";

for i = 1, alphabet:len() do
c = alphabet:sub(i,i);

if seps:find(c, 1, true) then
tmp_seps = tmp_seps .. c;
else
tmp_alpha = tmp_alpha .. c;
end
end

seps = consistent_shuffle(tmp_seps, salt);
alphabet = tmp_alpha;

-- constants
local SEPS_DIV = 3.5;
local GUARD_DIV = 12;

if seps:len() == 0 or (alphabet:len() / seps:len()) > SEPS_DIV then
local seps_len = floor(ceil(alphabet:len() / SEPS_DIV));
if seps_len == 1 then seps_len = 2 end

if seps_len > seps:len() then
local diff = seps_len - seps:len();
seps = seps .. alphabet:sub(1, diff);
alphabet = alphabet:sub(diff + 1);
else
seps = seps:sub(1, seps_len);
end
end

alphabet = consistent_shuffle(alphabet, salt);
local guards = "";
local guard_count = ceil(alphabet:len() / GUARD_DIV);
if alphabet:len() < 3 then
guards = seps:sub(1, guard_count);
seps = seps:sub(guard_count + 1);
else
guards = alphabet:sub(1, guard_count);
alphabet = alphabet:sub(guard_count + 1);
end

local obj = { salt = salt, alphabet = alphabet, seps = seps, guards = guards, min_hash_length = min_hash_length };
return setmetatable(obj, hash_mt);
end
}
Loading

0 comments on commit c274c98

Please sign in to comment.