├── .gitignore ├── dist.ini ├── t └── post.t ├── README.markdown └── lib └── resty └── post.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *~ 4 | go 5 | t/servroot/ 6 | reindex 7 | *.t_ 8 | tags 9 | .hg 10 | .hgignore 11 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name = lua-resty-post 2 | abstract = Openresty utility for parsing HTTP POST data 3 | author = Anton Heryanto 4 | is_original = yes 5 | license = 2bsd 6 | lib_dir = lib 7 | doc_dir = lib 8 | repo_link = https://github.com/antonheryanto/lua-resty-post 9 | main_module = lib/resty/post.lua 10 | -------------------------------------------------------------------------------- /t/post.t: -------------------------------------------------------------------------------- 1 | use Test::Nginx::Socket::Lua; 2 | use Cwd qw(cwd); 3 | 4 | repeat_each(2); 5 | 6 | plan tests => repeat_each() * (blocks() * 2); 7 | 8 | my $pwd = cwd(); 9 | 10 | our $HttpConfig = <<"_EOC_"; 11 | lua_package_path "$pwd/lib/?.lua;;"; 12 | _EOC_ 13 | 14 | no_long_string(); 15 | no_diff(); 16 | run_tests(); 17 | 18 | __DATA__ 19 | 20 | === TEST 1: simple post 21 | --- http_config eval: $::HttpConfig 22 | --- config 23 | location /t { 24 | content_by_lua_block { 25 | local cjson = require 'cjson' 26 | local post = require 'resty.post':new() 27 | local m = post:read() 28 | ngx.say(cjson.encode(m)) 29 | } 30 | } 31 | --- request 32 | POST /t 33 | a=3&b=4&c 34 | --- response_body 35 | {"b":"4","a":"3","c":true} 36 | 37 | 38 | === TEST 2: array post 39 | --- http_config eval: $::HttpConfig 40 | --- config 41 | location /t { 42 | content_by_lua_block { 43 | local cjson = require 'cjson' 44 | local post = require 'resty.post':new() 45 | local m = post:read() 46 | ngx.say(cjson.encode(m)) 47 | } 48 | } 49 | --- request 50 | POST /t 51 | a=1&a=2&b=1&c=1&c=2 52 | --- response_body 53 | {"b":"1","a":["1","2"],"c":["1","2"]} 54 | 55 | 56 | === TEST 3: json 57 | --- http_config eval: $::HttpConfig 58 | --- config 59 | location /t { 60 | content_by_lua_block { 61 | local cjson = require "cjson" 62 | local post = require "resty.post":new() 63 | local m = post:read() 64 | ngx.say(cjson.encode(m)) 65 | } 66 | } 67 | --- more_headers 68 | Content-Type: application/json 69 | --- request 70 | POST /t 71 | {"a":3,"b":4,"c":true} 72 | --- response_body 73 | {"b":4,"a":3,"c":true} 74 | --- error_log 75 | 76 | 77 | === TEST 4: post with formdata file 78 | --- http_config eval: $::HttpConfig 79 | --- config 80 | location /t { 81 | content_by_lua_block { 82 | local cjson = require 'cjson' 83 | local post = require 'resty.post':new() 84 | local m = post:read() 85 | m.files.file1.tmp_name = nil 86 | ngx.say(cjson.encode(m)) 87 | } 88 | } 89 | --- more_headers 90 | Content-Type: multipart/form-data; boundary=---------------------------820127721219505131303151179 91 | --- request eval 92 | qq{POST /t\n-----------------------------820127721219505131303151179\r 93 | Content-Disposition: form-data; name="file1"; filename="a.txt"\r 94 | Content-Type: text/plain\r 95 | \r 96 | Hello, world\r\n-----------------------------820127721219505131303151179\r 97 | Content-Disposition: form-data; name="test"\r 98 | \r 99 | value\r 100 | \r\n-----------------------------820127721219505131303151179--\r 101 | } 102 | --- response_body 103 | {"files":{"file1":{"type":"text\/plain","size":12,"name":"a.txt"}},"test":"value\r\n"} 104 | 105 | 106 | === TEST 5: post with formdata array input 107 | --- http_config eval: $::HttpConfig 108 | --- config 109 | location /t { 110 | content_by_lua_block { 111 | local cjson = require 'cjson' 112 | local post = require 'resty.post':new() 113 | local m = post:read() 114 | m.files = nil 115 | ngx.say(cjson.encode(m.name)) 116 | ngx.say(cjson.encode(m.user)) 117 | ngx.say(cjson.encode(m.friend)) 118 | } 119 | } 120 | --- more_headers 121 | Content-Type: multipart/form-data; boundary=---------------------------820127721219505131303151179 122 | --- request eval 123 | qq{POST /t\n-----------------------------820127721219505131303151179\r 124 | Content-Disposition: form-data; name="name[1]"\r 125 | \r 126 | Foo\r 127 | -----------------------------820127721219505131303151179\r 128 | Content-Disposition: form-data; name="name[0]"\r 129 | \r 130 | Bar\r 131 | -----------------------------820127721219505131303151179\r 132 | Content-Disposition: form-data; name="user.name"\r 133 | \r 134 | Foo Bar\r 135 | -----------------------------820127721219505131303151179\r 136 | Content-Disposition: form-data; name="user[title]"\r 137 | \r 138 | Mr.\r 139 | -----------------------------820127721219505131303151179\r 140 | Content-Disposition: form-data; name="friend[0].title"\r 141 | \r 142 | Ms.\r 143 | -----------------------------820127721219505131303151179\r 144 | Content-Disposition: form-data; name="friend[0].name"\r 145 | \r 146 | Jane Doo\r 147 | -----------------------------820127721219505131303151179\r 148 | Content-Disposition: form-data; name="friend[1][title]"\r 149 | \r 150 | Mr.\r 151 | -----------------------------820127721219505131303151179\r 152 | Content-Disposition: form-data; name="friend[1][name]"\r 153 | \r 154 | John Doo\r 155 | -----------------------------820127721219505131303151179--\r 156 | } 157 | --- response_body 158 | ["Bar","Foo"] 159 | {"name":"Foo Bar","title":"Mr."} 160 | [{"title":"Ms.","name":"Jane Doo"},{"title":"Mr.","name":"John Doo"}] 161 | 162 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | lua-resty-post 2 | ============== 3 | 4 | Openresty utility for HTTP post 5 | 6 | Table of Contents 7 | ================= 8 | * [Status](#status) 9 | * [Description](#description) 10 | * [Installation](#installation) 11 | * [How to use](#how-to-use) 12 | * [Copyright and License](#copyright-and-license) 13 | * [See Also](#see-also) 14 | 15 | Status 16 | ====== 17 | 18 | This library beta tested and used in production. 19 | 20 | Description 21 | =========== 22 | 23 | This library processed HTTP using [lua-resty-upload](https://github.com/openresty/lua-resty-upload) which very fast and low memory used, it handles multiple type of HTTP POST and converted into lua table: 24 | * application/x-www-form-urlencoded 25 | * application/json 26 | * multipart/form-data 27 | * [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) 28 | * [File Upload](#file-upload) 29 | * [Array Input](#array-input) 30 | 31 | [Back to TOC](#table-of-contents) 32 | 33 | Installation 34 | ============ 35 | 36 | * Download or clone this repo 37 | * copy or link to openresty/lualib/resty/ or to any your lua_package_path 38 | 39 | [Back to TOC](#table-of-contents) 40 | 41 | How to use 42 | ========== 43 | 44 | ```lua 45 | local resty_post = require 'resty.post' 46 | local post = resty_post:new() 47 | local m = post:read() 48 | -- return table with all form value and file 49 | ``` 50 | 51 | [Back to TOC](#table-of-contents) 52 | 53 | File Upload 54 | =========== 55 | 56 | * Support multiple file upload 57 | * Files info are stored in files property using field name as key 58 | ```lua 59 | { 60 | files = { 61 | file1 = { -- input name 62 | name = "a.txt", 63 | type = "text/plain", 64 | size = 10240, 65 | tmp_name = 1454551131.5459 66 | }, 67 | file2 = { 68 | name = "b.png", 69 | type = "image/png", 70 | size = 20480, 71 | tmp_name = 1454553275.6401 72 | } 73 | } 74 | ``` 75 | 76 | * Define path for files upload or default to logs directory (follow ngx.config.prefix) 77 | * Default file will be saved to tmp name (require moving action to destination) 78 | ``` lua 79 | local resty_post = require "resty.post" 80 | local post = resty_post:new({ 81 | path = "/my/path", -- path upload file will be saved 82 | chunk_size = 10240, -- default 8192 83 | no_tmp = true, -- if set original name will uses or generate random name 84 | name = function(name, field) -- overide name with user defined function 85 | return name.."_"..field 86 | end 87 | }) 88 | post:read() 89 | ``` 90 | 91 | 92 | Array Input 93 | =========== 94 | 95 | Support multiple input of similar name 96 | -------------------------------------- 97 | It is useful for thing like HTML input checkboxes or select in multiple mode 98 | ```html 99 | 100 | 101 | 106 | ``` 107 | converted into 108 | ``` lua 109 | { 110 | check_multi = { 1, 2 }, 111 | select_multi = { 1, 2 } 112 | } 113 | ``` 114 | When checked one similar with [ngx.req.get_post_args](https://github.com/openresty/lua-nginx-module#ngxreqget_post_args) 115 | ``` lua 116 | { 117 | check_multi = 2, 118 | select_multi = 1 119 | } 120 | ``` 121 | 122 | 123 | Support array input with name 124 | -------------------------------------- 125 | This is like supporting input which mimic class and property, which can be uses to handle dynamic input 126 | support PHP style (dynamic language) and ASP.NET MVC binding style (static language which uses class) 127 | ```html 128 |