diff --git a/src/l52util.c b/src/l52util.c index 5633f5d..3eb86d8 100644 --- a/src/l52util.c +++ b/src/l52util.c @@ -156,3 +156,11 @@ int64_t lutil_optint64(lua_State *L, int idx, int64_t v){ void lutil_pushnvalues(lua_State *L, int n){ for(;n;--n) lua_pushvalue(L, -n); } + +int lutil_is_null(lua_State *L, int i){ + return lua_islightuserdata(L, i) && 0 == lua_touserdata(L, i); +} + +void lutil_push_null(lua_State *L){ + lua_pushlightuserdata(L, (void*)0); +} diff --git a/src/l52util.h b/src/l52util.h index 4d6b2fe..14849fe 100644 --- a/src/l52util.h +++ b/src/l52util.h @@ -88,4 +88,8 @@ int64_t lutil_optint64(lua_State *L, int idx, int64_t v); void lutil_pushnvalues(lua_State *L, int n); +int lutil_is_null(lua_State *L, int i); + +void lutil_push_null(lua_State *L); + #endif diff --git a/src/lceasy.c b/src/lceasy.c index a85dc6b..a5b8685 100644 --- a/src/lceasy.c +++ b/src/lceasy.c @@ -344,16 +344,22 @@ static int lcurl_opt_set_off_(lua_State *L, int opt){ static int lcurl_opt_set_string_(lua_State *L, int opt, int store){ lcurl_easy_t *p = lcurl_geteasy(L); - CURLcode code; + CURLcode code; const char *value; - luaL_argcheck(L, lua_type(L, 2) == LUA_TSTRING, 2, "string expected"); + luaL_argcheck(L, lua_type(L, 2) == LUA_TSTRING || lutil_is_null(L, 2), 2, "string expected"); - code = curl_easy_setopt(p->curl, opt, lua_tostring(L, 2)); + value = lua_tostring(L, 2); + code = curl_easy_setopt(p->curl, opt, value); if(code != CURLE_OK){ return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); } - if(store)lcurl_storage_preserve_iv(L, p->storage, opt, 2); + if(store){ + if(value) + lcurl_storage_preserve_iv(L, p->storage, opt, 2); + else + lcurl_storage_remove_i(L, p->storage, opt); + } lua_settop(L, 1); return 1; @@ -365,7 +371,7 @@ static int lcurl_opt_set_slist_(lua_State *L, int opt, int list_no){ CURLcode code; int ref = p->lists[list_no]; - luaL_argcheck(L, list || lua_istable(L, 2), 2, "array expected"); + luaL_argcheck(L, list || lua_istable(L, 2) || lutil_is_null(L, 2), 2, "array expected"); if(ref != LUA_NOREF){ struct curl_slist *tmp = lcurl_storage_remove_slist(L, p->storage, ref); diff --git a/src/lcmime.c b/src/lcmime.c index 06c3550..33e12a9 100644 --- a/src/lcmime.c +++ b/src/lcmime.c @@ -135,7 +135,7 @@ int lcurl_mime_set_lua(lua_State *L, lcurl_mime_t *p, lua_State *v){ #define IS_NILORSTR(L, i) (lua_type(L, i) == LUA_TSTRING) || (lua_type(L, i) == LUA_TNIL) #define IS_TABLE(L, i) lua_type(L, i) == LUA_TTABLE -#define IS_FALSE(L, i) (lua_type(L, i) == LUA_TBOOLEAN) && (!lua_toboolean(L, i)) +#define IS_FALSE(L, i) ((lua_type(L, i) == LUA_TBOOLEAN) && (!lua_toboolean(L, i))) || lutil_is_null(L,i) #define IS_OPTSTR(L, i) (IS_FALSE(L, i)) || (IS_NILORSTR(L, i)) static int lutil_isarray(lua_State *L, int i){ @@ -512,13 +512,13 @@ static int lcurl_mime_part_headers(lua_State *L){ } else{ list = lcurl_util_to_slist(L, 2); - luaL_argcheck(L, list, 2, "array or nil expected"); + luaL_argcheck(L, list || IS_TABLE(L, 2), 2, "array or null expected"); } ret = curl_mime_headers(p->part, list, 1); if(ret != CURLE_OK){ - curl_slist_free_all(list); + if(list) curl_slist_free_all(list); return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, ret); } diff --git a/src/lcmulti.c b/src/lcmulti.c index 4bfc1e7..9485fca 100644 --- a/src/lcmulti.c +++ b/src/lcmulti.c @@ -424,16 +424,22 @@ static int lcurl_opt_set_string_array_(lua_State *L, int opt){ lcurl_multi_t *p = lcurl_getmulti(L); CURLMcode code; int n; - luaL_argcheck(L, lua_type(L, 2) == LUA_TTABLE, 2, "array expected"); - n = lua_rawlen(L, 2); + + if (lutil_is_null(L, 2)) { + n = 0; + } + else { + luaL_argcheck(L, lua_type(L, 2) == LUA_TTABLE, 2, "array expected"); + n = lua_rawlen(L, 2); + } + if(n == 0){ - char *val[] = {NULL}; - code = curl_multi_setopt(p->curl, opt, val); + code = curl_multi_setopt(p->curl, opt, 0); } else{ int i; - char const* *val = malloc(sizeof(char*) * (n + 1)); - if(!*val){ + char const**val = malloc(sizeof(char*) * (n + 1)); + if(!val){ return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_MULTI, CURLM_OUT_OF_MEMORY); } for(i = 1; i <= n; ++i){ diff --git a/src/lcoptshare.h b/src/lcoptshare.h index 298255d..95960bb 100644 --- a/src/lcoptshare.h +++ b/src/lcoptshare.h @@ -14,11 +14,14 @@ OPT_ENTRY(unshare, UNSHARE, LNG, 0 ) FLG_ENTRY( LOCK_DATA_COOKIE ) FLG_ENTRY( LOCK_DATA_DNS ) FLG_ENTRY( LOCK_DATA_SSL_SESSION ) +FLG_ENTRY( LOCK_DATA_CONNECT ) #ifdef OPT_ENTRY_IS_NULL # undef OPT_ENTRY +# undef OPT_ENTRY_IS_NULL #endif #ifdef FLG_ENTRY_IS_NULL # undef FLG_ENTRY +# undef FLG_ENTRY_IS_NULL #endif diff --git a/src/lcurl.c b/src/lcurl.c index 41b266d..507b0ee 100644 --- a/src/lcurl.c +++ b/src/lcurl.c @@ -270,6 +270,9 @@ static int luaopen_lcurl_(lua_State *L, const struct luaL_Reg *func){ lcurl_util_set_const(L, lcurl_flags); + lutil_push_null(L); + lua_setfield(L, -2, "null"); + return 1; } diff --git a/src/lcutils.c b/src/lcutils.c index e4428fb..cbf1865 100644 --- a/src/lcutils.c +++ b/src/lcutils.c @@ -171,8 +171,6 @@ int lcurl_set_callback(lua_State *L, lcurl_callback_t *c, int i, const char *met luaL_argcheck(L, !lua_isnoneornil(L, i), i, "no function present"); luaL_argcheck(L, (top < (i + 2)), i + 2, "no arguments expected"); - // if(top > (i + 1)) lua_settop(L, i + 1); // this for force ignore other arguments - assert((top == i)||(top == (i + 1))); if(c->ud_ref != LUA_NOREF){ @@ -185,6 +183,19 @@ int lcurl_set_callback(lua_State *L, lcurl_callback_t *c, int i, const char *met c->cb_ref = LUA_NOREF; } + if(lutil_is_null(L, i)){ + if(top == (i + 1)){ + // Do we can just ignore this? + luaL_argcheck(L, + lua_isnoneornil(L, i + 1) || lutil_is_null(L, i + 1) + ,i + 1, "no context allowed when set callback to null" + ); + } + lua_pop(L, top - i + 1); + + return 1; + } + if(lua_gettop(L) == (i + 1)){// function + context c->ud_ref = luaL_ref(L, LCURL_LUA_REGISTRY); c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY); diff --git a/test/test_easy.lua b/test/test_easy.lua index baef535..be9e08f 100644 --- a/test/test_easy.lua +++ b/test/test_easy.lua @@ -35,6 +35,8 @@ local POST_URL = "http://127.0.0.1:7090/post" local weak_ptr, gc_collect, is_curl_ge, read_file, stream, Stream, dump_request = utils.import('weak_ptr', 'gc_collect', 'is_curl_ge', 'read_file', 'stream', 'Stream', 'dump_request') +local null = curl.null + local ENABLE = true local _ENV = TEST_CASE'curl error' if ENABLE then @@ -151,9 +153,16 @@ function test_reset_write_callback() assert_equal(c, c:setopt_writefunction(f)) assert_equal(c, c:setopt_writefunction(f.write, f)) assert_equal(c, c:setopt_writefunction(print)) + assert_equal(c, c:setopt_writefunction(print, null)) + assert_equal(c, c:setopt_writefunction(null)) + assert_equal(c, c:setopt_writefunction(null, nil)) + assert_equal(c, c:setopt_writefunction(null, null)) assert_error(function()c:setopt_writefunction()end) assert_error(function()c:setopt_writefunction(nil)end) assert_error(function()c:setopt_writefunction(nil, f)end) + assert_error(function()c:setopt_writefunction(null, {})end) + assert_error(function()c:setopt_writefunction(print, {}, nil)end) + assert_error(function()c:setopt_writefunction(print, {}, null)end) end function test_write_pass_01() @@ -208,6 +217,38 @@ function test_write_coro() assert_equal(co2, called) end +function test_write_pass_null_context() + c = assert(curl.easy{ + url = GET_URL; + }) + + local context + assert_equal(c, c:setopt_writefunction(function(ctx) + context = ctx + return true + end, null)) + + assert_equal(c, c:perform()) + assert_equal(null, context) +end + +function test_write_pass_nil_context() + c = assert(curl.easy{ + url = GET_URL; + }) + + local context, called + assert_equal(c, c:setopt_writefunction(function(ctx) + context = ctx + called = true + return true + end, nil)) + + assert_equal(c, c:perform()) + assert_true(called) + assert_nil(context) +end + end local _ENV = TEST_CASE'progress_callback' if ENABLE then @@ -379,9 +420,14 @@ function test_reset_header_callback() assert_equal(c, c:setopt_headerfunction(f)) assert_equal(c, c:setopt_headerfunction(f.header, f)) assert_equal(c, c:setopt_headerfunction(print)) + assert_equal(c, c:setopt_headerfunction(null)) + assert_equal(c, c:setopt_headerfunction(null, nil)) + assert_equal(c, c:setopt_headerfunction(null, null)) assert_error(function()c:setopt_headerfunction()end) assert_error(function()c:setopt_headerfunction(nil)end) assert_error(function()c:setopt_headerfunction(nil, f)end) + assert_error(function()c:setopt_headerfunction(null, {})end) + assert_error(function()c:setopt_headerfunction(print, {}, nil)end) end function test_header_pass_01() @@ -1060,4 +1106,62 @@ end end +local _ENV = TEST_CASE'set_null' if ENABLE then + +local c, m + +function teardown() + if c then c:close() end + if m then m:close() end + m, c = nil +end + +function test_string() + c = curl.easy() + c:setopt_accept_encoding('gzip') + local body, headers = assert_string(dump_request(c)) + assert_match("Accept%-Encoding:%s*gzip", headers) + + c:setopt_accept_encoding(null) + body, headers = assert_string(dump_request(c)) + assert_not_match("Accept%-Encoding:%s*gzip", headers) +end + +function test_string_via_table() + c = curl.easy() + c:setopt_accept_encoding('gzip') + local body, headers = assert_string(dump_request(c)) + assert_match("Accept%-Encoding:%s*gzip", headers) + + c:setopt{ accept_encoding = null } + body, headers = assert_string(dump_request(c)) + assert_not_match("Accept%-Encoding:%s*gzip", headers) +end + +function test_slist() + c = curl.easy() + c:setopt_httpheader({'X-Custom: value'}) + c:setopt_httpheader(null) + local body, headers = assert_string(dump_request(c)) + assert_not_match("X%-Custom:%s*value\r\n", headers) +end + +function test_slist_via_table() + c = curl.easy() + c:setopt_httpheader({'X-Custom: value'}) + c:setopt{httpheader = null} + local body, headers = assert_string(dump_request(c)) + assert_not_match("X%-Custom:%s*value\r\n", headers) +end + +function test_multi_set_array() + m = curl.multi() + m:setopt_pipelining_site_bl{ + '127.0.0.1' + } + assert_equal(m, m:setopt_pipelining_site_bl(null)) +end + +end + RUN() diff --git a/test/test_mime.lua b/test/test_mime.lua index e7239f4..762d896 100644 --- a/test/test_mime.lua +++ b/test/test_mime.lua @@ -19,6 +19,8 @@ local weak_ptr, gc_collect, dump_mime_ = utils.import('weak_ptr', 'gc_collect', local GET_URL = 'http://127.0.0.1:7090/get' +local null = curl.null + local function is_freed(c) return not not string.find(tostring(c), '%(freed%)') end @@ -313,6 +315,15 @@ function test_unset_name() assert_not_match('Content%-Disposition:.-name="test"', info) end +function test_unset_name_by_null() + mime:addpart():data('hello', 'test/html', 'test'):name(null) + + local info = assert_string(dump_mime(mime)) + assert_match('\r\n\r\nhello', info) + assert_match('Content%-Type:%s+test/html', info) + assert_not_match('Content%-Disposition:.-name="test"', info) +end + function test_unset_type() mime:addpart():data('hello', 'test/html'):type(false) @@ -321,6 +332,14 @@ function test_unset_type() assert_not_match('Content%-Type:%s+test/html', info) end +function test_unset_type_by_null() + mime:addpart():data('hello', 'test/html'):type(null) + + local info = assert_string(dump_mime(mime)) + assert_match('\r\n\r\nhello', info) + assert_not_match('Content%-Type:%s+test/html', info) +end + function test_unset_headers() mime:addpart():data('hello', 'test/html',{ 'X-Custom-Header: hello' @@ -331,6 +350,26 @@ function test_unset_headers() assert_not_match('X%-Custom%-Header:%s*hello', info) end +function test_unset_headers_by_null() + mime:addpart():data('hello', 'test/html',{ + 'X-Custom-Header: hello' + }):headers(null) + + local info = assert_string(dump_mime(mime)) + assert_match('\r\n\r\nhello', info) + assert_not_match('X%-Custom%-Header:%s*hello', info) +end + +function test_unset_headers_by_empty_array() + mime:addpart():data('hello', 'test/html',{ + 'X-Custom-Header: hello' + }):headers({}) + + local info = assert_string(dump_mime(mime)) + assert_match('\r\n\r\nhello', info) + assert_not_match('X%-Custom%-Header:%s*hello', info) +end + function test_unset_data() mime:addpart():data('hello', 'test/html', 'test'):data(false) @@ -340,6 +379,15 @@ function test_unset_data() assert_match('Content%-Disposition:.-name="test"', info) end +function test_unset_data_by_null() + mime:addpart():data('hello', 'test/html', 'test'):data(null) + + local info = assert_string(dump_mime(mime)) + assert_not_match('\r\n\r\nhello', info) + assert_match('Content%-Type:%s+test/html', info) + assert_match('Content%-Disposition:.-name="test"', info) +end + function test_unset_data_type_1() local part = mime:addpart():data('hello', 'test/html', 'test', { 'X-Custom-Header: hello' @@ -352,6 +400,18 @@ function test_unset_data_type_1() assert_match('X%-Custom%-Header:%s*hello', info) end +function test_unset_data_type_1_by_null() + local part = mime:addpart():data('hello', 'test/html', 'test', { + 'X-Custom-Header: hello' + }):data('hello', null) + + local info = assert_string(dump_mime(mime)) + assert_match('\r\n\r\nhello', info) + assert_not_match('Content%-Type:%s+test/html', info) + assert_match('Content%-Disposition:.-name="test"', info) + assert_match('X%-Custom%-Header:%s*hello', info) +end + function test_unset_data_type_2() local part = mime:addpart():data('hello', 'test/html', 'test', { 'X-Custom-Header: hello' @@ -376,6 +436,18 @@ function test_unset_data_name_1() assert_match('X%-Custom%-Header:%s*hello', info) end +function test_unset_data_name_1_by_null() + local part = mime:addpart():data('hello', 'test/html', 'test', { + 'X-Custom-Header: hello' + }):data('hello', nil, null) + + local info = assert_string(dump_mime(mime)) + assert_match('\r\n\r\nhello', info) + assert_match('Content%-Type:%s+test/html', info) + assert_not_match('Content%-Disposition:.-name="test"', info) + assert_match('X%-Custom%-Header:%s*hello', info) +end + function test_unset_data_name_2() local part = mime:addpart():data('hello', 'test/html', 'test', { 'X-Custom-Header: hello' @@ -400,6 +472,18 @@ function test_unset_data_header() assert_not_match('X%-Custom%-Header:%s*hello', info) end +function test_unset_data_header_by_null() + local part = mime:addpart():data('hello', 'test/html', 'test', { + 'X-Custom-Header: hello' + }):data('hello', nil, nil, nil, null) + + local info = assert_string(dump_mime(mime)) + assert_match('\r\n\r\nhello', info) + assert_match('Content%-Type:%s+test/html', info) + assert_match('Content%-Disposition:.-name="test"', info) + assert_not_match('X%-Custom%-Header:%s*hello', info) +end + function test_unset_data_filename_1() local part = mime:addpart():data('hello', 'test/html', 'test', 'test.html', { 'X-Custom-Header: hello' @@ -409,7 +493,20 @@ function test_unset_data_filename_1() assert_match('\r\n\r\nhello', info) assert_match('Content%-Type:%s+test/html', info) assert_match('Content%-Disposition:.-%sname="test"', info) - assert_not_match('Content%-Disposition:.-%sname="test%.html"', info) + assert_not_match('Content%-Disposition:.-%sfilename="test%.html"', info) + assert_match('X%-Custom%-Header:%s*hello', info) +end + +function test_unset_data_filename_1_by_null() + local part = mime:addpart():data('hello', 'test/html', 'test', 'test.html', { + 'X-Custom-Header: hello' + }):data('hello', nil, nil, null) + + local info = assert_string(dump_mime(mime)) + assert_match('\r\n\r\nhello', info) + assert_match('Content%-Type:%s+test/html', info) + assert_match('Content%-Disposition:.-%sname="test"', info) + assert_not_match('Content%-Disposition:.-%sfilename="test%.html"', info) assert_match('X%-Custom%-Header:%s*hello', info) end