diff options
author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-06-18 08:02:09 +0000 |
---|---|---|
committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-06-18 08:02:09 +0000 |
commit | ac4aac0909da26befaaeb6b415f66cf35b6980e0 (patch) | |
tree | 3d3289e6192508484dcbefa10e2d862c5cc06d64 | |
parent | 62799a416d2b29d8058331f3d8725fb67c75d261 (diff) | |
download | luasocket-ac4aac0909da26befaaeb6b415f66cf35b6980e0.tar.gz luasocket-ac4aac0909da26befaaeb6b415f66cf35b6980e0.tar.bz2 luasocket-ac4aac0909da26befaaeb6b415f66cf35b6980e0.zip |
Implemented safe exceptions. This looks preeety good.
-rw-r--r-- | TODO | 6 | ||||
-rw-r--r-- | doc/reference.html | 3 | ||||
-rw-r--r-- | doc/socket.html | 66 | ||||
-rw-r--r-- | doc/tcp.html | 28 | ||||
-rw-r--r-- | src/except.c | 32 | ||||
-rw-r--r-- | src/ftp.lua | 182 | ||||
-rw-r--r-- | src/http.lua | 55 | ||||
-rw-r--r-- | src/smtp.lua | 64 | ||||
-rw-r--r-- | src/socket.lua | 4 | ||||
-rw-r--r-- | test/ftptest.lua | 10 | ||||
-rw-r--r-- | test/httptest.lua | 1 |
11 files changed, 273 insertions, 178 deletions
@@ -1,4 +1,5 @@ | |||
1 | 1 | ||
2 | |||
2 | ajeitar os README.* | 3 | ajeitar os README.* |
3 | ajeitar as referencias a RFCS e LTNS em todos os arquivos. | 4 | ajeitar as referencias a RFCS e LTNS em todos os arquivos. |
4 | 5 | ||
@@ -8,8 +9,9 @@ check garbage collection in test*.lua | |||
8 | 9 | ||
9 | 10 | ||
10 | manual | 11 | manual |
11 | socket.skip | 12 | socket.newtry |
12 | send return convention changed. | 13 | *socket.skip |
14 | *send return convention changed. | ||
13 | * compatibility: select sets are associative | 15 | * compatibility: select sets are associative |
14 | * add socket.connect and socket.bind to the manual | 16 | * add socket.connect and socket.bind to the manual |
15 | * add shutdown | 17 | * add shutdown |
diff --git a/doc/reference.html b/doc/reference.html index 607958b..f130d7b 100644 --- a/doc/reference.html +++ b/doc/reference.html | |||
@@ -143,8 +143,9 @@ | |||
143 | <a href="socket.html#protect">protect</a>, | 143 | <a href="socket.html#protect">protect</a>, |
144 | <a href="socket.html#select">select</a>, | 144 | <a href="socket.html#select">select</a>, |
145 | <a href="socket.html#sink">sink</a>, | 145 | <a href="socket.html#sink">sink</a>, |
146 | <a href="socket.html#source">source</a>, | 146 | <a href="socket.html#skip">skip</a>, |
147 | <a href="socket.html#sleep">sleep</a>, | 147 | <a href="socket.html#sleep">sleep</a>, |
148 | <a href="socket.html#source">source</a>, | ||
148 | <a href="socket.html#time">time</a>, | 149 | <a href="socket.html#time">time</a>, |
149 | <a href="tcp.html#tcp">tcp</a>, | 150 | <a href="tcp.html#tcp">tcp</a>, |
150 | <a href="socket.html#try">try</a>, | 151 | <a href="socket.html#try">try</a>, |
diff --git a/doc/socket.html b/doc/socket.html index d7739f8..06296a3 100644 --- a/doc/socket.html +++ b/doc/socket.html | |||
@@ -169,6 +169,49 @@ socket, leaving it open when done. | |||
169 | The function returns a sink with the appropriate behavior. | 169 | The function returns a sink with the appropriate behavior. |
170 | </p> | 170 | </p> |
171 | 171 | ||
172 | <!-- skip ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
173 | |||
174 | <p class=name id=skip> | ||
175 | socket.<b>skip(</b>d [, ret<sub>1</sub>, ret<sub>2</sub> ... ret<sub>N</sub>]<b>)</b> | ||
176 | </p> | ||
177 | |||
178 | <p class=description> | ||
179 | Drops a number of arguments and returns the remaining. | ||
180 | </p> | ||
181 | |||
182 | <p class=parameters> | ||
183 | <tt>D</tt> is the number of arguments to drop. <tt>Ret<sub>1</sub></tt> to | ||
184 | <tt>ret<sub>N</sub></tt> are the arguments. | ||
185 | </p> | ||
186 | |||
187 | <p class=return> | ||
188 | The function returns <tt>ret<sub>d+1</sub></tt> to <tt>ret<sub>N</sub></tt>. | ||
189 | </p> | ||
190 | |||
191 | <p class=note> | ||
192 | Note: This function is useful to avoid creation of dummy variables: | ||
193 | </p> | ||
194 | |||
195 | <pre class=example> | ||
196 | -- get the status code and separator from SMTP server reply | ||
197 | local code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) | ||
198 | </pre> | ||
199 | |||
200 | <!-- sleep ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
201 | |||
202 | <p class=name id=sleep> | ||
203 | socket.<b>sleep(</b>time<b>)</b> | ||
204 | </p> | ||
205 | |||
206 | <p class=description> | ||
207 | Freezes the program execution during a given amount of time. | ||
208 | </p> | ||
209 | |||
210 | <p class=parameters> | ||
211 | <tt>Time</tt> is the number of seconds to sleep for. | ||
212 | The function truncates <tt>time</tt> to the nearest integer. | ||
213 | </p> | ||
214 | |||
172 | <!-- source +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | 215 | <!-- source +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> |
173 | 216 | ||
174 | <p class=name id=source> | 217 | <p class=name id=source> |
@@ -201,6 +244,27 @@ side closes the connection. | |||
201 | The function returns a source with the appropriate behavior. | 244 | The function returns a source with the appropriate behavior. |
202 | </p> | 245 | </p> |
203 | 246 | ||
247 | <!-- time ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | ||
248 | |||
249 | <p class=name id=time> | ||
250 | socket.<b>time()</b> | ||
251 | </p> | ||
252 | |||
253 | <p class=description> | ||
254 | Returns the time in seconds, relative to the origin of the | ||
255 | universe. Only time differences are meaninful. | ||
256 | </p> | ||
257 | |||
258 | <p class=return> | ||
259 | The function returns the time as a number. | ||
260 | </p> | ||
261 | |||
262 | <pre class=example> | ||
263 | t = socket.time() | ||
264 | -- do stuff | ||
265 | print(socket.time() - t .. " seconds elapsed") | ||
266 | </pre> | ||
267 | |||
204 | <!-- try ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | 268 | <!-- try ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> |
205 | 269 | ||
206 | <p class=name id=try> | 270 | <p class=name id=try> |
@@ -212,7 +276,7 @@ Throws an exception in case of error. | |||
212 | </p> | 276 | </p> |
213 | 277 | ||
214 | <p class=parameters> | 278 | <p class=parameters> |
215 | <tt>Ret</tt><sub>1</sub> to <tt>ret</tt><sub>N</sub> can be arbitrary | 279 | <tt>Ret<sub>1</sub></tt> to <tt>ret<sub>N</sub></tt> can be arbitrary |
216 | arguments, but are usually the return values of a function call | 280 | arguments, but are usually the return values of a function call |
217 | nested with <tt>try</tt>. | 281 | nested with <tt>try</tt>. |
218 | </p> | 282 | </p> |
diff --git a/doc/tcp.html b/doc/tcp.html index 781ec6f..cd417a5 100644 --- a/doc/tcp.html +++ b/doc/tcp.html | |||
@@ -277,9 +277,10 @@ the transmission. | |||
277 | <p class=note> | 277 | <p class=note> |
278 | <b>Important note</b>: This function was changed <em>severely</em>. It used | 278 | <b>Important note</b>: This function was changed <em>severely</em>. It used |
279 | to support multiple patterns (but I have never seen this feature used) and | 279 | to support multiple patterns (but I have never seen this feature used) and |
280 | partial results used to be returned in the same way as successful results. | 280 | now it doesn't anymore. Partial results used to be returned in the same |
281 | This last feature violated the idea that all functions should return | 281 | way as successful results. This last feature violated the idea that all |
282 | <tt><b>nil</b></tt> on error. Thus the change. | 282 | functions should return <tt><b>nil</b></tt> on error. Thus it was changed |
283 | too. | ||
283 | </p> | 284 | </p> |
284 | 285 | ||
285 | <!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | 286 | <!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> |
@@ -300,20 +301,25 @@ result to LuaSocket instead of passing several independent strings. | |||
300 | </p> | 301 | </p> |
301 | 302 | ||
302 | <p class=return> | 303 | <p class=return> |
303 | The method returns the number of bytes accepted by the transport layer, | 304 | If successful, the method returns the number of bytes accepted by |
304 | followed by an error code. The error code is <b><tt>nil</tt></b> if the operation | 305 | the transport layer. In case of error, the method returns |
305 | completed with no errors, the string '<tt>closed</tt>' in case | 306 | <b><tt>nil</tt></b>, followed by an error message, followed by the |
307 | partial number of bytes accepted by the transport layer. | ||
308 | The error message can be '<tt>closed</tt>' in case | ||
306 | the connection was closed before the transmission was completed or the | 309 | the connection was closed before the transmission was completed or the |
307 | string '<tt>timeout</tt>' in case there was a timeout during the | 310 | string '<tt>timeout</tt>' in case there was a timeout during the |
308 | operation. | 311 | operation. |
309 | </p> | 312 | </p> |
310 | 313 | ||
311 | <p class=note> | 314 | <p class=note> |
312 | Note: The return values for the <tt>send</tt> method have been changed in | 315 | <b>Important note</b>: |
313 | LuaSocket 2.0! In previous versions, the method returned only the | 316 | The return values for the <tt>send</tt> method have been changed in |
314 | error message. Since returning <b><tt>nil</tt></b> in case of success goes | 317 | LuaSocket 2.0 alpha <b>and again</b> in the beta (sorry)! |
315 | against all other LuaSocket methods and functions, the | 318 | In previous versions, the method returned only the |
316 | <tt>send</tt> method been changed for the sake of uniformity. | 319 | error message. Since returning <b><tt>nil</tt></b> in case of success was |
320 | nonsense, in alpha the first return value became the number of bytes sent. | ||
321 | Alas, it wasn't returning <tt><b>nil</b></tt> in case of | ||
322 | error. So it was changed again in beta. | ||
317 | </p> | 323 | </p> |
318 | 324 | ||
319 | <!-- setoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | 325 | <!-- setoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> |
diff --git a/src/except.c b/src/except.c index 0482e1c..53a65ac 100644 --- a/src/except.c +++ b/src/except.c | |||
@@ -14,17 +14,20 @@ | |||
14 | \*=========================================================================*/ | 14 | \*=========================================================================*/ |
15 | static int global_try(lua_State *L); | 15 | static int global_try(lua_State *L); |
16 | static int global_protect(lua_State *L); | 16 | static int global_protect(lua_State *L); |
17 | static int global_newtry(lua_State *L); | ||
17 | static int protected(lua_State *L); | 18 | static int protected(lua_State *L); |
19 | static int finalize(lua_State *L); | ||
18 | 20 | ||
19 | /* except functions */ | 21 | /* except functions */ |
20 | static luaL_reg func[] = { | 22 | static luaL_reg func[] = { |
21 | {"try", global_try}, | 23 | {"try", global_try}, |
22 | {"protect", global_protect}, | 24 | {"newtry", global_newtry}, |
23 | {NULL, NULL} | 25 | {"protect", global_protect}, |
26 | {NULL, NULL} | ||
24 | }; | 27 | }; |
25 | 28 | ||
26 | /*-------------------------------------------------------------------------*\ | 29 | /*-------------------------------------------------------------------------*\ |
27 | * Exception handling: try method | 30 | * Try method |
28 | \*-------------------------------------------------------------------------*/ | 31 | \*-------------------------------------------------------------------------*/ |
29 | static int global_try(lua_State *L) { | 32 | static int global_try(lua_State *L) { |
30 | if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) { | 33 | if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) { |
@@ -35,7 +38,25 @@ static int global_try(lua_State *L) { | |||
35 | } | 38 | } |
36 | 39 | ||
37 | /*-------------------------------------------------------------------------*\ | 40 | /*-------------------------------------------------------------------------*\ |
38 | * Exception handling: protect factory | 41 | * Finalizer factory |
42 | \*-------------------------------------------------------------------------*/ | ||
43 | static int finalize(lua_State *L) { | ||
44 | if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) { | ||
45 | lua_pushvalue(L, lua_upvalueindex(1)); | ||
46 | lua_pcall(L, 0, 0, 0); | ||
47 | lua_settop(L, 2); | ||
48 | lua_error(L); | ||
49 | return 0; | ||
50 | } else return lua_gettop(L); | ||
51 | } | ||
52 | |||
53 | static int global_newtry(lua_State *L) { | ||
54 | lua_pushcclosure(L, finalize, 1); | ||
55 | return 1; | ||
56 | } | ||
57 | |||
58 | /*-------------------------------------------------------------------------*\ | ||
59 | * Protect factory | ||
39 | \*-------------------------------------------------------------------------*/ | 60 | \*-------------------------------------------------------------------------*/ |
40 | static int protected(lua_State *L) { | 61 | static int protected(lua_State *L) { |
41 | lua_pushvalue(L, lua_upvalueindex(1)); | 62 | lua_pushvalue(L, lua_upvalueindex(1)); |
@@ -48,7 +69,6 @@ static int protected(lua_State *L) { | |||
48 | } | 69 | } |
49 | 70 | ||
50 | static int global_protect(lua_State *L) { | 71 | static int global_protect(lua_State *L) { |
51 | lua_insert(L, 1); | ||
52 | lua_pushcclosure(L, protected, 1); | 72 | lua_pushcclosure(L, protected, 1); |
53 | return 1; | 73 | return 1; |
54 | } | 74 | } |
diff --git a/src/ftp.lua b/src/ftp.lua index 3868158..ad02c72 100644 --- a/src/ftp.lua +++ b/src/ftp.lua | |||
@@ -32,143 +32,153 @@ local metat = { __index = {} } | |||
32 | 32 | ||
33 | function open(server, port) | 33 | function open(server, port) |
34 | local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT)) | 34 | local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT)) |
35 | return setmetatable({tp = tp}, metat) | 35 | local f = { tp = tp } |
36 | -- make sure everything gets closed in an exception | ||
37 | f.try = socket.newtry(function() | ||
38 | tp:close() | ||
39 | if f.data then f.data:close() end | ||
40 | if f.server then f.server:close() end | ||
41 | end) | ||
42 | return setmetatable(f, metat) | ||
36 | end | 43 | end |
37 | 44 | ||
38 | local function port(portt) | 45 | function metat.__index:portconnect() |
39 | return portt.server:accept() | 46 | self.try(self.server:settimeout(TIMEOUT)) |
47 | self.data = self.try(self.server:accept()) | ||
48 | self.try(self.data:settimeout(TIMEOUT)) | ||
40 | end | 49 | end |
41 | 50 | ||
42 | local function pasv(pasvt) | 51 | function metat.__index:pasvconnect() |
43 | local data = socket.try(socket.tcp()) | 52 | self.data = self.try(socket.tcp()) |
44 | socket.try(data:settimeout(TIMEOUT)) | 53 | self.try(self.data:settimeout(TIMEOUT)) |
45 | socket.try(data:connect(pasvt.ip, pasvt.port)) | 54 | self.try(self.data:connect(self.pasvt.ip, self.pasvt.port)) |
46 | return data | ||
47 | end | 55 | end |
48 | 56 | ||
49 | function metat.__index:login(user, password) | 57 | function metat.__index:login(user, password) |
50 | socket.try(self.tp:command("user", user or USER)) | 58 | self.try(self.tp:command("user", user or USER)) |
51 | local code, reply = socket.try(self.tp:check{"2..", 331}) | 59 | local code, reply = self.try(self.tp:check{"2..", 331}) |
52 | if code == 331 then | 60 | if code == 331 then |
53 | socket.try(self.tp:command("pass", password or PASSWORD)) | 61 | self.try(self.tp:command("pass", password or PASSWORD)) |
54 | socket.try(self.tp:check("2..")) | 62 | self.try(self.tp:check("2..")) |
55 | end | 63 | end |
56 | return 1 | 64 | return 1 |
57 | end | 65 | end |
58 | 66 | ||
59 | function metat.__index:pasv() | 67 | function metat.__index:pasv() |
60 | socket.try(self.tp:command("pasv")) | 68 | self.try(self.tp:command("pasv")) |
61 | local code, reply = socket.try(self.tp:check("2..")) | 69 | local code, reply = self.try(self.tp:check("2..")) |
62 | local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" | 70 | local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" |
63 | local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) | 71 | local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) |
64 | socket.try(a and b and c and d and p1 and p2, reply) | 72 | self.try(a and b and c and d and p1 and p2, reply) |
65 | self.pasvt = { | 73 | self.pasvt = { |
66 | ip = string.format("%d.%d.%d.%d", a, b, c, d), | 74 | ip = string.format("%d.%d.%d.%d", a, b, c, d), |
67 | port = p1*256 + p2 | 75 | port = p1*256 + p2 |
68 | } | 76 | } |
69 | if self.portt then | 77 | if self.server then |
70 | self.portt.server:close() | 78 | self.server:close() |
71 | self.portt = nil | 79 | self.server = nil |
72 | end | 80 | end |
73 | return self.pasvt.ip, self.pasvt.port | 81 | return self.pasvt.ip, self.pasvt.port |
74 | end | 82 | end |
75 | 83 | ||
76 | function metat.__index:port(ip, port) | 84 | function metat.__index:port(ip, port) |
77 | self.pasvt = nil | 85 | self.pasvt = nil |
78 | local server | ||
79 | if not ip then | 86 | if not ip then |
80 | ip, port = socket.try(self.tp:getcontrol():getsockname()) | 87 | ip, port = self.try(self.tp:getcontrol():getsockname()) |
81 | server = socket.try(socket.bind(ip, 0)) | 88 | self.server = self.try(socket.bind(ip, 0)) |
82 | ip, port = socket.try(server:getsockname()) | 89 | ip, port = self.try(self.server:getsockname()) |
83 | socket.try(server:settimeout(TIMEOUT)) | 90 | self.try(server:settimeout(TIMEOUT)) |
84 | end | 91 | end |
85 | local pl = math.mod(port, 256) | 92 | local pl = math.mod(port, 256) |
86 | local ph = (port - pl)/256 | 93 | local ph = (port - pl)/256 |
87 | local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") | 94 | local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") |
88 | socket.try(self.tp:command("port", arg)) | 95 | self.try(self.tp:command("port", arg)) |
89 | socket.try(self.tp:check("2..")) | 96 | self.try(self.tp:check("2..")) |
90 | self.portt = server and {ip = ip, port = port, server = server} | ||
91 | return 1 | 97 | return 1 |
92 | end | 98 | end |
93 | 99 | ||
94 | function metat.__index:send(sendt) | 100 | function metat.__index:send(sendt) |
95 | local data | 101 | self.try(self.pasvt or self.server, "need port or pasv first") |
96 | socket.try(self.pasvt or self.portt, "need port or pasv first") | 102 | -- if there is a pasvt table, we already sent a PASV command |
97 | if self.pasvt then data = socket.try(pasv(self.pasvt)) end | 103 | -- we just get the data connection into self.data |
104 | if self.pasvt then self:pasvconnect() end | ||
105 | -- get the transfer argument and command | ||
98 | local argument = sendt.argument or string.gsub(sendt.path, "^/", "") | 106 | local argument = sendt.argument or string.gsub(sendt.path, "^/", "") |
99 | if argument == "" then argument = nil end | 107 | if argument == "" then argument = nil end |
100 | local command = sendt.command or "stor" | 108 | local command = sendt.command or "stor" |
101 | socket.try(self.tp:command(command, argument)) | 109 | -- send the transfer command and check the reply |
102 | local code, reply = socket.try(self.tp:check{"2..", "1.."}) | 110 | self.try(self.tp:command(command, argument)) |
103 | if self.portt then data = socket.try(port(self.portt)) end | 111 | local code, reply = self.try(self.tp:check{"2..", "1.."}) |
112 | -- if there is not a a pasvt table, then there is a server | ||
113 | -- and we already sent a PORT command | ||
114 | if not self.pasvt then self:portconnect() end | ||
115 | -- get the sink, source and step for the transfer | ||
104 | local step = sendt.step or ltn12.pump.step | 116 | local step = sendt.step or ltn12.pump.step |
105 | local checkstep = function(src, snk) | 117 | local checkstep = function(src, snk) |
118 | -- check status in control connection while downloading | ||
106 | local readyt = socket.select(readt, nil, 0) | 119 | local readyt = socket.select(readt, nil, 0) |
107 | if readyt[tp] then | 120 | if readyt[tp] then self.try(self.tp:check("2..")) end |
108 | code, reply = self.tp:check("2..") | 121 | return step(src, snk) |
109 | if not code then | ||
110 | data:close() | ||
111 | return nil, reply | ||
112 | end | ||
113 | end | ||
114 | local ret, err = step(src, snk) | ||
115 | if err then data:close() end | ||
116 | return ret, err | ||
117 | end | 122 | end |
118 | local sink = socket.sink("close-when-done", data) | 123 | local sink = socket.sink("close-when-done", self.data) |
119 | socket.try(ltn12.pump.all(sendt.source, sink, checkstep)) | 124 | -- transfer all data and check error |
120 | if string.find(code, "1..") then socket.try(self.tp:check("2..")) end | 125 | self.try(ltn12.pump.all(sendt.source, sink, checkstep)) |
126 | if string.find(code, "1..") then self.try(self.tp:check("2..")) end | ||
127 | -- done with data connection | ||
128 | self.data:close() | ||
129 | self.data = nil | ||
121 | return 1 | 130 | return 1 |
122 | end | 131 | end |
123 | 132 | ||
124 | function metat.__index:receive(recvt) | 133 | function metat.__index:receive(recvt) |
125 | local data | 134 | self.try(self.pasvt or self.server, "need port or pasv first") |
126 | socket.try(self.pasvt or self.portt, "need port or pasv first") | 135 | if self.pasvt then self:pasvconnect() end |
127 | if self.pasvt then data = socket.try(pasv(self.pasvt)) end | ||
128 | local argument = recvt.argument or string.gsub(recvt.path, "^/", "") | 136 | local argument = recvt.argument or string.gsub(recvt.path, "^/", "") |
129 | if argument == "" then argument = nil end | 137 | if argument == "" then argument = nil end |
130 | local command = recvt.command or "retr" | 138 | local command = recvt.command or "retr" |
131 | socket.try(self.tp:command(command, argument)) | 139 | self.try(self.tp:command(command, argument)) |
132 | local code = socket.try(self.tp:check{"1..", "2.."}) | 140 | local code = self.try(self.tp:check{"1..", "2.."}) |
133 | if self.portt then data = socket.try(port(self.portt)) end | 141 | if not self.pasvt then self:portconnect() end |
134 | local source = socket.source("until-closed", data) | 142 | local source = socket.source("until-closed", self.data) |
135 | local step = recvt.step or ltn12.pump.step | 143 | local step = recvt.step or ltn12.pump.step |
136 | local checkstep = function(src, snk) | 144 | self.try(ltn12.pump.all(source, recvt.sink, step)) |
137 | local ret, err = step(src, snk) | 145 | if string.find(code, "1..") then self.try(self.tp:check("2..")) end |
138 | if err then data:close() end | 146 | self.data:close() |
139 | return ret, err | 147 | self.data = nil |
140 | end | ||
141 | socket.try(ltn12.pump.all(source, recvt.sink, checkstep)) | ||
142 | if string.find(code, "1..") then socket.try(self.tp:check("2..")) end | ||
143 | return 1 | 148 | return 1 |
144 | end | 149 | end |
145 | 150 | ||
146 | function metat.__index:cwd(dir) | 151 | function metat.__index:cwd(dir) |
147 | socket.try(self.tp:command("cwd", dir)) | 152 | self.try(self.tp:command("cwd", dir)) |
148 | socket.try(self.tp:check(250)) | 153 | self.try(self.tp:check(250)) |
149 | return 1 | 154 | return 1 |
150 | end | 155 | end |
151 | 156 | ||
152 | function metat.__index:type(type) | 157 | function metat.__index:type(type) |
153 | socket.try(self.tp:command("type", type)) | 158 | self.try(self.tp:command("type", type)) |
154 | socket.try(self.tp:check(200)) | 159 | self.try(self.tp:check(200)) |
155 | return 1 | 160 | return 1 |
156 | end | 161 | end |
157 | 162 | ||
158 | function metat.__index:greet() | 163 | function metat.__index:greet() |
159 | local code = socket.try(self.tp:check{"1..", "2.."}) | 164 | local code = self.try(self.tp:check{"1..", "2.."}) |
160 | if string.find(code, "1..") then socket.try(self.tp:check("2..")) end | 165 | if string.find(code, "1..") then self.try(self.tp:check("2..")) end |
161 | return 1 | 166 | return 1 |
162 | end | 167 | end |
163 | 168 | ||
164 | function metat.__index:quit() | 169 | function metat.__index:quit() |
165 | socket.try(self.tp:command("quit")) | 170 | self.try(self.tp:command("quit")) |
166 | socket.try(self.tp:check("2..")) | 171 | self.try(self.tp:check("2..")) |
167 | return 1 | 172 | return 1 |
168 | end | 173 | end |
169 | 174 | ||
170 | function metat.__index:close() | 175 | function metat.__index:close() |
171 | socket.try(self.tp:close()) | 176 | self.tp:close() |
177 | if self.data then self.data:close() end | ||
178 | if self.server then self.server:close() end | ||
179 | self.tp = nil | ||
180 | self.data = nil | ||
181 | self.server = nil | ||
172 | return 1 | 182 | return 1 |
173 | end | 183 | end |
174 | 184 | ||
@@ -176,14 +186,14 @@ end | |||
176 | -- High level FTP API | 186 | -- High level FTP API |
177 | ----------------------------------------------------------------------------- | 187 | ----------------------------------------------------------------------------- |
178 | local function tput(putt) | 188 | local function tput(putt) |
179 | local con = open(putt.host, putt.port) | 189 | local f = open(putt.host, putt.port) |
180 | con:greet() | 190 | f:greet() |
181 | con:login(putt.user, putt.password) | 191 | f:login(putt.user, putt.password) |
182 | if putt.type then con:type(putt.type) end | 192 | if putt.type then f:type(putt.type) end |
183 | con:pasv() | 193 | f:pasv() |
184 | con:send(putt) | 194 | f:send(putt) |
185 | con:quit() | 195 | f:quit() |
186 | return con:close() | 196 | return f:close() |
187 | end | 197 | end |
188 | 198 | ||
189 | local default = { | 199 | local default = { |
@@ -216,14 +226,14 @@ put = socket.protect(function(putt, body) | |||
216 | end) | 226 | end) |
217 | 227 | ||
218 | local function tget(gett) | 228 | local function tget(gett) |
219 | local con = open(gett.host, gett.port) | 229 | local f = open(gett.host, gett.port) |
220 | con:greet() | 230 | f:greet() |
221 | con:login(gett.user, gett.password) | 231 | f:login(gett.user, gett.password) |
222 | if gett.type then con:type(gett.type) end | 232 | if gett.type then f:type(gett.type) end |
223 | con:pasv() | 233 | f:pasv() |
224 | con:receive(gett) | 234 | f:receive(gett) |
225 | con:quit() | 235 | f:quit() |
226 | return con:close() | 236 | return f:close() |
227 | end | 237 | end |
228 | 238 | ||
229 | local function sget(u) | 239 | local function sget(u) |
diff --git a/src/http.lua b/src/http.lua index 3bd4d6a..d8889e1 100644 --- a/src/http.lua +++ b/src/http.lua | |||
@@ -31,24 +31,25 @@ BLOCKSIZE = 2048 | |||
31 | local metat = { __index = {} } | 31 | local metat = { __index = {} } |
32 | 32 | ||
33 | function open(host, port) | 33 | function open(host, port) |
34 | local con = socket.try(socket.tcp()) | 34 | local c = socket.try(socket.tcp()) |
35 | socket.try(con:settimeout(TIMEOUT)) | 35 | -- make sure the connection gets closed on exception |
36 | port = port or PORT | 36 | local try = socket.newtry(function() c:close() end) |
37 | socket.try(con:connect(host, port)) | 37 | try(c:settimeout(TIMEOUT)) |
38 | return setmetatable({ con = con }, metat) | 38 | try(c:connect(host, port or PORT)) |
39 | return setmetatable({ c = c, try = try }, metat) | ||
39 | end | 40 | end |
40 | 41 | ||
41 | function metat.__index:sendrequestline(method, uri) | 42 | function metat.__index:sendrequestline(method, uri) |
42 | local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) | 43 | local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) |
43 | return socket.try(self.con:send(reqline)) | 44 | return self.try(self.c:send(reqline)) |
44 | end | 45 | end |
45 | 46 | ||
46 | function metat.__index:sendheaders(headers) | 47 | function metat.__index:sendheaders(headers) |
47 | for i, v in pairs(headers) do | 48 | for i, v in pairs(headers) do |
48 | socket.try(self.con:send(i .. ": " .. v .. "\r\n")) | 49 | self.try(self.c:send(i .. ": " .. v .. "\r\n")) |
49 | end | 50 | end |
50 | -- mark end of request headers | 51 | -- mark end of request headers |
51 | socket.try(self.con:send("\r\n")) | 52 | self.try(self.c:send("\r\n")) |
52 | return 1 | 53 | return 1 |
53 | end | 54 | end |
54 | 55 | ||
@@ -59,32 +60,32 @@ function metat.__index:sendbody(headers, source, step) | |||
59 | local mode | 60 | local mode |
60 | if headers["content-length"] then mode = "keep-open" | 61 | if headers["content-length"] then mode = "keep-open" |
61 | else mode = "http-chunked" end | 62 | else mode = "http-chunked" end |
62 | return socket.try(ltn12.pump.all(source, socket.sink(mode, self.con), step)) | 63 | return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) |
63 | end | 64 | end |
64 | 65 | ||
65 | function metat.__index:receivestatusline() | 66 | function metat.__index:receivestatusline() |
66 | local status = socket.try(self.con:receive()) | 67 | local status = self.try(self.c:receive()) |
67 | local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) | 68 | local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) |
68 | return socket.try(tonumber(code), status) | 69 | return self.try(tonumber(code), status) |
69 | end | 70 | end |
70 | 71 | ||
71 | function metat.__index:receiveheaders() | 72 | function metat.__index:receiveheaders() |
72 | local line, name, value | 73 | local line, name, value |
73 | local headers = {} | 74 | local headers = {} |
74 | -- get first line | 75 | -- get first line |
75 | line = socket.try(self.con:receive()) | 76 | line = self.try(self.c:receive()) |
76 | -- headers go until a blank line is found | 77 | -- headers go until a blank line is found |
77 | while line ~= "" do | 78 | while line ~= "" do |
78 | -- get field-name and value | 79 | -- get field-name and value |
79 | name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) | 80 | name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) |
80 | socket.try(name and value, "malformed reponse headers") | 81 | self.try(name and value, "malformed reponse headers") |
81 | name = string.lower(name) | 82 | name = string.lower(name) |
82 | -- get next line (value might be folded) | 83 | -- get next line (value might be folded) |
83 | line = socket.try(self.con:receive()) | 84 | line = self.try(self.c:receive()) |
84 | -- unfold any folded values | 85 | -- unfold any folded values |
85 | while string.find(line, "^%s") do | 86 | while string.find(line, "^%s") do |
86 | value = value .. line | 87 | value = value .. line |
87 | line = socket.try(self.con:receive()) | 88 | line = self.try(self.c:receive()) |
88 | end | 89 | end |
89 | -- save pair in table | 90 | -- save pair in table |
90 | if headers[name] then headers[name] = headers[name] .. ", " .. value | 91 | if headers[name] then headers[name] = headers[name] .. ", " .. value |
@@ -102,12 +103,12 @@ function metat.__index:receivebody(headers, sink, step) | |||
102 | if TE and TE ~= "identity" then mode = "http-chunked" | 103 | if TE and TE ~= "identity" then mode = "http-chunked" |
103 | elseif tonumber(headers["content-length"]) then mode = "by-length" | 104 | elseif tonumber(headers["content-length"]) then mode = "by-length" |
104 | else mode = "default" end | 105 | else mode = "default" end |
105 | return socket.try(ltn12.pump.all(socket.source(mode, self.con, length), | 106 | return self.try(ltn12.pump.all(socket.source(mode, self.c, length), |
106 | sink, step)) | 107 | sink, step)) |
107 | end | 108 | end |
108 | 109 | ||
109 | function metat.__index:close() | 110 | function metat.__index:close() |
110 | return self.con:close() | 111 | return self.c:close() |
111 | end | 112 | end |
112 | 113 | ||
113 | ----------------------------------------------------------------------------- | 114 | ----------------------------------------------------------------------------- |
@@ -204,23 +205,23 @@ end | |||
204 | 205 | ||
205 | function trequest(reqt) | 206 | function trequest(reqt) |
206 | reqt = adjustrequest(reqt) | 207 | reqt = adjustrequest(reqt) |
207 | local con = open(reqt.host, reqt.port) | 208 | local h = open(reqt.host, reqt.port) |
208 | con:sendrequestline(reqt.method, reqt.uri) | 209 | h:sendrequestline(reqt.method, reqt.uri) |
209 | con:sendheaders(reqt.headers) | 210 | h:sendheaders(reqt.headers) |
210 | con:sendbody(reqt.headers, reqt.source, reqt.step) | 211 | h:sendbody(reqt.headers, reqt.source, reqt.step) |
211 | local code, headers, status | 212 | local code, headers, status |
212 | code, status = con:receivestatusline() | 213 | code, status = h:receivestatusline() |
213 | headers = con:receiveheaders() | 214 | headers = h:receiveheaders() |
214 | if shouldredirect(reqt, code) then | 215 | if shouldredirect(reqt, code) then |
215 | con:close() | 216 | h:close() |
216 | return tredirect(reqt, headers) | 217 | return tredirect(reqt, headers) |
217 | elseif shouldauthorize(reqt, code) then | 218 | elseif shouldauthorize(reqt, code) then |
218 | con:close() | 219 | h:close() |
219 | return tauthorize(reqt) | 220 | return tauthorize(reqt) |
220 | elseif shouldreceivebody(reqt, code) then | 221 | elseif shouldreceivebody(reqt, code) then |
221 | con:receivebody(headers, reqt.sink, reqt.step) | 222 | h:receivebody(headers, reqt.sink, reqt.step) |
222 | end | 223 | end |
223 | con:close() | 224 | h:close() |
224 | return 1, code, headers, status | 225 | return 1, code, headers, status |
225 | end | 226 | end |
226 | 227 | ||
diff --git a/src/smtp.lua b/src/smtp.lua index 1708053..d6357d2 100644 --- a/src/smtp.lua +++ b/src/smtp.lua | |||
@@ -31,51 +31,51 @@ ZONE = "-0000" | |||
31 | local metat = { __index = {} } | 31 | local metat = { __index = {} } |
32 | 32 | ||
33 | function metat.__index:greet(domain) | 33 | function metat.__index:greet(domain) |
34 | socket.try(self.tp:check("2..")) | 34 | self.try(self.tp:check("2..")) |
35 | socket.try(self.tp:command("EHLO", domain or DOMAIN)) | 35 | self.try(self.tp:command("EHLO", domain or DOMAIN)) |
36 | return socket.skip(1, socket.try(self.tp:check("2.."))) | 36 | return socket.skip(1, self.try(self.tp:check("2.."))) |
37 | end | 37 | end |
38 | 38 | ||
39 | function metat.__index:mail(from) | 39 | function metat.__index:mail(from) |
40 | socket.try(self.tp:command("MAIL", "FROM:" .. from)) | 40 | self.try(self.tp:command("MAIL", "FROM:" .. from)) |
41 | return socket.try(self.tp:check("2..")) | 41 | return self.try(self.tp:check("2..")) |
42 | end | 42 | end |
43 | 43 | ||
44 | function metat.__index:rcpt(to) | 44 | function metat.__index:rcpt(to) |
45 | socket.try(self.tp:command("RCPT", "TO:" .. to)) | 45 | self.try(self.tp:command("RCPT", "TO:" .. to)) |
46 | return socket.try(self.tp:check("2..")) | 46 | return self.try(self.tp:check("2..")) |
47 | end | 47 | end |
48 | 48 | ||
49 | function metat.__index:data(src, step) | 49 | function metat.__index:data(src, step) |
50 | socket.try(self.tp:command("DATA")) | 50 | self.try(self.tp:command("DATA")) |
51 | socket.try(self.tp:check("3..")) | 51 | self.try(self.tp:check("3..")) |
52 | socket.try(self.tp:source(src, step)) | 52 | self.try(self.tp:source(src, step)) |
53 | socket.try(self.tp:send("\r\n.\r\n")) | 53 | self.try(self.tp:send("\r\n.\r\n")) |
54 | return socket.try(self.tp:check("2..")) | 54 | return self.try(self.tp:check("2..")) |
55 | end | 55 | end |
56 | 56 | ||
57 | function metat.__index:quit() | 57 | function metat.__index:quit() |
58 | socket.try(self.tp:command("QUIT")) | 58 | self.try(self.tp:command("QUIT")) |
59 | return socket.try(self.tp:check("2..")) | 59 | return self.try(self.tp:check("2..")) |
60 | end | 60 | end |
61 | 61 | ||
62 | function metat.__index:close() | 62 | function metat.__index:close() |
63 | return socket.try(self.tp:close()) | 63 | return self.try(self.tp:close()) |
64 | end | 64 | end |
65 | 65 | ||
66 | function metat.__index:login(user, password) | 66 | function metat.__index:login(user, password) |
67 | socket.try(self.tp:command("AUTH", "LOGIN")) | 67 | self.try(self.tp:command("AUTH", "LOGIN")) |
68 | socket.try(self.tp:check("3..")) | 68 | self.try(self.tp:check("3..")) |
69 | socket.try(self.tp:command(mime.b64(user))) | 69 | self.try(self.tp:command(mime.b64(user))) |
70 | socket.try(self.tp:check("3..")) | 70 | self.try(self.tp:check("3..")) |
71 | socket.try(self.tp:command(mime.b64(password))) | 71 | self.try(self.tp:command(mime.b64(password))) |
72 | return socket.try(self.tp:check("2..")) | 72 | return self.try(self.tp:check("2..")) |
73 | end | 73 | end |
74 | 74 | ||
75 | function metat.__index:plain(user, password) | 75 | function metat.__index:plain(user, password) |
76 | local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) | 76 | local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) |
77 | socket.try(self.tp:command("AUTH", auth)) | 77 | self.try(self.tp:command("AUTH", auth)) |
78 | return socket.try(self.tp:check("2..")) | 78 | return self.try(self.tp:check("2..")) |
79 | end | 79 | end |
80 | 80 | ||
81 | function metat.__index:auth(user, password, ext) | 81 | function metat.__index:auth(user, password, ext) |
@@ -85,7 +85,7 @@ function metat.__index:auth(user, password, ext) | |||
85 | elseif string.find(ext, "AUTH[^\n]+PLAIN") then | 85 | elseif string.find(ext, "AUTH[^\n]+PLAIN") then |
86 | return self:plain(user, password) | 86 | return self:plain(user, password) |
87 | else | 87 | else |
88 | socket.try(nil, "authentication not supported") | 88 | self.try(nil, "authentication not supported") |
89 | end | 89 | end |
90 | end | 90 | end |
91 | 91 | ||
@@ -104,7 +104,9 @@ end | |||
104 | 104 | ||
105 | function open(server, port) | 105 | function open(server, port) |
106 | local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT)) | 106 | local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT)) |
107 | return setmetatable({tp = tp}, metat) | 107 | -- make sure tp is closed if we get an exception |
108 | local try = socket.newtry(function() tp:close() end) | ||
109 | return setmetatable({ tp = tp, try = try}, metat) | ||
108 | end | 110 | end |
109 | 111 | ||
110 | --------------------------------------------------------------------------- | 112 | --------------------------------------------------------------------------- |
@@ -222,10 +224,10 @@ end | |||
222 | -- High level SMTP API | 224 | -- High level SMTP API |
223 | ----------------------------------------------------------------------------- | 225 | ----------------------------------------------------------------------------- |
224 | send = socket.protect(function(mailt) | 226 | send = socket.protect(function(mailt) |
225 | local con = open(mailt.server, mailt.port) | 227 | local s = open(mailt.server, mailt.port) |
226 | local ext = con:greet(mailt.domain) | 228 | local ext = s:greet(mailt.domain) |
227 | con:auth(mailt.user, mailt.password, ext) | 229 | s:auth(mailt.user, mailt.password, ext) |
228 | con:send(mailt) | 230 | s:send(mailt) |
229 | con:quit() | 231 | s:quit() |
230 | return con:close() | 232 | return s:close() |
231 | end) | 233 | end) |
diff --git a/src/socket.lua b/src/socket.lua index f73d167..0a681bf 100644 --- a/src/socket.lua +++ b/src/socket.lua | |||
@@ -156,8 +156,8 @@ socket.sourcet["http-chunked"] = function(sock) | |||
156 | else | 156 | else |
157 | -- get chunk and skip terminating CRLF | 157 | -- get chunk and skip terminating CRLF |
158 | local chunk, err = sock:receive(size) | 158 | local chunk, err = sock:receive(size) |
159 | if err or socket.skip(2, sock:receive()) then return nil, err | 159 | if chunk then sock:receive() end |
160 | else return chunk end | 160 | return chunk, err |
161 | end | 161 | end |
162 | end | 162 | end |
163 | }) | 163 | }) |
diff --git a/test/ftptest.lua b/test/ftptest.lua index 37e3edc..f578c82 100644 --- a/test/ftptest.lua +++ b/test/ftptest.lua | |||
@@ -86,16 +86,6 @@ back, err = socket.ftp.get { | |||
86 | } | 86 | } |
87 | check(not err and back == index, err) | 87 | check(not err and back == index, err) |
88 | 88 | ||
89 | io.write("testing home directory listing: ") | ||
90 | expected = capture("ls -F /var/ftp | grep -v /") | ||
91 | back, err = socket.ftp.get("ftp://localhost/") | ||
92 | check(back and similar(back, expected), nil, err) | ||
93 | |||
94 | io.write("testing directory listing: ") | ||
95 | expected = capture("ls -F /var/ftp/pub | grep -v /") | ||
96 | back, err = socket.ftp.get("ftp://localhost/pub;type=d") | ||
97 | check(similar(back, expected)) | ||
98 | |||
99 | io.write("testing upload denial: ") | 89 | io.write("testing upload denial: ") |
100 | ret, err = socket.ftp.put("ftp://localhost/index.up.html;type=a", index) | 90 | ret, err = socket.ftp.put("ftp://localhost/index.up.html;type=a", index) |
101 | check(err, err) | 91 | check(err, err) |
diff --git a/test/httptest.lua b/test/httptest.lua index d1f2b88..45d7e8d 100644 --- a/test/httptest.lua +++ b/test/httptest.lua | |||
@@ -70,7 +70,6 @@ io.write("testing request uri correctness: ") | |||
70 | local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" | 70 | local forth = cgiprefix .. "/request-uri?" .. "this+is+the+query+string" |
71 | local back, c, h = http.request("http://" .. host .. forth) | 71 | local back, c, h = http.request("http://" .. host .. forth) |
72 | if not back then fail(c) end | 72 | if not back then fail(c) end |
73 | print(back) | ||
74 | back = url.parse(back) | 73 | back = url.parse(back) |
75 | if similar(back.query, "this+is+the+query+string") then print("ok") | 74 | if similar(back.query, "this+is+the+query+string") then print("ok") |
76 | else fail(back.query) end | 75 | else fail(back.query) end |