Skip to content
Merged
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ script:
- lua -e "print(require 'cURL.utils'.find_ca_bundle())"
- lunit.sh run.lua
- lua test_pause02.c.lua
- lua test_multi_callback.lua
- lua test_multi_nested_callback.lua
# - lunit.sh test_easy.lua
# - lunit.sh test_safe.lua
# - lunit.sh test_form.lua
Expand Down
2 changes: 2 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ test_script:
- cd %APPVEYOR_BUILD_FOLDER%\test
- lua run.lua
- lua test_pause02.c.lua
- lua test_multi_callback.lua
- lua test_multi_nested_callback.lua

after_test:
- cd %APPVEYOR_BUILD_FOLDER%
Expand Down
2 changes: 1 addition & 1 deletion examples/cURLv3/uvwget.lua
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ end

on_curl_action = function(easy, fd, action)
local ok, err = pcall(function()
trace("CURL::SOCKET", easy, s, ACTION_NAMES[action] or action)
trace("CURL::SOCKET", easy, fd, ACTION_NAMES[action] or action)

local context = easy.data.context

Expand Down
7 changes: 5 additions & 2 deletions lakefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ PROJECT = 'cURL'

INITLAKEFILE()

DEFINES = L{DEFINES,
DEFINES = L{DEFINES,
IF(WINDOWS, 'DLL_EXPORT', '');
IF(DEBUG, 'LCURL_RESET_NULL_LUA', '');
}

cURL = c.shared{'lcurl',
Expand All @@ -18,7 +19,7 @@ cURL = c.shared{'lcurl',
target('build', cURL)

install = target('install', {
file.group{odir=LIBDIR; src = cURL };
file.group{odir=LIBDIR; src = cURL };
file.group{odir=LIBDIR; src = J("src", "lua") ; recurse = true };
file.group{odir=J(ROOT, 'examples'); src = 'examples'; recurse = true };
file.group{odir=TESTDIR; src = 'test'; recurse = true };
Expand All @@ -30,6 +31,8 @@ target('test', install, function()
run_test('test_form.lua')
run_test('test_pause02.c.lua')
run_test('test_curl.lua')
run_test('test_multi_callback.lua')
run_test('test_multi_nested_callback.lua')

if not test_summary() then
quit("test fail")
Expand Down
115 changes: 92 additions & 23 deletions src/lceasy.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "lcutils.h"
#include "lchttppost.h"
#include "lcshare.h"
#include "lcmulti.h"
#include <memory.h>

static const char *LCURL_ERROR_TAG = "LCURL_ERROR_TAG";
Expand All @@ -27,6 +28,33 @@ static const char *LCURL_EASY = LCURL_EASY_NAME;
# define LCURL_E_UNKNOWN_OPTION CURLE_UNKNOWN_TELNET_OPTION
#endif

/* Before call curl_XXX function which can call any callback
* need set Curren Lua thread pointer in easy/multi contexts.
* But it also possible that we already in callback call.
* E.g. `curl_easy_pause` function may be called from write callback.
* and it even may be called in different thread.
* ```Lua
* multi:add_handle(easy)
* easy:setopt_writefunction(function(...)
* coroutine.wrap(function() multi:add_handle(easy2) end)()
* end)
* ```
* So we have to restore previews Lua state in callback contexts.
* But if previews Lua state is NULL then we can just do not set it back.
* But set it to NULL make esier debug code.
*/
void lcurl__easy_assign_lua(lua_State *L, lcurl_easy_t *p, lua_State *value, int assign_multi){
if(p->multi && assign_multi){
lcurl__multi_assign_lua(L, p->multi, value, 1);
}
else{
p->L = value;
if(p->post){
p->post->L = value;
}
}
}

//{

int lcurl_easy_create(lua_State *L, int error_mode){
Expand All @@ -45,6 +73,7 @@ int lcurl_easy_create(lua_State *L, int error_mode){
p->magic = LCURL_EASY_MAGIC;
p->L = NULL;
p->post = NULL;
p->multi = 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;
Expand All @@ -66,16 +95,38 @@ int lcurl_easy_create(lua_State *L, int error_mode){

lcurl_easy_t *lcurl_geteasy_at(lua_State *L, int i){
lcurl_easy_t *p = (lcurl_easy_t *)lutil_checkudatap (L, i, LCURL_EASY);
luaL_argcheck (L, p != NULL, 1, LCURL_EASY_NAME" expected");
luaL_argcheck (L, p != NULL, 1, LCURL_EASY_NAME" object expected");
return p;
}

static int lcurl_easy_to_s(lua_State *L){
lcurl_easy_t *p = (lcurl_easy_t *)lutil_checkudatap (L, 1, LCURL_EASY);
lua_pushfstring(L, LCURL_EASY_NAME" (%p)", (void*)p);
return 1;
}

static int lcurl_easy_cleanup(lua_State *L){
lcurl_easy_t *p = lcurl_geteasy(L);
int i;

if(p->multi){
CURLMcode code = lcurl__multi_remove_handle(L, p->multi, p);

//! @todo what I can do if I can not remove it???
}

if(p->curl){
lua_State *curL;

// In my tests when I cleanup some easy handle.
// timerfunction called only for single multi handle.
curL = p->L; lcurl__easy_assign_lua(L, p, L, 1);
curl_easy_cleanup(p->curl);
#ifndef LCURL_RESET_NULL_LUA
if(curL != NULL)
#endif
lcurl__easy_assign_lua(L, p, curL, 1);

p->curl = NULL;
}

Expand Down Expand Up @@ -113,18 +164,20 @@ static int lcurl_easy_cleanup(lua_State *L){
static int lcurl_easy_perform(lua_State *L){
lcurl_easy_t *p = lcurl_geteasy(L);
CURLcode code;
lua_State *curL;
int top = 1;
lua_settop(L, top);

assert(p->rbuffer.ref == LUA_NOREF);

// store reference to current coroutine to callbacks
p->L = L;
if(p->post){
p->post->L = L;
}

// User should not call `perform` if handle assign to multi
curL = p->L; lcurl__easy_assign_lua(L, p, L, 0);
code = curl_easy_perform(p->curl);
#ifndef LCURL_RESET_NULL_LUA
if(curL != NULL)
#endif
lcurl__easy_assign_lua(L, p, curL, 0);

if(p->rbuffer.ref != LUA_NOREF){
luaL_unref(L, LCURL_LUA_REGISTRY, p->rbuffer.ref);
Expand Down Expand Up @@ -730,6 +783,7 @@ static size_t lcurl_write_callback_(lua_State*L,

static size_t lcurl_write_callback(char *ptr, size_t size, size_t nmemb, void *arg){
lcurl_easy_t *p = arg;
assert(NULL != p->L);
return lcurl_write_callback_(p->L, p, &p->wr, ptr, size, nmemb);
}

Expand Down Expand Up @@ -833,11 +887,13 @@ static size_t lcurl_easy_read_callback(char *buffer, size_t size, size_t nitems,
if(p->magic == LCURL_HPOST_STREAM_MAGIC){
return lcurl_hpost_read_callback(buffer, size, nitems, arg);
}
assert(NULL != p->L);
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;
assert(NULL != p->L);
return lcurl_read_callback(*p->L, &p->rd, &p->rbuffer, buffer, size, nitems);
}

Expand All @@ -855,6 +911,7 @@ static int lcurl_easy_set_READFUNCTION(lua_State *L){

static size_t lcurl_header_callback(char *ptr, size_t size, size_t nmemb, void *arg){
lcurl_easy_t *p = arg;
assert(NULL != p->L);
return lcurl_write_callback_(p->L, p, &p->hd, ptr, size, nmemb);
}

Expand All @@ -875,10 +932,12 @@ static int lcurl_xferinfo_callback(void *arg, curl_off_t dltotal, curl_off_t dln
{
lcurl_easy_t *p = arg;
lua_State *L = p->L;
int n, top, ret = 0;

int ret = 0;
int top = lua_gettop(L);
int n = lcurl_util_push_cb(L, &p->pr);
assert(NULL != p->L);

top = lua_gettop(L);
n = lcurl_util_push_cb(L, &p->pr);

lua_pushnumber( L, (lua_Number)dltotal );
lua_pushnumber( L, (lua_Number)dlnow );
Expand Down Expand Up @@ -1019,8 +1078,17 @@ static int lcurl_easy_getinfo(lua_State *L){

static int lcurl_easy_pause(lua_State *L){
lcurl_easy_t *p = lcurl_geteasy(L);
lua_State *curL;
int mask = luaL_checkint(L, 2);
CURLcode code = curl_easy_pause(p->curl, mask);
CURLcode code;

curL = p->L; lcurl__easy_assign_lua(L, p, L, 1);
code = curl_easy_pause(p->curl, mask);
#ifndef LCURL_RESET_NULL_LUA
if(curL != NULL)
#endif
lcurl__easy_assign_lua(L, p, curL, 1);

if(code != CURLE_OK){
return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code);
}
Expand Down Expand Up @@ -1082,19 +1150,20 @@ static const struct luaL_Reg lcurl_easy_methods[] = {
#include "lcinfoeasy.h"
#undef OPT_ENTRY

{ "pause", lcurl_easy_pause },
{ "reset", lcurl_easy_reset },
{ "setopt", lcurl_easy_setopt },
{ "getinfo", lcurl_easy_getinfo },
{ "unsetopt", lcurl_easy_unsetopt },
{ "escape", lcurl_easy_escape },
{ "unescape", lcurl_easy_unescape },
{ "perform", lcurl_easy_perform },
{ "close", lcurl_easy_cleanup },
{ "__gc", lcurl_easy_cleanup },

{ "setdata", lcurl_easy_setdata },
{ "getdata", lcurl_easy_getdata },
{ "pause", lcurl_easy_pause },
{ "reset", lcurl_easy_reset },
{ "setopt", lcurl_easy_setopt },
{ "getinfo", lcurl_easy_getinfo },
{ "unsetopt", lcurl_easy_unsetopt },
{ "escape", lcurl_easy_escape },
{ "unescape", lcurl_easy_unescape },
{ "perform", lcurl_easy_perform },
{ "close", lcurl_easy_cleanup },
{ "__gc", lcurl_easy_cleanup },
{ "__tostring", lcurl_easy_to_s },

{ "setdata", lcurl_easy_setdata },
{ "getdata", lcurl_easy_getdata },

{NULL,NULL}
};
Expand Down
6 changes: 6 additions & 0 deletions src/lceasy.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ enum {

#define LCURL_EASY_MAGIC 0xEA

typedef struct lcurl_multi_tag lcurl_multi_t;

typedef struct lcurl_easy_tag{
unsigned char magic;

Expand All @@ -44,6 +46,8 @@ typedef struct lcurl_easy_tag{

lcurl_hpost_t *post;

lcurl_multi_t *multi;

CURL *curl;
int storage;
int lists[LCURL_LIST_COUNT];
Expand All @@ -61,4 +65,6 @@ lcurl_easy_t *lcurl_geteasy_at(lua_State *L, int i);

void lcurl_easy_initlib(lua_State *L, int nup);

void lcurl__easy_assign_lua(lua_State *L, lcurl_easy_t *p, lua_State *value, int assign_multi);

#endif
8 changes: 7 additions & 1 deletion src/lchttppost.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,16 @@ int lcurl_hpost_create(lua_State *L, int error_mode){

lcurl_hpost_t *lcurl_gethpost_at(lua_State *L, int i){
lcurl_hpost_t *p = (lcurl_hpost_t *)lutil_checkudatap (L, i, LCURL_HTTPPOST);
luaL_argcheck (L, p != NULL, 1, LCURL_PREFIX"HTTPPost object expected");
luaL_argcheck (L, p != NULL, 1, LCURL_HTTPPOST_NAME" object expected");
return p;
}

static int lcurl_hpost_to_s(lua_State *L){
lcurl_hpost_t *p = (lcurl_hpost_t *)lutil_checkudatap (L, 1, LCURL_HTTPPOST);
lua_pushfstring(L, LCURL_HTTPPOST_NAME" (%p)", (void*)p);
return 1;
}

static int lcurl_hpost_add_content(lua_State *L){
// add_buffer(name, data, [type,] [headers])
lcurl_hpost_t *p = lcurl_gethpost(L);
Expand Down
Loading