├── LICENSE
├── README.md
└── simple-url-shorten
├── config.lua
├── functions.lua
├── index.lua
└── shorten.lua
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 peano
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # simple-url-shorten #
2 | ## 基于lua-ngx的简单URL缩短系统 ##
3 |
4 | simple-url-shorten是一个基于openresty的Lua模块和Redis模块开发的简单、快速的网址缩短“系统”,它能够对提交的网址就行编码,缩短到指定长度的网址。Nginx、Lua、Redis的我这里就不做介绍了,但是他们都有一个共同的优点——性能高。simple-url-shorten具有如下特点:
5 |
6 | 1. 使用Nginx+lua+redis,性能非常高;
7 | 2. 具有域名黑名单、白名单,支持简单认证;
8 | 3. 支持自定义短URL长度;
9 | 4. 支持自定义短网址字符前缀、后缀
10 | 5. 使用302方式跳转,302模板可以通过修改Nginx302模板修改
11 | 6. 使用json方式返回数据
12 |
13 | 既然是简单的URL网址缩短服务,当然也会有不足之处:
14 |
15 | 1. 没有计数及分析功能:前期考虑添加计数及分析功能,但是考虑跳转的高效性,省略了计数及统计分析功能,该功能可能在以后的更新中添加;
16 | 2. 需要手工编译安装Openresty,对于不熟悉Linux软件编译安装的用户有一定的难度;
17 |
18 | ## 安装方法 ##
19 | 1. 编译安装openresty,要启用lua模块。[http://openresty.org/](http://openresty.org/ "Openresty")
20 | 2. 安装并配置redis软件
21 | 3. 下载simple-url-shorten文件,lualib(默认/usr/local/openresty/lualib)目录中新建short目录,将lua代码文件放置于目录中
22 | [root@localhost ~]# ll /usr/local/openresty/lualib/short
23 | -rw-r--r--. 1 root root 2434 Dec 22 16:44 config.lua
24 | -rw-r--r--. 1 root root 3491 Dec 22 16:44 functions.lua
25 | -rw-r--r--. 1 root root 313 Dec 22 16:13 index.lua
26 | -rw-r--r--. 1 root root 369 Dec 22 16:35 shorten.lua
27 |
28 | 4. 配置nginx.conf(文件默认位置/usr/local/openresty/nginx/conf/nginx.conf)
29 |
30 | server {
31 |
32 | ....
33 |
34 | location / {
35 | lua_socket_keepalive_timeout 30s;
36 | content_by_lua_file /usr/local/openresty/lualib/short/index.lua;
37 | }
38 | location /short {
39 | lua_socket_keepalive_timeout 30s;
40 | content_by_lua_file /usr/local/openresty/lualib/short/shorten.lua;
41 | }
42 |
43 | ...
44 | }
45 | 5. 配置simple-url-shorten
46 |
47 | ...
48 |
49 | -- 配置Redis数据库信息
50 | local redis = {}
51 | redis['host'] = '127.0.0.1'
52 | redis['port'] = 6379
53 | redis['password'] = 'passwd'
54 |
55 |
56 | -- 设置起始的短网址,根据长度可以选择需要多少数量的短网址
57 | -- 1 -> 62
58 | -- 2 -> 3844
59 | -- 3 -> 238328
60 | -- 4 -> 14776336
61 | -- 5 -> 916132832
62 | -- 6 -> 56800235584
63 | -- 7 -> 3521614606208
64 | -- 8 -> 218340105584896
65 | -- 9 -> 13537086546263552
66 | -- 10 -> 839299365868340224
67 | -- 11 -> 52036560683837093888
68 | -- 12 -> 3226266762397899821056
69 | -- 13 -> 200028539268669788905472
70 | -- 14 -> 12401769434657526912139264
71 | -- 15 -> 768909704948766668552634368
72 | -- 16 -> 47672401706823533450263330816
73 | local start_url = {'0','0','0','0'}
74 |
75 | -- 短网址前缀
76 | local prefix = ''
77 |
78 | -- 短网址后缀
79 | local suffix = ''
80 |
81 | -- 短网址的域名
82 | local domain = 'http://192.168.56.201/'
83 |
84 | -- 接受网址缩短的白名单和黑名单,白名单优先级高于黑名单;
85 | -- 黑名单中默认包含短网址的域名
86 | local white_host = {}
87 | local black_host = {domain}
88 |
89 | ......
90 |
91 |
92 | 5. 启动nginx服务
93 |
94 |
95 | ## 使用方法 ##
96 |
97 | 想要对网址
98 |
99 | https://www.google.com/search?newwindow=1&biw=1600&bih=753&q=simple+url+shorten&oq=simple+url+shorten&gs_l=serp.3..0i19l5j0i30i19j0i5i30i19l4.17382489.17389081.0.17389560.18.18.0.0.0.0.418.2756.0j5j3j2j1.11.0.msedr...0...1c.1.60.serp..7.11.2753.th4LFd5J5uU
100 |
101 | 进行网址缩短,只需进行GET方式请求即可:
102 |
103 | Request:
104 | http://XXX.com/short?url=https%3A%2f%2fwww.google.com%2fsearch%3Fnewwindow%3D1%26biw%3D1600%26bih%3D753%26q%3Dsimple%2burl%2bshorten%26oq%3Dsimple%2burl%2bshorten%26gs_l%3Dserp.3..0i19l5j0i30i19j0i5i30i19l4.17382489.17389081.0.17389560.18.18.0.0.0.0.418.2756.0j5j3j2j1.11.0.msedr...0...1c.1.60.serp..7.11.2753.th4LFd5J5uU
105 |
106 | Response:
107 | {"status":1,"shorturl":"http://XXX.com/Rqo3F"}
108 |
109 | 访问过程:
110 | Request:
111 | http://XXX.com/Rqo3F
112 |
113 | Response:
114 |
115 | HTTP/1.1 302 Moved Temporarily
116 | Server: openresty
117 | Date: Tue, 23 Dec 2014 06:57:47 GMT
118 | Content-Type: text/html
119 | Content-Length: 154
120 | Connection: keep-alive
121 | Location: http://www.baidu.com/index.php?xxx=234
122 |
123 |
124 |
302 Found
125 |
126 | 302 Found
127 |
nginx
128 |
129 |
130 |
131 |
132 | ## 注意事项 ##
133 | 1. host白名单优先级高于黑名单,设置了白名单将只能对白名单的host网址进行网址缩短
134 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/simple-url-shorten/config.lua:
--------------------------------------------------------------------------------
1 | local mod_name = ...
2 | local M = {}
3 | local base_table = {'e','Q','W','3','s','5','R','J','a','g','M','T','7','n','O','X','8','f','2','o','z','w','G','K','x','D','Z','F','b','u','N','L','S','l','Y','V','I','t','4','q','6','A','y','1','i','B','C','c','P','0','U','r','k','j','m','p','9','h','v','d','E','H'}
4 |
5 | -- Base table for short URL string
6 | -- You can use random table as this table
7 | -- local base_table = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
8 |
9 | -- Base string of Base table
10 | local base_str = table.concat( base_table, "")
11 |
12 | -- analysis switch
13 | -- local analysis = 1
14 |
15 | -- Redis database config
16 | local redis = {}
17 | redis['host'] = '127.0.0.1'
18 | redis['port'] = 6379
19 | redis['password'] = 'passwd'
20 |
21 |
22 | -- Initial Short URL
23 | -- The first Short URL,will no use
24 | -- The length depens on the table's numbers
25 | -- chars length and the numbers of URL are following
26 | -- 1 -> 62
27 | -- 2 -> 3844
28 | -- 3 -> 238328
29 | -- 4 -> 14776336
30 | -- 5 -> 916132832
31 | -- 6 -> 56800235584
32 | -- 7 -> 3521614606208
33 | -- 8 -> 218340105584896
34 | -- 9 -> 13537086546263552
35 | -- 10 -> 839299365868340224
36 | -- 11 -> 52036560683837093888
37 | -- 12 -> 3226266762397899821056
38 | -- 13 -> 200028539268669788905472
39 | -- 14 -> 12401769434657526912139264
40 | -- 15 -> 768909704948766668552634368
41 | -- 16 -> 47672401706823533450263330816
42 | local start_url = {'0','0','0','0'}
43 |
44 | -- Short URL's prefix
45 | -- Default is NULL
46 | local prefix = ''
47 |
48 | -- Short URL's suffix
49 | local suffix = ''
50 |
51 | -- Domain for short URL
52 | -- if is empty,will return short string
53 | local domain = 'http://192.168.56.201/'
54 |
55 | -- White list of domain
56 | -- Host just as: www.example.com:8081
57 | -- if you set white_host,this tool is only allow the url in the white list host
58 | local white_host = {}
59 | local black_host = {domain}
60 |
61 | -- Errors
62 | local err_msg = {}
63 | err_msg[34] = 'URL has existed'
64 | err_msg[41] = 'URL is invalid'
65 | err_msg[42] = 'URL is not allow to shorten'
66 | err_msg[51] = 'Database is out of service'
67 | err_msg[52] = 'Getting data error'
68 | err_msg[53] = 'Larger than maximum numbers of URLs'
69 |
70 | M['base_table'] = base_table
71 | M['base_str'] = base_str
72 | M['prefix'] = prefix
73 | M['start_url'] = start_url
74 | M['suffix'] = suffix
75 | M['white_host'] = white_host
76 | M['black_host'] = black_host
77 | M['err_msg'] = err_msg
78 | M['redis'] = redis
79 | M['domain'] = domain
80 |
81 |
82 | return M
83 |
--------------------------------------------------------------------------------
/simple-url-shorten/functions.lua:
--------------------------------------------------------------------------------
1 | local mod_name = (...)
2 | local config = require('short/config')
3 | local cjson = require('cjson')
4 | local redis = require ("resty.redis")
5 | local M = {}
6 |
7 | function M.find_in_table(table, value)
8 | if table == ngx.null or #table==0 then
9 | return false
10 | end
11 | for k,v in pairs(table) do
12 | if v == value then
13 | return true
14 | end
15 | end
16 | return false
17 | end
18 |
19 | -- get url details
20 | function M.get_url_details(long_url)
21 | if long_url==nil or long_url=='' or #long_url>2048 then
22 | return nil, 41
23 | end
24 | local result = {}
25 | local matches = ngx.re.match(long_url, '^(http|https|ftp)://([^/]+)((/[^\\\\?#]+)([^#]+)?(.+))?')
26 | if matches[2] then
27 | result['protocol'] = matches[1]
28 | result['host'] = matches[2]
29 | else
30 | return nil,41
31 | end
32 | if #config['white_host'] ~= 0 then
33 | if M.find_in_table(config['white_host'], result['host']) then
34 | return result
35 | else
36 | return nil, 42
37 | end
38 | end
39 | if M.find_in_table(config['black_host'], result['host']) then
40 | return nil, 42
41 | end
42 | return result
43 | end
44 |
45 | -- connect redis database
46 | function M.redis_connect()
47 | local red = redis:new()
48 | red:set_timeout(1000)
49 | local ok, err = red:connect(config['redis']['host'], config['redis']['port'])
50 | if not ok then
51 | return nil, 51
52 | end
53 | local res, err = red:auth(config['redis']['password'])
54 | if not res then
55 | return nil, 51
56 | end
57 | return red
58 | end
59 |
60 | function M.get_short_string(last)
61 | local result = {}
62 | local str = ''
63 | if last ~= ngx.null then
64 | str = string.sub(last, #config['prefix']+1, #last-#config['suffix'])
65 | for k in string.gmatch(str, "([0-9a-zA-Z])") do
66 | table.insert(result, k)
67 | end
68 | else
69 | str = table.concat(config['start_url'],'')
70 | result = config['start_url']
71 | end
72 | for i=#str, 1, -1 do
73 | local char = string.sub(str, i, i)
74 |
75 | local pos = string.find(config['base_str'], char)
76 |
77 | local pos_start = string.find(config['base_str'], config['start_url'][i])
78 |
79 | if pos == #config['base_str'] then
80 | pos =0
81 | end
82 |
83 | result[i] = config['base_table'][pos+1]
84 | if pos_start ~= pos+1 then
85 | break
86 | elseif i == 1 then
87 | return nil, 53
88 | end
89 | end
90 |
91 | return config['prefix'].. table.concat(result,'') .. config['suffix']
92 | end
93 |
94 | function M.get_long_url(short_string)
95 | if ngx.re.find(short_string, '[^0-9a-zA-Z]') then
96 | return false,41
97 | end
98 | local red, err = M.redis_connect()
99 | if err then
100 | return false, err
101 | end
102 | local result, err = red:get('S_' .. short_string)
103 | if err then
104 | return nil, 52
105 | end
106 | red:set_keepalive(10000, 100)
107 | if result ~= ngx.null then
108 | return result
109 | else
110 | return nil, 52
111 | end
112 | end
113 |
114 | function M.show_error(err_code, long_url)
115 | local result = {}
116 | result['status'] = 0
117 | result['error'] = err_code
118 | result['msg'] = config['err_msg'][err_code]
119 | if long_url then
120 | result['url'] = long_url
121 | end
122 | ngx.say(cjson.encode(result))
123 | ngx.exit(ngx.HTTP_OK)
124 | end
125 |
126 | function M.url_create(long_url, short_string)
127 | local red, err = M.redis_connect()
128 | if err then
129 | return false, err
130 | end
131 | local url_md5 = ngx.md5(long_url)
132 | local result, err = red:get('M_' .. url_md5)
133 | if err then
134 | return nil, 52
135 | end
136 | if result ~= ngx.null then
137 | return config['domain']..result
138 | end
139 | local url_details, err = M.get_url_details(long_url)
140 | if err then
141 | return nil, err
142 | end
143 | local last, err = red:get('V_last')
144 | if err or last==ngx.NULL then
145 | return nil, 52
146 | end
147 | local short_string, err = M.get_short_string(last)
148 | if err then
149 | return nil, err
150 | end
151 | red:set('M_' .. url_md5, short_string)
152 | red:set('V_last', short_string)
153 | red:set('S_' .. short_string, long_url)
154 | red:set_keepalive(10000, 100)
155 | return config['domain']..short_string
156 | end
157 |
158 |
159 | return M
160 |
--------------------------------------------------------------------------------
/simple-url-shorten/index.lua:
--------------------------------------------------------------------------------
1 | ngx.header.content_type = 'text/html'
2 | local config = require('short/config')
3 | local functions = require('short/functions')
4 | local short_string = string.sub(ngx.var.uri, -#config['start_url'])
5 | local long_url, err = functions.get_long_url(short_string)
6 | if err then
7 | functions.show_err(err)
8 | end
9 | ngx.redirect(long_url)
10 |
--------------------------------------------------------------------------------
/simple-url-shorten/shorten.lua:
--------------------------------------------------------------------------------
1 | ngx.header.content_type = 'text/json'
2 |
3 | local functions = require('short/functions')
4 | -- local cjson = require('cjson')
5 | local args = ngx.req.get_uri_args()
6 |
7 | local long_url = args['url']
8 | local short_string = args['short']
9 | local short_url, err = functions.url_create(long_url)
10 | if err then
11 | functions.show_error(err)
12 | end
13 | ngx.say('{"status":1,"shorturl":"'..short_url..'"}')
14 |
--------------------------------------------------------------------------------