diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/auxiliar.c | 50 | ||||
-rw-r--r-- | src/auxiliar.h | 25 | ||||
-rw-r--r-- | src/buffer.c | 9 | ||||
-rw-r--r-- | src/except.c | 52 | ||||
-rw-r--r-- | src/except.h | 35 | ||||
-rw-r--r-- | src/ftp.lua | 73 | ||||
-rw-r--r-- | src/http.lua | 351 | ||||
-rw-r--r-- | src/inet.c | 1 | ||||
-rw-r--r-- | src/ltn12.lua | 21 | ||||
-rw-r--r-- | src/luasocket.c | 71 | ||||
-rw-r--r-- | src/luasocket.h | 1 | ||||
-rw-r--r-- | src/mime.c | 12 | ||||
-rw-r--r-- | src/mime.h | 1 | ||||
-rw-r--r-- | src/mime.lua | 14 | ||||
-rw-r--r-- | src/select.c | 221 | ||||
-rw-r--r-- | src/smtp.lua | 50 | ||||
-rw-r--r-- | src/socket.lua | 13 | ||||
-rw-r--r-- | src/tcp.c | 5 | ||||
-rw-r--r-- | src/timeout.c | 8 | ||||
-rw-r--r-- | src/tp.lua | 26 | ||||
-rw-r--r-- | src/udp.c | 5 | ||||
-rw-r--r-- | src/url.lua | 4 |
22 files changed, 621 insertions, 427 deletions
diff --git a/src/auxiliar.c b/src/auxiliar.c index b1f9203..9a37e10 100644 --- a/src/auxiliar.c +++ b/src/auxiliar.c | |||
@@ -7,7 +7,6 @@ | |||
7 | #include <string.h> | 7 | #include <string.h> |
8 | #include <stdio.h> | 8 | #include <stdio.h> |
9 | 9 | ||
10 | #include "luasocket.h" | ||
11 | #include "auxiliar.h" | 10 | #include "auxiliar.h" |
12 | 11 | ||
13 | /*=========================================================================*\ | 12 | /*=========================================================================*\ |
@@ -16,16 +15,15 @@ | |||
16 | /*-------------------------------------------------------------------------*\ | 15 | /*-------------------------------------------------------------------------*\ |
17 | * Initializes the module | 16 | * Initializes the module |
18 | \*-------------------------------------------------------------------------*/ | 17 | \*-------------------------------------------------------------------------*/ |
19 | int aux_open(lua_State *L) | 18 | int aux_open(lua_State *L) { |
20 | { | ||
21 | return 0; | 19 | return 0; |
22 | } | 20 | } |
23 | 21 | ||
24 | /*-------------------------------------------------------------------------*\ | 22 | /*-------------------------------------------------------------------------*\ |
25 | * Creates a new class with given methods | 23 | * Creates a new class with given methods |
24 | * Methods whose names start with __ are passed directly to the metatable. | ||
26 | \*-------------------------------------------------------------------------*/ | 25 | \*-------------------------------------------------------------------------*/ |
27 | void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) | 26 | void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) { |
28 | { | ||
29 | luaL_newmetatable(L, classname); /* mt */ | 27 | luaL_newmetatable(L, classname); /* mt */ |
30 | /* create __index table to place methods */ | 28 | /* create __index table to place methods */ |
31 | lua_pushstring(L, "__index"); /* mt,"__index" */ | 29 | lua_pushstring(L, "__index"); /* mt,"__index" */ |
@@ -46,10 +44,30 @@ void aux_newclass(lua_State *L, const char *classname, luaL_reg *func) | |||
46 | } | 44 | } |
47 | 45 | ||
48 | /*-------------------------------------------------------------------------*\ | 46 | /*-------------------------------------------------------------------------*\ |
47 | * Prints the value of a class in a nice way | ||
48 | \*-------------------------------------------------------------------------*/ | ||
49 | int aux_tostring(lua_State *L) { | ||
50 | char buf[32]; | ||
51 | if (!lua_getmetatable(L, 1)) goto error; | ||
52 | lua_pushstring(L, "__index"); | ||
53 | lua_gettable(L, -2); | ||
54 | if (!lua_istable(L, -1)) goto error; | ||
55 | lua_pushstring(L, "class"); | ||
56 | lua_gettable(L, -2); | ||
57 | if (!lua_isstring(L, -1)) goto error; | ||
58 | sprintf(buf, "%p", lua_touserdata(L, 1)); | ||
59 | lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf); | ||
60 | return 1; | ||
61 | error: | ||
62 | lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'"); | ||
63 | lua_error(L); | ||
64 | return 1; | ||
65 | } | ||
66 | |||
67 | /*-------------------------------------------------------------------------*\ | ||
49 | * Insert class into group | 68 | * Insert class into group |
50 | \*-------------------------------------------------------------------------*/ | 69 | \*-------------------------------------------------------------------------*/ |
51 | void aux_add2group(lua_State *L, const char *classname, const char *groupname) | 70 | void aux_add2group(lua_State *L, const char *classname, const char *groupname) { |
52 | { | ||
53 | luaL_getmetatable(L, classname); | 71 | luaL_getmetatable(L, classname); |
54 | lua_pushstring(L, groupname); | 72 | lua_pushstring(L, groupname); |
55 | lua_pushboolean(L, 1); | 73 | lua_pushboolean(L, 1); |
@@ -60,8 +78,7 @@ void aux_add2group(lua_State *L, const char *classname, const char *groupname) | |||
60 | /*-------------------------------------------------------------------------*\ | 78 | /*-------------------------------------------------------------------------*\ |
61 | * Make sure argument is a boolean | 79 | * Make sure argument is a boolean |
62 | \*-------------------------------------------------------------------------*/ | 80 | \*-------------------------------------------------------------------------*/ |
63 | int aux_checkboolean(lua_State *L, int objidx) | 81 | int aux_checkboolean(lua_State *L, int objidx) { |
64 | { | ||
65 | if (!lua_isboolean(L, objidx)) | 82 | if (!lua_isboolean(L, objidx)) |
66 | luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN)); | 83 | luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN)); |
67 | return lua_toboolean(L, objidx); | 84 | return lua_toboolean(L, objidx); |
@@ -71,8 +88,7 @@ int aux_checkboolean(lua_State *L, int objidx) | |||
71 | * Return userdata pointer if object belongs to a given class, abort with | 88 | * Return userdata pointer if object belongs to a given class, abort with |
72 | * error otherwise | 89 | * error otherwise |
73 | \*-------------------------------------------------------------------------*/ | 90 | \*-------------------------------------------------------------------------*/ |
74 | void *aux_checkclass(lua_State *L, const char *classname, int objidx) | 91 | void *aux_checkclass(lua_State *L, const char *classname, int objidx) { |
75 | { | ||
76 | void *data = aux_getclassudata(L, classname, objidx); | 92 | void *data = aux_getclassudata(L, classname, objidx); |
77 | if (!data) { | 93 | if (!data) { |
78 | char msg[45]; | 94 | char msg[45]; |
@@ -86,8 +102,7 @@ void *aux_checkclass(lua_State *L, const char *classname, int objidx) | |||
86 | * Return userdata pointer if object belongs to a given group, abort with | 102 | * Return userdata pointer if object belongs to a given group, abort with |
87 | * error otherwise | 103 | * error otherwise |
88 | \*-------------------------------------------------------------------------*/ | 104 | \*-------------------------------------------------------------------------*/ |
89 | void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) | 105 | void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) { |
90 | { | ||
91 | void *data = aux_getgroupudata(L, groupname, objidx); | 106 | void *data = aux_getgroupudata(L, groupname, objidx); |
92 | if (!data) { | 107 | if (!data) { |
93 | char msg[45]; | 108 | char msg[45]; |
@@ -100,8 +115,7 @@ void *aux_checkgroup(lua_State *L, const char *groupname, int objidx) | |||
100 | /*-------------------------------------------------------------------------*\ | 115 | /*-------------------------------------------------------------------------*\ |
101 | * Set object class | 116 | * Set object class |
102 | \*-------------------------------------------------------------------------*/ | 117 | \*-------------------------------------------------------------------------*/ |
103 | void aux_setclass(lua_State *L, const char *classname, int objidx) | 118 | void aux_setclass(lua_State *L, const char *classname, int objidx) { |
104 | { | ||
105 | luaL_getmetatable(L, classname); | 119 | luaL_getmetatable(L, classname); |
106 | if (objidx < 0) objidx--; | 120 | if (objidx < 0) objidx--; |
107 | lua_setmetatable(L, objidx); | 121 | lua_setmetatable(L, objidx); |
@@ -111,8 +125,7 @@ void aux_setclass(lua_State *L, const char *classname, int objidx) | |||
111 | * Get a userdata pointer if object belongs to a given group. Return NULL | 125 | * Get a userdata pointer if object belongs to a given group. Return NULL |
112 | * otherwise | 126 | * otherwise |
113 | \*-------------------------------------------------------------------------*/ | 127 | \*-------------------------------------------------------------------------*/ |
114 | void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) | 128 | void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) { |
115 | { | ||
116 | if (!lua_getmetatable(L, objidx)) | 129 | if (!lua_getmetatable(L, objidx)) |
117 | return NULL; | 130 | return NULL; |
118 | lua_pushstring(L, groupname); | 131 | lua_pushstring(L, groupname); |
@@ -130,7 +143,6 @@ void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx) | |||
130 | * Get a userdata pointer if object belongs to a given class. Return NULL | 143 | * Get a userdata pointer if object belongs to a given class. Return NULL |
131 | * otherwise | 144 | * otherwise |
132 | \*-------------------------------------------------------------------------*/ | 145 | \*-------------------------------------------------------------------------*/ |
133 | void *aux_getclassudata(lua_State *L, const char *classname, int objidx) | 146 | void *aux_getclassudata(lua_State *L, const char *classname, int objidx) { |
134 | { | ||
135 | return luaL_checkudata(L, objidx, classname); | 147 | return luaL_checkudata(L, objidx, classname); |
136 | } | 148 | } |
diff --git a/src/auxiliar.h b/src/auxiliar.h index bc45182..70f4704 100644 --- a/src/auxiliar.h +++ b/src/auxiliar.h | |||
@@ -2,26 +2,28 @@ | |||
2 | #define AUX_H | 2 | #define AUX_H |
3 | /*=========================================================================*\ | 3 | /*=========================================================================*\ |
4 | * Auxiliar routines for class hierarchy manipulation | 4 | * Auxiliar routines for class hierarchy manipulation |
5 | * LuaSocket toolkit | 5 | * LuaSocket toolkit (but completely independent of other LuaSocket modules) |
6 | * | 6 | * |
7 | * A LuaSocket class is a name associated with Lua metatables. A LuaSocket | 7 | * A LuaSocket class is a name associated with Lua metatables. A LuaSocket |
8 | * group is a name associated to a class. A class can belong to any number | 8 | * group is a name associated with a class. A class can belong to any number |
9 | * of groups. This module provides the functionality to: | 9 | * of groups. This module provides the functionality to: |
10 | * | 10 | * |
11 | * - create new classes | 11 | * - create new classes |
12 | * - add classes to groups | 12 | * - add classes to groups |
13 | * - set the class of object | 13 | * - set the class of objects |
14 | * - check if an object belongs to a given class or group | 14 | * - check if an object belongs to a given class or group |
15 | * - get the userdata associated to objects | ||
16 | * - print objects in a pretty way | ||
15 | * | 17 | * |
16 | * LuaSocket class names follow the convention <module>{<class>}. Modules | 18 | * LuaSocket class names follow the convention <module>{<class>}. Modules |
17 | * can define any number of classes and groups. The module tcp.c, for | 19 | * can define any number of classes and groups. The module tcp.c, for |
18 | * example, defines the classes tcp{master}, tcp{client} and tcp{server} and | 20 | * example, defines the classes tcp{master}, tcp{client} and tcp{server} and |
19 | * the groups tcp{client, server} and tcp{any}. Module functions can then | 21 | * the groups tcp{client,server} and tcp{any}. Module functions can then |
20 | * perform type-checking on it's arguments by either class or group. | 22 | * perform type-checking on their arguments by either class or group. |
21 | * | 23 | * |
22 | * LuaSocket metatables define the __index metamethod as being a table. This | 24 | * LuaSocket metatables define the __index metamethod as being a table. This |
23 | * table has one field for each method supported by the class. In DEBUG | 25 | * table has one field for each method supported by the class, and a field |
24 | * mode, it also has one field with the class name. | 26 | * "class" with the class name. |
25 | * | 27 | * |
26 | * The mapping from class name to the corresponding metatable and the | 28 | * The mapping from class name to the corresponding metatable and the |
27 | * reverse mapping are done using lauxlib. | 29 | * reverse mapping are done using lauxlib. |
@@ -32,14 +34,6 @@ | |||
32 | #include <lua.h> | 34 | #include <lua.h> |
33 | #include <lauxlib.h> | 35 | #include <lauxlib.h> |
34 | 36 | ||
35 | /* min and max macros */ | ||
36 | #ifndef MIN | ||
37 | #define MIN(x, y) ((x) < (y) ? x : y) | ||
38 | #endif | ||
39 | #ifndef MAX | ||
40 | #define MAX(x, y) ((x) > (y) ? x : y) | ||
41 | #endif | ||
42 | |||
43 | int aux_open(lua_State *L); | 37 | int aux_open(lua_State *L); |
44 | void aux_newclass(lua_State *L, const char *classname, luaL_reg *func); | 38 | void aux_newclass(lua_State *L, const char *classname, luaL_reg *func); |
45 | void aux_add2group(lua_State *L, const char *classname, const char *group); | 39 | void aux_add2group(lua_State *L, const char *classname, const char *group); |
@@ -49,5 +43,6 @@ void *aux_checkgroup(lua_State *L, const char *groupname, int objidx); | |||
49 | void *aux_getclassudata(lua_State *L, const char *groupname, int objidx); | 43 | void *aux_getclassudata(lua_State *L, const char *groupname, int objidx); |
50 | void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx); | 44 | void *aux_getgroupudata(lua_State *L, const char *groupname, int objidx); |
51 | int aux_checkboolean(lua_State *L, int objidx); | 45 | int aux_checkboolean(lua_State *L, int objidx); |
46 | int aux_tostring(lua_State *L); | ||
52 | 47 | ||
53 | #endif /* AUX_H */ | 48 | #endif /* AUX_H */ |
diff --git a/src/buffer.c b/src/buffer.c index b771047..fd885a2 100644 --- a/src/buffer.c +++ b/src/buffer.c | |||
@@ -7,7 +7,6 @@ | |||
7 | #include <lua.h> | 7 | #include <lua.h> |
8 | #include <lauxlib.h> | 8 | #include <lauxlib.h> |
9 | 9 | ||
10 | #include "auxiliar.h" | ||
11 | #include "buffer.h" | 10 | #include "buffer.h" |
12 | 11 | ||
13 | /*=========================================================================*\ | 12 | /*=========================================================================*\ |
@@ -20,6 +19,14 @@ static int buf_get(p_buf buf, const char **data, size_t *count); | |||
20 | static void buf_skip(p_buf buf, size_t count); | 19 | static void buf_skip(p_buf buf, size_t count); |
21 | static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent); | 20 | static int sendraw(p_buf buf, const char *data, size_t count, size_t *sent); |
22 | 21 | ||
22 | /* min and max macros */ | ||
23 | #ifndef MIN | ||
24 | #define MIN(x, y) ((x) < (y) ? x : y) | ||
25 | #endif | ||
26 | #ifndef MAX | ||
27 | #define MAX(x, y) ((x) > (y) ? x : y) | ||
28 | #endif | ||
29 | |||
23 | /*=========================================================================*\ | 30 | /*=========================================================================*\ |
24 | * Exported functions | 31 | * Exported functions |
25 | \*=========================================================================*/ | 32 | \*=========================================================================*/ |
diff --git a/src/except.c b/src/except.c new file mode 100644 index 0000000..c9eb20e --- /dev/null +++ b/src/except.c | |||
@@ -0,0 +1,52 @@ | |||
1 | #include <lauxlib.h> | ||
2 | #include <stdio.h> | ||
3 | |||
4 | #include "except.h" | ||
5 | |||
6 | static int global_try(lua_State *L); | ||
7 | static int global_protect(lua_State *L); | ||
8 | static int protected(lua_State *L); | ||
9 | |||
10 | static luaL_reg func[] = { | ||
11 | {"try", global_try}, | ||
12 | {"protect", global_protect}, | ||
13 | {NULL, NULL} | ||
14 | }; | ||
15 | |||
16 | /*-------------------------------------------------------------------------*\ | ||
17 | * Exception handling: try method | ||
18 | \*-------------------------------------------------------------------------*/ | ||
19 | static int global_try(lua_State *L) { | ||
20 | if (lua_isnil(L, 1) || (lua_isboolean(L, 1) && !lua_toboolean(L, 1))) { | ||
21 | lua_settop(L, 2); | ||
22 | lua_error(L); | ||
23 | return 0; | ||
24 | } else return lua_gettop(L); | ||
25 | } | ||
26 | |||
27 | /*-------------------------------------------------------------------------*\ | ||
28 | * Exception handling: protect factory | ||
29 | \*-------------------------------------------------------------------------*/ | ||
30 | static int protected(lua_State *L) { | ||
31 | lua_pushvalue(L, lua_upvalueindex(1)); | ||
32 | lua_insert(L, 1); | ||
33 | if (lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) != 0) { | ||
34 | lua_pushnil(L); | ||
35 | lua_insert(L, 1); | ||
36 | return 2; | ||
37 | } else return lua_gettop(L); | ||
38 | } | ||
39 | |||
40 | static int global_protect(lua_State *L) { | ||
41 | lua_insert(L, 1); | ||
42 | lua_pushcclosure(L, protected, 1); | ||
43 | return 1; | ||
44 | } | ||
45 | |||
46 | /*-------------------------------------------------------------------------*\ | ||
47 | * Init module | ||
48 | \*-------------------------------------------------------------------------*/ | ||
49 | int except_open(lua_State *L) { | ||
50 | luaL_openlib(L, NULL, func, 0); | ||
51 | return 0; | ||
52 | } | ||
diff --git a/src/except.h b/src/except.h new file mode 100644 index 0000000..2c57b27 --- /dev/null +++ b/src/except.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef EXCEPT_H | ||
2 | #define EXCEPT_H | ||
3 | /*=========================================================================*\ | ||
4 | * Exception control | ||
5 | * LuaSocket toolkit (but completely independent from other modules) | ||
6 | * | ||
7 | * This provides support for simple exceptions in Lua. During the | ||
8 | * development of the HTTP/FTP/SMTP support, it became aparent that | ||
9 | * error checking was taking a substantial amount of the coding. These | ||
10 | * function greatly simplify the task of checking errors. | ||
11 | * | ||
12 | * The main idea is that functions should return nil as its first return | ||
13 | * value when it finds an error, and return an error message (or value) | ||
14 | * following nil. In case of success, as long as the first value is not nil, | ||
15 | * the other values don't matter. | ||
16 | * | ||
17 | * The idea is to nest function calls with the "try" function. This function | ||
18 | * checks the first value, and calls "error" on the second if the first is | ||
19 | * nil. Otherwise, it returns all values it received. | ||
20 | * | ||
21 | * The protect function returns a new function that behaves exactly like the | ||
22 | * function it receives, but the new function doesn't throw exceptions: it | ||
23 | * returns nil followed by the error message instead. | ||
24 | * | ||
25 | * With these two function, it's easy to write functions that throw | ||
26 | * exceptions on error, but that don't interrupt the user script. | ||
27 | * | ||
28 | * RCS ID: $Id$ | ||
29 | \*=========================================================================*/ | ||
30 | |||
31 | #include <lua.h> | ||
32 | |||
33 | int except_open(lua_State *L); | ||
34 | |||
35 | #endif | ||
diff --git a/src/ftp.lua b/src/ftp.lua index 79772f8..c130d1a 100644 --- a/src/ftp.lua +++ b/src/ftp.lua | |||
@@ -7,7 +7,7 @@ | |||
7 | ----------------------------------------------------------------------------- | 7 | ----------------------------------------------------------------------------- |
8 | 8 | ||
9 | ----------------------------------------------------------------------------- | 9 | ----------------------------------------------------------------------------- |
10 | -- Load other required modules | 10 | -- Load required modules |
11 | ----------------------------------------------------------------------------- | 11 | ----------------------------------------------------------------------------- |
12 | local socket = require("socket") | 12 | local socket = require("socket") |
13 | local ltn12 = require("ltn12") | 13 | local ltn12 = require("ltn12") |
@@ -17,10 +17,7 @@ local tp = require("tp") | |||
17 | ----------------------------------------------------------------------------- | 17 | ----------------------------------------------------------------------------- |
18 | -- Setup namespace | 18 | -- Setup namespace |
19 | ----------------------------------------------------------------------------- | 19 | ----------------------------------------------------------------------------- |
20 | local ftp = {} | 20 | _LOADED["ftp"] = getfenv(1) |
21 | -- make all module globals fall into namespace | ||
22 | setmetatable(ftp, { __index = _G }) | ||
23 | setfenv(1, ftp) | ||
24 | 21 | ||
25 | ----------------------------------------------------------------------------- | 22 | ----------------------------------------------------------------------------- |
26 | -- Program constants | 23 | -- Program constants |
@@ -32,9 +29,7 @@ PORT = 21 | |||
32 | -- this is the default anonymous password. used when no password is | 29 | -- this is the default anonymous password. used when no password is |
33 | -- provided in url. should be changed to your e-mail. | 30 | -- provided in url. should be changed to your e-mail. |
34 | USER = "ftp" | 31 | USER = "ftp" |
35 | EMAIL = "anonymous@anonymous.org" | 32 | PASSWORD = "anonymous@anonymous.org" |
36 | -- block size used in transfers | ||
37 | BLOCKSIZE = 2048 | ||
38 | 33 | ||
39 | ----------------------------------------------------------------------------- | 34 | ----------------------------------------------------------------------------- |
40 | -- Low level FTP API | 35 | -- Low level FTP API |
@@ -42,7 +37,7 @@ BLOCKSIZE = 2048 | |||
42 | local metat = { __index = {} } | 37 | local metat = { __index = {} } |
43 | 38 | ||
44 | function open(server, port) | 39 | function open(server, port) |
45 | local tp = socket.try(socket.tp.connect(server, port or PORT)) | 40 | local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT)) |
46 | return setmetatable({tp = tp}, metat) | 41 | return setmetatable({tp = tp}, metat) |
47 | end | 42 | end |
48 | 43 | ||
@@ -51,14 +46,17 @@ local function port(portt) | |||
51 | end | 46 | end |
52 | 47 | ||
53 | local function pasv(pasvt) | 48 | local function pasv(pasvt) |
54 | return socket.connect(pasvt.ip, pasvt.port) | 49 | local data = socket.try(socket.tcp()) |
50 | socket.try(data:settimeout(TIMEOUT)) | ||
51 | socket.try(data:connect(pasvt.ip, pasvt.port)) | ||
52 | return data | ||
55 | end | 53 | end |
56 | 54 | ||
57 | function metat.__index:login(user, password) | 55 | function metat.__index:login(user, password) |
58 | socket.try(self.tp:command("user", user or USER)) | 56 | socket.try(self.tp:command("user", user or USER)) |
59 | local code, reply = socket.try(self.tp:check{"2..", 331}) | 57 | local code, reply = socket.try(self.tp:check{"2..", 331}) |
60 | if code == 331 then | 58 | if code == 331 then |
61 | socket.try(self.tp:command("pass", password or EMAIL)) | 59 | socket.try(self.tp:command("pass", password or PASSWORD)) |
62 | socket.try(self.tp:check("2..")) | 60 | socket.try(self.tp:check("2..")) |
63 | end | 61 | end |
64 | return 1 | 62 | return 1 |
@@ -104,6 +102,7 @@ function metat.__index:send(sendt) | |||
104 | socket.try(self.pasvt or self.portt, "need port or pasv first") | 102 | socket.try(self.pasvt or self.portt, "need port or pasv first") |
105 | if self.pasvt then data = socket.try(pasv(self.pasvt)) end | 103 | if self.pasvt then data = socket.try(pasv(self.pasvt)) end |
106 | local argument = sendt.argument or string.gsub(sendt.path, "^/", "") | 104 | local argument = sendt.argument or string.gsub(sendt.path, "^/", "") |
105 | if argument == "" then argument = nil end | ||
107 | local command = sendt.command or "stor" | 106 | local command = sendt.command or "stor" |
108 | socket.try(self.tp:command(command, argument)) | 107 | socket.try(self.tp:command(command, argument)) |
109 | local code, reply = socket.try(self.tp:check{"2..", "1.."}) | 108 | local code, reply = socket.try(self.tp:check{"2..", "1.."}) |
@@ -133,6 +132,7 @@ function metat.__index:receive(recvt) | |||
133 | socket.try(self.pasvt or self.portt, "need port or pasv first") | 132 | socket.try(self.pasvt or self.portt, "need port or pasv first") |
134 | if self.pasvt then data = socket.try(pasv(self.pasvt)) end | 133 | if self.pasvt then data = socket.try(pasv(self.pasvt)) end |
135 | local argument = recvt.argument or string.gsub(recvt.path, "^/", "") | 134 | local argument = recvt.argument or string.gsub(recvt.path, "^/", "") |
135 | if argument == "" then argument = nil end | ||
136 | local command = recvt.command or "retr" | 136 | local command = recvt.command or "retr" |
137 | socket.try(self.tp:command(command, argument)) | 137 | socket.try(self.tp:command(command, argument)) |
138 | local code = socket.try(self.tp:check{"1..", "2.."}) | 138 | local code = socket.try(self.tp:check{"1..", "2.."}) |
@@ -182,14 +182,14 @@ end | |||
182 | -- High level FTP API | 182 | -- High level FTP API |
183 | ----------------------------------------------------------------------------- | 183 | ----------------------------------------------------------------------------- |
184 | local function tput(putt) | 184 | local function tput(putt) |
185 | local ftp = socket.ftp.open(putt.host, putt.port) | 185 | local con = ftp.open(putt.host, putt.port) |
186 | ftp:greet() | 186 | con:greet() |
187 | ftp:login(putt.user, putt.password) | 187 | con:login(putt.user, putt.password) |
188 | if putt.type then ftp:type(putt.type) end | 188 | if putt.type then con:type(putt.type) end |
189 | ftp:pasv() | 189 | con:pasv() |
190 | ftp:send(putt) | 190 | con:send(putt) |
191 | ftp:quit() | 191 | con:quit() |
192 | return ftp:close() | 192 | return con:close() |
193 | end | 193 | end |
194 | 194 | ||
195 | local default = { | 195 | local default = { |
@@ -198,15 +198,16 @@ local default = { | |||
198 | } | 198 | } |
199 | 199 | ||
200 | local function parse(u) | 200 | local function parse(u) |
201 | local putt = socket.try(url.parse(u, default)) | 201 | local t = socket.try(url.parse(u, default)) |
202 | socket.try(putt.scheme == "ftp", "invalid scheme '" .. putt.scheme .. "'") | 202 | socket.try(t.scheme == "ftp", "invalid scheme '" .. t.scheme .. "'") |
203 | socket.try(putt.host, "invalid host") | 203 | socket.try(t.host, "invalid host") |
204 | local pat = "^type=(.)$" | 204 | local pat = "^type=(.)$" |
205 | if putt.params then | 205 | if t.params then |
206 | putt.type = socket.skip(2, string.find(putt.params, pat)) | 206 | t.type = socket.skip(2, string.find(t.params, pat)) |
207 | socket.try(putt.type == "a" or putt.type == "i") | 207 | socket.try(t.type == "a" or t.type == "i", |
208 | "invalid type '" .. t.type .. "'") | ||
208 | end | 209 | end |
209 | return putt | 210 | return t |
210 | end | 211 | end |
211 | 212 | ||
212 | local function sput(u, body) | 213 | local function sput(u, body) |
@@ -221,17 +222,17 @@ put = socket.protect(function(putt, body) | |||
221 | end) | 222 | end) |
222 | 223 | ||
223 | local function tget(gett) | 224 | local function tget(gett) |
224 | local ftp = socket.ftp.open(gett.host, gett.port) | 225 | local con = ftp.open(gett.host, gett.port) |
225 | ftp:greet() | 226 | con:greet() |
226 | ftp:login(gett.user, gett.password) | 227 | con:login(gett.user, gett.password) |
227 | if gett.type then ftp:type(gett.type) end | 228 | if gett.type then con:type(gett.type) end |
228 | ftp:pasv() | 229 | con:pasv() |
229 | ftp:receive(gett) | 230 | con:receive(gett) |
230 | ftp:quit() | 231 | con:quit() |
231 | return ftp:close() | 232 | return con:close() |
232 | end | 233 | end |
233 | 234 | ||
234 | local function sget(u, body) | 235 | local function sget(u) |
235 | local gett = parse(u) | 236 | local gett = parse(u) |
236 | local t = {} | 237 | local t = {} |
237 | gett.sink = ltn12.sink.table(t) | 238 | gett.sink = ltn12.sink.table(t) |
@@ -240,7 +241,7 @@ local function sget(u, body) | |||
240 | end | 241 | end |
241 | 242 | ||
242 | get = socket.protect(function(gett) | 243 | get = socket.protect(function(gett) |
243 | if type(gett) == "string" then return sget(gett, body) | 244 | if type(gett) == "string" then return sget(gett) |
244 | else return tget(gett) end | 245 | else return tget(gett) end |
245 | end) | 246 | end) |
246 | 247 | ||
diff --git a/src/http.lua b/src/http.lua index ebe6b54..129b562 100644 --- a/src/http.lua +++ b/src/http.lua | |||
@@ -7,7 +7,7 @@ | |||
7 | ----------------------------------------------------------------------------- | 7 | ----------------------------------------------------------------------------- |
8 | 8 | ||
9 | ----------------------------------------------------------------------------- | 9 | ----------------------------------------------------------------------------- |
10 | -- Load other required modules | 10 | -- Load required modules |
11 | ------------------------------------------------------------------------------- | 11 | ------------------------------------------------------------------------------- |
12 | local socket = require("socket") | 12 | local socket = require("socket") |
13 | local ltn12 = require("ltn12") | 13 | local ltn12 = require("ltn12") |
@@ -17,42 +17,68 @@ local url = require("url") | |||
17 | ----------------------------------------------------------------------------- | 17 | ----------------------------------------------------------------------------- |
18 | -- Setup namespace | 18 | -- Setup namespace |
19 | ------------------------------------------------------------------------------- | 19 | ------------------------------------------------------------------------------- |
20 | http = {} | 20 | _LOADED["http"] = getfenv(1) |
21 | -- make all module globals fall into namespace | ||
22 | setmetatable(http, { __index = _G }) | ||
23 | setfenv(1, http) | ||
24 | 21 | ||
25 | ----------------------------------------------------------------------------- | 22 | ----------------------------------------------------------------------------- |
26 | -- Program constants | 23 | -- Program constants |
27 | ----------------------------------------------------------------------------- | 24 | ----------------------------------------------------------------------------- |
28 | -- connection timeout in seconds | 25 | -- connection timeout in seconds |
29 | TIMEOUT = 60 | 26 | TIMEOUT = 4 |
30 | -- default port for document retrieval | 27 | -- default port for document retrieval |
31 | PORT = 80 | 28 | PORT = 80 |
32 | -- user agent field sent in request | 29 | -- user agent field sent in request |
33 | USERAGENT = socket.version | 30 | USERAGENT = socket.VERSION |
34 | -- block size used in transfers | 31 | -- block size used in transfers |
35 | BLOCKSIZE = 2048 | 32 | BLOCKSIZE = 2048 |
36 | 33 | ||
37 | ----------------------------------------------------------------------------- | 34 | ----------------------------------------------------------------------------- |
38 | -- Function return value selectors | 35 | -- Low level HTTP API |
39 | ----------------------------------------------------------------------------- | 36 | ----------------------------------------------------------------------------- |
40 | local function second(a, b) | 37 | local metat = { __index = {} } |
41 | return b | 38 | |
39 | function open(host, port) | ||
40 | local con = socket.try(socket.tcp()) | ||
41 | socket.try(con:settimeout(TIMEOUT)) | ||
42 | port = port or PORT | ||
43 | socket.try(con:connect(host, port)) | ||
44 | return setmetatable({ con = con }, metat) | ||
45 | end | ||
46 | |||
47 | function metat.__index:sendrequestline(method, uri) | ||
48 | local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) | ||
49 | return socket.try(self.con:send(reqline)) | ||
42 | end | 50 | end |
43 | 51 | ||
44 | local function third(a, b, c) | 52 | function metat.__index:sendheaders(headers) |
45 | return c | 53 | for i, v in pairs(headers) do |
54 | socket.try(self.con:send(i .. ": " .. v .. "\r\n")) | ||
55 | end | ||
56 | -- mark end of request headers | ||
57 | socket.try(self.con:send("\r\n")) | ||
58 | return 1 | ||
46 | end | 59 | end |
47 | 60 | ||
48 | local function receive_headers(reqt, respt, tmp) | 61 | function metat.__index:sendbody(headers, source, step) |
49 | local sock = tmp.sock | 62 | source = source or ltn12.source.empty() |
63 | step = step or ltn12.pump.step | ||
64 | -- if we don't know the size in advance, send chunked and hope for the best | ||
65 | local mode | ||
66 | if headers["content-length"] then mode = "keep-open" | ||
67 | else mode = "http-chunked" end | ||
68 | return socket.try(ltn12.pump.all(source, socket.sink(mode, self.con), step)) | ||
69 | end | ||
70 | |||
71 | function metat.__index:receivestatusline() | ||
72 | local status = socket.try(self.con:receive()) | ||
73 | local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) | ||
74 | return socket.try(tonumber(code), status) | ||
75 | end | ||
76 | |||
77 | function metat.__index:receiveheaders() | ||
50 | local line, name, value | 78 | local line, name, value |
51 | local headers = {} | 79 | local headers = {} |
52 | -- store results | ||
53 | respt.headers = headers | ||
54 | -- get first line | 80 | -- get first line |
55 | line = socket.try(sock:receive()) | 81 | line = socket.try(self.con:receive()) |
56 | -- headers go until a blank line is found | 82 | -- headers go until a blank line is found |
57 | while line ~= "" do | 83 | while line ~= "" do |
58 | -- get field-name and value | 84 | -- get field-name and value |
@@ -60,189 +86,137 @@ local function receive_headers(reqt, respt, tmp) | |||
60 | socket.try(name and value, "malformed reponse headers") | 86 | socket.try(name and value, "malformed reponse headers") |
61 | name = string.lower(name) | 87 | name = string.lower(name) |
62 | -- get next line (value might be folded) | 88 | -- get next line (value might be folded) |
63 | line = socket.try(sock:receive()) | 89 | line = socket.try(self.con:receive()) |
64 | -- unfold any folded values | 90 | -- unfold any folded values |
65 | while string.find(line, "^%s") do | 91 | while string.find(line, "^%s") do |
66 | value = value .. line | 92 | value = value .. line |
67 | line = socket.try(sock:receive()) | 93 | line = socket.try(self.con:receive()) |
68 | end | 94 | end |
69 | -- save pair in table | 95 | -- save pair in table |
70 | if headers[name] then headers[name] = headers[name] .. ", " .. value | 96 | if headers[name] then headers[name] = headers[name] .. ", " .. value |
71 | else headers[name] = value end | 97 | else headers[name] = value end |
72 | end | 98 | end |
99 | return headers | ||
73 | end | 100 | end |
74 | 101 | ||
75 | local function receive_body(reqt, respt, tmp) | 102 | function metat.__index:receivebody(headers, sink, step) |
76 | local sink = reqt.sink or ltn12.sink.null() | 103 | sink = sink or ltn12.sink.null() |
77 | local step = reqt.step or ltn12.pump.step | 104 | step = step or ltn12.pump.step |
78 | local source | 105 | local length = tonumber(headers["content-length"]) |
79 | local te = respt.headers["transfer-encoding"] | 106 | local TE = headers["transfer-encoding"] |
80 | if te and te ~= "identity" then | 107 | local mode |
81 | -- get by chunked transfer-coding of message body | 108 | if TE and TE ~= "identity" then mode = "http-chunked" |
82 | source = socket.source("http-chunked", tmp.sock) | 109 | elseif tonumber(headers["content-length"]) then mode = "by-length" |
83 | elseif tonumber(respt.headers["content-length"]) then | 110 | else mode = "default" end |
84 | -- get by content-length | 111 | return socket.try(ltn12.pump.all(socket.source(mode, self.con, length), |
85 | local length = tonumber(respt.headers["content-length"]) | 112 | sink, step)) |
86 | source = socket.source("by-length", tmp.sock, length) | ||
87 | else | ||
88 | -- get it all until connection closes | ||
89 | source = socket.source(tmp.sock) | ||
90 | end | ||
91 | socket.try(ltn12.pump.all(source, sink, step)) | ||
92 | end | 113 | end |
93 | 114 | ||
94 | local function send_headers(sock, headers) | 115 | function metat.__index:close() |
95 | -- send request headers | 116 | return self.con:close() |
96 | for i, v in pairs(headers) do | ||
97 | socket.try(sock:send(i .. ": " .. v .. "\r\n")) | ||
98 | end | ||
99 | -- mark end of request headers | ||
100 | socket.try(sock:send("\r\n")) | ||
101 | end | 117 | end |
102 | 118 | ||
103 | local function should_receive_body(reqt, respt, tmp) | 119 | ----------------------------------------------------------------------------- |
104 | if reqt.method == "HEAD" then return nil end | 120 | -- High level HTTP API |
105 | if respt.code == 204 or respt.code == 304 then return nil end | 121 | ----------------------------------------------------------------------------- |
106 | if respt.code >= 100 and respt.code < 200 then return nil end | 122 | local function uri(reqt) |
107 | return 1 | 123 | local u = reqt |
108 | end | 124 | if not reqt.proxy and not PROXY then |
109 | 125 | u = { | |
110 | local function receive_status(reqt, respt, tmp) | 126 | path = reqt.path, |
111 | local status = socket.try(tmp.sock:receive()) | 127 | params = reqt.params, |
112 | local code = third(string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) | 128 | query = reqt.query, |
113 | -- store results | 129 | fragment = reqt.fragment |
114 | respt.code, respt.status = tonumber(code), status | ||
115 | end | ||
116 | |||
117 | local function request_uri(reqt, respt, tmp) | ||
118 | local u = tmp.parsed | ||
119 | if not reqt.proxy then | ||
120 | local parsed = tmp.parsed | ||
121 | u = { | ||
122 | path = parsed.path, | ||
123 | params = parsed.params, | ||
124 | query = parsed.query, | ||
125 | fragment = parsed.fragment | ||
126 | } | 130 | } |
127 | end | 131 | end |
128 | return url.build(u) | 132 | return url.build(u) |
129 | end | 133 | end |
130 | 134 | ||
131 | local function send_request(reqt, respt, tmp) | 135 | local function adjustheaders(headers, host) |
132 | local uri = request_uri(reqt, respt, tmp) | ||
133 | local headers = tmp.headers | ||
134 | local step = reqt.step or ltn12.pump.step | ||
135 | -- send request line | ||
136 | socket.try(tmp.sock:send((reqt.method or "GET") | ||
137 | .. " " .. uri .. " HTTP/1.1\r\n")) | ||
138 | if reqt.source and not headers["content-length"] then | ||
139 | headers["transfer-encoding"] = "chunked" | ||
140 | end | ||
141 | send_headers(tmp.sock, headers) | ||
142 | -- send request message body, if any | ||
143 | if not reqt.source then return end | ||
144 | if headers["content-length"] then | ||
145 | socket.try(ltn12.pump.all(reqt.source, | ||
146 | socket.sink(tmp.sock), step)) | ||
147 | else | ||
148 | socket.try(ltn12.pump.all(reqt.source, | ||
149 | socket.sink("http-chunked", tmp.sock), step)) | ||
150 | end | ||
151 | end | ||
152 | |||
153 | local function open(reqt, respt, tmp) | ||
154 | local proxy = reqt.proxy or PROXY | ||
155 | local host, port | ||
156 | if proxy then | ||
157 | local pproxy = url.parse(proxy) | ||
158 | socket.try(pproxy.port and pproxy.host, "invalid proxy") | ||
159 | host, port = pproxy.host, pproxy.port | ||
160 | else | ||
161 | host, port = tmp.parsed.host, tmp.parsed.port | ||
162 | end | ||
163 | -- store results | ||
164 | tmp.sock = socket.try(socket.tcp()) | ||
165 | socket.try(tmp.sock:settimeout(reqt.timeout or TIMEOUT)) | ||
166 | socket.try(tmp.sock:connect(host, port)) | ||
167 | end | ||
168 | |||
169 | local function adjust_headers(reqt, respt, tmp) | ||
170 | local lower = {} | 136 | local lower = {} |
171 | -- override with user values | 137 | -- override with user values |
172 | for i,v in (reqt.headers or lower) do | 138 | for i,v in (headers or lower) do |
173 | lower[string.lower(i)] = v | 139 | lower[string.lower(i)] = v |
174 | end | 140 | end |
175 | lower["user-agent"] = lower["user-agent"] or USERAGENT | 141 | lower["user-agent"] = lower["user-agent"] or USERAGENT |
176 | -- these cannot be overriden | 142 | -- these cannot be overriden |
177 | lower["host"] = tmp.parsed.host | 143 | lower["host"] = host |
178 | lower["connection"] = "close" | 144 | return lower |
179 | -- store results | ||
180 | tmp.headers = lower | ||
181 | end | 145 | end |
182 | 146 | ||
183 | local function parse_url(reqt, respt, tmp) | 147 | local function adjustrequest(reqt) |
184 | -- parse url with default fields | 148 | -- parse url with default fields |
185 | local parsed = url.parse(reqt.url, { | 149 | local parsed = url.parse(reqt.url, { |
186 | host = "", | 150 | host = "", |
187 | port = PORT, | 151 | port = PORT, |
188 | path ="/", | 152 | path ="/", |
189 | scheme = "http" | 153 | scheme = "http" |
190 | }) | 154 | }) |
191 | -- scheme has to be http | 155 | -- explicit info in reqt overrides that given by the URL |
192 | socket.try(parsed.scheme == "http", | 156 | for i,v in reqt do parsed[i] = v end |
193 | string.format("unknown scheme '%s'", parsed.scheme)) | 157 | -- compute uri if user hasn't overriden |
194 | -- explicit authentication info overrides that given by the URL | 158 | parsed.uri = parsed.uri or uri(parsed) |
195 | parsed.user = reqt.user or parsed.user | 159 | -- adjust headers in request |
196 | parsed.password = reqt.password or parsed.password | 160 | parsed.headers = adjustheaders(parsed.headers, parsed.host) |
197 | -- store results | 161 | return parsed |
198 | tmp.parsed = parsed | ||
199 | end | 162 | end |
200 | 163 | ||
201 | -- forward declaration | 164 | local function shouldredirect(reqt, respt) |
202 | local request_p | 165 | return (reqt.redirect ~= false) and |
166 | (respt.code == 301 or respt.code == 302) and | ||
167 | (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") | ||
168 | and (not reqt.nredirects or reqt.nredirects < 5) | ||
169 | end | ||
203 | 170 | ||
204 | local function should_authorize(reqt, respt, tmp) | 171 | local function shouldauthorize(reqt, respt) |
205 | -- if there has been an authorization attempt, it must have failed | 172 | -- if there has been an authorization attempt, it must have failed |
206 | if reqt.headers and reqt.headers["authorization"] then return nil end | 173 | if reqt.headers and reqt.headers["authorization"] then return nil end |
207 | -- if last attempt didn't fail due to lack of authentication, | 174 | -- if last attempt didn't fail due to lack of authentication, |
208 | -- or we don't have authorization information, we can't retry | 175 | -- or we don't have authorization information, we can't retry |
209 | return respt.code == 401 and tmp.parsed.user and tmp.parsed.password | 176 | return respt.code == 401 and reqt.user and reqt.password |
210 | end | 177 | end |
211 | 178 | ||
212 | local function clone(headers) | 179 | local function shouldreceivebody(reqt, respt) |
213 | if not headers then return nil end | 180 | if reqt.method == "HEAD" then return nil end |
214 | local copy = {} | 181 | local code = respt.code |
215 | for i,v in pairs(headers) do | 182 | if code == 204 or code == 304 then return nil end |
216 | copy[i] = v | 183 | if code >= 100 and code < 200 then return nil end |
217 | end | 184 | return 1 |
218 | return copy | ||
219 | end | 185 | end |
220 | 186 | ||
221 | local function authorize(reqt, respt, tmp) | 187 | local requestp, authorizep, redirectp |
222 | local headers = clone(reqt.headers) or {} | 188 | |
223 | headers["authorization"] = "Basic " .. | 189 | function requestp(reqt) |
224 | (mime.b64(tmp.parsed.user .. ":" .. tmp.parsed.password)) | 190 | local reqt = adjustrequest(reqt) |
225 | local autht = { | 191 | local respt = {} |
226 | method = reqt.method, | 192 | local con = open(reqt.host, reqt.port) |
227 | url = reqt.url, | 193 | con:sendrequestline(reqt.method, reqt.uri) |
228 | source = reqt.source, | 194 | con:sendheaders(reqt.headers) |
229 | sink = reqt.sink, | 195 | con:sendbody(reqt.headers, reqt.source, reqt.step) |
230 | headers = headers, | 196 | respt.code, respt.status = con:receivestatusline() |
231 | timeout = reqt.timeout, | 197 | respt.headers = con:receiveheaders() |
232 | proxy = reqt.proxy, | 198 | if shouldredirect(reqt, respt) then |
233 | } | 199 | con:close() |
234 | request_p(autht, respt, tmp) | 200 | return redirectp(reqt, respt) |
201 | elseif shouldauthorize(reqt, respt) then | ||
202 | con:close() | ||
203 | return authorizep(reqt, respt) | ||
204 | elseif shouldreceivebody(reqt, respt) then | ||
205 | con:receivebody(respt.headers, reqt.sink, reqt.step) | ||
206 | end | ||
207 | con:close() | ||
208 | return respt | ||
235 | end | 209 | end |
236 | 210 | ||
237 | local function should_redirect(reqt, respt, tmp) | 211 | function authorizep(reqt, respt) |
238 | return (reqt.redirect ~= false) and | 212 | local auth = "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password)) |
239 | (respt.code == 301 or respt.code == 302) and | 213 | reqt.headers["authorization"] = auth |
240 | (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") | 214 | return requestp(reqt) |
241 | and (not tmp.nredirects or tmp.nredirects < 5) | ||
242 | end | 215 | end |
243 | 216 | ||
244 | local function redirect(reqt, respt, tmp) | 217 | function redirectp(reqt, respt) |
245 | tmp.nredirects = (tmp.nredirects or 0) + 1 | 218 | -- we create a new table to get rid of anything we don't |
219 | -- absolutely need, including authentication info | ||
246 | local redirt = { | 220 | local redirt = { |
247 | method = reqt.method, | 221 | method = reqt.method, |
248 | -- the RFC says the redirect URL has to be absolute, but some | 222 | -- the RFC says the redirect URL has to be absolute, but some |
@@ -251,69 +225,38 @@ local function redirect(reqt, respt, tmp) | |||
251 | source = reqt.source, | 225 | source = reqt.source, |
252 | sink = reqt.sink, | 226 | sink = reqt.sink, |
253 | headers = reqt.headers, | 227 | headers = reqt.headers, |
254 | timeout = reqt.timeout, | 228 | proxy = reqt.proxy, |
255 | proxy = reqt.proxy | 229 | nredirects = (reqt.nredirects or 0) + 1 |
256 | } | 230 | } |
257 | request_p(redirt, respt, tmp) | 231 | respt = requestp(redirt) |
258 | -- we pass the location header as a clue we redirected | 232 | -- we pass the location header as a clue we redirected |
259 | if respt.headers then respt.headers.location = redirt.url end | 233 | if respt.headers then respt.headers.location = redirt.url end |
260 | end | ||
261 | |||
262 | local function skip_continue(reqt, respt, tmp) | ||
263 | if respt.code == 100 then | ||
264 | receive_status(reqt, respt, tmp) | ||
265 | end | ||
266 | end | ||
267 | |||
268 | -- execute a request of through an exception | ||
269 | function request_p(reqt, respt, tmp) | ||
270 | parse_url(reqt, respt, tmp) | ||
271 | adjust_headers(reqt, respt, tmp) | ||
272 | open(reqt, respt, tmp) | ||
273 | send_request(reqt, respt, tmp) | ||
274 | receive_status(reqt, respt, tmp) | ||
275 | skip_continue(reqt, respt, tmp) | ||
276 | receive_headers(reqt, respt, tmp) | ||
277 | if should_redirect(reqt, respt, tmp) then | ||
278 | tmp.sock:close() | ||
279 | redirect(reqt, respt, tmp) | ||
280 | elseif should_authorize(reqt, respt, tmp) then | ||
281 | tmp.sock:close() | ||
282 | authorize(reqt, respt, tmp) | ||
283 | elseif should_receive_body(reqt, respt, tmp) then | ||
284 | receive_body(reqt, respt, tmp) | ||
285 | end | ||
286 | end | ||
287 | |||
288 | function request(reqt) | ||
289 | local respt, tmp = {}, {} | ||
290 | local s, e = pcall(request_p, reqt, respt, tmp) | ||
291 | if not s then respt.error = e end | ||
292 | if tmp.sock then tmp.sock:close() end | ||
293 | return respt | 234 | return respt |
294 | end | 235 | end |
295 | 236 | ||
296 | function get(u) | 237 | request = socket.protect(requestp) |
238 | |||
239 | get = socket.protect(function(u) | ||
297 | local t = {} | 240 | local t = {} |
298 | respt = request { | 241 | local respt = requestp { |
299 | url = u, | 242 | url = u, |
300 | sink = ltn12.sink.table(t) | 243 | sink = ltn12.sink.table(t) |
301 | } | 244 | } |
302 | return (table.getn(t) > 0 or nil) and table.concat(t), respt.headers, | 245 | return (table.getn(t) > 0 or nil) and table.concat(t), respt.headers, |
303 | respt.code, respt.error | 246 | respt.code |
304 | end | 247 | end) |
305 | 248 | ||
306 | function post(u, body) | 249 | post = socket.protect(function(u, body) |
307 | local t = {} | 250 | local t = {} |
308 | respt = request { | 251 | local respt = requestp { |
309 | url = u, | 252 | url = u, |
310 | method = "POST", | 253 | method = "POST", |
311 | source = ltn12.source.string(body), | 254 | source = ltn12.source.string(body), |
312 | sink = ltn12.sink.table(t), | 255 | sink = ltn12.sink.table(t), |
313 | headers = { ["content-length"] = string.len(body) } | 256 | headers = { ["content-length"] = string.len(body) } |
314 | } | 257 | } |
315 | return (table.getn(t) > 0 or nil) and table.concat(t), | 258 | return (table.getn(t) > 0 or nil) and table.concat(t), |
316 | respt.headers, respt.code, respt.error | 259 | respt.headers, respt.code |
317 | end | 260 | end) |
318 | 261 | ||
319 | return http | 262 | return http |
@@ -10,7 +10,6 @@ | |||
10 | #include <lua.h> | 10 | #include <lua.h> |
11 | #include <lauxlib.h> | 11 | #include <lauxlib.h> |
12 | 12 | ||
13 | #include "luasocket.h" | ||
14 | #include "inet.h" | 13 | #include "inet.h" |
15 | 14 | ||
16 | /*=========================================================================*\ | 15 | /*=========================================================================*\ |
diff --git a/src/ltn12.lua b/src/ltn12.lua index 41855f0..6228247 100644 --- a/src/ltn12.lua +++ b/src/ltn12.lua | |||
@@ -8,9 +8,8 @@ | |||
8 | ----------------------------------------------------------------------------- | 8 | ----------------------------------------------------------------------------- |
9 | -- Setup namespace | 9 | -- Setup namespace |
10 | ----------------------------------------------------------------------------- | 10 | ----------------------------------------------------------------------------- |
11 | local ltn12 = {} | 11 | _LOADED["ltn12"] = getfenv(1) |
12 | setmetatable(ltn12, { __index = _G }) | 12 | |
13 | setfenv(1, ltn12) | ||
14 | filter = {} | 13 | filter = {} |
15 | source = {} | 14 | source = {} |
16 | sink = {} | 15 | sink = {} |
@@ -19,10 +18,6 @@ pump = {} | |||
19 | -- 2048 seems to be better in windows... | 18 | -- 2048 seems to be better in windows... |
20 | BLOCKSIZE = 2048 | 19 | BLOCKSIZE = 2048 |
21 | 20 | ||
22 | local function shift(a, b, c) | ||
23 | return b, c | ||
24 | end | ||
25 | |||
26 | ----------------------------------------------------------------------------- | 21 | ----------------------------------------------------------------------------- |
27 | -- Filter stuff | 22 | -- Filter stuff |
28 | ----------------------------------------------------------------------------- | 23 | ----------------------------------------------------------------------------- |
@@ -53,7 +48,9 @@ local function chain2(f1, f2) | |||
53 | end | 48 | end |
54 | end) | 49 | end) |
55 | return function(chunk) | 50 | return function(chunk) |
56 | return shift(coroutine.resume(co, chunk)) | 51 | local ret, a, b = coroutine.resume(co, chunk) |
52 | if ret then return a, b | ||
53 | else return nil, a end | ||
57 | end | 54 | end |
58 | end | 55 | end |
59 | 56 | ||
@@ -149,7 +146,9 @@ function source.chain(src, f) | |||
149 | end | 146 | end |
150 | end) | 147 | end) |
151 | return function() | 148 | return function() |
152 | return shift(coroutine.resume(co)) | 149 | local ret, a, b = coroutine.resume(co) |
150 | if ret then return a, b | ||
151 | else return nil, a end | ||
153 | end | 152 | end |
154 | end | 153 | end |
155 | 154 | ||
@@ -166,7 +165,9 @@ function source.cat(...) | |||
166 | end | 165 | end |
167 | end) | 166 | end) |
168 | return function() | 167 | return function() |
169 | return shift(coroutine.resume(co)) | 168 | local ret, a, b = coroutine.resume(co) |
169 | if ret then return a, b | ||
170 | else return nil, a end | ||
170 | end | 171 | end |
171 | end | 172 | end |
172 | 173 | ||
diff --git a/src/luasocket.c b/src/luasocket.c index ca3a52c..2b0a1fa 100644 --- a/src/luasocket.c +++ b/src/luasocket.c | |||
@@ -26,7 +26,7 @@ | |||
26 | #include "luasocket.h" | 26 | #include "luasocket.h" |
27 | 27 | ||
28 | #include "auxiliar.h" | 28 | #include "auxiliar.h" |
29 | #include "base.h" | 29 | #include "except.h" |
30 | #include "timeout.h" | 30 | #include "timeout.h" |
31 | #include "buffer.h" | 31 | #include "buffer.h" |
32 | #include "inet.h" | 32 | #include "inet.h" |
@@ -35,11 +35,18 @@ | |||
35 | #include "select.h" | 35 | #include "select.h" |
36 | 36 | ||
37 | /*-------------------------------------------------------------------------*\ | 37 | /*-------------------------------------------------------------------------*\ |
38 | * Modules | 38 | * Internal function prototypes |
39 | \*-------------------------------------------------------------------------*/ | ||
40 | static int global_skip(lua_State *L); | ||
41 | static int global_unload(lua_State *L); | ||
42 | static int base_open(lua_State *L); | ||
43 | |||
44 | /*-------------------------------------------------------------------------*\ | ||
45 | * Modules and functions | ||
39 | \*-------------------------------------------------------------------------*/ | 46 | \*-------------------------------------------------------------------------*/ |
40 | static const luaL_reg mod[] = { | 47 | static const luaL_reg mod[] = { |
41 | {"auxiliar", aux_open}, | 48 | {"auxiliar", aux_open}, |
42 | {"base", base_open}, | 49 | {"except", except_open}, |
43 | {"timeout", tm_open}, | 50 | {"timeout", tm_open}, |
44 | {"buffer", buf_open}, | 51 | {"buffer", buf_open}, |
45 | {"inet", inet_open}, | 52 | {"inet", inet_open}, |
@@ -49,11 +56,69 @@ static const luaL_reg mod[] = { | |||
49 | {NULL, NULL} | 56 | {NULL, NULL} |
50 | }; | 57 | }; |
51 | 58 | ||
59 | static luaL_reg func[] = { | ||
60 | {"skip", global_skip}, | ||
61 | {"__unload", global_unload}, | ||
62 | {NULL, NULL} | ||
63 | }; | ||
64 | |||
65 | /*-------------------------------------------------------------------------*\ | ||
66 | * Skip a few arguments | ||
67 | \*-------------------------------------------------------------------------*/ | ||
68 | static int global_skip(lua_State *L) { | ||
69 | int amount = luaL_checkint(L, 1); | ||
70 | int ret = lua_gettop(L) - amount - 1; | ||
71 | return ret >= 0 ? ret : 0; | ||
72 | } | ||
73 | |||
74 | /*-------------------------------------------------------------------------*\ | ||
75 | * Unloads the library | ||
76 | \*-------------------------------------------------------------------------*/ | ||
77 | static int global_unload(lua_State *L) { | ||
78 | sock_close(); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | /*-------------------------------------------------------------------------*\ | ||
83 | * Setup basic stuff. | ||
84 | \*-------------------------------------------------------------------------*/ | ||
85 | static int base_open(lua_State *L) { | ||
86 | if (sock_open()) { | ||
87 | /* whoever is loading the library replaced the global environment | ||
88 | * with the namespace table */ | ||
89 | lua_pushvalue(L, LUA_GLOBALSINDEX); | ||
90 | /* make sure library is still "requirable" if initialized staticaly */ | ||
91 | lua_pushstring(L, "_LOADEDLIB"); | ||
92 | lua_gettable(L, -2); | ||
93 | lua_pushstring(L, LUASOCKET_LIBNAME); | ||
94 | lua_pushcfunction(L, (lua_CFunction) luaopen_socket); | ||
95 | lua_settable(L, -3); | ||
96 | lua_pop(L, 1); | ||
97 | #ifdef LUASOCKET_DEBUG | ||
98 | lua_pushstring(L, "DEBUG"); | ||
99 | lua_pushboolean(L, 1); | ||
100 | lua_rawset(L, -3); | ||
101 | #endif | ||
102 | /* make version string available to scripts */ | ||
103 | lua_pushstring(L, "VERSION"); | ||
104 | lua_pushstring(L, LUASOCKET_VERSION); | ||
105 | lua_rawset(L, -3); | ||
106 | /* export other functions */ | ||
107 | luaL_openlib(L, NULL, func, 0); | ||
108 | return 1; | ||
109 | } else { | ||
110 | lua_pushstring(L, "unable to initialize library"); | ||
111 | lua_error(L); | ||
112 | return 0; | ||
113 | } | ||
114 | } | ||
115 | |||
52 | /*-------------------------------------------------------------------------*\ | 116 | /*-------------------------------------------------------------------------*\ |
53 | * Initializes all library modules. | 117 | * Initializes all library modules. |
54 | \*-------------------------------------------------------------------------*/ | 118 | \*-------------------------------------------------------------------------*/ |
55 | LUASOCKET_API int luaopen_socket(lua_State *L) { | 119 | LUASOCKET_API int luaopen_socket(lua_State *L) { |
56 | int i; | 120 | int i; |
121 | base_open(L); | ||
57 | for (i = 0; mod[i].name; i++) mod[i].func(L); | 122 | for (i = 0; mod[i].name; i++) mod[i].func(L); |
58 | return 1; | 123 | return 1; |
59 | } | 124 | } |
diff --git a/src/luasocket.h b/src/luasocket.h index 716b7ff..6d30605 100644 --- a/src/luasocket.h +++ b/src/luasocket.h | |||
@@ -25,6 +25,7 @@ | |||
25 | /*-------------------------------------------------------------------------*\ | 25 | /*-------------------------------------------------------------------------*\ |
26 | * Initializes the library. | 26 | * Initializes the library. |
27 | \*-------------------------------------------------------------------------*/ | 27 | \*-------------------------------------------------------------------------*/ |
28 | #define LUASOCKET_LIBNAME "socket" | ||
28 | LUASOCKET_API int luaopen_socket(lua_State *L); | 29 | LUASOCKET_API int luaopen_socket(lua_State *L); |
29 | 30 | ||
30 | #endif /* LUASOCKET_H */ | 31 | #endif /* LUASOCKET_H */ |
@@ -76,7 +76,17 @@ static UC b64unbase[256]; | |||
76 | \*-------------------------------------------------------------------------*/ | 76 | \*-------------------------------------------------------------------------*/ |
77 | MIME_API int luaopen_mime(lua_State *L) | 77 | MIME_API int luaopen_mime(lua_State *L) |
78 | { | 78 | { |
79 | lua_newtable(L); | 79 | /* whoever is loading the library replaced the global environment |
80 | * with the namespace table */ | ||
81 | lua_pushvalue(L, LUA_GLOBALSINDEX); | ||
82 | /* make sure library is still "requirable" if initialized staticaly */ | ||
83 | lua_pushstring(L, "_LOADEDLIB"); | ||
84 | lua_gettable(L, -2); | ||
85 | lua_pushstring(L, MIME_LIBNAME); | ||
86 | lua_pushcfunction(L, (lua_CFunction) luaopen_mime); | ||
87 | lua_settable(L, -3); | ||
88 | lua_pop(L, 1); | ||
89 | /* export functions */ | ||
80 | luaL_openlib(L, NULL, func, 0); | 90 | luaL_openlib(L, NULL, func, 0); |
81 | /* initialize lookup tables */ | 91 | /* initialize lookup tables */ |
82 | qpsetup(qpclass, qpunbase); | 92 | qpsetup(qpclass, qpunbase); |
@@ -19,6 +19,7 @@ | |||
19 | #define MIME_API extern | 19 | #define MIME_API extern |
20 | #endif | 20 | #endif |
21 | 21 | ||
22 | #define MIME_LIBNAME "mime" | ||
22 | MIME_API int luaopen_mime(lua_State *L); | 23 | MIME_API int luaopen_mime(lua_State *L); |
23 | 24 | ||
24 | #endif /* MIME_H */ | 25 | #endif /* MIME_H */ |
diff --git a/src/mime.lua b/src/mime.lua index ecf310d..000404f 100644 --- a/src/mime.lua +++ b/src/mime.lua | |||
@@ -6,23 +6,15 @@ | |||
6 | ----------------------------------------------------------------------------- | 6 | ----------------------------------------------------------------------------- |
7 | 7 | ||
8 | ----------------------------------------------------------------------------- | 8 | ----------------------------------------------------------------------------- |
9 | -- Load MIME from dynamic library | ||
10 | -- Comment these lines if you are loading static | ||
11 | ----------------------------------------------------------------------------- | ||
12 | local open = assert(loadlib("mime", "luaopen_mime")) | ||
13 | local mime = assert(open()) | ||
14 | |||
15 | ----------------------------------------------------------------------------- | ||
16 | -- Load other required modules | 9 | -- Load other required modules |
17 | ----------------------------------------------------------------------------- | 10 | ----------------------------------------------------------------------------- |
11 | local mime = requirelib("mime", "luaopen_mime", getfenv(1)) | ||
18 | local ltn12 = require("ltn12") | 12 | local ltn12 = require("ltn12") |
19 | 13 | ||
20 | ----------------------------------------------------------------------------- | 14 | ----------------------------------------------------------------------------- |
21 | -- Setup namespace | 15 | -- Setup namespace |
22 | ----------------------------------------------------------------------------- | 16 | ----------------------------------------------------------------------------- |
23 | -- make all module globals fall into mime namespace | 17 | _LOADED["mime"] = mime |
24 | setmetatable(mime, { __index = _G }) | ||
25 | setfenv(1, mime) | ||
26 | 18 | ||
27 | -- encode, decode and wrap algorithm tables | 19 | -- encode, decode and wrap algorithm tables |
28 | encodet = {} | 20 | encodet = {} |
@@ -48,7 +40,7 @@ end | |||
48 | 40 | ||
49 | encodet['quoted-printable'] = function(mode) | 41 | encodet['quoted-printable'] = function(mode) |
50 | return ltn12.filter.cycle(qp, "", | 42 | return ltn12.filter.cycle(qp, "", |
51 | (mode == "binary") and "=0D=0A" or "\13\10") | 43 | (mode == "binary") and "=0D=0A" or "\r\n") |
52 | end | 44 | end |
53 | 45 | ||
54 | -- define the decoding filters | 46 | -- define the decoding filters |
diff --git a/src/select.c b/src/select.c index 1ebd82c..13f9d6e 100644 --- a/src/select.c +++ b/src/select.c | |||
@@ -9,26 +9,21 @@ | |||
9 | #include <lua.h> | 9 | #include <lua.h> |
10 | #include <lauxlib.h> | 10 | #include <lauxlib.h> |
11 | 11 | ||
12 | #include "luasocket.h" | ||
13 | #include "socket.h" | 12 | #include "socket.h" |
14 | #include "auxiliar.h" | ||
15 | #include "select.h" | 13 | #include "select.h" |
16 | 14 | ||
17 | /*=========================================================================*\ | 15 | /*=========================================================================*\ |
18 | * Internal function prototypes. | 16 | * Internal function prototypes. |
19 | \*=========================================================================*/ | 17 | \*=========================================================================*/ |
20 | static int meth_set(lua_State *L); | 18 | static int getfd(lua_State *L); |
21 | static int meth_isset(lua_State *L); | 19 | static int dirty(lua_State *L); |
22 | static int c_select(lua_State *L); | 20 | static int collect_fd(lua_State *L, int tab, int max_fd, int itab, fd_set *set); |
21 | static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set); | ||
22 | static void return_fd(lua_State *L, fd_set *set, int max_fd, | ||
23 | int itab, int tab, int start); | ||
24 | static void make_assoc(lua_State *L, int tab); | ||
23 | static int global_select(lua_State *L); | 25 | static int global_select(lua_State *L); |
24 | 26 | ||
25 | /* fd_set object methods */ | ||
26 | static luaL_reg set[] = { | ||
27 | {"set", meth_set}, | ||
28 | {"isset", meth_isset}, | ||
29 | {NULL, NULL} | ||
30 | }; | ||
31 | |||
32 | /* functions in library namespace */ | 27 | /* functions in library namespace */ |
33 | static luaL_reg func[] = { | 28 | static luaL_reg func[] = { |
34 | {"select", global_select}, | 29 | {"select", global_select}, |
@@ -36,22 +31,13 @@ static luaL_reg func[] = { | |||
36 | }; | 31 | }; |
37 | 32 | ||
38 | /*=========================================================================*\ | 33 | /*=========================================================================*\ |
39 | * Internal function prototypes. | 34 | * Exported functions |
40 | \*=========================================================================*/ | 35 | \*=========================================================================*/ |
41 | /*-------------------------------------------------------------------------*\ | 36 | /*-------------------------------------------------------------------------*\ |
42 | * Initializes module | 37 | * Initializes module |
43 | \*-------------------------------------------------------------------------*/ | 38 | \*-------------------------------------------------------------------------*/ |
44 | int select_open(lua_State *L) | 39 | int select_open(lua_State *L) { |
45 | { | 40 | luaL_openlib(L, NULL, func, 0); |
46 | /* get select auxiliar lua function from lua code and register | ||
47 | * pass it as an upvalue to global_select */ | ||
48 | #ifdef LUASOCKET_COMPILED | ||
49 | #include "select.lch" | ||
50 | #else | ||
51 | lua_dofile(L, "select.lua"); | ||
52 | #endif | ||
53 | luaL_openlib(L, NULL, func, 1); | ||
54 | aux_newclass(L, "select{fd_set}", set); | ||
55 | return 0; | 41 | return 0; |
56 | } | 42 | } |
57 | 43 | ||
@@ -61,64 +47,149 @@ int select_open(lua_State *L) | |||
61 | /*-------------------------------------------------------------------------*\ | 47 | /*-------------------------------------------------------------------------*\ |
62 | * Waits for a set of sockets until a condition is met or timeout. | 48 | * Waits for a set of sockets until a condition is met or timeout. |
63 | \*-------------------------------------------------------------------------*/ | 49 | \*-------------------------------------------------------------------------*/ |
64 | static int global_select(lua_State *L) | 50 | static int global_select(lua_State *L) { |
65 | { | 51 | int timeout, rtab, wtab, itab, max_fd, ret, ndirty; |
66 | fd_set *read_fd_set, *write_fd_set; | 52 | fd_set rset, wset; |
67 | /* make sure we have enough arguments (nil is the default) */ | 53 | FD_ZERO(&rset); FD_ZERO(&wset); |
68 | lua_settop(L, 3); | 54 | lua_settop(L, 3); |
69 | /* check timeout */ | 55 | timeout = lua_isnil(L, 3) ? -1 : (int)(luaL_checknumber(L, 3) * 1000); |
70 | if (!lua_isnil(L, 3) && !lua_isnumber(L, 3)) | 56 | lua_newtable(L); itab = lua_gettop(L); |
71 | luaL_argerror(L, 3, "number or nil expected"); | 57 | lua_newtable(L); rtab = lua_gettop(L); |
72 | /* select auxiliar lua function to be called comes first */ | 58 | lua_newtable(L); wtab = lua_gettop(L); |
73 | lua_pushvalue(L, lua_upvalueindex(1)); | 59 | max_fd = collect_fd(L, 1, -1, itab, &rset); |
74 | lua_insert(L, 1); | 60 | ndirty = check_dirty(L, 1, rtab, &rset); |
75 | /* pass fd_set objects */ | 61 | timeout = ndirty > 0? 0: timeout; |
76 | read_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set)); | 62 | max_fd = collect_fd(L, 2, max_fd, itab, &wset); |
77 | FD_ZERO(read_fd_set); | 63 | ret = sock_select(max_fd+1, &rset, &wset, NULL, timeout); |
78 | aux_setclass(L, "select{fd_set}", -1); | 64 | if (ret > 0 || (ret == 0 && ndirty > 0)) { |
79 | write_fd_set = (fd_set *) lua_newuserdata(L, sizeof(fd_set)); | 65 | return_fd(L, &rset, max_fd+1, itab, rtab, ndirty); |
80 | FD_ZERO(write_fd_set); | 66 | return_fd(L, &wset, max_fd+1, itab, wtab, 0); |
81 | aux_setclass(L, "select{fd_set}", -1); | 67 | make_assoc(L, rtab); |
82 | /* pass select auxiliar C function */ | 68 | make_assoc(L, wtab); |
83 | lua_pushcfunction(L, c_select); | 69 | return 2; |
84 | /* call select auxiliar lua function */ | 70 | } else if (ret == 0) { |
85 | lua_call(L, 6, 3); | 71 | lua_pushstring(L, "timeout"); |
86 | return 3; | 72 | return 3; |
73 | } else { | ||
74 | lua_pushnil(L); | ||
75 | lua_pushnil(L); | ||
76 | lua_pushstring(L, "error"); | ||
77 | return 3; | ||
78 | } | ||
87 | } | 79 | } |
88 | 80 | ||
89 | /*=========================================================================*\ | 81 | /*=========================================================================*\ |
90 | * Lua methods | 82 | * Internal functions |
91 | \*=========================================================================*/ | 83 | \*=========================================================================*/ |
92 | static int meth_set(lua_State *L) | 84 | static int getfd(lua_State *L) { |
93 | { | 85 | int fd = -1; |
94 | fd_set *set = (fd_set *) aux_checkclass(L, "select{fd_set}", 1); | 86 | lua_pushstring(L, "getfd"); |
95 | t_sock fd = (t_sock) lua_tonumber(L, 2); | 87 | lua_gettable(L, -2); |
96 | if (fd >= 0) FD_SET(fd, set); | 88 | if (!lua_isnil(L, -1)) { |
97 | return 0; | 89 | lua_pushvalue(L, -2); |
90 | lua_call(L, 1, 1); | ||
91 | if (lua_isnumber(L, -1)) | ||
92 | fd = lua_tonumber(L, -1); | ||
93 | } | ||
94 | lua_pop(L, 1); | ||
95 | return fd; | ||
98 | } | 96 | } |
99 | 97 | ||
100 | static int meth_isset(lua_State *L) | 98 | static int dirty(lua_State *L) { |
101 | { | 99 | int is = 0; |
102 | fd_set *set = (fd_set *) aux_checkclass(L, "select{fd_set}", 1); | 100 | lua_pushstring(L, "dirty"); |
103 | t_sock fd = (t_sock) lua_tonumber(L, 2); | 101 | lua_gettable(L, -2); |
104 | if (fd >= 0 && FD_ISSET(fd, set)) lua_pushnumber(L, 1); | 102 | if (!lua_isnil(L, -1)) { |
105 | else lua_pushnil(L); | 103 | lua_pushvalue(L, -2); |
106 | return 1; | 104 | lua_call(L, 1, 1); |
105 | is = lua_toboolean(L, -1); | ||
106 | } | ||
107 | lua_pop(L, 1); | ||
108 | return is; | ||
107 | } | 109 | } |
108 | 110 | ||
109 | /*=========================================================================*\ | 111 | static int collect_fd(lua_State *L, int tab, int max_fd, |
110 | * Internal functions | 112 | int itab, fd_set *set) { |
111 | \*=========================================================================*/ | 113 | int i = 1; |
112 | static int c_select(lua_State *L) | 114 | if (lua_isnil(L, tab)) |
113 | { | 115 | return max_fd; |
114 | int max_fd = (int) lua_tonumber(L, 1); | 116 | while (1) { |
115 | fd_set *read_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 2); | 117 | int fd; |
116 | fd_set *write_fd_set = (fd_set *) aux_checkclass(L, "select{fd_set}", 3); | 118 | lua_pushnumber(L, i); |
117 | int timeout = lua_isnil(L, 4) ? -1 : (int)(lua_tonumber(L, 4) * 1000); | 119 | lua_gettable(L, tab); |
118 | struct timeval tv; | 120 | if (lua_isnil(L, -1)) { |
119 | tv.tv_sec = timeout / 1000; | 121 | lua_pop(L, 1); |
120 | tv.tv_usec = (timeout % 1000) * 1000; | 122 | break; |
121 | lua_pushnumber(L, select(max_fd, read_fd_set, write_fd_set, NULL, | 123 | } |
122 | timeout < 0 ? NULL : &tv)); | 124 | fd = getfd(L); |
123 | return 1; | 125 | if (fd > 0) { |
126 | FD_SET(fd, set); | ||
127 | if (max_fd < fd) max_fd = fd; | ||
128 | lua_pushnumber(L, fd); | ||
129 | lua_pushvalue(L, -2); | ||
130 | lua_settable(L, itab); | ||
131 | } | ||
132 | lua_pop(L, 1); | ||
133 | i = i + 1; | ||
134 | } | ||
135 | return max_fd; | ||
136 | } | ||
137 | |||
138 | static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) { | ||
139 | int ndirty = 0, i = 1; | ||
140 | if (lua_isnil(L, tab)) | ||
141 | return 0; | ||
142 | while (1) { | ||
143 | int fd; | ||
144 | lua_pushnumber(L, i); | ||
145 | lua_gettable(L, tab); | ||
146 | if (lua_isnil(L, -1)) { | ||
147 | lua_pop(L, 1); | ||
148 | break; | ||
149 | } | ||
150 | fd = getfd(L); | ||
151 | if (fd > 0 && dirty(L)) { | ||
152 | lua_pushnumber(L, ++ndirty); | ||
153 | lua_pushvalue(L, -2); | ||
154 | lua_settable(L, dtab); | ||
155 | FD_CLR(fd, set); | ||
156 | } | ||
157 | lua_pop(L, 1); | ||
158 | i = i + 1; | ||
159 | } | ||
160 | return ndirty; | ||
161 | } | ||
162 | |||
163 | static void return_fd(lua_State *L, fd_set *set, int max_fd, | ||
164 | int itab, int tab, int start) { | ||
165 | int fd; | ||
166 | for (fd = 0; fd < max_fd; fd++) { | ||
167 | if (FD_ISSET(fd, set)) { | ||
168 | lua_pushnumber(L, ++start); | ||
169 | lua_pushnumber(L, fd); | ||
170 | lua_gettable(L, itab); | ||
171 | lua_settable(L, tab); | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | static void make_assoc(lua_State *L, int tab) { | ||
177 | int i = 1, atab; | ||
178 | lua_newtable(L); atab = lua_gettop(L); | ||
179 | while (1) { | ||
180 | lua_pushnumber(L, i); | ||
181 | lua_gettable(L, tab); | ||
182 | if (!lua_isnil(L, -1)) { | ||
183 | lua_pushnumber(L, i); | ||
184 | lua_pushvalue(L, -2); | ||
185 | lua_settable(L, atab); | ||
186 | lua_pushnumber(L, i); | ||
187 | lua_settable(L, atab); | ||
188 | } else { | ||
189 | lua_pop(L, 1); | ||
190 | break; | ||
191 | } | ||
192 | i = i+1; | ||
193 | } | ||
124 | } | 194 | } |
195 | |||
diff --git a/src/smtp.lua b/src/smtp.lua index 7ae99a5..dc80c35 100644 --- a/src/smtp.lua +++ b/src/smtp.lua | |||
@@ -7,15 +7,9 @@ | |||
7 | ----------------------------------------------------------------------------- | 7 | ----------------------------------------------------------------------------- |
8 | 8 | ||
9 | ----------------------------------------------------------------------------- | 9 | ----------------------------------------------------------------------------- |
10 | -- Load SMTP from dynamic library | 10 | -- Load required modules |
11 | -- Comment these lines if you are loading static | ||
12 | ----------------------------------------------------------------------------- | ||
13 | local open = assert(loadlib("smtp", "luaopen_smtp")) | ||
14 | local smtp = assert(open()) | ||
15 | |||
16 | ----------------------------------------------------------------------------- | ||
17 | -- Load other required modules | ||
18 | ----------------------------------------------------------------------------- | 11 | ----------------------------------------------------------------------------- |
12 | local smtp = requirelib("smtp") | ||
19 | local socket = require("socket") | 13 | local socket = require("socket") |
20 | local ltn12 = require("ltn12") | 14 | local ltn12 = require("ltn12") |
21 | local tp = require("tp") | 15 | local tp = require("tp") |
@@ -23,10 +17,10 @@ local tp = require("tp") | |||
23 | ----------------------------------------------------------------------------- | 17 | ----------------------------------------------------------------------------- |
24 | -- Setup namespace | 18 | -- Setup namespace |
25 | ----------------------------------------------------------------------------- | 19 | ----------------------------------------------------------------------------- |
26 | -- make all module globals fall into smtp namespace | 20 | _LOADED["smtp"] = smtp |
27 | setmetatable(smtp, { __index = _G }) | ||
28 | setfenv(1, smtp) | ||
29 | 21 | ||
22 | -- timeout for connection | ||
23 | TIMEOUT = 60 | ||
30 | -- default server used to send e-mails | 24 | -- default server used to send e-mails |
31 | SERVER = "localhost" | 25 | SERVER = "localhost" |
32 | -- default port | 26 | -- default port |
@@ -94,9 +88,7 @@ function metat.__index:send(mailt) | |||
94 | end | 88 | end |
95 | 89 | ||
96 | function open(server, port) | 90 | function open(server, port) |
97 | print(server or SERVER, port or PORT) | 91 | local tp = socket.try(tp.connect(server or SERVER, port or PORT, TIMEOUT)) |
98 | local tp, error = tp.connect(server or SERVER, port or PORT) | ||
99 | if not tp then return nil, error end | ||
100 | return setmetatable({tp = tp}, metat) | 92 | return setmetatable({tp = tp}, metat) |
101 | end | 93 | end |
102 | 94 | ||
@@ -121,7 +113,10 @@ local function send_multipart(mesgt) | |||
121 | coroutine.yield('content-type: multipart/mixed; boundary="' .. | 113 | coroutine.yield('content-type: multipart/mixed; boundary="' .. |
122 | bd .. '"\r\n\r\n') | 114 | bd .. '"\r\n\r\n') |
123 | -- send preamble | 115 | -- send preamble |
124 | if mesgt.body.preamble then coroutine.yield(mesgt.body.preamble) end | 116 | if mesgt.body.preamble then |
117 | coroutine.yield(mesgt.body.preamble) | ||
118 | coroutine.yield("\r\n") | ||
119 | end | ||
125 | -- send each part separated by a boundary | 120 | -- send each part separated by a boundary |
126 | for i, m in ipairs(mesgt.body) do | 121 | for i, m in ipairs(mesgt.body) do |
127 | coroutine.yield("\r\n--" .. bd .. "\r\n") | 122 | coroutine.yield("\r\n--" .. bd .. "\r\n") |
@@ -130,7 +125,10 @@ local function send_multipart(mesgt) | |||
130 | -- send last boundary | 125 | -- send last boundary |
131 | coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") | 126 | coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") |
132 | -- send epilogue | 127 | -- send epilogue |
133 | if mesgt.body.epilogue then coroutine.yield(mesgt.body.epilogue) end | 128 | if mesgt.body.epilogue then |
129 | coroutine.yield(mesgt.body.epilogue) | ||
130 | coroutine.yield("\r\n") | ||
131 | end | ||
134 | end | 132 | end |
135 | 133 | ||
136 | -- yield message body from a source | 134 | -- yield message body from a source |
@@ -183,12 +181,12 @@ end | |||
183 | -- set defaul headers | 181 | -- set defaul headers |
184 | local function adjust_headers(mesgt) | 182 | local function adjust_headers(mesgt) |
185 | local lower = {} | 183 | local lower = {} |
186 | for i,v in (mesgt or lower) do | 184 | for i,v in (mesgt.headers or lower) do |
187 | lower[string.lower(i)] = v | 185 | lower[string.lower(i)] = v |
188 | end | 186 | end |
189 | lower["date"] = lower["date"] or | 187 | lower["date"] = lower["date"] or |
190 | os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE) | 188 | os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE) |
191 | lower["x-mailer"] = lower["x-mailer"] or socket.version | 189 | lower["x-mailer"] = lower["x-mailer"] or socket.VERSION |
192 | -- this can't be overriden | 190 | -- this can't be overriden |
193 | lower["mime-version"] = "1.0" | 191 | lower["mime-version"] = "1.0" |
194 | mesgt.headers = lower | 192 | mesgt.headers = lower |
@@ -198,18 +196,22 @@ function message(mesgt) | |||
198 | adjust_headers(mesgt) | 196 | adjust_headers(mesgt) |
199 | -- create and return message source | 197 | -- create and return message source |
200 | local co = coroutine.create(function() send_message(mesgt) end) | 198 | local co = coroutine.create(function() send_message(mesgt) end) |
201 | return function() return socket.skip(1, coroutine.resume(co)) end | 199 | return function() |
200 | local ret, a, b = coroutine.resume(co) | ||
201 | if ret then return a, b | ||
202 | else return nil, a end | ||
203 | end | ||
202 | end | 204 | end |
203 | 205 | ||
204 | --------------------------------------------------------------------------- | 206 | --------------------------------------------------------------------------- |
205 | -- High level SMTP API | 207 | -- High level SMTP API |
206 | ----------------------------------------------------------------------------- | 208 | ----------------------------------------------------------------------------- |
207 | send = socket.protect(function(mailt) | 209 | send = socket.protect(function(mailt) |
208 | local smtp = socket.try(open(mailt.server, mailt.port)) | 210 | local con = open(mailt.server, mailt.port) |
209 | smtp:greet(mailt.domain) | 211 | con:greet(mailt.domain) |
210 | smtp:send(mailt) | 212 | con:send(mailt) |
211 | smtp:quit() | 213 | con:quit() |
212 | return smtp:close() | 214 | return con:close() |
213 | end) | 215 | end) |
214 | 216 | ||
215 | return smtp | 217 | return smtp |
diff --git a/src/socket.lua b/src/socket.lua index 418cd1b..9aa6437 100644 --- a/src/socket.lua +++ b/src/socket.lua | |||
@@ -7,8 +7,8 @@ | |||
7 | ----------------------------------------------------------------------------- | 7 | ----------------------------------------------------------------------------- |
8 | -- Load LuaSocket from dynamic library | 8 | -- Load LuaSocket from dynamic library |
9 | ----------------------------------------------------------------------------- | 9 | ----------------------------------------------------------------------------- |
10 | local open = assert(loadlib("luasocket", "luaopen_socket")) | 10 | local socket = requirelib("luasocket", "luaopen_socket", getfenv(1)) |
11 | local socket = assert(open()) | 11 | _LOADED["socket"] = socket |
12 | 12 | ||
13 | ----------------------------------------------------------------------------- | 13 | ----------------------------------------------------------------------------- |
14 | -- Auxiliar functions | 14 | -- Auxiliar functions |
@@ -116,18 +116,21 @@ socket.sourcet["by-length"] = function(sock, length) | |||
116 | end | 116 | end |
117 | 117 | ||
118 | socket.sourcet["until-closed"] = function(sock) | 118 | socket.sourcet["until-closed"] = function(sock) |
119 | local done | ||
119 | return setmetatable({ | 120 | return setmetatable({ |
120 | getfd = function() return sock:getfd() end, | 121 | getfd = function() return sock:getfd() end, |
121 | dirty = function() return sock:dirty() end | 122 | dirty = function() return sock:dirty() end |
122 | }, { | 123 | }, { |
123 | __call = ltn12.source.simplify(function() | 124 | __call = function() |
125 | if done then return nil end | ||
124 | local chunk, err, partial = sock:receive(socket.BLOCKSIZE) | 126 | local chunk, err, partial = sock:receive(socket.BLOCKSIZE) |
125 | if not err then return chunk | 127 | if not err then return chunk |
126 | elseif err == "closed" then | 128 | elseif err == "closed" then |
127 | sock:close() | 129 | sock:close() |
128 | return partial, ltn12.source.empty() | 130 | done = 1 |
131 | return partial | ||
129 | else return nil, err end | 132 | else return nil, err end |
130 | end) | 133 | end |
131 | }) | 134 | }) |
132 | end | 135 | end |
133 | 136 | ||
@@ -9,13 +9,10 @@ | |||
9 | #include <lua.h> | 9 | #include <lua.h> |
10 | #include <lauxlib.h> | 10 | #include <lauxlib.h> |
11 | 11 | ||
12 | #include "luasocket.h" | ||
13 | |||
14 | #include "auxiliar.h" | 12 | #include "auxiliar.h" |
15 | #include "socket.h" | 13 | #include "socket.h" |
16 | #include "inet.h" | 14 | #include "inet.h" |
17 | #include "options.h" | 15 | #include "options.h" |
18 | #include "base.h" | ||
19 | #include "tcp.h" | 16 | #include "tcp.h" |
20 | 17 | ||
21 | /*=========================================================================*\ | 18 | /*=========================================================================*\ |
@@ -41,7 +38,7 @@ static int meth_dirty(lua_State *L); | |||
41 | /* tcp object methods */ | 38 | /* tcp object methods */ |
42 | static luaL_reg tcp[] = { | 39 | static luaL_reg tcp[] = { |
43 | {"__gc", meth_close}, | 40 | {"__gc", meth_close}, |
44 | {"__tostring", base_meth_tostring}, | 41 | {"__tostring", aux_tostring}, |
45 | {"accept", meth_accept}, | 42 | {"accept", meth_accept}, |
46 | {"bind", meth_bind}, | 43 | {"bind", meth_bind}, |
47 | {"close", meth_close}, | 44 | {"close", meth_close}, |
diff --git a/src/timeout.c b/src/timeout.c index bd6c3b4..4f9a315 100644 --- a/src/timeout.c +++ b/src/timeout.c | |||
@@ -26,6 +26,14 @@ | |||
26 | #endif | 26 | #endif |
27 | #endif | 27 | #endif |
28 | 28 | ||
29 | /* min and max macros */ | ||
30 | #ifndef MIN | ||
31 | #define MIN(x, y) ((x) < (y) ? x : y) | ||
32 | #endif | ||
33 | #ifndef MAX | ||
34 | #define MAX(x, y) ((x) > (y) ? x : y) | ||
35 | #endif | ||
36 | |||
29 | /*=========================================================================*\ | 37 | /*=========================================================================*\ |
30 | * Internal function prototypes | 38 | * Internal function prototypes |
31 | \*=========================================================================*/ | 39 | \*=========================================================================*/ |
@@ -2,24 +2,28 @@ | |||
2 | -- Unified SMTP/FTP subsystem | 2 | -- Unified SMTP/FTP subsystem |
3 | -- LuaSocket toolkit. | 3 | -- LuaSocket toolkit. |
4 | -- Author: Diego Nehab | 4 | -- Author: Diego Nehab |
5 | -- Conforming to: RFC 2616, LTN7 | ||
6 | -- RCS ID: $Id$ | 5 | -- RCS ID: $Id$ |
7 | ----------------------------------------------------------------------------- | 6 | ----------------------------------------------------------------------------- |
8 | 7 | ||
9 | ----------------------------------------------------------------------------- | 8 | ----------------------------------------------------------------------------- |
10 | -- Load other required modules | 9 | -- Load required modules |
11 | ----------------------------------------------------------------------------- | 10 | ----------------------------------------------------------------------------- |
12 | local socket = require("socket") | 11 | local socket = require("socket") |
12 | local ltn12 = require("ltn12") | ||
13 | 13 | ||
14 | ----------------------------------------------------------------------------- | 14 | ----------------------------------------------------------------------------- |
15 | -- Setup namespace | 15 | -- Setup namespace |
16 | ----------------------------------------------------------------------------- | 16 | ----------------------------------------------------------------------------- |
17 | tp = {} | 17 | _LOADED["tp"] = getfenv(1) |
18 | setmetatable(tp, { __index = _G }) | ||
19 | setfenv(1, tp) | ||
20 | 18 | ||
19 | ----------------------------------------------------------------------------- | ||
20 | -- Program constants | ||
21 | ----------------------------------------------------------------------------- | ||
21 | TIMEOUT = 60 | 22 | TIMEOUT = 60 |
22 | 23 | ||
24 | ----------------------------------------------------------------------------- | ||
25 | -- Implementation | ||
26 | ----------------------------------------------------------------------------- | ||
23 | -- gets server reply (works for SMTP and FTP) | 27 | -- gets server reply (works for SMTP and FTP) |
24 | local function get_reply(control) | 28 | local function get_reply(control) |
25 | local code, current, sep | 29 | local code, current, sep |
@@ -37,7 +41,6 @@ local function get_reply(control) | |||
37 | -- reply ends with same code | 41 | -- reply ends with same code |
38 | until code == current and sep == " " | 42 | until code == current and sep == " " |
39 | end | 43 | end |
40 | print(reply) | ||
41 | return code, reply | 44 | return code, reply |
42 | end | 45 | end |
43 | 46 | ||
@@ -46,6 +49,7 @@ local metat = { __index = {} } | |||
46 | 49 | ||
47 | function metat.__index:check(ok) | 50 | function metat.__index:check(ok) |
48 | local code, reply = get_reply(self.control) | 51 | local code, reply = get_reply(self.control) |
52 | print(reply) | ||
49 | if not code then return nil, reply end | 53 | if not code then return nil, reply end |
50 | if type(ok) ~= "function" then | 54 | if type(ok) ~= "function" then |
51 | if type(ok) == "table" then | 55 | if type(ok) == "table" then |
@@ -103,11 +107,11 @@ function metat.__index:close() | |||
103 | end | 107 | end |
104 | 108 | ||
105 | -- connect with server and return control object | 109 | -- connect with server and return control object |
106 | function connect(host, port) | 110 | connect = socket.protect(function(host, port, timeout) |
107 | local control, err = socket.connect(host, port) | 111 | local control = socket.try(socket.tcp()) |
108 | if not control then return nil, err end | 112 | socket.try(control:settimeout(timeout or TIMEOUT)) |
109 | control:settimeout(TIMEOUT) | 113 | socket.try(control:connect(host, port)) |
110 | return setmetatable({control = control}, metat) | 114 | return setmetatable({control = control}, metat) |
111 | end | 115 | end) |
112 | 116 | ||
113 | return tp | 117 | return tp |
@@ -9,13 +9,10 @@ | |||
9 | #include <lua.h> | 9 | #include <lua.h> |
10 | #include <lauxlib.h> | 10 | #include <lauxlib.h> |
11 | 11 | ||
12 | #include "luasocket.h" | ||
13 | |||
14 | #include "auxiliar.h" | 12 | #include "auxiliar.h" |
15 | #include "socket.h" | 13 | #include "socket.h" |
16 | #include "inet.h" | 14 | #include "inet.h" |
17 | #include "options.h" | 15 | #include "options.h" |
18 | #include "base.h" | ||
19 | #include "udp.h" | 16 | #include "udp.h" |
20 | 17 | ||
21 | /*=========================================================================*\ | 18 | /*=========================================================================*\ |
@@ -51,7 +48,7 @@ static luaL_reg udp[] = { | |||
51 | {"close", meth_close}, | 48 | {"close", meth_close}, |
52 | {"setoption", meth_setoption}, | 49 | {"setoption", meth_setoption}, |
53 | {"__gc", meth_close}, | 50 | {"__gc", meth_close}, |
54 | {"__tostring", base_meth_tostring}, | 51 | {"__tostring", aux_tostring}, |
55 | {"getfd", meth_getfd}, | 52 | {"getfd", meth_getfd}, |
56 | {"setfd", meth_setfd}, | 53 | {"setfd", meth_setfd}, |
57 | {"dirty", meth_dirty}, | 54 | {"dirty", meth_dirty}, |
diff --git a/src/url.lua b/src/url.lua index 2441268..aac2a47 100644 --- a/src/url.lua +++ b/src/url.lua | |||
@@ -9,9 +9,7 @@ | |||
9 | ----------------------------------------------------------------------------- | 9 | ----------------------------------------------------------------------------- |
10 | -- Setup namespace | 10 | -- Setup namespace |
11 | ----------------------------------------------------------------------------- | 11 | ----------------------------------------------------------------------------- |
12 | local url = {} | 12 | _LOADED["url"] = getfenv(1) |
13 | setmetatable(url, { __index = _G }) | ||
14 | setfenv(1, url) | ||
15 | 13 | ||
16 | ----------------------------------------------------------------------------- | 14 | ----------------------------------------------------------------------------- |
17 | -- Encodes a string into its escaped hexadecimal representation | 15 | -- Encodes a string into its escaped hexadecimal representation |