diff options
author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-06-15 06:24:00 +0000 |
---|---|---|
committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-06-15 06:24:00 +0000 |
commit | 58096449c6044b7aade5cd41cfd71c6bec1d273d (patch) | |
tree | 1814ffebe89c4c2556d84f97f66db37a7e8b4554 /src | |
parent | 9ed7f955e5fc69af9bf1794fa2c8cd227981ba24 (diff) | |
download | luasocket-58096449c6044b7aade5cd41cfd71c6bec1d273d.tar.gz luasocket-58096449c6044b7aade5cd41cfd71c6bec1d273d.tar.bz2 luasocket-58096449c6044b7aade5cd41cfd71c6bec1d273d.zip |
Manual is almost done. HTTP is missing.
Implemented new distribution scheme.
Select is now purely C.
HTTP reimplemented seems faster dunno why.
LTN12 functions that coroutines fail gracefully.
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 |