aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorV1K1NGbg <victor@ilchev.com>2024-08-02 23:11:30 +0300
committerV1K1NGbg <victor@ilchev.com>2024-08-05 20:51:31 +0300
commit9a5c9fb314c8ec4d8876a424d12c17879d58db5f (patch)
tree8bdd8f0103cdbd0c9c50b67a40d801e77932e8bc
parent02aa936b397951eaa5e3eba9a22f5e05e404fb6b (diff)
downloadluarocks-9a5c9fb314c8ec4d8876a424d12c17879d58db5f.tar.gz
luarocks-9a5c9fb314c8ec4d8876a424d12c17879d58db5f.tar.bz2
luarocks-9a5c9fb314c8ec4d8876a424d12c17879d58db5f.zip
multipart
-rw-r--r--src/luarocks/upload/multipart-original.lua109
-rw-r--r--src/luarocks/upload/multipart.lua70
-rw-r--r--src/luarocks/upload/multipart.tl30
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
2local multipart = {}
3
4local File = {}
5
6local unpack = unpack or table.unpack
7
8-- socket.url.escape(s) from LuaSocket 3.0rc1
9function 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))
13end
14
15function 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
24end
25
26function 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
34end
35
36local 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))
46end
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-- }
56function 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
97end
98
99function 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
106end
107
108return 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 @@
1local _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
2local multipart = {File = {}, }
1 3
2local multipart = {}
3 4
4local File = {}
5 5
6local 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
17local File = multipart.File
18
19
9function multipart.url_escape(s) 20function 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))
13end 24end
14 25
15function File:mime() 26function 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
24end 35end
25 36
26function File:content() 37function 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))
46end 57end
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
56function multipart.encode(params) 67function 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), 103table.concat(chunks, inner),
96 "\r\n", "--", boundary, "--", "\r\n" }), boundary 104"\r\n", "--", boundary, "--", "\r\n", }), boundary
97end 105end
98 106
99function multipart.new_file(fname, mime) 107function 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
106end 115end
107 116
108return multipart 117return 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
2local record multipart 2local 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
7end 14end
8 15
9local type Parameters = multipart.Parameters 16local type Parameters = multipart.Parameters
10 17local type File = multipart.File
11local record File
12 mimetype: string
13 fname: string
14 mime: function(File): string
15end
16 18
17-- socket.url.escape(s) from LuaSocket 3.0rc1 --? 19-- socket.url.escape(s) from LuaSocket 3.0rc1 --?
18function multipart.url_escape(s: string): string 20function multipart.url_escape(s: string): string
@@ -21,7 +23,7 @@ function multipart.url_escape(s: string): string
21 end)) 23 end))
22end 24end
23 25
24function File.mime(self: File): string 26function 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
33end 35end
34 36
35function File:content(): string, string 37function 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-- }
65function multipart.encode(params: Parameters): string, string --! Table map type 67function 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