├── LICENSE ├── README.md ├── dist.ini ├── lib └── resty │ └── upload_rate_limit.lua └── nginx.conf /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, luvjoey1996 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # resty-upload-rate-limit 2 | 3 | ### this repo keeps a minimum openresty configuration and some lua code, show you how to limit upload rate. 4 | 5 | #### [关于这个功能的思路和一些想法](https://yukityan.cc/openresty-xian-zhi-wen-jian-shang-chuan-su-du/) 6 | 7 | #### setup 8 | ```console 9 | foo@bar> git clone https://github.com/luvjoey1996/resty-upload-rate-limit.git 10 | foo@bar> cd resty-upload-rate-limit 11 | foo@bar> mkdir logs 12 | foo@bar> openresty -p `pwd` -c nginx.conf 13 | ``` 14 | 15 | #### test 16 | ```console 17 | foo@bar> curl 127.0.0.1:8888 -T {some file} 18 | ``` 19 | 20 | #### main code 21 | ```lua 22 | -- iter req body, limit upload speed in 200kb/s 23 | for chunk in limit_recv_body(200) do 24 | ngx.req.append_body(chunk) 25 | end 26 | ``` 27 | 28 | #### demo 29 | ![截图_2020-08-03_23-37-17.png](https://i.loli.net/2020/08/03/Z5JlBWvPNEpYhsk.png) 30 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name=lua-resty-upload-rate-limit 2 | abstract=limit request body upload speed 3 | author=luvjoey1996 4 | is_original=yes 5 | license=3bsd 6 | lib_dir=lib 7 | doc_dir=lib 8 | repo_link=https://github.com/luvjoey1996/resty-upload-rate-limit 9 | main_module=lib/resty/upload_rate_limit.lua 10 | -------------------------------------------------------------------------------- /lib/resty/upload_rate_limit.lua: -------------------------------------------------------------------------------- 1 | local ngx_req = ngx.req 2 | local req_socket = ngx.req.socket 3 | local ngx_now = ngx.now 4 | local ngx_sleep = ngx.sleep 5 | local _M = { version = '0.0.3' } 6 | 7 | local CHUNK_SIZE = 4 8 | local BUFFER_SIZE = 128 9 | 10 | local mt = { 11 | __index = _M 12 | } 13 | 14 | local function limit_upload_rate(rate, after, buf_size, chunk_size) 15 | if rate then 16 | local sock,err = req_socket() 17 | if ( err == "no body" ) then 18 | ngx.log(ngx.ERR,err) 19 | return 20 | end 21 | local start = ngx_now() 22 | 23 | local rate_in_bytes = rate * 1024 24 | local body_size = 0 - after * 1024 25 | local content_length = tonumber(ngx.req.get_headers()["Content-Length"]) 26 | 27 | ngx_req.init_body(buf_size * 1024) 28 | while true do 29 | local read_size = chunk_size * 1024 30 | if content_length > 0 and content_length < read_size then 31 | read_size = content_length 32 | end 33 | content_length = content_length - read_size 34 | local chunk, err = sock:receive(read_size) 35 | if not chunk then 36 | if err == "closed" then 37 | break 38 | else 39 | ngx.log(ngx.ERR, "fail to read request body, err: " .. err) 40 | return false, "fail to read body" 41 | end 42 | else 43 | ngx_req.append_body(chunk) 44 | body_size = body_size + #chunk 45 | local delay = ngx_now() - start 46 | local expected = body_size / rate_in_bytes 47 | local interval = expected - delay 48 | if interval > 0 then 49 | ngx_sleep(interval) 50 | end 51 | end 52 | end 53 | ngx_req.finish_body() 54 | return true, "" 55 | else 56 | ngx_req.read_body() 57 | return true, "" 58 | end 59 | end 60 | 61 | function _M.new(self, rate, after, buf_size, chunk_size) 62 | return setmetatable({ 63 | rate = rate or 0, 64 | after = after or 0, 65 | buf_size = buf_size or BUFFER_SIZE, 66 | chunk_size = chunk_size or CHUNK_SIZE 67 | }, mt) 68 | end 69 | 70 | function _M.upload(self) 71 | return limit_upload_rate(self.rate, self.after, self.buf_size, self.chunk_size) 72 | end 73 | 74 | 75 | 76 | return _M 77 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | http { 8 | server { 9 | listen 8888; 10 | client_max_body_size 100M; 11 | client_body_buffer_size 10K; 12 | access_by_lua_block { 13 | local upload = require("resty.upload_rate_limit") 14 | local limit, err = upload:new(200, 0, 128, 1) 15 | limit:upload() 16 | } 17 | location / { 18 | content_by_lua_block { 19 | ngx.say("hello world"); 20 | } 21 | } 22 | } 23 | } 24 | --------------------------------------------------------------------------------