Add. `mime:easy()` method.

master
Alexey Melnichuk 2018-04-30 13:24:43 +03:00
parent d86d77b582
commit 58847b11ad
5 changed files with 143 additions and 80 deletions

View File

@ -7,7 +7,7 @@ shallow_clone: true
environment:
LR_EXTERNAL: c:\external
CURL_VER: 7.56.1
CURL_VER: 7.59.0
matrix:
- LUA: "lua 5.1"

View File

@ -14,6 +14,22 @@
#include "lcerror.h"
#include "lcutils.h"
/* API Notes.
* 1. Each mime can be root or child. If mime is a child (subpart) then curl free it
* when parent mime is freed or when remove this part from parent. There no way reuse same mime.
* Its not clear is it possible use mime created by one easy handle when do preform in another.
* `m=e1:mime() e2:setopt_httpmime(m) e1:close() e2:perform()`
*
* // Attach child to root (root also can have parent)
* curl_mime_subparts(root, child);
*
* // curl free `child` and all its childs
* curl_mime_subparts(root, other_child_or_null);
*
* // forbidden
* curl_mime_free(child);
*/
#if LCURL_CURL_VER_GE(7,56,0)
#define LCURL_MIME_NAME LCURL_PREFIX" MIME"
@ -22,6 +38,8 @@ static const char *LCURL_MIME = LCURL_MIME_NAME;
#define LCURL_MIME_PART_NAME LCURL_PREFIX" MIME Part"
static const char *LCURL_MIME_PART = LCURL_MIME_PART_NAME;
//{ Free mime and subparts
static void lcurl_mime_part_remove_subparts(lua_State *L, lcurl_mime_part_t *p, int free_it);
static lcurl_mime_t* lcurl_mime_part_get_subparts(lua_State *L, lcurl_mime_part_t *part){
@ -66,9 +84,45 @@ static int lcurl_mime_reset(lua_State *L, lcurl_mime_t *p){
p->parts = p->parent = NULL;
p->mime = NULL;
/* remove weak reference to easy */
lua_pushnil(L);
lua_rawsetp(L, LCURL_MIME_EASY, p);
return 0;
}
static void lcurl_mime_part_remove_subparts(lua_State *L, lcurl_mime_part_t *p, int free_it){
lcurl_mime_t *sub = lcurl_mime_part_get_subparts(L, p);
if(sub){
assert(LUA_NOREF != p->subpart_ref);
/* detach `subpart` mime from current mime part */
/* if set `sub->parent = NULL` then gc for mime will try free curl_mime_free. */
luaL_unref(L, LCURL_LUA_REGISTRY, p->subpart_ref);
p->subpart_ref = LUA_NOREF;
if(p->part && free_it){
curl_mime_subparts(p->part, NULL);
}
/* seems curl_mime_subparts(h, NULL) free asubparts.
so we have to invalidate all reference to all nested objects (part/mime).
NOTE. All resources already feed. So just need set all pointers to NULL
and free all Lua resources (like references and storages)
*/
{
lcurl_mime_part_t *ptr;
/* reset all parts*/
for(ptr = sub->parts; ptr; ptr=ptr->next){
lcurl_mime_part_remove_subparts(L, p, 0);
}
lcurl_mime_reset(L, sub);
}
}
}
//}
int lcurl_mime_set_lua(lua_State *L, lcurl_mime_t *p, lua_State *v){
lcurl_mime_part_t *part;
for(part = p->parts; part; part=part->next){
@ -86,6 +140,7 @@ int lcurl_mime_set_lua(lua_State *L, lcurl_mime_t *p, lua_State *v){
static int lutil_isarray(lua_State *L, int i){
int ret = 0;
i = lua_absindex(L, i);
lua_pushnil(L);
if(lua_next(L, i)){
ret = lua_isnumber(L, -2);
@ -94,7 +149,7 @@ static int lutil_isarray(lua_State *L, int i){
return ret;
}
static int lcurl_mime_part_assig(lua_State *L, int part, const char *method){
static int lcurl_mime_part_assign(lua_State *L, int part, const char *method){
int top = lua_gettop(L);
lua_pushvalue(L, part);
@ -107,7 +162,7 @@ static int lcurl_mime_part_assig(lua_State *L, int part, const char *method){
}
static const char *lcurl_mime_part_fields[] = {
"data", "filedata", "name", "filename", "headers", "encoder", NULL
"data", "filedata", "name", "filename", "headers", "encoder", "type", NULL
};
static int lcurl_mime_part_assing_table(lua_State *L, int part, int t){
@ -120,7 +175,7 @@ static int lcurl_mime_part_assing_table(lua_State *L, int part, int t){
if(lutil_isarray(L, t)){
int ret;
lua_pushvalue(L, t);
ret = lcurl_mime_part_assig(L, part, "headers");
ret = lcurl_mime_part_assign(L, part, "headers");
if(ret != 1) return ret;
lua_pop(L, 1);
@ -131,7 +186,7 @@ static int lcurl_mime_part_assing_table(lua_State *L, int part, int t){
for(i=0;method = lcurl_mime_part_fields[i]; ++i){
lua_getfield(L, t, method);
if(!lua_isnil(L, -1)){
int ret = lcurl_mime_part_assig(L, part, method);
int ret = lcurl_mime_part_assign(L, part, method);
if(ret != 1) return ret;
}
lua_pop(L, 1);
@ -141,8 +196,8 @@ static int lcurl_mime_part_assing_table(lua_State *L, int part, int t){
lua_getfield(L, t, "subparts");
if(!lua_isnil(L, -1)){
if(IS_FALSE(L, -1) || lcurl_getmimepart_at(L, -1)){
int ret = lcurl_mime_part_assig(L, part, "subparts");
if(IS_FALSE(L, -1) || lcurl_getmime_at(L, -1)){
int ret = lcurl_mime_part_assign(L, part, "subparts");
if(ret != 1) return ret;
}
}
@ -190,6 +245,10 @@ int lcurl_mime_create(lua_State *L, int error_mode){
p->err_mode = error_mode;
p->parts = p->parent = NULL;
/* weak reference from mime to easy handle */
lua_pushvalue(L, 1);
lua_rawsetp(L, LCURL_MIME_EASY, (void*)p);
return 1;
}
@ -242,6 +301,12 @@ static int lcurl_mime_addpart(lua_State *L){
return 1;
}
static int lcurl_mime_easy(lua_State *L){
lcurl_mime_t *p = lcurl_getmime(L);
lua_rawgetp(L, LCURL_MIME_EASY, p);
return 1;
}
//}
//{ MIME Part
@ -289,40 +354,6 @@ static int lcurl_mime_part_free(lua_State *L){
return 0;
}
static void lcurl_mime_part_remove_subparts(lua_State *L, lcurl_mime_part_t *p, int free_it){
lcurl_mime_t *sub = lcurl_mime_part_get_subparts(L, p);
if(sub){
assert(LUA_NOREF != p->subpart_ref);
/* detach `subpart` mime from current mime part */
/* if set `sub->parent = NULL` then gc for mime will try free curl_mime_free. */
/* So do not set it unless `curl_mime_subparts(p->part, NULL)` does not free mime */
luaL_unref(L, LCURL_LUA_REGISTRY, p->subpart_ref);
p->subpart_ref = LUA_NOREF;
if(p->part && free_it){
curl_mime_subparts(p->part, NULL);
}
/* issues #1961 */
/* seems curl_mime_subparts(h, NULL) free asubparts.
so we have to invalidate all reference to all nested objects (part/mime).
NOTE. All resources already feed. So just need set all pointers to NULL
and free all Lua resources (like references and storages)
*/
{
lcurl_mime_part_t *ptr;
/* reset all parts*/
for(ptr = sub->parts; ptr; ptr=ptr->next){
lcurl_mime_part_remove_subparts(L, p, 0);
}
lcurl_mime_reset(L, sub);
}
}
}
static int lcurl_mime_part_assing_ext(lua_State *L, int part, int i){
#define UNSET_VALUE (const char*)-1
@ -589,6 +620,7 @@ static int lcurl_mime_part_encoder(lua_State *L){
static const struct luaL_Reg lcurl_mime_methods[] = {
{"addpart", lcurl_mime_addpart },
{"easy", lcurl_mime_easy },
{"free", lcurl_mime_free },
{"__gc", lcurl_mime_free },
@ -646,6 +678,7 @@ void lcurl_mime_initlib(lua_State *L, int nup){
if(!lutil_createmetap(L, LCURL_MIME_PART, lcurl_mime_part_methods, nup))
lua_pop(L, nup);
lua_pop(L, 1);
#else
lua_pop(L, nup);
#endif

View File

@ -189,6 +189,21 @@ static volatile int LCURL_INIT = 0;
static const char* LCURL_REGISTRY = "LCURL Registry";
static const char* LCURL_USERVAL = "LCURL Uservalues";
#if LCURL_CURL_VER_GE(7,56,0)
static const char* LCURL_MIME_EASY_MAP = "LCURL Mime easy";
#endif
#if LCURL_CURL_VER_GE(7,56,0)
#define NUP 3
#else
#define NUP 2
#endif
#if LCURL_CURL_VER_GE(7,56,0)
#define LCURL_PUSH_NUP(L) lua_pushvalue(L, -NUP-1);lua_pushvalue(L, -NUP-1);lua_pushvalue(L, -NUP-1);
#else
#define LCURL_PUSH_NUP(L) lua_pushvalue(L, -NUP-1);lua_pushvalue(L, -NUP-1);
#endif
static int luaopen_lcurl_(lua_State *L, const struct luaL_Reg *func){
if(!LCURL_INIT){
@ -214,20 +229,32 @@ static int luaopen_lcurl_(lua_State *L, const struct luaL_Reg *func){
lcurl_util_new_weak_table(L, "k");
}
#if LCURL_CURL_VER_GE(7,56,0)
lua_rawgetp(L, LUA_REGISTRYINDEX, LCURL_MIME_EASY_MAP);
if(!lua_istable(L, -1)){ /* Mime->Easy */
lua_pop(L, 1);
lcurl_util_new_weak_table(L, "v");
}
#endif
lua_newtable(L); /* library */
lua_pushvalue(L, -3); lua_pushvalue(L, -3); luaL_setfuncs(L, func, 2);
lua_pushvalue(L, -3); lua_pushvalue(L, -3); lcurl_error_initlib(L, 2);
lua_pushvalue(L, -3); lua_pushvalue(L, -3); lcurl_hpost_initlib(L, 2);
lua_pushvalue(L, -3); lua_pushvalue(L, -3); lcurl_easy_initlib (L, 2);
lua_pushvalue(L, -3); lua_pushvalue(L, -3); lcurl_mime_initlib (L, 2);
lua_pushvalue(L, -3); lua_pushvalue(L, -3); lcurl_multi_initlib(L, 2);
lua_pushvalue(L, -3); lua_pushvalue(L, -3); lcurl_share_initlib(L, 2);
LCURL_PUSH_NUP(L); luaL_setfuncs(L, func, NUP);
LCURL_PUSH_NUP(L); lcurl_error_initlib(L, NUP);
LCURL_PUSH_NUP(L); lcurl_hpost_initlib(L, NUP);
LCURL_PUSH_NUP(L); lcurl_easy_initlib (L, NUP);
LCURL_PUSH_NUP(L); lcurl_mime_initlib (L, NUP);
LCURL_PUSH_NUP(L); lcurl_multi_initlib(L, NUP);
LCURL_PUSH_NUP(L); lcurl_share_initlib(L, NUP);
lua_pushvalue(L, -3); lua_rawsetp(L, LUA_REGISTRYINDEX, LCURL_REGISTRY);
lua_pushvalue(L, -2); lua_rawsetp(L, LUA_REGISTRYINDEX, LCURL_USERVAL);
LCURL_PUSH_NUP(L);
lua_remove(L, -2); /* registry */
#if LCURL_CURL_VER_GE(7,56,0)
lua_rawsetp(L, LUA_REGISTRYINDEX, LCURL_MIME_EASY_MAP);
#endif
lua_rawsetp(L, LUA_REGISTRYINDEX, LCURL_USERVAL);
lua_rawsetp(L, LUA_REGISTRYINDEX, LCURL_REGISTRY);
lcurl_util_set_const(L, lcurl_flags);

View File

@ -1,7 +1,7 @@
/******************************************************************************
* Author: Alexey Melnichuk <mimir@newmail.ru>
*
* Copyright (C) 2014 Alexey Melnichuk <mimir@newmail.ru>
* Copyright (C) 2014-2017 Alexey Melnichuk <mimir@newmail.ru>
*
* Licensed according to the included 'LICENSE' document
*
@ -25,4 +25,7 @@
#define LCURL_USERVALUES lua_upvalueindex(2)
/* only for `mime` API */
#define LCURL_MIME_EASY lua_upvalueindex(3)
#endif

View File

@ -235,17 +235,17 @@ function test_data()
end
function test_data_type()
mime:addpart():data('hello', 'text/html')
mime:addpart():data('hello', 'test/html')
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_match('Content%-Type:%s+text/html', info)
assert_match('Content%-Type:%s+test/html', info)
end
function test_data_type_name()
mime:addpart():data('hello', 'text/html', 'test')
mime:addpart():data('hello', 'test/html', 'test')
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_match('Content%-Type:%s+text/html', info)
assert_match('Content%-Type:%s+test/html', info)
assert_match('Content%-Disposition:.-%sname="test"', info)
end
@ -264,19 +264,19 @@ function test_data_filename()
end
function test_data_should_not_unset_on_nil()
local part = mime:addpart():data('hello', 'text/html', 'test', {
local part = mime:addpart():data('hello', 'test/html', 'test', {
'X-Custom-Header: hello'
})
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_match('Content%-Type:%s+text/html', info)
assert_match('Content%-Type:%s+test/html', info)
assert_match('Content%-Disposition:.-name="test"', info)
assert_match('X%-Custom%-Header:%s*hello', info)
part:data('world', nil, 'test2')
info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nworld', info)
assert_match('Content%-Type:%s+text/html', info)
assert_match('Content%-Type:%s+test/html', info)
assert_match('Content%-Disposition:.-name="test2"', info)
assert_match('X%-Custom%-Header:%s*hello', info)
@ -305,24 +305,24 @@ function test_data_headers()
end
function test_unset_name()
mime:addpart():data('hello', 'text/html', 'test'):name(false)
mime:addpart():data('hello', 'test/html', 'test'):name(false)
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_match('Content%-Type:%s+text/html', 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', 'text/html'):type(false)
mime:addpart():data('hello', 'test/html'):type(false)
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_not_match('Content%-Type:%s+text/html', info)
assert_not_match('Content%-Type:%s+test/html', info)
end
function test_unset_headers()
mime:addpart():data('hello', 'text/html',{
mime:addpart():data('hello', 'test/html',{
'X-Custom-Header: hello'
}):headers(false)
@ -332,82 +332,82 @@ function test_unset_headers()
end
function test_unset_data()
mime:addpart():data('hello', 'text/html', 'test'):data(false)
mime:addpart():data('hello', 'test/html', 'test'):data(false)
local info = assert_string(dump_mime(mime))
assert_not_match('\r\n\r\nhello', info)
assert_match('Content%-Type:%s+text/html', 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', 'text/html', 'test', {
local part = mime:addpart():data('hello', 'test/html', 'test', {
'X-Custom-Header: hello'
}):data('hello', false)
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_not_match('Content%-Type:%s+text/html', 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', 'text/html', 'test', {
local part = mime:addpart():data('hello', 'test/html', 'test', {
'X-Custom-Header: hello'
}):data('hello', false, nil, nil)
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_not_match('Content%-Type:%s+text/html', 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_name_1()
local part = mime:addpart():data('hello', 'text/html', 'test', {
local part = mime:addpart():data('hello', 'test/html', 'test', {
'X-Custom-Header: hello'
}):data('hello', nil, false)
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_match('Content%-Type:%s+text/html', 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', 'text/html', 'test', {
local part = mime:addpart():data('hello', 'test/html', 'test', {
'X-Custom-Header: hello'
}):data('hello', nil, false, nil)
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_match('Content%-Type:%s+text/html', 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_header()
local part = mime:addpart():data('hello', 'text/html', 'test', {
local part = mime:addpart():data('hello', 'test/html', 'test', {
'X-Custom-Header: hello'
}):data('hello', nil, nil, nil, false)
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_match('Content%-Type:%s+text/html', 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', 'text/html', 'test', 'test.html', {
local part = mime:addpart():data('hello', 'test/html', 'test', 'test.html', {
'X-Custom-Header: hello'
}):data('hello', nil, nil, false)
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\nhello', info)
assert_match('Content%-Type:%s+text/html', 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_match('X%-Custom%-Header:%s*hello', info)
@ -461,7 +461,7 @@ function test_pass_args()
encoder = 'base64';
name = 'test';
filename = 'test.html';
type = 'text/html';
type = 'test/html';
headers = {
'X-Custom-Header: hello';
}
@ -469,7 +469,7 @@ function test_pass_args()
local info = assert_string(dump_mime(mime))
assert_match('\r\n\r\naGVsbG8=', info)
assert_match('Content%-Type:%s+text/html', 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_match('X%-Custom%-Header:%s*hello', info)