-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ourls-Resty, ported from takashiki/Ourls
- Loading branch information
0 parents
commit c274c98
Showing
15 changed files
with
1,008 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.