From d18fb3adcac8e76ed18e75409d71ff92793a06a7 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Wed, 27 Aug 2014 13:44:50 +0500 Subject: [PATCH] Implement basic multi iterface. --- README.md | 39 +++++++++++++++++++++++- doc/lcurl.ldoc | 49 ++++++++++++++++++++++++------ src/lcmulti.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/lcmulti.h | 1 + src/lcutils.c | 10 +++++++ src/lcutils.h | 2 ++ 6 files changed, 170 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4b8fa88..1610e2b 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,43 @@ curl:easy() ) :perform() :close() - +``` + +```Lua +-- Multi FTP Upload + +-- We get error E_LOGIN_DENIED for this operation +e1 = curl:easy() + :setopt_url("ftp://moteus:999999@127.0.0.1/test1.dat") + :setopt_upload(true) + :setopt_readfunction( + function(t) return table.remove(t) end, {"1111", "2222"} + ) + +e2 = curl:easy() + :setopt_url("ftp://moteus:123456@127.0.0.1/test2.dat") + :setopt_upload(true) + :setopt_readfunction(get_bin_by(("e"):rep(1000), 5)) + +m = curl:multi() +m:add_handle(e1) +m:add_handle(e2) + +while m:perform() > 0 do end + +while true do + h, ok, err = m:info_read() + if h == 0 then break end + + if h == e1 then + assert(ok == nil) + assert(err:name() == "LOGIN_DENIED") + assert(err:no() == curl.E_LOGIN_DENIED) + end + + if h == e2 then + assert(ok == true) + end +end ``` diff --git a/doc/lcurl.ldoc b/doc/lcurl.ldoc index 110a51e..22fb583 100644 --- a/doc/lcurl.ldoc +++ b/doc/lcurl.ldoc @@ -183,7 +183,7 @@ function setopt_writefunction() end --- Set writer function. -- --- This call same as easy:set_writefunction(writer.write, writer) +-- This call same as easy:setopt_writefunction(writer.write, writer) -- -- @tparam object writer -- @return[1] self @@ -205,7 +205,7 @@ function setopt_headerfunction() end --- Set header function. -- --- This call same as easy:set_headerfunction(writer.header, writer) +-- This call same as easy:setopt_headerfunction(writer.header, writer) -- -- @tparam object writer -- @return[1] self @@ -228,7 +228,7 @@ function setopt_readfunction() end --- Set reader function. -- --- This call same as easy:set_readfunction(reader.read, reader) +-- This call same as easy:setopt_readfunction(reader.read, reader) -- -- @tparam object reader -- @return[1] self @@ -252,17 +252,19 @@ function setopt_readfunction() end -- function setopt_progressfunction() end ---- Set reader function. +--- Set progress function. -- --- This call same as easy:set_readfunction(reader.read, reader) +-- This call same as easy:setopt_progressfunction(progress.progress, progress) -- --- @tparam object reader +-- @tparam object progress -- @return[1] self -- -function setopt_readfunction() end +function setopt_progressfunction() end --- Set HTTP multipart/formdata -- +-- Caller does not have to save data. +-- -- @tparam httpform data -- @return[1] self function setopt_httpform() end @@ -281,8 +283,37 @@ end -- do ---- End multi session --- +--- Add Easy object. +-- +-- Caller must ensure that easy object is alive until end of operation. +-- +-- @treturn multi self +-- @tparam easy handle +function add_handle() end + +--- Remove Easy object. +-- +-- @tparam easy handle +-- @treturn multi self +function remove_handle() end + +--- reads/writes available data from each easy handle. +-- +-- @treturn number handles number of active easy handles +function perfom() end + +--- Read multi stack informationals. +-- +-- @treturn[1] number 0 there no informationals +-- @treturn[2] easy handle +-- @treturn[2] boolean true +-- @treturn[3] easy handle +-- @treturn[3] nil +-- @treturn[3] error error code +function info_read() end + +--- End multi session. +-- function close() end end diff --git a/src/lcmulti.c b/src/lcmulti.c index 15940fe..83e826f 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -15,6 +15,9 @@ int lcurl_multi_create(lua_State *L, int error_mode){ lcurl_multi_t *p = lutil_newudatap(L, lcurl_multi_t, LCURL_MULTI); p->curl = curl_multi_init(); if(!p->curl) return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, CURLM_INTERNAL_ERROR); + p->err_mode = error_mode; + lcurl_util_new_weak_table(L, "v"); + p->h_ref = luaL_ref(L, LCURL_LUA_REGISTRY); return 1; } @@ -32,15 +35,89 @@ static int lcurl_multi_cleanup(lua_State *L){ p->curl = NULL; } + if(p->h_ref != LUA_NOREF){ + luaL_unref(L, LCURL_LUA_REGISTRY, p->h_ref); + p->h_ref = LUA_NOREF; + } + return 0; } +static int lcurl_multi_add_handle(lua_State *L){ + lcurl_multi_t *p = lcurl_getmulti(L); + lcurl_easy_t *e = lcurl_geteasy_at(L, 2); + CURLMcode code = curl_multi_add_handle(p->curl, e->curl); + if(code != CURLM_OK){ + lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); + } + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); + lua_pushvalue(L, 2); + lua_rawsetp(L, -2, e->curl); + lua_settop(L, 1); + return 1; +} + +static int lcurl_multi_remove_handle(lua_State *L){ + lcurl_multi_t *p = lcurl_getmulti(L); + lcurl_easy_t *e = lcurl_geteasy_at(L, 2); + CURLMcode code = curl_multi_remove_handle(p->curl, e->curl); + if(code != CURLM_OK){ + lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); + } + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); + lua_pushnil(L); + lua_rawsetp(L, -2, e->curl); + lua_settop(L, 1); + return 1; +} + +static int lcurl_multi_perform(lua_State *L){ + lcurl_multi_t *p = lcurl_getmulti(L); + int running_handles = 0; + CURLMcode code = curl_multi_perform(p->curl, &running_handles); + if(code != CURLM_OK){ + lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); + } + lua_pushnumber(L, running_handles); + return 1; +} + +static int lcurl_multi_info_read(lua_State *L){ + lcurl_multi_t *p = lcurl_getmulti(L); + int msgs_in_queue = 0; + CURLMsg *msg = curl_multi_info_read(p->curl, &msgs_in_queue); + lcurl_easy_t *e; + if(!msg){ + lua_pushnumber(L, msgs_in_queue); + return 1; + } + + if(msg->msg == CURLMSG_DONE){ + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); + lua_rawgetp(L, -1, msg->easy_handle); + e = lcurl_geteasy_at(L, -1); + if(msg->data.result == CURLE_OK){ + lua_pushboolean(L, 1); + return 2; + } + return 1 + lcurl_fail_ex(L, LCURL_ERROR_RETURN, LCURL_ERROR_EASY, msg->data.result); + } + + // @todo handle unknown message + lua_pushboolean(L, 0); + return 1; +} //} static const struct luaL_Reg lcurl_multi_methods[] = { - {"close", lcurl_multi_cleanup }, - {"__gc", lcurl_multi_cleanup }, + {"add_handle", lcurl_multi_add_handle }, + {"remove_handle", lcurl_multi_remove_handle }, + {"perform", lcurl_multi_perform }, + {"info_read", lcurl_multi_info_read }, + + {"close", lcurl_multi_cleanup }, + {"__gc", lcurl_multi_cleanup }, {NULL,NULL} }; diff --git a/src/lcmulti.h b/src/lcmulti.h index 455a6cc..9a2a252 100644 --- a/src/lcmulti.h +++ b/src/lcmulti.h @@ -7,6 +7,7 @@ typedef struct lcurl_multi_tag{ CURLM *curl; int err_mode; + int h_ref; }lcurl_multi_t; int lcurl_multi_create(lua_State *L, int error_mode); diff --git a/src/lcutils.c b/src/lcutils.c index aa95be7..a436e69 100644 --- a/src/lcutils.c +++ b/src/lcutils.c @@ -116,3 +116,13 @@ int lcurl_util_push_cb(lua_State *L, lcurl_callback_t *c){ return 1; } +int lcurl_util_new_weak_table(lua_State*L, const char *mode){ + int top = lua_gettop(L); + lua_newtable(L); + lua_newtable(L); + lua_pushstring(L, mode); + lua_setfield(L, -2, "__mode"); + lua_setmetatable(L,-2); + assert((top+1) == lua_gettop(L)); + return 1; +} diff --git a/src/lcutils.h b/src/lcutils.h index 06c7f4e..468be79 100644 --- a/src/lcutils.h +++ b/src/lcutils.h @@ -38,4 +38,6 @@ void lcurl_util_set_const(lua_State *L, const lcurl_const_t *reg); int lcurl_util_push_cb(lua_State *L, lcurl_callback_t *c); +int lcurl_util_new_weak_table(lua_State*L, const char *mode); + #endif