diff options
author | V1K1NGbg <victor@ilchev.com> | 2024-08-02 23:11:30 +0300 |
---|---|---|
committer | V1K1NGbg <victor@ilchev.com> | 2024-08-05 20:51:31 +0300 |
commit | 9a5c9fb314c8ec4d8876a424d12c17879d58db5f (patch) | |
tree | 8bdd8f0103cdbd0c9c50b67a40d801e77932e8bc | |
parent | 02aa936b397951eaa5e3eba9a22f5e05e404fb6b (diff) | |
download | luarocks-9a5c9fb314c8ec4d8876a424d12c17879d58db5f.tar.gz luarocks-9a5c9fb314c8ec4d8876a424d12c17879d58db5f.tar.bz2 luarocks-9a5c9fb314c8ec4d8876a424d12c17879d58db5f.zip |
multipart
-rw-r--r-- | src/luarocks/upload/multipart-original.lua | 109 | ||||
-rw-r--r-- | src/luarocks/upload/multipart.lua | 70 | ||||
-rw-r--r-- | src/luarocks/upload/multipart.tl | 30 |
3 files changed, 162 insertions, 47 deletions
diff --git a/src/luarocks/upload/multipart-original.lua b/src/luarocks/upload/multipart-original.lua new file mode 100644 index 00000000..790e368f --- /dev/null +++ b/src/luarocks/upload/multipart-original.lua | |||
@@ -0,0 +1,109 @@ | |||
1 | |||
2 | local multipart = {} | ||
3 | |||
4 | local File = {} | ||
5 | |||
6 | local unpack = unpack or table.unpack | ||
7 | |||
8 | -- socket.url.escape(s) from LuaSocket 3.0rc1 | ||
9 | function multipart.url_escape(s) | ||
10 | return (string.gsub(s, "([^A-Za-z0-9_])", function(c) | ||
11 | return string.format("%%%02x", string.byte(c)) | ||
12 | end)) | ||
13 | end | ||
14 | |||
15 | function File:mime() | ||
16 | if not self.mimetype then | ||
17 | local mimetypes_ok, mimetypes = pcall(require, "mimetypes") | ||
18 | if mimetypes_ok then | ||
19 | self.mimetype = mimetypes.guess(self.fname) | ||
20 | end | ||
21 | self.mimetype = self.mimetype or "application/octet-stream" | ||
22 | end | ||
23 | return self.mimetype | ||
24 | end | ||
25 | |||
26 | function File:content() | ||
27 | local fd = io.open(self.fname, "rb") | ||
28 | if not fd then | ||
29 | return nil, "Failed to open file: "..self.fname | ||
30 | end | ||
31 | local data = fd:read("*a") | ||
32 | fd:close() | ||
33 | return data | ||
34 | end | ||
35 | |||
36 | local function rand_string(len) | ||
37 | local shuffled = {} | ||
38 | for i = 1, len do | ||
39 | local r = math.random(97, 122) | ||
40 | if math.random() >= 0.5 then | ||
41 | r = r - 32 | ||
42 | end | ||
43 | shuffled[i] = r | ||
44 | end | ||
45 | return string.char(unpack(shuffled)) | ||
46 | end | ||
47 | |||
48 | -- multipart encodes params | ||
49 | -- returns encoded string,boundary | ||
50 | -- params is an a table of tuple tables: | ||
51 | -- params = { | ||
52 | -- {key1, value2}, | ||
53 | -- {key2, value2}, | ||
54 | -- key3: value3 | ||
55 | -- } | ||
56 | function multipart.encode(params) | ||
57 | local tuples = { } | ||
58 | for i = 1, #params do | ||
59 | tuples[i] = params[i] | ||
60 | end | ||
61 | for k,v in pairs(params) do | ||
62 | if type(k) == "string" then | ||
63 | table.insert(tuples, {k, v}) | ||
64 | end | ||
65 | end | ||
66 | local chunks = {} | ||
67 | for _, tuple in ipairs(tuples) do | ||
68 | local k,v = unpack(tuple) | ||
69 | k = multipart.url_escape(k) | ||
70 | local buffer = { 'Content-Disposition: form-data; name="' .. k .. '"' } | ||
71 | local content | ||
72 | if type(v) == "table" and v.__class == File then | ||
73 | buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*[/\\]", "") .. '"') | ||
74 | table.insert(buffer, "Content-type: " .. v:mime()) | ||
75 | content = v:content() | ||
76 | else | ||
77 | content = v | ||
78 | end | ||
79 | table.insert(buffer, "") | ||
80 | table.insert(buffer, content) | ||
81 | table.insert(chunks, table.concat(buffer, "\r\n")) | ||
82 | end | ||
83 | local boundary | ||
84 | while not boundary do | ||
85 | boundary = "Boundary" .. rand_string(16) | ||
86 | for _, chunk in ipairs(chunks) do | ||
87 | if chunk:find(boundary) then | ||
88 | boundary = nil | ||
89 | break | ||
90 | end | ||
91 | end | ||
92 | end | ||
93 | local inner = "\r\n--" .. boundary .. "\r\n" | ||
94 | return table.concat({ "--", boundary, "\r\n", | ||
95 | table.concat(chunks, inner), | ||
96 | "\r\n", "--", boundary, "--", "\r\n" }), boundary | ||
97 | end | ||
98 | |||
99 | function multipart.new_file(fname, mime) | ||
100 | local self = {} | ||
101 | setmetatable(self, { __index = File }) | ||
102 | self.__class = File | ||
103 | self.fname = fname | ||
104 | self.mimetype = mime | ||
105 | return self | ||
106 | end | ||
107 | |||
108 | return multipart | ||
109 | |||
diff --git a/src/luarocks/upload/multipart.lua b/src/luarocks/upload/multipart.lua index 790e368f..cf68bdef 100644 --- a/src/luarocks/upload/multipart.lua +++ b/src/luarocks/upload/multipart.lua | |||
@@ -1,18 +1,29 @@ | |||
1 | local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local math = _tl_compat and _tl_compat.math or math; local pairs = _tl_compat and _tl_compat.pairs or pairs; local pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table; local _tl_table_unpack = unpack or table.unpack | ||
2 | local multipart = {File = {}, } | ||
1 | 3 | ||
2 | local multipart = {} | ||
3 | 4 | ||
4 | local File = {} | ||
5 | 5 | ||
6 | local unpack = unpack or table.unpack | ||
7 | 6 | ||
8 | -- socket.url.escape(s) from LuaSocket 3.0rc1 | 7 | |
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
15 | |||
16 | |||
17 | local File = multipart.File | ||
18 | |||
19 | |||
9 | function multipart.url_escape(s) | 20 | function multipart.url_escape(s) |
10 | return (string.gsub(s, "([^A-Za-z0-9_])", function(c) | 21 | return (string.gsub(s, "([^A-Za-z0-9_])", function(c) |
11 | return string.format("%%%02x", string.byte(c)) | 22 | return string.format("%%%02x", string.byte(c)) |
12 | end)) | 23 | end)) |
13 | end | 24 | end |
14 | 25 | ||
15 | function File:mime() | 26 | function multipart.File:mime() |
16 | if not self.mimetype then | 27 | if not self.mimetype then |
17 | local mimetypes_ok, mimetypes = pcall(require, "mimetypes") | 28 | local mimetypes_ok, mimetypes = pcall(require, "mimetypes") |
18 | if mimetypes_ok then | 29 | if mimetypes_ok then |
@@ -23,10 +34,10 @@ function File:mime() | |||
23 | return self.mimetype | 34 | return self.mimetype |
24 | end | 35 | end |
25 | 36 | ||
26 | function File:content() | 37 | function multipart.File:content() |
27 | local fd = io.open(self.fname, "rb") | 38 | local fd = io.open(self.fname, "rb") |
28 | if not fd then | 39 | if not fd then |
29 | return nil, "Failed to open file: "..self.fname | 40 | return nil, "Failed to open file: " .. self.fname |
30 | end | 41 | end |
31 | local data = fd:read("*a") | 42 | local data = fd:read("*a") |
32 | fd:close() | 43 | fd:close() |
@@ -38,38 +49,35 @@ local function rand_string(len) | |||
38 | for i = 1, len do | 49 | for i = 1, len do |
39 | local r = math.random(97, 122) | 50 | local r = math.random(97, 122) |
40 | if math.random() >= 0.5 then | 51 | if math.random() >= 0.5 then |
41 | r = r - 32 | 52 | r = r - 32 |
42 | end | 53 | end |
43 | shuffled[i] = r | 54 | shuffled[i] = r |
44 | end | 55 | end |
45 | return string.char(unpack(shuffled)) | 56 | return string.char(_tl_table_unpack(shuffled)) |
46 | end | 57 | end |
47 | 58 | ||
48 | -- multipart encodes params | 59 | |
49 | -- returns encoded string,boundary | 60 | |
50 | -- params is an a table of tuple tables: | 61 | |
51 | -- params = { | 62 | |
52 | -- {key1, value2}, | 63 | |
53 | -- {key2, value2}, | 64 | |
54 | -- key3: value3 | 65 | |
55 | -- } | 66 | |
56 | function multipart.encode(params) | 67 | function multipart.encode(params) |
57 | local tuples = { } | 68 | local tuples = {} |
58 | for i = 1, #params do | 69 | for k, v in pairs(params) do |
59 | tuples[i] = params[i] | ||
60 | end | ||
61 | for k,v in pairs(params) do | ||
62 | if type(k) == "string" then | 70 | if type(k) == "string" then |
63 | table.insert(tuples, {k, v}) | 71 | table.insert(tuples, { k, v }) |
64 | end | 72 | end |
65 | end | 73 | end |
66 | local chunks = {} | 74 | local chunks = {} |
67 | for _, tuple in ipairs(tuples) do | 75 | for _, tuple in ipairs(tuples) do |
68 | local k,v = unpack(tuple) | 76 | local k, v = _tl_table_unpack(tuple) |
69 | k = multipart.url_escape(k) | 77 | k = multipart.url_escape(k) |
70 | local buffer = { 'Content-Disposition: form-data; name="' .. k .. '"' } | 78 | local buffer = { 'Content-Disposition: form-data; name="' .. k .. '"' } |
71 | local content | 79 | local content |
72 | if type(v) == "table" and v.__class == File then | 80 | if type(v) == "table" then |
73 | buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*[/\\]", "") .. '"') | 81 | buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*[/\\]", "") .. '"') |
74 | table.insert(buffer, "Content-type: " .. v:mime()) | 82 | table.insert(buffer, "Content-type: " .. v:mime()) |
75 | content = v:content() | 83 | content = v:content() |
@@ -90,20 +98,20 @@ function multipart.encode(params) | |||
90 | end | 98 | end |
91 | end | 99 | end |
92 | end | 100 | end |
93 | local inner = "\r\n--" .. boundary .. "\r\n" | 101 | local inner = "\r\n--" .. boundary .. "\r\n" |
94 | return table.concat({ "--", boundary, "\r\n", | 102 | return table.concat({ "--", boundary, "\r\n", |
95 | table.concat(chunks, inner), | 103 | table.concat(chunks, inner), |
96 | "\r\n", "--", boundary, "--", "\r\n" }), boundary | 104 | "\r\n", "--", boundary, "--", "\r\n", }), boundary |
97 | end | 105 | end |
98 | 106 | ||
99 | function multipart.new_file(fname, mime) | 107 | function multipart.new_file(fname, mime) |
100 | local self = {} | 108 | local self = {} |
109 | |||
101 | setmetatable(self, { __index = File }) | 110 | setmetatable(self, { __index = File }) |
102 | self.__class = File | 111 | |
103 | self.fname = fname | 112 | self.fname = fname |
104 | self.mimetype = mime | 113 | self.mimetype = mime |
105 | return self | 114 | return self |
106 | end | 115 | end |
107 | 116 | ||
108 | return multipart | 117 | return multipart |
109 | |||
diff --git a/src/luarocks/upload/multipart.tl b/src/luarocks/upload/multipart.tl index 5bcbc502..28787da2 100644 --- a/src/luarocks/upload/multipart.tl +++ b/src/luarocks/upload/multipart.tl | |||
@@ -1,18 +1,20 @@ | |||
1 | 1 | ||
2 | local record multipart | 2 | local record multipart |
3 | record Parameters | 3 | -- record Parameters |
4 | {{string, (string | File)}} | 4 | -- {{string, (string | File)}} |
5 | map: {string: (string | File)} | 5 | -- map: {string: (string | File)} |
6 | -- end | ||
7 | type Parameters = {string: (string | File)} | ||
8 | |||
9 | record File | ||
10 | mimetype: string | ||
11 | fname: string | ||
12 | mime: function(File): string | ||
6 | end | 13 | end |
7 | end | 14 | end |
8 | 15 | ||
9 | local type Parameters = multipart.Parameters | 16 | local type Parameters = multipart.Parameters |
10 | 17 | local type File = multipart.File | |
11 | local record File | ||
12 | mimetype: string | ||
13 | fname: string | ||
14 | mime: function(File): string | ||
15 | end | ||
16 | 18 | ||
17 | -- socket.url.escape(s) from LuaSocket 3.0rc1 --? | 19 | -- socket.url.escape(s) from LuaSocket 3.0rc1 --? |
18 | function multipart.url_escape(s: string): string | 20 | function multipart.url_escape(s: string): string |
@@ -21,7 +23,7 @@ function multipart.url_escape(s: string): string | |||
21 | end)) | 23 | end)) |
22 | end | 24 | end |
23 | 25 | ||
24 | function File.mime(self: File): string | 26 | function multipart.File:mime(): string |
25 | if not self.mimetype then | 27 | if not self.mimetype then |
26 | local mimetypes_ok, mimetypes = pcall(require, "mimetypes") | 28 | local mimetypes_ok, mimetypes = pcall(require, "mimetypes") |
27 | if mimetypes_ok then | 29 | if mimetypes_ok then |
@@ -32,7 +34,7 @@ function File.mime(self: File): string | |||
32 | return self.mimetype | 34 | return self.mimetype |
33 | end | 35 | end |
34 | 36 | ||
35 | function File:content(): string, string | 37 | function multipart.File:content(): string, string |
36 | local fd = io.open(self.fname, "rb") | 38 | local fd = io.open(self.fname, "rb") |
37 | if not fd then | 39 | if not fd then |
38 | return nil, "Failed to open file: "..self.fname | 40 | return nil, "Failed to open file: "..self.fname |
@@ -64,10 +66,7 @@ end | |||
64 | -- } | 66 | -- } |
65 | function multipart.encode(params: Parameters): string, string --! Table map type | 67 | function multipart.encode(params: Parameters): string, string --! Table map type |
66 | local tuples: {{string, string | File}} = {} --! type | 68 | local tuples: {{string, string | File}} = {} --! type |
67 | for i = 1, #params do | 69 | for k,v in pairs(params) do |
68 | tuples[i] = params[i] | ||
69 | end | ||
70 | for k,v in pairs(params.map) do | ||
71 | if k is string then | 70 | if k is string then |
72 | table.insert(tuples, {k, v}) | 71 | table.insert(tuples, {k, v}) |
73 | end | 72 | end |
@@ -109,7 +108,6 @@ function multipart.new_file(fname: string, mime: string): File | |||
109 | local self: File = {} | 108 | local self: File = {} |
110 | 109 | ||
111 | setmetatable(self, { __index = File }) | 110 | setmetatable(self, { __index = File }) |
112 | self.__class = File --! no such field in teal | ||
113 | 111 | ||
114 | self.fname = fname | 112 | self.fname = fname |
115 | self.mimetype = mime | 113 | self.mimetype = mime |