luacurl源码阅读3 - binding函数(成员函数)

注册在curlT表下,可以看作curlT类的成员函数

static const struct luaL_reg luacurl_meths[] =
{
    {"close", lcurl_easy_close},
    {"setopt", lcurl_easy_setopt},
    {"perform", lcurl_easy_perform},
    {"getinfo", lcurl_easy_getinfo},
    {"__gc", lcurl_gc},
    {0, 0},
};

LUACURL_API int luaopen_luacurl (lua_State *L) 
{  
    //......

    createmeta(L);
    luaL_openlib (L, 0, luacurl_meths, 0); //将函数注册到curlT表下

    //......
    
    return 1; //栈顶弹出1个元素, 作为lua层拿到的返回值
}

 

lcurl_easy_getinfo函数

/* Request internal information from the curl session */
static int lcurl_easy_getinfo(lua_State* L)
{
    printf("lcurl_easy_getinfo begin: %d, %s\n", lua_gettop(L), lua_typename(L, lua_type(L, -1)) ); // 2, number

    curlT* c=tocurl(L, 1); //将栈底元素转换为curlT类型

    CURLINFO nInfo;
    CURLcode code=-1;
    luaL_checktype(L, 2, LUA_TNUMBER); /* accept info code number only, 函数参数只能是CURLINFO枚举值, CURLINFO_Xxx */
    nInfo=lua_tonumber(L, 2);
    if (nInfo>CURLINFO_SLIST) //信息结果是列表
    {
        /* string list */
        struct curl_slist *slist=0;
        if (CURLE_OK == (code=curl_easy_getinfo(c->curl, nInfo, &slist)))
        {
            if (slist)
            {
                /*相当于
                local infoTab = {}
                infoTab[i] = "xxx"
                */
                int i;
                lua_newtable(L); //这个是操作对象infoTab
                for (i=1; slist; i++, slist=slist->next)
                {
                    lua_pushnumber(L, i); //这个是key
                    lua_pushstring(L, slist->data); //这个是value
                    lua_settable(L, -3); //infoTab[key] = value, 执行完后栈顶弹出2个元素
                }
                curl_slist_free_all(slist);
            } else
            {
                lua_pushnil(L);
            }
            return 1; //栈顶弹出1个元素, 作为lua层拿到的返回值
        } else
        {
            /* curl_easy_getinfo returns error */
        }
    } else if (nInfo>CURLINFO_DOUBLE) //信息结果是double
    {
        /* double */
        double value;
        if (CURLE_OK == (code=curl_easy_getinfo(c->curl, nInfo, &value)))
        {
            lua_pushnumber(L, value);
            return 1;
        } else
        {
            /* curl_easy_getinfo returns error */
        }
    } else if (nInfo>CURLINFO_LONG) //信息结果是long
    {
        /* long */
        long value;
        if (CURLE_OK == (code=curl_easy_getinfo(c->curl, nInfo, &value)))
        {
            lua_pushinteger(L, (lua_Integer)value);
            return 1;
        } else
        {
            /* curl_easy_getinfo returns error */
        }
    } else if (nInfo>CURLINFO_STRING) //信息结果是string
    {
        /* string */
        char* value;
        if (CURLE_OK == (code=curl_easy_getinfo(c->curl, nInfo, &value)))
        {
            lua_pushstring(L, value);
            return 1;
        } else
        {
            /* curl_easy_getinfo returns error */
        }
    }

    /* on error */
    /* return nil, error message, error code */
    lua_pushnil(L);
    if (code>CURLE_OK)
    {
#if CURL_NEWER(7,11,2)
        lua_pushstring(L, curl_easy_strerror(code)); //根据错误码, 获取错误描述
#else
        lua_pushfstring(L, "Curl error: #%d", (code));
#endif
        lua_pushnumber(L, code);
        return 3; //栈顶弹出3个元素, 作为lua层拿到的返回值: nil, 错误描述, 错误码
    }
    else
    {
        lua_pushfstring(L, "Invalid CURLINFO number: %d", nInfo);
        return 2; //栈顶弹出3个元素, 作为lua层拿到的返回值: nil, 错误描述
    }
}

