diff --git a/.travis.yml b/.travis.yml index 977ea41..b251498 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,31 +2,45 @@ language: c sudo: false -env: - global: - - LUAROCKS=2.2.2 - matrix: - - LUA=lua5.1 - - LUA=lua5.2 - - LUA=lua5.3 - - LUA=luajit +matrix: + include: + - compiler: ": Lua51" + env: LUA="lua 5.1" + - compiler: ": Lua52" + env: LUA="lua 5.2" + - compiler: ": Lua53" + env: LUA="lua 5.3" + - compiler: ": LuaJIT20" + env: LUA="luajit 2.0" + - compiler: ": LuaJIT21" + env: LUA="luajit 2.1" + +cache: + directories: + - here + - $HOME/.cache/pip branches: only: - master before_install: - - source .travis/setenv_lua.sh + - export CC=gcc - pip install --user cpp-coveralls - - luarocks install luafilesystem --from=https://rocks.moonscript.org/dev - - luarocks install luacov-coveralls - - luarocks install lunitx - - luarocks install dkjson --deps-mode=none + - pip install --user hererocks + - hererocks here -r^ --$LUA + - export PATH=$PATH:$PWD/here/bin install: - luarocks make rockspecs/lua-curl-scm-0.rockspec CFLAGS="-O2 -fPIC -ftest-coverage -fprofile-arcs" LIBFLAG="-shared --coverage" -script: +before_script: + - luarocks show luacov-coveralls || luarocks install luacov-coveralls + - luarocks show lunitx || luarocks install lunitx + - luarocks show luafilesystem || luarocks install luafilesystem + - luarocks show dkjson || luarocks install dkjson --deps-mode=none + +script: - cd test - lua -e "print(require 'cURL.utils'.find_ca_bundle())" - lunit.sh test_easy.lua @@ -37,7 +51,7 @@ script: after_success: - coveralls -b .. -r .. --dump c.report.json - - luacov-coveralls -j c.report.json + - luacov-coveralls -j c.report.json -v notifications: email: diff --git a/.travis/platform.sh b/.travis/platform.sh deleted file mode 100644 index 7259a7d..0000000 --- a/.travis/platform.sh +++ /dev/null @@ -1,15 +0,0 @@ -if [ -z "${PLATFORM:-}" ]; then - PLATFORM=$TRAVIS_OS_NAME; -fi - -if [ "$PLATFORM" == "osx" ]; then - PLATFORM="macosx"; -fi - -if [ -z "$PLATFORM" ]; then - if [ "$(uname)" == "Linux" ]; then - PLATFORM="linux"; - else - PLATFORM="macosx"; - fi; -fi diff --git a/.travis/setenv_lua.sh b/.travis/setenv_lua.sh deleted file mode 100644 index 8d8c825..0000000 --- a/.travis/setenv_lua.sh +++ /dev/null @@ -1,3 +0,0 @@ -export PATH=${PATH}:$HOME/.lua:$HOME/.local/bin:${TRAVIS_BUILD_DIR}/install/luarocks/bin -bash .travis/setup_lua.sh -eval `$HOME/.lua/luarocks path` diff --git a/.travis/setup_lua.sh b/.travis/setup_lua.sh deleted file mode 100644 index 5908085..0000000 --- a/.travis/setup_lua.sh +++ /dev/null @@ -1,122 +0,0 @@ -#! /bin/bash - -# A script for setting up environment for travis-ci testing. -# Sets up Lua and Luarocks. -# LUA must be "lua5.1", "lua5.2" or "luajit". -# luajit2.0 - master v2.0 -# luajit2.1 - master v2.1 - -set -eufo pipefail - -LUAJIT_BASE="LuaJIT-2.0.3" - -source .travis/platform.sh - -LUA_HOME_DIR=$TRAVIS_BUILD_DIR/install/lua - -LR_HOME_DIR=$TRAVIS_BUILD_DIR/install/luarocks - -mkdir $HOME/.lua - -LUAJIT="no" - -if [ "$PLATFORM" == "macosx" ]; then - if [ "$LUA" == "luajit" ]; then - LUAJIT="yes"; - fi - if [ "$LUA" == "luajit2.0" ]; then - LUAJIT="yes"; - fi - if [ "$LUA" == "luajit2.1" ]; then - LUAJIT="yes"; - fi; -elif [ "$(expr substr $LUA 1 6)" == "luajit" ]; then - LUAJIT="yes"; -fi - -mkdir -p "$LUA_HOME_DIR" - -if [ "$LUAJIT" == "yes" ]; then - - if [ "$LUA" == "luajit" ]; then - curl http://luajit.org/download/$LUAJIT_BASE.tar.gz | tar xz; - else - git clone http://luajit.org/git/luajit-2.0.git $LUAJIT_BASE; - fi - - cd $LUAJIT_BASE - - if [ "$LUA" == "luajit2.1" ]; then - git checkout v2.1; - fi - - make && make install PREFIX="$LUA_HOME_DIR" - - if [ "$LUA" == "luajit2.1" ]; then - ln -s $LUA_HOME_DIR/bin/luajit-2.1.0-alpha $HOME/.lua/luajit - ln -s $LUA_HOME_DIR/bin/luajit-2.1.0-alpha $HOME/.lua/lua; - else - ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/luajit - ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/lua; - fi; - -else - - if [ "$LUA" == "lua5.1" ]; then - curl http://www.lua.org/ftp/lua-5.1.5.tar.gz | tar xz - cd lua-5.1.5; - elif [ "$LUA" == "lua5.2" ]; then - curl http://www.lua.org/ftp/lua-5.2.4.tar.gz | tar xz - cd lua-5.2.4; - elif [ "$LUA" == "lua5.3" ]; then - curl http://www.lua.org/ftp/lua-5.3.0.tar.gz | tar xz - cd lua-5.3.0; - fi - - make $PLATFORM - make INSTALL_TOP="$LUA_HOME_DIR" install; - - ln -s $LUA_HOME_DIR/bin/lua $HOME/.lua/lua - ln -s $LUA_HOME_DIR/bin/luac $HOME/.lua/luac; - -fi - -cd $TRAVIS_BUILD_DIR - -lua -v - -LUAROCKS_BASE=luarocks-$LUAROCKS - -curl --location http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz | tar xz - -cd $LUAROCKS_BASE - -if [ "$LUA" == "luajit" ]; then - ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR"; -elif [ "$LUA" == "luajit2.0" ]; then - ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR"; -elif [ "$LUA" == "luajit2.1" ]; then - ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.1" --prefix="$LR_HOME_DIR"; -else - ./configure --with-lua="$LUA_HOME_DIR" --prefix="$LR_HOME_DIR" -fi - -make build && make install - -ln -s $LR_HOME_DIR/bin/luarocks $HOME/.lua/luarocks - -cd $TRAVIS_BUILD_DIR - -luarocks --version - -rm -rf $LUAROCKS_BASE - -if [ "$LUAJIT" == "yes" ]; then - rm -rf $LUAJIT_BASE; -elif [ "$LUA" == "lua5.1" ]; then - rm -rf lua-5.1.5; -elif [ "$LUA" == "lua5.2" ]; then - rm -rf lua-5.2.4; -elif [ "$LUA" == "lua5.3" ]; then - rm -rf lua-5.3.0; -fi diff --git a/src/lceasy.c b/src/lceasy.c index 2819457..72b85b5 100644 --- a/src/lceasy.c +++ b/src/lceasy.c @@ -41,7 +41,10 @@ int lcurl_easy_create(lua_State *L, int error_mode){ p->err_mode = error_mode; if(!p->curl) return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, CURLE_FAILED_INIT); - p->L = L; + + p->magic = LCURL_EASY_MAGIC; + p->L = NULL; + p->post = NULL; p->storage = lcurl_storage_init(L); p->wr.cb_ref = p->wr.ud_ref = LUA_NOREF; p->rd.cb_ref = p->rd.ud_ref = LUA_NOREF; @@ -115,6 +118,12 @@ static int lcurl_easy_perform(lua_State *L){ assert(p->rbuffer.ref == LUA_NOREF); + // store reference to current coroutine to callbacks + p->L = L; + if(p->post){ + p->post->L = L; + } + code = curl_easy_perform(p->curl); if(p->rbuffer.ref != LUA_NOREF){ @@ -308,6 +317,8 @@ static int lcurl_easy_set_HTTPPOST(lua_State *L){ curl_easy_setopt(p->curl, CURLOPT_READFUNCTION, lcurl_hpost_read_callback); } + p->post = post; + lua_settop(L, 1); return 1; } @@ -420,6 +431,8 @@ static int lcurl_easy_unset_HTTPPOST(lua_State *L){ lcurl_storage_remove_i(L, p->storage, CURLOPT_HTTPPOST); } + p->post = NULL; + lua_settop(L, 1); return 1; } @@ -758,12 +771,15 @@ static size_t lcurl_read_callback(lua_State *L, static size_t lcurl_easy_read_callback(char *buffer, size_t size, size_t nitems, void *arg){ lcurl_easy_t *p = arg; + if(p->magic == LCURL_HPOST_STREAM_MAGIC){ + return lcurl_hpost_read_callback(buffer, size, nitems, arg); + } return lcurl_read_callback(p->L, &p->rd, &p->rbuffer, buffer, size, nitems); } static size_t lcurl_hpost_read_callback(char *buffer, size_t size, size_t nitems, void *arg){ lcurl_hpost_stream_t *p = arg; - return lcurl_read_callback(p->L, &p->rd, &p->rbuffer, buffer, size, nitems); + return lcurl_read_callback(*p->L, &p->rd, &p->rbuffer, buffer, size, nitems); } static int lcurl_easy_set_READFUNCTION(lua_State *L){ @@ -1035,9 +1051,8 @@ void lcurl_easy_initlib(lua_State *L, int nup){ /* Hack. We ensure that lcurl_easy_t and lcurl_hpost_stream_t compatiable for readfunction */ - LCURL_STATIC_ASSERT(offsetof(lcurl_easy_t, L) == offsetof(lcurl_hpost_stream_t, L)); - LCURL_STATIC_ASSERT(offsetof(lcurl_easy_t, rd) == offsetof(lcurl_hpost_stream_t, rd)); - LCURL_STATIC_ASSERT(offsetof(lcurl_easy_t, rbuffer) == offsetof(lcurl_hpost_stream_t, rbuffer)); + LCURL_STATIC_ASSERT(offsetof(lcurl_easy_t, magic) == offsetof(lcurl_hpost_stream_t, magic)); + LCURL_STATIC_ASSERT(sizeof(((lcurl_easy_t*)0)->magic) == sizeof(((lcurl_hpost_stream_t*)0)->magic)); if(!lutil_createmetap(L, LCURL_EASY, lcurl_easy_methods, nup)) lua_pop(L, nup); diff --git a/src/lceasy.h b/src/lceasy.h index ee28bb1..e64f2b4 100644 --- a/src/lceasy.h +++ b/src/lceasy.h @@ -13,6 +13,7 @@ #include "lcurl.h" #include "lcutils.h" +#include "lchttppost.h" #define LCURL_LST_INDEX(N) LCURL_##N##_LIST, #define LCURL_STR_INDEX(N) @@ -32,11 +33,17 @@ enum { #undef LCURL_LNG_INDEX #undef OPT_ENTRY +#define LCURL_EASY_MAGIC 0xEA + typedef struct lcurl_easy_tag{ + unsigned char magic; + lua_State *L; lcurl_callback_t rd; lcurl_read_buffer_t rbuffer; + lcurl_hpost_t *post; + CURL *curl; int storage; int lists[LCURL_LIST_COUNT]; diff --git a/src/lchttppost.c b/src/lchttppost.c index cebecbd..1ceff82 100644 --- a/src/lchttppost.c +++ b/src/lchttppost.c @@ -23,7 +23,8 @@ static lcurl_hpost_stream_t *lcurl_hpost_stream_add(lua_State *L, lcurl_hpost_t lcurl_hpost_stream_t *stream = malloc(sizeof(lcurl_hpost_stream_t)); if(!stream) return NULL; - stream->L = L; + stream->magic = LCURL_HPOST_STREAM_MAGIC; + stream->L = &p->L; stream->rbuffer.ref = LUA_NOREF; stream->rd.cb_ref = stream->rd.ud_ref = LUA_NOREF; stream->next = NULL; diff --git a/src/lchttppost.h b/src/lchttppost.h index cf618b8..02cace7 100644 --- a/src/lchttppost.h +++ b/src/lchttppost.h @@ -15,14 +15,19 @@ #include "lcutils.h" #include +#define LCURL_HPOST_STREAM_MAGIC 0xAA + typedef struct lcurl_hpost_stream_tag{ - lua_State *L; + unsigned char magic; + + lua_State **L; lcurl_callback_t rd; lcurl_read_buffer_t rbuffer; struct lcurl_hpost_stream_tag *next; }lcurl_hpost_stream_t; typedef struct lcurl_hpost_tag{ + lua_State *L; struct curl_httppost *post; struct curl_httppost *last; int storage; diff --git a/src/lcmulti.c b/src/lcmulti.c index 588507e..7aee895 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -118,6 +118,21 @@ static int lcurl_multi_perform(lua_State *L){ lcurl_multi_t *p = lcurl_getmulti(L); int running_handles = 0; CURLMcode code; + + lua_settop(L, 1); + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->h_ref); + lua_pushnil(L); + while(lua_next(L, 2)){ + lcurl_easy_t *e = lcurl_geteasy_at(L, -1); + e->L = L; + if(e->post){ + e->post->L = L; + } + lua_pop(L, 1); + } + + lua_settop(L, 1); + while((code = curl_multi_perform(p->curl, &running_handles)) == CURLM_CALL_MULTI_PERFORM); if(code != CURLM_OK){ lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, code); diff --git a/test/.luacov b/test/.luacov index 08b5c3b..e6e0a49 100644 --- a/test/.luacov +++ b/test/.luacov @@ -32,7 +32,7 @@ return { -- configuration for luacov-coveralls reporter coveralls = { pathcorrect = { - {"/usr/local/share/lua/5.[123]", "src/lua"}; + {"^.-[/\\]share[/\\]lua[/\\]5.%d", "src/lua"}; }, }, } \ No newline at end of file diff --git a/test/test_curl.lua b/test/test_curl.lua index 39c6892..cc5d2c7 100644 --- a/test/test_curl.lua +++ b/test/test_curl.lua @@ -63,7 +63,7 @@ function test_add_handle() end end - m = assert_equal(m, m:add_handle(next_easy())) + assert_equal(m, m:add_handle(next_easy())) for data, type, easy in m:iperform() do diff --git a/test/test_easy.lua b/test/test_easy.lua index 6efcbba..d11b6e2 100644 --- a/test/test_easy.lua +++ b/test/test_easy.lua @@ -64,6 +64,29 @@ local function strem(ch, n, m) return n, get_bin_by( (ch):rep(n), m) end +local function Stream(ch, n, m) + local size, reader + + local _stream = {} + + function _stream:read(...) + _stream.called_ctx = self + _stream.called_co = coroutine.running() + return reader(...) + end + + function _stream:size() + return size + end + + function _stream:reset() + size, reader = strem(ch, n, m) + return self + end + + return _stream:reset() +end + local ENABLE = true local _ENV = TEST_CASE'write_callback' if ENABLE then @@ -165,6 +188,31 @@ function test_write_pass_03() assert_equal(c, c:perform()) end +function test_write_coro() + local co1, co2 + local called + + co1 = coroutine.create(function() + c = assert(curl.easy{ + url = url; + writefunction = function() + called = coroutine.running() + return true + end + }) + coroutine.yield() + end) + + co2 = coroutine.create(function() + assert_equal(c, c:perform()) + end) + + coroutine.resume(co1) + coroutine.resume(co2) + + assert_equal(co2, called) +end + end local _ENV = TEST_CASE'progress_callback' if ENABLE then @@ -380,7 +428,7 @@ local _ENV = TEST_CASE'read_stream_callback' if ENABLE and is_curl_ge(7,30,0) th local url = "http://httpbin.org/post" -local c, f, t +local m, c, f, t local function json_data() return json.decode(table.concat(t)) @@ -399,17 +447,86 @@ end function teardown() if f then f:free() end if c then c:close() end - t, f, c = nil + if m then m:close() end + t, f, c, m = nil end function test() assert_equal(f, f:add_stream('SSSSS', strem('X', 128, 13))) assert_equal(c, c:setopt_httppost(f)) + + -- should be called only stream callback + local read_called + assert_equal(c, c:setopt_readfunction(function() + read_called = true + end)) + + assert_equal(c, c:perform()) + + assert_nil(read_called) + + assert_equal(200, c:getinfo_response_code()) + local data = assert_table(json_data()) + assert_table(data.form) + assert_equal(('X'):rep(128), data.form.SSSSS) +end + +function test_object() + local s = Stream('X', 128, 13) + + assert_equal(f, f:add_stream('SSSSS', s:size(), s)) + assert_equal(c, c:setopt_httppost(f)) assert_equal(c, c:perform()) + + assert_equal(s, s.called_ctx) + + assert_equal(200, c:getinfo_response_code()) + local data = assert_table(json_data()) + assert_table(data.form) + assert_equal(('X'):rep(128), data.form.SSSSS) +end + +function test_co_multi() + local s = Stream('X', 128, 13) + assert_equal(f, f:add_stream('SSSSS', s:size(), s)) + assert_equal(c, c:setopt_httppost(f)) + + m = assert(scurl.multi()) + assert_equal(m, m:add_handle(c)) + + co = coroutine.create(function() + while 1== m:perform() do end + end) + + coroutine.resume(co) + + assert_equal(co, s.called_co) + + assert_equal(200, c:getinfo_response_code()) + local data = assert_table(json_data()) + assert_table(data.form) + assert_equal(('X'):rep(128), data.form.SSSSS) +end + +function test_co() + local s = Stream('X', 128, 13) + + assert_equal(f, f:add_stream('SSSSS', s:size(), s)) + assert_equal(c, c:setopt_httppost(f)) + + co = coroutine.create(function() + assert_equal(c, c:perform()) + end) + + coroutine.resume(co) + + assert_equal(co, s.called_co) + assert_equal(200, c:getinfo_response_code()) local data = assert_table(json_data()) assert_table(data.form) assert_equal(('X'):rep(128), data.form.SSSSS) + end function test_abort_01()