Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
541017d
support set multipart/form file
zhucebuliaopx Mar 22, 2020
0ee96a5
fix add content-length
zhucebuliaopx Mar 22, 2020
513cb46
update README
zhucebuliaopx Mar 22, 2020
1588a79
Merge tag '0.0.1' into develop
zhucebuliaopx Mar 22, 2020
3e7fe23
Merge branch 'release/0.0.1'
zhucebuliaopx Mar 22, 2020
8633569
local var
zhucebuliaopx Mar 22, 2020
51bd31a
fix code style
zhucebuliaopx Mar 23, 2020
fedcf9e
Merge tag '0.0.2' into develop
zhucebuliaopx Mar 23, 2020
a84db7e
Merge branch 'release/0.0.2'
zhucebuliaopx Mar 23, 2020
8a23a08
remove git address,once upstream merage my pr,so could install lua-mu…
zhucebuliaopx Mar 23, 2020
b38e3fd
Merge tag '0.0.3' into develop
zhucebuliaopx Mar 23, 2020
4a19a63
Merge branch 'release/0.0.3'
zhucebuliaopx Mar 23, 2020
3e46a90
add test example and fix code style
zhucebuliaopx Mar 24, 2020
b57d2ff
Merge branch 'release/0.0.4'
zhucebuliaopx Mar 24, 2020
51bf1e4
Merge tag '0.0.4' into develop
zhucebuliaopx Mar 24, 2020
117b669
del unnecessary local var
zhucebuliaopx Mar 24, 2020
3a47469
fix code style
zhucebuliaopx Apr 8, 2020
4c445cf
update multipart code
zhucebuliaopx May 1, 2020
158f7a3
fix code style
zhucebuliaopx May 1, 2020
0a8ebf4
fix code style && update test case
zhucebuliaopx May 2, 2020
e104658
Merge branch 'feature/0.0.2' into develop
zhucebuliaopx May 2, 2020
1cc478d
remove dependencies by Kong\lua-multipart && update readme
zhucebuliaopx May 2, 2020
aae1b4f
fix files body with userdata fp
zhucebuliaopx May 2, 2020
868162e
fix iter_base_func is_array
zhucebuliaopx May 2, 2020
2a9c604
local var error
zhucebuliaopx May 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@ luac.out
*.x86_64
*.hex

#develop env
.idea