下面的lua代码,就会调用c层的lcurl_easy_getinfo

local curl = require("luacurl")

local obj = curl.new()
local size = obj:getinfo(curl.INFO_FILETIME)
print(size)

 

lcurl_gc函数

/* garbage collect the curl object */
static int lcurl_gc(lua_State* L)
{
    printf("lcurl_gc begin: %d, %s\n", lua_gettop(L), lua_typename(L, lua_type(L, -1)));

    curlT* c = (curlT*)luaL_checkudata(L, 1, CURLHANDLE); //尝试将栈底元素转换为curlT类型
    if (c && c->curl)
    {
        curl_easy_cleanup(c->curl);
    }

    printf("lcurl_gc end: %d, %s\n", lua_gettop(L), lua_typename(L, lua_type(L, -1)));

    return 0;
}

lua虚拟机回收lua对象时,会调用该函数

 

lcurl_easy_setopt函数

/* set any supported curl option (see also ALL_CURL_OPT) */
static int lcurl_easy_setopt(lua_State* L)
{
    //调用这个函数的时候, 需要保证栈上至少有3个元素: curl对象, CURL_OPT_名字, opt值
    printf("lcurl_easy_setopt begin: %d, %s\n", lua_gettop(L), lua_typename(L, lua_type(L, 1)) ); // 3, userdata

    union luaValueT v; /* the result option value, 会存放到curlT的xud字段上 */
    int curlOpt; /* the provided option code */
    CURLcode code; /* return error code from curl */

    curlT* c = tocurl(L, 1); /* get self object, 将栈底元素转换为curlT类型 */
    luaL_checktype(L, 2, LUA_TNUMBER); /* accept only number option codes, 检查CURL_OPT_名字参数类型是否合法 */
    if (lua_gettop(L)<3) /* option value is always required */
    {
        luaL_error(L, "Invalid number of arguments %d to `setopt' method", lua_gettop(L));
    }

    curlOpt=(int)lua_tonumber(L, 2); /* get the curl option code */
    v.nval=0;

    switch (curlOpt)
    {
        case CURLOPT_PROGRESSFUNCTION:
        case CURLOPT_READFUNCTION:
        case CURLOPT_WRITEFUNCTION:
        case CURLOPT_HEADERFUNCTION:
        case CURLOPT_IOCTLFUNCTION:
            luaL_checktype(L, 3, LUA_TFUNCTION); /* callback options require Lua function value */

        case CURLOPT_READDATA:
        case CURLOPT_WRITEDATA:
        case CURLOPT_PROGRESSDATA:
        case CURLOPT_HEADERDATA:
#if CURL_NEWER(7,12,3)
        case CURLOPT_IOCTLDATA: //io control data
#endif
            switch (lua_type(L, 3)) /* handle table, userdata and funtion callback params specially */
            {
                case LUA_TTABLE:
                case LUA_TUSERDATA:
                case LUA_TTHREAD:
                case LUA_TFUNCTION:
                {
                    int ref;
                    lua_pushvalue(L, 3); //栈[3]处的元素(即: lua层调用setopt的第2个参数)再压入栈顶
                    //弹出栈顶元素, 分配一个refId, 将弹出的元素存放在: 栈[LUA_REGISTRYINDEX][refId]处
                    ref=luaL_ref(L, LUA_REGISTRYINDEX); /* get reference to the lua object in registry */

                    if (curlOpt == CURLOPT_READFUNCTION)
                    {
                        //先将之前存放在栈[LUA_REGISTRYINDEX][freaderRef]处的元素置为nil
                        luaL_unref(L, LUA_REGISTRYINDEX, c->freaderRef); /* unregister previous reference to reader if any */
                        c->freaderRef=ref; /* keep the reader function reference in self */

                        v.rcb=(curl_read_callback)readerCallback; /* redirect the option value to readerCallback */
                        //CURLOPT_READDATA的值会在readerCallback的最后一个参数拿到
                        if (CURLE_OK != (code=curl_easy_setopt(c->curl, CURLOPT_READDATA, c)))
                            goto on_error;
                    }
                    else if (curlOpt == CURLOPT_WRITEFUNCTION)
                    {
                        luaL_unref(L, LUA_REGISTRYINDEX, c->fwriterRef);
                        c->fwriterRef=ref;

                        v.wcb=(curl_write_callback)writerCallback;
                        //CURLOPT_WRITEDATA的值会在writeCallback的最后一个参数上拿到
                        if (CURLE_OK != (code=curl_easy_setopt(c->curl, CURLOPT_WRITEDATA, c)))
                            goto on_error;
                    }
                    else if (curlOpt == CURLOPT_PROGRESSFUNCTION)
                    {
                        luaL_unref(L, LUA_REGISTRYINDEX, c->fprogressRef);
                        c->fprogressRef=ref;

                        v.pcb=(curl_progress_callback)progressCallback;
                        if (CURLE_OK != (code=curl_easy_setopt(c->curl, CURLOPT_PROGRESSDATA, c)))
                            goto on_error;
                    }
                    else if (curlOpt == CURLOPT_HEADERFUNCTION)
                    {
                        luaL_unref(L, LUA_REGISTRYINDEX, c->fheaderRef);
                        c->fheaderRef=ref;

                        v.wcb=(curl_write_callback)headerCallback;
                        if (CURLE_OK != (code=curl_easy_setopt(c->curl, CURLOPT_HEADERDATA, c)))
                            goto on_error;
                    }
#if CURL_NEWER(7,12,3)
                    else if (curlOpt == CURLOPT_IOCTLFUNCTION)
                    {
                        luaL_unref(L, LUA_REGISTRYINDEX, c->fioctlRef);
                        c->fioctlRef=ref;

                        v.icb=ioctlCallback;
                        if (CURLE_OK != (code=curl_easy_setopt(c->curl, CURLOPT_IOCTLDATA, c)))
                            goto on_error;
                    }
#endif
                    else
                    {
                        /* When the option code is any of CURLOPT_xxxDATA and the argument is table,
                        /* userdata or function set the curl option value to the lua object reference */
                        //CURLOPT_WRITEDATA这种
                        printf("setopt: nval:%x\n", ref);
                        v.nval=ref;
                    }
                }break;

                default:
                {
                    //nothing
                }break;
            }break;

        //其他的CURLOPT_Xxx, 这边使用宏的方式来写
/* Handle all supported curl options differently according the specific option argument type */
#undef C_OPT
// 例如: case CURLOPT_URL: get_string(L, 3);
#define C_OPT(n, t) \
        case CURLOPT_##n: \
            v=get_##t(L, 3); \
            break;

#undef C_OPT_SL
// 例如: case CURLOPT_HTTPHEADER: get_slist(L, 3, &KEY_HTTPHEADER); KEY_##n是一个标识符, 在一开始定义的static const char* KEY_##n
#define C_OPT_SL(n) \
        case CURLOPT_##n: \
            { \
                v=get_slist(L, 3, &KEY_##n); \
            }break;

/* Expands all the list of switch-case's here, 剩余的case */
ALL_CURL_OPT

        default:
            luaL_error(L, "Not supported curl option %d", curlOpt);
    }


    /* additional check if the option value has compatible type with the option code */
    switch (lua_type(L, 3))
    {
    case LUA_TFUNCTION: /* allow function argument only for the special option codes */
        if (curlOpt == CURLOPT_READFUNCTION
            || curlOpt == CURLOPT_WRITEFUNCTION
            || curlOpt == CURLOPT_PROGRESSFUNCTION
            || curlOpt == CURLOPT_HEADERFUNCTION
            || curlOpt == CURLOPT_IOCTLFUNCTION
            )
        break;

    case LUA_TTABLE: /* allow table or userdata only for the callback parameter option */
    case LUA_TUSERDATA:
        if (curlOpt != CURLOPT_READDATA
            && curlOpt != CURLOPT_WRITEDATA
            && curlOpt != CURLOPT_PROGRESSDATA
            && curlOpt != CURLOPT_HEADERDATA
 #if CURL_NEWER(7,12,3)
            && curlOpt != CURLOPT_IOCTLDATA
#endif
            )
            luaL_error(L, "argument #2 type %s is not compatible with this option", lua_typename(L, 3));
        break;
    }

    /* handle curl option for setting callback parameter */
    switch (curlOpt)
    {
        case CURLOPT_READDATA:
            //lua层多次调用的情况: obj:setopt(curl.OPT_WRITEDATA, userData_X)
            if (c->rudtype == LUA_TFUNCTION || c->rudtype == LUA_TUSERDATA || c->rudtype == LUA_TTABLE || c->rudtype == LUA_TTHREAD)
                luaL_unref(L, LUA_REGISTRYINDEX, c->rud.nval); /* unref previously referenced read data */

            c->rudtype=lua_type(L, 3); /* set the read data type */
            c->rud=v; /* set the read data value (it can be reference) */
            v.ptr=c; /* set the real read data to curl as our self object */
            break;

        case CURLOPT_WRITEDATA:
            printf("lcurl_easy_setopt: writedata: %d\n", c->wud.nval);
            if (c->wudtype == LUA_TFUNCTION || c->wudtype == LUA_TUSERDATA || c->wudtype == LUA_TTABLE || c->wudtype == LUA_TTHREAD)
                luaL_unref(L, LUA_REGISTRYINDEX, c->wud.nval);
            c->wudtype=lua_type(L, 3);
            c->wud=v;
            v.ptr=c;
            break;

        case CURLOPT_PROGRESSDATA:
            if (c->pudtype == LUA_TFUNCTION || c->pudtype == LUA_TUSERDATA || c->pudtype == LUA_TTABLE || c->pudtype == LUA_TTHREAD)
                luaL_unref(L, LUA_REGISTRYINDEX, c->pud.nval);
            c->pudtype=lua_type(L, 3);
            c->pud=v;
            v.ptr=c;
            break;

        case CURLOPT_HEADERDATA:
            if (c->hudtype == LUA_TFUNCTION || c->hudtype == LUA_TUSERDATA || c->hudtype == LUA_TTABLE || c->hudtype == LUA_TTHREAD)
                luaL_unref(L, LUA_REGISTRYINDEX, c->hud.nval);
            c->hudtype=lua_type(L, 3);
            c->hud=v;
            v.ptr=c;
            break;

#if CURL_NEWER(7,12,3)
        case CURLOPT_IOCTLDATA:
            if (c->iudtype == LUA_TFUNCTION || c->iudtype == LUA_TUSERDATA || c->iudtype == LUA_TTABLE || c->iudtype == LUA_TTHREAD)
                luaL_unref(L, LUA_REGISTRYINDEX, c->iud.nval);
            c->iudtype=lua_type(L, 3);
            c->iud=v;
            v.ptr=c;
            break;
#endif
    }

    /* set easy the curl option with the processed value */
    int opt = (int)lua_tonumber(L, 2);
    //CURLOPT_WRITEDATA时, opt值为curlT指针, v.ptr
    //CURLOPT_WRITEFUNCTION时, opt值为回调函数指针, v.wcb
    //CURLOPT_URL时, opt值为v.sval
    if (CURLE_OK == (code=curl_easy_setopt(c->curl, opt, v.ptr)))
    {
        /* on success return true */
        lua_pushboolean(L, 1);
        printf("lcurl_easy_setopt end: %d, %s\n", lua_gettop(L), lua_typename(L, lua_type(L, 1)) ); // 4, userdata
        return 1; //栈顶弹出1个元素, 作为lua层拿到的返回值: true
    }

on_error:
    /* on fail return nil, error message, error code */
    lua_pushnil(L);
#if CURL_NEWER(7,11,2)
    lua_pushstring(L, curl_easy_strerror(code));
#else
    lua_pushfstring(L, "Curl error: #%d", (code));
#endif
    lua_pushnumber(L, code);
    printf("lcurl_easy_setopt end: %d, %s\n", lua_gettop(L), lua_typename(L, lua_type(L, 1)) );
    return 3; //栈顶弹出3个元素, 作为lua层拿到的返回值: nil, 错误描述, 错误码
}

 

lcurl_easy_perform函数

/* perform the curl commands */
static int lcurl_easy_perform(lua_State* L)
{
    printf("lcurl_easy_perform begin: %d, %s\n", lua_gettop(L), lua_typename(L, lua_type(L, 1)) ); // 1, userdata

    CURLcode code;                       /* return error code from curl */
    curlT* c = tocurl(L, 1);             /* get self object, 将栈底元素转换为curlT类型 */
    code = curl_easy_perform(c->curl);   /* do the curl perform */
    if (CURLE_OK == code)
    {
        /* on success return true */
        lua_pushboolean(L, 1);
        printf("lcurl_easy_perform end: %d, %s\n", lua_gettop(L), lua_typename(L, lua_type(L, 1)) ); // 3, userdata
        return 1; //栈顶弹出1个元素, 作为lua层拿到的返回值: true
    }

    /* on fail return nil, error message, error code */
    lua_pushnil(L);
#if CURL_NEWER(7,11,2)
    lua_pushstring(L, curl_easy_strerror(code));
#else
    lua_pushfstring(L, "Curl error: #%d", (code));
#endif
    lua_pushnumber(L, code);

    return 3; //栈顶弹出3个元素, 作为lua层拿到的返回值: nil, 错误描述, 错误码
}

 

lcurl_easy_close函数

/* Finalizes CURL */
static int lcurl_easy_close(lua_State* L)
{
    printf("lcurl_easy_close begin: %d, %s\n", lua_gettop(L), lua_typename(L, lua_type(L, 1)) ); // 1, userdata

    curlT* c=tocurl(L, 1); //将栈底元素转换为curlT类型
    curl_easy_cleanup(c->curl);
    luaL_unref(L, LUA_REGISTRYINDEX, c->freaderRef); //栈[LUA_REGISTRYINDEX][xxxRefId]设为nil, 即: CURLOPT_XxxFUNCTION关联的lua对象
    luaL_unref(L, LUA_REGISTRYINDEX, c->fwriterRef);
    luaL_unref(L, LUA_REGISTRYINDEX, c->fprogressRef);
    luaL_unref(L, LUA_REGISTRYINDEX, c->fheaderRef);
    luaL_unref(L, LUA_REGISTRYINDEX, c->fioctlRef);

    //栈[LUA_REGISTRYINDEX][nval]设为nil, 即: CURLOPT_XxxDATA关联的lua对象
    if (c->rudtype == LUA_TFUNCTION || c->rudtype == LUA_TUSERDATA || c->rudtype == LUA_TTABLE || c->rudtype == LUA_TTHREAD)
        luaL_unref(L, LUA_REGISTRYINDEX, c->rud.nval);
    if (c->wudtype == LUA_TFUNCTION || c->wudtype == LUA_TUSERDATA || c->wudtype == LUA_TTABLE || c->wudtype == LUA_TTHREAD)
        luaL_unref(L, LUA_REGISTRYINDEX, c->wud.nval);
    if (c->pudtype == LUA_TFUNCTION || c->pudtype == LUA_TUSERDATA || c->pudtype == LUA_TTABLE || c->pudtype == LUA_TTHREAD)
        luaL_unref(L, LUA_REGISTRYINDEX, c->pud.nval);
    if (c->hudtype == LUA_TFUNCTION || c->hudtype == LUA_TUSERDATA || c->hudtype == LUA_TTABLE || c->hudtype == LUA_TTHREAD)
        luaL_unref(L, LUA_REGISTRYINDEX, c->hud.nval);
    if (c->iudtype == LUA_TFUNCTION || c->iudtype == LUA_TUSERDATA || c->iudtype == LUA_TTABLE || c->iudtype == LUA_TTHREAD)
        luaL_unref(L, LUA_REGISTRYINDEX, c->iud.nval);

#undef C_OPT
#undef C_OPT_SL
#undef C_OPT_SPECIAL

#define C_OPT(n, t)
#define C_OPT_SL(n) free_slist(L, &KEY_##n);
#define C_OPT_SPECIAL(n)

    //宏展开
    ALL_CURL_OPT

    c->curl=0;
    lua_pushboolean(L, 1);
    return 1; //栈顶弹出1个元素, 作为lua层拿到的返回值
}

 

posted @ 2025-07-06 13:28  yanghui01  阅读(6)  评论(0)    收藏  举报