t/servroot/*
1 change: 1 addition & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ The third param, an optional Lua table, which contains a number of options:
* a Lua string, or
* a Lua function, without parameter and returns a piece of data (string) or an empty Lua string to represent EOF, or
* a Lua table, each key-value pair will be concatenated with the "&", and Content-Type header will `"application/x-www-form-urlencoded"`
* `files`, multipart/form upload file body, should be table contains more tables, like that: {{"name", file_body, "file_name", "file_type"},...}

* `error_filter`, holds a Lua function which takes two parameters, `state` and `err`.
the parameter `err` describes the error and `state` is always one of these values(represents the current stage):
Expand Down
330 changes: 330 additions & 0 deletions lib/resty/requests/multipart.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
--https://github.com/pytpeng/lua-multipart
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just manage this dependency by LuaRocks/OPM rather than copy it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upstream code not merge my pull request yet,so left the problem,Kong/lua-multipart#27

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should suspend this MR until your MR merged by Kong.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx,this is better

--fork from https://github.com/Kong/lua-multipart

local setmetatable = setmetatable
local tostring = tostring
local insert = table.insert
local remove = table.remove
local concat = table.concat
local ipairs = ipairs
local pairs = pairs
local match = string.match
local find = string.find
local sub = string.sub


local RANDOM_BOUNDARY = sub(tostring({}), 10)


local MultipartData = { RANDOM_BOUNDARY = RANDOM_BOUNDARY}


MultipartData.__index = MultipartData


setmetatable(MultipartData, {
__call = function (cls, ...)
return cls.new(...)
end,
})


local function is_header(value)
return match(value, "(%S+):%s*(%S+)")
end


local function table_size(t)
local res = 0

if t then
for _,_ in pairs(t) do
res = res + 1
end
end

return res
end

-- Create a table representation of multipart/data body
--
-- @param {string} body The multipart/data string body
-- @param {string} boundary The multipart/data boundary
-- @return {table} Lua representation of the body
local function decode(body, boundary)
local result = {
data = {},
indexes = {},
}

if not boundary then
return result
end

local part_name
local part_index = 1
local part_headers = {}
local part_value = {}
local part_value_ct = 0

local end_boundary_length = boundary and #boundary + 2
local processing_part_value = false

local position = 1
local done = false

repeat
local s = find(body, "[\r\n]", position)

local line

if s then
line = sub(body, position, s - 1)
position = s + 1

else
if position == 1 then
line = body

else
line = sub(body, position)
end

done = true
end

if line == "" then
if s and processing_part_value then
part_value_ct = part_value_ct + 1
part_value[part_value_ct] = sub(body, s, s)
end

else
if sub(line, 1, 2) == "--" and sub(line, 3, end_boundary_length) == boundary then
processing_part_value = false

if part_name ~= nil then
if part_value[part_value_ct] == "\n" then
part_value[part_value_ct] = nil
end

if part_value[part_value_ct - 1] == "\r" then
part_value[part_value_ct - 1] = nil
end

result.data[part_index] = {
name = part_name,
headers = part_headers,
value = concat(part_value)
}

result.indexes[part_name] = part_index

-- Reset fields for the next part
part_headers = {}
part_value = {}
part_value_ct = 0
part_name = nil
part_index = part_index + 1
end

else
--Beginning of part
if not processing_part_value and line:sub(1, 19):lower() == "content-disposition" then
-- Extract part_name
for v in line:gmatch("[^;]+") do
if not is_header(v) then -- If it's not content disposition part
local pos = v:match("^%s*[Nn][Aa][Mm][Ee]=()")
if pos then
local current_value = v:match("^%s*([^=]*)", pos):gsub("%s*$", "")
part_name = sub(current_value, 2, #current_value - 1)
end
end
end

insert(part_headers, line)

if s and sub(body, s, s + 3) == "\r\n\r\n" then
processing_part_value = true
position = s + 4
end

elseif not processing_part_value and is_header(line) then
insert(part_headers, line)

if s and sub(body, s, s + 3) == "\r\n\r\n" then
processing_part_value = true
position = s + 4
end

else
processing_part_value = true

-- The value part begins
part_value_ct = part_value_ct + 1
part_value[part_value_ct] = line

if s then
part_value_ct = part_value_ct + 1
part_value[part_value_ct] = sub(body, s, s)
end
end
end
end

until done

if part_name ~= nil then
result.data[part_index] = {
name = part_name,
headers = part_headers,
value = concat(part_value)
}

result.indexes[part_name] = part_index
end

return result
end

-- Creates a multipart/data body from a table
--
-- @param {table} t The table that contains the multipart/data body properties
-- @param {boundary} boundary The multipart/data boundary to use
-- @return {string} The multipart/data string body
local function encode(t, boundary)
if not boundary then
boundary = RANDOM_BOUNDARY
end

local result = {}
local i = 0

for _, v in ipairs(t.data) do
if v.value then
result[i + 1] = "--"
result[i + 2] = boundary
result[i + 3] = "\r\n"

i = i + 3

for _, header in ipairs(v.headers) do
result[i + 1] = header
result[i + 2] = "\r\n"

i = i + 2
end

result[i + 1] = "\r\n"
result[i + 2] = v.value
result[i + 3] = "\r\n"

i = i + 3
end
end

if i == 0 then
return ""
end

result[i + 1] = "--"
result[i + 2] = boundary
result[i + 3] = "--\r\n"

return concat(result)
end


function MultipartData.new(data, content_type)
local instance = setmetatable({}, MultipartData)

if content_type then
local boundary = match(content_type, ";%s*boundary=(%S+)")
if boundary then
if (sub(boundary, 1, 1) == '"' and sub(boundary, -1) == '"') or
(sub(boundary, 1, 1) == "'" and sub(boundary, -1) == "'") then
boundary = sub(boundary, 2, -2)
end

if boundary ~= "" then
instance._boundary = boundary
end
end
end

instance._data = decode(data or "", instance._boundary)

return instance
end


function MultipartData:get(name)
return self._data.data[self._data.indexes[name]]
end


function MultipartData:get_all()
local result = {}

for k, v in pairs(self._data.indexes) do
result[k] = self._data.data[v].value
end

return result
end


function MultipartData:set_simple(name, value, filename, content_type)
local headers = {'Content-Disposition: form-data; name="' , name , '"'}
if filename then
headers[4] = '; filename="'
headers[5] = filename
headers[6] = '"'
end
if content_type then
headers[7] = "\r\ncontent-type: "
headers[8] = content_type
end
headers = concat(headers)
if self._data.indexes[name] then
self._data.data[self._data.indexes[name]] = {
name = name,
value = value,
headers = {headers}
}

else
local part_index = table_size(self._data.indexes) + 1
self._data.indexes[name] = part_index
self._data.data[part_index] = {
name = name,
value = value,
headers = {headers}
}
end
end


function MultipartData:delete(name)
local index = self._data.indexes[name]

if index then
remove(self._data.data, index)
self._data.indexes[name] = nil

-- need to recount index
for key, value in pairs(self._data.indexes) do
if value > index then
self._data.indexes[key] = value - 1
end
end
end
end


function MultipartData:tostring()
return encode(self._data, self._boundary)
end


return MultipartData
10 changes: 10 additions & 0 deletions lib/resty/requests/request.lua
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,21 @@ local function prepare(url_parts, session, config)
local content
local json = config.json
local body = config.body
local files = config.files

if json then
content = cjson.encode(json)
headers["content-length"] = #content
headers["content-type"] = "application/json"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to leave an empty line here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

elseif files and is_tab(files) then
local content_type = headers["content-type"]
if not content_type then
content_type = "multipart/form-data; boundary="..util.choose_boundary()
end
local multipart_body= util.make_multipart_body(files, content_type)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: a space character should be kept before =.

headers["content-type"] = content_type
headers["content-length"] = #multipart_body
content = multipart_body
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto, leave an empty line here.

else
content = body
if is_func(body) then
Expand Down
Loading